aboutsummaryrefslogtreecommitdiffstats
path: root/components/script
diff options
context:
space:
mode:
authorJack Moffitt <jack@metajack.im>2014-08-28 09:34:23 -0600
committerJack Moffitt <jack@metajack.im>2014-09-08 20:21:42 -0600
commitc6ab60dbfc6da7b4f800c9e40893c8b58413960c (patch)
treed1d74076cf7fa20e4f77ec7cb82cae98b67362cb /components/script
parentdb2f642c32fc5bed445bb6f2e45b0f6f0b4342cf (diff)
downloadservo-c6ab60dbfc6da7b4f800c9e40893c8b58413960c.tar.gz
servo-c6ab60dbfc6da7b4f800c9e40893c8b58413960c.zip
Cargoify servo
Diffstat (limited to 'components/script')
-rw-r--r--components/script/Cargo.toml56
-rw-r--r--components/script/cors.rs419
-rw-r--r--components/script/dom/attr.rs200
-rw-r--r--components/script/dom/bindings/DESIGN.md38
-rw-r--r--components/script/dom/bindings/callback.rs156
-rw-r--r--components/script/dom/bindings/codegen/BindingGen.py52
-rw-r--r--components/script/dom/bindings/codegen/BindingUtils.cpp633
-rw-r--r--components/script/dom/bindings/codegen/BindingUtils.h1151
-rw-r--r--components/script/dom/bindings/codegen/Bindings.conf28
-rw-r--r--components/script/dom/bindings/codegen/Codegen.py5788
-rw-r--r--components/script/dom/bindings/codegen/CodegenRust.py5534
-rw-r--r--components/script/dom/bindings/codegen/Configuration.py341
-rw-r--r--components/script/dom/bindings/codegen/DOMJSClass.h114
-rw-r--r--components/script/dom/bindings/codegen/DOMJSProxyHandler.cpp247
-rw-r--r--components/script/dom/bindings/codegen/DOMJSProxyHandler.h109
-rw-r--r--components/script/dom/bindings/codegen/ErrorResult.h59
-rw-r--r--components/script/dom/bindings/codegen/Errors.msg30
-rw-r--r--components/script/dom/bindings/codegen/GenerateCSS2PropertiesWebIDL.py26
-rw-r--r--components/script/dom/bindings/codegen/GlobalGen.py83
-rw-r--r--components/script/dom/bindings/codegen/Makefile.in165
-rw-r--r--components/script/dom/bindings/codegen/Nullable.h68
-rw-r--r--components/script/dom/bindings/codegen/PrimitiveConversions.h350
-rw-r--r--components/script/dom/bindings/codegen/RegisterBindings.h14
-rw-r--r--components/script/dom/bindings/codegen/TypedArray.h121
-rw-r--r--components/script/dom/bindings/codegen/crashtests/769464.html11
-rw-r--r--components/script/dom/bindings/codegen/crashtests/crashtests.list1
-rw-r--r--components/script/dom/bindings/codegen/parser/README1
-rw-r--r--components/script/dom/bindings/codegen/parser/UPSTREAM1
-rw-r--r--components/script/dom/bindings/codegen/parser/WebIDL.py5583
-rw-r--r--components/script/dom/bindings/codegen/parser/external.patch49
-rw-r--r--components/script/dom/bindings/codegen/parser/module.patch12
-rw-r--r--components/script/dom/bindings/codegen/parser/runtests.py79
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_any_null.py14
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_argument_identifier_conflicts.py14
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_argument_novoid.py14
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_array_of_interface.py13
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_arraybuffer.py84
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_attr.py302
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_attr_sequence_type.py67
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_builtin_filename.py11
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_builtins.py41
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_callback.py34
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_callback_interface.py47
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_const.py64
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_constructor.py75
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_constructor_no_interface_object.py28
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_deduplicate.py15
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_dictionary.py198
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_distinguishability.py150
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_double_null.py14
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_duplicate_qualifiers.py84
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_empty_enum.py14
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_enum.py81
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_enum_duplicate_values.py13
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_error_colno.py20
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_error_lineno.py28
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_extended_attributes.py107
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_forward_decl.py15
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_implements.py216
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_incomplete_parent.py18
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_incomplete_types.py44
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_interface.py188
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_interface_const_identifier_conflicts.py15
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_interface_identifier_conflicts_across_members.py60
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_method.py145
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_nullable_equivalency.py126
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_nullable_void.py14
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_optional_constraints.py14
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_overload.py47
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_sanity.py7
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_special_method_signature_mismatch.py294
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_special_methods.py73
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_special_methods_uniqueness.py62
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_treatNonCallableAsNull.py56
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_typedef.py76
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_union.py169
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_union_any.py14
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_union_nullable.py53
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_variadic_callback.py10
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_variadic_constraints.py39
-rwxr-xr-xcomponents/script/dom/bindings/codegen/parser/update.sh3
-rw-r--r--components/script/dom/bindings/codegen/ply/COPYING28
-rw-r--r--components/script/dom/bindings/codegen/ply/README9
-rw-r--r--components/script/dom/bindings/codegen/ply/ply/__init__.py4
-rw-r--r--components/script/dom/bindings/codegen/ply/ply/lex.py1058
-rw-r--r--components/script/dom/bindings/codegen/ply/ply/yacc.py3276
-rw-r--r--components/script/dom/bindings/codegen/pythonpath.py60
-rw-r--r--components/script/dom/bindings/codegen/stubgenerator/Skeleton.cpp40
-rw-r--r--components/script/dom/bindings/codegen/stubgenerator/Skeleton.h40
-rw-r--r--components/script/dom/bindings/codegen/stubgenerator/generate.sh16
-rw-r--r--components/script/dom/bindings/codegen/test/Makefile.in87
-rw-r--r--components/script/dom/bindings/codegen/test/TestBindingHeader.h653
-rw-r--r--components/script/dom/bindings/codegen/test/TestCodeGen.webidl442
-rw-r--r--components/script/dom/bindings/codegen/test/TestDictionary.webidl9
-rw-r--r--components/script/dom/bindings/codegen/test/TestTypedef.webidl7
-rw-r--r--components/script/dom/bindings/codegen/test/file_bug775543.html5
-rw-r--r--components/script/dom/bindings/codegen/test/forOf_iframe.html13
-rw-r--r--components/script/dom/bindings/codegen/test/test_InstanceOf.html28
-rw-r--r--components/script/dom/bindings/codegen/test/test_bug773326.html11
-rw-r--r--components/script/dom/bindings/codegen/test/test_bug775543.html37
-rw-r--r--components/script/dom/bindings/codegen/test/test_bug788369.html30
-rw-r--r--components/script/dom/bindings/codegen/test/test_enums.html15
-rw-r--r--components/script/dom/bindings/codegen/test/test_forOf.html94
-rw-r--r--components/script/dom/bindings/codegen/test/test_integers.html45
-rw-r--r--components/script/dom/bindings/codegen/test/test_interfaceToString.html38
-rw-r--r--components/script/dom/bindings/codegen/test/test_lookupGetter.html49
-rw-r--r--components/script/dom/bindings/codegen/test/test_sequence_wrapping.html60
-rw-r--r--components/script/dom/bindings/codegen/test/test_traceProtos.html37
-rw-r--r--components/script/dom/bindings/conversions.rs378
-rw-r--r--components/script/dom/bindings/error.rs114
-rw-r--r--components/script/dom/bindings/global.rs121
-rw-r--r--components/script/dom/bindings/js.rs496
-rw-r--r--components/script/dom/bindings/proxyhandler.rs155
-rw-r--r--components/script/dom/bindings/str.rs157
-rw-r--r--components/script/dom/bindings/trace.rs185
-rw-r--r--components/script/dom/bindings/utils.rs791
-rw-r--r--components/script/dom/blob.rs59
-rw-r--r--components/script/dom/browsercontext.rs120
-rw-r--r--components/script/dom/canvasrenderingcontext2d.rs79
-rw-r--r--components/script/dom/characterdata.rs106
-rw-r--r--components/script/dom/comment.rs55
-rw-r--r--components/script/dom/console.rs65
-rw-r--r--components/script/dom/customevent.rs79
-rw-r--r--components/script/dom/dedicatedworkerglobalscope.rs200
-rw-r--r--components/script/dom/document.rs855
-rw-r--r--components/script/dom/documentfragment.rs78
-rw-r--r--components/script/dom/documenttype.rs81
-rw-r--r--components/script/dom/domexception.rs132
-rw-r--r--components/script/dom/domimplementation.rs172
-rw-r--r--components/script/dom/domparser.rs64
-rw-r--r--components/script/dom/domrect.rs72
-rw-r--r--components/script/dom/domrectlist.rs62
-rw-r--r--components/script/dom/domtokenlist.rs101
-rw-r--r--components/script/dom/element.rs958
-rw-r--r--components/script/dom/event.rs174
-rw-r--r--components/script/dom/eventdispatcher.rs139
-rw-r--r--components/script/dom/eventtarget.rs287
-rw-r--r--components/script/dom/file.rs48
-rw-r--r--components/script/dom/formdata.rs115
-rw-r--r--components/script/dom/htmlanchorelement.rs132
-rw-r--r--components/script/dom/htmlappletelement.rs44
-rw-r--r--components/script/dom/htmlareaelement.rs81
-rw-r--r--components/script/dom/htmlaudioelement.rs44
-rw-r--r--components/script/dom/htmlbaseelement.rs44
-rw-r--r--components/script/dom/htmlbodyelement.rs98
-rw-r--r--components/script/dom/htmlbrelement.rs44
-rw-r--r--components/script/dom/htmlbuttonelement.rs130
-rw-r--r--components/script/dom/htmlcanvaselement.rs160
-rw-r--r--components/script/dom/htmlcollection.rs257
-rw-r--r--components/script/dom/htmldataelement.rs44
-rw-r--r--components/script/dom/htmldatalistelement.rs62
-rw-r--r--components/script/dom/htmldirectoryelement.rs44
-rw-r--r--components/script/dom/htmldivelement.rs44
-rw-r--r--components/script/dom/htmldlistelement.rs44
-rw-r--r--components/script/dom/htmlelement.rs120
-rw-r--r--components/script/dom/htmlembedelement.rs44
-rw-r--r--components/script/dom/htmlfieldsetelement.rs156
-rw-r--r--components/script/dom/htmlfontelement.rs44
-rw-r--r--components/script/dom/htmlformelement.rs44
-rw-r--r--components/script/dom/htmlframeelement.rs44
-rw-r--r--components/script/dom/htmlframesetelement.rs44
-rw-r--r--components/script/dom/htmlheadelement.rs44
-rw-r--r--components/script/dom/htmlheadingelement.rs56
-rw-r--r--components/script/dom/htmlhrelement.rs44
-rw-r--r--components/script/dom/htmlhtmlelement.rs44
-rw-r--r--components/script/dom/htmliframeelement.rs225
-rw-r--r--components/script/dom/htmlimageelement.rs233
-rw-r--r--components/script/dom/htmlinputelement.rs124
-rw-r--r--components/script/dom/htmllabelelement.rs44
-rw-r--r--components/script/dom/htmllegendelement.rs44
-rw-r--r--components/script/dom/htmllielement.rs44
-rw-r--r--components/script/dom/htmllinkelement.rs81
-rw-r--r--components/script/dom/htmlmapelement.rs44
-rw-r--r--components/script/dom/htmlmediaelement.rs42
-rw-r--r--components/script/dom/htmlmetaelement.rs44
-rw-r--r--components/script/dom/htmlmeterelement.rs44
-rw-r--r--components/script/dom/htmlmodelement.rs44
-rw-r--r--components/script/dom/htmlobjectelement.rs113
-rw-r--r--components/script/dom/htmlolistelement.rs44
-rw-r--r--components/script/dom/htmloptgroupelement.rs106
-rw-r--r--components/script/dom/htmloptionelement.rs124
-rw-r--r--components/script/dom/htmloutputelement.rs53
-rw-r--r--components/script/dom/htmlparagraphelement.rs44
-rw-r--r--components/script/dom/htmlparamelement.rs44
-rw-r--r--components/script/dom/htmlpreelement.rs44
-rw-r--r--components/script/dom/htmlprogresselement.rs44
-rw-r--r--components/script/dom/htmlquoteelement.rs44
-rw-r--r--components/script/dom/htmlscriptelement.rs130
-rw-r--r--components/script/dom/htmlselectelement.rs136
-rw-r--r--components/script/dom/htmlserializer.rs171
-rw-r--r--components/script/dom/htmlsourceelement.rs44
-rw-r--r--components/script/dom/htmlspanelement.rs44
-rw-r--r--components/script/dom/htmlstyleelement.rs97
-rw-r--r--components/script/dom/htmltablecaptionelement.rs44
-rw-r--r--components/script/dom/htmltablecellelement.rs42
-rw-r--r--components/script/dom/htmltablecolelement.rs44
-rw-r--r--components/script/dom/htmltabledatacellelement.rs44
-rw-r--r--components/script/dom/htmltableelement.rs81
-rw-r--r--components/script/dom/htmltableheadercellelement.rs44
-rw-r--r--components/script/dom/htmltablerowelement.rs44
-rw-r--r--components/script/dom/htmltablesectionelement.rs44
-rw-r--r--components/script/dom/htmltemplateelement.rs44
-rw-r--r--components/script/dom/htmltextareaelement.rs124
-rw-r--r--components/script/dom/htmltimeelement.rs44
-rw-r--r--components/script/dom/htmltitleelement.rs69
-rw-r--r--components/script/dom/htmltrackelement.rs44
-rw-r--r--components/script/dom/htmlulistelement.rs44
-rw-r--r--components/script/dom/htmlunknownelement.rs44
-rw-r--r--components/script/dom/htmlvideoelement.rs44
-rw-r--r--components/script/dom/location.rs64
-rw-r--r--components/script/dom/macros.rs44
-rw-r--r--components/script/dom/messageevent.rs99
-rw-r--r--components/script/dom/mouseevent.rs182
-rw-r--r--components/script/dom/namednodemap.rs54
-rw-r--r--components/script/dom/navigator.rs58
-rw-r--r--components/script/dom/node.rs2085
-rw-r--r--components/script/dom/nodeiterator.rs35
-rw-r--r--components/script/dom/nodelist.rs82
-rw-r--r--components/script/dom/performance.rs52
-rw-r--r--components/script/dom/performancetiming.rs57
-rw-r--r--components/script/dom/processinginstruction.rs53
-rw-r--r--components/script/dom/progressevent.rs75
-rw-r--r--components/script/dom/range.rs50
-rw-r--r--components/script/dom/screen.rs45
-rw-r--r--components/script/dom/testbinding.rs299
-rw-r--r--components/script/dom/text.rs52
-rw-r--r--components/script/dom/treewalker.rs35
-rw-r--r--components/script/dom/uievent.rs95
-rw-r--r--components/script/dom/urlsearchparams.rs152
-rw-r--r--components/script/dom/validitystate.rs36
-rw-r--r--components/script/dom/virtualmethods.rs219
-rw-r--r--components/script/dom/webidls/Attr.webidl18
-rw-r--r--components/script/dom/webidls/Blob.webidl29
-rw-r--r--components/script/dom/webidls/CanvasRenderingContext2D.webidl104
-rw-r--r--components/script/dom/webidls/CharacterData.webidl28
-rw-r--r--components/script/dom/webidls/ChildNode.webidl25
-rw-r--r--components/script/dom/webidls/Comment.webidl15
-rw-r--r--components/script/dom/webidls/Console.webidl21
-rw-r--r--components/script/dom/webidls/CustomEvent.webidl27
-rw-r--r--components/script/dom/webidls/DOMException.webidl47
-rw-r--r--components/script/dom/webidls/DOMImplementation.webidl25
-rw-r--r--components/script/dom/webidls/DOMParser.webidl21
-rw-r--r--components/script/dom/webidls/DOMRect.webidl14
-rw-r--r--components/script/dom/webidls/DOMRectList.webidl12
-rw-r--r--components/script/dom/webidls/DOMTokenList.webidl18
-rw-r--r--components/script/dom/webidls/DedicatedWorkerGlobalScope.webidl10
-rw-r--r--components/script/dom/webidls/Document.webidl71
-rw-r--r--components/script/dom/webidls/DocumentFragment.webidl11
-rw-r--r--components/script/dom/webidls/DocumentType.webidl19
-rw-r--r--components/script/dom/webidls/Element.webidl70
-rw-r--r--components/script/dom/webidls/Event.webidl43
-rw-r--r--components/script/dom/webidls/EventHandler.webidl46
-rw-r--r--components/script/dom/webidls/EventListener.webidl16
-rw-r--r--components/script/dom/webidls/EventTarget.webidl22
-rw-r--r--components/script/dom/webidls/File.webidl15
-rw-r--r--components/script/dom/webidls/FormData.webidl22
-rw-r--r--components/script/dom/webidls/HTMLAnchorElement.webidl38
-rw-r--r--components/script/dom/webidls/HTMLAppletElement.webidl19
-rw-r--r--components/script/dom/webidls/HTMLAreaElement.webidl26
-rw-r--r--components/script/dom/webidls/HTMLAudioElement.webidl8
-rw-r--r--components/script/dom/webidls/HTMLBRElement.webidl14
-rw-r--r--components/script/dom/webidls/HTMLBaseElement.webidl10
-rw-r--r--components/script/dom/webidls/HTMLBodyElement.webidl21
-rw-r--r--components/script/dom/webidls/HTMLButtonElement.webidl29
-rw-r--r--components/script/dom/webidls/HTMLCanvasElement.webidl24
-rw-r--r--components/script/dom/webidls/HTMLCollection.webidl10
-rw-r--r--components/script/dom/webidls/HTMLDListElement.webidl14
-rw-r--r--components/script/dom/webidls/HTMLDataElement.webidl9
-rw-r--r--components/script/dom/webidls/HTMLDataListElement.webidl9
-rw-r--r--components/script/dom/webidls/HTMLDirectoryElement.webidl9
-rw-r--r--components/script/dom/webidls/HTMLDivElement.webidl14
-rw-r--r--components/script/dom/webidls/HTMLElement.webidl48
-rw-r--r--components/script/dom/webidls/HTMLEmbedElement.webidl21
-rw-r--r--components/script/dom/webidls/HTMLFieldSetElement.webidl23
-rw-r--r--components/script/dom/webidls/HTMLFontElement.webidl11
-rw-r--r--components/script/dom/webidls/HTMLFormElement.webidl30
-rw-r--r--components/script/dom/webidls/HTMLFrameElement.webidl19
-rw-r--r--components/script/dom/webidls/HTMLFrameSetElement.webidl11
-rw-r--r--components/script/dom/webidls/HTMLHRElement.webidl18
-rw-r--r--components/script/dom/webidls/HTMLHeadElement.webidl7
-rw-r--r--components/script/dom/webidls/HTMLHeadingElement.webidl14
-rw-r--r--components/script/dom/webidls/HTMLHtmlElement.webidl14
-rw-r--r--components/script/dom/webidls/HTMLIFrameElement.webidl33
-rw-r--r--components/script/dom/webidls/HTMLImageElement.webidl34
-rw-r--r--components/script/dom/webidls/HTMLInputElement.webidl76
-rw-r--r--components/script/dom/webidls/HTMLLIElement.webidl16
-rw-r--r--components/script/dom/webidls/HTMLLabelElement.webidl11
-rw-r--r--components/script/dom/webidls/HTMLLegendElement.webidl16
-rw-r--r--components/script/dom/webidls/HTMLLinkElement.webidl26
-rw-r--r--components/script/dom/webidls/HTMLMapElement.webidl11
-rw-r--r--components/script/dom/webidls/HTMLMediaElement.webidl67
-rw-r--r--components/script/dom/webidls/HTMLMetaElement.webidl18
-rw-r--r--components/script/dom/webidls/HTMLMeterElement.webidl15
-rw-r--r--components/script/dom/webidls/HTMLModElement.webidl10
-rw-r--r--components/script/dom/webidls/HTMLOListElement.webidl18
-rw-r--r--components/script/dom/webidls/HTMLObjectElement.webidl44
-rw-r--r--components/script/dom/webidls/HTMLOptGroupElement.webidl10
-rw-r--r--components/script/dom/webidls/HTMLOptionElement.webidl18
-rw-r--r--components/script/dom/webidls/HTMLOutputElement.webidl24
-rw-r--r--components/script/dom/webidls/HTMLParagraphElement.webidl14
-rw-r--r--components/script/dom/webidls/HTMLParamElement.webidl18
-rw-r--r--components/script/dom/webidls/HTMLPreElement.webidl14
-rw-r--r--components/script/dom/webidls/HTMLProgressElement.webidl12
-rw-r--r--components/script/dom/webidls/HTMLQuoteElement.webidl9
-rw-r--r--components/script/dom/webidls/HTMLScriptElement.webidl25
-rw-r--r--components/script/dom/webidls/HTMLSelectElement.webidl40
-rw-r--r--components/script/dom/webidls/HTMLSourceElement.webidl10
-rw-r--r--components/script/dom/webidls/HTMLSpanElement.webidl7
-rw-r--r--components/script/dom/webidls/HTMLStyleElement.webidl12
-rw-r--r--components/script/dom/webidls/HTMLTableCaptionElement.webidl14
-rw-r--r--components/script/dom/webidls/HTMLTableCellElement.webidl29
-rw-r--r--components/script/dom/webidls/HTMLTableColElement.webidl20
-rw-r--r--components/script/dom/webidls/HTMLTableDataCellElement.webidl14
-rw-r--r--components/script/dom/webidls/HTMLTableElement.webidl40
-rw-r--r--components/script/dom/webidls/HTMLTableHeaderCellElement.webidl12
-rw-r--r--components/script/dom/webidls/HTMLTableRowElement.webidl25
-rw-r--r--components/script/dom/webidls/HTMLTableSectionElement.webidl21
-rw-r--r--components/script/dom/webidls/HTMLTemplateElement.webidl9
-rw-r--r--components/script/dom/webidls/HTMLTextAreaElement.webidl45
-rw-r--r--components/script/dom/webidls/HTMLTimeElement.webidl9
-rw-r--r--components/script/dom/webidls/HTMLTitleElement.webidl10
-rw-r--r--components/script/dom/webidls/HTMLTrackElement.webidl21
-rw-r--r--components/script/dom/webidls/HTMLUListElement.webidl15
-rw-r--r--components/script/dom/webidls/HTMLUnknownElement.webidl16
-rw-r--r--components/script/dom/webidls/HTMLVideoElement.webidl13
-rw-r--r--components/script/dom/webidls/Location.webidl12
-rw-r--r--components/script/dom/webidls/MessageEvent.webidl23
-rw-r--r--components/script/dom/webidls/MouseEvent.webidl43
-rw-r--r--components/script/dom/webidls/NamedNodeMap.webidl8
-rw-r--r--components/script/dom/webidls/Navigator.webidl27
-rw-r--r--components/script/dom/webidls/Node.webidl79
-rw-r--r--components/script/dom/webidls/NodeFilter.webidl33
-rw-r--r--components/script/dom/webidls/NodeIterator.webidl32
-rw-r--r--components/script/dom/webidls/NodeList.webidl13
-rw-r--r--components/script/dom/webidls/ParentNode.webidl34
-rw-r--r--components/script/dom/webidls/Performance.webidl19
-rw-r--r--components/script/dom/webidls/PerformanceTiming.webidl32
-rw-r--r--components/script/dom/webidls/ProcessingInstruction.webidl12
-rw-r--r--components/script/dom/webidls/ProgressEvent.webidl28
-rw-r--r--components/script/dom/webidls/Range.webidl85
-rw-r--r--components/script/dom/webidls/Screen.webidl14
-rw-r--r--components/script/dom/webidls/TestBinding.webidl276
-rw-r--r--components/script/dom/webidls/Text.webidl18
-rw-r--r--components/script/dom/webidls/TreeWalker.webidl23
-rw-r--r--components/script/dom/webidls/UIEvent.webidl25
-rw-r--r--components/script/dom/webidls/URLSearchParams.webidl19
-rw-r--r--components/script/dom/webidls/URLUtils.webidl25
-rw-r--r--components/script/dom/webidls/URLUtilsReadOnly.webidl23
-rw-r--r--components/script/dom/webidls/ValidityState.webidl19
-rw-r--r--components/script/dom/webidls/Window.webidl131
-rw-r--r--components/script/dom/webidls/Worker.webidl20
-rw-r--r--components/script/dom/webidls/WorkerGlobalScope.webidl32
-rw-r--r--components/script/dom/webidls/WorkerLocation.webidl9
-rw-r--r--components/script/dom/webidls/WorkerNavigator.webidl11
-rw-r--r--components/script/dom/webidls/XMLHttpRequest.webidl72
-rw-r--r--components/script/dom/webidls/XMLHttpRequestEventTarget.webidl26
-rw-r--r--components/script/dom/webidls/XMLHttpRequestUpload.webidl18
-rw-r--r--components/script/dom/window.rs513
-rw-r--r--components/script/dom/worker.rs160
-rw-r--r--components/script/dom/workerglobalscope.rs145
-rw-r--r--components/script/dom/workerlocation.rs64
-rw-r--r--components/script/dom/workernavigator.rs58
-rw-r--r--components/script/dom/xmlhttprequest.rs970
-rw-r--r--components/script/dom/xmlhttprequesteventtarget.rs112
-rw-r--r--components/script/dom/xmlhttprequestupload.rs41
-rw-r--r--components/script/html/cssparse.rs72
-rw-r--r--components/script/html/hubbub_html_parser.rs615
-rw-r--r--components/script/layout_interface.rs204
-rw-r--r--components/script/lib.rs209
-rw-r--r--components/script/makefile.cargo45
-rw-r--r--components/script/page.rs437
-rw-r--r--components/script/script_task.rs933
372 files changed, 54475 insertions, 0 deletions
diff --git a/components/script/Cargo.toml b/components/script/Cargo.toml
new file mode 100644
index 00000000000..1748a57956d
--- /dev/null
+++ b/components/script/Cargo.toml
@@ -0,0 +1,56 @@
+[package]
+name = "script"
+version = "0.0.1"
+authors = ["The Servo Project Developers"]
+
+build = "make -f makefile.cargo"
+
+[lib]
+name = "script"
+path = "lib.rs"
+
+[dependencies.macros]
+path = "../macros"
+
+[dependencies.util]
+path = "../util"
+
+[dependencies.msg]
+path = "../msg"
+
+[dependencies.net]
+path = "../net"
+
+[dependencies.script_traits]
+path = "../script_traits"
+
+[dependencies.style]
+path = "../style"
+
+[dependencies.gfx]
+path = "../gfx"
+
+[dependencies.canvas]
+path = "../canvas"
+
+[dependencies.cssparser]
+git = "https://github.com/servo/rust-cssparser"
+
+[dependencies.geom]
+git = "https://github.com/servo/rust-geom"
+
+[dependencies.hubbub]
+git = "https://github.com/servo/rust-hubbub"
+
+[dependencies.encoding]
+git = "https://github.com/lifthrasiir/rust-encoding"
+
+[dependencies.http]
+git = "https://github.com/servo/rust-http"
+branch = "servo"
+
+[dependencies.js]
+git = "https://github.com/servo/rust-mozjs"
+
+[dependencies.url]
+git = "https://github.com/servo/rust-url"
diff --git a/components/script/cors.rs b/components/script/cors.rs
new file mode 100644
index 00000000000..3a3fd98ee90
--- /dev/null
+++ b/components/script/cors.rs
@@ -0,0 +1,419 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! A partial implementation of CORS
+//! For now this library is XHR-specific.
+//! For stuff involving `<img>`, `<iframe>`, `<form>`, etc please check what
+//! the request mode should be and compare with the fetch spec
+//! This library will eventually become the core of the Fetch crate
+//! with CORSRequest being expanded into FetchRequest (etc)
+
+use std::ascii::{StrAsciiExt, OwnedStrAsciiExt};
+use std::from_str::FromStr;
+use std::io::BufReader;
+use std::str::StrSlice;
+use time;
+use time::{now, Timespec};
+
+use ResponseHeaderCollection = http::headers::response::HeaderCollection;
+use RequestHeaderCollection = http::headers::request::HeaderCollection;
+use RequestHeader = http::headers::request::Header;
+
+use http::client::{RequestWriter, NetworkStream};
+use http::headers::{HeaderConvertible, HeaderEnum, HeaderValueByteIterator};
+use http::headers::content_type::MediaType;
+use http::headers::request::{Accept, AcceptLanguage, ContentLanguage, ContentType};
+use http::method::{Method, Get, Head, Post, Options};
+
+use url::{RelativeSchemeData, Url, UrlParser};
+
+#[deriving(Clone)]
+pub struct CORSRequest {
+ pub origin: Url,
+ pub destination: Url,
+ pub mode: RequestMode,
+ pub method: Method,
+ pub headers: RequestHeaderCollection,
+ /// CORS preflight flag (http://fetch.spec.whatwg.org/#concept-http-fetch)
+ /// Indicates that a CORS preflight request and/or cache check is to be performed
+ pub preflight_flag: bool
+}
+
+/// http://fetch.spec.whatwg.org/#concept-request-mode
+/// This only covers some of the request modes. The
+/// `same-origin` and `no CORS` modes are unnecessary for XHR.
+#[deriving(PartialEq, Clone)]
+pub enum RequestMode {
+ CORSMode, // CORS
+ ForcedPreflightMode // CORS-with-forced-preflight
+}
+
+impl CORSRequest {
+ /// Creates a CORS request if necessary. Will return an error when fetching is forbidden
+ pub fn maybe_new(referer: Url, destination: Url, mode: RequestMode,
+ method: Method, headers: RequestHeaderCollection) -> Result<Option<CORSRequest>, ()> {
+ if referer.scheme == destination.scheme &&
+ referer.host() == destination.host() &&
+ referer.port() == destination.port() {
+ return Ok(None); // Not cross-origin, proceed with a normal fetch
+ }
+ match destination.scheme.as_slice() {
+ // Todo: If the request's same origin data url flag is set (which isn't the case for XHR)
+ // we can fetch a data URL normally. about:blank can also be fetched by XHR
+ "http" | "https" => {
+ let mut req = CORSRequest::new(referer, destination, mode, method, headers);
+ req.preflight_flag = !is_simple_method(&req.method) || mode == ForcedPreflightMode;
+ if req.headers.iter().all(|h| is_simple_header(&h)) {
+ req.preflight_flag = true;
+ }
+ Ok(Some(req))
+ },
+ _ => Err(()),
+ }
+ }
+
+ fn new(mut referer: Url, destination: Url, mode: RequestMode, method: Method,
+ headers: RequestHeaderCollection) -> CORSRequest {
+ match referer.scheme_data {
+ RelativeSchemeData(ref mut data) => data.path = vec!(),
+ _ => {}
+ };
+ referer.fragment = None;
+ referer.query = None;
+ CORSRequest {
+ origin: referer,
+ destination: destination,
+ mode: mode,
+ method: method,
+ headers: headers,
+ preflight_flag: false
+ }
+ }
+
+ /// http://fetch.spec.whatwg.org/#concept-http-fetch
+ /// This method assumes that the CORS flag is set
+ /// This does not perform the full HTTP fetch, rather it handles part of the CORS filtering
+ /// if self.mode is ForcedPreflightMode, then the CORS-with-forced-preflight
+ /// fetch flag is set as well
+ pub fn http_fetch(&self) -> CORSResponse {
+ let response = CORSResponse::new();
+ // Step 2: Handle service workers (unimplemented)
+ // Step 3
+ // Substep 1: Service workers (unimplemented )
+ // Substep 2
+ let cache = &mut CORSCache(vec!()); // XXXManishearth Should come from user agent
+ if self.preflight_flag &&
+ !cache.match_method(self, &self.method) &&
+ !self.headers.iter().all(|h| is_simple_header(&h) && cache.match_header(self, h.header_name().as_slice())) {
+ if !is_simple_method(&self.method) || self.mode == ForcedPreflightMode {
+ return self.preflight_fetch();
+ // Everything after this is part of XHR::fetch()
+ // Expect the organization of code to improve once we have a fetch crate
+ }
+ }
+ response
+ }
+
+ /// http://fetch.spec.whatwg.org/#cors-preflight-fetch
+ fn preflight_fetch(&self) -> CORSResponse {
+ let error = CORSResponse::new_error();
+ let mut cors_response = CORSResponse::new();
+
+ let mut preflight = self.clone(); // Step 1
+ preflight.method = Options; // Step 2
+ preflight.headers = RequestHeaderCollection::new(); // Step 3
+ // Step 4
+ preflight.insert_string_header("Access-Control-Request-Method".to_string(), self.method.http_value());
+
+ // Step 5 - 7
+ let mut header_names = vec!();
+ for header in self.headers.iter() {
+ header_names.push(header.header_name().into_ascii_lower());
+ }
+ header_names.sort();
+ let header_list = header_names.connect(", "); // 0x2C 0x20
+ preflight.insert_string_header("Access-Control-Request-Headers".to_string(), header_list);
+
+ // Step 8 unnecessary, we don't use the request body
+ // Step 9, 10 unnecessary, we're writing our own fetch code
+
+ // Step 11
+ let preflight_request = RequestWriter::<NetworkStream>::new(preflight.method, preflight.destination);
+ let mut writer = match preflight_request {
+ Ok(w) => box w,
+ Err(_) => return error
+ };
+
+ let host = writer.headers.host.clone();
+ writer.headers = box preflight.headers.clone();
+ writer.headers.host = host;
+ let response = match writer.read_response() {
+ Ok(r) => r,
+ Err(_) => return error
+ };
+
+ // Step 12
+ match response.status.code() {
+ 200 .. 299 => {}
+ _ => return error
+ }
+ cors_response.headers = *response.headers.clone();
+ // Substeps 1-3 (parsing rules: http://fetch.spec.whatwg.org/#http-new-header-syntax)
+ fn find_header(headers: &ResponseHeaderCollection, name: &str) -> Option<String> {
+ headers.iter().find(|h| h.header_name().as_slice()
+ .eq_ignore_ascii_case(name))
+ .map(|h| h.header_value())
+ }
+ let methods_string = match find_header(&*response.headers, "Access-Control-Allow-Methods") {
+ Some(s) => s,
+ _ => return error
+ };
+ let methods = methods_string.as_slice().split(',');
+ let headers_string = match find_header(&*response.headers, "Access-Control-Allow-Headers") {
+ Some(s) => s,
+ _ => return error
+ };
+ let headers = headers_string.as_slice().split(0x2Cu8 as char);
+ // The ABNF # rule will consider consecutive delimeters as a single delimeter
+ let mut methods: Vec<String> = methods.filter(|s| s.len() > 0).map(|s| s.to_string()).collect();
+ let headers: Vec<String> = headers.filter(|s| s.len() > 0).map(|s| s.to_string()).collect();
+ // Substep 4
+ if methods.len() == 0 || preflight.mode == ForcedPreflightMode {
+ methods = vec!(self.method.http_value());
+ }
+ // Substep 5
+ if !is_simple_method(&self.method) &&
+ !methods.iter().any(|ref m| self.method.http_value().as_slice().eq_ignore_ascii_case(m.as_slice())) {
+ return error;
+ }
+ // Substep 6
+ for h in self.headers.iter() {
+ if is_simple_header(&h) {
+ continue;
+ }
+ if !headers.iter().any(|ref h2| h.header_name().as_slice().eq_ignore_ascii_case(h2.as_slice())) {
+ return error;
+ }
+ }
+ // Substep 7, 8
+ let max_age: uint = find_header(&*response.headers, "Access-Control-Max-Age")
+ .and_then(|h| FromStr::from_str(h.as_slice())).unwrap_or(0);
+ // Substep 9: Impose restrictions on max-age, if any (unimplemented)
+ // Substeps 10-12: Add a cache (partially implemented, XXXManishearth)
+ // This cache should come from the user agent, creating a new one here to check
+ // for compile time errors
+ let cache = &mut CORSCache(vec!());
+ for m in methods.iter() {
+ let maybe_method: Option<Method> = FromStr::from_str(m.as_slice());
+ maybe_method.map(|ref m| {
+ let cache_match = cache.match_method_and_update(self, m, max_age);
+ if !cache_match {
+ cache.insert(CORSCacheEntry::new(self.origin.clone(), self.destination.clone(),
+ max_age, false, MethodData(m.clone())));
+ }
+ });
+ }
+ for h in headers.iter() {
+ let cache_match = cache.match_header_and_update(self, h.as_slice(), max_age);
+ if !cache_match {
+ cache.insert(CORSCacheEntry::new(self.origin.clone(), self.destination.clone(),
+ max_age, false, HeaderData(h.to_string())));
+ }
+ }
+ cors_response
+ }
+
+ fn insert_string_header(&mut self, name: String, value: String) {
+ let value_bytes = value.into_bytes();
+ let mut reader = BufReader::new(value_bytes.as_slice());
+ let maybe_header: Option<RequestHeader> = HeaderEnum::value_from_stream(
+ String::from_str(name.as_slice()),
+ &mut HeaderValueByteIterator::new(&mut reader));
+ self.headers.insert(maybe_header.unwrap());
+ }
+}
+
+
+pub struct CORSResponse {
+ pub network_error: bool,
+ pub headers: ResponseHeaderCollection
+}
+
+impl CORSResponse {
+ fn new() -> CORSResponse {
+ CORSResponse {
+ network_error: false,
+ headers: ResponseHeaderCollection::new()
+ }
+ }
+
+ fn new_error() -> CORSResponse {
+ CORSResponse {
+ network_error: true,
+ headers: ResponseHeaderCollection::new()
+ }
+ }
+}
+
+// CORS Cache stuff
+
+/// A CORS cache object. Anchor it somewhere to the user agent.
+#[deriving(Clone)]
+pub struct CORSCache(Vec<CORSCacheEntry>);
+
+/// Union type for CORS cache entries
+/// Each entry might pertain to a header or method
+#[deriving(Clone)]
+pub enum HeaderOrMethod {
+ HeaderData(String),
+ MethodData(Method)
+}
+
+impl HeaderOrMethod {
+ fn match_header(&self, header_name: &str) -> bool {
+ match *self {
+ HeaderData(ref s) => s.as_slice().eq_ignore_ascii_case(header_name),
+ _ => false
+ }
+ }
+
+ fn match_method(&self, method: &Method) -> bool {
+ match *self {
+ MethodData(ref m) => m == method,
+ _ => false
+ }
+ }
+}
+
+// An entry in the CORS cache
+#[deriving(Clone)]
+pub struct CORSCacheEntry {
+ pub origin: Url,
+ pub url: Url,
+ pub max_age: uint,
+ pub credentials: bool,
+ pub header_or_method: HeaderOrMethod,
+ created: Timespec
+}
+
+impl CORSCacheEntry {
+ fn new (origin:Url, url: Url, max_age: uint, credentials: bool, header_or_method: HeaderOrMethod) -> CORSCacheEntry {
+ CORSCacheEntry {
+ origin: origin,
+ url: url,
+ max_age: max_age,
+ credentials: credentials,
+ header_or_method: header_or_method,
+ created: time::now().to_timespec()
+ }
+ }
+}
+
+impl CORSCache {
+ /// http://fetch.spec.whatwg.org/#concept-cache-clear
+ #[allow(dead_code)]
+ fn clear (&mut self, request: &CORSRequest) {
+ let CORSCache(buf) = self.clone();
+ let new_buf: Vec<CORSCacheEntry> = buf.move_iter().filter(|e| e.origin == request.origin && request.destination == e.url).collect();
+ *self = CORSCache(new_buf);
+ }
+
+ // Remove old entries
+ fn cleanup(&mut self) {
+ let CORSCache(buf) = self.clone();
+ let now = time::now().to_timespec();
+ let new_buf: Vec<CORSCacheEntry> = buf.move_iter().filter(|e| now.sec > e.created.sec + e.max_age as i64).collect();
+ *self = CORSCache(new_buf);
+ }
+
+ /// http://fetch.spec.whatwg.org/#concept-cache-match-header
+ fn find_entry_by_header<'a>(&'a mut self, request: &CORSRequest, header_name: &str) -> Option<&'a mut CORSCacheEntry> {
+ self.cleanup();
+ let CORSCache(ref mut buf) = *self;
+ // Credentials are not yet implemented here
+ let entry = buf.mut_iter().find(|e| e.origin.scheme == request.origin.scheme &&
+ e.origin.host() == request.origin.host() &&
+ e.origin.port() == request.origin.port() &&
+ e.url == request.destination &&
+ e.header_or_method.match_header(header_name));
+ entry
+ }
+
+ fn match_header(&mut self, request: &CORSRequest, header_name: &str) -> bool {
+ self.find_entry_by_header(request, header_name).is_some()
+ }
+
+ fn match_header_and_update(&mut self, request: &CORSRequest, header_name: &str, new_max_age: uint) -> bool {
+ self.find_entry_by_header(request, header_name).map(|e| e.max_age = new_max_age).is_some()
+ }
+
+ fn find_entry_by_method<'a>(&'a mut self, request: &CORSRequest, method: &Method) -> Option<&'a mut CORSCacheEntry> {
+ // we can take the method from CORSRequest itself
+ self.cleanup();
+ let CORSCache(ref mut buf) = *self;
+ // Credentials are not yet implemented here
+ let entry = buf.mut_iter().find(|e| e.origin.scheme == request.origin.scheme &&
+ e.origin.host() == request.origin.host() &&
+ e.origin.port() == request.origin.port() &&
+ e.url == request.destination &&
+ e.header_or_method.match_method(method));
+ entry
+ }
+
+ /// http://fetch.spec.whatwg.org/#concept-cache-match-method
+ fn match_method(&mut self, request: &CORSRequest, method: &Method) -> bool {
+ self.find_entry_by_method(request, method).is_some()
+ }
+
+ fn match_method_and_update(&mut self, request: &CORSRequest, method: &Method, new_max_age: uint) -> bool {
+ self.find_entry_by_method(request, method).map(|e| e.max_age = new_max_age).is_some()
+ }
+
+ fn insert(&mut self, entry: CORSCacheEntry) {
+ self.cleanup();
+ let CORSCache(ref mut buf) = *self;
+ buf.push(entry);
+ }
+}
+
+fn is_simple_header(h: &RequestHeader) -> bool {
+ match *h {
+ Accept(_) | AcceptLanguage(_) | ContentLanguage(_) => true,
+ ContentType(MediaType {type_: ref t, subtype: ref s, ..}) => match (t.as_slice(), s.as_slice()) {
+ ("text", "plain") | ("application", "x-www-form-urlencoded") | ("multipart", "form-data") => true,
+ _ => false
+ },
+ _ => false
+ }
+}
+
+fn is_simple_method(m: &Method) -> bool {
+ match *m {
+ Get | Head | Post => true,
+ _ => false
+ }
+}
+
+/// Perform a CORS check on a header list and CORS request
+/// http://fetch.spec.whatwg.org/#cors-check
+pub fn allow_cross_origin_request(req: &CORSRequest, headers: &ResponseHeaderCollection) -> bool {
+ let allow_cross_origin_request = headers.iter().find(|h| h.header_name()
+ .as_slice()
+ .eq_ignore_ascii_case("Access-Control-Allow-Origin"));
+ match allow_cross_origin_request {
+ Some(h) => {
+ let origin_str = h.header_value();
+ if origin_str == "*".to_string() {
+ return true; // Not always true, depends on credentials mode
+ }
+ match UrlParser::new().parse(origin_str.as_slice()) {
+ Ok(parsed) => parsed.scheme == req.origin.scheme &&
+ parsed.host() == req.origin.host() &&
+ parsed.port() == req.origin.port(),
+ _ => false
+ }
+ },
+ None => false
+ }
+}
diff --git a/components/script/dom/attr.rs b/components/script/dom/attr.rs
new file mode 100644
index 00000000000..9f3fc9dc96e
--- /dev/null
+++ b/components/script/dom/attr.rs
@@ -0,0 +1,200 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::AttrBinding;
+use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
+use dom::bindings::codegen::InheritTypes::NodeCast;
+use dom::bindings::global::Window;
+use dom::bindings::js::{JS, JSRef, Temporary};
+use dom::bindings::trace::Traceable;
+use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
+use dom::element::{Element, AttributeHandlers};
+use dom::node::Node;
+use dom::window::Window;
+use dom::virtualmethods::vtable_for;
+use servo_util::atom::Atom;
+use servo_util::namespace;
+use servo_util::namespace::Namespace;
+use servo_util::str::{DOMString, split_html_space_chars};
+use std::cell::{Ref, RefCell};
+use std::mem;
+use std::slice::Items;
+
+pub enum AttrSettingType {
+ FirstSetAttr,
+ ReplacedAttr,
+}
+
+#[deriving(PartialEq, Clone, Encodable)]
+pub enum AttrValue {
+ StringAttrValue(DOMString),
+ TokenListAttrValue(DOMString, Vec<Atom>),
+ UIntAttrValue(DOMString, u32),
+ AtomAttrValue(Atom),
+}
+
+impl AttrValue {
+ pub fn from_tokenlist(tokens: DOMString) -> AttrValue {
+ let atoms = split_html_space_chars(tokens.as_slice())
+ .map(|token| Atom::from_slice(token)).collect();
+ TokenListAttrValue(tokens, atoms)
+ }
+
+ pub fn from_u32(string: DOMString, default: u32) -> AttrValue {
+ let result: u32 = from_str(string.as_slice()).unwrap_or(default);
+ UIntAttrValue(string, result)
+ }
+
+ pub fn from_atomic(string: DOMString) -> AttrValue {
+ let value = Atom::from_slice(string.as_slice());
+ AtomAttrValue(value)
+ }
+
+ pub fn tokens<'a>(&'a self) -> Option<Items<'a, Atom>> {
+ match *self {
+ TokenListAttrValue(_, ref tokens) => Some(tokens.iter()),
+ _ => None
+ }
+ }
+}
+
+impl Str for AttrValue {
+ fn as_slice<'a>(&'a self) -> &'a str {
+ match *self {
+ StringAttrValue(ref value) |
+ TokenListAttrValue(ref value, _) |
+ UIntAttrValue(ref value, _) => value.as_slice(),
+ AtomAttrValue(ref value) => value.as_slice(),
+ }
+ }
+}
+
+#[deriving(Encodable)]
+pub struct Attr {
+ reflector_: Reflector,
+ local_name: Atom,
+ value: Traceable<RefCell<AttrValue>>,
+ pub name: Atom,
+ pub namespace: Namespace,
+ pub prefix: Option<DOMString>,
+
+ /// the element that owns this attribute.
+ owner: JS<Element>,
+}
+
+impl Reflectable for Attr {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ &self.reflector_
+ }
+}
+
+impl Attr {
+ fn new_inherited(local_name: Atom, value: AttrValue,
+ name: Atom, namespace: Namespace,
+ prefix: Option<DOMString>, owner: &JSRef<Element>) -> Attr {
+ Attr {
+ reflector_: Reflector::new(),
+ local_name: local_name,
+ value: Traceable::new(RefCell::new(value)),
+ name: name,
+ namespace: namespace,
+ prefix: prefix,
+ owner: JS::from_rooted(owner),
+ }
+ }
+
+ pub fn new(window: &JSRef<Window>, local_name: Atom, value: AttrValue,
+ name: Atom, namespace: Namespace,
+ prefix: Option<DOMString>, owner: &JSRef<Element>) -> Temporary<Attr> {
+ let attr = Attr::new_inherited(local_name, value, name, namespace, prefix, owner);
+ reflect_dom_object(box attr, &Window(*window), AttrBinding::Wrap)
+ }
+
+ pub fn set_value(&self, set_type: AttrSettingType, value: AttrValue) {
+ let owner = self.owner.root();
+ let node: &JSRef<Node> = NodeCast::from_ref(&*owner);
+ let namespace_is_null = self.namespace == namespace::Null;
+
+ match set_type {
+ ReplacedAttr => {
+ if namespace_is_null {
+ vtable_for(node).before_remove_attr(
+ self.local_name(),
+ self.value().as_slice().to_string())
+ }
+ }
+ FirstSetAttr => {}
+ }
+
+ *self.value.deref().borrow_mut() = value;
+
+ if namespace_is_null {
+ vtable_for(node).after_set_attr(
+ self.local_name(),
+ self.value().as_slice().to_string())
+ }
+ }
+
+ pub fn value<'a>(&'a self) -> Ref<'a, AttrValue> {
+ self.value.deref().borrow()
+ }
+
+ pub fn local_name<'a>(&'a self) -> &'a Atom {
+ &self.local_name
+ }
+}
+
+impl<'a> AttrMethods for JSRef<'a, Attr> {
+ fn LocalName(&self) -> DOMString {
+ self.local_name().as_slice().to_string()
+ }
+
+ fn Value(&self) -> DOMString {
+ self.value().as_slice().to_string()
+ }
+
+ fn SetValue(&self, value: DOMString) {
+ let owner = self.owner.root();
+ let value = owner.deref().parse_attribute(
+ &self.namespace, self.local_name(), value);
+ self.set_value(ReplacedAttr, value);
+ }
+
+ fn Name(&self) -> DOMString {
+ self.name.as_slice().to_string()
+ }
+
+ fn GetNamespaceURI(&self) -> Option<DOMString> {
+ match self.namespace.to_str() {
+ "" => None,
+ url => Some(url.to_string()),
+ }
+ }
+
+ fn GetPrefix(&self) -> Option<DOMString> {
+ self.prefix.clone()
+ }
+}
+
+pub trait AttrHelpersForLayout {
+ unsafe fn value_ref_forever(&self) -> &'static str;
+ unsafe fn value_atom_forever(&self) -> Option<Atom>;
+}
+
+impl AttrHelpersForLayout for Attr {
+ unsafe fn value_ref_forever(&self) -> &'static str {
+ // cast to point to T in RefCell<T> directly
+ let value = mem::transmute::<&RefCell<AttrValue>, &AttrValue>(self.value.deref());
+ value.as_slice()
+ }
+
+ unsafe fn value_atom_forever(&self) -> Option<Atom> {
+ // cast to point to T in RefCell<T> directly
+ let value = mem::transmute::<&RefCell<AttrValue>, &AttrValue>(self.value.deref());
+ match *value {
+ AtomAttrValue(ref val) => Some(val.clone()),
+ _ => None,
+ }
+ }
+}
diff --git a/components/script/dom/bindings/DESIGN.md b/components/script/dom/bindings/DESIGN.md
new file mode 100644
index 00000000000..0b8f6b01dd4
--- /dev/null
+++ b/components/script/dom/bindings/DESIGN.md
@@ -0,0 +1,38 @@
+# The design of Garbage collected DOM
+
+These are how Servo provides an object graph to SpiderMonkey's Garbage Collection.
+
+## Construct
+When Servo creates a Rusty DOM object, the binding code creates a wrapper `JSObject` with SpiderMonkey, is correspond to each Rusty DOM Object. It’s produced and set to the Rusty object in `FooBinding::Wrap`.
+
+In `FooBinding::Wrap`, the wrapper JSObject gets the pointer for Rusty Object to itself. And the same time, the wrapper `JSObject` are set to the Rusty Object’s `Reflector` field (All Rusty DOM objects have `dom::bindings::utils::Reflector` in their most basis field). These step are the “binding” work to create the relationship of both objects.
+
+
+## Trace object graph from SpiderMonkey GC.
+This is very tricky and magically mechanism helped by Rust Compiler.
+The outline is:
+
+1. SpiderMonkey's GC calls `JSClass.trace` defined in `FooBinding` when marking phase. This JSClass is basis of each wrapper JSObject.
+2. `JSClass.trace` calls `Foo::trace()` defined in InhertTypes.rs.
+3. `Foo::trace()` calls `Foo::encode()`. This `encode()` method is derived by the annotation of `#[deriving(Encodable)]` for a Rust DOM Element struct.
+4. `Foo::encode()` calls `JS<T>::encode()` method of `JS<T>` which is contained to `Foo`’s member. So this is the compiler magic! Rust compiler generates [codes like this](https://github.com/mozilla/rust/blob/db5206c32a879d5058d6a5cdce39c13c763fbdd5/src/libsyntax/ext/deriving/encodable.rs) for all structs annotated `#[deriving(Encodable)]`. This is based on [the assumption](https://github.com/mozilla/servo/blob/54da52fa774ce2ee59fcf811af595bf292169ad8/src/components/script/dom/bindings/trace.rs#L16).
+5. `JS<T>::encode()` calls `dom::bindings::trace::trace_reflector()`.
+6. `trace_reflector()` fetches the reflector that is reachable from a Rust object, and notifies it to the GC with using JSTracer.
+7. This operation continues to the end of the graph.
+8. Finally, GC gets whether Rust object lives or not from JSObjects which is hold by Rust object.
+
+
+## Destruct
+When destructing DOM objects (wrapper JSObjects) by SpiderMonkey, SpiderMonkey calls the `JSClass.finalize()` which is basis of each wrapper `JSObject`s. This method refers each `FooBinding::_finalize()`.
+
+In this function, the pointer of Rusty DOM Object that is contained in the wrapper JSObject is unwrapped, it cast to Rust owned pointer, and we assign its owned pointer to the empty local variable of `FooBinding::_finalize()`. Thus we can destruct the Rusty Object after we left from it.
+
+
+## Interact with Exact GC’s rooting
+For supporting SpiderMonkey’s exact GC rooting, we introduce [some types](https://github.com/mozilla/servo/wiki/Using-DOM-types):
+
+- `JS<T>` is used for the DOM typed field in a DOM type structure. GC can trace them recursively while enclosing DOM object (maybe root) is alive.
+- `Temporary<T>` is used as a return value of functions returning DOM type. They are rooted while they are alive. But a retun value gets moved around. It’s breakable for the LIFO ordering constraint. Thus we need introduce `Root<T>`.
+- `Root<T>` contains the pointer to `JSObject` which the represented DOM type has. SpiderMonkey's conservative stack scanner scans its pointer and mark a pointed `JSObject` as GC root.
+- `JSRef` is just a reference to the value rooted by `Root<T>`.
+- `RootCollection` is used for dynamic checking about rooting satisfies LIFO ordering, because SpiderMonkey GC requres LIFO order (See also: [Exact Stack Rooting - Storing a GCPointer on the CStack](https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/Internals/GC/Exact_Stack_Rooting)).
diff --git a/components/script/dom/bindings/callback.rs b/components/script/dom/bindings/callback.rs
new file mode 100644
index 00000000000..266abc3ab10
--- /dev/null
+++ b/components/script/dom/bindings/callback.rs
@@ -0,0 +1,156 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! Base classes to work with IDL callbacks.
+
+use dom::bindings::js::JSRef;
+use dom::bindings::trace::Traceable;
+use dom::bindings::utils::{Reflectable, global_object_for_js_object};
+use js::jsapi::{JSContext, JSObject, JS_WrapObject, JS_ObjectIsCallable};
+use js::jsapi::JS_GetProperty;
+use js::jsval::{JSVal, UndefinedValue};
+
+use std::ptr;
+
+use serialize::{Encodable, Encoder};
+
+/// The exception handling used for a call.
+pub enum ExceptionHandling {
+ /// Report any exception and don't throw it to the caller code.
+ ReportExceptions,
+ /// Throw an exception to the caller code if the thrown exception is a
+ /// binding object for a DOMError from the caller's scope, otherwise report
+ /// it.
+ RethrowContentExceptions,
+ /// Throw any exception to the caller code.
+ RethrowExceptions
+}
+
+/// A common base class for representing IDL callback function types.
+#[deriving(Clone,PartialEq,Encodable)]
+pub struct CallbackFunction {
+ object: CallbackObject
+}
+
+impl CallbackFunction {
+ pub fn new(callback: *mut JSObject) -> CallbackFunction {
+ CallbackFunction {
+ object: CallbackObject {
+ callback: Traceable::new(callback)
+ }
+ }
+ }
+}
+
+/// A common base class for representing IDL callback interface types.
+#[deriving(Clone,PartialEq,Encodable)]
+pub struct CallbackInterface {
+ object: CallbackObject
+}
+
+/// A common base class for representing IDL callback function and
+/// callback interface types.
+#[allow(raw_pointer_deriving)]
+#[deriving(Clone,PartialEq,Encodable)]
+struct CallbackObject {
+ /// The underlying `JSObject`.
+ callback: Traceable<*mut JSObject>,
+}
+
+/// A trait to be implemented by concrete IDL callback function and
+/// callback interface types.
+pub trait CallbackContainer {
+ /// Create a new CallbackContainer object for the given `JSObject`.
+ fn new(callback: *mut JSObject) -> Self;
+ /// Returns the underlying `JSObject`.
+ fn callback(&self) -> *mut JSObject;
+}
+
+impl CallbackInterface {
+ /// Returns the underlying `JSObject`.
+ pub fn callback(&self) -> *mut JSObject {
+ *self.object.callback
+ }
+}
+
+impl CallbackFunction {
+ /// Returns the underlying `JSObject`.
+ pub fn callback(&self) -> *mut JSObject {
+ *self.object.callback
+ }
+}
+
+impl CallbackInterface {
+ /// Create a new CallbackInterface object for the given `JSObject`.
+ pub fn new(callback: *mut JSObject) -> CallbackInterface {
+ CallbackInterface {
+ object: CallbackObject {
+ callback: Traceable::new(callback)
+ }
+ }
+ }
+
+ /// Returns the property with the given `name`, if it is a callable object,
+ /// or `Err(())` otherwise. If it returns `Err(())`, a JSAPI exception is
+ /// pending.
+ pub fn GetCallableProperty(&self, cx: *mut JSContext, name: &str) -> Result<JSVal, ()> {
+ let mut callable = UndefinedValue();
+ unsafe {
+ let name = name.to_c_str();
+ if JS_GetProperty(cx, self.callback(), name.as_ptr(), &mut callable) == 0 {
+ return Err(());
+ }
+
+ if !callable.is_object() ||
+ JS_ObjectIsCallable(cx, callable.to_object()) == 0 {
+ // FIXME(#347)
+ //ThrowErrorMessage(cx, MSG_NOT_CALLABLE, description.get());
+ return Err(());
+ }
+ }
+ Ok(callable)
+ }
+}
+
+/// Wraps the reflector for `p` into the compartment of `cx`.
+pub fn WrapCallThisObject<T: Reflectable>(cx: *mut JSContext,
+ p: &JSRef<T>) -> *mut JSObject {
+ let mut obj = p.reflector().get_jsobject();
+ assert!(obj.is_not_null());
+
+ unsafe {
+ if JS_WrapObject(cx, &mut obj) == 0 {
+ return ptr::mut_null();
+ }
+ }
+
+ return obj;
+}
+
+/// A class that performs whatever setup we need to safely make a call while
+/// this class is on the stack. After `new` returns, the call is safe to make.
+pub struct CallSetup {
+ /// The `JSContext` used for the call.
+ cx: *mut JSContext,
+ /// The exception handling used for the call.
+ _handling: ExceptionHandling
+}
+
+impl CallSetup {
+ /// Performs the setup needed to make a call.
+ pub fn new<T: CallbackContainer>(callback: &T, handling: ExceptionHandling) -> CallSetup {
+ let global = global_object_for_js_object(callback.callback());
+ let global = global.root();
+ let cx = global.root_ref().get_cx();
+ CallSetup {
+ cx: cx,
+ _handling: handling
+ }
+ }
+
+ /// Returns the `JSContext` used for the call.
+ pub fn GetContext(&self) -> *mut JSContext {
+ self.cx
+ }
+}
diff --git a/components/script/dom/bindings/codegen/BindingGen.py b/components/script/dom/bindings/codegen/BindingGen.py
new file mode 100644
index 00000000000..408280dacfb
--- /dev/null
+++ b/components/script/dom/bindings/codegen/BindingGen.py
@@ -0,0 +1,52 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import sys
+sys.path.append("./parser/")
+sys.path.append("./ply/")
+import os
+import cPickle
+import WebIDL
+from Configuration import *
+from CodegenRust import CGBindingRoot, replaceFileIfChanged
+
+def generate_binding_rs(config, outputprefix, webidlfile):
+ """
+ |config| Is the configuration object.
+ |outputprefix| is a prefix to use for the header guards and filename.
+ """
+
+ filename = outputprefix + ".rs"
+ root = CGBindingRoot(config, outputprefix, webidlfile)
+ if replaceFileIfChanged(filename, root.define()):
+ print "Generating binding implementation: %s" % (filename)
+
+def main():
+ # Parse arguments.
+ from optparse import OptionParser
+ usagestring = "usage: %prog configFile outputPrefix webIDLFile"
+ o = OptionParser(usage=usagestring)
+ o.add_option("--verbose-errors", action='store_true', default=False,
+ help="When an error happens, display the Python traceback.")
+ (options, args) = o.parse_args()
+
+ if len(args) != 3:
+ o.error(usagestring)
+ configFile = os.path.normpath(args[0])
+ outputPrefix = args[1]
+ webIDLFile = os.path.normpath(args[2])
+
+ # Load the parsing results
+ f = open('ParserResults.pkl', 'rb')
+ parserData = cPickle.load(f)
+ f.close()
+
+ # Create the configuration data.
+ config = Configuration(configFile, parserData)
+
+ # Generate the prototype classes.
+ generate_binding_rs(config, outputPrefix, webIDLFile);
+
+if __name__ == '__main__':
+ main()
diff --git a/components/script/dom/bindings/codegen/BindingUtils.cpp b/components/script/dom/bindings/codegen/BindingUtils.cpp
new file mode 100644
index 00000000000..27ac92e3596
--- /dev/null
+++ b/components/script/dom/bindings/codegen/BindingUtils.cpp
@@ -0,0 +1,633 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
+/* vim: set ts=2 sw=2 et tw=79: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stdarg.h>
+
+#include "BindingUtils.h"
+
+#include "WrapperFactory.h"
+#include "xpcprivate.h"
+#include "XPCQuickStubs.h"
+
+namespace mozilla {
+namespace dom {
+
+JSErrorFormatString ErrorFormatString[] = {
+#define MSG_DEF(_name, _argc, _str) \
+ { _str, _argc, JSEXN_TYPEERR },
+#include "mozilla/dom/Errors.msg"
+#undef MSG_DEF
+};
+
+const JSErrorFormatString*
+GetErrorMessage(void* aUserRef, const char* aLocale,
+ const unsigned aErrorNumber)
+{
+ MOZ_ASSERT(aErrorNumber < ArrayLength(ErrorFormatString));
+ return &ErrorFormatString[aErrorNumber];
+}
+
+bool
+ThrowErrorMessage(JSContext* aCx, const ErrNum aErrorNumber, ...)
+{
+ va_list ap;
+ va_start(ap, aErrorNumber);
+ JS_ReportErrorNumberVA(aCx, GetErrorMessage, NULL,
+ static_cast<const unsigned>(aErrorNumber), ap);
+ va_end(ap);
+ return false;
+}
+
+bool
+DefineConstants(JSContext* cx, JSObject* obj, ConstantSpec* cs)
+{
+ for (; cs->name; ++cs) {
+ JSBool ok =
+ JS_DefineProperty(cx, obj, cs->name, cs->value, NULL, NULL,
+ JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
+ if (!ok) {
+ return false;
+ }
+ }
+ return true;
+}
+
+static inline bool
+Define(JSContext* cx, JSObject* obj, JSFunctionSpec* spec) {
+ return JS_DefineFunctions(cx, obj, spec);
+}
+static inline bool
+Define(JSContext* cx, JSObject* obj, JSPropertySpec* spec) {
+ return JS_DefineProperties(cx, obj, spec);
+}
+static inline bool
+Define(JSContext* cx, JSObject* obj, ConstantSpec* spec) {
+ return DefineConstants(cx, obj, spec);
+}
+
+template<typename T>
+bool
+DefinePrefable(JSContext* cx, JSObject* obj, Prefable<T>* props)
+{
+ MOZ_ASSERT(props);
+ MOZ_ASSERT(props->specs);
+ do {
+ // Define if enabled
+ if (props->enabled) {
+ if (!Define(cx, obj, props->specs)) {
+ return false;
+ }
+ }
+ } while ((++props)->specs);
+ return true;
+}
+
+// We should use JSFunction objects for interface objects, but we need a custom
+// hasInstance hook because we have new interface objects on prototype chains of
+// old (XPConnect-based) bindings. Because Function.prototype.toString throws if
+// passed a non-Function object we also need to provide our own toString method
+// for interface objects.
+
+enum {
+ TOSTRING_CLASS_RESERVED_SLOT = 0,
+ TOSTRING_NAME_RESERVED_SLOT = 1
+};
+
+JSBool
+InterfaceObjectToString(JSContext* cx, unsigned argc, JS::Value *vp)
+{
+ JSObject* callee = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
+
+ JSObject* obj = JS_THIS_OBJECT(cx, vp);
+ if (!obj) {
+ JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_CONVERT_TO,
+ "null", "object");
+ return false;
+ }
+
+ jsval v = js::GetFunctionNativeReserved(callee, TOSTRING_CLASS_RESERVED_SLOT);
+ JSClass* clasp = static_cast<JSClass*>(JSVAL_TO_PRIVATE(v));
+
+ v = js::GetFunctionNativeReserved(callee, TOSTRING_NAME_RESERVED_SLOT);
+ JSString* jsname = static_cast<JSString*>(JSVAL_TO_STRING(v));
+ size_t length;
+ const jschar* name = JS_GetInternedStringCharsAndLength(jsname, &length);
+
+ if (js::GetObjectJSClass(obj) != clasp) {
+ JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO,
+ NS_ConvertUTF16toUTF8(name).get(), "toString",
+ "object");
+ return false;
+ }
+
+ nsString str;
+ str.AppendLiteral("function ");
+ str.Append(name, length);
+ str.AppendLiteral("() {");
+ str.Append('\n');
+ str.AppendLiteral(" [native code]");
+ str.Append('\n');
+ str.AppendLiteral("}");
+
+ return xpc::NonVoidStringToJsval(cx, str, vp);
+}
+
+static JSObject*
+CreateInterfaceObject(JSContext* cx, JSObject* global, JSObject* receiver,
+ JSClass* constructorClass, JSNative constructorNative,
+ unsigned ctorNargs, JSObject* proto,
+ Prefable<JSFunctionSpec>* staticMethods,
+ Prefable<ConstantSpec>* constants,
+ const char* name)
+{
+ JSObject* constructor;
+ if (constructorClass) {
+ JSObject* functionProto = JS_GetFunctionPrototype(cx, global);
+ if (!functionProto) {
+ return NULL;
+ }
+ constructor = JS_NewObject(cx, constructorClass, functionProto, global);
+ } else {
+ MOZ_ASSERT(constructorNative);
+ JSFunction* fun = JS_NewFunction(cx, constructorNative, ctorNargs,
+ JSFUN_CONSTRUCTOR, global, name);
+ if (!fun) {
+ return NULL;
+ }
+ constructor = JS_GetFunctionObject(fun);
+ }
+ if (!constructor) {
+ return NULL;
+ }
+
+ if (staticMethods && !DefinePrefable(cx, constructor, staticMethods)) {
+ return NULL;
+ }
+
+ if (constructorClass) {
+ JSFunction* toString = js::DefineFunctionWithReserved(cx, constructor,
+ "toString",
+ InterfaceObjectToString,
+ 0, 0);
+ if (!toString) {
+ return NULL;
+ }
+
+ JSObject* toStringObj = JS_GetFunctionObject(toString);
+ js::SetFunctionNativeReserved(toStringObj, TOSTRING_CLASS_RESERVED_SLOT,
+ PRIVATE_TO_JSVAL(constructorClass));
+
+ JSString *str = ::JS_InternString(cx, name);
+ if (!str) {
+ return NULL;
+ }
+ js::SetFunctionNativeReserved(toStringObj, TOSTRING_NAME_RESERVED_SLOT,
+ STRING_TO_JSVAL(str));
+ }
+
+ if (constants && !DefinePrefable(cx, constructor, constants)) {
+ return NULL;
+ }
+
+ if (proto && !JS_LinkConstructorAndPrototype(cx, constructor, proto)) {
+ return NULL;
+ }
+
+ JSBool alreadyDefined;
+ if (!JS_AlreadyHasOwnProperty(cx, receiver, name, &alreadyDefined)) {
+ return NULL;
+ }
+
+ // This is Enumerable: False per spec.
+ if (!alreadyDefined &&
+ !JS_DefineProperty(cx, receiver, name, OBJECT_TO_JSVAL(constructor), NULL,
+ NULL, 0)) {
+ return NULL;
+ }
+
+ return constructor;
+}
+
+static JSObject*
+CreateInterfacePrototypeObject(JSContext* cx, JSObject* global,
+ JSObject* parentProto, JSClass* protoClass,
+ Prefable<JSFunctionSpec>* methods,
+ Prefable<JSPropertySpec>* properties,
+ Prefable<ConstantSpec>* constants)
+{
+ JSObject* ourProto = JS_NewObjectWithUniqueType(cx, protoClass, parentProto,
+ global);
+ if (!ourProto) {
+ return NULL;
+ }
+
+ if (methods && !DefinePrefable(cx, ourProto, methods)) {
+ return NULL;
+ }
+
+ if (properties && !DefinePrefable(cx, ourProto, properties)) {
+ return NULL;
+ }
+
+ if (constants && !DefinePrefable(cx, ourProto, constants)) {
+ return NULL;
+ }
+
+ return ourProto;
+}
+
+JSObject*
+CreateInterfaceObjects(JSContext* cx, JSObject* global, JSObject *receiver,
+ JSObject* protoProto, JSClass* protoClass,
+ JSClass* constructorClass, JSNative constructor,
+ unsigned ctorNargs, const DOMClass* domClass,
+ Prefable<JSFunctionSpec>* methods,
+ Prefable<JSPropertySpec>* properties,
+ Prefable<ConstantSpec>* constants,
+ Prefable<JSFunctionSpec>* staticMethods, const char* name)
+{
+ MOZ_ASSERT(protoClass || constructorClass || constructor,
+ "Need at least one class or a constructor!");
+ MOZ_ASSERT(!(methods || properties) || protoClass,
+ "Methods or properties but no protoClass!");
+ MOZ_ASSERT(!staticMethods || constructorClass || constructor,
+ "Static methods but no constructorClass or constructor!");
+ MOZ_ASSERT(bool(name) == bool(constructorClass || constructor),
+ "Must have name precisely when we have an interface object");
+ MOZ_ASSERT(!constructorClass || !constructor);
+
+ JSObject* proto;
+ if (protoClass) {
+ proto = CreateInterfacePrototypeObject(cx, global, protoProto, protoClass,
+ methods, properties, constants);
+ if (!proto) {
+ return NULL;
+ }
+
+ js::SetReservedSlot(proto, DOM_PROTO_INSTANCE_CLASS_SLOT,
+ JS::PrivateValue(const_cast<DOMClass*>(domClass)));
+ }
+ else {
+ proto = NULL;
+ }
+
+ JSObject* interface;
+ if (constructorClass || constructor) {
+ interface = CreateInterfaceObject(cx, global, receiver, constructorClass,
+ constructor, ctorNargs, proto,
+ staticMethods, constants, name);
+ if (!interface) {
+ return NULL;
+ }
+ }
+
+ return protoClass ? proto : interface;
+}
+
+static bool
+NativeInterface2JSObjectAndThrowIfFailed(XPCLazyCallContext& aLccx,
+ JSContext* aCx,
+ JS::Value* aRetval,
+ xpcObjectHelper& aHelper,
+ const nsIID* aIID,
+ bool aAllowNativeWrapper)
+{
+ nsresult rv;
+ if (!XPCConvert::NativeInterface2JSObject(aLccx, aRetval, NULL, aHelper, aIID,
+ NULL, aAllowNativeWrapper, &rv)) {
+ // I can't tell if NativeInterface2JSObject throws JS exceptions
+ // or not. This is a sloppy stab at the right semantics; the
+ // method really ought to be fixed to behave consistently.
+ if (!JS_IsExceptionPending(aCx)) {
+ Throw<true>(aCx, NS_FAILED(rv) ? rv : NS_ERROR_UNEXPECTED);
+ }
+ return false;
+ }
+ return true;
+}
+
+bool
+DoHandleNewBindingWrappingFailure(JSContext* cx, JSObject* scope,
+ nsISupports* value, JS::Value* vp)
+{
+ if (JS_IsExceptionPending(cx)) {
+ return false;
+ }
+
+ XPCLazyCallContext lccx(JS_CALLER, cx, scope);
+
+ if (value) {
+ xpcObjectHelper helper(value);
+ return NativeInterface2JSObjectAndThrowIfFailed(lccx, cx, vp, helper, NULL,
+ true);
+ }
+
+ return Throw<true>(cx, NS_ERROR_XPC_BAD_CONVERT_JS);
+}
+
+// Can only be called with the immediate prototype of the instance object. Can
+// only be called on the prototype of an object known to be a DOM instance.
+JSBool
+InstanceClassHasProtoAtDepth(JSHandleObject protoObject, uint32_t protoID,
+ uint32_t depth)
+{
+ const DOMClass* domClass = static_cast<DOMClass*>(
+ js::GetReservedSlot(protoObject, DOM_PROTO_INSTANCE_CLASS_SLOT).toPrivate());
+ return (uint32_t)domClass->mInterfaceChain[depth] == protoID;
+}
+
+// Only set allowNativeWrapper to false if you really know you need it, if in
+// doubt use true. Setting it to false disables security wrappers.
+bool
+XPCOMObjectToJsval(JSContext* cx, JSObject* scope, xpcObjectHelper &helper,
+ const nsIID* iid, bool allowNativeWrapper, JS::Value* rval)
+{
+ XPCLazyCallContext lccx(JS_CALLER, cx, scope);
+
+ if (!NativeInterface2JSObjectAndThrowIfFailed(lccx, cx, rval, helper, iid,
+ allowNativeWrapper)) {
+ return false;
+ }
+
+#ifdef DEBUG
+ JSObject* jsobj = JSVAL_TO_OBJECT(*rval);
+ if (jsobj && !js::GetObjectParent(jsobj))
+ NS_ASSERTION(js::GetObjectClass(jsobj)->flags & JSCLASS_IS_GLOBAL,
+ "Why did we recreate this wrapper?");
+#endif
+
+ return true;
+}
+
+JSBool
+QueryInterface(JSContext* cx, unsigned argc, JS::Value* vp)
+{
+ JS::Value thisv = JS_THIS(cx, vp);
+ if (thisv == JSVAL_NULL)
+ return false;
+
+ // Get the object. It might be a security wrapper, in which case we do a checked
+ // unwrap.
+ JSObject* origObj = JSVAL_TO_OBJECT(thisv);
+ JSObject* obj = js::UnwrapObjectChecked(cx, origObj);
+ if (!obj)
+ return false;
+
+ nsISupports* native;
+ if (!UnwrapDOMObjectToISupports(obj, native)) {
+ return Throw<true>(cx, NS_ERROR_FAILURE);
+ }
+
+ if (argc < 1) {
+ return Throw<true>(cx, NS_ERROR_XPC_NOT_ENOUGH_ARGS);
+ }
+
+ JS::Value* argv = JS_ARGV(cx, vp);
+ if (!argv[0].isObject()) {
+ return Throw<true>(cx, NS_ERROR_XPC_BAD_CONVERT_JS);
+ }
+
+ nsIJSIID* iid;
+ xpc_qsSelfRef iidRef;
+ if (NS_FAILED(xpc_qsUnwrapArg<nsIJSIID>(cx, argv[0], &iid, &iidRef.ptr,
+ &argv[0]))) {
+ return Throw<true>(cx, NS_ERROR_XPC_BAD_CONVERT_JS);
+ }
+ MOZ_ASSERT(iid);
+
+ if (iid->GetID()->Equals(NS_GET_IID(nsIClassInfo))) {
+ nsresult rv;
+ nsCOMPtr<nsIClassInfo> ci = do_QueryInterface(native, &rv);
+ if (NS_FAILED(rv)) {
+ return Throw<true>(cx, rv);
+ }
+
+ return WrapObject(cx, origObj, ci, &NS_GET_IID(nsIClassInfo), vp);
+ }
+
+ // Lie, otherwise we need to check classinfo or QI
+ *vp = thisv;
+ return true;
+}
+
+JSBool
+ThrowingConstructor(JSContext* cx, unsigned argc, JS::Value* vp)
+{
+ return ThrowErrorMessage(cx, MSG_ILLEGAL_CONSTRUCTOR);
+}
+
+bool
+XrayResolveProperty(JSContext* cx, JSObject* wrapper, jsid id,
+ JSPropertyDescriptor* desc,
+ // And the things we need to determine the descriptor
+ Prefable<JSFunctionSpec>* methods,
+ jsid* methodIds,
+ JSFunctionSpec* methodSpecs,
+ size_t methodCount,
+ Prefable<JSPropertySpec>* attributes,
+ jsid* attributeIds,
+ JSPropertySpec* attributeSpecs,
+ size_t attributeCount,
+ Prefable<ConstantSpec>* constants,
+ jsid* constantIds,
+ ConstantSpec* constantSpecs,
+ size_t constantCount)
+{
+ for (size_t prefIdx = 0; prefIdx < methodCount; ++prefIdx) {
+ MOZ_ASSERT(methods[prefIdx].specs);
+ if (methods[prefIdx].enabled) {
+ // Set i to be the index into our full list of ids/specs that we're
+ // looking at now.
+ size_t i = methods[prefIdx].specs - methodSpecs;
+ for ( ; methodIds[i] != JSID_VOID; ++i) {
+ if (id == methodIds[i]) {
+ JSFunction *fun = JS_NewFunctionById(cx, methodSpecs[i].call.op,
+ methodSpecs[i].nargs, 0,
+ wrapper, id);
+ if (!fun) {
+ return false;
+ }
+ SET_JITINFO(fun, methodSpecs[i].call.info);
+ JSObject *funobj = JS_GetFunctionObject(fun);
+ desc->value.setObject(*funobj);
+ desc->attrs = methodSpecs[i].flags;
+ desc->obj = wrapper;
+ desc->setter = nullptr;
+ desc->getter = nullptr;
+ return true;
+ }
+ }
+ }
+ }
+
+ for (size_t prefIdx = 0; prefIdx < attributeCount; ++prefIdx) {
+ MOZ_ASSERT(attributes[prefIdx].specs);
+ if (attributes[prefIdx].enabled) {
+ // Set i to be the index into our full list of ids/specs that we're
+ // looking at now.
+ size_t i = attributes[prefIdx].specs - attributeSpecs;
+ for ( ; attributeIds[i] != JSID_VOID; ++i) {
+ if (id == attributeIds[i]) {
+ // Because of centralization, we need to make sure we fault in the
+ // JitInfos as well. At present, until the JSAPI changes, the easiest
+ // way to do this is wrap them up as functions ourselves.
+ desc->attrs = attributeSpecs[i].flags & ~JSPROP_NATIVE_ACCESSORS;
+ // They all have getters, so we can just make it.
+ JSObject *global = JS_GetGlobalForObject(cx, wrapper);
+ JSFunction *fun = JS_NewFunction(cx, (JSNative)attributeSpecs[i].getter.op,
+ 0, 0, global, NULL);
+ if (!fun)
+ return false;
+ SET_JITINFO(fun, attributeSpecs[i].getter.info);
+ JSObject *funobj = JS_GetFunctionObject(fun);
+ desc->getter = js::CastAsJSPropertyOp(funobj);
+ desc->attrs |= JSPROP_GETTER;
+ if (attributeSpecs[i].setter.op) {
+ // We have a setter! Make it.
+ fun = JS_NewFunction(cx, (JSNative)attributeSpecs[i].setter.op,
+ 1, 0, global, NULL);
+ if (!fun)
+ return false;
+ SET_JITINFO(fun, attributeSpecs[i].setter.info);
+ funobj = JS_GetFunctionObject(fun);
+ desc->setter = js::CastAsJSStrictPropertyOp(funobj);
+ desc->attrs |= JSPROP_SETTER;
+ } else {
+ desc->setter = NULL;
+ }
+ desc->obj = wrapper;
+ return true;
+ }
+ }
+ }
+ }
+
+ for (size_t prefIdx = 0; prefIdx < constantCount; ++prefIdx) {
+ MOZ_ASSERT(constants[prefIdx].specs);
+ if (constants[prefIdx].enabled) {
+ // Set i to be the index into our full list of ids/specs that we're
+ // looking at now.
+ size_t i = constants[prefIdx].specs - constantSpecs;
+ for ( ; constantIds[i] != JSID_VOID; ++i) {
+ if (id == constantIds[i]) {
+ desc->attrs = JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT;
+ desc->obj = wrapper;
+ desc->value = constantSpecs[i].value;
+ return true;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+bool
+XrayEnumerateProperties(JS::AutoIdVector& props,
+ Prefable<JSFunctionSpec>* methods,
+ jsid* methodIds,
+ JSFunctionSpec* methodSpecs,
+ size_t methodCount,
+ Prefable<JSPropertySpec>* attributes,
+ jsid* attributeIds,
+ JSPropertySpec* attributeSpecs,
+ size_t attributeCount,
+ Prefable<ConstantSpec>* constants,
+ jsid* constantIds,
+ ConstantSpec* constantSpecs,
+ size_t constantCount)
+{
+ for (size_t prefIdx = 0; prefIdx < methodCount; ++prefIdx) {
+ MOZ_ASSERT(methods[prefIdx].specs);
+ if (methods[prefIdx].enabled) {
+ // Set i to be the index into our full list of ids/specs that we're
+ // looking at now.
+ size_t i = methods[prefIdx].specs - methodSpecs;
+ for ( ; methodIds[i] != JSID_VOID; ++i) {
+ if ((methodSpecs[i].flags & JSPROP_ENUMERATE) &&
+ !props.append(methodIds[i])) {
+ return false;
+ }
+ }
+ }
+ }
+
+ for (size_t prefIdx = 0; prefIdx < attributeCount; ++prefIdx) {
+ MOZ_ASSERT(attributes[prefIdx].specs);
+ if (attributes[prefIdx].enabled) {
+ // Set i to be the index into our full list of ids/specs that we're
+ // looking at now.
+ size_t i = attributes[prefIdx].specs - attributeSpecs;
+ for ( ; attributeIds[i] != JSID_VOID; ++i) {
+ if ((attributeSpecs[i].flags & JSPROP_ENUMERATE) &&
+ !props.append(attributeIds[i])) {
+ return false;
+ }
+ }
+ }
+ }
+
+ for (size_t prefIdx = 0; prefIdx < constantCount; ++prefIdx) {
+ MOZ_ASSERT(constants[prefIdx].specs);
+ if (constants[prefIdx].enabled) {
+ // Set i to be the index into our full list of ids/specs that we're
+ // looking at now.
+ size_t i = constants[prefIdx].specs - constantSpecs;
+ for ( ; constantIds[i] != JSID_VOID; ++i) {
+ if (!props.append(constantIds[i])) {
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+bool
+GetPropertyOnPrototype(JSContext* cx, JSObject* proxy, jsid id, bool* found,
+ JS::Value* vp)
+{
+ JSObject* proto;
+ if (!js::GetObjectProto(cx, proxy, &proto)) {
+ return false;
+ }
+ if (!proto) {
+ *found = false;
+ return true;
+ }
+
+ JSBool hasProp;
+ if (!JS_HasPropertyById(cx, proto, id, &hasProp)) {
+ return false;
+ }
+
+ *found = hasProp;
+ if (!hasProp || !vp) {
+ return true;
+ }
+
+ return JS_ForwardGetPropertyTo(cx, proto, id, proxy, vp);
+}
+
+bool
+HasPropertyOnPrototype(JSContext* cx, JSObject* proxy, DOMProxyHandler* handler,
+ jsid id)
+{
+ Maybe<JSAutoCompartment> ac;
+ if (xpc::WrapperFactory::IsXrayWrapper(proxy)) {
+ proxy = js::UnwrapObject(proxy);
+ ac.construct(cx, proxy);
+ }
+ MOZ_ASSERT(js::IsProxy(proxy) && js::GetProxyHandler(proxy) == handler);
+
+ bool found;
+ // We ignore an error from GetPropertyOnPrototype.
+ return !GetPropertyOnPrototype(cx, proxy, id, &found, NULL) || found;
+}
+
+} // namespace dom
+} // namespace mozilla
diff --git a/components/script/dom/bindings/codegen/BindingUtils.h b/components/script/dom/bindings/codegen/BindingUtils.h
new file mode 100644
index 00000000000..ee9d6c3691c
--- /dev/null
+++ b/components/script/dom/bindings/codegen/BindingUtils.h
@@ -0,0 +1,1151 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
+/* vim: set ts=2 sw=2 et tw=79: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_BindingUtils_h__
+#define mozilla_dom_BindingUtils_h__
+
+#include "mozilla/dom/DOMJSClass.h"
+#include "mozilla/dom/DOMJSProxyHandler.h"
+#include "mozilla/dom/workers/Workers.h"
+#include "mozilla/ErrorResult.h"
+
+#include "jsapi.h"
+#include "jsfriendapi.h"
+#include "jswrapper.h"
+
+#include "nsIXPConnect.h"
+#include "qsObjectHelper.h"
+#include "xpcpublic.h"
+#include "nsTraceRefcnt.h"
+#include "nsWrapperCacheInlines.h"
+#include "mozilla/Likely.h"
+
+// nsGlobalWindow implements nsWrapperCache, but doesn't always use it. Don't
+// try to use it without fixing that first.
+class nsGlobalWindow;
+
+namespace mozilla {
+namespace dom {
+
+enum ErrNum {
+#define MSG_DEF(_name, _argc, _str) \
+ _name,
+#include "mozilla/dom/Errors.msg"
+#undef MSG_DEF
+ Err_Limit
+};
+
+bool
+ThrowErrorMessage(JSContext* aCx, const ErrNum aErrorNumber, ...);
+
+template<bool mainThread>
+inline bool
+Throw(JSContext* cx, nsresult rv)
+{
+ using mozilla::dom::workers::exceptions::ThrowDOMExceptionForNSResult;
+
+ // XXX Introduce exception machinery.
+ if (mainThread) {
+ xpc::Throw(cx, rv);
+ } else {
+ if (!JS_IsExceptionPending(cx)) {
+ ThrowDOMExceptionForNSResult(cx, rv);
+ }
+ }
+ return false;
+}
+
+template<bool mainThread>
+inline bool
+ThrowMethodFailedWithDetails(JSContext* cx, const ErrorResult& rv,
+ const char* /* ifaceName */,
+ const char* /* memberName */)
+{
+ return Throw<mainThread>(cx, rv.ErrorCode());
+}
+
+inline bool
+IsDOMClass(const JSClass* clasp)
+{
+ return clasp->flags & JSCLASS_IS_DOMJSCLASS;
+}
+
+inline bool
+IsDOMClass(const js::Class* clasp)
+{
+ return IsDOMClass(Jsvalify(clasp));
+}
+
+// It's ok for eRegularDOMObject and eProxyDOMObject to be the same, but
+// eNonDOMObject should always be different from the other two. This enum
+// shouldn't be used to differentiate between non-proxy and proxy bindings.
+enum DOMObjectSlot {
+ eNonDOMObject = -1,
+ eRegularDOMObject = DOM_OBJECT_SLOT,
+ eProxyDOMObject = DOM_PROXY_OBJECT_SLOT
+};
+
+template <class T>
+inline T*
+UnwrapDOMObject(JSObject* obj, DOMObjectSlot slot)
+{
+ MOZ_ASSERT(slot != eNonDOMObject,
+ "Don't pass non-DOM objects to this function");
+
+#ifdef DEBUG
+ if (IsDOMClass(js::GetObjectClass(obj))) {
+ MOZ_ASSERT(slot == eRegularDOMObject);
+ } else {
+ MOZ_ASSERT(js::IsObjectProxyClass(js::GetObjectClass(obj)) ||
+ js::IsFunctionProxyClass(js::GetObjectClass(obj)));
+ MOZ_ASSERT(js::GetProxyHandler(obj)->family() == ProxyFamily());
+ MOZ_ASSERT(IsNewProxyBinding(js::GetProxyHandler(obj)));
+ MOZ_ASSERT(slot == eProxyDOMObject);
+ }
+#endif
+
+ JS::Value val = js::GetReservedSlot(obj, slot);
+ // XXXbz/khuey worker code tries to unwrap interface objects (which have
+ // nothing here). That needs to stop.
+ // XXX We don't null-check UnwrapObject's result; aren't we going to crash
+ // anyway?
+ if (val.isUndefined()) {
+ return NULL;
+ }
+
+ return static_cast<T*>(val.toPrivate());
+}
+
+// Only use this with a new DOM binding object (either proxy or regular).
+inline const DOMClass*
+GetDOMClass(JSObject* obj)
+{
+ js::Class* clasp = js::GetObjectClass(obj);
+ if (IsDOMClass(clasp)) {
+ return &DOMJSClass::FromJSClass(clasp)->mClass;
+ }
+
+ js::BaseProxyHandler* handler = js::GetProxyHandler(obj);
+ MOZ_ASSERT(handler->family() == ProxyFamily());
+ MOZ_ASSERT(IsNewProxyBinding(handler));
+ return &static_cast<DOMProxyHandler*>(handler)->mClass;
+}
+
+inline DOMObjectSlot
+GetDOMClass(JSObject* obj, const DOMClass*& result)
+{
+ js::Class* clasp = js::GetObjectClass(obj);
+ if (IsDOMClass(clasp)) {
+ result = &DOMJSClass::FromJSClass(clasp)->mClass;
+ return eRegularDOMObject;
+ }
+
+ if (js::IsObjectProxyClass(clasp) || js::IsFunctionProxyClass(clasp)) {
+ js::BaseProxyHandler* handler = js::GetProxyHandler(obj);
+ if (handler->family() == ProxyFamily() && IsNewProxyBinding(handler)) {
+ result = &static_cast<DOMProxyHandler*>(handler)->mClass;
+ return eProxyDOMObject;
+ }
+ }
+
+ return eNonDOMObject;
+}
+
+inline bool
+UnwrapDOMObjectToISupports(JSObject* obj, nsISupports*& result)
+{
+ const DOMClass* clasp;
+ DOMObjectSlot slot = GetDOMClass(obj, clasp);
+ if (slot == eNonDOMObject || !clasp->mDOMObjectIsISupports) {
+ return false;
+ }
+
+ result = UnwrapDOMObject<nsISupports>(obj, slot);
+ return true;
+}
+
+inline bool
+IsDOMObject(JSObject* obj)
+{
+ js::Class* clasp = js::GetObjectClass(obj);
+ return IsDOMClass(clasp) ||
+ ((js::IsObjectProxyClass(clasp) || js::IsFunctionProxyClass(clasp)) &&
+ (js::GetProxyHandler(obj)->family() == ProxyFamily() &&
+ IsNewProxyBinding(js::GetProxyHandler(obj))));
+}
+
+// Some callers don't want to set an exception when unwrapping fails
+// (for example, overload resolution uses unwrapping to tell what sort
+// of thing it's looking at).
+// U must be something that a T* can be assigned to (e.g. T* or an nsRefPtr<T>).
+template <prototypes::ID PrototypeID, class T, typename U>
+inline nsresult
+UnwrapObject(JSContext* cx, JSObject* obj, U& value)
+{
+ /* First check to see whether we have a DOM object */
+ const DOMClass* domClass;
+ DOMObjectSlot slot = GetDOMClass(obj, domClass);
+ if (slot == eNonDOMObject) {
+ /* Maybe we have a security wrapper or outer window? */
+ if (!js::IsWrapper(obj)) {
+ /* Not a DOM object, not a wrapper, just bail */
+ return NS_ERROR_XPC_BAD_CONVERT_JS;
+ }
+
+ obj = xpc::Unwrap(cx, obj, false);
+ if (!obj) {
+ return NS_ERROR_XPC_SECURITY_MANAGER_VETO;
+ }
+ MOZ_ASSERT(!js::IsWrapper(obj));
+ slot = GetDOMClass(obj, domClass);
+ if (slot == eNonDOMObject) {
+ /* We don't have a DOM object */
+ return NS_ERROR_XPC_BAD_CONVERT_JS;
+ }
+ }
+
+ /* This object is a DOM object. Double-check that it is safely
+ castable to T by checking whether it claims to inherit from the
+ class identified by protoID. */
+ if (domClass->mInterfaceChain[PrototypeTraits<PrototypeID>::Depth] ==
+ PrototypeID) {
+ value = UnwrapDOMObject<T>(obj, slot);
+ return NS_OK;
+ }
+
+ /* It's the wrong sort of DOM object */
+ return NS_ERROR_XPC_BAD_CONVERT_JS;
+}
+
+inline bool
+IsArrayLike(JSContext* cx, JSObject* obj)
+{
+ MOZ_ASSERT(obj);
+ // For simplicity, check for security wrappers up front. In case we
+ // have a security wrapper, don't forget to enter the compartment of
+ // the underlying object after unwrapping.
+ Maybe<JSAutoCompartment> ac;
+ if (js::IsWrapper(obj)) {
+ obj = xpc::Unwrap(cx, obj, false);
+ if (!obj) {
+ // Let's say it's not
+ return false;
+ }
+
+ ac.construct(cx, obj);
+ }
+
+ // XXXbz need to detect platform objects (including listbinding
+ // ones) with indexGetters here!
+ return JS_IsArrayObject(cx, obj) || JS_IsTypedArrayObject(obj, cx);
+}
+
+inline bool
+IsPlatformObject(JSContext* cx, JSObject* obj)
+{
+ // XXXbz Should be treating list-binding objects as platform objects
+ // too? The one consumer so far wants non-array-like platform
+ // objects, so listbindings that have an indexGetter should test
+ // false from here. Maybe this function should have a different
+ // name?
+ MOZ_ASSERT(obj);
+ // Fast-path the common case
+ JSClass* clasp = js::GetObjectJSClass(obj);
+ if (IsDOMClass(clasp)) {
+ return true;
+ }
+ // Now for simplicity check for security wrappers before anything else
+ if (js::IsWrapper(obj)) {
+ obj = xpc::Unwrap(cx, obj, false);
+ if (!obj) {
+ // Let's say it's not
+ return false;
+ }
+ clasp = js::GetObjectJSClass(obj);
+ }
+ return IS_WRAPPER_CLASS(js::Valueify(clasp)) || IsDOMClass(clasp) ||
+ JS_IsArrayBufferObject(obj, cx);
+}
+
+// U must be something that a T* can be assigned to (e.g. T* or an nsRefPtr<T>).
+template <class T, typename U>
+inline nsresult
+UnwrapObject(JSContext* cx, JSObject* obj, U& value)
+{
+ return UnwrapObject<static_cast<prototypes::ID>(
+ PrototypeIDMap<T>::PrototypeID), T>(cx, obj, value);
+}
+
+const size_t kProtoOrIfaceCacheCount =
+ prototypes::id::_ID_Count + constructors::id::_ID_Count;
+
+inline void
+AllocateProtoOrIfaceCache(JSObject* obj)
+{
+ MOZ_ASSERT(js::GetObjectClass(obj)->flags & JSCLASS_DOM_GLOBAL);
+ MOZ_ASSERT(js::GetReservedSlot(obj, DOM_PROTOTYPE_SLOT).isUndefined());
+
+ // Important: The () at the end ensure zero-initialization
+ JSObject** protoOrIfaceArray = new JSObject*[kProtoOrIfaceCacheCount]();
+
+ js::SetReservedSlot(obj, DOM_PROTOTYPE_SLOT,
+ JS::PrivateValue(protoOrIfaceArray));
+}
+
+inline void
+TraceProtoOrIfaceCache(JSTracer* trc, JSObject* obj)
+{
+ MOZ_ASSERT(js::GetObjectClass(obj)->flags & JSCLASS_DOM_GLOBAL);
+
+ if (!HasProtoOrIfaceArray(obj))
+ return;
+ JSObject** protoOrIfaceArray = GetProtoOrIfaceArray(obj);
+ for (size_t i = 0; i < kProtoOrIfaceCacheCount; ++i) {
+ JSObject* proto = protoOrIfaceArray[i];
+ if (proto) {
+ JS_CALL_OBJECT_TRACER(trc, proto, "protoOrIfaceArray[i]");
+ }
+ }
+}
+
+inline void
+DestroyProtoOrIfaceCache(JSObject* obj)
+{
+ MOZ_ASSERT(js::GetObjectClass(obj)->flags & JSCLASS_DOM_GLOBAL);
+
+ JSObject** protoOrIfaceArray = GetProtoOrIfaceArray(obj);
+
+ delete [] protoOrIfaceArray;
+}
+
+struct ConstantSpec
+{
+ const char* name;
+ JS::Value value;
+};
+
+/**
+ * Add constants to an object.
+ */
+bool
+DefineConstants(JSContext* cx, JSObject* obj, ConstantSpec* cs);
+
+template<typename T>
+struct Prefable {
+ // A boolean indicating whether this set of specs is enabled
+ bool enabled;
+ // Array of specs, terminated in whatever way is customary for T.
+ // Null to indicate a end-of-array for Prefable, when such an
+ // indicator is needed.
+ T* specs;
+};
+
+/*
+ * Create a DOM interface object (if constructorClass is non-null) and/or a
+ * DOM interface prototype object (if protoClass is non-null).
+ *
+ * global is used as the parent of the interface object and the interface
+ * prototype object
+ * receiver is the object on which we need to define the interface object as a
+ * property
+ * protoProto is the prototype to use for the interface prototype object.
+ * protoClass is the JSClass to use for the interface prototype object.
+ * This is null if we should not create an interface prototype
+ * object.
+ * constructorClass is the JSClass to use for the interface object.
+ * This is null if we should not create an interface object or
+ * if it should be a function object.
+ * constructor is the JSNative to use as a constructor. If this is non-null, it
+ * should be used as a JSNative to back the interface object, which
+ * should be a Function. If this is null, then we should create an
+ * object of constructorClass, unless that's also null, in which
+ * case we should not create an interface object at all.
+ * ctorNargs is the length of the constructor function; 0 if no constructor
+ * instanceClass is the JSClass of instance objects for this class. This can
+ * be null if this is not a concrete proto.
+ * methods and properties are to be defined on the interface prototype object;
+ * these arguments are allowed to be null if there are no
+ * methods or properties respectively.
+ * constants are to be defined on the interface object and on the interface
+ * prototype object; allowed to be null if there are no constants.
+ * staticMethods are to be defined on the interface object; allowed to be null
+ * if there are no static methods.
+ *
+ * At least one of protoClass and constructorClass should be non-null.
+ * If constructorClass is non-null, the resulting interface object will be
+ * defined on the given global with property name |name|, which must also be
+ * non-null.
+ *
+ * returns the interface prototype object if protoClass is non-null, else it
+ * returns the interface object.
+ */
+JSObject*
+CreateInterfaceObjects(JSContext* cx, JSObject* global, JSObject* receiver,
+ JSObject* protoProto, JSClass* protoClass,
+ JSClass* constructorClass, JSNative constructor,
+ unsigned ctorNargs, const DOMClass* domClass,
+ Prefable<JSFunctionSpec>* methods,
+ Prefable<JSPropertySpec>* properties,
+ Prefable<ConstantSpec>* constants,
+ Prefable<JSFunctionSpec>* staticMethods, const char* name);
+
+template <class T>
+inline bool
+WrapNewBindingObject(JSContext* cx, JSObject* scope, T* value, JS::Value* vp)
+{
+ JSObject* obj = value->GetWrapper();
+ if (obj && js::GetObjectCompartment(obj) == js::GetObjectCompartment(scope)) {
+ *vp = JS::ObjectValue(*obj);
+ return true;
+ }
+
+ if (!obj) {
+ bool triedToWrap;
+ obj = value->WrapObject(cx, scope, &triedToWrap);
+ if (!obj) {
+ // At this point, obj is null, so just return false. We could
+ // try to communicate triedToWrap to the caller, but in practice
+ // callers seem to be testing JS_IsExceptionPending(cx) to
+ // figure out whether WrapObject() threw instead.
+ return false;
+ }
+ }
+
+ // When called via XrayWrapper, we end up here while running in the
+ // chrome compartment. But the obj we have would be created in
+ // whatever the content compartment is. So at this point we need to
+ // make sure it's correctly wrapped for the compartment of |scope|.
+ // cx should already be in the compartment of |scope| here.
+ MOZ_ASSERT(js::IsObjectInContextCompartment(scope, cx));
+ *vp = JS::ObjectValue(*obj);
+ return JS_WrapValue(cx, vp);
+}
+
+// Helper for smart pointers (nsAutoPtr/nsRefPtr/nsCOMPtr).
+template <template <typename> class SmartPtr, class T>
+inline bool
+WrapNewBindingObject(JSContext* cx, JSObject* scope, const SmartPtr<T>& value,
+ JS::Value* vp)
+{
+ return WrapNewBindingObject(cx, scope, value.get(), vp);
+}
+
+template <class T>
+inline bool
+WrapNewBindingNonWrapperCachedObject(JSContext* cx, JSObject* scope, T* value,
+ JS::Value* vp)
+{
+ // We try to wrap in the compartment of the underlying object of "scope"
+ JSObject* obj;
+ {
+ // scope for the JSAutoCompartment so that we restore the compartment
+ // before we call JS_WrapValue.
+ Maybe<JSAutoCompartment> ac;
+ if (js::IsWrapper(scope)) {
+ scope = xpc::Unwrap(cx, scope, false);
+ if (!scope)
+ return false;
+ ac.construct(cx, scope);
+ }
+
+ obj = value->WrapObject(cx, scope);
+ }
+
+ // We can end up here in all sorts of compartments, per above. Make
+ // sure to JS_WrapValue!
+ *vp = JS::ObjectValue(*obj);
+ return JS_WrapValue(cx, vp);
+}
+
+// Helper for smart pointers (nsAutoPtr/nsRefPtr/nsCOMPtr).
+template <template <typename> class SmartPtr, typename T>
+inline bool
+WrapNewBindingNonWrapperCachedObject(JSContext* cx, JSObject* scope,
+ const SmartPtr<T>& value, JS::Value* vp)
+{
+ return WrapNewBindingNonWrapperCachedObject(cx, scope, value.get(), vp);
+}
+
+/**
+ * A method to handle new-binding wrap failure, by possibly falling back to
+ * wrapping as a non-new-binding object.
+ */
+bool
+DoHandleNewBindingWrappingFailure(JSContext* cx, JSObject* scope,
+ nsISupports* value, JS::Value* vp);
+
+/**
+ * An easy way to call the above when you have a value which
+ * multiply-inherits from nsISupports.
+ */
+template <class T>
+bool
+HandleNewBindingWrappingFailure(JSContext* cx, JSObject* scope, T* value,
+ JS::Value* vp)
+{
+ nsCOMPtr<nsISupports> val;
+ CallQueryInterface(value, getter_AddRefs(val));
+ return DoHandleNewBindingWrappingFailure(cx, scope, val, vp);
+}
+
+// Helper for smart pointers (nsAutoPtr/nsRefPtr/nsCOMPtr).
+template <template <typename> class SmartPtr, class T>
+MOZ_ALWAYS_INLINE bool
+HandleNewBindingWrappingFailure(JSContext* cx, JSObject* scope,
+ const SmartPtr<T>& value, JS::Value* vp)
+{
+ return HandleNewBindingWrappingFailure(cx, scope, value.get(), vp);
+}
+
+struct EnumEntry {
+ const char* value;
+ size_t length;
+};
+
+template<bool Fatal>
+inline bool
+EnumValueNotFound(JSContext* cx, const jschar* chars, size_t length,
+ const char* type)
+{
+ return false;
+}
+
+template<>
+inline bool
+EnumValueNotFound<false>(JSContext* cx, const jschar* chars, size_t length,
+ const char* type)
+{
+ // TODO: Log a warning to the console.
+ return true;
+}
+
+template<>
+inline bool
+EnumValueNotFound<true>(JSContext* cx, const jschar* chars, size_t length,
+ const char* type)
+{
+ NS_LossyConvertUTF16toASCII deflated(static_cast<const PRUnichar*>(chars),
+ length);
+ return ThrowErrorMessage(cx, MSG_INVALID_ENUM_VALUE, deflated.get(), type);
+}
+
+
+template<bool InvalidValueFatal>
+inline int
+FindEnumStringIndex(JSContext* cx, JS::Value v, const EnumEntry* values,
+ const char* type, bool* ok)
+{
+ // JS_StringEqualsAscii is slow as molasses, so don't use it here.
+ JSString* str = JS_ValueToString(cx, v);
+ if (!str) {
+ *ok = false;
+ return 0;
+ }
+ JS::Anchor<JSString*> anchor(str);
+ size_t length;
+ const jschar* chars = JS_GetStringCharsAndLength(cx, str, &length);
+ if (!chars) {
+ *ok = false;
+ return 0;
+ }
+ int i = 0;
+ for (const EnumEntry* value = values; value->value; ++value, ++i) {
+ if (length != value->length) {
+ continue;
+ }
+
+ bool equal = true;
+ const char* val = value->value;
+ for (size_t j = 0; j != length; ++j) {
+ if (unsigned(val[j]) != unsigned(chars[j])) {
+ equal = false;
+ break;
+ }
+ }
+
+ if (equal) {
+ *ok = true;
+ return i;
+ }
+ }
+
+ *ok = EnumValueNotFound<InvalidValueFatal>(cx, chars, length, type);
+ return -1;
+}
+
+inline nsWrapperCache*
+GetWrapperCache(nsWrapperCache* cache)
+{
+ return cache;
+}
+
+inline nsWrapperCache*
+GetWrapperCache(nsGlobalWindow* not_allowed);
+
+inline nsWrapperCache*
+GetWrapperCache(void* p)
+{
+ return NULL;
+}
+
+struct ParentObject {
+ template<class T>
+ ParentObject(T* aObject) :
+ mObject(aObject),
+ mWrapperCache(GetWrapperCache(aObject))
+ {}
+
+ template<class T, template<typename> class SmartPtr>
+ ParentObject(const SmartPtr<T>& aObject) :
+ mObject(aObject.get()),
+ mWrapperCache(GetWrapperCache(aObject.get()))
+ {}
+
+ ParentObject(nsISupports* aObject, nsWrapperCache* aCache) :
+ mObject(aObject),
+ mWrapperCache(aCache)
+ {}
+
+ nsISupports* const mObject;
+ nsWrapperCache* const mWrapperCache;
+};
+
+inline nsWrapperCache*
+GetWrapperCache(const ParentObject& aParentObject)
+{
+ return aParentObject.mWrapperCache;
+}
+
+template<class T>
+inline nsISupports*
+GetParentPointer(T* aObject)
+{
+ return ToSupports(aObject);
+}
+
+inline nsISupports*
+GetParentPointer(const ParentObject& aObject)
+{
+ return ToSupports(aObject.mObject);
+}
+
+template<class T>
+inline void
+ClearWrapper(T* p, nsWrapperCache* cache)
+{
+ cache->ClearWrapper();
+}
+
+template<class T>
+inline void
+ClearWrapper(T* p, void*)
+{
+ nsWrapperCache* cache;
+ CallQueryInterface(p, &cache);
+ ClearWrapper(p, cache);
+}
+
+// Can only be called with the immediate prototype of the instance object. Can
+// only be called on the prototype of an object known to be a DOM instance.
+JSBool
+InstanceClassHasProtoAtDepth(JSHandleObject protoObject, uint32_t protoID,
+ uint32_t depth);
+
+// Only set allowNativeWrapper to false if you really know you need it, if in
+// doubt use true. Setting it to false disables security wrappers.
+bool
+XPCOMObjectToJsval(JSContext* cx, JSObject* scope, xpcObjectHelper &helper,
+ const nsIID* iid, bool allowNativeWrapper, JS::Value* rval);
+
+template<class T>
+inline bool
+WrapObject(JSContext* cx, JSObject* scope, T* p, nsWrapperCache* cache,
+ const nsIID* iid, JS::Value* vp)
+{
+ if (xpc_FastGetCachedWrapper(cache, scope, vp))
+ return true;
+ qsObjectHelper helper(p, cache);
+ return XPCOMObjectToJsval(cx, scope, helper, iid, true, vp);
+}
+
+template<class T>
+inline bool
+WrapObject(JSContext* cx, JSObject* scope, T* p, const nsIID* iid,
+ JS::Value* vp)
+{
+ return WrapObject(cx, scope, p, GetWrapperCache(p), iid, vp);
+}
+
+template<class T>
+inline bool
+WrapObject(JSContext* cx, JSObject* scope, T* p, JS::Value* vp)
+{
+ return WrapObject(cx, scope, p, NULL, vp);
+}
+
+template<class T>
+inline bool
+WrapObject(JSContext* cx, JSObject* scope, nsCOMPtr<T> &p, const nsIID* iid,
+ JS::Value* vp)
+{
+ return WrapObject(cx, scope, p.get(), iid, vp);
+}
+
+template<class T>
+inline bool
+WrapObject(JSContext* cx, JSObject* scope, nsCOMPtr<T> &p, JS::Value* vp)
+{
+ return WrapObject(cx, scope, p, NULL, vp);
+}
+
+template<class T>
+inline bool
+WrapObject(JSContext* cx, JSObject* scope, nsRefPtr<T> &p, const nsIID* iid,
+ JS::Value* vp)
+{
+ return WrapObject(cx, scope, p.get(), iid, vp);
+}
+
+template<class T>
+inline bool
+WrapObject(JSContext* cx, JSObject* scope, nsRefPtr<T> &p, JS::Value* vp)
+{
+ return WrapObject(cx, scope, p, NULL, vp);
+}
+
+template<>
+inline bool
+WrapObject<JSObject>(JSContext* cx, JSObject* scope, JSObject* p, JS::Value* vp)
+{
+ vp->setObjectOrNull(p);
+ return true;
+}
+
+template<typename T>
+static inline JSObject*
+WrapNativeParent(JSContext* cx, JSObject* scope, const T& p)
+{
+ if (!GetParentPointer(p))
+ return scope;
+
+ nsWrapperCache* cache = GetWrapperCache(p);
+ JSObject* obj;
+ if (cache && (obj = cache->GetWrapper())) {
+#ifdef DEBUG
+ qsObjectHelper helper(GetParentPointer(p), cache);
+ JS::Value debugVal;
+
+ bool ok = XPCOMObjectToJsval(cx, scope, helper, NULL, false, &debugVal);
+ NS_ASSERTION(ok && JSVAL_TO_OBJECT(debugVal) == obj,
+ "Unexpected object in nsWrapperCache");
+#endif
+ return obj;
+ }
+
+ qsObjectHelper helper(GetParentPointer(p), cache);
+ JS::Value v;
+ return XPCOMObjectToJsval(cx, scope, helper, NULL, false, &v) ?
+ JSVAL_TO_OBJECT(v) :
+ NULL;
+}
+
+static inline bool
+InternJSString(JSContext* cx, jsid& id, const char* chars)
+{
+ if (JSString *str = ::JS_InternString(cx, chars)) {
+ id = INTERNED_STRING_TO_JSID(cx, str);
+ return true;
+ }
+ return false;
+}
+
+// Spec needs a name property
+template <typename Spec>
+static bool
+InitIds(JSContext* cx, Prefable<Spec>* prefableSpecs, jsid* ids)
+{
+ MOZ_ASSERT(prefableSpecs);
+ MOZ_ASSERT(prefableSpecs->specs);
+ do {
+ // We ignore whether the set of ids is enabled and just intern all the IDs,
+ // because this is only done once per application runtime.
+ Spec* spec = prefableSpecs->specs;
+ do {
+ if (!InternJSString(cx, *ids, spec->name)) {
+ return false;
+ }
+ } while (++ids, (++spec)->name);
+
+ // We ran out of ids for that pref. Put a JSID_VOID in on the id
+ // corresponding to the list terminator for the pref.
+ *ids = JSID_VOID;
+ ++ids;
+ } while ((++prefableSpecs)->specs);
+
+ return true;
+}
+
+JSBool
+QueryInterface(JSContext* cx, unsigned argc, JS::Value* vp);
+JSBool
+ThrowingConstructor(JSContext* cx, unsigned argc, JS::Value* vp);
+
+bool
+GetPropertyOnPrototype(JSContext* cx, JSObject* proxy, jsid id, bool* found,
+ JS::Value* vp);
+
+bool
+HasPropertyOnPrototype(JSContext* cx, JSObject* proxy, DOMProxyHandler* handler,
+ jsid id);
+
+template<class T>
+class NonNull
+{
+public:
+ NonNull()
+#ifdef DEBUG
+ : inited(false)
+#endif
+ {}
+
+ operator T&() {
+ MOZ_ASSERT(inited);
+ MOZ_ASSERT(ptr, "NonNull<T> was set to null");
+ return *ptr;
+ }
+
+ operator const T&() const {
+ MOZ_ASSERT(inited);
+ MOZ_ASSERT(ptr, "NonNull<T> was set to null");
+ return *ptr;
+ }
+
+ void operator=(T* t) {
+ ptr = t;
+ MOZ_ASSERT(ptr);
+#ifdef DEBUG
+ inited = true;
+#endif
+ }
+
+ template<typename U>
+ void operator=(U* t) {
+ ptr = t->ToAStringPtr();
+ MOZ_ASSERT(ptr);
+#ifdef DEBUG
+ inited = true;
+#endif
+ }
+
+ T** Slot() {
+#ifdef DEBUG
+ inited = true;
+#endif
+ return &ptr;
+ }
+
+protected:
+ T* ptr;
+#ifdef DEBUG
+ bool inited;
+#endif
+};
+
+template<class T>
+class OwningNonNull
+{
+public:
+ OwningNonNull()
+#ifdef DEBUG
+ : inited(false)
+#endif
+ {}
+
+ operator T&() {
+ MOZ_ASSERT(inited);
+ MOZ_ASSERT(ptr, "OwningNonNull<T> was set to null");
+ return *ptr;
+ }
+
+ void operator=(T* t) {
+ init(t);
+ }
+
+ void operator=(const already_AddRefed<T>& t) {
+ init(t);
+ }
+
+protected:
+ template<typename U>
+ void init(U t) {
+ ptr = t;
+ MOZ_ASSERT(ptr);
+#ifdef DEBUG
+ inited = true;
+#endif
+ }
+
+ nsRefPtr<T> ptr;
+#ifdef DEBUG
+ bool inited;
+#endif
+};
+
+// A struct that has the same layout as an nsDependentString but much
+// faster constructor and destructor behavior
+struct FakeDependentString {
+ FakeDependentString() :
+ mFlags(nsDependentString::F_TERMINATED)
+ {
+ }
+
+ void SetData(const nsDependentString::char_type* aData,
+ nsDependentString::size_type aLength) {
+ MOZ_ASSERT(mFlags == nsDependentString::F_TERMINATED);
+ mData = aData;
+ mLength = aLength;
+ }
+
+ void Truncate() {
+ mData = nsDependentString::char_traits::sEmptyBuffer;
+ mLength = 0;
+ }
+
+ void SetNull() {
+ Truncate();
+ mFlags |= nsDependentString::F_VOIDED;
+ }
+
+ const nsAString* ToAStringPtr() const {
+ return reinterpret_cast<const nsDependentString*>(this);
+ }
+
+ nsAString* ToAStringPtr() {
+ return reinterpret_cast<nsDependentString*>(this);
+ }
+
+ operator const nsAString& () const {
+ return *reinterpret_cast<const nsDependentString*>(this);
+ }
+
+private:
+ const nsDependentString::char_type* mData;
+ nsDependentString::size_type mLength;
+ uint32_t mFlags;
+
+ // A class to use for our static asserts to ensure our object layout
+ // matches that of nsDependentString.
+ class DependentStringAsserter;
+ friend class DependentStringAsserter;
+
+ class DepedentStringAsserter : public nsDependentString {
+ public:
+ static void StaticAsserts() {
+ MOZ_STATIC_ASSERT(sizeof(FakeDependentString) == sizeof(nsDependentString),
+ "Must have right object size");
+ MOZ_STATIC_ASSERT(offsetof(FakeDependentString, mData) ==
+ offsetof(DepedentStringAsserter, mData),
+ "Offset of mData should match");
+ MOZ_STATIC_ASSERT(offsetof(FakeDependentString, mLength) ==
+ offsetof(DepedentStringAsserter, mLength),
+ "Offset of mLength should match");
+ MOZ_STATIC_ASSERT(offsetof(FakeDependentString, mFlags) ==
+ offsetof(DepedentStringAsserter, mFlags),
+ "Offset of mFlags should match");
+ }
+ };
+};
+
+enum StringificationBehavior {
+ eStringify,
+ eEmpty,
+ eNull
+};
+
+// pval must not be null and must point to a rooted JS::Value
+static inline bool
+ConvertJSValueToString(JSContext* cx, const JS::Value& v, JS::Value* pval,
+ StringificationBehavior nullBehavior,
+ StringificationBehavior undefinedBehavior,
+ FakeDependentString& result)
+{
+ JSString *s;
+ if (v.isString()) {
+ s = v.toString();
+ } else {
+ StringificationBehavior behavior;
+ if (v.isNull()) {
+ behavior = nullBehavior;
+ } else if (v.isUndefined()) {
+ behavior = undefinedBehavior;
+ } else {
+ behavior = eStringify;
+ }
+
+ if (behavior != eStringify) {
+ if (behavior == eEmpty) {
+ result.Truncate();
+ } else {
+ result.SetNull();
+ }
+ return true;
+ }
+
+ s = JS_ValueToString(cx, v);
+ if (!s) {
+ return false;
+ }
+ pval->setString(s); // Root the new string.
+ }
+
+ size_t len;
+ const jschar *chars = JS_GetStringCharsZAndLength(cx, s, &len);
+ if (!chars) {
+ return false;
+ }
+
+ result.SetData(chars, len);
+ return true;
+}
+
+// Class for representing optional arguments.
+template<typename T>
+class Optional {
+public:
+ Optional() {}
+
+ bool WasPassed() const {
+ return !mImpl.empty();
+ }
+
+ void Construct() {
+ mImpl.construct();
+ }
+
+ template <class T1, class T2>
+ void Construct(const T1 &t1, const T2 &t2) {
+ mImpl.construct(t1, t2);
+ }
+
+ const T& Value() const {
+ return mImpl.ref();
+ }
+
+ T& Value() {
+ return mImpl.ref();
+ }
+
+private:
+ // Forbid copy-construction and assignment
+ Optional(const Optional& other) MOZ_DELETE;
+ const Optional &operator=(const Optional &other) MOZ_DELETE;
+
+ Maybe<T> mImpl;
+};
+
+// Specialization for strings.
+template<>
+class Optional<nsAString> {
+public:
+ Optional() : mPassed(false) {}
+
+ bool WasPassed() const {
+ return mPassed;
+ }
+
+ void operator=(const nsAString* str) {
+ MOZ_ASSERT(str);
+ mStr = str;
+ mPassed = true;
+ }
+
+ void operator=(const FakeDependentString* str) {
+ MOZ_ASSERT(str);
+ mStr = str->ToAStringPtr();
+ mPassed = true;
+ }
+
+ const nsAString& Value() const {
+ MOZ_ASSERT(WasPassed());
+ return *mStr;
+ }
+
+private:
+ // Forbid copy-construction and assignment
+ Optional(const Optional& other) MOZ_DELETE;
+ const Optional &operator=(const Optional &other) MOZ_DELETE;
+
+ bool mPassed;
+ const nsAString* mStr;
+};
+
+// Class for representing sequences in arguments. We use an auto array that can
+// hold 16 elements, to avoid having to allocate in common cases. This needs to
+// be fallible because web content controls the length of the array, and can
+// easily try to create very large lengths.
+template<typename T>
+class Sequence : public AutoFallibleTArray<T, 16>
+{
+public:
+ Sequence() : AutoFallibleTArray<T, 16>() {}
+};
+
+// Class for holding the type of members of a union. The union type has an enum
+// to keep track of which of its UnionMembers has been constructed.
+template<class T>
+class UnionMember {
+ AlignedStorage2<T> storage;
+
+public:
+ T& SetValue() {
+ new (storage.addr()) T();
+ return *storage.addr();
+ }
+ const T& Value() const {
+ return *storage.addr();
+ }
+ void Destroy() {
+ storage.addr()->~T();
+ }
+};
+
+// Implementation of the bits that XrayWrapper needs
+bool
+XrayResolveProperty(JSContext* cx, JSObject* wrapper, jsid id,
+ JSPropertyDescriptor* desc,
+ // And the things we need to determine the descriptor
+ Prefable<JSFunctionSpec>* methods,
+ jsid* methodIds,
+ JSFunctionSpec* methodSpecs,
+ size_t methodCount,
+ Prefable<JSPropertySpec>* attributes,
+ jsid* attributeIds,
+ JSPropertySpec* attributeSpecs,
+ size_t attributeCount,
+ Prefable<ConstantSpec>* constants,
+ jsid* constantIds,
+ ConstantSpec* constantSpecs,
+ size_t constantCount);
+
+bool
+XrayEnumerateProperties(JS::AutoIdVector& props,
+ Prefable<JSFunctionSpec>* methods,
+ jsid* methodIds,
+ JSFunctionSpec* methodSpecs,
+ size_t methodCount,
+ Prefable<JSPropertySpec>* attributes,
+ jsid* attributeIds,
+ JSPropertySpec* attributeSpecs,
+ size_t attributeCount,
+ Prefable<ConstantSpec>* constants,
+ jsid* constantIds,
+ ConstantSpec* constantSpecs,
+ size_t constantCount);
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_BindingUtils_h__ */
diff --git a/components/script/dom/bindings/codegen/Bindings.conf b/components/script/dom/bindings/codegen/Bindings.conf
new file mode 100644
index 00000000000..f8119bc71f5
--- /dev/null
+++ b/components/script/dom/bindings/codegen/Bindings.conf
@@ -0,0 +1,28 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+# DOM Bindings Configuration.
+#
+# The WebIDL interfaces are defined in dom/webidls. For each such interface,
+# there is a corresponding entry in the configuration table below.
+# The configuration table maps each interface name to a |descriptor|.
+#
+# Valid fields for all descriptors:
+# * createGlobal: True for global objects.
+# * outerObjectHook: string to use in place of default value for outerObject and thisObject
+# JS class hooks
+
+DOMInterfaces = {
+
+'EventListener': {
+ 'nativeType': 'EventListenerBinding::EventListener',
+},
+'Window': {
+ 'outerObjectHook': 'Some(bindings::utils::outerize_global)',
+},
+
+#FIXME(jdm): This should be 'register': False, but then we don't generate enum types
+'TestBinding': {},
+
+}
diff --git a/components/script/dom/bindings/codegen/Codegen.py b/components/script/dom/bindings/codegen/Codegen.py
new file mode 100644
index 00000000000..6d2cc0bde36
--- /dev/null
+++ b/components/script/dom/bindings/codegen/Codegen.py
@@ -0,0 +1,5788 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at http://mozilla.org/MPL/2.0/.
+
+# Common codegen classes.
+
+import os
+import string
+import operator
+
+from WebIDL import *
+from Configuration import NoSuchDescriptorError
+
+AUTOGENERATED_WARNING_COMMENT = \
+ "/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n\n"
+ADDPROPERTY_HOOK_NAME = '_addProperty'
+FINALIZE_HOOK_NAME = '_finalize'
+TRACE_HOOK_NAME = '_trace'
+CONSTRUCT_HOOK_NAME = '_constructor'
+HASINSTANCE_HOOK_NAME = '_hasInstance'
+
+def replaceFileIfChanged(filename, newContents):
+ """
+ Read a copy of the old file, so that we don't touch it if it hasn't changed.
+ Returns True if the file was updated, false otherwise.
+ """
+ oldFileContents = ""
+ try:
+ oldFile = open(filename, 'rb')
+ oldFileContents = ''.join(oldFile.readlines())
+ oldFile.close()
+ except:
+ pass
+
+ if newContents == oldFileContents:
+ return False
+
+ f = open(filename, 'wb')
+ f.write(newContents)
+ f.close()
+
+def toStringBool(arg):
+ return str(not not arg).lower()
+
+def toBindingNamespace(arg):
+ return re.sub("((_workers)?$)", "Binding\\1", arg);
+
+class CGThing():
+ """
+ Abstract base class for things that spit out code.
+ """
+ def __init__(self):
+ pass # Nothing for now
+ def declare(self):
+ """Produce code for a header file."""
+ assert(False) # Override me!
+ def define(self):
+ """Produce code for a cpp file."""
+ assert(False) # Override me!
+
+class CGNativePropertyHooks(CGThing):
+ """
+ Generate a NativePropertyHooks for a given descriptor
+ """
+ def __init__(self, descriptor):
+ CGThing.__init__(self)
+ self.descriptor = descriptor
+ def declare(self):
+ if self.descriptor.workers:
+ return ""
+ return "extern const NativePropertyHooks NativeHooks;\n"
+ def define(self):
+ if self.descriptor.workers:
+ return ""
+ if self.descriptor.concrete and self.descriptor.proxy:
+ resolveOwnProperty = "ResolveOwnProperty"
+ enumerateOwnProperties = "EnumerateOwnProperties"
+ else:
+ enumerateOwnProperties = resolveOwnProperty = "NULL"
+ parent = self.descriptor.interface.parent
+ parentHooks = ("&" + toBindingNamespace(parent.identifier.name) + "::NativeHooks"
+ if parent else 'NULL')
+ return """
+const NativePropertyHooks NativeHooks = { %s, ResolveProperty, %s, EnumerateProperties, %s };
+""" % (resolveOwnProperty, enumerateOwnProperties, parentHooks)
+
+def DOMClass(descriptor):
+ protoList = ['prototypes::id::' + proto for proto in descriptor.prototypeChain]
+ # Pad out the list to the right length with _ID_Count so we
+ # guarantee that all the lists are the same length. _ID_Count
+ # is never the ID of any prototype, so it's safe to use as
+ # padding.
+ protoList.extend(['prototypes::id::_ID_Count'] * (descriptor.config.maxProtoChainLength - len(protoList)))
+ prototypeChainString = ', '.join(protoList)
+ nativeHooks = "NULL" if descriptor.workers else "&NativeHooks"
+ return """{
+ { %s },
+ %s, %s
+}""" % (prototypeChainString, toStringBool(descriptor.nativeIsISupports),
+ nativeHooks)
+
+class CGDOMJSClass(CGThing):
+ """
+ Generate a DOMJSClass for a given descriptor
+ """
+ def __init__(self, descriptor):
+ CGThing.__init__(self)
+ self.descriptor = descriptor
+ def declare(self):
+ return "extern DOMJSClass Class;\n"
+ def define(self):
+ traceHook = TRACE_HOOK_NAME if self.descriptor.customTrace else 'NULL'
+ return """
+DOMJSClass Class = {
+ { "%s",
+ JSCLASS_IS_DOMJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(1),
+ %s, /* addProperty */
+ JS_PropertyStub, /* delProperty */
+ JS_PropertyStub, /* getProperty */
+ JS_StrictPropertyStub, /* setProperty */
+ JS_EnumerateStub,
+ JS_ResolveStub,
+ JS_ConvertStub,
+ %s, /* finalize */
+ NULL, /* checkAccess */
+ NULL, /* call */
+ NULL, /* hasInstance */
+ NULL, /* construct */
+ %s, /* trace */
+ JSCLASS_NO_INTERNAL_MEMBERS
+ },
+ %s
+};
+""" % (self.descriptor.interface.identifier.name,
+ ADDPROPERTY_HOOK_NAME if self.descriptor.concrete and not self.descriptor.workers and self.descriptor.wrapperCache else 'JS_PropertyStub',
+ FINALIZE_HOOK_NAME, traceHook,
+ CGIndenter(CGGeneric(DOMClass(self.descriptor))).define())
+
+class CGPrototypeJSClass(CGThing):
+ def __init__(self, descriptor):
+ CGThing.__init__(self)
+ self.descriptor = descriptor
+ def declare(self):
+ # We're purely for internal consumption
+ return ""
+ def define(self):
+ return """static JSClass PrototypeClass = {
+ "%sPrototype",
+ JSCLASS_HAS_RESERVED_SLOTS(1),
+ JS_PropertyStub, /* addProperty */
+ JS_PropertyStub, /* delProperty */
+ JS_PropertyStub, /* getProperty */
+ JS_StrictPropertyStub, /* setProperty */
+ JS_EnumerateStub,
+ JS_ResolveStub,
+ JS_ConvertStub,
+ NULL, /* finalize */
+ NULL, /* checkAccess */
+ NULL, /* call */
+ NULL, /* hasInstance */
+ NULL, /* construct */
+ NULL, /* trace */
+ JSCLASS_NO_INTERNAL_MEMBERS
+};
+""" % (self.descriptor.interface.identifier.name)
+
+class CGInterfaceObjectJSClass(CGThing):
+ def __init__(self, descriptor):
+ CGThing.__init__(self)
+ self.descriptor = descriptor
+ def declare(self):
+ # We're purely for internal consumption
+ return ""
+ def define(self):
+ if not self.descriptor.hasInstanceInterface:
+ return ""
+ ctorname = "NULL" if not self.descriptor.interface.ctor() else CONSTRUCT_HOOK_NAME
+ hasinstance = HASINSTANCE_HOOK_NAME
+ return """
+static JSClass InterfaceObjectClass = {
+ "Function", 0,
+ JS_PropertyStub, /* addProperty */
+ JS_PropertyStub, /* delProperty */
+ JS_PropertyStub, /* getProperty */
+ JS_StrictPropertyStub, /* setProperty */
+ JS_EnumerateStub,
+ JS_ResolveStub,
+ JS_ConvertStub,
+ NULL, /* finalize */
+ NULL, /* checkAccess */
+ %s, /* call */
+ %s, /* hasInstance */
+ %s, /* construct */
+ NULL, /* trace */
+ JSCLASS_NO_INTERNAL_MEMBERS
+};
+""" % (ctorname, hasinstance, ctorname)
+
+class CGList(CGThing):
+ """
+ Generate code for a list of GCThings. Just concatenates them together, with
+ an optional joiner string. "\n" is a common joiner.
+ """
+ def __init__(self, children, joiner=""):
+ CGThing.__init__(self)
+ self.children = children
+ self.joiner = joiner
+ def append(self, child):
+ self.children.append(child)
+ def prepend(self, child):
+ self.children.insert(0, child)
+ def join(self, generator):
+ return self.joiner.join(filter(lambda s: len(s) > 0, (child for child in generator)))
+ def declare(self):
+ return self.join(child.declare() for child in self.children if child is not None)
+ def define(self):
+ return self.join(child.define() for child in self.children if child is not None)
+
+class CGGeneric(CGThing):
+ """
+ A class that spits out a fixed string into the codegen. Can spit out a
+ separate string for the declaration too.
+ """
+ def __init__(self, define="", declare=""):
+ self.declareText = declare
+ self.defineText = define
+ def declare(self):
+ return self.declareText
+ def define(self):
+ return self.defineText
+
+# We'll want to insert the indent at the beginnings of lines, but we
+# don't want to indent empty lines. So only indent lines that have a
+# non-newline character on them.
+lineStartDetector = re.compile("^(?=[^\n#])", re.MULTILINE)
+class CGIndenter(CGThing):
+ """
+ A class that takes another CGThing and generates code that indents that
+ CGThing by some number of spaces. The default indent is two spaces.
+ """
+ def __init__(self, child, indentLevel=2, declareOnly=False):
+ CGThing.__init__(self)
+ self.child = child
+ self.indent = " " * indentLevel
+ self.declareOnly = declareOnly
+ def declare(self):
+ decl = self.child.declare()
+ if decl is not "":
+ return re.sub(lineStartDetector, self.indent, decl)
+ else:
+ return ""
+ def define(self):
+ defn = self.child.define()
+ if defn is not "" and not self.declareOnly:
+ return re.sub(lineStartDetector, self.indent, defn)
+ else:
+ return defn
+
+class CGWrapper(CGThing):
+ """
+ Generic CGThing that wraps other CGThings with pre and post text.
+ """
+ def __init__(self, child, pre="", post="", declarePre=None,
+ declarePost=None, definePre=None, definePost=None,
+ declareOnly=False, defineOnly=False, reindent=False):
+ CGThing.__init__(self)
+ self.child = child
+ self.declarePre = declarePre or pre
+ self.declarePost = declarePost or post
+ self.definePre = definePre or pre
+ self.definePost = definePost or post
+ self.declareOnly = declareOnly
+ self.defineOnly = defineOnly
+ self.reindent = reindent
+ def declare(self):
+ if self.defineOnly:
+ return ''
+ decl = self.child.declare()
+ if self.reindent:
+ # We don't use lineStartDetector because we don't want to
+ # insert whitespace at the beginning of our _first_ line.
+ decl = stripTrailingWhitespace(
+ decl.replace("\n", "\n" + (" " * len(self.declarePre))))
+ return self.declarePre + decl + self.declarePost
+ def define(self):
+ if self.declareOnly:
+ return ''
+ defn = self.child.define()
+ if self.reindent:
+ # We don't use lineStartDetector because we don't want to
+ # insert whitespace at the beginning of our _first_ line.
+ defn = stripTrailingWhitespace(
+ defn.replace("\n", "\n" + (" " * len(self.definePre))))
+ return self.definePre + defn + self.definePost
+
+class CGIfWrapper(CGWrapper):
+ def __init__(self, child, condition):
+ pre = CGWrapper(CGGeneric(condition), pre="if (", post=") {\n",
+ reindent=True)
+ CGWrapper.__init__(self, CGIndenter(child), pre=pre.define(),
+ post="\n}")
+
+class CGNamespace(CGWrapper):
+ def __init__(self, namespace, child, declareOnly=False):
+ pre = "namespace %s {\n" % namespace
+ post = "} // namespace %s\n" % namespace
+ CGWrapper.__init__(self, child, pre=pre, post=post,
+ declareOnly=declareOnly)
+ @staticmethod
+ def build(namespaces, child, declareOnly=False):
+ """
+ Static helper method to build multiple wrapped namespaces.
+ """
+ if not namespaces:
+ return CGWrapper(child, declareOnly=declareOnly)
+ inner = CGNamespace.build(namespaces[1:], child, declareOnly=declareOnly)
+ return CGNamespace(namespaces[0], inner, declareOnly=declareOnly)
+
+class CGIncludeGuard(CGWrapper):
+ """
+ Generates include guards for a header.
+ """
+ def __init__(self, prefix, child):
+ """|prefix| is the filename without the extension."""
+ define = 'mozilla_dom_%s_h__' % prefix
+ CGWrapper.__init__(self, child,
+ declarePre='#ifndef %s\n#define %s\n\n' % (define, define),
+ declarePost='\n#endif // %s\n' % define)
+
+def getTypes(descriptor):
+ """
+ Get all argument and return types for all members of the descriptor
+ """
+ members = [m for m in descriptor.interface.members]
+ if descriptor.interface.ctor():
+ members.append(descriptor.interface.ctor())
+ signatures = [s for m in members if m.isMethod() for s in m.signatures()]
+ types = []
+ for s in signatures:
+ assert len(s) == 2
+ (returnType, arguments) = s
+ types.append(returnType)
+ types.extend([a.type for a in arguments])
+
+ types.extend(a.type for a in members if a.isAttr())
+ return types
+
+class CGHeaders(CGWrapper):
+ """
+ Generates the appropriate include statements.
+ """
+ def __init__(self, descriptors, dictionaries, declareIncludes,
+ defineIncludes, child):
+ """
+ Builds a set of includes to cover |descriptors|.
+
+ Also includes the files in |declareIncludes| in the header
+ file and the files in |defineIncludes| in the .cpp.
+ """
+
+ # Determine the filenames for which we need headers.
+ interfaceDeps = [d.interface for d in descriptors]
+ ancestors = []
+ for iface in interfaceDeps:
+ while iface.parent:
+ ancestors.append(iface.parent)
+ iface = iface.parent
+ interfaceDeps.extend(ancestors)
+ bindingIncludes = set(self.getDeclarationFilename(d) for d in interfaceDeps)
+
+ # Grab all the implementation declaration files we need.
+ implementationIncludes = set(d.headerFile for d in descriptors)
+
+ # Now find all the things we'll need as arguments because we
+ # need to wrap or unwrap them.
+ bindingHeaders = set()
+ for d in descriptors:
+ types = getTypes(d)
+ for dictionary in dictionaries:
+ curDict = dictionary
+ while curDict:
+ types.extend([m.type for m in curDict.members])
+ curDict = curDict.parent
+
+ for t in types:
+ if t.unroll().isUnion():
+ # UnionConversions.h includes UnionTypes.h
+ bindingHeaders.add("mozilla/dom/UnionConversions.h")
+ elif t.unroll().isInterface():
+ if t.unroll().isSpiderMonkeyInterface():
+ bindingHeaders.add("jsfriendapi.h")
+ bindingHeaders.add("mozilla/dom/TypedArray.h")
+ else:
+ typeDesc = d.getDescriptor(t.unroll().inner.identifier.name)
+ if typeDesc is not None:
+ implementationIncludes.add(typeDesc.headerFile)
+ bindingHeaders.add(self.getDeclarationFilename(typeDesc.interface))
+ elif t.unroll().isDictionary():
+ bindingHeaders.add(self.getDeclarationFilename(t.unroll().inner))
+
+ declareIncludes = set(declareIncludes)
+ for d in dictionaries:
+ if d.parent:
+ declareIncludes.add(self.getDeclarationFilename(d.parent))
+ bindingHeaders.add(self.getDeclarationFilename(d))
+
+ # Let the machinery do its thing.
+ def _includeString(includes):
+ return ''.join(['#include "%s"\n' % i for i in includes]) + '\n'
+ CGWrapper.__init__(self, child,
+ declarePre=_includeString(sorted(declareIncludes)),
+ definePre=_includeString(sorted(set(defineIncludes) |
+ bindingIncludes |
+ bindingHeaders |
+ implementationIncludes)))
+ @staticmethod
+ def getDeclarationFilename(decl):
+ # Use our local version of the header, not the exported one, so that
+ # test bindings, which don't export, will work correctly.
+ basename = os.path.basename(decl.filename())
+ return basename.replace('.webidl', 'Binding.h')
+
+def SortedTuples(l):
+ """
+ Sort a list of tuples based on the first item in the tuple
+ """
+ return sorted(l, key=operator.itemgetter(0))
+
+def SortedDictValues(d):
+ """
+ Returns a list of values from the dict sorted by key.
+ """
+ # Create a list of tuples containing key and value, sorted on key.
+ d = SortedTuples(d.items())
+ # We're only interested in the values.
+ return (i[1] for i in d)
+
+def UnionTypes(descriptors):
+ """
+ Returns a tuple containing a set of header filenames to include, a set of
+ tuples containing a type declaration and a boolean if the type is a struct
+ for member types of the unions and a CGList containing CGUnionStructs for
+ every union.
+ """
+
+ # Now find all the things we'll need as arguments and return values because
+ # we need to wrap or unwrap them.
+ headers = set()
+ declarations = set()
+ unionStructs = dict()
+ for d in descriptors:
+ if d.interface.isExternal():
+ continue
+
+ for t in getTypes(d):
+ t = t.unroll()
+ if t.isUnion():
+ name = str(t)
+ if not name in unionStructs:
+ unionStructs[name] = CGUnionStruct(t, d)
+ for f in t.flatMemberTypes:
+ f = f.unroll()
+ if f.isInterface():
+ if f.isSpiderMonkeyInterface():
+ headers.add("jsfriendapi.h")
+ headers.add("mozilla/dom/TypedArray.h")
+ else:
+ typeDesc = d.getDescriptor(f.inner.identifier.name)
+ if typeDesc is not None:
+ declarations.add((typeDesc.nativeType, False))
+ elif f.isDictionary():
+ declarations.add((f.inner.identifier.name, True))
+
+ return (headers, declarations, CGList(SortedDictValues(unionStructs), "\n"))
+
+def UnionConversions(descriptors):
+ """
+ Returns a CGThing to declare all union argument conversion helper structs.
+ """
+ # Now find all the things we'll need as arguments because we
+ # need to unwrap them.
+ unionConversions = dict()
+ for d in descriptors:
+ if d.interface.isExternal():
+ continue
+
+ def addUnionTypes(type):
+ if type.isUnion():
+ type = type.unroll()
+ name = str(type)
+ if not name in unionConversions:
+ unionConversions[name] = CGUnionConversionStruct(type, d)
+
+ members = [m for m in d.interface.members]
+ if d.interface.ctor():
+ members.append(d.interface.ctor())
+ signatures = [s for m in members if m.isMethod() for s in m.signatures()]
+ for s in signatures:
+ assert len(s) == 2
+ (_, arguments) = s
+ for a in arguments:
+ addUnionTypes(a.type)
+
+ for m in members:
+ if m.isAttr() and not m.readonly:
+ addUnionTypes(m.type)
+
+ return CGWrapper(CGList(SortedDictValues(unionConversions), "\n"),
+ post="\n\n")
+
+class Argument():
+ """
+ A class for outputting the type and name of an argument
+ """
+ def __init__(self, argType, name):
+ self.argType = argType
+ self.name = name
+ def __str__(self):
+ return self.argType + ' ' + self.name
+
+class CGAbstractMethod(CGThing):
+ """
+ An abstract class for generating code for a method. Subclasses
+ should override definition_body to create the actual code.
+
+ descriptor is the descriptor for the interface the method is associated with
+
+ name is the name of the method as a string
+
+ returnType is the IDLType of the return value
+
+ args is a list of Argument objects
+
+ inline should be True to generate an inline method, whose body is
+ part of the declaration.
+
+ alwaysInline should be True to generate an inline method annotated with
+ MOZ_ALWAYS_INLINE.
+
+ static should be True to generate a static method, which only has
+ a definition.
+
+ If templateArgs is not None it should be a list of strings containing
+ template arguments, and the function will be templatized using those
+ arguments.
+ """
+ def __init__(self, descriptor, name, returnType, args, inline=False, alwaysInline=False, static=False, templateArgs=None):
+ CGThing.__init__(self)
+ self.descriptor = descriptor
+ self.name = name
+ self.returnType = returnType
+ self.args = args
+ self.inline = inline
+ self.alwaysInline = alwaysInline
+ self.static = static
+ self.templateArgs = templateArgs
+ def _argstring(self):
+ return ', '.join([str(a) for a in self.args])
+ def _template(self):
+ if self.templateArgs is None:
+ return ''
+ return 'template <%s>\n' % ', '.join(self.templateArgs)
+ def _decorators(self):
+ decorators = []
+ if self.alwaysInline:
+ decorators.append('MOZ_ALWAYS_INLINE')
+ elif self.inline:
+ decorators.append('inline')
+ if self.static:
+ decorators.append('static')
+ decorators.append(self.returnType)
+ maybeNewline = " " if self.inline else "\n"
+ return ' '.join(decorators) + maybeNewline
+ def declare(self):
+ if self.inline:
+ return self._define()
+ return "%s%s%s(%s);\n" % (self._template(), self._decorators(), self.name, self._argstring())
+ def _define(self):
+ return self.definition_prologue() + "\n" + self.definition_body() + self.definition_epilogue()
+ def define(self):
+ return "" if self.inline else self._define()
+ def definition_prologue(self):
+ return "%s%s%s(%s)\n{" % (self._template(), self._decorators(),
+ self.name, self._argstring())
+ def definition_epilogue(self):
+ return "\n}\n"
+ def definition_body(self):
+ assert(False) # Override me!
+
+class CGAbstractStaticMethod(CGAbstractMethod):
+ """
+ Abstract base class for codegen of implementation-only (no
+ declaration) static methods.
+ """
+ def __init__(self, descriptor, name, returnType, args):
+ CGAbstractMethod.__init__(self, descriptor, name, returnType, args,
+ inline=False, static=True)
+ def declare(self):
+ # We only have implementation
+ return ""
+
+class CGAbstractClassHook(CGAbstractStaticMethod):
+ """
+ Meant for implementing JSClass hooks, like Finalize or Trace. Does very raw
+ 'this' unwrapping as it assumes that the unwrapped type is always known.
+ """
+ def __init__(self, descriptor, name, returnType, args):
+ CGAbstractStaticMethod.__init__(self, descriptor, name, returnType,
+ args)
+
+ def definition_body_prologue(self):
+ return """
+ %s* self = UnwrapDOMObject<%s>(obj, eRegularDOMObject);
+""" % (self.descriptor.nativeType, self.descriptor.nativeType)
+
+ def definition_body(self):
+ return self.definition_body_prologue() + self.generate_code()
+
+ def generate_code(self):
+ # Override me
+ assert(False)
+
+class CGAddPropertyHook(CGAbstractClassHook):
+ """
+ A hook for addProperty, used to preserve our wrapper from GC.
+ """
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'), Argument('JSHandleObject', 'obj'),
+ Argument('JSHandleId', 'id'), Argument('JSMutableHandleValue', 'vp')]
+ CGAbstractClassHook.__init__(self, descriptor, ADDPROPERTY_HOOK_NAME,
+ 'JSBool', args)
+
+ def generate_code(self):
+ # FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=774279
+ # Using a real trace hook might enable us to deal with non-nsISupports
+ # wrappercached things here.
+ assert self.descriptor.nativeIsISupports
+ return """ nsContentUtils::PreserveWrapper(reinterpret_cast<nsISupports*>(self), self);
+ return true;"""
+
+def finalizeHook(descriptor, hookName, context):
+ if descriptor.customFinalize:
+ return """if (self) {
+ self->%s(%s);
+}""" % (hookName, context)
+ clearWrapper = "ClearWrapper(self, self);\n" if descriptor.wrapperCache else ""
+ if descriptor.workers:
+ release = "self->Release();"
+ else:
+ assert descriptor.nativeIsISupports
+ release = """XPCJSRuntime *rt = nsXPConnect::GetRuntimeInstance();
+if (rt) {
+ rt->DeferredRelease(reinterpret_cast<nsISupports*>(self));
+} else {
+ NS_RELEASE(self);
+}"""
+ return clearWrapper + release
+
+class CGClassFinalizeHook(CGAbstractClassHook):
+ """
+ A hook for finalize, used to release our native object.
+ """
+ def __init__(self, descriptor):
+ args = [Argument('JSFreeOp*', 'fop'), Argument('JSObject*', 'obj')]
+ CGAbstractClassHook.__init__(self, descriptor, FINALIZE_HOOK_NAME,
+ 'void', args)
+
+ def generate_code(self):
+ return CGIndenter(CGGeneric(finalizeHook(self.descriptor, self.name, self.args[0].name))).define()
+
+class CGClassTraceHook(CGAbstractClassHook):
+ """
+ A hook to trace through our native object; used for GC and CC
+ """
+ def __init__(self, descriptor):
+ args = [Argument('JSTracer*', 'trc'), Argument('JSObject*', 'obj')]
+ CGAbstractClassHook.__init__(self, descriptor, TRACE_HOOK_NAME, 'void',
+ args)
+
+ def generate_code(self):
+ return """ if (self) {
+ self->%s(%s);
+ }""" % (self.name, self.args[0].name)
+
+class CGClassConstructHook(CGAbstractStaticMethod):
+ """
+ JS-visible constructor for our objects
+ """
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'), Argument('unsigned', 'argc'), Argument('JS::Value*', 'vp')]
+ CGAbstractStaticMethod.__init__(self, descriptor, CONSTRUCT_HOOK_NAME,
+ 'JSBool', args)
+ self._ctor = self.descriptor.interface.ctor()
+
+ def define(self):
+ if not self._ctor:
+ return ""
+ return CGAbstractStaticMethod.define(self)
+
+ def definition_body(self):
+ return self.generate_code()
+
+ def generate_code(self):
+ preamble = """
+ JSObject* obj = JS_GetGlobalForObject(cx, JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)));
+"""
+ if self.descriptor.workers:
+ preArgs = ["cx", "obj"]
+ else:
+ preamble += """
+ nsISupports* global;
+ xpc_qsSelfRef globalRef;
+ {
+ nsresult rv;
+ JS::Value val = OBJECT_TO_JSVAL(obj);
+ rv = xpc_qsUnwrapArg<nsISupports>(cx, val, &global, &globalRef.ptr, &val);
+ if (NS_FAILED(rv)) {
+ return Throw<true>(cx, NS_ERROR_XPC_BAD_CONVERT_JS);
+ }
+ }
+"""
+ preArgs = ["global"]
+
+ name = self._ctor.identifier.name
+ nativeName = MakeNativeName(self.descriptor.binaryNames.get(name, name))
+ callGenerator = CGMethodCall(preArgs, nativeName, True,
+ self.descriptor, self._ctor)
+ return preamble + callGenerator.define();
+
+class CGClassHasInstanceHook(CGAbstractStaticMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'), Argument('JSHandleObject', 'obj'),
+ Argument('JSMutableHandleValue', 'vp'), Argument('JSBool*', 'bp')]
+ CGAbstractStaticMethod.__init__(self, descriptor, HASINSTANCE_HOOK_NAME,
+ 'JSBool', args)
+
+ def define(self):
+ if not self.descriptor.hasInstanceInterface:
+ return ""
+ return CGAbstractStaticMethod.define(self)
+
+ def definition_body(self):
+ return self.generate_code()
+
+ def generate_code(self):
+ return """ if (!vp.isObject()) {
+ *bp = false;
+ return true;
+ }
+
+ jsval protov;
+ if (!JS_GetProperty(cx, obj, "prototype", &protov))
+ return false;
+ if (!protov.isObject()) {
+ JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_PROTOTYPE,
+ "%s");
+ return false;
+ }
+ JSObject *objProto = &protov.toObject();
+
+ JSObject* instance = &vp.toObject();
+ JSObject* proto;
+ if (!JS_GetPrototype(cx, instance, &proto))
+ return false;
+ while (proto) {
+ if (proto == objProto) {
+ *bp = true;
+ return true;
+ }
+ if (!JS_GetPrototype(cx, proto, &proto))
+ return false;
+ }
+
+ nsISupports* native =
+ nsContentUtils::XPConnect()->GetNativeOfWrapper(cx, instance);
+ nsCOMPtr<%s> qiResult = do_QueryInterface(native);
+ *bp = !!qiResult;
+ return true;
+""" % (self.descriptor.name, self.descriptor.hasInstanceInterface)
+
+def isChromeOnly(m):
+ return m.getExtendedAttribute("ChromeOnly")
+
+class PropertyDefiner:
+ """
+ A common superclass for defining things on prototype objects.
+
+ Subclasses should implement generateArray to generate the actual arrays of
+ things we're defining. They should also set self.chrome to the list of
+ things exposed to chrome and self.regular to the list of things exposed to
+ web pages. self.chrome must be a superset of self.regular but also include
+ all the ChromeOnly stuff.
+ """
+ def __init__(self, descriptor, name):
+ self.descriptor = descriptor
+ self.name = name
+ # self.prefCacheData will store an array of (prefname, bool*)
+ # pairs for our bool var caches. generateArray will fill it
+ # in as needed.
+ self.prefCacheData = []
+ def hasChromeOnly(self):
+ return len(self.chrome) > len(self.regular)
+ def hasNonChromeOnly(self):
+ return len(self.regular) > 0
+ def variableName(self, chrome):
+ if chrome and self.hasChromeOnly():
+ return "sChrome" + self.name
+ if self.hasNonChromeOnly():
+ return "s" + self.name
+ return "NULL"
+ def usedForXrays(self, chrome):
+ # We only need Xrays for methods, attributes and constants. And we only
+ # need them for the non-chrome ones if we have no chromeonly things.
+ # Otherwise (we have chromeonly attributes) we need Xrays for the chrome
+ # methods/attributes/constants. Finally, in workers there are no Xrays.
+ return ((self.name is "Methods" or self.name is "Attributes" or
+ self.name is "Constants") and
+ chrome == self.hasChromeOnly() and
+ not self.descriptor.workers)
+
+ def __str__(self):
+ # We only need to generate id arrays for things that will end
+ # up used via ResolveProperty or EnumerateProperties.
+ str = self.generateArray(self.regular, self.variableName(False),
+ self.usedForXrays(False))
+ if self.hasChromeOnly():
+ str += self.generateArray(self.chrome, self.variableName(True),
+ self.usedForXrays(True))
+ return str
+
+ @staticmethod
+ def getControllingPref(interfaceMember):
+ prefName = interfaceMember.getExtendedAttribute("Pref")
+ if prefName is None:
+ return None
+ # It's a list of strings
+ assert(len(prefName) is 1)
+ assert(prefName[0] is not None)
+ return prefName[0]
+
+ def generatePrefableArray(self, array, name, specTemplate, specTerminator,
+ specType, getPref, getDataTuple, doIdArrays):
+ """
+ This method generates our various arrays.
+
+ array is an array of interface members as passed to generateArray
+
+ name is the name as passed to generateArray
+
+ specTemplate is a template for each entry of the spec array
+
+ specTerminator is a terminator for the spec array (inserted every time
+ our controlling pref changes and at the end of the array)
+
+ specType is the actual typename of our spec
+
+ getPref is a callback function that takes an array entry and returns
+ the corresponding pref value.
+
+ getDataTuple is a callback function that takes an array entry and
+ returns a tuple suitable for substitution into specTemplate.
+ """
+
+ # We want to generate a single list of specs, but with specTerminator
+ # inserted at every point where the pref name controlling the member
+ # changes. That will make sure the order of the properties as exposed
+ # on the interface and interface prototype objects does not change when
+ # pref control is added to members while still allowing us to define all
+ # the members in the smallest number of JSAPI calls.
+ assert(len(array) is not 0)
+ lastPref = getPref(array[0]) # So we won't put a specTerminator
+ # at the very front of the list.
+ specs = []
+ prefableSpecs = []
+ if doIdArrays:
+ prefableIds = []
+
+ prefableTemplate = ' { true, &%s[%d] }'
+ prefCacheTemplate = '&%s[%d].enabled'
+ def switchToPref(props, pref):
+ # Remember the info about where our pref-controlled
+ # booleans live.
+ if pref is not None:
+ props.prefCacheData.append(
+ (pref, prefCacheTemplate % (name, len(prefableSpecs)))
+ )
+ # Set up pointers to the new sets of specs and ids
+ # inside prefableSpecs and prefableIds
+ prefableSpecs.append(prefableTemplate %
+ (name + "_specs", len(specs)))
+
+ switchToPref(self, lastPref)
+
+ for member in array:
+ curPref = getPref(member)
+ if lastPref != curPref:
+ # Terminate previous list
+ specs.append(specTerminator)
+ # And switch to our new pref
+ switchToPref(self, curPref)
+ lastPref = curPref
+ # And the actual spec
+ specs.append(specTemplate % getDataTuple(member))
+ specs.append(specTerminator)
+ prefableSpecs.append(" { false, NULL }");
+
+ arrays = (("static %s %s_specs[] = {\n" +
+ ',\n'.join(specs) + "\n" +
+ "};\n\n" +
+ "static Prefable<%s> %s[] = {\n" +
+ ',\n'.join(prefableSpecs) + "\n" +
+ "};\n\n") % (specType, name, specType, name))
+ if doIdArrays:
+ arrays += ("static jsid %s_ids[%i] = { JSID_VOID };\n\n" %
+ (name, len(specs)))
+ return arrays
+
+
+# The length of a method is the maximum of the lengths of the
+# argument lists of all its overloads.
+def methodLength(method):
+ signatures = method.signatures()
+ return max([len(arguments) for (retType, arguments) in signatures])
+
+class MethodDefiner(PropertyDefiner):
+ """
+ A class for defining methods on a prototype object.
+ """
+ def __init__(self, descriptor, name, static):
+ PropertyDefiner.__init__(self, descriptor, name)
+
+ # FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=772822
+ # We should be able to check for special operations without an
+ # identifier. For now we check if the name starts with __
+ methods = [m for m in descriptor.interface.members if
+ m.isMethod() and m.isStatic() == static and
+ not m.isIdentifierLess()]
+ self.chrome = [{"name": m.identifier.name,
+ "length": methodLength(m),
+ "flags": "JSPROP_ENUMERATE",
+ "pref": PropertyDefiner.getControllingPref(m) }
+ for m in methods]
+ self.regular = [{"name": m.identifier.name,
+ "length": methodLength(m),
+ "flags": "JSPROP_ENUMERATE",
+ "pref": PropertyDefiner.getControllingPref(m) }
+ for m in methods if not isChromeOnly(m)]
+
+ # FIXME Check for an existing iterator on the interface first.
+ if any(m.isGetter() and m.isIndexed() for m in methods):
+ self.chrome.append({"name": 'iterator',
+ "methodInfo": False,
+ "nativeName": "JS_ArrayIterator",
+ "length": 0,
+ "flags": "JSPROP_ENUMERATE",
+ "pref": None })
+ self.regular.append({"name": 'iterator',
+ "methodInfo": False,
+ "nativeName": "JS_ArrayIterator",
+ "length": 0,
+ "flags": "JSPROP_ENUMERATE",
+ "pref": None })
+
+ if not descriptor.interface.parent and not static and not descriptor.workers:
+ self.chrome.append({"name": 'QueryInterface',
+ "methodInfo": False,
+ "length": 1,
+ "flags": "0",
+ "pref": None })
+
+ if static:
+ if not descriptor.interface.hasInterfaceObject():
+ # static methods go on the interface object
+ assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
+ else:
+ if not descriptor.interface.hasInterfacePrototypeObject():
+ # non-static methods go on the interface prototype object
+ assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
+
+ def generateArray(self, array, name, doIdArrays):
+ if len(array) == 0:
+ return ""
+
+ def pref(m):
+ return m["pref"]
+
+ def specData(m):
+ if m.get("methodInfo", True):
+ jitinfo = ("&%s_methodinfo" % m["name"])
+ accessor = "genericMethod"
+ else:
+ jitinfo = "nullptr"
+ accessor = m.get("nativeName", m["name"])
+ return (m["name"], accessor, jitinfo, m["length"], m["flags"])
+
+ return self.generatePrefableArray(
+ array, name,
+ ' JS_FNINFO("%s", %s, %s, %s, %s)',
+ ' JS_FS_END',
+ 'JSFunctionSpec',
+ pref, specData, doIdArrays)
+
+class AttrDefiner(PropertyDefiner):
+ def __init__(self, descriptor, name):
+ PropertyDefiner.__init__(self, descriptor, name)
+ self.name = name
+ self.chrome = [m for m in descriptor.interface.members if m.isAttr()]
+ self.regular = [m for m in self.chrome if not isChromeOnly(m)]
+
+ def generateArray(self, array, name, doIdArrays):
+ if len(array) == 0:
+ return ""
+
+ def flags(attr):
+ return "JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_NATIVE_ACCESSORS"
+
+ def getter(attr):
+ native = ("genericLenientGetter" if attr.hasLenientThis()
+ else "genericGetter")
+ return ("{(JSPropertyOp)%(native)s, &%(name)s_getterinfo}"
+ % {"name" : attr.identifier.name,
+ "native" : native})
+
+ def setter(attr):
+ if attr.readonly:
+ return "JSOP_NULLWRAPPER"
+ native = ("genericLenientSetter" if attr.hasLenientThis()
+ else "genericSetter")
+ return ("{(JSStrictPropertyOp)%(native)s, &%(name)s_setterinfo}"
+ % {"name" : attr.identifier.name,
+ "native" : native})
+
+ def specData(attr):
+ return (attr.identifier.name, flags(attr), getter(attr),
+ setter(attr))
+
+ return self.generatePrefableArray(
+ array, name,
+ ' { "%s", 0, %s, %s, %s}',
+ ' { 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER }',
+ 'JSPropertySpec',
+ PropertyDefiner.getControllingPref, specData, doIdArrays)
+
+class ConstDefiner(PropertyDefiner):
+ """
+ A class for definining constants on the interface object
+ """
+ def __init__(self, descriptor, name):
+ PropertyDefiner.__init__(self, descriptor, name)
+ self.name = name
+ self.chrome = [m for m in descriptor.interface.members if m.isConst()]
+ self.regular = [m for m in self.chrome if not isChromeOnly(m)]
+
+ def generateArray(self, array, name, doIdArrays):
+ if len(array) == 0:
+ return ""
+
+ def specData(const):
+ return (const.identifier.name,
+ convertConstIDLValueToJSVal(const.value))
+
+ return self.generatePrefableArray(
+ array, name,
+ ' { "%s", %s }',
+ ' { 0, JSVAL_VOID }',
+ 'ConstantSpec',
+ PropertyDefiner.getControllingPref, specData, doIdArrays)
+
+class PropertyArrays():
+ def __init__(self, descriptor):
+ self.staticMethods = MethodDefiner(descriptor, "StaticMethods", True)
+ self.methods = MethodDefiner(descriptor, "Methods", False)
+ self.attrs = AttrDefiner(descriptor, "Attributes")
+ self.consts = ConstDefiner(descriptor, "Constants")
+
+ @staticmethod
+ def arrayNames():
+ return [ "staticMethods", "methods", "attrs", "consts" ]
+
+ @staticmethod
+ def xrayRelevantArrayNames():
+ return [ "methods", "attrs", "consts" ]
+
+ def hasChromeOnly(self):
+ return reduce(lambda b, a: b or getattr(self, a).hasChromeOnly(),
+ self.arrayNames(), False)
+ def variableNames(self, chrome):
+ names = {}
+ for array in self.arrayNames():
+ names[array] = getattr(self, array).variableName(chrome)
+ return names
+ def __str__(self):
+ define = ""
+ for array in self.arrayNames():
+ define += str(getattr(self, array))
+ return define
+
+class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
+ """
+ Generate the CreateInterfaceObjects method for an interface descriptor.
+
+ properties should be a PropertyArrays instance.
+ """
+ def __init__(self, descriptor, properties):
+ args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aGlobal'),
+ Argument('JSObject*', 'aReceiver')]
+ CGAbstractMethod.__init__(self, descriptor, 'CreateInterfaceObjects', 'JSObject*', args)
+ self.properties = properties
+ def definition_body(self):
+ protoChain = self.descriptor.prototypeChain
+ if len(protoChain) == 1:
+ getParentProto = "JS_GetObjectPrototype(aCx, aGlobal)"
+ else:
+ parentProtoName = self.descriptor.prototypeChain[-2]
+ getParentProto = ("%s::GetProtoObject(aCx, aGlobal, aReceiver)" %
+ toBindingNamespace(parentProtoName))
+
+ needInterfaceObject = self.descriptor.interface.hasInterfaceObject()
+ needInterfacePrototypeObject = self.descriptor.interface.hasInterfacePrototypeObject()
+
+ # if we don't need to create anything, why are we generating this?
+ assert needInterfaceObject or needInterfacePrototypeObject
+
+ idsToInit = []
+ # There is no need to init any IDs in workers, because worker bindings
+ # don't have Xrays.
+ if not self.descriptor.workers:
+ for var in self.properties.xrayRelevantArrayNames():
+ props = getattr(self.properties, var)
+ # We only have non-chrome ids to init if we have no chrome ids.
+ if props.hasChromeOnly():
+ idsToInit.append(props.variableName(True))
+ elif props.hasNonChromeOnly():
+ idsToInit.append(props.variableName(False))
+ if len(idsToInit) > 0:
+ initIds = CGList(
+ [CGGeneric("!InitIds(aCx, %s, %s_ids)" % (varname, varname)) for
+ varname in idsToInit], ' ||\n')
+ if len(idsToInit) > 1:
+ initIds = CGWrapper(initIds, pre="(", post=")", reindent=True)
+ initIds = CGList(
+ [CGGeneric("%s_ids[0] == JSID_VOID &&" % idsToInit[0]), initIds],
+ "\n")
+ initIds = CGWrapper(initIds, pre="if (", post=") {", reindent=True)
+ initIds = CGList(
+ [initIds,
+ CGGeneric((" %s_ids[0] = JSID_VOID;\n"
+ " return NULL;") % idsToInit[0]),
+ CGGeneric("}")],
+ "\n")
+ else:
+ initIds = None
+
+ prefCacheData = []
+ for var in self.properties.arrayNames():
+ props = getattr(self.properties, var)
+ prefCacheData.extend(props.prefCacheData)
+ if len(prefCacheData) is not 0:
+ prefCacheData = [
+ CGGeneric('Preferences::AddBoolVarCache(%s, "%s");' % (ptr, pref)) for
+ (pref, ptr) in prefCacheData]
+ prefCache = CGWrapper(CGIndenter(CGList(prefCacheData, "\n")),
+ pre=("static bool sPrefCachesInited = false;\n"
+ "if (!sPrefCachesInited) {\n"
+ " sPrefCachesInited = true;\n"),
+ post="\n}")
+ else:
+ prefCache = None
+
+ getParentProto = ("JSObject* parentProto = %s;\n" +
+ "if (!parentProto) {\n" +
+ " return NULL;\n" +
+ "}\n") % getParentProto
+
+ needInterfaceObjectClass = (needInterfaceObject and
+ self.descriptor.hasInstanceInterface)
+ needConstructor = (needInterfaceObject and
+ not self.descriptor.hasInstanceInterface)
+ if self.descriptor.interface.ctor():
+ constructHook = CONSTRUCT_HOOK_NAME
+ constructArgs = methodLength(self.descriptor.interface.ctor())
+ else:
+ constructHook = "ThrowingConstructor"
+ constructArgs = 0
+
+ if self.descriptor.concrete:
+ if self.descriptor.proxy:
+ domClass = "&Class"
+ else:
+ domClass = "&Class.mClass"
+ else:
+ domClass = "nullptr"
+
+ call = """return dom::CreateInterfaceObjects(aCx, aGlobal, aReceiver, parentProto,
+ %s, %s, %s, %d,
+ %s,
+ %%(methods)s, %%(attrs)s,
+ %%(consts)s, %%(staticMethods)s,
+ %s);""" % (
+ "&PrototypeClass" if needInterfacePrototypeObject else "NULL",
+ "&InterfaceObjectClass" if needInterfaceObjectClass else "NULL",
+ constructHook if needConstructor else "NULL",
+ constructArgs,
+ domClass,
+ '"' + self.descriptor.interface.identifier.name + '"' if needInterfaceObject else "NULL")
+ if self.properties.hasChromeOnly():
+ if self.descriptor.workers:
+ accessCheck = "mozilla::dom::workers::GetWorkerPrivateFromContext(aCx)->IsChromeWorker()"
+ else:
+ accessCheck = "xpc::AccessCheck::isChrome(js::GetObjectCompartment(aGlobal))"
+ chrome = CGIfWrapper(CGGeneric(call % self.properties.variableNames(True)),
+ accessCheck)
+ chrome = CGWrapper(chrome, pre="\n\n")
+ else:
+ chrome = None
+
+ functionBody = CGList(
+ [CGGeneric(getParentProto), initIds, prefCache, chrome,
+ CGGeneric(call % self.properties.variableNames(False))],
+ "\n\n")
+ return CGIndenter(functionBody).define()
+
+class CGGetPerInterfaceObject(CGAbstractMethod):
+ """
+ A method for getting a per-interface object (a prototype object or interface
+ constructor object).
+ """
+ def __init__(self, descriptor, name, idPrefix=""):
+ args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aGlobal'),
+ Argument('JSObject*', 'aReceiver')]
+ CGAbstractMethod.__init__(self, descriptor, name,
+ 'JSObject*', args, inline=True)
+ self.id = idPrefix + "id::" + self.descriptor.name
+ def definition_body(self):
+ return """
+
+ /* aGlobal and aReceiver are usually the same, but they can be different
+ too. For example a sandbox often has an xray wrapper for a window as the
+ prototype of the sandbox's global. In that case aReceiver is the xray
+ wrapper and aGlobal is the sandbox's global.
+ */
+
+ /* Make sure our global is sane. Hopefully we can remove this sometime */
+ if (!(js::GetObjectClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL)) {
+ return NULL;
+ }
+ /* Check to see whether the interface objects are already installed */
+ JSObject** protoOrIfaceArray = GetProtoOrIfaceArray(aGlobal);
+ JSObject* cachedObject = protoOrIfaceArray[%s];
+ if (!cachedObject) {
+ protoOrIfaceArray[%s] = cachedObject = CreateInterfaceObjects(aCx, aGlobal, aReceiver);
+ }
+
+ /* cachedObject might _still_ be null, but that's OK */
+ return cachedObject;""" % (self.id, self.id)
+
+class CGGetProtoObjectMethod(CGGetPerInterfaceObject):
+ """
+ A method for getting the interface prototype object.
+ """
+ def __init__(self, descriptor):
+ CGGetPerInterfaceObject.__init__(self, descriptor, "GetProtoObject",
+ "prototypes::")
+ def definition_body(self):
+ return """
+ /* Get the interface prototype object for this class. This will create the
+ object as needed. */""" + CGGetPerInterfaceObject.definition_body(self)
+
+class CGGetConstructorObjectMethod(CGGetPerInterfaceObject):
+ """
+ A method for getting the interface constructor object.
+ """
+ def __init__(self, descriptor):
+ CGGetPerInterfaceObject.__init__(self, descriptor, "GetConstructorObject",
+ "constructors::")
+ def definition_body(self):
+ return """
+ /* Get the interface object for this class. This will create the object as
+ needed. */""" + CGGetPerInterfaceObject.definition_body(self)
+
+def CheckPref(descriptor, globalName, varName, retval, wrapperCache = None):
+ """
+ Check whether bindings should be enabled for this descriptor. If not, set
+ varName to false and return retval.
+ """
+ if not descriptor.prefable:
+ return ""
+
+ if wrapperCache:
+ wrapperCache = " %s->ClearIsDOMBinding();\n" % (wrapperCache)
+ else:
+ wrapperCache = ""
+
+ failureCode = (" %s = false;\n" +
+ " return %s;") % (varName, retval)
+ return """
+ {
+ XPCWrappedNativeScope* scope =
+ XPCWrappedNativeScope::FindInJSObjectScope(aCx, %s);
+ if (!scope) {
+%s
+ }
+
+ if (!scope->ExperimentalBindingsEnabled()) {
+%s%s
+ }
+ }
+""" % (globalName, failureCode, wrapperCache, failureCode)
+
+class CGDefineDOMInterfaceMethod(CGAbstractMethod):
+ """
+ A method for resolve hooks to try to lazily define the interface object for
+ a given interface.
+ """
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aReceiver'),
+ Argument('bool*', 'aEnabled')]
+ CGAbstractMethod.__init__(self, descriptor, 'DefineDOMInterface', 'bool', args)
+
+ def declare(self):
+ if self.descriptor.workers:
+ return ''
+ return CGAbstractMethod.declare(self)
+
+ def define(self):
+ if self.descriptor.workers:
+ return ''
+ return CGAbstractMethod.define(self)
+
+ def definition_body(self):
+ if self.descriptor.interface.hasInterfacePrototypeObject():
+ # We depend on GetProtoObject defining an interface constructor
+ # object as needed.
+ getter = "GetProtoObject"
+ else:
+ getter = "GetConstructorObject"
+
+ return (" JSObject* global = JS_GetGlobalForObject(aCx, aReceiver);\n" +
+ CheckPref(self.descriptor, "global", "*aEnabled", "false") +
+ """
+ *aEnabled = true;
+ return !!%s(aCx, global, aReceiver);""" % (getter))
+
+class CGPrefEnabled(CGAbstractMethod):
+ """
+ A method for testing whether the preference controlling this
+ interface is enabled. When it's not, the interface should not be
+ visible on the global.
+ """
+ def __init__(self, descriptor):
+ CGAbstractMethod.__init__(self, descriptor, 'PrefEnabled', 'bool', [])
+
+ def declare(self):
+ return CGAbstractMethod.declare(self)
+
+ def define(self):
+ return CGAbstractMethod.define(self)
+
+ def definition_body(self):
+ return " return %s::PrefEnabled();" % self.descriptor.nativeType
+
+class CGIsMethod(CGAbstractMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSObject*', 'obj')]
+ CGAbstractMethod.__init__(self, descriptor, 'Is', 'bool', args)
+
+ def definition_body(self):
+ # Non-proxy implementation would check
+ # js::GetObjectJSClass(obj) == &Class.mBase
+ return """ return IsProxy(obj);"""
+
+def CreateBindingJSObject(descriptor, parent):
+ if descriptor.proxy:
+ create = """ JSObject *obj = NewProxyObject(aCx, DOMProxyHandler::getInstance(),
+ JS::PrivateValue(aObject), proto, %s);
+ if (!obj) {
+ return NULL;
+ }
+
+"""
+ else:
+ create = """ JSObject* obj = JS_NewObject(aCx, &Class.mBase, proto, %s);
+ if (!obj) {
+ return NULL;
+ }
+
+ js::SetReservedSlot(obj, DOM_OBJECT_SLOT, PRIVATE_TO_JSVAL(aObject));
+"""
+ return create % parent
+
+class CGWrapWithCacheMethod(CGAbstractMethod):
+ def __init__(self, descriptor):
+ assert descriptor.interface.hasInterfacePrototypeObject()
+ args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aScope'),
+ Argument(descriptor.nativeType + '*', 'aObject'),
+ Argument('nsWrapperCache*', 'aCache'),
+ Argument('bool*', 'aTriedToWrap')]
+ CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'JSObject*', args)
+
+ def definition_body(self):
+ if self.descriptor.workers:
+ return """ *aTriedToWrap = true;
+ return aObject->GetJSObject();"""
+
+ return """ *aTriedToWrap = true;
+
+ JSObject* parent = WrapNativeParent(aCx, aScope, aObject->GetParentObject());
+ if (!parent) {
+ return NULL;
+ }
+
+ JSAutoCompartment ac(aCx, parent);
+ JSObject* global = JS_GetGlobalForObject(aCx, parent);
+%s
+ JSObject* proto = GetProtoObject(aCx, global, global);
+ if (!proto) {
+ return NULL;
+ }
+
+%s
+ NS_ADDREF(aObject);
+
+ aCache->SetWrapper(obj);
+
+ return obj;""" % (CheckPref(self.descriptor, "global", "*aTriedToWrap", "NULL", "aCache"),
+ CreateBindingJSObject(self.descriptor, "parent"))
+
+class CGWrapMethod(CGAbstractMethod):
+ def __init__(self, descriptor):
+ # XXX can we wrap if we don't have an interface prototype object?
+ assert descriptor.interface.hasInterfacePrototypeObject()
+ args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aScope'),
+ Argument('T*', 'aObject'), Argument('bool*', 'aTriedToWrap')]
+ CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'JSObject*', args, inline=True, templateArgs=["class T"])
+
+ def definition_body(self):
+ return " return Wrap(aCx, aScope, aObject, aObject, aTriedToWrap);"
+
+class CGWrapNonWrapperCacheMethod(CGAbstractMethod):
+ def __init__(self, descriptor):
+ # XXX can we wrap if we don't have an interface prototype object?
+ assert descriptor.interface.hasInterfacePrototypeObject()
+ args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aScope'),
+ Argument(descriptor.nativeType + '*', 'aObject')]
+ CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'JSObject*', args)
+
+ def definition_body(self):
+ return """
+ JSObject* global = JS_GetGlobalForObject(aCx, aScope);
+ JSObject* proto = GetProtoObject(aCx, global, global);
+ if (!proto) {
+ return NULL;
+ }
+
+%s
+ NS_ADDREF(aObject);
+
+ return obj;""" % CreateBindingJSObject(self.descriptor, "global")
+
+builtinNames = {
+ IDLType.Tags.bool: 'bool',
+ IDLType.Tags.int8: 'int8_t',
+ IDLType.Tags.int16: 'int16_t',
+ IDLType.Tags.int32: 'int32_t',
+ IDLType.Tags.int64: 'int64_t',
+ IDLType.Tags.uint8: 'uint8_t',
+ IDLType.Tags.uint16: 'uint16_t',
+ IDLType.Tags.uint32: 'uint32_t',
+ IDLType.Tags.uint64: 'uint64_t',
+ IDLType.Tags.float: 'float',
+ IDLType.Tags.double: 'double'
+}
+
+numericTags = [
+ IDLType.Tags.int8, IDLType.Tags.uint8,
+ IDLType.Tags.int16, IDLType.Tags.uint16,
+ IDLType.Tags.int32, IDLType.Tags.uint32,
+ IDLType.Tags.int64, IDLType.Tags.uint64,
+ IDLType.Tags.float, IDLType.Tags.double
+ ]
+
+class CastableObjectUnwrapper():
+ """
+ A class for unwrapping an object named by the "source" argument
+ based on the passed-in descriptor and storing it in a variable
+ called by the name in the "target" argument.
+
+ codeOnFailure is the code to run if unwrapping fails.
+ """
+ def __init__(self, descriptor, source, target, codeOnFailure):
+ assert descriptor.castable
+
+ self.substitution = { "type" : descriptor.nativeType,
+ "protoID" : "prototypes::id::" + descriptor.name,
+ "source" : source,
+ "target" : target,
+ "codeOnFailure" : CGIndenter(CGGeneric(codeOnFailure), 4).define() }
+ if descriptor.hasXPConnectImpls:
+ # We don't use xpc_qsUnwrapThis because it will always throw on
+ # unwrap failure, whereas we want to control whether we throw or
+ # not.
+ self.substitution["codeOnFailure"] = CGIndenter(CGGeneric(string.Template(
+ "${type} *objPtr;\n"
+ "xpc_qsSelfRef objRef;\n"
+ "JS::Value val = JS::ObjectValue(*${source});\n"
+ "nsresult rv = xpc_qsUnwrapArg<${type}>(cx, val, &objPtr, &objRef.ptr, &val);\n"
+ "if (NS_FAILED(rv)) {\n"
+ "${codeOnFailure}\n"
+ "}\n"
+ "// We should be castable!\n"
+ "MOZ_ASSERT(!objRef.ptr);\n"
+ "// We should have an object, too!\n"
+ "MOZ_ASSERT(objPtr);\n"
+ "${target} = objPtr;").substitute(self.substitution)), 4).define()
+
+ def __str__(self):
+ return string.Template(
+"""{
+ nsresult rv = UnwrapObject<${protoID}, ${type}>(cx, ${source}, ${target});
+ if (NS_FAILED(rv)) {
+${codeOnFailure}
+ }
+}""").substitute(self.substitution)
+
+class FailureFatalCastableObjectUnwrapper(CastableObjectUnwrapper):
+ """
+ As CastableObjectUnwrapper, but defaulting to throwing if unwrapping fails
+ """
+ def __init__(self, descriptor, source, target):
+ CastableObjectUnwrapper.__init__(self, descriptor, source, target,
+ "return Throw<%s>(cx, rv);" %
+ toStringBool(not descriptor.workers))
+
+class CallbackObjectUnwrapper:
+ """
+ A class for unwrapping objects implemented in JS.
+
+ |source| is the JSObject we want to use in native code.
+ |target| is an nsCOMPtr of the appropriate type in which we store the result.
+ """
+ def __init__(self, descriptor, source, target, codeOnFailure=None):
+ if codeOnFailure is None:
+ codeOnFailure = ("return Throw<%s>(cx, rv);" %
+ toStringBool(not descriptor.workers))
+ self.descriptor = descriptor
+ self.substitution = { "nativeType" : descriptor.nativeType,
+ "source" : source,
+ "target" : target,
+ "codeOnFailure" : CGIndenter(CGGeneric(codeOnFailure)).define() }
+
+ def __str__(self):
+ if self.descriptor.workers:
+ return string.Template(
+ "${target} = ${source};"
+ ).substitute(self.substitution)
+
+ return string.Template(
+ """nsresult rv;
+XPCCallContext ccx(JS_CALLER, cx);
+if (!ccx.IsValid()) {
+ rv = NS_ERROR_XPC_BAD_CONVERT_JS;
+${codeOnFailure}
+}
+
+const nsIID& iid = NS_GET_IID(${nativeType});
+nsRefPtr<nsXPCWrappedJS> wrappedJS;
+rv = nsXPCWrappedJS::GetNewOrUsed(ccx, ${source}, iid,
+ NULL, getter_AddRefs(wrappedJS));
+if (NS_FAILED(rv) || !wrappedJS) {
+${codeOnFailure}
+}
+
+// Use a temp nsCOMPtr for the null-check, because ${target} might be
+// OwningNonNull, not an nsCOMPtr.
+nsCOMPtr<${nativeType}> tmp = do_QueryObject(wrappedJS.get());
+if (!tmp) {
+${codeOnFailure}
+}
+${target} = tmp.forget();""").substitute(self.substitution)
+
+def dictionaryHasSequenceMember(dictionary):
+ return (any(typeIsSequenceOrHasSequenceMember(m.type) for m in
+ dictionary.members) or
+ (dictionary.parent and
+ dictionaryHasSequenceMember(dictionary.parent)))
+
+def typeIsSequenceOrHasSequenceMember(type):
+ if type.nullable():
+ type = type.inner
+ if type.isSequence():
+ return True
+ if type.isArray():
+ elementType = type.inner
+ return typeIsSequenceOrHasSequenceMember(elementType)
+ if type.isDictionary():
+ return dictionaryHasSequenceMember(type.inner)
+ if type.isUnion():
+ return any(typeIsSequenceOrHasSequenceMember(m.type) for m in
+ type.flatMemberTypes)
+ return False
+
+def getJSToNativeConversionTemplate(type, descriptorProvider, failureCode=None,
+ isDefinitelyObject=False,
+ isMember=False,
+ isOptional=False,
+ invalidEnumValueFatal=True,
+ defaultValue=None,
+ treatNullAs="Default",
+ treatUndefinedAs="Default",
+ isEnforceRange=False,
+ isClamp=False):
+ """
+ Get a template for converting a JS value to a native object based on the
+ given type and descriptor. If failureCode is given, then we're actually
+ testing whether we can convert the argument to the desired type. That
+ means that failures to convert due to the JS value being the wrong type of
+ value need to use failureCode instead of throwing exceptions. Failures to
+ convert that are due to JS exceptions (from toString or valueOf methods) or
+ out of memory conditions need to throw exceptions no matter what
+ failureCode is.
+
+ If isDefinitelyObject is True, that means we know the value
+ isObject() and we have no need to recheck that.
+
+ if isMember is True, we're being converted from a property of some
+ JS object, not from an actual method argument, so we can't rely on
+ our jsval being rooted or outliving us in any way. Any caller
+ passing true needs to ensure that it is handled correctly in
+ typeIsSequenceOrHasSequenceMember.
+
+ If isOptional is true, then we are doing conversion of an optional
+ argument with no default value.
+
+ invalidEnumValueFatal controls whether an invalid enum value conversion
+ attempt will throw (if true) or simply return without doing anything (if
+ false).
+
+ If defaultValue is not None, it's the IDL default value for this conversion
+
+ If isEnforceRange is true, we're converting an integer and throwing if the
+ value is out of range.
+
+ If isClamp is true, we're converting an integer and clamping if the
+ value is out of range.
+
+ The return value from this function is a tuple consisting of four things:
+
+ 1) A string representing the conversion code. This will have template
+ substitution performed on it as follows:
+
+ ${val} replaced by an expression for the JS::Value in question
+ ${valPtr} is a pointer to the JS::Value in question
+ ${holderName} replaced by the holder's name, if any
+ ${declName} replaced by the declaration's name
+ ${haveValue} replaced by an expression that evaluates to a boolean
+ for whether we have a JS::Value. Only used when
+ defaultValue is not None.
+
+ 2) A CGThing representing the native C++ type we're converting to
+ (declType). This is allowed to be None if the conversion code is
+ supposed to be used as-is.
+ 3) A CGThing representing the type of a "holder" (holderType) which will
+ hold a possible reference to the C++ thing whose type we returned in #1,
+ or None if no such holder is needed.
+ 4) A boolean indicating whether the caller has to do optional-argument handling.
+ This will only be true if isOptional is true and if the returned template
+ expects both declType and holderType to be wrapped in Optional<>, with
+ ${declName} and ${holderName} adjusted to point to the Value() of the
+ Optional, and Construct() calls to be made on the Optional<>s as needed.
+
+ ${declName} must be in scope before the generated code is entered.
+
+ If holderType is not None then ${holderName} must be in scope
+ before the generated code is entered.
+ """
+ # If we have a defaultValue then we're not actually optional for
+ # purposes of what we need to be declared as.
+ assert(defaultValue is None or not isOptional)
+
+ # Also, we should not have a defaultValue if we know we're an object
+ assert(not isDefinitelyObject or defaultValue is None)
+
+ # Helper functions for dealing with failures due to the JS value being the
+ # wrong type of value
+ def onFailureNotAnObject(failureCode):
+ return CGWrapper(CGGeneric(
+ failureCode or
+ 'return ThrowErrorMessage(cx, MSG_NOT_OBJECT);'), post="\n")
+ def onFailureBadType(failureCode, typeName):
+ return CGWrapper(CGGeneric(
+ failureCode or
+ 'return ThrowErrorMessage(cx, MSG_DOES_NOT_IMPLEMENT_INTERFACE, "%s");' % typeName), post="\n")
+
+ # A helper function for handling default values. Takes a template
+ # body and the C++ code to set the default value and wraps the
+ # given template body in handling for the default value.
+ def handleDefault(template, setDefault):
+ if defaultValue is None:
+ return template
+ return CGWrapper(
+ CGIndenter(CGGeneric(template)),
+ pre="if (${haveValue}) {\n",
+ post=("\n"
+ "} else {\n"
+ "%s;\n"
+ "}" %
+ CGIndenter(CGGeneric(setDefault)).define())).define()
+
+ # A helper function for handling null default values. Much like
+ # handleDefault, but checks that the default value, if it exists, is null.
+ def handleDefaultNull(template, codeToSetNull):
+ if (defaultValue is not None and
+ not isinstance(defaultValue, IDLNullValue)):
+ raise TypeError("Can't handle non-null default value here")
+ return handleDefault(template, codeToSetNull)
+
+ # A helper function for wrapping up the template body for
+ # possibly-nullable objecty stuff
+ def wrapObjectTemplate(templateBody, isDefinitelyObject, type,
+ codeToSetNull, failureCode=None):
+ if not isDefinitelyObject:
+ # Handle the non-object cases by wrapping up the whole
+ # thing in an if cascade.
+ templateBody = (
+ "if (${val}.isObject()) {\n" +
+ CGIndenter(CGGeneric(templateBody)).define() + "\n")
+ if type.nullable():
+ templateBody += (
+ "} else if (${val}.isNullOrUndefined()) {\n"
+ " %s;\n" % codeToSetNull)
+ templateBody += (
+ "} else {\n" +
+ CGIndenter(onFailureNotAnObject(failureCode)).define() +
+ "}")
+ if type.nullable():
+ templateBody = handleDefaultNull(templateBody, codeToSetNull)
+ else:
+ assert(defaultValue is None)
+
+ return templateBody
+
+ assert not (isEnforceRange and isClamp) # These are mutually exclusive
+
+ if type.isArray():
+ raise TypeError("Can't handle array arguments yet")
+
+ if type.isSequence():
+ assert not isEnforceRange and not isClamp
+
+ if failureCode is not None:
+ raise TypeError("Can't handle sequences when failureCode is not None")
+ nullable = type.nullable();
+ # Be very careful not to change "type": we need it later
+ if nullable:
+ elementType = type.inner.inner
+ else:
+ elementType = type.inner
+
+ # We have to be careful with reallocation behavior for arrays. In
+ # particular, if we have a sequence of elements which are themselves
+ # sequences (so nsAutoTArrays) or have sequences as members, we have a
+ # problem. In that case, resizing the outermost nsAutoTarray to the
+ # right size will memmove its elements, but nsAutoTArrays are not
+ # memmovable and hence will end up with pointers to bogus memory, which
+ # is bad. To deal with this, we disallow sequences, arrays,
+ # dictionaries, and unions which contain sequences as sequence item
+ # types. If WebIDL ever adds another container type, we'd have to
+ # disallow it as well.
+ if typeIsSequenceOrHasSequenceMember(elementType):
+ raise TypeError("Can't handle a sequence containing another "
+ "sequence as an element or member of an element. "
+ "See the big comment explaining why.\n%s" %
+ str(type.location))
+
+ (elementTemplate, elementDeclType,
+ elementHolderType, dealWithOptional) = getJSToNativeConversionTemplate(
+ elementType, descriptorProvider, isMember=True)
+ if dealWithOptional:
+ raise TypeError("Shouldn't have optional things in sequences")
+ if elementHolderType is not None:
+ raise TypeError("Shouldn't need holders for sequences")
+
+ typeName = CGWrapper(elementDeclType, pre="Sequence< ", post=" >")
+ if nullable:
+ typeName = CGWrapper(typeName, pre="Nullable< ", post=" >")
+ arrayRef = "${declName}.Value()"
+ else:
+ arrayRef = "${declName}"
+ # If we're optional, the const will come from the Optional
+ mutableTypeName = typeName
+ if not isOptional:
+ typeName = CGWrapper(typeName, pre="const ")
+
+ templateBody = ("""JSObject* seq = &${val}.toObject();\n
+if (!IsArrayLike(cx, seq)) {
+ return Throw<%s>(cx, NS_ERROR_XPC_BAD_CONVERT_JS);
+}
+uint32_t length;
+// JS_GetArrayLength actually works on all objects
+if (!JS_GetArrayLength(cx, seq, &length)) {
+ return false;
+}
+Sequence< %s > &arr = const_cast< Sequence< %s >& >(%s);
+if (!arr.SetCapacity(length)) {
+ return Throw<%s>(cx, NS_ERROR_OUT_OF_MEMORY);
+}
+for (uint32_t i = 0; i < length; ++i) {
+ jsval temp;
+ if (!JS_GetElement(cx, seq, i, &temp)) {
+ return false;
+ }
+""" % (toStringBool(descriptorProvider.workers),
+ elementDeclType.define(),
+ elementDeclType.define(),
+ arrayRef,
+ toStringBool(descriptorProvider.workers)))
+
+ templateBody += CGIndenter(CGGeneric(
+ string.Template(elementTemplate).substitute(
+ {
+ "val" : "temp",
+ "valPtr": "&temp",
+ "declName" : "(*arr.AppendElement())"
+ }
+ ))).define()
+
+ templateBody += "\n}"
+ templateBody = wrapObjectTemplate(templateBody, isDefinitelyObject,
+ type,
+ "const_cast< %s & >(${declName}).SetNull()" % mutableTypeName.define())
+ return (templateBody, typeName, None, isOptional)
+
+ if type.isUnion():
+ if isMember:
+ raise TypeError("Can't handle unions as members, we have a "
+ "holderType")
+ nullable = type.nullable();
+ if nullable:
+ type = type.inner
+
+ assert(defaultValue is None or
+ (isinstance(defaultValue, IDLNullValue) and nullable))
+
+ unionArgumentObj = "${holderName}"
+ if isOptional or nullable:
+ unionArgumentObj += ".ref()"
+
+ memberTypes = type.flatMemberTypes
+ names = []
+
+ interfaceMemberTypes = filter(lambda t: t.isNonCallbackInterface(), memberTypes)
+ if len(interfaceMemberTypes) > 0:
+ interfaceObject = []
+ for memberType in interfaceMemberTypes:
+ if type.isGeckoInterface():
+ name = memberType.inner.identifier.name
+ else:
+ name = memberType.name
+ interfaceObject.append(CGGeneric("(failed = !%s.TrySetTo%s(cx, ${val}, ${valPtr}, tryNext)) || !tryNext" % (unionArgumentObj, name)))
+ names.append(name)
+ interfaceObject = CGWrapper(CGList(interfaceObject, " ||\n"), pre="done = ", post=";\n", reindent=True)
+ else:
+ interfaceObject = None
+
+ arrayObjectMemberTypes = filter(lambda t: t.isArray() or t.isSequence(), memberTypes)
+ if len(arrayObjectMemberTypes) > 0:
+ assert len(arrayObjectMemberTypes) == 1
+ memberType = arrayObjectMemberTypes[0]
+ name = memberType.name
+ arrayObject = CGGeneric("done = (failed = !%s.TrySetTo%s(cx, ${val}, ${valPtr}, tryNext)) || !tryNext;" % (unionArgumentObj, name))
+ # XXX Now we're supposed to check for an array or a platform object
+ # that supports indexed properties... skip that last for now. It's a
+ # bit of a pain.
+ arrayObject = CGWrapper(CGIndenter(arrayObject),
+ pre="if (IsArrayLike(cx, &argObj)) {\n",
+ post="}")
+ names.append(name)
+ else:
+ arrayObject = None
+
+ dateObjectMemberTypes = filter(lambda t: t.isDate(), memberTypes)
+ if len(dateObjectMemberTypes) > 0:
+ assert len(dateObjectMemberTypes) == 1
+ memberType = dateObjectMemberTypes[0]
+ name = memberType.name
+ dateObject = CGGeneric("%s.SetTo%s(cx, ${val}, ${valPtr});\n"
+ "done = true;" % (unionArgumentObj, name))
+ dateObject = CGWrapper(CGIndenter(dateObject),
+ pre="if (JS_ObjectIsDate(cx, &argObj)) {\n",
+ post="\n}")
+ names.append(name)
+ else:
+ dateObject = None
+
+ callbackMemberTypes = filter(lambda t: t.isCallback() or t.isCallbackInterface(), memberTypes)
+ if len(callbackMemberTypes) > 0:
+ assert len(callbackMemberTypes) == 1
+ memberType = callbackMemberTypes[0]
+ name = memberType.name
+ callbackObject = CGGeneric("done = (failed = !%s.TrySetTo%s(cx, ${val}, ${valPtr}, tryNext)) || !tryNext;" % (unionArgumentObj, name))
+ names.append(name)
+ else:
+ callbackObject = None
+
+ dictionaryMemberTypes = filter(lambda t: t.isDictionary(), memberTypes)
+ if len(dictionaryMemberTypes) > 0:
+ raise TypeError("No support for unwrapping dictionaries as member "
+ "of a union")
+ else:
+ dictionaryObject = None
+
+ if callbackObject or dictionaryObject:
+ nonPlatformObject = CGList([callbackObject, dictionaryObject], "\n")
+ nonPlatformObject = CGWrapper(CGIndenter(nonPlatformObject),
+ pre="if (!IsPlatformObject(cx, &argObj)) {\n",
+ post="\n}")
+ else:
+ nonPlatformObject = None
+
+ objectMemberTypes = filter(lambda t: t.isObject(), memberTypes)
+ if len(objectMemberTypes) > 0:
+ object = CGGeneric("%s.SetToObject(&argObj);\n"
+ "done = true;" % unionArgumentObj)
+ else:
+ object = None
+
+ hasObjectTypes = interfaceObject or arrayObject or dateObject or nonPlatformObject or object
+ if hasObjectTypes:
+ # If we try more specific object types first then we need to check
+ # whether that succeeded before converting to object.
+ if object and (interfaceObject or arrayObject or dateObject or nonPlatformObject):
+ object = CGWrapper(CGIndenter(object), pre="if (!done) {\n",
+ post=("\n}"))
+
+ if arrayObject or dateObject or nonPlatformObject:
+ # An object can be both an array object and not a platform
+ # object, but we shouldn't have both in the union's members
+ # because they are not distinguishable.
+ assert not (arrayObject and nonPlatformObject)
+ templateBody = CGList([arrayObject, dateObject, nonPlatformObject], " else ")
+ else:
+ templateBody = None
+ if interfaceObject:
+ if templateBody:
+ templateBody = CGList([templateBody, object], "\n")
+ templateBody = CGWrapper(CGIndenter(templateBody),
+ pre="if (!done) {\n", post=("\n}"))
+ templateBody = CGList([interfaceObject, templateBody], "\n")
+ else:
+ templateBody = CGList([templateBody, object], "\n")
+
+ if any([arrayObject, dateObject, nonPlatformObject, object]):
+ templateBody.prepend(CGGeneric("JSObject& argObj = ${val}.toObject();"))
+ templateBody = CGWrapper(CGIndenter(templateBody),
+ pre="if (${val}.isObject()) {\n",
+ post="\n}")
+ else:
+ templateBody = CGGeneric()
+
+ otherMemberTypes = filter(lambda t: t.isString() or t.isEnum(),
+ memberTypes)
+ otherMemberTypes.extend(t for t in memberTypes if t.isPrimitive())
+ if len(otherMemberTypes) > 0:
+ assert len(otherMemberTypes) == 1
+ memberType = otherMemberTypes[0]
+ if memberType.isEnum():
+ name = memberType.inner.identifier.name
+ else:
+ name = memberType.name
+ other = CGGeneric("done = (failed = !%s.TrySetTo%s(cx, ${val}, ${valPtr}, tryNext)) || !tryNext;" % (unionArgumentObj, name))
+ names.append(name)
+ if hasObjectTypes:
+ other = CGWrapper(CGIndenter(other), "{\n", post="\n}")
+ if object:
+ join = " else "
+ else:
+ other = CGWrapper(other, pre="if (!done) ")
+ join = "\n"
+ templateBody = CGList([templateBody, other], join)
+ else:
+ other = None
+
+ templateBody = CGWrapper(templateBody, pre="bool done = false, failed = false, tryNext;\n")
+ throw = CGGeneric("if (failed) {\n"
+ " return false;\n"
+ "}\n"
+ "if (!done) {\n"
+ " return ThrowErrorMessage(cx, MSG_NOT_IN_UNION, \"%s\");\n"
+ "}" % ", ".join(names))
+ templateBody = CGWrapper(CGIndenter(CGList([templateBody, throw], "\n")), pre="{\n", post="\n}")
+
+ typeName = type.name
+ argumentTypeName = typeName + "Argument"
+ if nullable:
+ typeName = "Nullable<" + typeName + " >"
+ if isOptional:
+ nonConstDecl = "const_cast<Optional<" + typeName + " >& >(${declName})"
+ else:
+ nonConstDecl = "const_cast<" + typeName + "& >(${declName})"
+ typeName = "const " + typeName
+
+ def handleNull(templateBody, setToNullVar, extraConditionForNull=""):
+ null = CGGeneric("if (%s${val}.isNullOrUndefined()) {\n"
+ " %s.SetNull();\n"
+ "}" % (extraConditionForNull, setToNullVar))
+ templateBody = CGWrapper(CGIndenter(templateBody), pre="{\n", post="\n}")
+ return CGList([null, templateBody], " else ")
+
+ if type.hasNullableType:
+ templateBody = handleNull(templateBody, unionArgumentObj)
+
+ declType = CGGeneric(typeName)
+ holderType = CGGeneric(argumentTypeName)
+ if isOptional:
+ mutableDecl = nonConstDecl + ".Value()"
+ declType = CGWrapper(declType, pre="const Optional<", post=" >")
+ holderType = CGWrapper(holderType, pre="Maybe<", post=" >")
+ constructDecl = CGGeneric(nonConstDecl + ".Construct();")
+ if nullable:
+ constructHolder = CGGeneric("${holderName}.construct(%s.SetValue());" % mutableDecl)
+ else:
+ constructHolder = CGGeneric("${holderName}.construct(${declName}.Value());")
+ else:
+ mutableDecl = nonConstDecl
+ constructDecl = None
+ if nullable:
+ holderType = CGWrapper(holderType, pre="Maybe<", post=" >")
+ constructHolder = CGGeneric("${holderName}.construct(%s.SetValue());" % mutableDecl)
+ else:
+ constructHolder = CGWrapper(holderType, post=" ${holderName}(${declName});")
+ holderType = None
+
+ templateBody = CGList([constructHolder, templateBody], "\n")
+ if nullable:
+ if defaultValue:
+ assert(isinstance(defaultValue, IDLNullValue))
+ valueMissing = "!(${haveValue}) || "
+ else:
+ valueMissing = ""
+ templateBody = handleNull(templateBody, mutableDecl,
+ extraConditionForNull=valueMissing)
+ templateBody = CGList([constructDecl, templateBody], "\n")
+
+ return templateBody.define(), declType, holderType, False
+
+ if type.isGeckoInterface():
+ assert not isEnforceRange and not isClamp
+
+ descriptor = descriptorProvider.getDescriptor(
+ type.unroll().inner.identifier.name)
+ # This is an interface that we implement as a concrete class
+ # or an XPCOM interface.
+
+ # Allow null pointers for nullable types and old-binding classes
+ argIsPointer = type.nullable() or type.unroll().inner.isExternal()
+
+ # Sequences and non-worker callbacks have to hold a strong ref to the
+ # thing being passed down.
+ forceOwningType = (descriptor.interface.isCallback() and
+ not descriptor.workers) or isMember
+
+ typeName = descriptor.nativeType
+ typePtr = typeName + "*"
+
+ # Compute a few things:
+ # - declType is the type we want to return as the first element of our
+ # tuple.
+ # - holderType is the type we want to return as the third element
+ # of our tuple.
+
+ # Set up some sensible defaults for these things insofar as we can.
+ holderType = None
+ if argIsPointer:
+ if forceOwningType:
+ declType = "nsRefPtr<" + typeName + ">"
+ else:
+ declType = typePtr
+ else:
+ if forceOwningType:
+ declType = "OwningNonNull<" + typeName + ">"
+ else:
+ declType = "NonNull<" + typeName + ">"
+
+ templateBody = ""
+ if descriptor.castable:
+ if descriptor.prefable:
+ raise TypeError("We don't support prefable castable object "
+ "arguments (like %s), because we don't know "
+ "how to handle them being preffed off" %
+ descriptor.interface.identifier.name)
+ if descriptor.interface.isConsequential():
+ raise TypeError("Consequential interface %s being used as an "
+ "argument but flagged as castable" %
+ descriptor.interface.identifier.name)
+ if failureCode is not None:
+ templateBody += str(CastableObjectUnwrapper(
+ descriptor,
+ "&${val}.toObject()",
+ "${declName}",
+ failureCode))
+ else:
+ templateBody += str(FailureFatalCastableObjectUnwrapper(
+ descriptor,
+ "&${val}.toObject()",
+ "${declName}"))
+ elif descriptor.interface.isCallback():
+ templateBody += str(CallbackObjectUnwrapper(
+ descriptor,
+ "&${val}.toObject()",
+ "${declName}",
+ codeOnFailure=failureCode))
+ elif descriptor.workers:
+ templateBody += "${declName} = &${val}.toObject();"
+ else:
+ # Either external, or new-binding non-castable. We always have a
+ # holder for these, because we don't actually know whether we have
+ # to addref when unwrapping or not. So we just pass an
+ # getter_AddRefs(nsRefPtr) to XPConnect and if we'll need a release
+ # it'll put a non-null pointer in there.
+ if forceOwningType:
+ # Don't return a holderType in this case; our declName
+ # will just own stuff.
+ templateBody += "nsRefPtr<" + typeName + "> ${holderName};\n"
+ else:
+ holderType = "nsRefPtr<" + typeName + ">"
+ templateBody += (
+ "jsval tmpVal = ${val};\n" +
+ typePtr + " tmp;\n"
+ "if (NS_FAILED(xpc_qsUnwrapArg<" + typeName + ">(cx, ${val}, &tmp, static_cast<" + typeName + "**>(getter_AddRefs(${holderName})), &tmpVal))) {\n")
+ templateBody += CGIndenter(onFailureBadType(failureCode,
+ descriptor.interface.identifier.name)).define()
+ templateBody += ("}\n"
+ "MOZ_ASSERT(tmp);\n")
+
+ if not isDefinitelyObject:
+ # Our tmpVal will go out of scope, so we can't rely on it
+ # for rooting
+ templateBody += (
+ "if (tmpVal != ${val} && !${holderName}) {\n"
+ " // We have to have a strong ref, because we got this off\n"
+ " // some random object that might get GCed\n"
+ " ${holderName} = tmp;\n"
+ "}\n")
+
+ # And store our tmp, before it goes out of scope.
+ templateBody += "${declName} = tmp;"
+
+ templateBody = wrapObjectTemplate(templateBody, isDefinitelyObject,
+ type, "${declName} = NULL",
+ failureCode)
+
+ declType = CGGeneric(declType)
+ if holderType is not None:
+ holderType = CGGeneric(holderType)
+ return (templateBody, declType, holderType, isOptional)
+
+ if type.isSpiderMonkeyInterface():
+ assert not isEnforceRange and not isClamp
+ if isMember:
+ raise TypeError("Can't handle member arraybuffers or "
+ "arraybuffer views because making sure all the "
+ "objects are properly rooted is hard")
+ name = type.name
+ # By default, we use a Maybe<> to hold our typed array. And in the optional
+ # non-nullable case we want to pass Optional<TypedArray> to consumers, not
+ # Optional<NonNull<TypedArray> >, so jump though some hoops to do that.
+ holderType = "Maybe<%s>" % name
+ constructLoc = "${holderName}"
+ constructMethod = "construct"
+ constructInternal = "ref"
+ if type.nullable():
+ if isOptional:
+ declType = "const Optional<" + name + "*>"
+ else:
+ declType = name + "*"
+ else:
+ if isOptional:
+ declType = "const Optional<" + name + ">"
+ # We don't need a holder in this case
+ holderType = None
+ constructLoc = "(const_cast<Optional<" + name + ">& >(${declName}))"
+ constructMethod = "Construct"
+ constructInternal = "Value"
+ else:
+ declType = "NonNull<" + name + ">"
+ template = (
+ "%s.%s(cx, &${val}.toObject());\n"
+ "if (!%s.%s().inited()) {\n"
+ "%s" # No newline here because onFailureBadType() handles that
+ "}\n" %
+ (constructLoc, constructMethod, constructLoc, constructInternal,
+ CGIndenter(onFailureBadType(failureCode, type.name)).define()))
+ nullableTarget = ""
+ if type.nullable():
+ if isOptional:
+ mutableDecl = "(const_cast<Optional<" + name + "*>& >(${declName}))"
+ template += "%s.Construct();\n" % mutableDecl
+ nullableTarget = "%s.Value()" % mutableDecl
+ else:
+ nullableTarget = "${declName}"
+ template += "%s = ${holderName}.addr();" % nullableTarget
+ elif not isOptional:
+ template += "${declName} = ${holderName}.addr();"
+ template = wrapObjectTemplate(template, isDefinitelyObject, type,
+ "%s = NULL" % nullableTarget,
+ failureCode)
+
+ if holderType is not None:
+ holderType = CGGeneric(holderType)
+ # We handle all the optional stuff ourselves; no need for caller to do it.
+ return (template, CGGeneric(declType), holderType, False)
+
+ if type.isString():
+ assert not isEnforceRange and not isClamp
+
+ treatAs = {
+ "Default": "eStringify",
+ "EmptyString": "eEmpty",
+ "Null": "eNull"
+ }
+ if type.nullable():
+ # For nullable strings null becomes a null string.
+ treatNullAs = "Null"
+ # For nullable strings undefined becomes a null string unless
+ # specified otherwise.
+ if treatUndefinedAs == "Default":
+ treatUndefinedAs = "Null"
+ nullBehavior = treatAs[treatNullAs]
+ if treatUndefinedAs == "Missing":
+ raise TypeError("We don't support [TreatUndefinedAs=Missing]")
+ undefinedBehavior = treatAs[treatUndefinedAs]
+
+ def getConversionCode(varName):
+ conversionCode = (
+ "if (!ConvertJSValueToString(cx, ${val}, ${valPtr}, %s, %s, %s)) {\n"
+ " return false;\n"
+ "}" % (nullBehavior, undefinedBehavior, varName))
+ if defaultValue is None:
+ return conversionCode
+
+ if isinstance(defaultValue, IDLNullValue):
+ assert(type.nullable())
+ return handleDefault(conversionCode,
+ "%s.SetNull()" % varName)
+ return handleDefault(
+ conversionCode,
+ ("static const PRUnichar data[] = { %s };\n"
+ "%s.SetData(data, ArrayLength(data) - 1)" %
+ (", ".join(["'" + char + "'" for char in defaultValue.value] + ["0"]),
+ varName)))
+
+ if isMember:
+ # We have to make a copy, because our jsval may well not
+ # live as long as our string needs to.
+ declType = CGGeneric("nsString")
+ return (
+ "{\n"
+ " FakeDependentString str;\n"
+ "%s\n"
+ " ${declName} = str;\n"
+ "}\n" % CGIndenter(CGGeneric(getConversionCode("str"))).define(),
+ declType, None, isOptional)
+
+ if isOptional:
+ declType = "Optional<nsAString>"
+ else:
+ declType = "NonNull<nsAString>"
+
+ return (
+ "%s\n"
+ "const_cast<%s&>(${declName}) = &${holderName};" %
+ (getConversionCode("${holderName}"), declType),
+ CGGeneric("const " + declType), CGGeneric("FakeDependentString"),
+ # No need to deal with Optional here; we have handled it already
+ False)
+
+ if type.isEnum():
+ assert not isEnforceRange and not isClamp
+
+ if type.nullable():
+ raise TypeError("We don't support nullable enumerated arguments "
+ "yet")
+ enum = type.inner.identifier.name
+ if invalidEnumValueFatal:
+ handleInvalidEnumValueCode = " MOZ_ASSERT(index >= 0);\n"
+ else:
+ handleInvalidEnumValueCode = (
+ " if (index < 0) {\n"
+ " return true;\n"
+ " }\n")
+
+ template = (
+ "{\n"
+ " bool ok;\n"
+ " int index = FindEnumStringIndex<%(invalidEnumValueFatal)s>(cx, ${val}, %(values)s, \"%(enumtype)s\", &ok);\n"
+ " if (!ok) {\n"
+ " return false;\n"
+ " }\n"
+ "%(handleInvalidEnumValueCode)s"
+ " ${declName} = static_cast<%(enumtype)s>(index);\n"
+ "}" % { "enumtype" : enum,
+ "values" : enum + "Values::strings",
+ "invalidEnumValueFatal" : toStringBool(invalidEnumValueFatal),
+ "handleInvalidEnumValueCode" : handleInvalidEnumValueCode })
+
+ if defaultValue is not None:
+ assert(defaultValue.type.tag() == IDLType.Tags.domstring)
+ template = handleDefault(template,
+ ("${declName} = %sValues::%s" %
+ (enum,
+ getEnumValueName(defaultValue.value))))
+ return (template, CGGeneric(enum), None, isOptional)
+
+ if type.isCallback():
+ assert not isEnforceRange and not isClamp
+
+ if isMember:
+ raise TypeError("Can't handle member callbacks; need to sort out "
+ "rooting issues")
+ # XXXbz we're going to assume that callback types are always
+ # nullable and always have [TreatNonCallableAsNull] for now.
+ haveCallable = "${val}.isObject() && JS_ObjectIsCallable(cx, &${val}.toObject())"
+ if defaultValue is not None:
+ assert(isinstance(defaultValue, IDLNullValue))
+ haveCallable = "${haveValue} && " + haveCallable
+ return (
+ "if (%s) {\n"
+ " ${declName} = &${val}.toObject();\n"
+ "} else {\n"
+ " ${declName} = NULL;\n"
+ "}" % haveCallable,
+ CGGeneric("JSObject*"), None, isOptional)
+
+ if type.isAny():
+ assert not isEnforceRange and not isClamp
+
+ if isMember:
+ raise TypeError("Can't handle member 'any'; need to sort out "
+ "rooting issues")
+ templateBody = "${declName} = ${val};"
+ templateBody = handleDefaultNull(templateBody,
+ "${declName} = JS::NullValue()")
+ return (templateBody, CGGeneric("JS::Value"), None, isOptional)
+
+ if type.isObject():
+ assert not isEnforceRange and not isClamp
+
+ if isMember:
+ raise TypeError("Can't handle member 'object'; need to sort out "
+ "rooting issues")
+ template = wrapObjectTemplate("${declName} = &${val}.toObject();",
+ isDefinitelyObject, type,
+ "${declName} = NULL",
+ failureCode)
+ if type.nullable():
+ declType = CGGeneric("JSObject*")
+ else:
+ declType = CGGeneric("NonNull<JSObject>")
+ return (template, declType, None, isOptional)
+
+ if type.isDictionary():
+ if failureCode is not None:
+ raise TypeError("Can't handle dictionaries when failureCode is not None")
+ # There are no nullable dictionaries
+ assert not type.nullable()
+ # All optional dictionaries always have default values, so we
+ # should be able to assume not isOptional here.
+ assert not isOptional
+
+ typeName = CGDictionary.makeDictionaryName(type.inner,
+ descriptorProvider.workers)
+ actualTypeName = typeName
+ selfRef = "${declName}"
+
+ declType = CGGeneric(actualTypeName)
+
+ # If we're a member of something else, the const
+ # will come from the Optional or our container.
+ if not isMember:
+ declType = CGWrapper(declType, pre="const ")
+ selfRef = "const_cast<%s&>(%s)" % (typeName, selfRef)
+
+ # We do manual default value handling here, because we
+ # actually do want a jsval, and we only handle null anyway
+ if defaultValue is not None:
+ assert(isinstance(defaultValue, IDLNullValue))
+ val = "(${haveValue}) ? ${val} : JSVAL_NULL"
+ else:
+ val = "${val}"
+
+ template = ("if (!%s.Init(cx, %s)) {\n"
+ " return false;\n"
+ "}" % (selfRef, val))
+
+ return (template, declType, None, False)
+
+ if not type.isPrimitive():
+ raise TypeError("Need conversion for argument type '%s'" % str(type))
+
+ typeName = builtinNames[type.tag()]
+
+ conversionBehavior = "eDefault"
+ if isEnforceRange:
+ conversionBehavior = "eEnforceRange"
+ elif isClamp:
+ conversionBehavior = "eClamp"
+
+ if type.nullable():
+ dataLoc = "${declName}.SetValue()"
+ nullCondition = "${val}.isNullOrUndefined()"
+ if defaultValue is not None and isinstance(defaultValue, IDLNullValue):
+ nullCondition = "!(${haveValue}) || " + nullCondition
+ template = (
+ "if (%s) {\n"
+ " ${declName}.SetNull();\n"
+ "} else if (!ValueToPrimitive<%s, %s>(cx, ${val}, &%s)) {\n"
+ " return false;\n"
+ "}" % (nullCondition, typeName, conversionBehavior, dataLoc))
+ declType = CGGeneric("Nullable<" + typeName + ">")
+ else:
+ assert(defaultValue is None or
+ not isinstance(defaultValue, IDLNullValue))
+ dataLoc = "${declName}"
+ template = (
+ "if (!ValueToPrimitive<%s, %s>(cx, ${val}, &%s)) {\n"
+ " return false;\n"
+ "}" % (typeName, conversionBehavior, dataLoc))
+ declType = CGGeneric(typeName)
+ if (defaultValue is not None and
+ # We already handled IDLNullValue, so just deal with the other ones
+ not isinstance(defaultValue, IDLNullValue)):
+ tag = defaultValue.type.tag()
+ if tag in numericTags:
+ defaultStr = defaultValue.value
+ else:
+ assert(tag == IDLType.Tags.bool)
+ defaultStr = toStringBool(defaultValue.value)
+ template = CGWrapper(CGIndenter(CGGeneric(template)),
+ pre="if (${haveValue}) {\n",
+ post=("\n"
+ "} else {\n"
+ " %s = %s;\n"
+ "}" % (dataLoc, defaultStr))).define()
+
+ return (template, declType, None, isOptional)
+
+def instantiateJSToNativeConversionTemplate(templateTuple, replacements,
+ argcAndIndex=None):
+ """
+ Take a tuple as returned by getJSToNativeConversionTemplate and a set of
+ replacements as required by the strings in such a tuple, and generate code
+ to convert into stack C++ types.
+
+ If argcAndIndex is not None it must be a dict that can be used to
+ replace ${argc} and ${index}, where ${index} is the index of this
+ argument (0-based) and ${argc} is the total number of arguments.
+ """
+ (templateBody, declType, holderType, dealWithOptional) = templateTuple
+
+ if dealWithOptional and argcAndIndex is None:
+ raise TypeError("Have to deal with optional things, but don't know how")
+ if argcAndIndex is not None and declType is None:
+ raise TypeError("Need to predeclare optional things, so they will be "
+ "outside the check for big enough arg count!");
+
+ result = CGList([], "\n")
+ # Make a copy of "replacements" since we may be about to start modifying it
+ replacements = dict(replacements)
+ originalHolderName = replacements["holderName"]
+ if holderType is not None:
+ if dealWithOptional:
+ replacements["holderName"] = (
+ "const_cast< %s & >(%s.Value())" %
+ (holderType.define(), originalHolderName))
+ mutableHolderType = CGWrapper(holderType, pre="Optional< ", post=" >")
+ holderType = CGWrapper(mutableHolderType, pre="const ")
+ result.append(
+ CGList([holderType, CGGeneric(" "),
+ CGGeneric(originalHolderName),
+ CGGeneric(";")]))
+
+ originalDeclName = replacements["declName"]
+ if declType is not None:
+ if dealWithOptional:
+ replacements["declName"] = (
+ "const_cast< %s & >(%s.Value())" %
+ (declType.define(), originalDeclName))
+ mutableDeclType = CGWrapper(declType, pre="Optional< ", post=" >")
+ declType = CGWrapper(mutableDeclType, pre="const ")
+ result.append(
+ CGList([declType, CGGeneric(" "),
+ CGGeneric(originalDeclName),
+ CGGeneric(";")]))
+
+ conversion = CGGeneric(
+ string.Template(templateBody).substitute(replacements)
+ )
+
+ if argcAndIndex is not None:
+ if dealWithOptional:
+ declConstruct = CGIndenter(
+ CGGeneric("const_cast< %s &>(%s).Construct();" %
+ (mutableDeclType.define(), originalDeclName)))
+ if holderType is not None:
+ holderConstruct = CGIndenter(
+ CGGeneric("const_cast< %s &>(%s).Construct();" %
+ (mutableHolderType.define(), originalHolderName)))
+ else:
+ holderConstruct = None
+ else:
+ declConstruct = None
+ holderConstruct = None
+
+ conversion = CGList(
+ [CGGeneric(
+ string.Template("if (${index} < ${argc}) {").substitute(
+ argcAndIndex
+ )),
+ declConstruct,
+ holderConstruct,
+ CGIndenter(conversion),
+ CGGeneric("}")],
+ "\n")
+
+ result.append(conversion)
+ # Add an empty CGGeneric to get an extra newline after the argument
+ # conversion.
+ result.append(CGGeneric(""))
+ return result;
+
+def convertConstIDLValueToJSVal(value):
+ if isinstance(value, IDLNullValue):
+ return "JSVAL_NULL"
+ tag = value.type.tag()
+ if tag in [IDLType.Tags.int8, IDLType.Tags.uint8, IDLType.Tags.int16,
+ IDLType.Tags.uint16, IDLType.Tags.int32]:
+ return "INT_TO_JSVAL(%s)" % (value.value)
+ if tag == IDLType.Tags.uint32:
+ return "UINT_TO_JSVAL(%s)" % (value.value)
+ if tag in [IDLType.Tags.int64, IDLType.Tags.uint64]:
+ return "DOUBLE_TO_JSVAL(%s)" % (value.value)
+ if tag == IDLType.Tags.bool:
+ return "JSVAL_TRUE" if value.value else "JSVAL_FALSE"
+ if tag in [IDLType.Tags.float, IDLType.Tags.double]:
+ return "DOUBLE_TO_JSVAL(%s)" % (value.value)
+ raise TypeError("Const value of unhandled type: " + value.type)
+
+class CGArgumentConverter(CGThing):
+ """
+ A class that takes an IDL argument object, its index in the
+ argument list, and the argv and argc strings and generates code to
+ unwrap the argument to the right native type.
+ """
+ def __init__(self, argument, index, argv, argc, descriptorProvider,
+ invalidEnumValueFatal=True):
+ CGThing.__init__(self)
+ self.argument = argument
+ if argument.variadic:
+ raise TypeError("We don't support variadic arguments yet " +
+ str(argument.location))
+ assert(not argument.defaultValue or argument.optional)
+
+ replacer = {
+ "index" : index,
+ "argc" : argc,
+ "argv" : argv
+ }
+ self.replacementVariables = {
+ "declName" : "arg%d" % index,
+ "holderName" : ("arg%d" % index) + "_holder"
+ }
+ self.replacementVariables["val"] = string.Template(
+ "${argv}[${index}]"
+ ).substitute(replacer)
+ self.replacementVariables["valPtr"] = (
+ "&" + self.replacementVariables["val"])
+ if argument.defaultValue:
+ self.replacementVariables["haveValue"] = string.Template(
+ "${index} < ${argc}").substitute(replacer)
+ self.descriptorProvider = descriptorProvider
+ if self.argument.optional and not self.argument.defaultValue:
+ self.argcAndIndex = replacer
+ else:
+ self.argcAndIndex = None
+ self.invalidEnumValueFatal = invalidEnumValueFatal
+
+ def define(self):
+ return instantiateJSToNativeConversionTemplate(
+ getJSToNativeConversionTemplate(self.argument.type,
+ self.descriptorProvider,
+ isOptional=(self.argcAndIndex is not None),
+ invalidEnumValueFatal=self.invalidEnumValueFatal,
+ defaultValue=self.argument.defaultValue,
+ treatNullAs=self.argument.treatNullAs,
+ treatUndefinedAs=self.argument.treatUndefinedAs,
+ isEnforceRange=self.argument.enforceRange,
+ isClamp=self.argument.clamp),
+ self.replacementVariables,
+ self.argcAndIndex).define()
+
+def getWrapTemplateForType(type, descriptorProvider, result, successCode,
+ isCreator):
+ """
+ Reflect a C++ value stored in "result", of IDL type "type" into JS. The
+ "successCode" is the code to run once we have successfully done the
+ conversion. The resulting string should be used with string.Template, it
+ needs the following keys when substituting: jsvalPtr/jsvalRef/obj.
+
+ Returns (templateString, infallibility of conversion template)
+ """
+ haveSuccessCode = successCode is not None
+ if not haveSuccessCode:
+ successCode = "return true;"
+
+ def setValue(value, callWrapValue=False):
+ """
+ Returns the code to set the jsval to value. If "callWrapValue" is true
+ JS_WrapValue will be called on the jsval.
+ """
+ if not callWrapValue:
+ tail = successCode
+ elif haveSuccessCode:
+ tail = ("if (!JS_WrapValue(cx, ${jsvalPtr})) {\n" +
+ " return false;\n" +
+ "}\n" +
+ successCode)
+ else:
+ tail = "return JS_WrapValue(cx, ${jsvalPtr});"
+ return ("${jsvalRef} = %s;\n" +
+ tail) % (value)
+
+ def wrapAndSetPtr(wrapCall, failureCode=None):
+ """
+ Returns the code to set the jsval by calling "wrapCall". "failureCode"
+ is the code to run if calling "wrapCall" fails
+ """
+ if failureCode is None:
+ if not haveSuccessCode:
+ return "return " + wrapCall + ";"
+ failureCode = "return false;"
+ str = ("if (!%s) {\n" +
+ CGIndenter(CGGeneric(failureCode)).define() + "\n" +
+ "}\n" +
+ successCode) % (wrapCall)
+ return str
+
+ if type is None or type.isVoid():
+ return (setValue("JSVAL_VOID"), True)
+
+ if type.isArray():
+ raise TypeError("Can't handle array return values yet")
+
+ if type.isSequence():
+ if type.nullable():
+ # Nullable sequences are Nullable< nsTArray<T> >
+ (recTemplate, recInfall) = getWrapTemplateForType(type.inner, descriptorProvider,
+ "%s.Value()" % result, successCode,
+ isCreator)
+ return ("""
+if (%s.IsNull()) {
+%s
+}
+%s""" % (result, CGIndenter(CGGeneric(setValue("JSVAL_NULL"))).define(), recTemplate), recInfall)
+
+ # Now do non-nullable sequences. We use setting the element
+ # in the array as our succcess code because when we succeed in
+ # wrapping that's what we should do.
+ innerTemplate = wrapForType(
+ type.inner, descriptorProvider,
+ {
+ 'result' : "%s[i]" % result,
+ 'successCode': ("if (!JS_DefineElement(cx, returnArray, i, tmp,\n"
+ " NULL, NULL, JSPROP_ENUMERATE)) {\n"
+ " return false;\n"
+ "}"),
+ 'jsvalRef': "tmp",
+ 'jsvalPtr': "&tmp",
+ 'isCreator': isCreator
+ }
+ )
+ innerTemplate = CGIndenter(CGGeneric(innerTemplate)).define()
+ return (("""
+uint32_t length = %s.Length();
+JSObject *returnArray = JS_NewArrayObject(cx, length, NULL);
+if (!returnArray) {
+ return false;
+}
+jsval tmp;
+for (uint32_t i = 0; i < length; ++i) {
+%s
+}\n""" % (result, innerTemplate)) + setValue("JS::ObjectValue(*returnArray)"), False)
+
+ if type.isGeckoInterface():
+ descriptor = descriptorProvider.getDescriptor(type.unroll().inner.identifier.name)
+ if type.nullable():
+ wrappingCode = ("if (!%s) {\n" % (result) +
+ CGIndenter(CGGeneric(setValue("JSVAL_NULL"))).define() + "\n" +
+ "}\n")
+ else:
+ wrappingCode = ""
+ if (not descriptor.interface.isExternal() and
+ not descriptor.interface.isCallback()):
+ if descriptor.wrapperCache:
+ wrapMethod = "WrapNewBindingObject"
+ else:
+ if not isCreator:
+ raise MethodNotCreatorError(descriptor.interface.identifier.name)
+ wrapMethod = "WrapNewBindingNonWrapperCachedObject"
+ wrap = "%s(cx, ${obj}, %s, ${jsvalPtr})" % (wrapMethod, result)
+ # We don't support prefable stuff in workers.
+ assert(not descriptor.prefable or not descriptor.workers)
+ if not descriptor.prefable:
+ # Non-prefable bindings can only fail to wrap as a new-binding object
+ # if they already threw an exception. Same thing for
+ # non-prefable bindings.
+ failed = ("MOZ_ASSERT(JS_IsExceptionPending(cx));\n" +
+ "return false;")
+ else:
+ if descriptor.notflattened:
+ raise TypeError("%s is prefable but not flattened; "
+ "fallback won't work correctly" %
+ descriptor.interface.identifier.name)
+ # Try old-style wrapping for bindings which might be preffed off.
+ failed = wrapAndSetPtr("HandleNewBindingWrappingFailure(cx, ${obj}, %s, ${jsvalPtr})" % result)
+ wrappingCode += wrapAndSetPtr(wrap, failed)
+ else:
+ if descriptor.notflattened:
+ getIID = "&NS_GET_IID(%s), " % descriptor.nativeType
+ else:
+ getIID = ""
+ wrap = "WrapObject(cx, ${obj}, %s, %s${jsvalPtr})" % (result, getIID)
+ wrappingCode += wrapAndSetPtr(wrap)
+ return (wrappingCode, False)
+
+ if type.isString():
+ if type.nullable():
+ return (wrapAndSetPtr("xpc::StringToJsval(cx, %s, ${jsvalPtr})" % result), False)
+ else:
+ return (wrapAndSetPtr("xpc::NonVoidStringToJsval(cx, %s, ${jsvalPtr})" % result), False)
+
+ if type.isEnum():
+ if type.nullable():
+ raise TypeError("We don't support nullable enumerated return types "
+ "yet")
+ return ("""MOZ_ASSERT(uint32_t(%(result)s) < ArrayLength(%(strings)s));
+JSString* %(resultStr)s = JS_NewStringCopyN(cx, %(strings)s[uint32_t(%(result)s)].value, %(strings)s[uint32_t(%(result)s)].length);
+if (!%(resultStr)s) {
+ return false;
+}
+""" % { "result" : result,
+ "resultStr" : result + "_str",
+ "strings" : type.inner.identifier.name + "Values::strings" } +
+ setValue("JS::StringValue(%s_str)" % result), False)
+
+ if type.isCallback():
+ assert not type.isInterface()
+ # XXXbz we're going to assume that callback types are always
+ # nullable and always have [TreatNonCallableAsNull] for now.
+ # See comments in WrapNewBindingObject explaining why we need
+ # to wrap here.
+ # NB: setValue(..., True) calls JS_WrapValue(), so is fallible
+ return (setValue("JS::ObjectOrNullValue(%s)" % result, True), False)
+
+ if type.tag() == IDLType.Tags.any:
+ # See comments in WrapNewBindingObject explaining why we need
+ # to wrap here.
+ # NB: setValue(..., True) calls JS_WrapValue(), so is fallible
+ return (setValue(result, True), False)
+
+ if type.isObject() or type.isSpiderMonkeyInterface():
+ # See comments in WrapNewBindingObject explaining why we need
+ # to wrap here.
+ if type.nullable():
+ toValue = "JS::ObjectOrNullValue(%s)"
+ else:
+ toValue = "JS::ObjectValue(*%s)"
+ # NB: setValue(..., True) calls JS_WrapValue(), so is fallible
+ return (setValue(toValue % result, True), False)
+
+ if not type.isPrimitive():
+ raise TypeError("Need to learn to wrap %s" % type)
+
+ if type.nullable():
+ (recTemplate, recInfal) = getWrapTemplateForType(type.inner, descriptorProvider,
+ "%s.Value()" % result, successCode,
+ isCreator)
+ return ("if (%s.IsNull()) {\n" % result +
+ CGIndenter(CGGeneric(setValue("JSVAL_NULL"))).define() + "\n" +
+ "}\n" + recTemplate, recInfal)
+
+ tag = type.tag()
+
+ if tag in [IDLType.Tags.int8, IDLType.Tags.uint8, IDLType.Tags.int16,
+ IDLType.Tags.uint16, IDLType.Tags.int32]:
+ return (setValue("INT_TO_JSVAL(int32_t(%s))" % result), True)
+
+ elif tag in [IDLType.Tags.int64, IDLType.Tags.uint64, IDLType.Tags.float,
+ IDLType.Tags.double]:
+ # XXXbz will cast to double do the "even significand" thing that webidl
+ # calls for for 64-bit ints? Do we care?
+ return (setValue("JS_NumberValue(double(%s))" % result), True)
+
+ elif tag == IDLType.Tags.uint32:
+ return (setValue("UINT_TO_JSVAL(%s)" % result), True)
+
+ elif tag == IDLType.Tags.bool:
+ return (setValue("BOOLEAN_TO_JSVAL(%s)" % result), True)
+
+ else:
+ raise TypeError("Need to learn to wrap primitive: %s" % type)
+
+def wrapForType(type, descriptorProvider, templateValues):
+ """
+ Reflect a C++ value of IDL type "type" into JS. TemplateValues is a dict
+ that should contain:
+
+ * 'jsvalRef': a C++ reference to the jsval in which to store the result of
+ the conversion
+ * 'jsvalPtr': a C++ pointer to the jsval in which to store the result of
+ the conversion
+ * 'obj' (optional): the name of the variable that contains the JSObject to
+ use as a scope when wrapping, if not supplied 'obj'
+ will be used as the name
+ * 'result' (optional): the name of the variable in which the C++ value is
+ stored, if not supplied 'result' will be used as
+ the name
+ * 'successCode' (optional): the code to run once we have successfully done
+ the conversion, if not supplied 'return true;'
+ will be used as the code
+ * 'isCreator' (optional): If true, we're wrapping for the return value of
+ a [Creator] method. Assumed false if not set.
+ """
+ wrap = getWrapTemplateForType(type, descriptorProvider,
+ templateValues.get('result', 'result'),
+ templateValues.get('successCode', None),
+ templateValues.get('isCreator', False))[0]
+
+ defaultValues = {'obj': 'obj'}
+ return string.Template(wrap).substitute(defaultValues, **templateValues)
+
+def infallibleForMember(member, type, descriptorProvider):
+ """
+ Determine the fallibility of changing a C++ value of IDL type "type" into
+ JS for the given attribute. Apart from isCreator, all the defaults are used,
+ since the fallbility does not change based on the boolean values,
+ and the template will be discarded.
+
+ CURRENT ASSUMPTIONS:
+ We assume that successCode for wrapping up return values cannot contain
+ failure conditions.
+ """
+ return getWrapTemplateForType(type, descriptorProvider, 'result', None,\
+ memberIsCreator(member))[1]
+
+def typeNeedsCx(type, retVal=False):
+ if type is None:
+ return False
+ if type.nullable():
+ type = type.inner
+ if type.isSequence() or type.isArray():
+ type = type.inner
+ if type.isUnion():
+ return any(typeNeedsCx(t) for t in type.unroll().flatMemberTypes)
+ if retVal and type.isSpiderMonkeyInterface():
+ return True
+ return type.isCallback() or type.isAny() or type.isObject()
+
+# Returns a tuple consisting of a CGThing containing the type of the return
+# value, or None if there is no need for a return value, and a boolean signaling
+# whether the return value is passed in an out parameter.
+def getRetvalDeclarationForType(returnType, descriptorProvider,
+ resultAlreadyAddRefed):
+ if returnType is None or returnType.isVoid():
+ # Nothing to declare
+ return None, False
+ if returnType.isPrimitive() and returnType.tag() in builtinNames:
+ result = CGGeneric(builtinNames[returnType.tag()])
+ if returnType.nullable():
+ result = CGWrapper(result, pre="Nullable<", post=">")
+ return result, False
+ if returnType.isString():
+ return CGGeneric("nsString"), True
+ if returnType.isEnum():
+ if returnType.nullable():
+ raise TypeError("We don't support nullable enum return values")
+ return CGGeneric(returnType.inner.identifier.name), False
+ if returnType.isGeckoInterface():
+ result = CGGeneric(descriptorProvider.getDescriptor(
+ returnType.unroll().inner.identifier.name).nativeType)
+ if resultAlreadyAddRefed:
+ result = CGWrapper(result, pre="nsRefPtr<", post=">")
+ else:
+ result = CGWrapper(result, post="*")
+ return result, False
+ if returnType.isCallback():
+ # XXXbz we're going to assume that callback types are always
+ # nullable for now.
+ return CGGeneric("JSObject*"), False
+ if returnType.isAny():
+ return CGGeneric("JS::Value"), False
+ if returnType.isObject() or returnType.isSpiderMonkeyInterface():
+ return CGGeneric("JSObject*"), False
+ if returnType.isSequence():
+ nullable = returnType.nullable()
+ if nullable:
+ returnType = returnType.inner
+ # If our result is already addrefed, use the right type in the
+ # sequence argument here.
+ (result, _) = getRetvalDeclarationForType(returnType.inner,
+ descriptorProvider,
+ resultAlreadyAddRefed)
+ result = CGWrapper(result, pre="nsTArray< ", post=" >")
+ if nullable:
+ result = CGWrapper(result, pre="Nullable< ", post=" >")
+ return result, True
+ raise TypeError("Don't know how to declare return value for %s" %
+ returnType)
+
+def isResultAlreadyAddRefed(descriptor, extendedAttributes):
+ # Default to already_AddRefed on the main thread, raw pointer in workers
+ return not descriptor.workers and not 'resultNotAddRefed' in extendedAttributes
+
+class CGCallGenerator(CGThing):
+ """
+ A class to generate an actual call to a C++ object. Assumes that the C++
+ object is stored in a variable whose name is given by the |object| argument.
+
+ errorReport should be a CGThing for an error report or None if no
+ error reporting is needed.
+ """
+ def __init__(self, errorReport, arguments, argsPre, returnType,
+ extendedAttributes, descriptorProvider, nativeMethodName,
+ static, object="self", declareResult=True):
+ CGThing.__init__(self)
+
+ assert errorReport is None or isinstance(errorReport, CGThing)
+
+ isFallible = errorReport is not None
+
+ resultAlreadyAddRefed = isResultAlreadyAddRefed(descriptorProvider,
+ extendedAttributes)
+ (result, resultOutParam) = getRetvalDeclarationForType(returnType,
+ descriptorProvider,
+ resultAlreadyAddRefed)
+
+ args = CGList([CGGeneric(arg) for arg in argsPre], ", ")
+ for (a, name) in arguments:
+ # This is a workaround for a bug in Apple's clang.
+ if a.type.isObject() and not a.type.nullable() and not a.optional:
+ name = "(JSObject&)" + name
+ args.append(CGGeneric(name))
+
+ # Return values that go in outparams go here
+ if resultOutParam:
+ args.append(CGGeneric("result"))
+ if isFallible:
+ args.append(CGGeneric("rv"))
+
+ needsCx = (typeNeedsCx(returnType, True) or
+ any(typeNeedsCx(a.type) for (a, _) in arguments) or
+ 'implicitJSContext' in extendedAttributes)
+
+ if not "cx" in argsPre and needsCx:
+ args.prepend(CGGeneric("cx"))
+
+ # Build up our actual call
+ self.cgRoot = CGList([], "\n")
+
+ call = CGGeneric(nativeMethodName)
+ if static:
+ call = CGWrapper(call, pre="%s::" % descriptorProvider.nativeType)
+ else:
+ call = CGWrapper(call, pre="%s->" % object)
+ call = CGList([call, CGWrapper(args, pre="(", post=");")])
+ if result is not None:
+ if declareResult:
+ result = CGWrapper(result, post=" result;")
+ self.cgRoot.prepend(result)
+ if not resultOutParam:
+ call = CGWrapper(call, pre="result = ")
+
+ call = CGWrapper(call)
+ self.cgRoot.append(call)
+
+ if isFallible:
+ self.cgRoot.prepend(CGGeneric("ErrorResult rv;"))
+ self.cgRoot.append(CGGeneric("if (rv.Failed()) {"))
+ self.cgRoot.append(CGIndenter(errorReport))
+ self.cgRoot.append(CGGeneric("}"))
+
+ def define(self):
+ return self.cgRoot.define()
+
+class MethodNotCreatorError(Exception):
+ def __init__(self, typename):
+ self.typename = typename
+
+class CGPerSignatureCall(CGThing):
+ """
+ This class handles the guts of generating code for a particular
+ call signature. A call signature consists of four things:
+
+ 1) A return type, which can be None to indicate that there is no
+ actual return value (e.g. this is an attribute setter) or an
+ IDLType if there's an IDL type involved (including |void|).
+ 2) An argument list, which is allowed to be empty.
+ 3) A name of a native method to call.
+ 4) Whether or not this method is static.
+
+ We also need to know whether this is a method or a getter/setter
+ to do error reporting correctly.
+
+ The idlNode parameter can be either a method or an attr. We can query
+ |idlNode.identifier| in both cases, so we can be agnostic between the two.
+ """
+ # XXXbz For now each entry in the argument list is either an
+ # IDLArgument or a FakeArgument, but longer-term we may want to
+ # have ways of flagging things like JSContext* or optional_argc in
+ # there.
+
+ def __init__(self, returnType, argsPre, arguments, nativeMethodName, static,
+ descriptor, idlNode, argConversionStartsAt=0,
+ getter=False, setter=False):
+ CGThing.__init__(self)
+ self.returnType = returnType
+ self.descriptor = descriptor
+ self.idlNode = idlNode
+ self.extendedAttributes = descriptor.getExtendedAttributes(idlNode,
+ getter=getter,
+ setter=setter)
+ self.argsPre = argsPre
+ self.arguments = arguments
+ self.argCount = len(arguments)
+ if self.argCount > argConversionStartsAt:
+ # Insert our argv in there
+ cgThings = [CGGeneric(self.getArgvDecl())]
+ else:
+ cgThings = []
+ cgThings.extend([CGArgumentConverter(arguments[i], i, self.getArgv(),
+ self.getArgc(), self.descriptor,
+ invalidEnumValueFatal=not setter) for
+ i in range(argConversionStartsAt, self.argCount)])
+
+ cgThings.append(CGCallGenerator(
+ self.getErrorReport() if self.isFallible() else None,
+ self.getArguments(), self.argsPre, returnType,
+ self.extendedAttributes, descriptor, nativeMethodName,
+ static))
+ self.cgRoot = CGList(cgThings, "\n")
+
+ def getArgv(self):
+ return "argv" if self.argCount > 0 else ""
+ def getArgvDecl(self):
+ return "\nJS::Value* argv = JS_ARGV(cx, vp);\n"
+ def getArgc(self):
+ return "argc"
+ def getArguments(self):
+ return [(a, "arg" + str(i)) for (i, a) in enumerate(self.arguments)]
+
+ def isFallible(self):
+ return not 'infallible' in self.extendedAttributes
+
+ def wrap_return_value(self):
+ isCreator = memberIsCreator(self.idlNode)
+ if isCreator:
+ # We better be returning addrefed things!
+ assert(isResultAlreadyAddRefed(self.descriptor,
+ self.extendedAttributes) or
+ # Workers use raw pointers for new-object return
+ # values or something
+ self.descriptor.workers)
+
+ resultTemplateValues = { 'jsvalRef': '*vp', 'jsvalPtr': 'vp',
+ 'isCreator': isCreator}
+ try:
+ return wrapForType(self.returnType, self.descriptor,
+ resultTemplateValues)
+ except MethodNotCreatorError, err:
+ assert not isCreator
+ raise TypeError("%s being returned from non-creator method or property %s.%s" %
+ (err.typename,
+ self.descriptor.interface.identifier.name,
+ self.idlNode.identifier.name))
+
+ def getErrorReport(self):
+ return CGGeneric('return ThrowMethodFailedWithDetails<%s>(cx, rv, "%s", "%s");'
+ % (toStringBool(not self.descriptor.workers),
+ self.descriptor.interface.identifier.name,
+ self.idlNode.identifier.name))
+
+ def define(self):
+ return (self.cgRoot.define() + "\n" + self.wrap_return_value())
+
+class CGSwitch(CGList):
+ """
+ A class to generate code for a switch statement.
+
+ Takes three constructor arguments: an expression, a list of cases,
+ and an optional default.
+
+ Each case is a CGCase. The default is a CGThing for the body of
+ the default case, if any.
+ """
+ def __init__(self, expression, cases, default=None):
+ CGList.__init__(self, [CGIndenter(c) for c in cases], "\n")
+ self.prepend(CGWrapper(CGGeneric(expression),
+ pre="switch (", post=") {"));
+ if default is not None:
+ self.append(
+ CGIndenter(
+ CGWrapper(
+ CGIndenter(default),
+ pre="default: {\n",
+ post="\n break;\n}"
+ )
+ )
+ )
+
+ self.append(CGGeneric("}"))
+
+class CGCase(CGList):
+ """
+ A class to generate code for a case statement.
+
+ Takes three constructor arguments: an expression, a CGThing for
+ the body (allowed to be None if there is no body), and an optional
+ argument (defaulting to False) for whether to fall through.
+ """
+ def __init__(self, expression, body, fallThrough=False):
+ CGList.__init__(self, [], "\n")
+ self.append(CGWrapper(CGGeneric(expression), pre="case ", post=": {"))
+ bodyList = CGList([body], "\n")
+ if fallThrough:
+ bodyList.append(CGGeneric("/* Fall through */"))
+ else:
+ bodyList.append(CGGeneric("break;"))
+ self.append(CGIndenter(bodyList));
+ self.append(CGGeneric("}"))
+
+class CGMethodCall(CGThing):
+ """
+ A class to generate selection of a method signature from a set of
+ signatures and generation of a call to that signature.
+ """
+ def __init__(self, argsPre, nativeMethodName, static, descriptor, method):
+ CGThing.__init__(self)
+
+ methodName = '"%s.%s"' % (descriptor.interface.identifier.name, method.identifier.name)
+
+ def requiredArgCount(signature):
+ arguments = signature[1]
+ if len(arguments) == 0:
+ return 0
+ requiredArgs = len(arguments)
+ while requiredArgs and arguments[requiredArgs-1].optional:
+ requiredArgs -= 1
+ return requiredArgs
+
+ def getPerSignatureCall(signature, argConversionStartsAt=0):
+ return CGPerSignatureCall(signature[0], argsPre, signature[1],
+ nativeMethodName, static, descriptor,
+ method, argConversionStartsAt)
+
+
+ signatures = method.signatures()
+ if len(signatures) == 1:
+ # Special case: we can just do a per-signature method call
+ # here for our one signature and not worry about switching
+ # on anything.
+ signature = signatures[0]
+ self.cgRoot = CGList([ CGIndenter(getPerSignatureCall(signature)) ])
+ requiredArgs = requiredArgCount(signature)
+
+
+ if requiredArgs > 0:
+ code = (
+ "if (argc < %d) {\n"
+ " return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, %s);\n"
+ "}" % (requiredArgs, methodName))
+ self.cgRoot.prepend(
+ CGWrapper(CGIndenter(CGGeneric(code)), pre="\n", post="\n"))
+ return
+
+ # Need to find the right overload
+ maxArgCount = method.maxArgCount
+ allowedArgCounts = method.allowedArgCounts
+
+ argCountCases = []
+ for argCount in allowedArgCounts:
+ possibleSignatures = method.signaturesForArgCount(argCount)
+ if len(possibleSignatures) == 1:
+ # easy case!
+ signature = possibleSignatures[0]
+
+ # (possibly) important optimization: if signature[1] has >
+ # argCount arguments and signature[1][argCount] is optional and
+ # there is only one signature for argCount+1, then the
+ # signature for argCount+1 is just ourselves and we can fall
+ # through.
+ if (len(signature[1]) > argCount and
+ signature[1][argCount].optional and
+ (argCount+1) in allowedArgCounts and
+ len(method.signaturesForArgCount(argCount+1)) == 1):
+ argCountCases.append(
+ CGCase(str(argCount), None, True))
+ else:
+ argCountCases.append(
+ CGCase(str(argCount), getPerSignatureCall(signature)))
+ continue
+
+ distinguishingIndex = method.distinguishingIndexForArgCount(argCount)
+
+ # We can't handle unions at the distinguishing index.
+ for (returnType, args) in possibleSignatures:
+ if args[distinguishingIndex].type.isUnion():
+ raise TypeError("No support for unions as distinguishing "
+ "arguments yet: %s",
+ args[distinguishingIndex].location)
+
+ # Convert all our arguments up to the distinguishing index.
+ # Doesn't matter which of the possible signatures we use, since
+ # they all have the same types up to that point; just use
+ # possibleSignatures[0]
+ caseBody = [CGGeneric("JS::Value* argv_start = JS_ARGV(cx, vp);")]
+ caseBody.extend([ CGArgumentConverter(possibleSignatures[0][1][i],
+ i, "argv_start", "argc",
+ descriptor) for i in
+ range(0, distinguishingIndex) ])
+
+ # Select the right overload from our set.
+ distinguishingArg = "argv_start[%d]" % distinguishingIndex
+
+ def pickFirstSignature(condition, filterLambda):
+ sigs = filter(filterLambda, possibleSignatures)
+ assert len(sigs) < 2
+ if len(sigs) > 0:
+ if condition is None:
+ caseBody.append(
+ getPerSignatureCall(sigs[0], distinguishingIndex))
+ else:
+ caseBody.append(CGGeneric("if (" + condition + ") {"))
+ caseBody.append(CGIndenter(
+ getPerSignatureCall(sigs[0], distinguishingIndex)))
+ caseBody.append(CGGeneric("}"))
+ return True
+ return False
+
+ # First check for null or undefined
+ pickFirstSignature("%s.isNullOrUndefined()" % distinguishingArg,
+ lambda s: (s[1][distinguishingIndex].type.nullable() or
+ s[1][distinguishingIndex].type.isDictionary()))
+
+ # Now check for distinguishingArg being an object that implements a
+ # non-callback interface. That includes typed arrays and
+ # arraybuffers.
+ interfacesSigs = [
+ s for s in possibleSignatures
+ if (s[1][distinguishingIndex].type.isObject() or
+ s[1][distinguishingIndex].type.isNonCallbackInterface()) ]
+ # There might be more than one of these; we need to check
+ # which ones we unwrap to.
+
+ if len(interfacesSigs) > 0:
+ # The spec says that we should check for "platform objects
+ # implementing an interface", but it's enough to guard on these
+ # being an object. The code for unwrapping non-callback
+ # interfaces and typed arrays will just bail out and move on to
+ # the next overload if the object fails to unwrap correctly. We
+ # could even not do the isObject() check up front here, but in
+ # cases where we have multiple object overloads it makes sense
+ # to do it only once instead of for each overload. That will
+ # also allow the unwrapping test to skip having to do codegen
+ # for the null-or-undefined case, which we already handled
+ # above.
+ caseBody.append(CGGeneric("if (%s.isObject()) {" %
+ (distinguishingArg)))
+ for sig in interfacesSigs:
+ caseBody.append(CGIndenter(CGGeneric("do {")));
+ type = sig[1][distinguishingIndex].type
+
+ # The argument at index distinguishingIndex can't possibly
+ # be unset here, because we've already checked that argc is
+ # large enough that we can examine this argument.
+ testCode = instantiateJSToNativeConversionTemplate(
+ getJSToNativeConversionTemplate(type, descriptor,
+ failureCode="break;",
+ isDefinitelyObject=True),
+ {
+ "declName" : "arg%d" % distinguishingIndex,
+ "holderName" : ("arg%d" % distinguishingIndex) + "_holder",
+ "val" : distinguishingArg
+ })
+
+ # Indent by 4, since we need to indent further than our "do" statement
+ caseBody.append(CGIndenter(testCode, 4));
+ # If we got this far, we know we unwrapped to the right
+ # interface, so just do the call. Start conversion with
+ # distinguishingIndex + 1, since we already converted
+ # distinguishingIndex.
+ caseBody.append(CGIndenter(
+ getPerSignatureCall(sig, distinguishingIndex + 1), 4))
+ caseBody.append(CGIndenter(CGGeneric("} while (0);")))
+
+ caseBody.append(CGGeneric("}"))
+
+ # XXXbz Now we're supposed to check for distinguishingArg being
+ # an array or a platform object that supports indexed
+ # properties... skip that last for now. It's a bit of a pain.
+ pickFirstSignature("%s.isObject() && IsArrayLike(cx, &%s.toObject())" %
+ (distinguishingArg, distinguishingArg),
+ lambda s:
+ (s[1][distinguishingIndex].type.isArray() or
+ s[1][distinguishingIndex].type.isSequence() or
+ s[1][distinguishingIndex].type.isObject()))
+
+ # Check for Date objects
+ # XXXbz Do we need to worry about security wrappers around the Date?
+ pickFirstSignature("%s.isObject() && JS_ObjectIsDate(cx, &%s.toObject())" %
+ (distinguishingArg, distinguishingArg),
+ lambda s: (s[1][distinguishingIndex].type.isDate() or
+ s[1][distinguishingIndex].type.isObject()))
+
+ # Check for vanilla JS objects
+ # XXXbz Do we need to worry about security wrappers?
+ pickFirstSignature("%s.isObject() && !IsPlatformObject(cx, &%s.toObject())" %
+ (distinguishingArg, distinguishingArg),
+ lambda s: (s[1][distinguishingIndex].type.isCallback() or
+ s[1][distinguishingIndex].type.isCallbackInterface() or
+ s[1][distinguishingIndex].type.isDictionary() or
+ s[1][distinguishingIndex].type.isObject()))
+
+ # The remaining cases are mutually exclusive. The
+ # pickFirstSignature calls are what change caseBody
+ # Check for strings or enums
+ if pickFirstSignature(None,
+ lambda s: (s[1][distinguishingIndex].type.isString() or
+ s[1][distinguishingIndex].type.isEnum())):
+ pass
+ # Check for primitives
+ elif pickFirstSignature(None,
+ lambda s: s[1][distinguishingIndex].type.isPrimitive()):
+ pass
+ # Check for "any"
+ elif pickFirstSignature(None,
+ lambda s: s[1][distinguishingIndex].type.isAny()):
+ pass
+ else:
+ # Just throw; we have no idea what we're supposed to
+ # do with this.
+ caseBody.append(CGGeneric("return Throw<%s>(cx, NS_ERROR_XPC_BAD_CONVERT_JS);" %
+ toStringBool(not descriptor.workers)))
+
+ argCountCases.append(CGCase(str(argCount),
+ CGList(caseBody, "\n")))
+
+ overloadCGThings = []
+ overloadCGThings.append(
+ CGGeneric("unsigned argcount = NS_MIN(argc, %du);" %
+ maxArgCount))
+ overloadCGThings.append(
+ CGSwitch("argcount",
+ argCountCases,
+ CGGeneric("return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, %s);\n" % methodName)))
+ overloadCGThings.append(
+ CGGeneric('MOZ_NOT_REACHED("We have an always-returning default case");\n'
+ 'return false;'))
+ self.cgRoot = CGWrapper(CGIndenter(CGList(overloadCGThings, "\n")),
+ pre="\n")
+
+ def define(self):
+ return self.cgRoot.define()
+
+class CGGetterCall(CGPerSignatureCall):
+ """
+ A class to generate a native object getter call for a particular IDL
+ getter.
+ """
+ def __init__(self, returnType, nativeMethodName, descriptor, attr):
+ CGPerSignatureCall.__init__(self, returnType, [], [],
+ nativeMethodName, False, descriptor,
+ attr, getter=True)
+
+class FakeArgument():
+ """
+ A class that quacks like an IDLArgument. This is used to make
+ setters look like method calls or for special operations.
+ """
+ def __init__(self, type, interfaceMember):
+ self.type = type
+ self.optional = False
+ self.variadic = False
+ self.defaultValue = None
+ self.treatNullAs = interfaceMember.treatNullAs
+ self.treatUndefinedAs = interfaceMember.treatUndefinedAs
+ self.enforceRange = False
+ self.clamp = False
+
+class CGSetterCall(CGPerSignatureCall):
+ """
+ A class to generate a native object setter call for a particular IDL
+ setter.
+ """
+ def __init__(self, argType, nativeMethodName, descriptor, attr):
+ CGPerSignatureCall.__init__(self, None, [],
+ [FakeArgument(argType, attr)],
+ nativeMethodName, False, descriptor, attr,
+ setter=True)
+ def wrap_return_value(self):
+ # We have no return value
+ return "\nreturn true;"
+ def getArgc(self):
+ return "1"
+ def getArgvDecl(self):
+ # We just get our stuff from our last arg no matter what
+ return ""
+
+class FakeCastableDescriptor():
+ def __init__(self, descriptor):
+ self.castable = True
+ self.workers = descriptor.workers
+ self.nativeType = descriptor.nativeType
+ self.name = descriptor.name
+ self.hasXPConnectImpls = descriptor.hasXPConnectImpls
+
+class CGAbstractBindingMethod(CGAbstractStaticMethod):
+ """
+ Common class to generate the JSNatives for all our methods, getters, and
+ setters. This will generate the function declaration and unwrap the
+ |this| object. Subclasses are expected to override the generate_code
+ function to do the rest of the work. This function should return a
+ CGThing which is already properly indented.
+ """
+ def __init__(self, descriptor, name, args, unwrapFailureCode=None):
+ CGAbstractStaticMethod.__init__(self, descriptor, name, "JSBool", args)
+
+ if unwrapFailureCode is None:
+ self.unwrapFailureCode = ("return Throw<%s>(cx, rv);" %
+ toStringBool(not descriptor.workers))
+ else:
+ self.unwrapFailureCode = unwrapFailureCode
+
+ def definition_body(self):
+ # Our descriptor might claim that we're not castable, simply because
+ # we're someone's consequential interface. But for this-unwrapping, we
+ # know that we're the real deal. So fake a descriptor here for
+ # consumption by FailureFatalCastableObjectUnwrapper.
+ unwrapThis = CGIndenter(CGGeneric(
+ str(CastableObjectUnwrapper(
+ FakeCastableDescriptor(self.descriptor),
+ "obj", "self", self.unwrapFailureCode))))
+ return CGList([ self.getThis(), unwrapThis,
+ self.generate_code() ], "\n").define()
+
+ def getThis(self):
+ return CGIndenter(
+ CGGeneric("js::RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));\n"
+ "if (!obj) {\n"
+ " return false;\n"
+ "}\n"
+ "\n"
+ "%s* self;" % self.descriptor.nativeType))
+
+ def generate_code(self):
+ assert(False) # Override me
+
+def MakeNativeName(name):
+ return name[0].upper() + name[1:]
+
+class CGGenericMethod(CGAbstractBindingMethod):
+ """
+ A class for generating the C++ code for an IDL method..
+ """
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'), Argument('unsigned', 'argc'),
+ Argument('JS::Value*', 'vp')]
+ CGAbstractBindingMethod.__init__(self, descriptor, 'genericMethod', args)
+
+ def generate_code(self):
+ return CGIndenter(CGGeneric(
+ "const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));\n"
+ "JSJitMethodOp method = (JSJitMethodOp)info->op;\n"
+ "return method(cx, obj, self, argc, vp);"))
+
+class CGSpecializedMethod(CGAbstractStaticMethod):
+ """
+ A class for generating the C++ code for a specialized method that the JIT
+ can call with lower overhead.
+ """
+ def __init__(self, descriptor, method):
+ self.method = method
+ name = method.identifier.name
+ args = [Argument('JSContext*', 'cx'), Argument('JSHandleObject', 'obj'),
+ Argument('%s*' % descriptor.nativeType, 'self'),
+ Argument('unsigned', 'argc'), Argument('JS::Value*', 'vp')]
+ CGAbstractStaticMethod.__init__(self, descriptor, name, 'bool', args)
+
+ def definition_body(self):
+ name = self.method.identifier.name
+ nativeName = MakeNativeName(self.descriptor.binaryNames.get(name, name))
+ return CGMethodCall([], nativeName, self.method.isStatic(),
+ self.descriptor, self.method).define()
+
+class CGGenericGetter(CGAbstractBindingMethod):
+ """
+ A class for generating the C++ code for an IDL attribute getter.
+ """
+ def __init__(self, descriptor, lenientThis=False):
+ args = [Argument('JSContext*', 'cx'), Argument('unsigned', 'argc'),
+ Argument('JS::Value*', 'vp')]
+ if lenientThis:
+ name = "genericLenientGetter"
+ unwrapFailureCode = (
+ "MOZ_ASSERT(!JS_IsExceptionPending(cx));\n"
+ "JS_SET_RVAL(cx, vp, JS::UndefinedValue());\n"
+ "return true;")
+ else:
+ name = "genericGetter"
+ unwrapFailureCode = None
+ CGAbstractBindingMethod.__init__(self, descriptor, name, args,
+ unwrapFailureCode)
+
+ def generate_code(self):
+ return CGIndenter(CGGeneric(
+ "const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));\n"
+ "JSJitPropertyOp getter = info->op;\n"
+ "return getter(cx, obj, self, vp);"))
+
+class CGSpecializedGetter(CGAbstractStaticMethod):
+ """
+ A class for generating the code for a specialized attribute getter
+ that the JIT can call with lower overhead.
+ """
+ def __init__(self, descriptor, attr):
+ self.attr = attr
+ name = 'get_' + attr.identifier.name
+ args = [ Argument('JSContext*', 'cx'),
+ Argument('JSHandleObject', 'obj'),
+ Argument('%s*' % descriptor.nativeType, 'self'),
+ Argument('JS::Value*', 'vp') ]
+ CGAbstractStaticMethod.__init__(self, descriptor, name, "bool", args)
+
+ def definition_body(self):
+ name = self.attr.identifier.name
+ nativeName = MakeNativeName(self.descriptor.binaryNames.get(name, name))
+ # resultOutParam does not depend on whether resultAlreadyAddRefed is set
+ (_, resultOutParam) = getRetvalDeclarationForType(self.attr.type,
+ self.descriptor,
+ False)
+ infallible = ('infallible' in
+ self.descriptor.getExtendedAttributes(self.attr,
+ getter=True))
+ if resultOutParam or self.attr.type.nullable() or not infallible:
+ nativeName = "Get" + nativeName
+ return CGIndenter(CGGetterCall(self.attr.type, nativeName,
+ self.descriptor, self.attr)).define()
+
+class CGGenericSetter(CGAbstractBindingMethod):
+ """
+ A class for generating the C++ code for an IDL attribute setter.
+ """
+ def __init__(self, descriptor, lenientThis=False):
+ args = [Argument('JSContext*', 'cx'), Argument('unsigned', 'argc'),
+ Argument('JS::Value*', 'vp')]
+ if lenientThis:
+ name = "genericLenientSetter"
+ unwrapFailureCode = (
+ "MOZ_ASSERT(!JS_IsExceptionPending(cx));\n"
+ "return true;")
+ else:
+ name = "genericSetter"
+ unwrapFailureCode = None
+ CGAbstractBindingMethod.__init__(self, descriptor, name, args,
+ unwrapFailureCode)
+
+ def generate_code(self):
+ return CGIndenter(CGGeneric(
+ "JS::Value* argv = JS_ARGV(cx, vp);\n"
+ "JS::Value undef = JS::UndefinedValue();\n"
+ "if (argc == 0) {\n"
+ " argv = &undef;\n"
+ "}\n"
+ "const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));\n"
+ "JSJitPropertyOp setter = info->op;\n"
+ "if (!setter(cx, obj, self, argv)) {\n"
+ " return false;\n"
+ "}\n"
+ "*vp = JSVAL_VOID;\n"
+ "return true;"))
+
+class CGSpecializedSetter(CGAbstractStaticMethod):
+ """
+ A class for generating the code for a specialized attribute setter
+ that the JIT can call with lower overhead.
+ """
+ def __init__(self, descriptor, attr):
+ self.attr = attr
+ name = 'set_' + attr.identifier.name
+ args = [ Argument('JSContext*', 'cx'),
+ Argument('JSHandleObject', 'obj'),
+ Argument('%s*' % descriptor.nativeType, 'self'),
+ Argument('JS::Value*', 'argv')]
+ CGAbstractStaticMethod.__init__(self, descriptor, name, "bool", args)
+
+ def definition_body(self):
+ name = self.attr.identifier.name
+ nativeName = "Set" + MakeNativeName(self.descriptor.binaryNames.get(name, name))
+ return CGIndenter(CGSetterCall(self.attr.type, nativeName,
+ self.descriptor, self.attr)).define()
+
+def memberIsCreator(member):
+ return member.getExtendedAttribute("Creator") is not None
+
+class CGMemberJITInfo(CGThing):
+ """
+ A class for generating the JITInfo for a property that points to
+ our specialized getter and setter.
+ """
+ def __init__(self, descriptor, member):
+ self.member = member
+ self.descriptor = descriptor
+
+ def declare(self):
+ return ""
+
+ def defineJitInfo(self, infoName, opName, infallible):
+ protoID = "prototypes::id::%s" % self.descriptor.name
+ depth = "PrototypeTraits<%s>::Depth" % protoID
+ failstr = "true" if infallible else "false"
+ return ("\n"
+ "const JSJitInfo %s = {\n"
+ " %s,\n"
+ " %s,\n"
+ " %s,\n"
+ " %s, /* isInfallible. False in setters. */\n"
+ " false /* isConstant. Only relevant for getters. */\n"
+ "};\n" % (infoName, opName, protoID, depth, failstr))
+
+ def define(self):
+ if self.member.isAttr():
+ getterinfo = ("%s_getterinfo" % self.member.identifier.name)
+ getter = ("(JSJitPropertyOp)get_%s" % self.member.identifier.name)
+ getterinfal = "infallible" in self.descriptor.getExtendedAttributes(self.member, getter=True)
+ getterinfal = getterinfal and infallibleForMember(self.member, self.member.type, self.descriptor)
+ result = self.defineJitInfo(getterinfo, getter, getterinfal)
+ if not self.member.readonly:
+ setterinfo = ("%s_setterinfo" % self.member.identifier.name)
+ setter = ("(JSJitPropertyOp)set_%s" % self.member.identifier.name)
+ # Setters are always fallible, since they have to do a typed unwrap.
+ result += self.defineJitInfo(setterinfo, setter, False)
+ return result
+ if self.member.isMethod():
+ methodinfo = ("%s_methodinfo" % self.member.identifier.name)
+ # Actually a JSJitMethodOp, but JSJitPropertyOp by struct definition.
+ method = ("(JSJitPropertyOp)%s" % self.member.identifier.name)
+
+ # Methods are infallible if they are infallible, have no arguments
+ # to unwrap, and have a return type that's infallible to wrap up for
+ # return.
+ methodInfal = False
+ sigs = self.member.signatures()
+ if len(sigs) == 1:
+ # Don't handle overloading. If there's more than one signature,
+ # one of them must take arguments.
+ sig = sigs[0]
+ if len(sig[1]) == 0 and infallibleForMember(self.member, sig[0], self.descriptor):
+ # No arguments and infallible return boxing
+ methodInfal = True
+
+ result = self.defineJitInfo(methodinfo, method, methodInfal)
+ return result
+ raise TypeError("Illegal member type to CGPropertyJITInfo")
+
+def getEnumValueName(value):
+ # Some enum values can be empty strings. Others might have weird
+ # characters in them. Deal with the former by returning "_empty",
+ # deal with possible name collisions from that by throwing if the
+ # enum value is actually "_empty", and throw on any value
+ # containing chars other than [a-z] or '-' for now. Replace '-' with '_'.
+ value = value.replace('-', '_')
+ if value == "_empty":
+ raise SyntaxError('"_empty" is not an IDL enum value we support yet')
+ if value == "":
+ return "_empty"
+ if not re.match("^[a-z_]+$", value):
+ raise SyntaxError('Enum value "' + value + '" contains characters '
+ 'outside [a-z_]')
+ return MakeNativeName(value)
+
+class CGEnum(CGThing):
+ def __init__(self, enum):
+ CGThing.__init__(self)
+ self.enum = enum
+
+ def declare(self):
+ return """
+ enum valuelist {
+ %s
+ };
+
+ extern const EnumEntry strings[%d];
+""" % (",\n ".join(map(getEnumValueName, self.enum.values())),
+ len(self.enum.values()) + 1)
+
+ def define(self):
+ return """
+ const EnumEntry strings[%d] = {
+ %s,
+ { NULL, 0 }
+ };
+""" % (len(self.enum.values()) + 1,
+ ",\n ".join(['{"' + val + '", ' + str(len(val)) + '}' for val in self.enum.values()]))
+
+def getUnionAccessorSignatureType(type, descriptorProvider):
+ """
+ Returns the types that are used in the getter and setter signatures for
+ union types
+ """
+ if type.isArray():
+ raise TypeError("Can't handle array arguments yet")
+
+ if type.isSequence():
+ nullable = type.nullable();
+ if nullable:
+ type = type.inner.inner
+ else:
+ type = type.inner
+ (elementTemplate, elementDeclType,
+ elementHolderType, dealWithOptional) = getJSToNativeConversionTemplate(
+ type, descriptorProvider, isSequenceMember=True)
+ typeName = CGWrapper(elementDeclType, pre="Sequence< ", post=" >&")
+ if nullable:
+ typeName = CGWrapper(typeName, pre="Nullable< ", post=" >&")
+
+ return typeName
+
+ if type.isUnion():
+ typeName = CGGeneric(type.name)
+ if type.nullable():
+ typeName = CGWrapper(typeName, pre="Nullable< ", post=" >&")
+
+ return typeName
+
+ if type.isGeckoInterface():
+ descriptor = descriptorProvider.getDescriptor(
+ type.unroll().inner.identifier.name)
+ typeName = CGGeneric(descriptor.nativeType)
+ # Allow null pointers for nullable types and old-binding classes
+ if type.nullable() or type.unroll().inner.isExternal():
+ typeName = CGWrapper(typeName, post="*")
+ else:
+ typeName = CGWrapper(typeName, post="&")
+ return typeName
+
+ if type.isSpiderMonkeyInterface():
+ typeName = CGGeneric(type.name)
+ if type.nullable():
+ typeName = CGWrapper(typeName, post="*")
+ else:
+ typeName = CGWrapper(typeName, post="&")
+ return typeName
+
+ if type.isString():
+ return CGGeneric("const nsAString&")
+
+ if type.isEnum():
+ if type.nullable():
+ raise TypeError("We don't support nullable enumerated arguments or "
+ "union members yet")
+ return CGGeneric(type.inner.identifier.name)
+
+ if type.isCallback():
+ return CGGeneric("JSObject*")
+
+ if type.isAny():
+ return CGGeneric("JS::Value")
+
+ if type.isObject():
+ typeName = CGGeneric("JSObject")
+ if type.nullable():
+ typeName = CGWrapper(typeName, post="*")
+ else:
+ typeName = CGWrapper(typeName, post="&")
+ return typeName
+
+ if not type.isPrimitive():
+ raise TypeError("Need native type for argument type '%s'" % str(type))
+
+ typeName = CGGeneric(builtinNames[type.tag()])
+ if type.nullable():
+ typeName = CGWrapper(typeName, pre="Nullable< ", post=" >&")
+ return typeName
+
+def getUnionTypeTemplateVars(type, descriptorProvider):
+ # For dictionaries and sequences we need to pass None as the failureCode
+ # for getJSToNativeConversionTemplate.
+ # Also, for dictionaries we would need to handle conversion of
+ # null/undefined to the dictionary correctly.
+ if type.isDictionary() or type.isSequence():
+ raise TypeError("Can't handle dictionaries or sequences in unions")
+
+ if type.isGeckoInterface():
+ name = type.inner.identifier.name
+ elif type.isEnum():
+ name = type.inner.identifier.name
+ elif type.isArray() or type.isSequence():
+ name = str(type)
+ else:
+ name = type.name
+
+ tryNextCode = """tryNext = true;
+return true;"""
+ if type.isGeckoInterface():
+ tryNextCode = ("""if (mUnion.mType != mUnion.eUninitialized) {
+ mUnion.Destroy%s();
+}""" % name) + tryNextCode
+ (template, declType, holderType,
+ dealWithOptional) = getJSToNativeConversionTemplate(
+ type, descriptorProvider, failureCode=tryNextCode,
+ isDefinitelyObject=True)
+
+ # This is ugly, but UnionMember needs to call a constructor with no
+ # arguments so the type can't be const.
+ structType = declType.define()
+ if structType.startswith("const "):
+ structType = structType[6:]
+ externalType = getUnionAccessorSignatureType(type, descriptorProvider).define()
+
+ if type.isObject():
+ setter = CGGeneric("void SetToObject(JSObject* obj)\n"
+ "{\n"
+ " mUnion.mValue.mObject.SetValue() = obj;\n"
+ " mUnion.mType = mUnion.eObject;\n"
+ "}")
+ else:
+ jsConversion = string.Template(template).substitute(
+ {
+ "val": "value",
+ "valPtr": "pvalue",
+ "declName": "SetAs" + name + "()",
+ "holderName": "m" + name + "Holder"
+ }
+ )
+ jsConversion = CGWrapper(CGGeneric(jsConversion),
+ post="\n"
+ "return true;")
+ setter = CGWrapper(CGIndenter(jsConversion),
+ pre="bool TrySetTo" + name + "(JSContext* cx, const JS::Value& value, JS::Value* pvalue, bool& tryNext)\n"
+ "{\n"
+ " tryNext = false;\n",
+ post="\n"
+ "}")
+
+ return {
+ "name": name,
+ "structType": structType,
+ "externalType": externalType,
+ "setter": CGIndenter(setter).define(),
+ "holderType": holderType.define() if holderType else None
+ }
+
+def mapTemplate(template, templateVarArray):
+ return map(lambda v: string.Template(template).substitute(v),
+ templateVarArray)
+
+class CGUnionStruct(CGThing):
+ def __init__(self, type, descriptorProvider):
+ CGThing.__init__(self)
+ self.type = type.unroll()
+ self.descriptorProvider = descriptorProvider
+
+ def declare(self):
+ templateVars = map(lambda t: getUnionTypeTemplateVars(t, self.descriptorProvider),
+ self.type.flatMemberTypes)
+
+ callDestructors = []
+ enumValues = []
+ methods = []
+ if self.type.hasNullableType:
+ callDestructors.append(" case eNull:\n"
+ " break;")
+ enumValues.append("eNull")
+ methods.append(""" bool IsNull() const
+ {
+ return mType == eNull;
+ }""")
+
+ destructorTemplate = """ void Destroy${name}()
+ {
+ MOZ_ASSERT(Is${name}(), "Wrong type!");
+ mValue.m${name}.Destroy();
+ mType = eUninitialized;
+ }"""
+ destructors = mapTemplate(destructorTemplate, templateVars)
+ callDestructors.extend(mapTemplate(" case e${name}:\n"
+ " Destroy${name}();\n"
+ " break;", templateVars))
+ enumValues.extend(mapTemplate("e${name}", templateVars))
+ methodTemplate = """ bool Is${name}() const
+ {
+ return mType == e${name};
+ }
+ ${externalType} GetAs${name}() const
+ {
+ MOZ_ASSERT(Is${name}(), "Wrong type!");
+ // The cast to ${externalType} is needed to work around a bug in Apple's
+ // clang compiler, for some reason it doesn't call |S::operator T&| when
+ // casting S<T> to T& and T is forward declared.
+ return (${externalType})mValue.m${name}.Value();
+ }
+ ${structType}& SetAs${name}()
+ {
+ mType = e${name};
+ return mValue.m${name}.SetValue();
+ }"""
+ methods.extend(mapTemplate(methodTemplate, templateVars))
+ values = mapTemplate("UnionMember<${structType} > m${name};", templateVars)
+ return string.Template("""
+class ${structName} {
+public:
+ ${structName}() : mType(eUninitialized)
+ {
+ }
+ ~${structName}()
+ {
+ switch (mType) {
+${callDestructors}
+ case eUninitialized:
+ break;
+ }
+ }
+
+${methods}
+
+private:
+ friend class ${structName}Argument;
+
+${destructors}
+
+ enum Type {
+ eUninitialized,
+ ${enumValues}
+ };
+ union Value {
+ ${values}
+ };
+
+ Type mType;
+ Value mValue;
+};
+
+""").substitute(
+ {
+ "structName": self.type.__str__(),
+ "callDestructors": "\n".join(callDestructors),
+ "destructors": "\n".join(destructors),
+ "methods": "\n\n".join(methods),
+ "enumValues": ",\n ".join(enumValues),
+ "values": "\n ".join(values),
+ })
+
+ def define(self):
+ return """
+"""
+
+class CGUnionConversionStruct(CGThing):
+ def __init__(self, type, descriptorProvider):
+ CGThing.__init__(self)
+ self.type = type.unroll()
+ self.descriptorProvider = descriptorProvider
+
+ def declare(self):
+ setters = []
+
+ if self.type.hasNullableType:
+ setters.append(""" bool SetNull()
+ {
+ mUnion.mType = mUnion.eNull;
+ return true;
+ }""")
+
+ templateVars = map(lambda t: getUnionTypeTemplateVars(t, self.descriptorProvider),
+ self.type.flatMemberTypes)
+ structName = self.type.__str__()
+
+ setters.extend(mapTemplate("${setter}", templateVars))
+ private = "\n".join(mapTemplate(""" ${structType}& SetAs${name}()
+ {
+ mUnion.mType = mUnion.e${name};
+ return mUnion.mValue.m${name}.SetValue();
+ }""", templateVars))
+ private += "\n\n"
+ holders = filter(lambda v: v["holderType"] is not None, templateVars)
+ if len(holders) > 0:
+ private += "\n".join(mapTemplate(" ${holderType} m${name}Holder;", holders))
+ private += "\n\n"
+ private += " " + structName + "& mUnion;"
+ return string.Template("""
+class ${structName}Argument {
+public:
+ ${structName}Argument(const ${structName}& aUnion) : mUnion(const_cast<${structName}&>(aUnion))
+ {
+ }
+
+${setters}
+
+private:
+${private}
+};
+""").substitute({"structName": structName,
+ "setters": "\n\n".join(setters),
+ "private": private
+ })
+
+ def define(self):
+ return """
+"""
+
+class ClassItem:
+ """ Use with CGClass """
+ def __init__(self, name, visibility):
+ self.name = name
+ self.visibility = visibility
+ def declare(self, cgClass):
+ assert False
+ def define(self, cgClass):
+ assert False
+
+class ClassBase(ClassItem):
+ def __init__(self, name, visibility='public'):
+ ClassItem.__init__(self, name, visibility)
+ def declare(self, cgClass):
+ return '%s %s' % (self.visibility, self.name)
+ def define(self, cgClass):
+ # Only in the header
+ return ''
+
+class ClassMethod(ClassItem):
+ def __init__(self, name, returnType, args, inline=False, static=False,
+ virtual=False, const=False, bodyInHeader=False,
+ templateArgs=None, visibility='public', body=None):
+ self.returnType = returnType
+ self.args = args
+ self.inline = inline or bodyInHeader
+ self.static = static
+ self.virtual = virtual
+ self.const = const
+ self.bodyInHeader = bodyInHeader
+ self.templateArgs = templateArgs
+ self.body = body
+ ClassItem.__init__(self, name, visibility)
+
+ def getDecorators(self, declaring):
+ decorators = []
+ if self.inline:
+ decorators.append('inline')
+ if declaring:
+ if self.static:
+ decorators.append('static')
+ if self.virtual:
+ decorators.append('virtual')
+ if decorators:
+ return ' '.join(decorators) + ' '
+ return ''
+
+ def getBody(self):
+ # Override me or pass a string to constructor
+ assert self.body is not None
+ return self.body
+
+ def declare(self, cgClass):
+ templateClause = 'template <%s>\n' % ', '.join(self.templateArgs) \
+ if self.bodyInHeader and self.templateArgs else ''
+ args = ', '.join([str(a) for a in self.args])
+ if self.bodyInHeader:
+ body = CGIndenter(CGGeneric(self.getBody())).define()
+ body = '\n{\n' + body + '\n}'
+ else:
+ body = ';'
+
+ return string.Template("""${templateClause}${decorators}${returnType}
+${name}(${args})${const}${body}
+""").substitute({ 'templateClause': templateClause,
+ 'decorators': self.getDecorators(True),
+ 'returnType': self.returnType,
+ 'name': self.name,
+ 'const': ' const' if self.const else '',
+ 'args': args,
+ 'body': body })
+
+ def define(self, cgClass):
+ if self.bodyInHeader:
+ return ''
+
+ templateArgs = cgClass.templateArgs
+ if templateArgs:
+ if cgClass.templateSpecialization:
+ templateArgs = \
+ templateArgs[len(cgClass.templateSpecialization):]
+
+ if templateArgs:
+ templateClause = \
+ 'template <%s>\n' % ', '.join([str(a) for a in templateArgs])
+ else:
+ templateClause = ''
+
+ args = ', '.join([str(a) for a in self.args])
+
+ body = CGIndenter(CGGeneric(self.getBody())).define()
+
+ return string.Template("""${templateClause}${decorators}${returnType}
+${className}::${name}(${args})${const}
+{
+${body}
+}\n
+""").substitute({ 'templateClause': templateClause,
+ 'decorators': self.getDecorators(False),
+ 'returnType': self.returnType,
+ 'className': cgClass.getNameString(),
+ 'name': self.name,
+ 'args': args,
+ 'const': ' const' if self.const else '',
+ 'body': body })
+
+class ClassConstructor(ClassItem):
+ """
+ Used for adding a constructor to a CGClass.
+
+ args is a list of Argument objects that are the arguments taken by the
+ constructor.
+
+ inline should be True if the constructor should be marked inline.
+
+ bodyInHeader should be True if the body should be placed in the class
+ declaration in the header.
+
+ visibility determines the visibility of the constructor (public,
+ protected, private), defaults to private.
+
+ baseConstructors is a list of strings containing calls to base constructors,
+ defaults to None.
+
+ body contains a string with the code for the constructor, defaults to None.
+ """
+ def __init__(self, args, inline=False, bodyInHeader=False,
+ visibility="private", baseConstructors=None, body=None):
+ self.args = args
+ self.inline = inline or bodyInHeader
+ self.bodyInHeader = bodyInHeader
+ self.baseConstructors = baseConstructors
+ self.body = body
+ ClassItem.__init__(self, None, visibility)
+
+ def getDecorators(self, declaring):
+ decorators = []
+ if self.inline and declaring:
+ decorators.append('inline')
+ if decorators:
+ return ' '.join(decorators) + ' '
+ return ''
+
+ def getInitializationList(self, cgClass):
+ items = [str(c) for c in self.baseConstructors]
+ for m in cgClass.members:
+ if not m.static:
+ initialize = m.getBody()
+ if initialize:
+ items.append(m.name + "(" + initialize + ")")
+
+ if len(items) > 0:
+ return '\n : ' + ',\n '.join(items)
+ return ''
+
+ def getBody(self):
+ assert self.body is not None
+ return self.body
+
+ def declare(self, cgClass):
+ args = ', '.join([str(a) for a in self.args])
+ if self.bodyInHeader:
+ body = ' ' + self.getBody();
+ body = stripTrailingWhitespace(body.replace('\n', '\n '))
+ if len(body) > 0:
+ body += '\n'
+ body = self.getInitializationList(cgClass) + '\n{\n' + body + '}'
+ else:
+ body = ';'
+
+ return string.Template("""${decorators}${className}(${args})${body}
+""").substitute({ 'decorators': self.getDecorators(True),
+ 'className': cgClass.getNameString(),
+ 'args': args,
+ 'body': body })
+
+ def define(self, cgClass):
+ if self.bodyInHeader:
+ return ''
+
+ args = ', '.join([str(a) for a in self.args])
+
+ body = ' ' + self.getBody()
+ body = '\n' + stripTrailingWhitespace(body.replace('\n', '\n '))
+ if len(body) > 0:
+ body += '\n'
+
+ return string.Template("""${decorators}
+${className}::${className}(${args})${initializationList}
+{${body}}\n
+""").substitute({ 'decorators': self.getDecorators(False),
+ 'className': cgClass.getNameString(),
+ 'args': args,
+ 'initializationList': self.getInitializationList(cgClass),
+ 'body': body })
+
+class ClassMember(ClassItem):
+ def __init__(self, name, type, visibility="private", static=False,
+ body=None):
+ self.type = type;
+ self.static = static
+ self.body = body
+ ClassItem.__init__(self, name, visibility)
+
+ def declare(self, cgClass):
+ return '%s%s %s;\n' % ('static ' if self.static else '', self.type,
+ self.name)
+
+ def define(self, cgClass):
+ if not self.static:
+ return ''
+ if self.body:
+ body = " = " + self.body
+ else:
+ body = ""
+ return '%s %s::%s%s;\n' % (self.type, cgClass.getNameString(),
+ self.name, body)
+
+class ClassTypedef(ClassItem):
+ def __init__(self, name, type, visibility="public"):
+ self.type = type
+ ClassItem.__init__(self, name, visibility)
+
+ def declare(self, cgClass):
+ return 'typedef %s %s;\n' % (self.type, self.name)
+
+ def define(self, cgClass):
+ # Only goes in the header
+ return ''
+
+class ClassEnum(ClassItem):
+ def __init__(self, name, entries, values=None, visibility="public"):
+ self.entries = entries
+ self.values = values
+ ClassItem.__init__(self, name, visibility)
+
+ def declare(self, cgClass):
+ entries = []
+ for i in range(0, len(self.entries)):
+ if i >= len(self.values):
+ entry = '%s' % self.entries[i]
+ else:
+ entry = '%s = %s' % (self.entries[i], self.values[i])
+ entries.append(entry)
+ name = '' if not self.name else ' ' + self.name
+ return 'enum%s\n{\n %s\n};\n' % (name, ',\n '.join(entries))
+
+ def define(self, cgClass):
+ # Only goes in the header
+ return ''
+
+class CGClass(CGThing):
+ def __init__(self, name, bases=[], members=[], constructors=[], methods=[],
+ typedefs = [], enums=[], templateArgs=[],
+ templateSpecialization=[], isStruct=False, indent=''):
+ CGThing.__init__(self)
+ self.name = name
+ self.bases = bases
+ self.members = members
+ self.constructors = constructors
+ self.methods = methods
+ self.typedefs = typedefs
+ self.enums = enums
+ self.templateArgs = templateArgs
+ self.templateSpecialization = templateSpecialization
+ self.isStruct = isStruct
+ self.indent = indent
+ self.defaultVisibility ='public' if isStruct else 'private'
+
+ def getNameString(self):
+ className = self.name
+ if self.templateSpecialization:
+ className = className + \
+ '<%s>' % ', '.join([str(a) for a
+ in self.templateSpecialization])
+ return className
+
+ def declare(self):
+ result = ''
+ if self.templateArgs:
+ templateArgs = [str(a) for a in self.templateArgs]
+ templateArgs = templateArgs[len(self.templateSpecialization):]
+ result = result + self.indent + 'template <%s>\n' \
+ % ','.join([str(a) for a in templateArgs])
+
+ type = 'struct' if self.isStruct else 'class'
+
+ if self.templateSpecialization:
+ specialization = \
+ '<%s>' % ', '.join([str(a) for a in self.templateSpecialization])
+ else:
+ specialization = ''
+
+ result = result + '%s%s %s%s' \
+ % (self.indent, type, self.name, specialization)
+
+ if self.bases:
+ result = result + ' : %s' % ', '.join([d.declare(self) for d in self.bases])
+
+ result = result + '\n%s{\n' % self.indent
+
+ def declareMembers(cgClass, memberList, defaultVisibility, itemCount,
+ separator=''):
+ members = { 'private': [], 'protected': [], 'public': [] }
+
+ for member in memberList:
+ members[member.visibility].append(member)
+
+
+ if defaultVisibility == 'public':
+ order = [ 'public', 'protected', 'private' ]
+ else:
+ order = [ 'private', 'protected', 'public' ]
+
+ result = ''
+
+ lastVisibility = defaultVisibility
+ for visibility in order:
+ list = members[visibility]
+ if list:
+ if visibility != lastVisibility:
+ if itemCount:
+ result = result + '\n'
+ result = result + visibility + ':\n'
+ itemCount = 0
+ for member in list:
+ if itemCount != 0:
+ result = result + separator
+ declaration = member.declare(cgClass)
+ declaration = CGIndenter(CGGeneric(declaration)).define()
+ result = result + declaration
+ itemCount = itemCount + 1
+ lastVisibility = visibility
+ return (result, lastVisibility, itemCount)
+
+ order = [(self.enums, ''), (self.typedefs, ''), (self.members, ''),
+ (self.constructors, '\n'), (self.methods, '\n')]
+
+ lastVisibility = self.defaultVisibility
+ itemCount = 0
+ for (memberList, separator) in order:
+ (memberString, lastVisibility, itemCount) = \
+ declareMembers(self, memberList, lastVisibility, itemCount,
+ separator)
+ if self.indent:
+ memberString = CGIndenter(CGGeneric(memberString),
+ len(self.indent)).define()
+ result = result + memberString
+
+ result = result + self.indent + '};\n'
+ return result
+
+ def define(self):
+ def defineMembers(cgClass, memberList, itemCount, separator=''):
+ result = ''
+ for member in memberList:
+ if itemCount != 0:
+ result = result + separator
+ result = result + member.define(cgClass)
+ itemCount = itemCount + 1
+ return (result, itemCount)
+
+ order = [(self.members, '\n'), (self.constructors, '\n'),
+ (self.methods, '\n')]
+
+ result = ''
+ itemCount = 0
+ for (memberList, separator) in order:
+ (memberString, itemCount) = defineMembers(self, memberList,
+ itemCount, separator)
+ result = result + memberString
+ return result
+
+class CGResolveOwnProperty(CGAbstractMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'wrapper'),
+ Argument('jsid', 'id'), Argument('bool', 'set'),
+ Argument('JSPropertyDescriptor*', 'desc')]
+ CGAbstractMethod.__init__(self, descriptor, "ResolveOwnProperty", "bool", args)
+ def definition_body(self):
+ return """ JSObject* obj = wrapper;
+ if (xpc::WrapperFactory::IsXrayWrapper(obj)) {
+ obj = js::UnwrapObject(obj);
+ }
+ // We rely on getOwnPropertyDescriptor not shadowing prototype properties by named
+ // properties. If that changes we'll need to filter here.
+ return js::GetProxyHandler(obj)->getOwnPropertyDescriptor(cx, wrapper, id, set, desc);
+"""
+
+class CGEnumerateOwnProperties(CGAbstractMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'wrapper'),
+ Argument('JS::AutoIdVector&', 'props')]
+ CGAbstractMethod.__init__(self, descriptor, "EnumerateOwnProperties", "bool", args)
+ def definition_body(self):
+ return """ JSObject* obj = wrapper;
+ if (xpc::WrapperFactory::IsXrayWrapper(obj)) {
+ obj = js::UnwrapObject(obj);
+ }
+ // We rely on getOwnPropertyNames not shadowing prototype properties by named
+ // properties. If that changes we'll need to filter here.
+ return js::GetProxyHandler(obj)->getOwnPropertyNames(cx, wrapper, props);
+"""
+
+class CGXrayHelper(CGAbstractMethod):
+ def __init__(self, descriptor, name, args, properties):
+ CGAbstractMethod.__init__(self, descriptor, name, "bool", args)
+ self.properties = properties
+
+ def definition_body(self):
+ varNames = self.properties.variableNames(True)
+
+ methods = self.properties.methods
+ if methods.hasNonChromeOnly() or methods.hasChromeOnly():
+ methodArgs = """// %(methods)s has an end-of-list marker at the end that we ignore
+%(methods)s, %(methods)s_ids, %(methods)s_specs, ArrayLength(%(methods)s) - 1""" % varNames
+ else:
+ methodArgs = "NULL, NULL, NULL, 0"
+ methodArgs = CGGeneric(methodArgs)
+
+ attrs = self.properties.attrs
+ if attrs.hasNonChromeOnly() or attrs.hasChromeOnly():
+ attrArgs = """// %(attrs)s has an end-of-list marker at the end that we ignore
+%(attrs)s, %(attrs)s_ids, %(attrs)s_specs, ArrayLength(%(attrs)s) - 1""" % varNames
+ else:
+ attrArgs = "NULL, NULL, NULL, 0"
+ attrArgs = CGGeneric(attrArgs)
+
+ consts = self.properties.consts
+ if consts.hasNonChromeOnly() or consts.hasChromeOnly():
+ constArgs = """// %(consts)s has an end-of-list marker at the end that we ignore
+%(consts)s, %(consts)s_ids, %(consts)s_specs, ArrayLength(%(consts)s) - 1""" % varNames
+ else:
+ constArgs = "NULL, NULL, NULL, 0"
+ constArgs = CGGeneric(constArgs)
+
+ prefixArgs = CGGeneric(self.getPrefixArgs())
+
+ return CGIndenter(
+ CGWrapper(CGList([prefixArgs, methodArgs, attrArgs, constArgs], ",\n"),
+ pre=("return Xray%s(" % self.name),
+ post=");",
+ reindent=True)).define()
+
+class CGResolveProperty(CGXrayHelper):
+ def __init__(self, descriptor, properties):
+ args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'wrapper'),
+ Argument('jsid', 'id'), Argument('bool', 'set'),
+ Argument('JSPropertyDescriptor*', 'desc')]
+ CGXrayHelper.__init__(self, descriptor, "ResolveProperty", args,
+ properties)
+
+ def getPrefixArgs(self):
+ return "cx, wrapper, id, desc"
+
+
+class CGEnumerateProperties(CGXrayHelper):
+ def __init__(self, descriptor, properties):
+ args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'wrapper'),
+ Argument('JS::AutoIdVector&', 'props')]
+ CGXrayHelper.__init__(self, descriptor, "EnumerateProperties", args,
+ properties)
+
+ def getPrefixArgs(self):
+ return "props"
+
+class CGPrototypeTraitsClass(CGClass):
+ def __init__(self, descriptor, indent=''):
+ templateArgs = [Argument('prototypes::ID', 'PrototypeID')]
+ templateSpecialization = ['prototypes::id::' + descriptor.name]
+ enums = [ClassEnum('', ['Depth'],
+ [descriptor.interface.inheritanceDepth()])]
+ typedefs = [ClassTypedef('NativeType', descriptor.nativeType)]
+ CGClass.__init__(self, 'PrototypeTraits', indent=indent,
+ templateArgs=templateArgs,
+ templateSpecialization=templateSpecialization,
+ enums=enums, typedefs=typedefs, isStruct=True)
+
+class CGPrototypeIDMapClass(CGClass):
+ def __init__(self, descriptor, indent=''):
+ templateArgs = [Argument('class', 'ConcreteClass')]
+ templateSpecialization = [descriptor.nativeType]
+ enums = [ClassEnum('', ['PrototypeID'],
+ ['prototypes::id::' + descriptor.name])]
+ CGClass.__init__(self, 'PrototypeIDMap', indent=indent,
+ templateArgs=templateArgs,
+ templateSpecialization=templateSpecialization,
+ enums=enums, isStruct=True)
+
+class CGClassForwardDeclare(CGThing):
+ def __init__(self, name, isStruct=False):
+ CGThing.__init__(self)
+ self.name = name
+ self.isStruct = isStruct
+ def declare(self):
+ type = 'struct' if self.isStruct else 'class'
+ return '%s %s;\n' % (type, self.name)
+ def define(self):
+ # Header only
+ return ''
+
+class CGProxySpecialOperation(CGPerSignatureCall):
+ """
+ Base class for classes for calling an indexed or named special operation
+ (don't use this directly, use the derived classes below).
+ """
+ def __init__(self, descriptor, operation):
+ nativeName = MakeNativeName(descriptor.binaryNames.get(operation, operation))
+ operation = descriptor.operations[operation]
+ assert len(operation.signatures()) == 1
+ signature = operation.signatures()[0]
+ extendedAttributes = descriptor.getExtendedAttributes(operation)
+
+ (returnType, arguments) = signature
+
+ # We pass len(arguments) as the final argument so that the
+ # CGPerSignatureCall won't do any argument conversion of its own.
+ CGPerSignatureCall.__init__(self, returnType, "", arguments, nativeName,
+ False, descriptor, operation,
+ len(arguments))
+
+ if operation.isSetter() or operation.isCreator():
+ # arguments[0] is the index or name of the item that we're setting.
+ argument = arguments[1]
+ template = getJSToNativeConversionTemplate(argument.type, descriptor,
+ treatNullAs=argument.treatNullAs,
+ treatUndefinedAs=argument.treatUndefinedAs)
+ templateValues = {
+ "declName": argument.identifier.name,
+ "holderName": argument.identifier.name + "_holder",
+ "val": "desc->value",
+ "valPtr": "&desc->value"
+ }
+ self.cgRoot.prepend(instantiateJSToNativeConversionTemplate(template, templateValues))
+ elif operation.isGetter():
+ self.cgRoot.prepend(CGGeneric("bool found;"))
+
+ def getArguments(self):
+ args = [(a, a.identifier.name) for a in self.arguments]
+ if self.idlNode.isGetter():
+ args.append((FakeArgument(BuiltinTypes[IDLBuiltinType.Types.boolean],
+ self.idlNode),
+ "found"))
+ return args
+
+ def wrap_return_value(self):
+ if not self.idlNode.isGetter() or self.templateValues is None:
+ return ""
+
+ wrap = CGGeneric(wrapForType(self.returnType, self.descriptor, self.templateValues))
+ wrap = CGIfWrapper(wrap, "found")
+ return "\n" + wrap.define()
+
+class CGProxyIndexedGetter(CGProxySpecialOperation):
+ """
+ Class to generate a call to an indexed getter. If templateValues is not None
+ the returned value will be wrapped with wrapForType using templateValues.
+ """
+ def __init__(self, descriptor, templateValues=None):
+ self.templateValues = templateValues
+ CGProxySpecialOperation.__init__(self, descriptor, 'IndexedGetter')
+
+class CGProxyIndexedSetter(CGProxySpecialOperation):
+ """
+ Class to generate a call to an indexed setter.
+ """
+ def __init__(self, descriptor):
+ CGProxySpecialOperation.__init__(self, descriptor, 'IndexedSetter')
+
+class CGProxyNamedGetter(CGProxySpecialOperation):
+ """
+ Class to generate a call to an named getter. If templateValues is not None
+ the returned value will be wrapped with wrapForType using templateValues.
+ """
+ def __init__(self, descriptor, templateValues=None):
+ self.templateValues = templateValues
+ CGProxySpecialOperation.__init__(self, descriptor, 'NamedGetter')
+
+class CGProxyNamedSetter(CGProxySpecialOperation):
+ """
+ Class to generate a call to a named setter.
+ """
+ def __init__(self, descriptor):
+ CGProxySpecialOperation.__init__(self, descriptor, 'NamedSetter')
+
+class CGProxyIsProxy(CGAbstractMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSObject*', 'obj')]
+ CGAbstractMethod.__init__(self, descriptor, "IsProxy", "bool", args, alwaysInline=True)
+ def declare(self):
+ return ""
+ def definition_body(self):
+ return " return js::IsProxy(obj) && js::GetProxyHandler(obj) == DOMProxyHandler::getInstance();"
+
+class CGProxyUnwrap(CGAbstractMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSObject*', 'obj')]
+ CGAbstractMethod.__init__(self, descriptor, "UnwrapProxy", descriptor.nativeType + '*', args, alwaysInline=True)
+ def declare(self):
+ return ""
+ def definition_body(self):
+ return """ if (xpc::WrapperFactory::IsXrayWrapper(obj)) {
+ obj = js::UnwrapObject(obj);
+ }
+ MOZ_ASSERT(IsProxy(obj));
+ return static_cast<%s*>(js::GetProxyPrivate(obj).toPrivate());""" % (self.descriptor.nativeType)
+
+class CGDOMJSProxyHandlerDOMClass(CGThing):
+ def __init__(self, descriptor):
+ CGThing.__init__(self)
+ self.descriptor = descriptor
+ def declare(self):
+ return "extern const DOMClass Class;\n"
+ def define(self):
+ return """
+const DOMClass Class = """ + DOMClass(self.descriptor) + """;
+
+"""
+
+class CGDOMJSProxyHandler_CGDOMJSProxyHandler(ClassConstructor):
+ def __init__(self):
+ ClassConstructor.__init__(self, [], inline=True, visibility="private",
+ baseConstructors=["mozilla::dom::DOMProxyHandler(Class)"],
+ body="")
+
+class CGDOMJSProxyHandler_getOwnPropertyDescriptor(ClassMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'proxy'),
+ Argument('jsid', 'id'), Argument('bool', 'set'),
+ Argument('JSPropertyDescriptor*', 'desc')]
+ ClassMethod.__init__(self, "getOwnPropertyDescriptor", "bool", args)
+ self.descriptor = descriptor
+ def getBody(self):
+ indexedGetter = self.descriptor.operations['IndexedGetter']
+ indexedSetter = self.descriptor.operations['IndexedSetter']
+
+ setOrIndexedGet = ""
+ if indexedGetter or indexedSetter:
+ setOrIndexedGet += "int32_t index = GetArrayIndexFromId(cx, id);\n"
+
+ if indexedGetter:
+ readonly = toStringBool(self.descriptor.operations['IndexedSetter'] is None)
+ fillDescriptor = "FillPropertyDescriptor(desc, proxy, %s);\nreturn true;" % readonly
+ templateValues = {'jsvalRef': 'desc->value', 'jsvalPtr': '&desc->value',
+ 'obj': 'proxy', 'successCode': fillDescriptor}
+ get = ("if (index >= 0) {\n" +
+ " %s* self = UnwrapProxy(proxy);\n" +
+ CGIndenter(CGProxyIndexedGetter(self.descriptor, templateValues)).define() + "\n" +
+ "}\n") % (self.descriptor.nativeType)
+
+ if indexedSetter or self.descriptor.operations['NamedSetter']:
+ setOrIndexedGet += "if (set) {\n"
+ if indexedSetter:
+ setOrIndexedGet += (" if (index >= 0) {\n")
+ if not 'IndexedCreator' in self.descriptor.operations:
+ # FIXME need to check that this is a 'supported property index'
+ assert False
+ setOrIndexedGet += (" FillPropertyDescriptor(desc, proxy, JSVAL_VOID, false);\n" +
+ " return true;\n" +
+ " }\n")
+ if self.descriptor.operations['NamedSetter']:
+ setOrIndexedGet += " if (JSID_IS_STRING(id)) {\n"
+ if not 'NamedCreator' in self.descriptor.operations:
+ # FIXME need to check that this is a 'supported property name'
+ assert False
+ setOrIndexedGet += (" FillPropertyDescriptor(desc, proxy, JSVAL_VOID, false);\n" +
+ " return true;\n" +
+ " }\n")
+ setOrIndexedGet += "}"
+ if indexedGetter:
+ setOrIndexedGet += (" else {\n" +
+ CGIndenter(CGGeneric(get)).define() +
+ "}")
+ setOrIndexedGet += "\n\n"
+ elif indexedGetter:
+ setOrIndexedGet += ("if (!set) {\n" +
+ CGIndenter(CGGeneric(get)).define() +
+ "}\n\n")
+
+ namedGetter = self.descriptor.operations['NamedGetter']
+ if namedGetter:
+ readonly = toStringBool(self.descriptor.operations['NamedSetter'] is None)
+ fillDescriptor = "FillPropertyDescriptor(desc, proxy, %s);\nreturn true;" % readonly
+ templateValues = {'jsvalRef': 'desc->value', 'jsvalPtr': '&desc->value',
+ 'obj': 'proxy', 'successCode': fillDescriptor}
+ # Once we start supporting OverrideBuiltins we need to make
+ # ResolveOwnProperty or EnumerateOwnProperties filter out named
+ # properties that shadow prototype properties.
+ namedGet = ("\n" +
+ "if (!set && JSID_IS_STRING(id) && !HasPropertyOnPrototype(cx, proxy, this, id)) {\n" +
+ " JS::Value nameVal = STRING_TO_JSVAL(JSID_TO_STRING(id));\n" +
+ " FakeDependentString name;\n"
+ " if (!ConvertJSValueToString(cx, nameVal, &nameVal,\n" +
+ " eStringify, eStringify, name)) {\n" +
+ " return false;\n" +
+ " }\n" +
+ "\n" +
+ " %s* self = UnwrapProxy(proxy);\n" +
+ CGIndenter(CGProxyNamedGetter(self.descriptor, templateValues)).define() + "\n" +
+ "}\n") % (self.descriptor.nativeType)
+ else:
+ namedGet = ""
+
+ return setOrIndexedGet + """JSObject* expando;
+if (!xpc::WrapperFactory::IsXrayWrapper(proxy) && (expando = GetExpandoObject(proxy))) {
+ unsigned flags = (set ? JSRESOLVE_ASSIGNING : 0) | JSRESOLVE_QUALIFIED;
+ if (!JS_GetPropertyDescriptorById(cx, expando, id, flags, desc)) {
+ return false;
+ }
+ if (desc->obj) {
+ // Pretend the property lives on the wrapper.
+ desc->obj = proxy;
+ return true;
+ }
+}
+""" + namedGet + """
+desc->obj = NULL;
+return true;"""
+
+class CGDOMJSProxyHandler_defineProperty(ClassMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'proxy'),
+ Argument('jsid', 'id'),
+ Argument('JSPropertyDescriptor*', 'desc')]
+ ClassMethod.__init__(self, "defineProperty", "bool", args)
+ self.descriptor = descriptor
+ def getBody(self):
+ set = ""
+
+ indexedSetter = self.descriptor.operations['IndexedSetter']
+ if indexedSetter:
+ if not (self.descriptor.operations['IndexedCreator'] is indexedSetter):
+ raise TypeError("Can't handle creator that's different from the setter")
+ set += ("int32_t index = GetArrayIndexFromId(cx, id);\n" +
+ "if (index >= 0) {\n" +
+ " %s* self = UnwrapProxy(proxy);\n" +
+ CGIndenter(CGProxyIndexedSetter(self.descriptor)).define() +
+ " return true;\n" +
+ "}\n") % (self.descriptor.nativeType)
+ elif self.descriptor.operations['IndexedGetter']:
+ set += ("if (GetArrayIndexFromId(cx, id) >= 0) {\n" +
+ " return ThrowErrorMessage(cx, MSG_NO_PROPERTY_SETTER, \"%s\");\n" +
+ "}\n") % self.descriptor.name
+
+ namedSetter = self.descriptor.operations['NamedSetter']
+ if namedSetter:
+ if not self.descriptor.operations['NamedCreator'] is namedSetter:
+ raise TypeError("Can't handle creator that's different from the setter")
+ set += ("if (JSID_IS_STRING(id)) {\n" +
+ " JS::Value nameVal = STRING_TO_JSVAL(JSID_TO_STRING(id));\n" +
+ " FakeDependentString name;\n"
+ " if (!ConvertJSValueToString(cx, nameVal, &nameVal,\n" +
+ " eStringify, eStringify, name)) {\n" +
+ " return false;\n" +
+ " }\n" +
+ "\n" +
+ " %s* self = UnwrapProxy(proxy);\n" +
+ CGIndenter(CGProxyNamedSetter(self.descriptor)).define() + "\n" +
+ "}\n") % (self.descriptor.nativeType)
+ elif self.descriptor.operations['NamedGetter']:
+ set += ("if (JSID_IS_STRING(id)) {\n" +
+ " JS::Value nameVal = STRING_TO_JSVAL(JSID_TO_STRING(id));\n" +
+ " FakeDependentString name;\n"
+ " if (!ConvertJSValueToString(cx, nameVal, &nameVal,\n" +
+ " eStringify, eStringify, name)) {\n" +
+ " return false;\n" +
+ " }\n" +
+ " %s* self = UnwrapProxy(proxy);\n" +
+ CGIndenter(CGProxyNamedGetter(self.descriptor)).define() +
+ " if (found) {\n"
+ " return ThrowErrorMessage(cx, MSG_NO_PROPERTY_SETTER, \"%s\");\n" +
+ " }\n" +
+ " return true;\n"
+ "}\n") % (self.descriptor.nativeType, self.descriptor.name)
+ return set + """return mozilla::dom::DOMProxyHandler::defineProperty(%s);""" % ", ".join(a.name for a in self.args)
+
+class CGDOMJSProxyHandler_getOwnPropertyNames(ClassMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'proxy'),
+ Argument('JS::AutoIdVector&', 'props')]
+ ClassMethod.__init__(self, "getOwnPropertyNames", "bool", args)
+ self.descriptor = descriptor
+ def getBody(self):
+ indexedGetter = self.descriptor.operations['IndexedGetter']
+ if indexedGetter:
+ addIndices = """uint32_t length = UnwrapProxy(proxy)->Length();
+MOZ_ASSERT(int32_t(length) >= 0);
+for (int32_t i = 0; i < int32_t(length); ++i) {
+ if (!props.append(INT_TO_JSID(i))) {
+ return false;
+ }
+}
+
+"""
+ else:
+ addIndices = ""
+
+ return addIndices + """JSObject* expando;
+if (!xpc::WrapperFactory::IsXrayWrapper(proxy) && (expando = DOMProxyHandler::GetExpandoObject(proxy)) &&
+ !js::GetPropertyNames(cx, expando, JSITER_OWNONLY | JSITER_HIDDEN, &props)) {
+ return false;
+}
+
+// FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=772869 Add named items
+return true;"""
+
+class CGDOMJSProxyHandler_hasOwn(ClassMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'proxy'),
+ Argument('jsid', 'id'), Argument('bool*', 'bp')]
+ ClassMethod.__init__(self, "hasOwn", "bool", args)
+ self.descriptor = descriptor
+ def getBody(self):
+ indexedGetter = self.descriptor.operations['IndexedGetter']
+ if indexedGetter:
+ indexed = ("int32_t index = GetArrayIndexFromId(cx, id);\n" +
+ "if (index >= 0) {\n" +
+ " %s* self = UnwrapProxy(proxy);\n" +
+ CGIndenter(CGProxyIndexedGetter(self.descriptor)).define() + "\n" +
+ " *bp = found;\n" +
+ " return true;\n" +
+ "}\n\n") % (self.descriptor.nativeType)
+ else:
+ indexed = ""
+
+ namedGetter = self.descriptor.operations['NamedGetter']
+ if namedGetter:
+ named = ("if (JSID_IS_STRING(id) && !HasPropertyOnPrototype(cx, proxy, this, id)) {\n" +
+ " jsval nameVal = STRING_TO_JSVAL(JSID_TO_STRING(id));\n" +
+ " FakeDependentString name;\n"
+ " if (!ConvertJSValueToString(cx, nameVal, &nameVal,\n" +
+ " eStringify, eStringify, name)) {\n" +
+ " return false;\n" +
+ " }\n" +
+ "\n" +
+ " %s* self = UnwrapProxy(proxy);\n" +
+ CGIndenter(CGProxyNamedGetter(self.descriptor)).define() + "\n" +
+ " *bp = found;\n"
+ " return true;\n"
+ "}\n" +
+ "\n") % (self.descriptor.nativeType)
+ else:
+ named = ""
+
+ return indexed + """JSObject* expando = GetExpandoObject(proxy);
+if (expando) {
+ JSBool b = true;
+ JSBool ok = JS_HasPropertyById(cx, expando, id, &b);
+ *bp = !!b;
+ if (!ok || *bp) {
+ return ok;
+ }
+}
+
+""" + named + """*bp = false;
+return true;"""
+
+class CGDOMJSProxyHandler_get(ClassMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'proxy'),
+ Argument('JSObject*', 'receiver'), Argument('jsid', 'id'),
+ Argument('JS::Value*', 'vp')]
+ ClassMethod.__init__(self, "get", "bool", args)
+ self.descriptor = descriptor
+ def getBody(self):
+ getFromExpando = """JSObject* expando = DOMProxyHandler::GetExpandoObject(proxy);
+if (expando) {
+ JSBool hasProp;
+ if (!JS_HasPropertyById(cx, expando, id, &hasProp)) {
+ return false;
+ }
+
+ if (hasProp) {
+ return JS_GetPropertyById(cx, expando, id, vp);
+ }
+}"""
+
+ templateValues = {'jsvalRef': '*vp', 'jsvalPtr': 'vp', 'obj': 'proxy'}
+
+ indexedGetter = self.descriptor.operations['IndexedGetter']
+ if indexedGetter:
+ getIndexedOrExpando = ("int32_t index = GetArrayIndexFromId(cx, id);\n" +
+ "if (index >= 0) {\n" +
+ " %s* self = UnwrapProxy(proxy);\n" +
+ CGIndenter(CGProxyIndexedGetter(self.descriptor, templateValues)).define()) % (self.descriptor.nativeType)
+ getIndexedOrExpando += """
+ // Even if we don't have this index, we don't forward the
+ // get on to our expando object.
+} else {
+ %s
+}
+""" % (stripTrailingWhitespace(getFromExpando.replace('\n', '\n ')))
+ else:
+ getIndexedOrExpando = getFromExpando + "\n"
+
+ namedGetter = self.descriptor.operations['NamedGetter']
+ if namedGetter:
+ getNamed = ("if (JSID_IS_STRING(id)) {\n" +
+ " JS::Value nameVal = STRING_TO_JSVAL(JSID_TO_STRING(id));\n" +
+ " FakeDependentString name;\n"
+ " if (!ConvertJSValueToString(cx, nameVal, &nameVal,\n" +
+ " eStringify, eStringify, name)) {\n" +
+ " return false;\n" +
+ " }\n" +
+ "\n" +
+ " %s* self = UnwrapProxy(proxy);\n" +
+ CGIndenter(CGProxyNamedGetter(self.descriptor, templateValues)).define() +
+ "}\n") % (self.descriptor.nativeType)
+ else:
+ getNamed = ""
+
+ return """MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
+ "Should not have a XrayWrapper here");
+
+%s
+bool found;
+if (!GetPropertyOnPrototype(cx, proxy, id, &found, vp)) {
+ return false;
+}
+
+if (found) {
+ return true;
+}
+%s
+vp->setUndefined();
+return true;""" % (getIndexedOrExpando, getNamed)
+
+class CGDOMJSProxyHandler_obj_toString(ClassMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'proxy')]
+ ClassMethod.__init__(self, "obj_toString", "JSString*", args)
+ self.descriptor = descriptor
+ def getBody(self):
+ stringifier = self.descriptor.operations['Stringifier']
+ if stringifier:
+ name = stringifier.identifier.name
+ nativeName = MakeNativeName(self.descriptor.binaryNames.get(name, name))
+ signature = stringifier.signatures()[0]
+ returnType = signature[0]
+ extendedAttributes = self.descriptor.getExtendedAttributes(stringifier)
+ infallible = 'infallible' in extendedAttributes
+ if not infallible:
+ error = CGGeneric(
+ ('ThrowMethodFailedWithDetails(cx, rv, "%s", "toString");\n' +
+ "return NULL;") % self.descriptor.interface.identifier.name)
+ else:
+ error = None
+ call = CGCallGenerator(error, [], "", returnType, extendedAttributes, self.descriptor, nativeName, False, object="UnwrapProxy(proxy)")
+ return call.define() + """
+
+JSString* jsresult;
+return xpc_qsStringToJsstring(cx, result, &jsresult) ? jsresult : NULL;"""
+
+ return "return mozilla::dom::DOMProxyHandler::obj_toString(cx, \"%s\");" % self.descriptor.name
+
+class CGDOMJSProxyHandler_finalize(ClassMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSFreeOp*', 'fop'), Argument('JSObject*', 'proxy')]
+ ClassMethod.__init__(self, "finalize", "void", args)
+ self.descriptor = descriptor
+ def getBody(self):
+ return ("%s self = UnwrapProxy(proxy);\n\n" % (self.descriptor.nativeType + "*") +
+ finalizeHook(self.descriptor, FINALIZE_HOOK_NAME, self.args[0].name))
+
+class CGDOMJSProxyHandler_getElementIfPresent(ClassMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'proxy'),
+ Argument('JSObject*', 'receiver'),
+ Argument('uint32_t', 'index'),
+ Argument('JS::Value*', 'vp'), Argument('bool*', 'present')]
+ ClassMethod.__init__(self, "getElementIfPresent", "bool", args)
+ self.descriptor = descriptor
+ def getBody(self):
+ indexedGetter = self.descriptor.operations['IndexedGetter']
+ if indexedGetter:
+ successCode = """*present = found;
+return true;"""
+ templateValues = {'jsvalRef': '*vp', 'jsvalPtr': 'vp',
+ 'obj': 'proxy', 'successCode': successCode}
+ get = ("%s* self = UnwrapProxy(proxy);\n" +
+ CGProxyIndexedGetter(self.descriptor, templateValues).define() + "\n"
+ "// We skip the expando object if there is an indexed getter.\n" +
+ "\n") % (self.descriptor.nativeType)
+ else:
+ get = """
+
+JSObject* expando = GetExpandoObject(proxy);
+if (expando) {
+ JSBool isPresent;
+ if (!JS_GetElementIfPresent(cx, expando, index, expando, vp, &isPresent)) {
+ return false;
+ }
+ if (isPresent) {
+ *present = true;
+ return true;
+ }
+}
+"""
+
+ return """MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
+ "Should not have a XrayWrapper here");
+
+""" + get + """
+// No need to worry about name getters here, so just check the proto.
+
+JSObject *proto;
+if (!js::GetObjectProto(cx, proxy, &proto)) {
+ return false;
+}
+if (proto) {
+ JSBool isPresent;
+ if (!JS_GetElementIfPresent(cx, proto, index, proxy, vp, &isPresent)) {
+ return false;
+ }
+ *present = isPresent;
+ return true;
+}
+
+*present = false;
+// Can't Debug_SetValueRangeToCrashOnTouch because it's not public
+return true;"""
+
+class CGDOMJSProxyHandler_getInstance(ClassMethod):
+ def __init__(self):
+ ClassMethod.__init__(self, "getInstance", "DOMProxyHandler*", [], static=True)
+ def getBody(self):
+ return """static DOMProxyHandler instance;
+return &instance;"""
+
+class CGDOMJSProxyHandler(CGClass):
+ def __init__(self, descriptor):
+ constructors = [CGDOMJSProxyHandler_CGDOMJSProxyHandler()]
+ methods = [CGDOMJSProxyHandler_getOwnPropertyDescriptor(descriptor)]
+ if descriptor.operations['IndexedSetter'] or descriptor.operations['NamedSetter']:
+ methods.append(CGDOMJSProxyHandler_defineProperty(descriptor))
+ methods.extend([CGDOMJSProxyHandler_getOwnPropertyNames(descriptor),
+ CGDOMJSProxyHandler_hasOwn(descriptor),
+ CGDOMJSProxyHandler_get(descriptor),
+ CGDOMJSProxyHandler_obj_toString(descriptor),
+ CGDOMJSProxyHandler_finalize(descriptor),
+ CGDOMJSProxyHandler_getElementIfPresent(descriptor),
+ CGDOMJSProxyHandler_getInstance()])
+ CGClass.__init__(self, 'DOMProxyHandler',
+ bases=[ClassBase('mozilla::dom::DOMProxyHandler')],
+ constructors=constructors,
+ methods=methods)
+
+def stripTrailingWhitespace(text):
+ tail = '\n' if text.endswith('\n') else ''
+ lines = text.splitlines()
+ for i in range(len(lines)):
+ lines[i] = lines[i].rstrip()
+ return '\n'.join(lines) + tail
+
+class CGDescriptor(CGThing):
+ def __init__(self, descriptor):
+ CGThing.__init__(self)
+
+ assert not descriptor.concrete or descriptor.interface.hasInterfacePrototypeObject()
+
+ cgThings = []
+ if descriptor.interface.hasInterfacePrototypeObject():
+ (hasMethod, hasGetter, hasLenientGetter,
+ hasSetter, hasLenientSetter) = False, False, False, False, False
+ for m in descriptor.interface.members:
+ if m.isMethod() and not m.isStatic() and not m.isIdentifierLess():
+ cgThings.append(CGSpecializedMethod(descriptor, m))
+ cgThings.append(CGMemberJITInfo(descriptor, m))
+ hasMethod = True
+ elif m.isAttr():
+ cgThings.append(CGSpecializedGetter(descriptor, m))
+ if m.hasLenientThis():
+ hasLenientGetter = True
+ else:
+ hasGetter = True
+ if not m.readonly:
+ cgThings.append(CGSpecializedSetter(descriptor, m))
+ if m.hasLenientThis():
+ hasLenientSetter = True
+ else:
+ hasSetter = True
+ cgThings.append(CGMemberJITInfo(descriptor, m))
+ if hasMethod: cgThings.append(CGGenericMethod(descriptor))
+ if hasGetter: cgThings.append(CGGenericGetter(descriptor))
+ if hasLenientGetter: cgThings.append(CGGenericGetter(descriptor,
+ lenientThis=True))
+ if hasSetter: cgThings.append(CGGenericSetter(descriptor))
+ if hasLenientSetter: cgThings.append(CGGenericSetter(descriptor,
+ lenientThis=True))
+
+ if descriptor.concrete and not descriptor.proxy:
+ if not descriptor.workers and descriptor.wrapperCache:
+ cgThings.append(CGAddPropertyHook(descriptor))
+
+ # Always have a finalize hook, regardless of whether the class wants a
+ # custom hook.
+ cgThings.append(CGClassFinalizeHook(descriptor))
+
+ # Only generate a trace hook if the class wants a custom hook.
+ if (descriptor.customTrace):
+ cgThings.append(CGClassTraceHook(descriptor))
+
+ if descriptor.interface.hasInterfaceObject():
+ cgThings.append(CGClassConstructHook(descriptor))
+ cgThings.append(CGClassHasInstanceHook(descriptor))
+ cgThings.append(CGInterfaceObjectJSClass(descriptor))
+
+ if descriptor.interface.hasInterfacePrototypeObject():
+ cgThings.append(CGPrototypeJSClass(descriptor))
+
+ properties = PropertyArrays(descriptor)
+ cgThings.append(CGGeneric(define=str(properties)))
+ cgThings.append(CGCreateInterfaceObjectsMethod(descriptor, properties))
+ if descriptor.interface.hasInterfacePrototypeObject():
+ cgThings.append(CGGetProtoObjectMethod(descriptor))
+ else:
+ cgThings.append(CGGetConstructorObjectMethod(descriptor))
+
+ # Set up our Xray callbacks as needed. Note that we don't need to do
+ # it in workers.
+ if (descriptor.interface.hasInterfacePrototypeObject() and
+ not descriptor.workers):
+ if descriptor.concrete and descriptor.proxy:
+ cgThings.append(CGResolveOwnProperty(descriptor))
+ cgThings.append(CGEnumerateOwnProperties(descriptor))
+ cgThings.append(CGResolveProperty(descriptor, properties))
+ cgThings.append(CGEnumerateProperties(descriptor, properties))
+
+ if descriptor.interface.hasInterfaceObject():
+ cgThings.append(CGDefineDOMInterfaceMethod(descriptor))
+ if (not descriptor.interface.isExternal() and
+ # Workers stuff is never pref-controlled
+ not descriptor.workers and
+ descriptor.interface.getExtendedAttribute("PrefControlled") is not None):
+ cgThings.append(CGPrefEnabled(descriptor))
+
+ if descriptor.interface.hasInterfacePrototypeObject():
+ cgThings.append(CGNativePropertyHooks(descriptor))
+
+ if descriptor.concrete:
+ if descriptor.proxy:
+ cgThings.append(CGProxyIsProxy(descriptor))
+ cgThings.append(CGProxyUnwrap(descriptor))
+ cgThings.append(CGDOMJSProxyHandlerDOMClass(descriptor))
+ cgThings.append(CGDOMJSProxyHandler(descriptor))
+ cgThings.append(CGIsMethod(descriptor))
+ else:
+ cgThings.append(CGDOMJSClass(descriptor))
+
+ if descriptor.wrapperCache:
+ cgThings.append(CGWrapWithCacheMethod(descriptor))
+ cgThings.append(CGWrapMethod(descriptor))
+ else:
+ cgThings.append(CGWrapNonWrapperCacheMethod(descriptor))
+
+ cgThings = CGList((CGIndenter(t, declareOnly=True) for t in cgThings), "\n")
+ cgThings = CGWrapper(cgThings, pre='\n', post='\n')
+ self.cgRoot = CGWrapper(CGNamespace(toBindingNamespace(descriptor.name),
+ cgThings),
+ post='\n')
+
+ def declare(self):
+ return self.cgRoot.declare()
+ def define(self):
+ return self.cgRoot.define()
+
+class CGNamespacedEnum(CGThing):
+ def __init__(self, namespace, enumName, names, values, comment=""):
+
+ if not values:
+ values = []
+
+ # Account for explicit enum values.
+ entries = []
+ for i in range(0, len(names)):
+ if len(values) > i and values[i] is not None:
+ entry = "%s = %s" % (names[i], values[i])
+ else:
+ entry = names[i]
+ entries.append(entry)
+
+ # Append a Count.
+ entries.append('_' + enumName + '_Count')
+
+ # Indent.
+ entries = [' ' + e for e in entries]
+
+ # Build the enum body.
+ enumstr = comment + 'enum %s\n{\n%s\n};\n' % (enumName, ',\n'.join(entries))
+ curr = CGGeneric(declare=enumstr)
+
+ # Add some whitespace padding.
+ curr = CGWrapper(curr, pre='\n',post='\n')
+
+ # Add the namespace.
+ curr = CGNamespace(namespace, curr)
+
+ # Add the typedef
+ typedef = '\ntypedef %s::%s %s;\n\n' % (namespace, enumName, enumName)
+ curr = CGList([curr, CGGeneric(declare=typedef)])
+
+ # Save the result.
+ self.node = curr
+
+ def declare(self):
+ return self.node.declare()
+ def define(self):
+ assert False # Only for headers.
+
+class CGDictionary(CGThing):
+ def __init__(self, dictionary, descriptorProvider):
+ self.dictionary = dictionary;
+ self.workers = descriptorProvider.workers
+ if all(CGDictionary(d, descriptorProvider).generatable for
+ d in CGDictionary.getDictionaryDependencies(dictionary)):
+ self.generatable = True
+ else:
+ self.generatable = False
+ # Nothing else to do here
+ return
+ # Getting a conversion template for interface types can fail
+ # if we don't have a relevant descriptor when self.workers is True.
+ # If that happens, just mark ourselves as not being
+ # generatable and move on.
+ try:
+ self.memberInfo = [
+ (member,
+ getJSToNativeConversionTemplate(member.type,
+ descriptorProvider,
+ isMember=True,
+ isOptional=(not member.defaultValue),
+ defaultValue=member.defaultValue))
+ for member in dictionary.members ]
+ except NoSuchDescriptorError, err:
+ if not self.workers:
+ raise err
+ self.generatable = False
+
+ def declare(self):
+ if not self.generatable:
+ return ""
+ d = self.dictionary
+ if d.parent:
+ inheritance = ": public %s " % self.makeClassName(d.parent)
+ else:
+ inheritance = ""
+ memberDecls = [" %s %s;" %
+ (self.getMemberType(m), m[0].identifier.name)
+ for m in self.memberInfo]
+
+ return (string.Template(
+ "struct ${selfName} ${inheritance}{\n"
+ " ${selfName}() {}\n"
+ " bool Init(JSContext* cx, const JS::Value& val);\n"
+ "\n" +
+ "\n".join(memberDecls) + "\n"
+ "private:\n"
+ " // Disallow copy-construction\n"
+ " ${selfName}(const ${selfName}&) MOZ_DELETE;\n" +
+ # NOTE: jsids are per-runtime, so don't use them in workers
+ (" static bool InitIds(JSContext* cx);\n"
+ " static bool initedIds;\n" if not self.workers else "") +
+ "\n".join(" static jsid " +
+ self.makeIdName(m.identifier.name) + ";" for
+ m in d.members) + "\n"
+ "};").substitute( { "selfName": self.makeClassName(d),
+ "inheritance": inheritance }))
+
+ def define(self):
+ if not self.generatable:
+ return ""
+ d = self.dictionary
+ if d.parent:
+ initParent = ("// Per spec, we init the parent's members first\n"
+ "if (!%s::Init(cx, val)) {\n"
+ " return false;\n"
+ "}\n" % self.makeClassName(d.parent))
+ else:
+ initParent = ""
+
+ memberInits = [CGIndenter(self.getMemberConversion(m)).define()
+ for m in self.memberInfo]
+ idinit = [CGGeneric('!InternJSString(cx, %s, "%s")' %
+ (m.identifier.name + "_id", m.identifier.name))
+ for m in d.members]
+ idinit = CGList(idinit, " ||\n")
+ idinit = CGWrapper(idinit, pre="if (",
+ post=(") {\n"
+ " return false;\n"
+ "}"),
+ reindent=True)
+
+ return string.Template(
+ # NOTE: jsids are per-runtime, so don't use them in workers
+ ("bool ${selfName}::initedIds = false;\n" +
+ "\n".join("jsid ${selfName}::%s = JSID_VOID;" %
+ self.makeIdName(m.identifier.name)
+ for m in d.members) + "\n"
+ "\n"
+ "bool\n"
+ "${selfName}::InitIds(JSContext* cx)\n"
+ "{\n"
+ " MOZ_ASSERT(!initedIds);\n"
+ "${idInit}\n"
+ " initedIds = true;\n"
+ " return true;\n"
+ "}\n"
+ "\n" if not self.workers else "") +
+ "bool\n"
+ "${selfName}::Init(JSContext* cx, const JS::Value& val)\n"
+ "{\n" +
+ # NOTE: jsids are per-runtime, so don't use them in workers
+ (" if (!initedIds && !InitIds(cx)) {\n"
+ " return false;\n"
+ " }\n" if not self.workers else "") +
+ "${initParent}"
+ " JSBool found;\n"
+ " JS::Value temp;\n"
+ " bool isNull = val.isNullOrUndefined();\n"
+ " if (!isNull && !val.isObject()) {\n"
+ " return Throw<${isMainThread}>(cx, NS_ERROR_XPC_BAD_CONVERT_JS);\n"
+ " }\n"
+ "\n"
+ "${initMembers}\n"
+ " return true;\n"
+ "}").substitute({
+ "selfName": self.makeClassName(d),
+ "initParent": CGIndenter(CGGeneric(initParent)).define(),
+ "initMembers": "\n\n".join(memberInits),
+ "idInit": CGIndenter(idinit).define(),
+ "isMainThread": toStringBool(not self.workers)
+ })
+
+ @staticmethod
+ def makeDictionaryName(dictionary, workers):
+ suffix = "Workers" if workers else ""
+ return dictionary.identifier.name + suffix
+
+ def makeClassName(self, dictionary):
+ return self.makeDictionaryName(dictionary, self.workers)
+
+ def getMemberType(self, memberInfo):
+ (member, (templateBody, declType,
+ holderType, dealWithOptional)) = memberInfo
+ # We can't handle having a holderType here
+ assert holderType is None
+ if dealWithOptional:
+ declType = CGWrapper(declType, pre="Optional< ", post=" >")
+ return declType.define()
+
+ def getMemberConversion(self, memberInfo):
+ (member, (templateBody, declType,
+ holderType, dealWithOptional)) = memberInfo
+ replacements = { "val": "temp",
+ "valPtr": "&temp",
+ # Use this->%s to refer to members, because we don't
+ # control the member names and want to make sure we're
+ # talking about the member, not some local that
+ # shadows the member. Another option would be to move
+ # the guts of init to a static method which is passed
+ # an explicit reference to our dictionary object, so
+ # we couldn't screw this up even if we wanted to....
+ "declName": ("(this->%s)" % member.identifier.name),
+ # We need a holder name for external interfaces, but
+ # it's scoped down to the conversion so we can just use
+ # anything we want.
+ "holderName": "holder"}
+ # We can't handle having a holderType here
+ assert holderType is None
+ if dealWithOptional:
+ replacements["declName"] = "(" + replacements["declName"] + ".Value())"
+ if member.defaultValue:
+ replacements["haveValue"] = "found"
+
+ # NOTE: jsids are per-runtime, so don't use them in workers
+ if self.workers:
+ propName = member.identifier.name
+ propCheck = ('JS_HasProperty(cx, &val.toObject(), "%s", &found)' %
+ propName)
+ propGet = ('JS_GetProperty(cx, &val.toObject(), "%s", &temp)' %
+ propName)
+ else:
+ propId = self.makeIdName(member.identifier.name);
+ propCheck = ("JS_HasPropertyById(cx, &val.toObject(), %s, &found)" %
+ propId)
+ propGet = ("JS_GetPropertyById(cx, &val.toObject(), %s, &temp)" %
+ propId)
+
+ conversionReplacements = {
+ "prop": "(this->%s)" % member.identifier.name,
+ "convert": string.Template(templateBody).substitute(replacements),
+ "propCheck": propCheck,
+ "propGet": propGet
+ }
+ conversion = ("if (isNull) {\n"
+ " found = false;\n"
+ "} else if (!${propCheck}) {\n"
+ " return false;\n"
+ "}\n")
+ if member.defaultValue:
+ conversion += (
+ "if (found) {\n"
+ " if (!${propGet}) {\n"
+ " return false;\n"
+ " }\n"
+ "}\n"
+ "${convert}")
+ else:
+ conversion += (
+ "if (found) {\n"
+ " ${prop}.Construct();\n"
+ " if (!${propGet}) {\n"
+ " return false;\n"
+ " }\n"
+ "${convert}\n"
+ "}")
+ conversionReplacements["convert"] = CGIndenter(
+ CGGeneric(conversionReplacements["convert"])).define()
+
+ return CGGeneric(
+ string.Template(conversion).substitute(conversionReplacements)
+ )
+
+ @staticmethod
+ def makeIdName(name):
+ return name + "_id"
+
+ @staticmethod
+ def getDictionaryDependencies(dictionary):
+ deps = set();
+ if dictionary.parent:
+ deps.add(dictionary.parent)
+ for member in dictionary.members:
+ if member.type.isDictionary():
+ deps.add(member.type.unroll().inner)
+ return deps
+
+
+class CGRegisterProtos(CGAbstractMethod):
+ def __init__(self, config):
+ CGAbstractMethod.__init__(self, None, 'Register', 'void',
+ [Argument('nsScriptNameSpaceManager*', 'aNameSpaceManager')])
+ self.config = config
+
+ def _defineMacro(self):
+ return """
+#define REGISTER_PROTO(_dom_class, _pref_check) \\
+ aNameSpaceManager->RegisterDefineDOMInterface(NS_LITERAL_STRING(#_dom_class), _dom_class##Binding::DefineDOMInterface, _pref_check);\n\n"""
+ def _undefineMacro(self):
+ return "\n#undef REGISTER_PROTO"
+ def _registerProtos(self):
+ def getPrefCheck(desc):
+ if desc.interface.getExtendedAttribute("PrefControlled") is None:
+ return "nullptr"
+ return "%sBinding::PrefEnabled" % desc.name
+ lines = ["REGISTER_PROTO(%s, %s);" % (desc.name, getPrefCheck(desc))
+ for desc in self.config.getDescriptors(hasInterfaceObject=True,
+ isExternal=False,
+ workers=False,
+ register=True)]
+ return '\n'.join(lines) + '\n'
+ def definition_body(self):
+ return self._defineMacro() + self._registerProtos() + self._undefineMacro()
+
+class CGBindingRoot(CGThing):
+ """
+ Root codegen class for binding generation. Instantiate the class, and call
+ declare or define to generate header or cpp code (respectively).
+ """
+ def __init__(self, config, prefix, webIDLFile):
+ descriptors = config.getDescriptors(webIDLFile=webIDLFile,
+ hasInterfaceOrInterfacePrototypeObject=True)
+ dictionaries = config.getDictionaries(webIDLFile)
+
+ forwardDeclares = [CGClassForwardDeclare('XPCWrappedNativeScope')]
+
+ descriptorsForForwardDeclaration = list(descriptors)
+ for dictionary in dictionaries:
+ curDict = dictionary
+ ifacemembers = []
+ while curDict:
+ ifacemembers.extend([m.type.unroll().inner for m
+ in curDict.members
+ if m.type.unroll().isInterface()])
+ curDict = curDict.parent
+ # Put in all the non-worker descriptors
+ descriptorsForForwardDeclaration.extend(
+ [config.getDescriptor(iface.identifier.name, False) for
+ iface in ifacemembers])
+ # And now the worker ones. But these may not exist, so we
+ # have to be more careful.
+ for iface in ifacemembers:
+ try:
+ descriptorsForForwardDeclaration.append(
+ config.getDescriptor(iface.identifier.name, True))
+ except NoSuchDescriptorError:
+ # just move along
+ pass
+
+ for x in descriptorsForForwardDeclaration:
+ nativeType = x.nativeType
+ components = x.nativeType.split('::')
+ className = components[-1]
+ # JSObject is a struct, not a class
+ declare = CGClassForwardDeclare(className, className is "JSObject")
+ if len(components) > 1:
+ declare = CGNamespace.build(components[:-1],
+ CGWrapper(declare, declarePre='\n',
+ declarePost='\n'),
+ declareOnly=True)
+ forwardDeclares.append(CGWrapper(declare, declarePost='\n'))
+
+ forwardDeclares = CGList(forwardDeclares)
+
+ descriptorsWithPrototype = filter(lambda d: d.interface.hasInterfacePrototypeObject(),
+ descriptors)
+ traitsClasses = [CGPrototypeTraitsClass(d) for d in descriptorsWithPrototype]
+
+ # We must have a 1:1 mapping here, skip for prototypes that have more
+ # than one concrete class implementation.
+ traitsClasses.extend([CGPrototypeIDMapClass(d) for d in descriptorsWithPrototype
+ if d.uniqueImplementation])
+
+ # Wrap all of that in our namespaces.
+ if len(traitsClasses) > 0:
+ traitsClasses = CGNamespace.build(['mozilla', 'dom'],
+ CGWrapper(CGList(traitsClasses),
+ declarePre='\n'),
+ declareOnly=True)
+ traitsClasses = CGWrapper(traitsClasses, declarePost='\n')
+ else:
+ traitsClasses = None
+
+ # Do codegen for all the enums
+ def makeEnum(e):
+ return CGNamespace.build([e.identifier.name + "Values"],
+ CGEnum(e))
+ def makeEnumTypedef(e):
+ return CGGeneric(declare=("typedef %sValues::valuelist %s;\n" %
+ (e.identifier.name, e.identifier.name)))
+ cgthings = [ fun(e) for e in config.getEnums(webIDLFile)
+ for fun in [makeEnum, makeEnumTypedef] ]
+
+ # Do codegen for all the dictionaries. We have to be a bit careful
+ # here, because we have to generate these in order from least derived
+ # to most derived so that class inheritance works out. We also have to
+ # generate members before the dictionary that contains them.
+ #
+ # XXXbz this will fail if we have two webidl files A and B such that A
+ # declares a dictionary which inherits from a dictionary in B and B
+ # declares a dictionary (possibly a different one!) that inherits from a
+ # dictionary in A. The good news is that I expect this to never happen.
+ reSortedDictionaries = []
+ dictionaries = set(dictionaries)
+ while len(dictionaries) != 0:
+ # Find the dictionaries that don't depend on anything else anymore
+ # and move them over.
+ toMove = [d for d in dictionaries if
+ len(CGDictionary.getDictionaryDependencies(d) &
+ dictionaries) == 0]
+ if len(toMove) == 0:
+ raise TypeError("Loop in dictionary dependency graph")
+ dictionaries = dictionaries - set(toMove)
+ reSortedDictionaries.extend(toMove)
+
+ dictionaries = reSortedDictionaries
+ cgthings.extend([CGDictionary(d, config.getDescriptorProvider(True))
+ for d in dictionaries])
+ cgthings.extend([CGDictionary(d, config.getDescriptorProvider(False))
+ for d in dictionaries])
+
+ # Do codegen for all the descriptors
+ cgthings.extend([CGDescriptor(x) for x in descriptors])
+
+ # And make sure we have the right number of newlines at the end
+ curr = CGWrapper(CGList(cgthings, "\n\n"), post="\n\n")
+
+ # Wrap all of that in our namespaces.
+ curr = CGNamespace.build(['mozilla', 'dom'],
+ CGWrapper(curr, pre="\n"))
+
+ curr = CGList([forwardDeclares,
+ CGWrapper(CGGeneric("using namespace mozilla::dom;"),
+ defineOnly=True),
+ traitsClasses, curr],
+ "\n")
+
+ # Add header includes.
+ curr = CGHeaders(descriptors,
+ dictionaries,
+ ['mozilla/dom/BindingUtils.h',
+ 'mozilla/dom/DOMJSClass.h',
+ 'mozilla/dom/DOMJSProxyHandler.h'],
+ ['mozilla/dom/Nullable.h',
+ 'PrimitiveConversions.h',
+ 'XPCQuickStubs.h',
+ 'nsDOMQS.h',
+ 'AccessCheck.h',
+ 'WorkerPrivate.h',
+ 'nsContentUtils.h',
+ 'mozilla/Preferences.h',
+ # Have to include nsDOMQS.h to get fast arg unwrapping
+ # for old-binding things with castability.
+ 'nsDOMQS.h'
+ ],
+ curr)
+
+ # Add include guards.
+ curr = CGIncludeGuard(prefix, curr)
+
+ # Add the auto-generated comment.
+ curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
+
+ # Store the final result.
+ self.root = curr
+
+ def declare(self):
+ return stripTrailingWhitespace(self.root.declare())
+ def define(self):
+ return stripTrailingWhitespace(self.root.define())
+
+
+class GlobalGenRoots():
+ """
+ Roots for global codegen.
+
+ To generate code, call the method associated with the target, and then
+ call the appropriate define/declare method.
+ """
+
+ @staticmethod
+ def PrototypeList(config):
+
+ # Prototype ID enum.
+ protos = [d.name for d in config.getDescriptors(hasInterfacePrototypeObject=True)]
+ idEnum = CGNamespacedEnum('id', 'ID', protos, [0])
+ idEnum = CGList([idEnum])
+ idEnum.append(CGGeneric(declare="const unsigned MaxProtoChainLength = " +
+ str(config.maxProtoChainLength) + ";\n\n"))
+
+ # Wrap all of that in our namespaces.
+ idEnum = CGNamespace.build(['mozilla', 'dom', 'prototypes'],
+ CGWrapper(idEnum, pre='\n'))
+ idEnum = CGWrapper(idEnum, post='\n')
+
+ curr = CGList([idEnum])
+
+ # Constructor ID enum.
+ constructors = [d.name for d in config.getDescriptors(hasInterfaceObject=True,
+ hasInterfacePrototypeObject=False)]
+ idEnum = CGNamespacedEnum('id', 'ID', constructors, [0])
+
+ # Wrap all of that in our namespaces.
+ idEnum = CGNamespace.build(['mozilla', 'dom', 'constructors'],
+ CGWrapper(idEnum, pre='\n'))
+ idEnum = CGWrapper(idEnum, post='\n')
+
+ curr.append(idEnum)
+
+ traitsDecl = CGGeneric(declare="""
+template <prototypes::ID PrototypeID>
+struct PrototypeTraits;
+
+template <class ConcreteClass>
+struct PrototypeIDMap;
+""")
+
+ traitsDecl = CGNamespace.build(['mozilla', 'dom'],
+ CGWrapper(traitsDecl, post='\n'))
+
+ curr.append(traitsDecl)
+
+ # Add include guards.
+ curr = CGIncludeGuard('PrototypeList', curr)
+
+ # Add the auto-generated comment.
+ curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
+
+ # Done.
+ return curr
+
+ @staticmethod
+ def RegisterBindings(config):
+
+ # TODO - Generate the methods we want
+ curr = CGRegisterProtos(config)
+
+ # Wrap all of that in our namespaces.
+ curr = CGNamespace.build(['mozilla', 'dom'],
+ CGWrapper(curr, post='\n'))
+ curr = CGWrapper(curr, post='\n')
+
+ # Add the includes
+ defineIncludes = [CGHeaders.getDeclarationFilename(desc.interface)
+ for desc in config.getDescriptors(hasInterfaceObject=True,
+ workers=False,
+ register=True)]
+ defineIncludes.append('nsScriptNameSpaceManager.h')
+ curr = CGHeaders([], [], [], defineIncludes, curr)
+
+ # Add include guards.
+ curr = CGIncludeGuard('RegisterBindings', curr)
+
+ # Done.
+ return curr
+
+ @staticmethod
+ def UnionTypes(config):
+
+ (includes, declarations, unions) = UnionTypes(config.getDescriptors())
+ includes.add("mozilla/dom/BindingUtils.h")
+
+ # Wrap all of that in our namespaces.
+ curr = CGNamespace.build(['mozilla', 'dom'], unions)
+
+ curr = CGWrapper(curr, post='\n')
+
+ namespaces = []
+ stack = [CGList([])]
+ for (clazz, isStruct) in SortedTuples(declarations):
+ elements = clazz.split("::")
+ clazz = CGClassForwardDeclare(elements.pop(), isStruct=isStruct)
+ i = 0
+ if len(elements) > 0:
+ common = min(len(namespaces), len(elements))
+ while i < common and namespaces[i] == elements[i]:
+ i += 1
+
+ # pop all the namespaces that should be closed
+ namespaces = namespaces[:i]
+
+ # add all the namespaces that should be opened
+ for j, namespace in enumerate(elements[i:]):
+ namespaces.append(namespace)
+ # every CGNamespace that we add holds a CGList
+ list = CGList([])
+ # add the new namespace to the list on top of the stack
+ stack[i + j].append(CGNamespace(namespace, list))
+ # set the top of the namespace stack to the list of the new
+ # namespace
+ stack[i + j + 1:] = [list]
+
+ stack[len(elements)].append(clazz)
+
+ curr = CGList([stack[0], curr], "\n")
+
+ curr = CGHeaders([], [], includes, [], curr)
+
+ # Add include guards.
+ curr = CGIncludeGuard('UnionTypes', curr)
+
+ # Done.
+ return curr
+
+ @staticmethod
+ def UnionConversions(config):
+
+ unions = UnionConversions(config.getDescriptors())
+
+ # Wrap all of that in our namespaces.
+ curr = CGNamespace.build(['mozilla', 'dom'], unions)
+
+ curr = CGWrapper(curr, post='\n')
+
+ curr = CGHeaders([], [], ["nsDebug.h", "mozilla/dom/UnionTypes.h", "nsDOMQS.h"], [], curr)
+
+ # Add include guards.
+ curr = CGIncludeGuard('UnionConversions', curr)
+
+ # Done.
+ return curr
diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py
new file mode 100644
index 00000000000..1666589940e
--- /dev/null
+++ b/components/script/dom/bindings/codegen/CodegenRust.py
@@ -0,0 +1,5534 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at http://mozilla.org/MPL/2.0/.
+
+# Common codegen classes.
+
+import operator
+import os
+import re
+import string
+
+from WebIDL import (
+ BuiltinTypes,
+ IDLBuiltinType,
+ IDLNullValue,
+ IDLType,
+ IDLUndefinedValue,
+)
+
+from Configuration import getTypesFromDescriptor, getTypesFromDictionary, getTypesFromCallback
+
+AUTOGENERATED_WARNING_COMMENT = \
+ "/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n\n"
+ADDPROPERTY_HOOK_NAME = '_addProperty'
+FINALIZE_HOOK_NAME = '_finalize'
+TRACE_HOOK_NAME = '_trace'
+CONSTRUCT_HOOK_NAME = '_constructor'
+HASINSTANCE_HOOK_NAME = '_hasInstance'
+
+def replaceFileIfChanged(filename, newContents):
+ """
+ Read a copy of the old file, so that we don't touch it if it hasn't changed.
+ Returns True if the file was updated, false otherwise.
+ """
+ #XXXjdm This doesn't play well with make right now.
+ # Force the file to always be updated, or else changing CodegenRust.py
+ # will cause many autogenerated bindings to be regenerated perpetually
+ # until the result is actually different.
+
+ #oldFileContents = ""
+ #try:
+ # oldFile = open(filename, 'rb')
+ # oldFileContents = ''.join(oldFile.readlines())
+ # oldFile.close()
+ #except:
+ # pass
+
+ #if newContents == oldFileContents:
+ # return False
+
+ f = open(filename, 'wb')
+ f.write(newContents)
+ f.close()
+
+ return True
+
+def toStringBool(arg):
+ return str(not not arg).lower()
+
+def toBindingNamespace(arg):
+ return re.sub("((_workers)?$)", "Binding\\1", arg);
+
+def stripTrailingWhitespace(text):
+ tail = '\n' if text.endswith('\n') else ''
+ lines = text.splitlines()
+ for i in range(len(lines)):
+ lines[i] = lines[i].rstrip()
+ return '\n'.join(lines) + tail
+
+def MakeNativeName(name):
+ return name[0].upper() + name[1:]
+
+builtinNames = {
+ IDLType.Tags.bool: 'bool',
+ IDLType.Tags.int8: 'i8',
+ IDLType.Tags.int16: 'i16',
+ IDLType.Tags.int32: 'i32',
+ IDLType.Tags.int64: 'i64',
+ IDLType.Tags.uint8: 'u8',
+ IDLType.Tags.uint16: 'u16',
+ IDLType.Tags.uint32: 'u32',
+ IDLType.Tags.uint64: 'u64',
+ IDLType.Tags.float: 'f32',
+ IDLType.Tags.double: 'f64'
+}
+
+numericTags = [
+ IDLType.Tags.int8, IDLType.Tags.uint8,
+ IDLType.Tags.int16, IDLType.Tags.uint16,
+ IDLType.Tags.int32, IDLType.Tags.uint32,
+ IDLType.Tags.int64, IDLType.Tags.uint64,
+ IDLType.Tags.float, IDLType.Tags.double
+ ]
+
+class CastableObjectUnwrapper():
+ """
+ A class for unwrapping an object named by the "source" argument
+ based on the passed-in descriptor. Stringifies to a Rust expression of
+ the appropriate type.
+
+ codeOnFailure is the code to run if unwrapping fails.
+ """
+ def __init__(self, descriptor, source, codeOnFailure):
+ self.substitution = {
+ "type": descriptor.nativeType,
+ "depth": descriptor.interface.inheritanceDepth(),
+ "prototype": "PrototypeList::id::" + descriptor.name,
+ "protoID": "PrototypeList::id::" + descriptor.name + " as uint",
+ "source": source,
+ "codeOnFailure": CGIndenter(CGGeneric(codeOnFailure), 4).define(),
+ }
+
+ def __str__(self):
+ return string.Template(
+"""match unwrap_jsmanaged(${source}, ${prototype}, ${depth}) {
+ Ok(val) => val,
+ Err(()) => {
+${codeOnFailure}
+ }
+}""").substitute(self.substitution)
+
+
+class CGThing():
+ """
+ Abstract base class for things that spit out code.
+ """
+ def __init__(self):
+ pass # Nothing for now
+
+ def define(self):
+ """Produce code for a Rust file."""
+ assert(False) # Override me!
+
+
+class CGNativePropertyHooks(CGThing):
+ """
+ Generate a NativePropertyHooks for a given descriptor
+ """
+ def __init__(self, descriptor, properties):
+ CGThing.__init__(self)
+ self.descriptor = descriptor
+ self.properties = properties
+
+ def define(self):
+ parent = self.descriptor.interface.parent
+ if parent:
+ parentHooks = "Some(&::dom::bindings::codegen::Bindings::%sBinding::sNativePropertyHooks)" % parent.identifier.name
+ else:
+ parentHooks = "None"
+
+ substitutions = {
+ "parentHooks": parentHooks
+ }
+
+ return string.Template(
+ "pub static sNativePropertyHooks: NativePropertyHooks = NativePropertyHooks {\n"
+ " native_properties: &sNativeProperties,\n"
+ " proto_hooks: ${parentHooks},\n"
+ "};\n").substitute(substitutions)
+
+
+class CGMethodCall(CGThing):
+ """
+ A class to generate selection of a method signature from a set of
+ signatures and generation of a call to that signature.
+ """
+ def __init__(self, argsPre, nativeMethodName, static, descriptor, method):
+ CGThing.__init__(self)
+
+ methodName = '\\"%s.%s\\"' % (descriptor.interface.identifier.name, method.identifier.name)
+
+ def requiredArgCount(signature):
+ arguments = signature[1]
+ if len(arguments) == 0:
+ return 0
+ requiredArgs = len(arguments)
+ while requiredArgs and arguments[requiredArgs-1].optional:
+ requiredArgs -= 1
+ return requiredArgs
+
+ def getPerSignatureCall(signature, argConversionStartsAt=0, signatureIndex=0):
+ return CGPerSignatureCall(signature[0], argsPre, signature[1],
+ nativeMethodName + '_'*signatureIndex,
+ static, descriptor,
+ method, argConversionStartsAt)
+
+
+ signatures = method.signatures()
+ if len(signatures) == 1:
+ # Special case: we can just do a per-signature method call
+ # here for our one signature and not worry about switching
+ # on anything.
+ signature = signatures[0]
+ self.cgRoot = CGList([getPerSignatureCall(signature)])
+ requiredArgs = requiredArgCount(signature)
+
+
+ if requiredArgs > 0:
+ code = (
+ "if argc < %d {\n"
+ " throw_type_error(cx, \"Not enough arguments to %s.\");\n"
+ " return 0;\n"
+ "}" % (requiredArgs, methodName))
+ self.cgRoot.prepend(
+ CGWrapper(CGGeneric(code), pre="\n", post="\n"))
+
+ return
+
+ # Need to find the right overload
+ maxArgCount = method.maxArgCount
+ allowedArgCounts = method.allowedArgCounts
+
+ argCountCases = []
+ for argCount in allowedArgCounts:
+ possibleSignatures = method.signaturesForArgCount(argCount)
+ if len(possibleSignatures) == 1:
+ # easy case!
+ signature = possibleSignatures[0]
+
+
+ sigIndex = signatures.index(signature)
+ argCountCases.append(
+ CGCase(str(argCount), getPerSignatureCall(signature,
+ signatureIndex=sigIndex)))
+ continue
+
+ distinguishingIndex = method.distinguishingIndexForArgCount(argCount)
+
+ # We can't handle unions at the distinguishing index.
+ for (returnType, args) in possibleSignatures:
+ if args[distinguishingIndex].type.isUnion():
+ raise TypeError("No support for unions as distinguishing "
+ "arguments yet: %s",
+ args[distinguishingIndex].location)
+
+ # Convert all our arguments up to the distinguishing index.
+ # Doesn't matter which of the possible signatures we use, since
+ # they all have the same types up to that point; just use
+ # possibleSignatures[0]
+ caseBody = [CGGeneric("let argv_start = JS_ARGV(cx, vp);")]
+ caseBody.extend([ CGArgumentConverter(possibleSignatures[0][1][i],
+ i, "argv_start", "argc",
+ descriptor) for i in
+ range(0, distinguishingIndex) ])
+
+ # Select the right overload from our set.
+ distinguishingArg = "(*argv_start.offset(%d))" % distinguishingIndex
+
+ def pickFirstSignature(condition, filterLambda):
+ sigs = filter(filterLambda, possibleSignatures)
+ assert len(sigs) < 2
+ if len(sigs) > 0:
+ if condition is None:
+ caseBody.append(
+ getPerSignatureCall(sigs[0], distinguishingIndex,
+ possibleSignatures.index(sigs[0])))
+ else:
+ caseBody.append(CGGeneric("if " + condition + " {"))
+ caseBody.append(CGIndenter(
+ getPerSignatureCall(sigs[0], distinguishingIndex,
+ possibleSignatures.index(sigs[0]))))
+ caseBody.append(CGGeneric("}"))
+ return True
+ return False
+
+ # First check for null or undefined
+ pickFirstSignature("%s.isNullOrUndefined()" % distinguishingArg,
+ lambda s: (s[1][distinguishingIndex].type.nullable() or
+ s[1][distinguishingIndex].type.isDictionary()))
+
+ # Now check for distinguishingArg being an object that implements a
+ # non-callback interface. That includes typed arrays and
+ # arraybuffers.
+ interfacesSigs = [
+ s for s in possibleSignatures
+ if (s[1][distinguishingIndex].type.isObject() or
+ s[1][distinguishingIndex].type.isNonCallbackInterface()) ]
+ # There might be more than one of these; we need to check
+ # which ones we unwrap to.
+
+ if len(interfacesSigs) > 0:
+ # The spec says that we should check for "platform objects
+ # implementing an interface", but it's enough to guard on these
+ # being an object. The code for unwrapping non-callback
+ # interfaces and typed arrays will just bail out and move on to
+ # the next overload if the object fails to unwrap correctly. We
+ # could even not do the isObject() check up front here, but in
+ # cases where we have multiple object overloads it makes sense
+ # to do it only once instead of for each overload. That will
+ # also allow the unwrapping test to skip having to do codegen
+ # for the null-or-undefined case, which we already handled
+ # above.
+ caseBody.append(CGGeneric("if (%s).is_object() {" %
+ (distinguishingArg)))
+ for idx, sig in enumerate(interfacesSigs):
+ caseBody.append(CGIndenter(CGGeneric("loop {")));
+ type = sig[1][distinguishingIndex].type
+
+ # The argument at index distinguishingIndex can't possibly
+ # be unset here, because we've already checked that argc is
+ # large enough that we can examine this argument.
+ template, _, declType, needsRooting = getJSToNativeConversionTemplate(
+ type, descriptor, failureCode="break;", isDefinitelyObject=True)
+
+ testCode = instantiateJSToNativeConversionTemplate(
+ template,
+ {"val": distinguishingArg},
+ declType,
+ "arg%d" % distinguishingIndex,
+ needsRooting)
+
+ # Indent by 4, since we need to indent further than our "do" statement
+ caseBody.append(CGIndenter(testCode, 4));
+ # If we got this far, we know we unwrapped to the right
+ # interface, so just do the call. Start conversion with
+ # distinguishingIndex + 1, since we already converted
+ # distinguishingIndex.
+ caseBody.append(CGIndenter(
+ getPerSignatureCall(sig, distinguishingIndex + 1, idx), 4))
+ caseBody.append(CGIndenter(CGGeneric("}")))
+
+ caseBody.append(CGGeneric("}"))
+
+ # XXXbz Now we're supposed to check for distinguishingArg being
+ # an array or a platform object that supports indexed
+ # properties... skip that last for now. It's a bit of a pain.
+ pickFirstSignature("%s.isObject() && IsArrayLike(cx, &%s.toObject())" %
+ (distinguishingArg, distinguishingArg),
+ lambda s:
+ (s[1][distinguishingIndex].type.isArray() or
+ s[1][distinguishingIndex].type.isSequence() or
+ s[1][distinguishingIndex].type.isObject()))
+
+ # Check for Date objects
+ # XXXbz Do we need to worry about security wrappers around the Date?
+ pickFirstSignature("%s.isObject() && JS_ObjectIsDate(cx, &%s.toObject())" %
+ (distinguishingArg, distinguishingArg),
+ lambda s: (s[1][distinguishingIndex].type.isDate() or
+ s[1][distinguishingIndex].type.isObject()))
+
+ # Check for vanilla JS objects
+ # XXXbz Do we need to worry about security wrappers?
+ pickFirstSignature("%s.isObject() && !IsPlatformObject(cx, &%s.toObject())" %
+ (distinguishingArg, distinguishingArg),
+ lambda s: (s[1][distinguishingIndex].type.isCallback() or
+ s[1][distinguishingIndex].type.isCallbackInterface() or
+ s[1][distinguishingIndex].type.isDictionary() or
+ s[1][distinguishingIndex].type.isObject()))
+
+ # The remaining cases are mutually exclusive. The
+ # pickFirstSignature calls are what change caseBody
+ # Check for strings or enums
+ if pickFirstSignature(None,
+ lambda s: (s[1][distinguishingIndex].type.isString() or
+ s[1][distinguishingIndex].type.isEnum())):
+ pass
+ # Check for primitives
+ elif pickFirstSignature(None,
+ lambda s: s[1][distinguishingIndex].type.isPrimitive()):
+ pass
+ # Check for "any"
+ elif pickFirstSignature(None,
+ lambda s: s[1][distinguishingIndex].type.isAny()):
+ pass
+ else:
+ # Just throw; we have no idea what we're supposed to
+ # do with this.
+ caseBody.append(CGGeneric("return Throw(cx, NS_ERROR_XPC_BAD_CONVERT_JS);"))
+
+ argCountCases.append(CGCase(str(argCount),
+ CGList(caseBody, "\n")))
+
+ overloadCGThings = []
+ overloadCGThings.append(
+ CGGeneric("let argcount = cmp::min(argc, %d);" %
+ maxArgCount))
+ overloadCGThings.append(
+ CGSwitch("argcount",
+ argCountCases,
+ CGGeneric("throw_type_error(cx, \"Not enough arguments to %s.\");\n"
+ "return 0;\n" % methodName)))
+ #XXXjdm Avoid unreachable statement warnings
+ #overloadCGThings.append(
+ # CGGeneric('fail!("We have an always-returning default case");\n'
+ # 'return 0;'))
+ self.cgRoot = CGWrapper(CGList(overloadCGThings, "\n"),
+ pre="\n")
+
+ def define(self):
+ return self.cgRoot.define()
+
+class FakeCastableDescriptor():
+ def __init__(self, descriptor):
+ self.nativeType = "*const %s" % descriptor.concreteType
+ self.name = descriptor.name
+ class FakeInterface:
+ def inheritanceDepth(self):
+ return descriptor.interface.inheritanceDepth()
+ self.interface = FakeInterface()
+
+def dictionaryHasSequenceMember(dictionary):
+ return (any(typeIsSequenceOrHasSequenceMember(m.type) for m in
+ dictionary.members) or
+ (dictionary.parent and
+ dictionaryHasSequenceMember(dictionary.parent)))
+
+def typeIsSequenceOrHasSequenceMember(type):
+ if type.nullable():
+ type = type.inner
+ if type.isSequence():
+ return True
+ if type.isArray():
+ elementType = type.inner
+ return typeIsSequenceOrHasSequenceMember(elementType)
+ if type.isDictionary():
+ return dictionaryHasSequenceMember(type.inner)
+ if type.isUnion():
+ return any(typeIsSequenceOrHasSequenceMember(m.type) for m in
+ type.flatMemberTypes)
+ return False
+
+def typeNeedsRooting(type, descriptorProvider):
+ return type.isGeckoInterface() and descriptorProvider.getDescriptor(type.name).needsRooting
+
+def getJSToNativeConversionTemplate(type, descriptorProvider, failureCode=None,
+ isDefinitelyObject=False,
+ isMember=False,
+ isArgument=False,
+ invalidEnumValueFatal=True,
+ defaultValue=None,
+ treatNullAs="Default",
+ isEnforceRange=False,
+ isClamp=False,
+ exceptionCode=None,
+ allowTreatNonObjectAsNull=False,
+ isCallbackReturnValue=False,
+ sourceDescription="value"):
+ """
+ Get a template for converting a JS value to a native object based on the
+ given type and descriptor. If failureCode is given, then we're actually
+ testing whether we can convert the argument to the desired type. That
+ means that failures to convert due to the JS value being the wrong type of
+ value need to use failureCode instead of throwing exceptions. Failures to
+ convert that are due to JS exceptions (from toString or valueOf methods) or
+ out of memory conditions need to throw exceptions no matter what
+ failureCode is.
+
+ If isDefinitelyObject is True, that means we know the value
+ isObject() and we have no need to recheck that.
+
+ if isMember is True, we're being converted from a property of some
+ JS object, not from an actual method argument, so we can't rely on
+ our jsval being rooted or outliving us in any way. Any caller
+ passing true needs to ensure that it is handled correctly in
+ typeIsSequenceOrHasSequenceMember.
+
+ invalidEnumValueFatal controls whether an invalid enum value conversion
+ attempt will throw (if true) or simply return without doing anything (if
+ false).
+
+ If defaultValue is not None, it's the IDL default value for this conversion
+
+ If isEnforceRange is true, we're converting an integer and throwing if the
+ value is out of range.
+
+ If isClamp is true, we're converting an integer and clamping if the
+ value is out of range.
+
+ If allowTreatNonObjectAsNull is true, then [TreatNonObjectAsNull]
+ extended attributes on nullable callback functions will be honored.
+
+ The return value from this function is a tuple consisting of four things:
+
+ 1) A string representing the conversion code. This will have template
+ substitution performed on it as follows:
+
+ ${val} replaced by an expression for the JS::Value in question
+
+ 2) A string or None representing Rust code for the default value (if any).
+
+ 3) A CGThing representing the native C++ type we're converting to
+ (declType). This is allowed to be None if the conversion code is
+ supposed to be used as-is.
+
+ 4) A boolean indicating whether the caller has to root the result.
+
+ """
+ # We should not have a defaultValue if we know we're an object
+ assert(not isDefinitelyObject or defaultValue is None)
+
+ # If exceptionCode is not set, we'll just rethrow the exception we got.
+ # Note that we can't just set failureCode to exceptionCode, because setting
+ # failureCode will prevent pending exceptions from being set in cases when
+ # they really should be!
+ if exceptionCode is None:
+ exceptionCode = "return 0;"
+
+ needsRooting = typeNeedsRooting(type, descriptorProvider)
+
+ def handleOptional(template, declType, default):
+ assert (defaultValue is None) == (default is None)
+ return (template, default, declType, needsRooting)
+
+ # Unfortunately, .capitalize() on a string will lowercase things inside the
+ # string, which we do not want.
+ def firstCap(string):
+ return string[0].upper() + string[1:]
+
+ # Helper functions for dealing with failures due to the JS value being the
+ # wrong type of value
+ # Helper functions for dealing with failures due to the JS value being the
+ # wrong type of value
+ def onFailureNotAnObject(failureCode):
+ return CGWrapper(
+ CGGeneric(
+ failureCode or
+ ('throw_type_error(cx, "%s is not an object.");\n'
+ '%s' % (firstCap(sourceDescription), exceptionCode))),
+ post="\n")
+ def onFailureBadType(failureCode, typeName):
+ return CGWrapper(
+ CGGeneric(
+ failureCode or
+ ('throw_type_error(cx, \"%s does not implement interface %s.\");\n'
+ '%s' % (firstCap(sourceDescription), typeName,
+ exceptionCode))),
+ post="\n")
+ def onFailureNotCallable(failureCode):
+ return CGWrapper(
+ CGGeneric(
+ failureCode or
+ ('throw_type_error(cx, \"%s is not callable.\");\n'
+ '%s' % (firstCap(sourceDescription), exceptionCode))),
+ post="\n")
+
+
+ # A helper function for handling null default values. Checks that the
+ # default value, if it exists, is null.
+ def handleDefaultNull(nullValue):
+ if defaultValue is None:
+ return None
+
+ if not isinstance(defaultValue, IDLNullValue):
+ raise TypeError("Can't handle non-null default value here")
+
+ assert type.nullable() or type.isDictionary()
+ return nullValue
+
+ # A helper function for wrapping up the template body for
+ # possibly-nullable objecty stuff
+ def wrapObjectTemplate(templateBody, isDefinitelyObject, type,
+ failureCode=None):
+ if not isDefinitelyObject:
+ # Handle the non-object cases by wrapping up the whole
+ # thing in an if cascade.
+ templateBody = (
+ "if (${val}).is_object() {\n" +
+ CGIndenter(CGGeneric(templateBody)).define() + "\n")
+ if type.nullable():
+ templateBody += (
+ "} else if (${val}).is_null_or_undefined() {\n"
+ " None\n")
+ templateBody += (
+ "} else {\n" +
+ CGIndenter(onFailureNotAnObject(failureCode)).define() +
+ "}\n")
+
+ return templateBody
+
+ assert not (isEnforceRange and isClamp) # These are mutually exclusive
+
+ if type.isArray():
+ raise TypeError("Can't handle array arguments yet")
+
+ if type.isSequence():
+ raise TypeError("Can't handle sequence arguments yet")
+
+ if type.isUnion():
+ declType = CGGeneric(type.name + "::" + type.name)
+ if type.nullable():
+ declType = CGWrapper(declType, pre="Option<", post=" >")
+
+ templateBody = ("match FromJSValConvertible::from_jsval(cx, ${val}, ()) {\n"
+ " Ok(value) => value,\n"
+ " Err(()) => { %s },\n"
+ "}" % exceptionCode)
+
+ return handleOptional(templateBody, declType, handleDefaultNull("None"))
+
+ if type.isGeckoInterface():
+ assert not isEnforceRange and not isClamp
+
+ descriptor = descriptorProvider.getDescriptor(
+ type.unroll().inner.identifier.name)
+
+ if descriptor.interface.isCallback():
+ name = descriptor.nativeType
+ declType = CGGeneric("Option<%s>" % name);
+ conversion = ("Some(%s::new((${val}).to_object()))" % name)
+
+ template = wrapObjectTemplate(conversion, isDefinitelyObject, type,
+ failureCode)
+ return handleOptional(template, declType, handleDefaultNull("None"))
+
+ if isMember:
+ descriptorType = descriptor.memberType
+ elif isArgument:
+ descriptorType = descriptor.argumentType
+ else:
+ descriptorType = descriptor.nativeType
+
+ templateBody = ""
+ if descriptor.interface.isConsequential():
+ raise TypeError("Consequential interface %s being used as an "
+ "argument" % descriptor.interface.identifier.name)
+
+ if failureCode is None:
+ substitutions = {
+ "sourceDescription": sourceDescription,
+ "interface": descriptor.interface.identifier.name,
+ "exceptionCode": exceptionCode,
+ }
+ unwrapFailureCode = string.Template(
+ 'throw_type_error(cx, "${sourceDescription} does not '
+ 'implement interface ${interface}.");\n'
+ '${exceptionCode}').substitute(substitutions)
+ else:
+ unwrapFailureCode = failureCode
+
+ templateBody = str(CastableObjectUnwrapper(
+ descriptor,
+ "(${val}).to_object()",
+ unwrapFailureCode))
+
+ declType = CGGeneric(descriptorType)
+ if type.nullable():
+ templateBody = "Some(%s)" % templateBody
+ declType = CGWrapper(declType, pre="Option<", post=">")
+
+ if isMember:
+ templateBody += ".root()"
+
+ templateBody = wrapObjectTemplate(templateBody, isDefinitelyObject,
+ type, failureCode)
+
+ return handleOptional(templateBody, declType, handleDefaultNull("None"))
+
+ if type.isSpiderMonkeyInterface():
+ raise TypeError("Can't handle SpiderMonkey interface arguments yet")
+
+ if type.isDOMString():
+ assert not isEnforceRange and not isClamp
+
+ treatAs = {
+ "Default": "Default",
+ "EmptyString": "Empty",
+ }
+ if treatNullAs not in treatAs:
+ raise TypeError("We don't support [TreatNullAs=%s]" % treatNullAs)
+ if type.nullable():
+ nullBehavior = "()"
+ else:
+ nullBehavior = treatAs[treatNullAs]
+
+ conversionCode = (
+ "match FromJSValConvertible::from_jsval(cx, ${val}, %s) {\n"
+ " Ok(strval) => strval,\n"
+ " Err(_) => { %s },\n"
+ "}" % (nullBehavior, exceptionCode))
+
+ if defaultValue is None:
+ default = None
+ elif isinstance(defaultValue, IDLNullValue):
+ assert type.nullable()
+ default = "None"
+ else:
+ assert defaultValue.type.tag() == IDLType.Tags.domstring
+ value = "str::from_utf8(data).unwrap().to_string()"
+ if type.nullable():
+ value = "Some(%s)" % value
+
+ default = (
+ "static data: [u8, ..%s] = [ %s ];\n"
+ "%s" %
+ (len(defaultValue.value) + 1,
+ ", ".join(["'" + char + "' as u8" for char in defaultValue.value] + ["0"]),
+ value))
+
+ declType = "DOMString"
+ if type.nullable():
+ declType = "Option<%s>" % declType
+
+ return handleOptional(conversionCode, CGGeneric(declType), default)
+
+ if type.isByteString():
+ assert not isEnforceRange and not isClamp
+
+ conversionCode = (
+ "match FromJSValConvertible::from_jsval(cx, ${val}, ()) {\n"
+ " Ok(strval) => strval,\n"
+ " Err(_) => { %s },\n"
+ "}" % exceptionCode)
+
+ declType = CGGeneric("ByteString")
+ if type.nullable():
+ declType = CGWrapper(declType, pre="Option<", post=">")
+
+ return handleOptional(conversionCode, declType, handleDefaultNull("None"))
+
+ if type.isEnum():
+ assert not isEnforceRange and not isClamp
+
+ if type.nullable():
+ raise TypeError("We don't support nullable enumerated arguments "
+ "yet")
+ enum = type.inner.identifier.name
+ if invalidEnumValueFatal:
+ handleInvalidEnumValueCode = exceptionCode
+ else:
+ handleInvalidEnumValueCode = "return 1;"
+
+ template = (
+ "match FindEnumStringIndex(cx, ${val}, %(values)s) {\n"
+ " Err(_) => { %(exceptionCode)s },\n"
+ " Ok(None) => { %(handleInvalidEnumValueCode)s },\n"
+ " Ok(Some(index)) => {\n"
+ " //XXXjdm need some range checks up in here.\n"
+ " unsafe { mem::transmute(index) }\n"
+ " },\n"
+ "}" % { "values" : enum + "Values::strings",
+ "exceptionCode" : exceptionCode,
+"handleInvalidEnumValueCode" : handleInvalidEnumValueCode })
+
+ if defaultValue is not None:
+ assert(defaultValue.type.tag() == IDLType.Tags.domstring)
+ default = "%sValues::%s" % (enum, getEnumValueName(defaultValue.value))
+ else:
+ default = None
+
+ return handleOptional(template, CGGeneric(enum), default)
+
+ if type.isCallback():
+ assert not isEnforceRange and not isClamp
+ assert not type.treatNonCallableAsNull()
+ assert not type.treatNonObjectAsNull() or type.nullable()
+ assert not type.treatNonObjectAsNull() or not type.treatNonCallableAsNull()
+
+ declType = CGGeneric('%s::%s' % (type.unroll().module(), type.unroll().identifier.name))
+
+ conversion = CGCallbackTempRoot(declType.define())
+
+ if type.nullable():
+ declType = CGTemplatedType("Option", declType)
+ conversion = CGWrapper(conversion, pre="Some(", post=")")
+
+ if allowTreatNonObjectAsNull and type.treatNonObjectAsNull():
+ if not isDefinitelyObject:
+ haveObject = "${val}.is_object()"
+ template = CGIfElseWrapper(haveObject,
+ conversion,
+ CGGeneric("None")).define()
+ else:
+ template = conversion
+ else:
+ template = CGIfElseWrapper("JS_ObjectIsCallable(cx, ${val}.to_object()) != 0",
+ conversion,
+ onFailureNotCallable(failureCode)).define()
+ template = wrapObjectTemplate(
+ template,
+ isDefinitelyObject,
+ type,
+ failureCode)
+
+ if defaultValue is not None:
+ assert allowTreatNonObjectAsNull
+ assert type.treatNonObjectAsNull()
+ assert type.nullable()
+ assert isinstance(defaultValue, IDLNullValue)
+ default = "None"
+ else:
+ default = None
+
+ return (template, default, declType, needsRooting)
+
+ if type.isAny():
+ assert not isEnforceRange and not isClamp
+
+ declType = CGGeneric("JSVal")
+
+ if defaultValue is None:
+ default = None
+ elif isinstance(defaultValue, IDLNullValue):
+ default = "NullValue()"
+ elif isinstance(defaultValue, IDLUndefinedValue):
+ default = "UndefinedValue()"
+ else:
+ raise TypeError("Can't handle non-null, non-undefined default value here")
+
+ return handleOptional("${val}", declType, default)
+
+ if type.isObject():
+ raise TypeError("Can't handle object arguments yet")
+
+ if type.isDictionary():
+ if failureCode is not None:
+ raise TypeError("Can't handle dictionaries when failureCode is not None")
+ # There are no nullable dictionaries
+ assert not type.nullable()
+
+ typeName = CGDictionary.makeDictionaryName(type.inner)
+ declType = CGGeneric(typeName)
+ template = ("match %s::new(cx, ${val}) {\n"
+ " Ok(dictionary) => dictionary,\n"
+ " Err(_) => return 0,\n"
+ "}" % typeName)
+
+ return handleOptional(template, declType, handleDefaultNull("%s::empty()" % typeName))
+
+ if type.isVoid():
+ # This one only happens for return values, and its easy: Just
+ # ignore the jsval.
+ return ("", None, None, False)
+
+ if not type.isPrimitive():
+ raise TypeError("Need conversion for argument type '%s'" % str(type))
+
+ assert not isEnforceRange and not isClamp
+
+ if failureCode is None:
+ failureCode = 'return 0'
+
+ declType = CGGeneric(builtinNames[type.tag()])
+ if type.nullable():
+ declType = CGWrapper(declType, pre="Option<", post=">")
+
+ #XXXjdm support conversionBehavior here
+ template = (
+ "match FromJSValConvertible::from_jsval(cx, ${val}, ()) {\n"
+ " Ok(v) => v,\n"
+ " Err(_) => { %s }\n"
+ "}" % exceptionCode)
+
+ if defaultValue is not None:
+ if isinstance(defaultValue, IDLNullValue):
+ assert type.nullable()
+ defaultStr = "None"
+ else:
+ tag = defaultValue.type.tag()
+ if tag in numericTags:
+ defaultStr = str(defaultValue.value)
+ else:
+ assert(tag == IDLType.Tags.bool)
+ defaultStr = toStringBool(defaultValue.value)
+
+ if type.nullable():
+ defaultStr = "Some(%s)" % defaultStr
+ else:
+ defaultStr = None
+
+ return handleOptional(template, declType, defaultStr)
+
+def instantiateJSToNativeConversionTemplate(templateBody, replacements,
+ declType, declName, needsRooting):
+ """
+ Take the templateBody and declType as returned by
+ getJSToNativeConversionTemplate, a set of replacements as required by the
+ strings in such a templateBody, and a declName, and generate code to
+ convert into a stack Rust binding with that name.
+ """
+ result = CGList([], "\n")
+
+ conversion = CGGeneric(
+ string.Template(templateBody).substitute(replacements)
+ )
+
+ if declType is not None:
+ newDecl = [
+ CGGeneric("let "),
+ CGGeneric(declName),
+ CGGeneric(": "),
+ declType,
+ CGGeneric(" = "),
+ conversion,
+ CGGeneric(";"),
+ ]
+ result.append(CGList(newDecl))
+ else:
+ result.append(conversion)
+
+ # Add an empty CGGeneric to get an extra newline after the argument
+ # conversion.
+ result.append(CGGeneric(""))
+
+ if needsRooting:
+ rootBody = "let %s = %s.root();" % (declName, declName)
+ result.append(CGGeneric(rootBody))
+ result.append(CGGeneric(""))
+
+ return result;
+
+def convertConstIDLValueToJSVal(value):
+ if isinstance(value, IDLNullValue):
+ return "NullVal"
+ tag = value.type.tag()
+ if tag in [IDLType.Tags.int8, IDLType.Tags.uint8, IDLType.Tags.int16,
+ IDLType.Tags.uint16, IDLType.Tags.int32]:
+ return "IntVal(%s)" % (value.value)
+ if tag == IDLType.Tags.uint32:
+ return "UintVal(%s)" % (value.value)
+ if tag in [IDLType.Tags.int64, IDLType.Tags.uint64]:
+ return "DoubleVal(%s)" % (value.value)
+ if tag == IDLType.Tags.bool:
+ return "BoolVal(true)" if value.value else "BoolVal(false)"
+ if tag in [IDLType.Tags.float, IDLType.Tags.double]:
+ return "DoubleVal(%s)" % (value.value)
+ raise TypeError("Const value of unhandled type: " + value.type)
+
+class CGArgumentConverter(CGThing):
+ """
+ A class that takes an IDL argument object, its index in the
+ argument list, and the argv and argc strings and generates code to
+ unwrap the argument to the right native type.
+ """
+ def __init__(self, argument, index, argv, argc, descriptorProvider,
+ invalidEnumValueFatal=True):
+ CGThing.__init__(self)
+ assert(not argument.defaultValue or argument.optional)
+
+ replacer = {
+ "index": index,
+ "argc": argc,
+ "argv": argv
+ }
+ condition = string.Template("${index} < ${argc}").substitute(replacer)
+
+ replacementVariables = {
+ "val": string.Template("(*${argv}.offset(${index}))").substitute(replacer),
+ }
+
+ template, default, declType, needsRooting = getJSToNativeConversionTemplate(
+ argument.type,
+ descriptorProvider,
+ invalidEnumValueFatal=invalidEnumValueFatal,
+ defaultValue=argument.defaultValue,
+ treatNullAs=argument.treatNullAs,
+ isEnforceRange=argument.enforceRange,
+ isClamp=argument.clamp,
+ isMember="Variadic" if argument.variadic else False,
+ allowTreatNonObjectAsNull=argument.allowTreatNonCallableAsNull())
+
+ if not argument.variadic:
+ if argument.optional:
+ if argument.defaultValue:
+ assert default
+ template = CGIfElseWrapper(condition,
+ CGGeneric(template),
+ CGGeneric(default)).define()
+ else:
+ assert not default
+ declType = CGWrapper(declType, pre="Option<", post=">")
+ template = CGIfElseWrapper(condition,
+ CGGeneric("Some(%s)" % template),
+ CGGeneric("None")).define()
+ else:
+ assert not default
+
+ self.converter = instantiateJSToNativeConversionTemplate(
+ template, replacementVariables, declType, "arg%d" % index,
+ needsRooting)
+ else:
+ assert argument.optional
+ variadicConversion = {
+ "val": string.Template("(*${argv}.offset(variadicArg as int))").substitute(replacer),
+ }
+ innerConverter = instantiateJSToNativeConversionTemplate(
+ template, variadicConversion, declType, "slot",
+ needsRooting)
+
+ seqType = CGTemplatedType("Vec", declType)
+ variadicConversion = string.Template(
+ "{\n"
+ " let mut vector: ${seqType} = Vec::with_capacity((${argc} - ${index}) as uint);\n"
+ " for variadicArg in range(${index}, ${argc}) {\n"
+ "${inner}\n"
+ " vector.push(slot);\n"
+ " }\n"
+ " vector\n"
+ "}"
+ ).substitute({
+ "index": index,
+ "argc": argc,
+ "seqType": seqType.define(),
+ "inner": CGIndenter(innerConverter, 4).define(),
+ })
+
+ self.converter = instantiateJSToNativeConversionTemplate(
+ variadicConversion, replacementVariables, seqType, "arg%d" % index,
+ False)
+
+ def define(self):
+ return self.converter.define()
+
+
+def wrapForType(jsvalRef, result='result', successCode='return 1;'):
+ """
+ Reflect a Rust value into JS.
+
+ * 'jsvalRef': a Rust reference to the JSVal in which to store the result
+ of the conversion;
+ * 'result': the name of the variable in which the Rust value is stored;
+ * 'successCode': the code to run once we have done the conversion.
+ """
+ return "%s = (%s).to_jsval(cx);\n%s" % (jsvalRef, result, successCode)
+
+
+def typeNeedsCx(type, retVal=False):
+ if type is None:
+ return False
+ if type.nullable():
+ type = type.inner
+ if type.isSequence() or type.isArray():
+ type = type.inner
+ if type.isUnion():
+ return any(typeNeedsCx(t) for t in type.unroll().flatMemberTypes)
+ if retVal and type.isSpiderMonkeyInterface():
+ return True
+ return type.isAny() or type.isObject()
+
+def typeRetValNeedsRooting(type):
+ if type is None:
+ return False
+ if type.nullable():
+ type = type.inner
+ return type.isGeckoInterface() and not type.isCallback() and not type.isCallbackInterface()
+
+def memberIsCreator(member):
+ return member.getExtendedAttribute("Creator") is not None
+
+# Returns a CGThing containing the type of the return value.
+def getRetvalDeclarationForType(returnType, descriptorProvider):
+ if returnType is None or returnType.isVoid():
+ # Nothing to declare
+ return CGGeneric("()")
+ if returnType.isPrimitive() and returnType.tag() in builtinNames:
+ result = CGGeneric(builtinNames[returnType.tag()])
+ if returnType.nullable():
+ result = CGWrapper(result, pre="Option<", post=">")
+ return result
+ if returnType.isDOMString():
+ result = CGGeneric("DOMString")
+ if returnType.nullable():
+ result = CGWrapper(result, pre="Option<", post=">")
+ return result
+ if returnType.isByteString():
+ result = CGGeneric("ByteString")
+ if returnType.nullable():
+ result = CGWrapper(result, pre="Option<", post=">")
+ return result
+ if returnType.isEnum():
+ result = CGGeneric(returnType.unroll().inner.identifier.name)
+ if returnType.nullable():
+ result = CGWrapper(result, pre="Option<", post=">")
+ return result
+ if returnType.isGeckoInterface():
+ descriptor = descriptorProvider.getDescriptor(
+ returnType.unroll().inner.identifier.name)
+ result = CGGeneric(descriptor.returnType)
+ if returnType.nullable():
+ result = CGWrapper(result, pre="Option<", post=">")
+ return result
+ if returnType.isCallback():
+ result = CGGeneric('%s::%s' % (returnType.unroll().module(),
+ returnType.unroll().identifier.name))
+ if returnType.nullable():
+ result = CGWrapper(result, pre="Option<", post=">")
+ return result
+ if returnType.isUnion():
+ result = CGGeneric('%s::%s' % (returnType.unroll().name, returnType.unroll().name))
+ if returnType.nullable():
+ result = CGWrapper(result, pre="Option<", post=">")
+ return result
+ if returnType.isAny():
+ return CGGeneric("JSVal")
+ if returnType.isObject() or returnType.isSpiderMonkeyInterface():
+ return CGGeneric("*mut JSObject")
+ if returnType.isSequence():
+ raise TypeError("We don't support sequence return values")
+
+ raise TypeError("Don't know how to declare return value for %s" %
+ returnType)
+
+class PropertyDefiner:
+ """
+ A common superclass for defining things on prototype objects.
+
+ Subclasses should implement generateArray to generate the actual arrays of
+ things we're defining. They should also set self.regular to the list of
+ things exposed to web pages.
+ """
+ def __init__(self, descriptor, name):
+ self.descriptor = descriptor
+ self.name = name
+
+ def variableName(self):
+ return "s" + self.name
+
+ def length(self):
+ return len(self.regular)
+
+ def __str__(self):
+ # We only need to generate id arrays for things that will end
+ # up used via ResolveProperty or EnumerateProperties.
+ return self.generateArray(self.regular, self.variableName())
+
+ def generatePrefableArray(self, array, name, specTemplate, specTerminator,
+ specType, getDataTuple):
+ """
+ This method generates our various arrays.
+
+ array is an array of interface members as passed to generateArray
+
+ name is the name as passed to generateArray
+
+ specTemplate is a template for each entry of the spec array
+
+ specTerminator is a terminator for the spec array (inserted at the end
+ of the array), or None
+
+ specType is the actual typename of our spec
+
+ getDataTuple is a callback function that takes an array entry and
+ returns a tuple suitable for substitution into specTemplate.
+ """
+
+ assert(len(array) is not 0)
+ specs = []
+
+ for member in array:
+ specs.append(specTemplate % getDataTuple(member))
+ if specTerminator:
+ specs.append(specTerminator)
+
+ return (("static %s: &'static [%s] = &[\n" +
+ ",\n".join(specs) + "\n" +
+ "];\n\n") % (name, specType))
+
+# The length of a method is the maximum of the lengths of the
+# argument lists of all its overloads.
+def methodLength(method):
+ signatures = method.signatures()
+ return max([len(arguments) for (retType, arguments) in signatures])
+
+class MethodDefiner(PropertyDefiner):
+ """
+ A class for defining methods on a prototype object.
+ """
+ def __init__(self, descriptor, name, static):
+ PropertyDefiner.__init__(self, descriptor, name)
+
+ # FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=772822
+ # We should be able to check for special operations without an
+ # identifier. For now we check if the name starts with __
+ methods = [m for m in descriptor.interface.members if
+ m.isMethod() and m.isStatic() == static and
+ not m.isIdentifierLess()]
+ self.regular = [{"name": m.identifier.name,
+ "methodInfo": not m.isStatic(),
+ "length": methodLength(m),
+ "flags": "JSPROP_ENUMERATE" }
+ for m in methods]
+
+ # FIXME Check for an existing iterator on the interface first.
+ if any(m.isGetter() and m.isIndexed() for m in methods):
+ self.regular.append({"name": 'iterator',
+ "methodInfo": False,
+ "nativeName": "JS_ArrayIterator",
+ "length": 0,
+ "flags": "JSPROP_ENUMERATE" })
+
+ def generateArray(self, array, name):
+ if len(array) == 0:
+ return ""
+
+ def specData(m):
+ if m.get("methodInfo", True):
+ jitinfo = ("&%s_methodinfo" % m["name"])
+ accessor = "genericMethod"
+ else:
+ jitinfo = "0 as *const JSJitInfo"
+ accessor = m.get("nativeName", m["name"])
+ return (m["name"], accessor, jitinfo, m["length"], m["flags"])
+
+ def stringDecl(m):
+ return "static %s_name: [u8, ..%i] = %s;\n" % (m["name"], len(m["name"]) + 1,
+ str_to_const_array(m["name"]))
+
+ decls = ''.join([stringDecl(m) for m in array])
+ return decls + self.generatePrefableArray(
+ array, name,
+ ' JSFunctionSpec {name: &%s_name as *const u8 as *const libc::c_char, call: JSNativeWrapper {op: Some(%s), info: %s}, nargs: %s, flags: %s as u16, selfHostedName: 0 as *const libc::c_char }',
+ ' JSFunctionSpec {name: 0 as *const libc::c_char, call: JSNativeWrapper {op: None, info: 0 as *const JSJitInfo}, nargs: 0, flags: 0, selfHostedName: 0 as *const libc::c_char }',
+ 'JSFunctionSpec',
+ specData)
+
+class AttrDefiner(PropertyDefiner):
+ def __init__(self, descriptor, name, static):
+ PropertyDefiner.__init__(self, descriptor, name)
+ self.name = name
+ self.regular = [
+ m
+ for m in descriptor.interface.members
+ if m.isAttr() and m.isStatic() == static
+ ]
+ self.static = static
+
+ def generateArray(self, array, name):
+ if len(array) == 0:
+ return ""
+
+ def flags(attr):
+ return "JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_NATIVE_ACCESSORS"
+
+ def getter(attr):
+ if self.static:
+ accessor = 'get_' + attr.identifier.name
+ jitinfo = "0"
+ else:
+ if attr.hasLenientThis():
+ accessor = "genericLenientGetter"
+ else:
+ accessor = "genericGetter"
+ jitinfo = "&%s_getterinfo" % attr.identifier.name
+
+ return ("JSPropertyOpWrapper {op: Some(%(native)s), info: %(info)s as *const JSJitInfo}"
+ % {"info" : jitinfo,
+ "native" : accessor})
+
+ def setter(attr):
+ if attr.readonly:
+ return "JSStrictPropertyOpWrapper {op: None, info: 0 as *const JSJitInfo}"
+
+ if self.static:
+ accessor = 'set_' + attr.identifier.name
+ jitinfo = "0"
+ else:
+ if attr.hasLenientThis():
+ accessor = "genericLenientSetter"
+ else:
+ accessor = "genericSetter"
+ jitinfo = "&%s_setterinfo" % attr.identifier.name
+
+ return ("JSStrictPropertyOpWrapper {op: Some(%(native)s), info: %(info)s as *const JSJitInfo}"
+ % {"info" : jitinfo,
+ "native" : accessor})
+
+ def specData(attr):
+ return (attr.identifier.name, flags(attr), getter(attr),
+ setter(attr))
+
+ def stringDecl(attr):
+ name = attr.identifier.name
+ return "static %s_name: [u8, ..%i] = %s;\n" % (name, len(name) + 1,
+ str_to_const_array(name))
+
+ decls = ''.join([stringDecl(m) for m in array])
+
+ return decls + self.generatePrefableArray(
+ array, name,
+ ' JSPropertySpec { name: &%s_name as *const u8 as *const libc::c_char, tinyid: 0, flags: ((%s) & 0xFF) as u8, getter: %s, setter: %s }',
+ ' JSPropertySpec { name: 0 as *const libc::c_char, tinyid: 0, flags: 0, getter: JSPropertyOpWrapper {op: None, info: 0 as *const JSJitInfo}, setter: JSStrictPropertyOpWrapper {op: None, info: 0 as *const JSJitInfo} }',
+ 'JSPropertySpec',
+ specData)
+
+class ConstDefiner(PropertyDefiner):
+ """
+ A class for definining constants on the interface object
+ """
+ def __init__(self, descriptor, name):
+ PropertyDefiner.__init__(self, descriptor, name)
+ self.name = name
+ self.regular = [m for m in descriptor.interface.members if m.isConst()]
+
+ def generateArray(self, array, name):
+ if len(array) == 0:
+ return ""
+
+ def specData(const):
+ return (const.identifier.name,
+ convertConstIDLValueToJSVal(const.value))
+
+ def stringDecl(const):
+ name = const.identifier.name
+ return "static %s_name: &'static [u8] = &%s;\n" % (name, str_to_const_array(name))
+
+ decls = ''.join([stringDecl(m) for m in array])
+
+ return decls + self.generatePrefableArray(
+ array, name,
+ ' ConstantSpec { name: %s_name, value: %s }',
+ None,
+ 'ConstantSpec',
+ specData)
+
+# We'll want to insert the indent at the beginnings of lines, but we
+# don't want to indent empty lines. So only indent lines that have a
+# non-newline character on them.
+lineStartDetector = re.compile("^(?=[^\n])", re.MULTILINE)
+class CGIndenter(CGThing):
+ """
+ A class that takes another CGThing and generates code that indents that
+ CGThing by some number of spaces. The default indent is two spaces.
+ """
+ def __init__(self, child, indentLevel=2):
+ CGThing.__init__(self)
+ self.child = child
+ self.indent = " " * indentLevel
+
+ def define(self):
+ defn = self.child.define()
+ if defn is not "":
+ return re.sub(lineStartDetector, self.indent, defn)
+ else:
+ return defn
+
+class CGWrapper(CGThing):
+ """
+ Generic CGThing that wraps other CGThings with pre and post text.
+ """
+ def __init__(self, child, pre="", post="", reindent=False):
+ CGThing.__init__(self)
+ self.child = child
+ self.pre = pre
+ self.post = post
+ self.reindent = reindent
+
+ def define(self):
+ defn = self.child.define()
+ if self.reindent:
+ # We don't use lineStartDetector because we don't want to
+ # insert whitespace at the beginning of our _first_ line.
+ defn = stripTrailingWhitespace(
+ defn.replace("\n", "\n" + (" " * len(self.pre))))
+ return self.pre + defn + self.post
+
+class CGImports(CGWrapper):
+ """
+ Generates the appropriate import/use statements.
+ """
+ def __init__(self, child, descriptors, imports):
+ """
+ Adds a set of imports.
+ """
+ ignored_warnings = [
+ # Allow unreachable_code because we use 'break' in a way that
+ # sometimes produces two 'break's in a row. See for example
+ # CallbackMember.getArgConversions.
+ 'unreachable_code',
+ 'non_camel_case_types',
+ 'non_uppercase_statics',
+ 'unnecessary_parens',
+ 'unused_imports',
+ 'unused_variable',
+ 'unused_unsafe',
+ 'unused_mut',
+ 'dead_assignment',
+ 'dead_code',
+ ]
+
+ statements = ['#![allow(%s)]' % ','.join(ignored_warnings)]
+ statements.extend('use %s;' % i for i in sorted(imports))
+
+ CGWrapper.__init__(self, child,
+ pre='\n'.join(statements) + '\n\n')
+
+ @staticmethod
+ def getDeclarationFilename(decl):
+ # Use our local version of the header, not the exported one, so that
+ # test bindings, which don't export, will work correctly.
+ basename = os.path.basename(decl.filename())
+ return basename.replace('.webidl', 'Binding.rs')
+
+class CGIfWrapper(CGWrapper):
+ def __init__(self, child, condition):
+ pre = CGWrapper(CGGeneric(condition), pre="if ", post=" {\n",
+ reindent=True)
+ CGWrapper.__init__(self, CGIndenter(child), pre=pre.define(),
+ post="\n}")
+
+class CGTemplatedType(CGWrapper):
+ def __init__(self, templateName, child):
+ CGWrapper.__init__(self, child, pre=templateName + "<", post=">")
+
+class CGNamespace(CGWrapper):
+ def __init__(self, namespace, child, public=False):
+ pre = "%smod %s {\n" % ("pub " if public else "", namespace)
+ post = "} // mod %s\n" % namespace
+ CGWrapper.__init__(self, child, pre=pre, post=post)
+
+ @staticmethod
+ def build(namespaces, child, public=False):
+ """
+ Static helper method to build multiple wrapped namespaces.
+ """
+ if not namespaces:
+ return child
+ inner = CGNamespace.build(namespaces[1:], child, public=public)
+ return CGNamespace(namespaces[0], inner, public=public)
+
+def DOMClass(descriptor):
+ protoList = ['PrototypeList::id::' + proto for proto in descriptor.prototypeChain]
+ # Pad out the list to the right length with IDCount so we
+ # guarantee that all the lists are the same length. IDCount
+ # is never the ID of any prototype, so it's safe to use as
+ # padding.
+ protoList.extend(['PrototypeList::id::IDCount'] * (descriptor.config.maxProtoChainLength - len(protoList)))
+ prototypeChainString = ', '.join(protoList)
+ return """DOMClass {
+ interface_chain: [ %s ],
+ native_hooks: &sNativePropertyHooks,
+}""" % prototypeChainString
+
+class CGDOMJSClass(CGThing):
+ """
+ Generate a DOMJSClass for a given descriptor
+ """
+ def __init__(self, descriptor):
+ CGThing.__init__(self)
+ self.descriptor = descriptor
+
+ def define(self):
+ traceHook = "Some(%s)" % TRACE_HOOK_NAME
+ if self.descriptor.isGlobal():
+ flags = "JSCLASS_IS_GLOBAL | JSCLASS_DOM_GLOBAL"
+ slots = "JSCLASS_GLOBAL_SLOT_COUNT + 1"
+ else:
+ flags = "0"
+ slots = "1"
+ return """
+static Class_name: [u8, ..%i] = %s;
+static Class: DOMJSClass = DOMJSClass {
+ base: js::Class {
+ name: &Class_name as *const u8 as *const libc::c_char,
+ flags: JSCLASS_IS_DOMJSCLASS | %s | (((%s) & JSCLASS_RESERVED_SLOTS_MASK) << JSCLASS_RESERVED_SLOTS_SHIFT as uint), //JSCLASS_HAS_RESERVED_SLOTS(%s),
+ addProperty: Some(JS_PropertyStub),
+ delProperty: Some(JS_PropertyStub),
+ getProperty: Some(JS_PropertyStub),
+ setProperty: Some(JS_StrictPropertyStub),
+ enumerate: Some(JS_EnumerateStub),
+ resolve: Some(JS_ResolveStub),
+ convert: Some(JS_ConvertStub),
+ finalize: Some(%s),
+ checkAccess: None,
+ call: None,
+ hasInstance: None,
+ construct: None,
+ trace: %s,
+
+ ext: js::ClassExtension {
+ equality: 0 as *const u8,
+ outerObject: %s,
+ innerObject: None,
+ iteratorObject: 0 as *const u8,
+ unused: 0 as *const u8,
+ isWrappedNative: 0 as *const u8,
+ },
+
+ ops: js::ObjectOps {
+ lookupGeneric: 0 as *const u8,
+ lookupProperty: 0 as *const u8,
+ lookupElement: 0 as *const u8,
+ lookupSpecial: 0 as *const u8,
+ defineGeneric: 0 as *const u8,
+ defineProperty: 0 as *const u8,
+ defineElement: 0 as *const u8,
+ defineSpecial: 0 as *const u8,
+ getGeneric: 0 as *const u8,
+ getProperty: 0 as *const u8,
+ getElement: 0 as *const u8,
+ getElementIfPresent: 0 as *const u8,
+ getSpecial: 0 as *const u8,
+ setGeneric: 0 as *const u8,
+ setProperty: 0 as *const u8,
+ setElement: 0 as *const u8,
+ setSpecial: 0 as *const u8,
+ getGenericAttributes: 0 as *const u8,
+ getPropertyAttributes: 0 as *const u8,
+ getElementAttributes: 0 as *const u8,
+ getSpecialAttributes: 0 as *const u8,
+ setGenericAttributes: 0 as *const u8,
+ setPropertyAttributes: 0 as *const u8,
+ setElementAttributes: 0 as *const u8,
+ setSpecialAttributes: 0 as *const u8,
+ deleteProperty: 0 as *const u8,
+ deleteElement: 0 as *const u8,
+ deleteSpecial: 0 as *const u8,
+
+ enumerate: 0 as *const u8,
+ typeOf: 0 as *const u8,
+ thisObject: %s,
+ clear: 0 as *const u8,
+ },
+ },
+ dom_class: %s
+};
+""" % (len(self.descriptor.interface.identifier.name) + 1,
+ str_to_const_array(self.descriptor.interface.identifier.name),
+ flags, slots, slots,
+ FINALIZE_HOOK_NAME, traceHook,
+ self.descriptor.outerObjectHook,
+ self.descriptor.outerObjectHook,
+ CGIndenter(CGGeneric(DOMClass(self.descriptor))).define())
+
+def str_to_const_array(s):
+ return "[" + (", ".join(map(lambda x: "'" + x + "' as u8", list(s)) + ['0 as u8'])) + "]"
+
+class CGPrototypeJSClass(CGThing):
+ def __init__(self, descriptor):
+ CGThing.__init__(self)
+ self.descriptor = descriptor
+
+ def define(self):
+ return """
+static PrototypeClassName__: [u8, ..%s] = %s;
+static PrototypeClass: JSClass = JSClass {
+ name: &PrototypeClassName__ as *const u8 as *const libc::c_char,
+ flags: (1 & JSCLASS_RESERVED_SLOTS_MASK) << JSCLASS_RESERVED_SLOTS_SHIFT as uint, //JSCLASS_HAS_RESERVED_SLOTS(1)
+ addProperty: Some(JS_PropertyStub),
+ delProperty: Some(JS_PropertyStub),
+ getProperty: Some(JS_PropertyStub),
+ setProperty: Some(JS_StrictPropertyStub),
+ enumerate: Some(JS_EnumerateStub),
+ resolve: Some(JS_ResolveStub),
+ convert: Some(JS_ConvertStub),
+ finalize: None,
+ checkAccess: None,
+ call: None,
+ hasInstance: None,
+ construct: None,
+ trace: None,
+ reserved: [0 as *mut libc::c_void, ..40]
+};
+""" % (len(self.descriptor.interface.identifier.name + "Prototype") + 1,
+ str_to_const_array(self.descriptor.interface.identifier.name + "Prototype"))
+
+class CGInterfaceObjectJSClass(CGThing):
+ def __init__(self, descriptor):
+ CGThing.__init__(self)
+ self.descriptor = descriptor
+
+ def define(self):
+ if True:
+ return ""
+ ctorname = "0 as *const u8" if not self.descriptor.interface.ctor() else CONSTRUCT_HOOK_NAME
+ hasinstance = HASINSTANCE_HOOK_NAME
+ return """
+static InterfaceObjectClass: JSClass = {
+ %s, 0,
+ JS_PropertyStub,
+ JS_PropertyStub,
+ JS_PropertyStub,
+ JS_StrictPropertyStub,
+ JS_EnumerateStub,
+ JS_ResolveStub,
+ JS_ConvertStub,
+ 0 as *const u8,
+ 0 as *const u8,
+ %s,
+ %s,
+ %s,
+ 0 as *const u8,
+ JSCLASS_NO_INTERNAL_MEMBERS
+};
+""" % (str_to_const_array("Function"), ctorname, hasinstance, ctorname)
+
+class CGList(CGThing):
+ """
+ Generate code for a list of GCThings. Just concatenates them together, with
+ an optional joiner string. "\n" is a common joiner.
+ """
+ def __init__(self, children, joiner=""):
+ CGThing.__init__(self)
+ self.children = children
+ self.joiner = joiner
+ def append(self, child):
+ self.children.append(child)
+ def prepend(self, child):
+ self.children.insert(0, child)
+ def join(self, generator):
+ return self.joiner.join(filter(lambda s: len(s) > 0, (child for child in generator)))
+
+ def define(self):
+ return self.join(child.define() for child in self.children if child is not None)
+
+
+class CGIfElseWrapper(CGList):
+ def __init__(self, condition, ifTrue, ifFalse):
+ kids = [ CGIfWrapper(ifTrue, condition),
+ CGWrapper(CGIndenter(ifFalse), pre=" else {\n", post="\n}") ]
+ CGList.__init__(self, kids)
+
+
+class CGGeneric(CGThing):
+ """
+ A class that spits out a fixed string into the codegen. Can spit out a
+ separate string for the declaration too.
+ """
+ def __init__(self, text):
+ self.text = text
+
+ def define(self):
+ return self.text
+
+class CGCallbackTempRoot(CGGeneric):
+ def __init__(self, name):
+ val = "%s::new(tempRoot)" % name
+ define = """{
+ let tempRoot = ${val}.to_object();
+ %s
+}""" % val
+ CGGeneric.__init__(self, define)
+
+
+def getAllTypes(descriptors, dictionaries, callbacks):
+ """
+ Generate all the types we're dealing with. For each type, a tuple
+ containing type, descriptor, dictionary is yielded. The
+ descriptor and dictionary can be None if the type does not come
+ from a descriptor or dictionary; they will never both be non-None.
+ """
+ for d in descriptors:
+ for t in getTypesFromDescriptor(d):
+ yield (t, d, None)
+ for dictionary in dictionaries:
+ for t in getTypesFromDictionary(dictionary):
+ yield (t, None, dictionary)
+ for callback in callbacks:
+ for t in getTypesFromCallback(callback):
+ yield (t, None, None)
+
+def SortedTuples(l):
+ """
+ Sort a list of tuples based on the first item in the tuple
+ """
+ return sorted(l, key=operator.itemgetter(0))
+
+def SortedDictValues(d):
+ """
+ Returns a list of values from the dict sorted by key.
+ """
+ # Create a list of tuples containing key and value, sorted on key.
+ d = SortedTuples(d.items())
+ # We're only interested in the values.
+ return (i[1] for i in d)
+
+def UnionTypes(descriptors, dictionaries, callbacks, config):
+ """
+ Returns a CGList containing CGUnionStructs for every union.
+ """
+
+ imports = [
+ 'dom::bindings::utils::unwrap_jsmanaged',
+ 'dom::bindings::codegen::PrototypeList',
+ 'dom::bindings::conversions::FromJSValConvertible',
+ 'dom::bindings::conversions::ToJSValConvertible',
+ 'dom::bindings::conversions::Default',
+ 'dom::bindings::error::throw_not_in_union',
+ 'dom::bindings::js::JS',
+ 'dom::types::*',
+ 'js::jsapi::JSContext',
+ 'js::jsval::JSVal',
+ 'servo_util::str::DOMString',
+ ]
+
+ # Now find all the things we'll need as arguments and return values because
+ # we need to wrap or unwrap them.
+ unionStructs = dict()
+ for (t, descriptor, dictionary) in getAllTypes(descriptors, dictionaries, callbacks):
+ assert not descriptor or not dictionary
+ t = t.unroll()
+ if not t.isUnion():
+ continue
+ name = str(t)
+ if not name in unionStructs:
+ provider = descriptor or config.getDescriptorProvider()
+ unionStructs[name] = CGNamespace(name,
+ CGImports(CGList([
+ CGUnionStruct(t, provider),
+ CGUnionConversionStruct(t, provider)
+ ]), [], imports),
+ public=True)
+
+ return CGList(SortedDictValues(unionStructs), "\n\n")
+
+
+class Argument():
+ """
+ A class for outputting the type and name of an argument
+ """
+ def __init__(self, argType, name, default=None, mutable=False):
+ self.argType = argType
+ self.name = name
+ self.default = default
+ self.mutable = mutable
+ def declare(self):
+ string = ('mut ' if self.mutable else '') + self.name + ((': ' + self.argType) if self.argType else '')
+ #XXXjdm Support default arguments somehow :/
+ #if self.default is not None:
+ # string += " = " + self.default
+ return string
+ def define(self):
+ return self.argType + ' ' + self.name
+
+class CGAbstractMethod(CGThing):
+ """
+ An abstract class for generating code for a method. Subclasses
+ should override definition_body to create the actual code.
+
+ descriptor is the descriptor for the interface the method is associated with
+
+ name is the name of the method as a string
+
+ returnType is the IDLType of the return value
+
+ args is a list of Argument objects
+
+ inline should be True to generate an inline method, whose body is
+ part of the declaration.
+
+ alwaysInline should be True to generate an inline method annotated with
+ MOZ_ALWAYS_INLINE.
+
+ If templateArgs is not None it should be a list of strings containing
+ template arguments, and the function will be templatized using those
+ arguments.
+ """
+ def __init__(self, descriptor, name, returnType, args, inline=False, alwaysInline=False, extern=False, pub=False, templateArgs=None, unsafe=True):
+ CGThing.__init__(self)
+ self.descriptor = descriptor
+ self.name = name
+ self.returnType = returnType
+ self.args = args
+ self.alwaysInline = alwaysInline
+ self.extern = extern
+ self.templateArgs = templateArgs
+ self.pub = pub;
+ self.unsafe = unsafe
+ def _argstring(self):
+ return ', '.join([a.declare() for a in self.args])
+ def _template(self):
+ if self.templateArgs is None:
+ return ''
+ return '<%s>\n' % ', '.join(self.templateArgs)
+
+ def _decorators(self):
+ decorators = []
+ if self.alwaysInline:
+ decorators.append('#[inline(always)]')
+
+ if self.extern:
+ decorators.append('extern')
+
+ if self.pub:
+ decorators.append('pub')
+
+ if not decorators:
+ return ''
+ return ' '.join(decorators) + ' '
+
+ def _returnType(self):
+ return (" -> %s" % self.returnType) if self.returnType != "void" else ""
+
+ def define(self):
+ body = self.definition_body()
+ if self.unsafe:
+ body = CGWrapper(body, pre="unsafe {\n", post="\n}")
+
+ return CGWrapper(CGIndenter(body),
+ pre=self.definition_prologue(),
+ post=self.definition_epilogue()).define()
+
+ def definition_prologue(self):
+ return "%sfn %s%s(%s)%s {\n" % (self._decorators(), self.name, self._template(),
+ self._argstring(), self._returnType())
+ def definition_epilogue(self):
+ return "\n}\n"
+ def definition_body(self):
+ assert(False) # Override me!
+
+def CreateBindingJSObject(descriptor, parent=None):
+ create = "let mut raw: JS<%s> = JS::from_raw(&*aObject);\n" % descriptor.concreteType
+ if descriptor.proxy:
+ assert not descriptor.isGlobal()
+ create += """
+let handler = RegisterBindings::proxy_handlers[PrototypeList::proxies::%s as uint];
+let mut private = PrivateValue(squirrel_away_unique(aObject) as *const libc::c_void);
+let obj = with_compartment(aCx, proto, || {
+ NewProxyObject(aCx, handler,
+ &private,
+ proto, %s,
+ ptr::mut_null(), ptr::mut_null())
+});
+assert!(obj.is_not_null());
+
+""" % (descriptor.name, parent)
+ else:
+ if descriptor.isGlobal():
+ create += "let obj = CreateDOMGlobal(aCx, &Class.base as *const js::Class as *const JSClass);\n"
+ else:
+ create += ("let obj = with_compartment(aCx, proto, || {\n"
+ " JS_NewObject(aCx, &Class.base as *const js::Class as *const JSClass, &*proto, &*%s)\n"
+ "});\n" % parent)
+ create += """assert!(obj.is_not_null());
+
+JS_SetReservedSlot(obj, DOM_OBJECT_SLOT as u32,
+ PrivateValue(squirrel_away_unique(aObject) as *const libc::c_void));
+"""
+ return create
+
+class CGWrapMethod(CGAbstractMethod):
+ """
+ Class that generates the FooBinding::Wrap function for non-callback
+ interfaces.
+ """
+ def __init__(self, descriptor):
+ assert not descriptor.interface.isCallback()
+ if not descriptor.isGlobal():
+ args = [Argument('*mut JSContext', 'aCx'), Argument('&GlobalRef', 'aScope'),
+ Argument("Box<%s>" % descriptor.concreteType, 'aObject', mutable=True)]
+ else:
+ args = [Argument('*mut JSContext', 'aCx'),
+ Argument("Box<%s>" % descriptor.concreteType, 'aObject', mutable=True)]
+ retval = 'Temporary<%s>' % descriptor.concreteType
+ CGAbstractMethod.__init__(self, descriptor, 'Wrap', retval, args, pub=True)
+
+ def definition_body(self):
+ if not self.descriptor.isGlobal():
+ return CGGeneric("""\
+let scope = aScope.reflector().get_jsobject();
+assert!(scope.is_not_null());
+assert!(((*JS_GetClass(scope)).flags & JSCLASS_IS_GLOBAL) != 0);
+
+let proto = with_compartment(aCx, scope, || GetProtoObject(aCx, scope, scope));
+assert!(proto.is_not_null());
+
+%s
+
+raw.reflector().set_jsobject(obj);
+
+Temporary::new(raw)""" % CreateBindingJSObject(self.descriptor, "scope"))
+ else:
+ return CGGeneric("""\
+%s
+with_compartment(aCx, obj, || {
+ let proto = GetProtoObject(aCx, obj, obj);
+ JS_SetPrototype(aCx, obj, proto);
+
+ raw.reflector().set_jsobject(obj);
+
+ RegisterBindings::Register(aCx, obj);
+});
+
+Temporary::new(raw)""" % CreateBindingJSObject(self.descriptor))
+
+
+class CGIDLInterface(CGThing):
+ """
+ Class for codegen of an implementation of the IDLInterface trait.
+ """
+ def __init__(self, descriptor):
+ CGThing.__init__(self)
+ self.descriptor = descriptor
+
+ def define(self):
+ replacer = {
+ 'type': self.descriptor.name,
+ 'depth': self.descriptor.interface.inheritanceDepth(),
+ }
+ return string.Template("""
+impl IDLInterface for ${type} {
+ fn get_prototype_id(_: Option<${type}>) -> PrototypeList::id::ID {
+ PrototypeList::id::${type}
+ }
+ fn get_prototype_depth(_: Option<${type}>) -> uint {
+ ${depth}
+ }
+}
+""").substitute(replacer)
+
+
+class CGAbstractExternMethod(CGAbstractMethod):
+ """
+ Abstract base class for codegen of implementation-only (no
+ declaration) static methods.
+ """
+ def __init__(self, descriptor, name, returnType, args):
+ CGAbstractMethod.__init__(self, descriptor, name, returnType, args,
+ inline=False, extern=True)
+
+class PropertyArrays():
+ def __init__(self, descriptor):
+ self.staticMethods = MethodDefiner(descriptor, "StaticMethods",
+ static=True)
+ self.staticAttrs = AttrDefiner(descriptor, "StaticAttributes",
+ static=True)
+ self.methods = MethodDefiner(descriptor, "Methods", static=False)
+ self.attrs = AttrDefiner(descriptor, "Attributes", static=False)
+ self.consts = ConstDefiner(descriptor, "Constants")
+ pass
+
+ @staticmethod
+ def arrayNames():
+ return [ "staticMethods", "staticAttrs", "methods", "attrs", "consts" ]
+
+ def variableNames(self):
+ names = {}
+ for array in self.arrayNames():
+ names[array] = getattr(self, array).variableName()
+ return names
+ def __str__(self):
+ define = ""
+ for array in self.arrayNames():
+ define += str(getattr(self, array))
+ return define
+
+
+class CGNativeProperties(CGThing):
+ def __init__(self, descriptor, properties):
+ CGThing.__init__(self)
+ self.properties = properties
+
+ def define(self):
+ def getField(array):
+ propertyArray = getattr(self.properties, array)
+ if propertyArray.length() > 0:
+ value = "Some(%s)" % propertyArray.variableName()
+ else:
+ value = "None"
+
+ return CGGeneric(string.Template('${name}: ${value},').substitute({
+ 'name': array,
+ 'value': value,
+ }))
+
+ nativeProps = CGList([getField(array) for array in self.properties.arrayNames()], '\n')
+ return CGWrapper(CGIndenter(nativeProps),
+ pre="static sNativeProperties: NativeProperties = NativeProperties {\n",
+ post="\n};\n").define()
+
+
+class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
+ """
+ Generate the CreateInterfaceObjects method for an interface descriptor.
+
+ properties should be a PropertyArrays instance.
+ """
+ def __init__(self, descriptor, properties):
+ assert not descriptor.interface.isCallback()
+ args = [Argument('*mut JSContext', 'aCx'), Argument('*mut JSObject', 'aGlobal'),
+ Argument('*mut JSObject', 'aReceiver')]
+ CGAbstractMethod.__init__(self, descriptor, 'CreateInterfaceObjects', '*mut JSObject', args)
+ self.properties = properties
+ def definition_body(self):
+ protoChain = self.descriptor.prototypeChain
+ if len(protoChain) == 1:
+ getParentProto = "JS_GetObjectPrototype(aCx, aGlobal)"
+ else:
+ parentProtoName = self.descriptor.prototypeChain[-2]
+ getParentProto = ("%s::GetProtoObject(aCx, aGlobal, aReceiver)" %
+ toBindingNamespace(parentProtoName))
+
+ getParentProto = ("let parentProto: *mut JSObject = %s;\n"
+ "assert!(parentProto.is_not_null());\n") % getParentProto
+
+ if self.descriptor.concrete:
+ if self.descriptor.proxy:
+ domClass = "&Class"
+ else:
+ domClass = "&Class.dom_class"
+ else:
+ domClass = "ptr::null()"
+
+ if self.descriptor.interface.hasInterfaceObject():
+ if self.descriptor.interface.ctor():
+ constructHook = CONSTRUCT_HOOK_NAME
+ constructArgs = methodLength(self.descriptor.interface.ctor())
+ else:
+ constructHook = "ThrowingConstructor"
+ constructArgs = 0
+
+ constructor = 'Some((%s, "%s", %d))' % (
+ constructHook, self.descriptor.interface.identifier.name,
+ constructArgs)
+ else:
+ constructor = 'None'
+
+ call = """return CreateInterfaceObjects2(aCx, aGlobal, aReceiver, parentProto,
+ &PrototypeClass, %s,
+ %s,
+ &sNativeProperties);""" % (constructor, domClass)
+
+ return CGList([
+ CGGeneric(getParentProto),
+ CGGeneric(call % self.properties.variableNames())
+ ], "\n")
+
+class CGGetPerInterfaceObject(CGAbstractMethod):
+ """
+ A method for getting a per-interface object (a prototype object or interface
+ constructor object).
+ """
+ def __init__(self, descriptor, name, idPrefix="", pub=False):
+ args = [Argument('*mut JSContext', 'aCx'), Argument('*mut JSObject', 'aGlobal'),
+ Argument('*mut JSObject', 'aReceiver')]
+ CGAbstractMethod.__init__(self, descriptor, name,
+ '*mut JSObject', args, pub=pub)
+ self.id = idPrefix + "id::" + self.descriptor.name
+ def definition_body(self):
+ return CGGeneric("""
+
+/* aGlobal and aReceiver are usually the same, but they can be different
+ too. For example a sandbox often has an xray wrapper for a window as the
+ prototype of the sandbox's global. In that case aReceiver is the xray
+ wrapper and aGlobal is the sandbox's global.
+ */
+
+assert!(((*JS_GetClass(aGlobal)).flags & JSCLASS_DOM_GLOBAL) != 0);
+
+/* Check to see whether the interface objects are already installed */
+let protoOrIfaceArray = GetProtoOrIfaceArray(aGlobal);
+let cachedObject: *mut JSObject = *protoOrIfaceArray.offset(%s as int);
+if cachedObject.is_null() {
+ let tmp: *mut JSObject = CreateInterfaceObjects(aCx, aGlobal, aReceiver);
+ assert!(tmp.is_not_null());
+ *protoOrIfaceArray.offset(%s as int) = tmp;
+ tmp
+} else {
+ cachedObject
+}""" % (self.id, self.id))
+
+class CGGetProtoObjectMethod(CGGetPerInterfaceObject):
+ """
+ A method for getting the interface prototype object.
+ """
+ def __init__(self, descriptor):
+ CGGetPerInterfaceObject.__init__(self, descriptor, "GetProtoObject",
+ "PrototypeList::", pub=True)
+ def definition_body(self):
+ return CGList([
+ CGGeneric("""\
+/* Get the interface prototype object for this class. This will create the
+ object as needed. */"""),
+ CGGetPerInterfaceObject.definition_body(self),
+ ])
+
+class CGGetConstructorObjectMethod(CGGetPerInterfaceObject):
+ """
+ A method for getting the interface constructor object.
+ """
+ def __init__(self, descriptor):
+ CGGetPerInterfaceObject.__init__(self, descriptor, "GetConstructorObject",
+ "constructors::")
+ def definition_body(self):
+ return CGList([
+ CGGeneric("""\
+/* Get the interface object for this class. This will create the object as
+ needed. */"""),
+ CGGetPerInterfaceObject.definition_body(self),
+ ])
+
+
+class CGDefineProxyHandler(CGAbstractMethod):
+ """
+ A method to create and cache the proxy trap for a given interface.
+ """
+ def __init__(self, descriptor):
+ assert descriptor.proxy
+ CGAbstractMethod.__init__(self, descriptor, 'DefineProxyHandler', '*const libc::c_void', [], pub=True)
+
+ def define(self):
+ return CGAbstractMethod.define(self)
+
+ def definition_body(self):
+ body = """\
+let traps = ProxyTraps {
+ getPropertyDescriptor: Some(getPropertyDescriptor),
+ getOwnPropertyDescriptor: Some(getOwnPropertyDescriptor),
+ defineProperty: Some(defineProperty),
+ getOwnPropertyNames: ptr::null(),
+ delete_: Some(delete_),
+ enumerate: ptr::null(),
+
+ has: None,
+ hasOwn: Some(hasOwn),
+ get: Some(get),
+ set: None,
+ keys: ptr::null(),
+ iterate: None,
+
+ call: None,
+ construct: None,
+ nativeCall: ptr::null(),
+ hasInstance: None,
+ typeOf: None,
+ objectClassIs: None,
+ obj_toString: Some(obj_toString),
+ fun_toString: None,
+ //regexp_toShared: ptr::null(),
+ defaultValue: None,
+ iteratorNext: None,
+ finalize: Some(%s),
+ getElementIfPresent: None,
+ getPrototypeOf: None,
+ trace: Some(%s)
+};
+
+CreateProxyHandler(&traps, &Class as *const _ as *const _)
+""" % (FINALIZE_HOOK_NAME,
+ TRACE_HOOK_NAME)
+ return CGGeneric(body)
+
+
+
+class CGDefineDOMInterfaceMethod(CGAbstractMethod):
+ """
+ A method for resolve hooks to try to lazily define the interface object for
+ a given interface.
+ """
+ def __init__(self, descriptor):
+ assert descriptor.interface.hasInterfaceObject()
+ args = [
+ Argument('*mut JSContext', 'cx'),
+ Argument('*mut JSObject', 'global'),
+ ]
+ CGAbstractMethod.__init__(self, descriptor, 'DefineDOMInterface', 'void', args, pub=True)
+
+ def define(self):
+ return CGAbstractMethod.define(self)
+
+ def definition_body(self):
+ return CGGeneric("""\
+assert!(global.is_not_null());
+assert!(GetProtoObject(cx, global, global).is_not_null());""")
+
+def needCx(returnType, arguments, considerTypes):
+ return (considerTypes and
+ (typeNeedsCx(returnType, True) or
+ any(typeNeedsCx(a.type) for a in arguments)))
+
+class CGCallGenerator(CGThing):
+ """
+ A class to generate an actual call to a C++ object. Assumes that the C++
+ object is stored in a variable whose name is given by the |object| argument.
+
+ errorResult should be a string for the value to return in case of an
+ exception from the native code, or None if no error reporting is needed.
+ """
+ def __init__(self, errorResult, arguments, argsPre, returnType,
+ extendedAttributes, descriptorProvider, nativeMethodName,
+ static, object="this"):
+ CGThing.__init__(self)
+
+ assert errorResult is None or isinstance(errorResult, str)
+
+ isFallible = errorResult is not None
+
+ result = getRetvalDeclarationForType(returnType, descriptorProvider)
+ if isFallible:
+ result = CGWrapper(result, pre="Result<", post=", Error>")
+
+ args = CGList([CGGeneric(arg) for arg in argsPre], ", ")
+ for (a, name) in arguments:
+ #XXXjdm Perhaps we should pass all nontrivial types by borrowed pointer
+ if a.type.isGeckoInterface():
+ if not (a.type.nullable() or a.optional):
+ name = "&" + name
+ elif a.type.isDictionary():
+ name = "&" + name
+ args.append(CGGeneric(name))
+
+ needsCx = needCx(returnType, (a for (a, _) in arguments), True)
+
+ if not "cx" in argsPre and needsCx:
+ args.prepend(CGGeneric("cx"))
+
+ # Build up our actual call
+ self.cgRoot = CGList([], "\n")
+
+ call = CGGeneric(nativeMethodName)
+ if static:
+ call = CGWrapper(call, pre="%s::" % descriptorProvider.interface.identifier.name)
+ else:
+ call = CGWrapper(call, pre="(*%s)." % object)
+ call = CGList([call, CGWrapper(args, pre="(", post=")")])
+
+ self.cgRoot.append(CGList([
+ CGGeneric("let result: "),
+ result,
+ CGGeneric(" = "),
+ call,
+ CGGeneric(";"),
+ ]))
+
+ if isFallible:
+ if static:
+ glob = ""
+ else:
+ glob = " let global = global_object_for_js_object(this.reflector().get_jsobject());\n"\
+ " let global = global.root();\n"
+
+ self.cgRoot.append(CGGeneric(
+ "let result = match result {\n"
+ " Ok(result) => result,\n"
+ " Err(e) => {\n"
+ "%s"
+ " throw_dom_exception(cx, &global.root_ref(), e);\n"
+ " return%s;\n"
+ " },\n"
+ "};\n" % (glob, errorResult)))
+
+ if typeRetValNeedsRooting(returnType):
+ self.cgRoot.append(CGGeneric("let result = result.root();"))
+
+ def define(self):
+ return self.cgRoot.define()
+
+class MethodNotCreatorError(Exception):
+ def __init__(self, typename):
+ self.typename = typename
+
+class CGPerSignatureCall(CGThing):
+ """
+ This class handles the guts of generating code for a particular
+ call signature. A call signature consists of four things:
+
+ 1) A return type, which can be None to indicate that there is no
+ actual return value (e.g. this is an attribute setter) or an
+ IDLType if there's an IDL type involved (including |void|).
+ 2) An argument list, which is allowed to be empty.
+ 3) A name of a native method to call.
+ 4) Whether or not this method is static.
+
+ We also need to know whether this is a method or a getter/setter
+ to do error reporting correctly.
+
+ The idlNode parameter can be either a method or an attr. We can query
+ |idlNode.identifier| in both cases, so we can be agnostic between the two.
+ """
+ # XXXbz For now each entry in the argument list is either an
+ # IDLArgument or a FakeArgument, but longer-term we may want to
+ # have ways of flagging things like JSContext* or optional_argc in
+ # there.
+
+ def __init__(self, returnType, argsPre, arguments, nativeMethodName, static,
+ descriptor, idlNode, argConversionStartsAt=0,
+ getter=False, setter=False):
+ CGThing.__init__(self)
+ self.returnType = returnType
+ self.descriptor = descriptor
+ self.idlNode = idlNode
+ self.extendedAttributes = descriptor.getExtendedAttributes(idlNode,
+ getter=getter,
+ setter=setter)
+ self.argsPre = argsPre
+ self.arguments = arguments
+ self.argCount = len(arguments)
+ if self.argCount > argConversionStartsAt:
+ # Insert our argv in there
+ cgThings = [CGGeneric(self.getArgvDecl())]
+ else:
+ cgThings = []
+ cgThings.extend([CGArgumentConverter(arguments[i], i, self.getArgv(),
+ self.getArgc(), self.descriptor,
+ invalidEnumValueFatal=not setter) for
+ i in range(argConversionStartsAt, self.argCount)])
+
+ cgThings.append(CGCallGenerator(
+ ' false as JSBool' if self.isFallible() else None,
+ self.getArguments(), self.argsPre, returnType,
+ self.extendedAttributes, descriptor, nativeMethodName,
+ static))
+ self.cgRoot = CGList(cgThings, "\n")
+
+ def getArgv(self):
+ return "argv" if self.argCount > 0 else ""
+ def getArgvDecl(self):
+ return "\nlet argv = JS_ARGV(cx, vp);\n"
+ def getArgc(self):
+ return "argc"
+ def getArguments(self):
+ def process(arg, i):
+ argVal = "arg" + str(i)
+ if arg.type.isGeckoInterface() and not arg.type.unroll().inner.isCallback():
+ argVal += ".root_ref()"
+ return argVal
+ return [(a, process(a, i)) for (i, a) in enumerate(self.arguments)]
+
+ def isFallible(self):
+ return not 'infallible' in self.extendedAttributes
+
+ def wrap_return_value(self):
+ return wrapForType('*vp')
+
+ def define(self):
+ return (self.cgRoot.define() + "\n" + self.wrap_return_value())
+
+class CGSwitch(CGList):
+ """
+ A class to generate code for a switch statement.
+
+ Takes three constructor arguments: an expression, a list of cases,
+ and an optional default.
+
+ Each case is a CGCase. The default is a CGThing for the body of
+ the default case, if any.
+ """
+ def __init__(self, expression, cases, default=None):
+ CGList.__init__(self, [CGIndenter(c) for c in cases], "\n")
+ self.prepend(CGWrapper(CGGeneric(expression),
+ pre="match ", post=" {"));
+ if default is not None:
+ self.append(
+ CGIndenter(
+ CGWrapper(
+ CGIndenter(default),
+ pre="_ => {\n",
+ post="\n}"
+ )
+ )
+ )
+
+ self.append(CGGeneric("}"))
+
+class CGCase(CGList):
+ """
+ A class to generate code for a case statement.
+
+ Takes three constructor arguments: an expression, a CGThing for
+ the body (allowed to be None if there is no body), and an optional
+ argument (defaulting to False) for whether to fall through.
+ """
+ def __init__(self, expression, body, fallThrough=False):
+ CGList.__init__(self, [], "\n")
+ self.append(CGWrapper(CGGeneric(expression), post=" => {"))
+ bodyList = CGList([body], "\n")
+ if fallThrough:
+ raise TypeError("fall through required but unsupported")
+ #bodyList.append(CGGeneric('fail!("fall through unsupported"); /* Fall through */'))
+ self.append(CGIndenter(bodyList));
+ self.append(CGGeneric("}"))
+
+class CGGetterCall(CGPerSignatureCall):
+ """
+ A class to generate a native object getter call for a particular IDL
+ getter.
+ """
+ def __init__(self, argsPre, returnType, nativeMethodName, descriptor, attr):
+ CGPerSignatureCall.__init__(self, returnType, argsPre, [],
+ nativeMethodName, attr.isStatic(), descriptor,
+ attr, getter=True)
+
+class FakeArgument():
+ """
+ A class that quacks like an IDLArgument. This is used to make
+ setters look like method calls or for special operations.
+ """
+ def __init__(self, type, interfaceMember, allowTreatNonObjectAsNull=False):
+ self.type = type
+ self.optional = False
+ self.variadic = False
+ self.defaultValue = None
+ self._allowTreatNonObjectAsNull = allowTreatNonObjectAsNull
+ self.treatNullAs = interfaceMember.treatNullAs
+ self.enforceRange = False
+ self.clamp = False
+
+ def allowTreatNonCallableAsNull(self):
+ return self._allowTreatNonObjectAsNull
+
+class CGSetterCall(CGPerSignatureCall):
+ """
+ A class to generate a native object setter call for a particular IDL
+ setter.
+ """
+ def __init__(self, argsPre, argType, nativeMethodName, descriptor, attr):
+ CGPerSignatureCall.__init__(self, None, argsPre,
+ [FakeArgument(argType, attr, allowTreatNonObjectAsNull=True)],
+ nativeMethodName, attr.isStatic(), descriptor, attr,
+ setter=True)
+ def wrap_return_value(self):
+ # We have no return value
+ return "\nreturn 1;"
+ def getArgc(self):
+ return "1"
+ def getArgvDecl(self):
+ # We just get our stuff from our last arg no matter what
+ return ""
+
+class CGAbstractBindingMethod(CGAbstractExternMethod):
+ """
+ Common class to generate the JSNatives for all our methods, getters, and
+ setters. This will generate the function declaration and unwrap the
+ |this| object. Subclasses are expected to override the generate_code
+ function to do the rest of the work. This function should return a
+ CGThing which is already properly indented.
+ """
+ def __init__(self, descriptor, name, args, unwrapFailureCode=None):
+ CGAbstractExternMethod.__init__(self, descriptor, name, "JSBool", args)
+
+ if unwrapFailureCode is None:
+ self.unwrapFailureCode = (
+ 'throw_type_error(cx, "\\"this\\" object does not '
+ 'implement interface %s.");\n'
+ 'return 0;' % descriptor.interface.identifier.name)
+ else:
+ self.unwrapFailureCode = unwrapFailureCode
+
+ def definition_body(self):
+ # Our descriptor might claim that we're not castable, simply because
+ # we're someone's consequential interface. But for this-unwrapping, we
+ # know that we're the real deal. So fake a descriptor here for
+ # consumption by FailureFatalCastableObjectUnwrapper.
+ unwrapThis = str(CastableObjectUnwrapper(
+ FakeCastableDescriptor(self.descriptor),
+ "obj", self.unwrapFailureCode))
+ unwrapThis = CGGeneric(
+ "let obj: *mut JSObject = JS_THIS_OBJECT(cx, vp as *mut JSVal);\n"
+ "if obj.is_null() {\n"
+ " return false as JSBool;\n"
+ "}\n"
+ "\n"
+ "let this: JS<%s> = %s;\n" % (self.descriptor.concreteType, unwrapThis))
+ return CGList([ unwrapThis, self.generate_code() ], "\n")
+
+ def generate_code(self):
+ assert(False) # Override me
+
+
+class CGAbstractStaticBindingMethod(CGAbstractMethod):
+ """
+ Common class to generate the JSNatives for all our static methods, getters
+ and setters. This will generate the function declaration and unwrap the
+ global object. Subclasses are expected to override the generate_code
+ function to do the rest of the work. This function should return a
+ CGThing which is already properly indented.
+ """
+ def __init__(self, descriptor, name):
+ args = [
+ Argument('*mut JSContext', 'cx'),
+ Argument('libc::c_uint', 'argc'),
+ Argument('*mut JSVal', 'vp'),
+ ]
+ CGAbstractMethod.__init__(self, descriptor, name, "JSBool", args, extern=True)
+
+ def definition_body(self):
+ return self.generate_code()
+
+ def generate_code(self):
+ assert False # Override me
+
+
+class CGGenericMethod(CGAbstractBindingMethod):
+ """
+ A class for generating the C++ code for an IDL method..
+ """
+ def __init__(self, descriptor):
+ args = [Argument('*mut JSContext', 'cx'), Argument('libc::c_uint', 'argc'),
+ Argument('*mut JSVal', 'vp')]
+ CGAbstractBindingMethod.__init__(self, descriptor, 'genericMethod', args)
+
+ def generate_code(self):
+ return CGGeneric(
+ "let _info: *const JSJitInfo = RUST_FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));\n"
+ "return CallJitMethodOp(_info, cx, obj, this.unsafe_get() as *mut libc::c_void, argc, vp);")
+
+class CGSpecializedMethod(CGAbstractExternMethod):
+ """
+ A class for generating the C++ code for a specialized method that the JIT
+ can call with lower overhead.
+ """
+ def __init__(self, descriptor, method):
+ self.method = method
+ name = method.identifier.name
+ args = [Argument('*mut JSContext', 'cx'), Argument('JSHandleObject', '_obj'),
+ Argument('*const %s' % descriptor.concreteType, 'this'),
+ Argument('libc::c_uint', 'argc'), Argument('*mut JSVal', 'vp')]
+ CGAbstractExternMethod.__init__(self, descriptor, name, 'JSBool', args)
+
+ def definition_body(self):
+ nativeName = CGSpecializedMethod.makeNativeName(self.descriptor,
+ self.method)
+ return CGWrapper(CGMethodCall([], nativeName, self.method.isStatic(),
+ self.descriptor, self.method),
+ pre="let this = JS::from_raw(this);\n"
+ "let this = this.root();\n")
+
+ @staticmethod
+ def makeNativeName(descriptor, method):
+ return MakeNativeName(method.identifier.name)
+
+class CGStaticMethod(CGAbstractStaticBindingMethod):
+ """
+ A class for generating the Rust code for an IDL static method.
+ """
+ def __init__(self, descriptor, method):
+ self.method = method
+ name = method.identifier.name
+ CGAbstractStaticBindingMethod.__init__(self, descriptor, name)
+
+ def generate_code(self):
+ nativeName = CGSpecializedMethod.makeNativeName(self.descriptor,
+ self.method)
+ return CGMethodCall([], nativeName, True, self.descriptor, self.method)
+
+
+class CGGenericGetter(CGAbstractBindingMethod):
+ """
+ A class for generating the C++ code for an IDL attribute getter.
+ """
+ def __init__(self, descriptor, lenientThis=False):
+ args = [Argument('*mut JSContext', 'cx'), Argument('libc::c_uint', 'argc'),
+ Argument('*mut JSVal', 'vp')]
+ if lenientThis:
+ name = "genericLenientGetter"
+ unwrapFailureCode = (
+ "MOZ_ASSERT(!JS_IsExceptionPending(cx));\n"
+ "JS_SET_RVAL(cx, vp, JS::UndefinedValue());\n"
+ "return true;")
+ else:
+ name = "genericGetter"
+ unwrapFailureCode = None
+ CGAbstractBindingMethod.__init__(self, descriptor, name, args,
+ unwrapFailureCode)
+
+ def generate_code(self):
+ return CGGeneric(
+ "let info: *const JSJitInfo = RUST_FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));\n"
+ "return CallJitPropertyOp(info, cx, obj, this.unsafe_get() as *mut libc::c_void, vp);\n")
+
+class CGSpecializedGetter(CGAbstractExternMethod):
+ """
+ A class for generating the code for a specialized attribute getter
+ that the JIT can call with lower overhead.
+ """
+ def __init__(self, descriptor, attr):
+ self.attr = attr
+ name = 'get_' + attr.identifier.name
+ args = [ Argument('*mut JSContext', 'cx'),
+ Argument('JSHandleObject', '_obj'),
+ Argument('*const %s' % descriptor.concreteType, 'this'),
+ Argument('*mut JSVal', 'vp') ]
+ CGAbstractExternMethod.__init__(self, descriptor, name, "JSBool", args)
+
+ def definition_body(self):
+ nativeName = CGSpecializedGetter.makeNativeName(self.descriptor,
+ self.attr)
+
+ return CGWrapper(CGGetterCall([], self.attr.type, nativeName,
+ self.descriptor, self.attr),
+ pre="let this = JS::from_raw(this);\n"
+ "let this = this.root();\n")
+
+ @staticmethod
+ def makeNativeName(descriptor, attr):
+ nativeName = MakeNativeName(attr.identifier.name)
+ infallible = ('infallible' in
+ descriptor.getExtendedAttributes(attr, getter=True))
+ if attr.type.nullable() or not infallible:
+ return "Get" + nativeName
+
+ return nativeName
+
+
+class CGStaticGetter(CGAbstractStaticBindingMethod):
+ """
+ A class for generating the C++ code for an IDL static attribute getter.
+ """
+ def __init__(self, descriptor, attr):
+ self.attr = attr
+ name = 'get_' + attr.identifier.name
+ CGAbstractStaticBindingMethod.__init__(self, descriptor, name)
+
+ def generate_code(self):
+ nativeName = CGSpecializedGetter.makeNativeName(self.descriptor,
+ self.attr)
+ return CGGetterCall([], self.attr.type, nativeName, self.descriptor,
+ self.attr)
+
+
+class CGGenericSetter(CGAbstractBindingMethod):
+ """
+ A class for generating the Rust code for an IDL attribute setter.
+ """
+ def __init__(self, descriptor, lenientThis=False):
+ args = [Argument('*mut JSContext', 'cx'), Argument('libc::c_uint', 'argc'),
+ Argument('*mut JSVal', 'vp')]
+ if lenientThis:
+ name = "genericLenientSetter"
+ unwrapFailureCode = (
+ "MOZ_ASSERT(!JS_IsExceptionPending(cx));\n"
+ "return true;")
+ else:
+ name = "genericSetter"
+ unwrapFailureCode = None
+ CGAbstractBindingMethod.__init__(self, descriptor, name, args,
+ unwrapFailureCode)
+
+ def generate_code(self):
+ return CGGeneric(
+ "let mut undef = UndefinedValue();\n"
+ "let argv: *mut JSVal = if argc != 0 { JS_ARGV(cx, vp) } else { &mut undef as *mut JSVal };\n"
+ "let info: *const JSJitInfo = RUST_FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));\n"
+ "if CallJitPropertyOp(info, cx, obj, this.unsafe_get() as *mut libc::c_void, argv) == 0 {\n"
+ " return 0;\n"
+ "}\n"
+ "*vp = UndefinedValue();\n"
+ "return 1;")
+
+class CGSpecializedSetter(CGAbstractExternMethod):
+ """
+ A class for generating the code for a specialized attribute setter
+ that the JIT can call with lower overhead.
+ """
+ def __init__(self, descriptor, attr):
+ self.attr = attr
+ name = 'set_' + attr.identifier.name
+ args = [ Argument('*mut JSContext', 'cx'),
+ Argument('JSHandleObject', '_obj'),
+ Argument('*const %s' % descriptor.concreteType, 'this'),
+ Argument('*mut JSVal', 'argv')]
+ CGAbstractExternMethod.__init__(self, descriptor, name, "JSBool", args)
+
+ def definition_body(self):
+ nativeName = CGSpecializedSetter.makeNativeName(self.descriptor,
+ self.attr)
+ return CGWrapper(CGSetterCall([], self.attr.type, nativeName,
+ self.descriptor, self.attr),
+ pre="let this = JS::from_raw(this);\n"
+ "let this = this.root();\n")
+
+ @staticmethod
+ def makeNativeName(descriptor, attr):
+ return "Set" + MakeNativeName(attr.identifier.name)
+
+
+class CGStaticSetter(CGAbstractStaticBindingMethod):
+ """
+ A class for generating the C++ code for an IDL static attribute setter.
+ """
+ def __init__(self, descriptor, attr):
+ self.attr = attr
+ name = 'set_' + attr.identifier.name
+ CGAbstractStaticBindingMethod.__init__(self, descriptor, name)
+
+ def generate_code(self):
+ nativeName = CGSpecializedSetter.makeNativeName(self.descriptor,
+ self.attr)
+ checkForArg = CGGeneric(
+ "let argv = JS_ARGV(cx, vp);\n"
+ "if (argc == 0) {\n"
+ " throw_type_error(cx, \"Not enough arguments to %s setter.\");\n"
+ " return 0;\n"
+ "}\n" % self.attr.identifier.name)
+ call = CGSetterCall([], self.attr.type, nativeName, self.descriptor,
+ self.attr)
+ return CGList([checkForArg, call])
+
+
+class CGMemberJITInfo(CGThing):
+ """
+ A class for generating the JITInfo for a property that points to
+ our specialized getter and setter.
+ """
+ def __init__(self, descriptor, member):
+ self.member = member
+ self.descriptor = descriptor
+
+ def defineJitInfo(self, infoName, opName, infallible):
+ protoID = "PrototypeList::id::%s as u32" % self.descriptor.name
+ depth = self.descriptor.interface.inheritanceDepth()
+ failstr = "true" if infallible else "false"
+ return ("\n"
+ "static %s: JSJitInfo = JSJitInfo {\n"
+ " op: %s as *const u8,\n"
+ " protoID: %s,\n"
+ " depth: %s,\n"
+ " isInfallible: %s, /* False in setters. */\n"
+ " isConstant: false /* Only relevant for getters. */\n"
+ "};\n" % (infoName, opName, protoID, depth, failstr))
+
+ def define(self):
+ if self.member.isAttr():
+ getterinfo = ("%s_getterinfo" % self.member.identifier.name)
+ getter = ("get_%s" % self.member.identifier.name)
+ getterinfal = "infallible" in self.descriptor.getExtendedAttributes(self.member, getter=True)
+ result = self.defineJitInfo(getterinfo, getter, getterinfal)
+ if not self.member.readonly:
+ setterinfo = ("%s_setterinfo" % self.member.identifier.name)
+ setter = ("set_%s" % self.member.identifier.name)
+ # Setters are always fallible, since they have to do a typed unwrap.
+ result += self.defineJitInfo(setterinfo, setter, False)
+ return result
+ if self.member.isMethod():
+ methodinfo = ("%s_methodinfo" % self.member.identifier.name)
+ # Actually a JSJitMethodOp, but JSJitPropertyOp by struct definition.
+ method = ("%s" % self.member.identifier.name)
+
+ # Methods are infallible if they are infallible, have no arguments
+ # to unwrap, and have a return type that's infallible to wrap up for
+ # return.
+ methodInfal = False
+ sigs = self.member.signatures()
+ if len(sigs) == 1:
+ # Don't handle overloading. If there's more than one signature,
+ # one of them must take arguments.
+ sig = sigs[0]
+ if len(sig[1]) == 0:
+ # No arguments and infallible return boxing
+ methodInfal = True
+
+ result = self.defineJitInfo(methodinfo, method, methodInfal)
+ return result
+ raise TypeError("Illegal member type to CGPropertyJITInfo")
+
+def getEnumValueName(value):
+ # Some enum values can be empty strings. Others might have weird
+ # characters in them. Deal with the former by returning "_empty",
+ # deal with possible name collisions from that by throwing if the
+ # enum value is actually "_empty", and throw on any value
+ # containing non-ASCII chars for now. Replace all chars other than
+ # [0-9A-Za-z_] with '_'.
+ if re.match("[^\x20-\x7E]", value):
+ raise SyntaxError('Enum value "' + value + '" contains non-ASCII characters')
+ if re.match("^[0-9]", value):
+ raise SyntaxError('Enum value "' + value + '" starts with a digit')
+ value = re.sub(r'[^0-9A-Za-z_]', '_', value)
+ if re.match("^_[A-Z]|__", value):
+ raise SyntaxError('Enum value "' + value + '" is reserved by the C++ spec')
+ if value == "_empty":
+ raise SyntaxError('"_empty" is not an IDL enum value we support yet')
+ if value == "":
+ return "_empty"
+ return MakeNativeName(value)
+
+class CGEnum(CGThing):
+ def __init__(self, enum):
+ CGThing.__init__(self)
+ inner = """
+use dom::bindings::conversions::ToJSValConvertible;
+use js::jsapi::JSContext;
+use js::jsval::JSVal;
+
+#[repr(uint)]
+#[deriving(Encodable, PartialEq)]
+pub enum valuelist {
+ %s
+}
+
+pub static strings: &'static [&'static str] = &[
+ %s,
+];
+
+impl ToJSValConvertible for valuelist {
+ fn to_jsval(&self, cx: *mut JSContext) -> JSVal {
+ strings[*self as uint].to_string().to_jsval(cx)
+ }
+}
+""" % (",\n ".join(map(getEnumValueName, enum.values())),
+ ",\n ".join(['"%s"' % val for val in enum.values()]))
+
+ self.cgRoot = CGList([
+ CGNamespace.build([enum.identifier.name + "Values"],
+ CGIndenter(CGGeneric(inner)), public=True),
+ CGGeneric("pub type %s = self::%sValues::valuelist;\n" %
+ (enum.identifier.name, enum.identifier.name)),
+ ])
+
+ def define(self):
+ return self.cgRoot.define()
+
+
+def convertConstIDLValueToRust(value):
+ tag = value.type.tag()
+ if tag in [IDLType.Tags.int8, IDLType.Tags.uint8,
+ IDLType.Tags.int16, IDLType.Tags.uint16,
+ IDLType.Tags.int32, IDLType.Tags.uint32,
+ IDLType.Tags.int64, IDLType.Tags.uint64,
+ IDLType.Tags.float, IDLType.Tags.double]:
+ return str(value.value)
+
+ if tag == IDLType.Tags.bool:
+ return toStringBool(value.value)
+
+ raise TypeError("Const value of unhandled type: " + value.type)
+
+class CGConstant(CGThing):
+ def __init__(self, constants):
+ CGThing.__init__(self)
+ self.constants = constants
+
+ def define(self):
+ def stringDecl(const):
+ name = const.identifier.name
+ value = convertConstIDLValueToRust(const.value)
+ return CGGeneric("pub static %s: %s = %s;\n" % (name, builtinNames[const.value.type.tag()], value))
+
+ return CGIndenter(CGList(stringDecl(m) for m in self.constants)).define()
+
+def getUnionTypeTemplateVars(type, descriptorProvider):
+ # For dictionaries and sequences we need to pass None as the failureCode
+ # for getJSToNativeConversionTemplate.
+ # Also, for dictionaries we would need to handle conversion of
+ # null/undefined to the dictionary correctly.
+ if type.isDictionary() or type.isSequence():
+ raise TypeError("Can't handle dictionaries or sequences in unions")
+
+ if type.isGeckoInterface():
+ name = type.inner.identifier.name
+ typeName = descriptorProvider.getDescriptor(name).nativeType
+ elif type.isEnum():
+ name = type.inner.identifier.name
+ typeName = name
+ elif type.isArray() or type.isSequence():
+ name = str(type)
+ #XXXjdm dunno about typeName here
+ typeName = "/*" + type.name + "*/"
+ elif type.isDOMString():
+ name = type.name
+ typeName = "DOMString"
+ elif type.isPrimitive():
+ name = type.name
+ typeName = builtinNames[type.tag()]
+ else:
+ name = type.name
+ typeName = "/*" + type.name + "*/"
+
+ template, _, _, _ = getJSToNativeConversionTemplate(
+ type, descriptorProvider, failureCode="return Ok(None);",
+ exceptionCode='return Err(());',
+ isDefinitelyObject=True)
+
+ assert not type.isObject()
+ jsConversion = string.Template(template).substitute({
+ "val": "value",
+ })
+ jsConversion = CGWrapper(CGGeneric(jsConversion), pre="Ok(Some(", post="))")
+
+ return {
+ "name": name,
+ "typeName": typeName,
+ "jsConversion": jsConversion,
+ }
+
+class CGUnionStruct(CGThing):
+ def __init__(self, type, descriptorProvider):
+ assert not type.nullable()
+ assert not type.hasNullableType
+
+ CGThing.__init__(self)
+ self.type = type
+ self.descriptorProvider = descriptorProvider
+
+ def define(self):
+ templateVars = map(lambda t: getUnionTypeTemplateVars(t, self.descriptorProvider),
+ self.type.flatMemberTypes)
+ enumValues = [
+ " e%s(%s)," % (v["name"], v["typeName"]) for v in templateVars
+ ]
+ enumConversions = [
+ " e%s(ref inner) => inner.to_jsval(cx)," % v["name"] for v in templateVars
+ ]
+ return ("""pub enum %s {
+%s
+}
+
+impl ToJSValConvertible for %s {
+ fn to_jsval(&self, cx: *mut JSContext) -> JSVal {
+ match *self {
+%s
+ }
+ }
+}
+""") % (self.type, "\n".join(enumValues),
+ self.type, "\n".join(enumConversions))
+
+
+class CGUnionConversionStruct(CGThing):
+ def __init__(self, type, descriptorProvider):
+ assert not type.nullable()
+ assert not type.hasNullableType
+
+ CGThing.__init__(self)
+ self.type = type
+ self.descriptorProvider = descriptorProvider
+
+ def from_jsval(self):
+ memberTypes = self.type.flatMemberTypes
+ names = []
+ conversions = []
+
+ interfaceMemberTypes = filter(lambda t: t.isNonCallbackInterface(), memberTypes)
+ if len(interfaceMemberTypes) > 0:
+ def get_name(memberType):
+ if self.type.isGeckoInterface():
+ return memberType.inner.identifier.name
+
+ return memberType.name
+
+ def get_match(name):
+ return (
+ "match %s::TryConvertTo%s(cx, value) {\n"
+ " Err(_) => return Err(()),\n"
+ " Ok(Some(value)) => return Ok(e%s(value)),\n"
+ " Ok(None) => (),\n"
+ "}\n") % (self.type, name, name)
+
+ typeNames = [get_name(memberType) for memberType in interfaceMemberTypes]
+ interfaceObject = CGList(CGGeneric(get_match(typeName)) for typeName in typeNames)
+ names.extend(typeNames)
+ else:
+ interfaceObject = None
+
+ arrayObjectMemberTypes = filter(lambda t: t.isArray() or t.isSequence(), memberTypes)
+ if len(arrayObjectMemberTypes) > 0:
+ assert len(arrayObjectMemberTypes) == 1
+ raise TypeError("Can't handle arrays or sequences in unions.")
+ else:
+ arrayObject = None
+
+ dateObjectMemberTypes = filter(lambda t: t.isDate(), memberTypes)
+ if len(dateObjectMemberTypes) > 0:
+ assert len(dateObjectMemberTypes) == 1
+ raise TypeError("Can't handle dates in unions.")
+ else:
+ dateObject = None
+
+ callbackMemberTypes = filter(lambda t: t.isCallback() or t.isCallbackInterface(), memberTypes)
+ if len(callbackMemberTypes) > 0:
+ assert len(callbackMemberTypes) == 1
+ raise TypeError("Can't handle callbacks in unions.")
+ else:
+ callbackObject = None
+
+ dictionaryMemberTypes = filter(lambda t: t.isDictionary(), memberTypes)
+ if len(dictionaryMemberTypes) > 0:
+ raise TypeError("No support for unwrapping dictionaries as member "
+ "of a union")
+ else:
+ dictionaryObject = None
+
+ if callbackObject or dictionaryObject:
+ assert False, "Not currently supported"
+ else:
+ nonPlatformObject = None
+
+ objectMemberTypes = filter(lambda t: t.isObject(), memberTypes)
+ if len(objectMemberTypes) > 0:
+ raise TypeError("Can't handle objects in unions.")
+ else:
+ object = None
+
+ hasObjectTypes = interfaceObject or arrayObject or dateObject or nonPlatformObject or object
+ if hasObjectTypes:
+ assert interfaceObject
+ templateBody = CGList([interfaceObject], "\n")
+ conversions.append(CGIfWrapper(templateBody, "value.is_object()"))
+
+ otherMemberTypes = [
+ t for t in memberTypes if t.isPrimitive() or t.isString() or t.isEnum()
+ ]
+ if len(otherMemberTypes) > 0:
+ assert len(otherMemberTypes) == 1
+ memberType = otherMemberTypes[0]
+ if memberType.isEnum():
+ name = memberType.inner.identifier.name
+ else:
+ name = memberType.name
+ match = (
+ "match %s::TryConvertTo%s(cx, value) {\n"
+ " Err(_) => return Err(()),\n"
+ " Ok(Some(value)) => return Ok(e%s(value)),\n"
+ " Ok(None) => (),\n"
+ "}\n") % (self.type, name, name)
+ conversions.append(CGGeneric(match))
+ names.append(name)
+
+ conversions.append(CGGeneric(
+ "throw_not_in_union(cx, \"%s\");\n"
+ "Err(())" % ", ".join(names)))
+ method = CGWrapper(
+ CGIndenter(CGList(conversions, "\n\n")),
+ pre="fn from_jsval(cx: *mut JSContext, value: JSVal, _option: ()) -> Result<%s, ()> {\n" % self.type,
+ post="\n}")
+ return CGWrapper(
+ CGIndenter(method),
+ pre="impl FromJSValConvertible<()> for %s {\n" % self.type,
+ post="\n}")
+
+ def try_method(self, t):
+ templateVars = getUnionTypeTemplateVars(t, self.descriptorProvider)
+ returnType = "Result<Option<%s>, ()>" % templateVars["typeName"]
+ jsConversion = templateVars["jsConversion"]
+
+ return CGWrapper(
+ CGIndenter(jsConversion, 4),
+ pre="fn TryConvertTo%s(cx: *mut JSContext, value: JSVal) -> %s {\n" % (t.name, returnType),
+ post="\n}")
+
+ def define(self):
+ from_jsval = self.from_jsval()
+ methods = CGIndenter(CGList([
+ self.try_method(t) for t in self.type.flatMemberTypes
+ ], "\n\n"))
+ return """
+%s
+
+impl %s {
+%s
+}
+""" % (from_jsval.define(), self.type, methods.define())
+
+
+class ClassItem:
+ """ Use with CGClass """
+ def __init__(self, name, visibility):
+ self.name = name
+ self.visibility = visibility
+ def declare(self, cgClass):
+ assert False
+ def define(self, cgClass):
+ assert False
+
+class ClassBase(ClassItem):
+ def __init__(self, name, visibility='pub'):
+ ClassItem.__init__(self, name, visibility)
+ def declare(self, cgClass):
+ return '%s %s' % (self.visibility, self.name)
+ def define(self, cgClass):
+ # Only in the header
+ return ''
+
+class ClassMethod(ClassItem):
+ def __init__(self, name, returnType, args, inline=False, static=False,
+ virtual=False, const=False, bodyInHeader=False,
+ templateArgs=None, visibility='public', body=None,
+ breakAfterReturnDecl="\n",
+ breakAfterSelf="\n", override=False):
+ """
+ override indicates whether to flag the method as MOZ_OVERRIDE
+ """
+ assert not override or virtual
+ self.returnType = returnType
+ self.args = args
+ self.inline = False
+ self.static = static
+ self.virtual = virtual
+ self.const = const
+ self.bodyInHeader = True
+ self.templateArgs = templateArgs
+ self.body = body
+ self.breakAfterReturnDecl = breakAfterReturnDecl
+ self.breakAfterSelf = breakAfterSelf
+ self.override = override
+ ClassItem.__init__(self, name, visibility)
+
+ def getDecorators(self, declaring):
+ decorators = []
+ if self.inline:
+ decorators.append('inline')
+ if declaring:
+ if self.static:
+ decorators.append('static')
+ if self.virtual:
+ decorators.append('virtual')
+ if decorators:
+ return ' '.join(decorators) + ' '
+ return ''
+
+ def getBody(self):
+ # Override me or pass a string to constructor
+ assert self.body is not None
+ return self.body
+
+ def declare(self, cgClass):
+ templateClause = '<%s>' % ', '.join(self.templateArgs) \
+ if self.bodyInHeader and self.templateArgs else ''
+ args = ', '.join([a.declare() for a in self.args])
+ if self.bodyInHeader:
+ body = CGIndenter(CGGeneric(self.getBody())).define()
+ body = ' {\n' + body + '\n}'
+ else:
+ body = ';'
+
+ return string.Template("${decorators}%s"
+ "${visibility}fn ${name}${templateClause}(${args})${returnType}${const}${override}${body}%s" %
+ (self.breakAfterReturnDecl, self.breakAfterSelf)
+ ).substitute({
+ 'templateClause': templateClause,
+ 'decorators': self.getDecorators(True),
+ 'returnType': (" -> %s" % self.returnType) if self.returnType else "",
+ 'name': self.name,
+ 'const': ' const' if self.const else '',
+ 'override': ' MOZ_OVERRIDE' if self.override else '',
+ 'args': args,
+ 'body': body,
+ 'visibility': self.visibility + ' ' if self.visibility is not 'priv' else ''
+ })
+
+ def define(self, cgClass):
+ pass
+
+class ClassUsingDeclaration(ClassItem):
+ """"
+ Used for importing a name from a base class into a CGClass
+
+ baseClass is the name of the base class to import the name from
+
+ name is the name to import
+
+ visibility determines the visibility of the name (public,
+ protected, private), defaults to public.
+ """
+ def __init__(self, baseClass, name, visibility='public'):
+ self.baseClass = baseClass
+ ClassItem.__init__(self, name, visibility)
+
+ def declare(self, cgClass):
+ return string.Template("""using ${baseClass}::${name};
+""").substitute({ 'baseClass': self.baseClass,
+ 'name': self.name })
+
+ def define(self, cgClass):
+ return ''
+
+class ClassConstructor(ClassItem):
+ """
+ Used for adding a constructor to a CGClass.
+
+ args is a list of Argument objects that are the arguments taken by the
+ constructor.
+
+ inline should be True if the constructor should be marked inline.
+
+ bodyInHeader should be True if the body should be placed in the class
+ declaration in the header.
+
+ visibility determines the visibility of the constructor (public,
+ protected, private), defaults to private.
+
+ explicit should be True if the constructor should be marked explicit.
+
+ baseConstructors is a list of strings containing calls to base constructors,
+ defaults to None.
+
+ body contains a string with the code for the constructor, defaults to empty.
+ """
+ def __init__(self, args, inline=False, bodyInHeader=False,
+ visibility="priv", explicit=False, baseConstructors=None,
+ body=""):
+ self.args = args
+ self.inline = False
+ self.bodyInHeader = bodyInHeader
+ self.explicit = explicit
+ self.baseConstructors = baseConstructors or []
+ self.body = body
+ ClassItem.__init__(self, None, visibility)
+
+ def getDecorators(self, declaring):
+ decorators = []
+ if self.explicit:
+ decorators.append('explicit')
+ if self.inline and declaring:
+ decorators.append('inline')
+ if decorators:
+ return ' '.join(decorators) + ' '
+ return ''
+
+ def getInitializationList(self, cgClass):
+ items = [str(c) for c in self.baseConstructors]
+ for m in cgClass.members:
+ if not m.static:
+ initialize = m.body
+ if initialize:
+ items.append(m.name + "(" + initialize + ")")
+
+ if len(items) > 0:
+ return '\n : ' + ',\n '.join(items)
+ return ''
+
+ def getBody(self, cgClass):
+ initializers = [" parent: %s" % str(self.baseConstructors[0])]
+ return (self.body + (
+ "%s {\n"
+ "%s\n"
+ "}") % (cgClass.name, '\n'.join(initializers)))
+
+ def declare(self, cgClass):
+ args = ', '.join([a.declare() for a in self.args])
+ body = ' ' + self.getBody(cgClass);
+ body = stripTrailingWhitespace(body.replace('\n', '\n '))
+ if len(body) > 0:
+ body += '\n'
+ body = ' {\n' + body + '}'
+
+ return string.Template("""pub fn ${decorators}new(${args}) -> ${className}${body}
+""").substitute({ 'decorators': self.getDecorators(True),
+ 'className': cgClass.getNameString(),
+ 'args': args,
+ 'body': body })
+
+ def define(self, cgClass):
+ if self.bodyInHeader:
+ return ''
+
+ args = ', '.join([a.define() for a in self.args])
+
+ body = ' ' + self.getBody()
+ body = '\n' + stripTrailingWhitespace(body.replace('\n', '\n '))
+ if len(body) > 0:
+ body += '\n'
+
+ return string.Template("""${decorators}
+${className}::${className}(${args})${initializationList}
+{${body}}
+""").substitute({ 'decorators': self.getDecorators(False),
+ 'className': cgClass.getNameString(),
+ 'args': args,
+ 'initializationList': self.getInitializationList(cgClass),
+ 'body': body })
+
+class ClassDestructor(ClassItem):
+ """
+ Used for adding a destructor to a CGClass.
+
+ inline should be True if the destructor should be marked inline.
+
+ bodyInHeader should be True if the body should be placed in the class
+ declaration in the header.
+
+ visibility determines the visibility of the destructor (public,
+ protected, private), defaults to private.
+
+ body contains a string with the code for the destructor, defaults to empty.
+
+ virtual determines whether the destructor is virtual, defaults to False.
+ """
+ def __init__(self, inline=False, bodyInHeader=False,
+ visibility="private", body='', virtual=False):
+ self.inline = inline or bodyInHeader
+ self.bodyInHeader = bodyInHeader
+ self.body = body
+ self.virtual = virtual
+ ClassItem.__init__(self, None, visibility)
+
+ def getDecorators(self, declaring):
+ decorators = []
+ if self.virtual and declaring:
+ decorators.append('virtual')
+ if self.inline and declaring:
+ decorators.append('inline')
+ if decorators:
+ return ' '.join(decorators) + ' '
+ return ''
+
+ def getBody(self):
+ return self.body
+
+ def declare(self, cgClass):
+ if self.bodyInHeader:
+ body = ' ' + self.getBody();
+ body = stripTrailingWhitespace(body.replace('\n', '\n '))
+ if len(body) > 0:
+ body += '\n'
+ body = '\n{\n' + body + '}'
+ else:
+ body = ';'
+
+ return string.Template("""${decorators}~${className}()${body}
+""").substitute({ 'decorators': self.getDecorators(True),
+ 'className': cgClass.getNameString(),
+ 'body': body })
+
+ def define(self, cgClass):
+ if self.bodyInHeader:
+ return ''
+
+ body = ' ' + self.getBody()
+ body = '\n' + stripTrailingWhitespace(body.replace('\n', '\n '))
+ if len(body) > 0:
+ body += '\n'
+
+ return string.Template("""${decorators}
+${className}::~${className}()
+{${body}}
+""").substitute({ 'decorators': self.getDecorators(False),
+ 'className': cgClass.getNameString(),
+ 'body': body })
+
+class ClassMember(ClassItem):
+ def __init__(self, name, type, visibility="priv", static=False,
+ body=None):
+ self.type = type;
+ self.static = static
+ self.body = body
+ ClassItem.__init__(self, name, visibility)
+
+ def declare(self, cgClass):
+ return '%s %s: %s,\n' % (self.visibility, self.name, self.type)
+
+ def define(self, cgClass):
+ if not self.static:
+ return ''
+ if self.body:
+ body = " = " + self.body
+ else:
+ body = ""
+ return '%s %s::%s%s;\n' % (self.type, cgClass.getNameString(),
+ self.name, body)
+
+class ClassTypedef(ClassItem):
+ def __init__(self, name, type, visibility="public"):
+ self.type = type
+ ClassItem.__init__(self, name, visibility)
+
+ def declare(self, cgClass):
+ return 'typedef %s %s;\n' % (self.type, self.name)
+
+ def define(self, cgClass):
+ # Only goes in the header
+ return ''
+
+class ClassEnum(ClassItem):
+ def __init__(self, name, entries, values=None, visibility="public"):
+ self.entries = entries
+ self.values = values
+ ClassItem.__init__(self, name, visibility)
+
+ def declare(self, cgClass):
+ entries = []
+ for i in range(0, len(self.entries)):
+ if not self.values or i >= len(self.values):
+ entry = '%s' % self.entries[i]
+ else:
+ entry = '%s = %s' % (self.entries[i], self.values[i])
+ entries.append(entry)
+ name = '' if not self.name else ' ' + self.name
+ return 'enum%s\n{\n %s\n};\n' % (name, ',\n '.join(entries))
+
+ def define(self, cgClass):
+ # Only goes in the header
+ return ''
+
+class ClassUnion(ClassItem):
+ def __init__(self, name, entries, visibility="public"):
+ self.entries = [entry + ";" for entry in entries]
+ ClassItem.__init__(self, name, visibility)
+
+ def declare(self, cgClass):
+ return 'union %s\n{\n %s\n};\n' % (self.name, '\n '.join(self.entries))
+
+ def define(self, cgClass):
+ # Only goes in the header
+ return ''
+
+class CGClass(CGThing):
+ def __init__(self, name, bases=[], members=[], constructors=[],
+ destructor=None, methods=[],
+ typedefs = [], enums=[], unions=[], templateArgs=[],
+ templateSpecialization=[], isStruct=False,
+ disallowCopyConstruction=False, indent='',
+ decorators='',
+ extradeclarations='',
+ extradefinitions=''):
+ CGThing.__init__(self)
+ self.name = name
+ self.bases = bases
+ self.members = members
+ self.constructors = constructors
+ # We store our single destructor in a list, since all of our
+ # code wants lists of members.
+ self.destructors = [destructor] if destructor else []
+ self.methods = methods
+ self.typedefs = typedefs
+ self.enums = enums
+ self.unions = unions
+ self.templateArgs = templateArgs
+ self.templateSpecialization = templateSpecialization
+ self.isStruct = isStruct
+ self.disallowCopyConstruction = disallowCopyConstruction
+ self.indent = indent
+ self.decorators = decorators
+ self.extradeclarations = extradeclarations
+ self.extradefinitions = extradefinitions
+
+ def getNameString(self):
+ className = self.name
+ if self.templateSpecialization:
+ className = className + \
+ '<%s>' % ', '.join([str(a) for a
+ in self.templateSpecialization])
+ return className
+
+ def define(self):
+ result = ''
+ if self.templateArgs:
+ templateArgs = [a.declare() for a in self.templateArgs]
+ templateArgs = templateArgs[len(self.templateSpecialization):]
+ result = result + self.indent + 'template <%s>\n' \
+ % ','.join([str(a) for a in templateArgs])
+
+ if self.templateSpecialization:
+ specialization = \
+ '<%s>' % ', '.join([str(a) for a in self.templateSpecialization])
+ else:
+ specialization = ''
+
+ myself = ''
+ if self.decorators != '':
+ myself += self.decorators + '\n'
+ myself += '%spub struct %s%s' % (self.indent, self.name, specialization)
+ result += myself
+
+ assert len(self.bases) == 1 #XXjdm Can we support multiple inheritance?
+
+ result += '{\n%s\n' % self.indent
+
+ if self.bases:
+ self.members = [ClassMember("parent", self.bases[0].name, "pub")] + self.members
+
+ result += CGIndenter(CGGeneric(self.extradeclarations),
+ len(self.indent)).define()
+
+ def declareMembers(cgClass, memberList):
+ result = ''
+
+ for member in memberList:
+ declaration = member.declare(cgClass)
+ declaration = CGIndenter(CGGeneric(declaration)).define()
+ result = result + declaration
+ return result
+
+ if self.disallowCopyConstruction:
+ class DisallowedCopyConstructor(object):
+ def __init__(self):
+ self.visibility = "private"
+ def declare(self, cgClass):
+ name = cgClass.getNameString()
+ return ("%s(const %s&) MOZ_DELETE;\n"
+ "void operator=(const %s) MOZ_DELETE;\n" % (name, name, name))
+ disallowedCopyConstructors = [DisallowedCopyConstructor()]
+ else:
+ disallowedCopyConstructors = []
+
+ order = [(self.enums, ''), (self.unions, ''),
+ (self.typedefs, ''), (self.members, '')]
+
+ for (memberList, separator) in order:
+ memberString = declareMembers(self, memberList)
+ if self.indent:
+ memberString = CGIndenter(CGGeneric(memberString),
+ len(self.indent)).define()
+ result = result + memberString
+
+ result += self.indent + '}\n\n'
+ result += 'impl %s {\n' % self.name
+
+ order = [(self.constructors + disallowedCopyConstructors, '\n'),
+ (self.destructors, '\n'), (self.methods, '\n)')]
+ for (memberList, separator) in order:
+ memberString = declareMembers(self, memberList)
+ if self.indent:
+ memberString = CGIndenter(CGGeneric(memberString),
+ len(self.indent)).define()
+ result = result + memberString
+
+ result += "}"
+ return result
+
+class CGProxySpecialOperation(CGPerSignatureCall):
+ """
+ Base class for classes for calling an indexed or named special operation
+ (don't use this directly, use the derived classes below).
+ """
+ def __init__(self, descriptor, operation):
+ nativeName = MakeNativeName(operation)
+ operation = descriptor.operations[operation]
+ assert len(operation.signatures()) == 1
+ signature = operation.signatures()[0]
+
+ (returnType, arguments) = signature
+
+ # We pass len(arguments) as the final argument so that the
+ # CGPerSignatureCall won't do any argument conversion of its own.
+ CGPerSignatureCall.__init__(self, returnType, "", arguments, nativeName,
+ False, descriptor, operation,
+ len(arguments))
+
+ if operation.isSetter() or operation.isCreator():
+ # arguments[0] is the index or name of the item that we're setting.
+ argument = arguments[1]
+ template, _, declType, needsRooting = getJSToNativeConversionTemplate(
+ argument.type, descriptor, treatNullAs=argument.treatNullAs)
+ templateValues = {
+ "val": "(*desc).value",
+ }
+ self.cgRoot.prepend(instantiateJSToNativeConversionTemplate(
+ template, templateValues, declType, argument.identifier.name,
+ needsRooting))
+ elif operation.isGetter():
+ self.cgRoot.prepend(CGGeneric("let mut found = false;"))
+
+ def getArguments(self):
+ def process(arg):
+ argVal = arg.identifier.name
+ if arg.type.isGeckoInterface() and not arg.type.unroll().inner.isCallback():
+ argVal += ".root_ref()"
+ return argVal
+ args = [(a, process(a)) for a in self.arguments]
+ if self.idlNode.isGetter():
+ args.append((FakeArgument(BuiltinTypes[IDLBuiltinType.Types.boolean],
+ self.idlNode),
+ "&mut found"))
+ return args
+
+ def wrap_return_value(self):
+ if not self.idlNode.isGetter() or self.templateValues is None:
+ return ""
+
+ wrap = CGGeneric(wrapForType(**self.templateValues))
+ wrap = CGIfWrapper(wrap, "found")
+ return "\n" + wrap.define()
+
+class CGProxyIndexedGetter(CGProxySpecialOperation):
+ """
+ Class to generate a call to an indexed getter. If templateValues is not None
+ the returned value will be wrapped with wrapForType using templateValues.
+ """
+ def __init__(self, descriptor, templateValues=None):
+ self.templateValues = templateValues
+ CGProxySpecialOperation.__init__(self, descriptor, 'IndexedGetter')
+
+class CGProxyIndexedSetter(CGProxySpecialOperation):
+ """
+ Class to generate a call to an indexed setter.
+ """
+ def __init__(self, descriptor):
+ CGProxySpecialOperation.__init__(self, descriptor, 'IndexedSetter')
+
+class CGProxyNamedGetter(CGProxySpecialOperation):
+ """
+ Class to generate a call to an named getter. If templateValues is not None
+ the returned value will be wrapped with wrapForType using templateValues.
+ """
+ def __init__(self, descriptor, templateValues=None):
+ self.templateValues = templateValues
+ CGProxySpecialOperation.__init__(self, descriptor, 'NamedGetter')
+
+class CGProxyNamedSetter(CGProxySpecialOperation):
+ """
+ Class to generate a call to a named setter.
+ """
+ def __init__(self, descriptor):
+ CGProxySpecialOperation.__init__(self, descriptor, 'NamedSetter')
+
+class CGProxyUnwrap(CGAbstractMethod):
+ def __init__(self, descriptor):
+ args = [Argument('*mut JSObject', 'obj')]
+ CGAbstractMethod.__init__(self, descriptor, "UnwrapProxy", '*const ' + descriptor.concreteType, args, alwaysInline=True)
+
+ def definition_body(self):
+ return CGGeneric("""/*if (xpc::WrapperFactory::IsXrayWrapper(obj)) {
+ obj = js::UnwrapObject(obj);
+}*/
+//MOZ_ASSERT(IsProxy(obj));
+let box_ = GetProxyPrivate(obj).to_private() as *const %s;
+return box_;""" % self.descriptor.concreteType)
+
+class CGDOMJSProxyHandler_getOwnPropertyDescriptor(CGAbstractExternMethod):
+ def __init__(self, descriptor):
+ args = [Argument('*mut JSContext', 'cx'), Argument('*mut JSObject', 'proxy'),
+ Argument('jsid', 'id'), Argument('bool', 'set'),
+ Argument('*mut JSPropertyDescriptor', 'desc')]
+ CGAbstractExternMethod.__init__(self, descriptor, "getOwnPropertyDescriptor",
+ "bool", args)
+ self.descriptor = descriptor
+ def getBody(self):
+ indexedGetter = self.descriptor.operations['IndexedGetter']
+ indexedSetter = self.descriptor.operations['IndexedSetter']
+
+ setOrIndexedGet = ""
+ if indexedGetter or indexedSetter:
+ setOrIndexedGet += "let index = GetArrayIndexFromId(cx, id);\n"
+
+ if indexedGetter:
+ readonly = toStringBool(self.descriptor.operations['IndexedSetter'] is None)
+ fillDescriptor = "FillPropertyDescriptor(&mut *desc, proxy, %s);\nreturn true;" % readonly
+ templateValues = {'jsvalRef': '(*desc).value', 'successCode': fillDescriptor}
+ get = ("if index.is_some() {\n" +
+ " let index = index.unwrap();\n" +
+ " let this = UnwrapProxy(proxy);\n" +
+ " let this = JS::from_raw(this);\n" +
+ " let this = this.root();\n" +
+ CGIndenter(CGProxyIndexedGetter(self.descriptor, templateValues)).define() + "\n" +
+ "}\n")
+
+ if indexedSetter or self.descriptor.operations['NamedSetter']:
+ setOrIndexedGet += "if set != 0 {\n"
+ if indexedSetter:
+ setOrIndexedGet += (" if index.is_some() {\n" +
+ " let index = index.unwrap();\n")
+ if not 'IndexedCreator' in self.descriptor.operations:
+ # FIXME need to check that this is a 'supported property index'
+ assert False
+ setOrIndexedGet += (" FillPropertyDescriptor(&mut *desc, proxy, false);\n" +
+ " return true;\n" +
+ " }\n")
+ if self.descriptor.operations['NamedSetter']:
+ setOrIndexedGet += " if RUST_JSID_IS_STRING(id) {\n"
+ if not 'NamedCreator' in self.descriptor.operations:
+ # FIXME need to check that this is a 'supported property name'
+ assert False
+ setOrIndexedGet += (" FillPropertyDescriptor(&mut *desc, proxy, false);\n" +
+ " return true;\n" +
+ " }\n")
+ setOrIndexedGet += "}"
+ if indexedGetter:
+ setOrIndexedGet += (" else {\n" +
+ CGIndenter(CGGeneric(get)).define() +
+ "}")
+ setOrIndexedGet += "\n\n"
+ elif indexedGetter:
+ setOrIndexedGet += ("if !set {\n" +
+ CGIndenter(CGGeneric(get)).define() +
+ "}\n\n")
+
+ namedGetter = self.descriptor.operations['NamedGetter']
+ if namedGetter:
+ readonly = toStringBool(self.descriptor.operations['NamedSetter'] is None)
+ fillDescriptor = "FillPropertyDescriptor(&mut *desc, proxy, %s);\nreturn true;" % readonly
+ templateValues = {'jsvalRef': '(*desc).value', 'successCode': fillDescriptor}
+ # Once we start supporting OverrideBuiltins we need to make
+ # ResolveOwnProperty or EnumerateOwnProperties filter out named
+ # properties that shadow prototype properties.
+ namedGet = ("\n" +
+ "if !set && RUST_JSID_IS_STRING(id) != 0 && !HasPropertyOnPrototype(cx, proxy, id) {\n" +
+ " let name = jsid_to_str(cx, id);\n" +
+ " let this = UnwrapProxy(proxy);\n" +
+ " let this = JS::from_raw(this);\n" +
+ " let this = this.root();\n" +
+ CGIndenter(CGProxyNamedGetter(self.descriptor, templateValues)).define() + "\n" +
+ "}\n")
+ else:
+ namedGet = ""
+
+ return setOrIndexedGet + """let expando: *mut JSObject = GetExpandoObject(proxy);
+//if (!xpc::WrapperFactory::IsXrayWrapper(proxy) && (expando = GetExpandoObject(proxy))) {
+if expando.is_not_null() {
+ let flags = if set { JSRESOLVE_ASSIGNING } else { 0 } | JSRESOLVE_QUALIFIED;
+ if JS_GetPropertyDescriptorById(cx, expando, id, flags, desc) == 0 {
+ return false;
+ }
+ if (*desc).obj.is_not_null() {
+ // Pretend the property lives on the wrapper.
+ (*desc).obj = proxy;
+ return true;
+ }
+}
+""" + namedGet + """
+(*desc).obj = ptr::mut_null();
+return true;"""
+
+ def definition_body(self):
+ return CGGeneric(self.getBody())
+
+class CGDOMJSProxyHandler_defineProperty(CGAbstractExternMethod):
+ def __init__(self, descriptor):
+ args = [Argument('*mut JSContext', 'cx'), Argument('*mut JSObject', 'proxy'),
+ Argument('jsid', 'id'),
+ Argument('*const JSPropertyDescriptor', 'desc')]
+ CGAbstractExternMethod.__init__(self, descriptor, "defineProperty", "bool", args)
+ self.descriptor = descriptor
+ def getBody(self):
+ set = ""
+
+ indexedSetter = self.descriptor.operations['IndexedSetter']
+ if indexedSetter:
+ if not (self.descriptor.operations['IndexedCreator'] is indexedSetter):
+ raise TypeError("Can't handle creator that's different from the setter")
+ set += ("let index = GetArrayIndexFromId(cx, id);\n" +
+ "if index.is_some() {\n" +
+ " let index = index.unwrap();\n" +
+ " let this = UnwrapProxy(proxy);\n" +
+ " let this = JS::from_raw(this);\n" +
+ " let this = this.root();\n" +
+ CGIndenter(CGProxyIndexedSetter(self.descriptor)).define() +
+ " return true;\n" +
+ "}\n")
+ elif self.descriptor.operations['IndexedGetter']:
+ set += ("if GetArrayIndexFromId(cx, id).is_some() {\n" +
+ " return false;\n" +
+ " //return ThrowErrorMessage(cx, MSG_NO_PROPERTY_SETTER, \"%s\");\n" +
+ "}\n") % self.descriptor.name
+
+ namedSetter = self.descriptor.operations['NamedSetter']
+ if namedSetter:
+ if not self.descriptor.operations['NamedCreator'] is namedSetter:
+ raise TypeError("Can't handle creator that's different from the setter")
+ set += ("if RUST_JSID_IS_STRING(id) != 0 {\n" +
+ " let name = jsid_to_str(cx, id);\n" +
+ " let this = UnwrapProxy(proxy);\n" +
+ " let this = JS::from_raw(this);\n" +
+ " let this = this.root();\n" +
+ CGIndenter(CGProxyNamedSetter(self.descriptor)).define() + "\n" +
+ "}\n")
+ elif self.descriptor.operations['NamedGetter']:
+ set += ("if RUST_JSID_IS_STRING(id) {\n" +
+ " let name = jsid_to_str(cx, id);\n" +
+ " let this = UnwrapProxy(proxy);\n" +
+ " let this = JS::from_raw(this);\n" +
+ " let this = this.root();\n" +
+ CGIndenter(CGProxyNamedGetter(self.descriptor)).define() +
+ " if (found) {\n"
+ " return false;\n" +
+ " //return ThrowErrorMessage(cx, MSG_NO_PROPERTY_SETTER, \"%s\");\n" +
+ " }\n" +
+ " return true;\n"
+ "}\n") % (self.descriptor.name)
+ return set + """return proxyhandler::defineProperty_(%s);""" % ", ".join(a.name for a in self.args)
+
+ def definition_body(self):
+ return CGGeneric(self.getBody())
+
+class CGDOMJSProxyHandler_hasOwn(CGAbstractExternMethod):
+ def __init__(self, descriptor):
+ args = [Argument('*mut JSContext', 'cx'), Argument('*mut JSObject', 'proxy'),
+ Argument('jsid', 'id'), Argument('*mut bool', 'bp')]
+ CGAbstractExternMethod.__init__(self, descriptor, "hasOwn", "bool", args)
+ self.descriptor = descriptor
+ def getBody(self):
+ indexedGetter = self.descriptor.operations['IndexedGetter']
+ if indexedGetter:
+ indexed = ("let index = GetArrayIndexFromId(cx, id);\n" +
+ "if index.is_some() {\n" +
+ " let index = index.unwrap();\n" +
+ " let this = UnwrapProxy(proxy);\n" +
+ " let this = JS::from_raw(this);\n" +
+ " let this = this.root();\n" +
+ CGIndenter(CGProxyIndexedGetter(self.descriptor)).define() + "\n" +
+ " *bp = found;\n" +
+ " return true;\n" +
+ "}\n\n")
+ else:
+ indexed = ""
+
+ namedGetter = self.descriptor.operations['NamedGetter']
+ if namedGetter:
+ named = ("if RUST_JSID_IS_STRING(id) != 0 && !HasPropertyOnPrototype(cx, proxy, id) {\n" +
+ " let name = jsid_to_str(cx, id);\n" +
+ " let this = UnwrapProxy(proxy);\n" +
+ " let this = JS::from_raw(this);\n" +
+ " let this = this.root();\n" +
+ CGIndenter(CGProxyNamedGetter(self.descriptor)).define() + "\n" +
+ " *bp = found;\n"
+ " return true;\n"
+ "}\n" +
+ "\n")
+ else:
+ named = ""
+
+ return indexed + """let expando: *mut JSObject = GetExpandoObject(proxy);
+if expando.is_not_null() {
+ let mut b: JSBool = 1;
+ let ok = JS_HasPropertyById(cx, expando, id, &mut b) != 0;
+ *bp = b != 0;
+ if !ok || *bp {
+ return ok;
+ }
+}
+
+""" + named + """*bp = false;
+return true;"""
+
+ def definition_body(self):
+ return CGGeneric(self.getBody())
+
+class CGDOMJSProxyHandler_get(CGAbstractExternMethod):
+ def __init__(self, descriptor):
+ args = [Argument('*mut JSContext', 'cx'), Argument('*mut JSObject', 'proxy'),
+ Argument('*mut JSObject', 'receiver'), Argument('jsid', 'id'),
+ Argument('*mut JSVal', 'vp')]
+ CGAbstractExternMethod.__init__(self, descriptor, "get", "bool", args)
+ self.descriptor = descriptor
+ def getBody(self):
+ getFromExpando = """let expando = GetExpandoObject(proxy);
+if expando.is_not_null() {
+ let mut hasProp = 0;
+ if JS_HasPropertyById(cx, expando, id, &mut hasProp) == 0 {
+ return false;
+ }
+
+ if hasProp != 0 {
+ return JS_GetPropertyById(cx, expando, id, vp) != 0;
+ }
+}"""
+
+ templateValues = {
+ 'jsvalRef': '*vp',
+ 'successCode': 'return true;',
+ }
+
+ indexedGetter = self.descriptor.operations['IndexedGetter']
+ if indexedGetter:
+ getIndexedOrExpando = ("let index = GetArrayIndexFromId(cx, id);\n" +
+ "if index.is_some() {\n" +
+ " let index = index.unwrap();\n" +
+ " let this = UnwrapProxy(proxy);\n" +
+ " let this = JS::from_raw(this);\n" +
+ " let this = this.root();\n" +
+ CGIndenter(CGProxyIndexedGetter(self.descriptor, templateValues)).define())
+ getIndexedOrExpando += """
+ // Even if we don't have this index, we don't forward the
+ // get on to our expando object.
+} else {
+ %s
+}
+""" % (stripTrailingWhitespace(getFromExpando.replace('\n', '\n ')))
+ else:
+ getIndexedOrExpando = getFromExpando + "\n"
+
+ namedGetter = self.descriptor.operations['NamedGetter']
+ if namedGetter and False: #XXXjdm unfinished
+ getNamed = ("if (JSID_IS_STRING(id)) {\n" +
+ " let name = jsid_to_str(cx, id);\n" +
+ " let this = UnwrapProxy(proxy);\n" +
+ " let this = JS::from_raw(this);\n" +
+ " let this = this.root();\n" +
+ CGIndenter(CGProxyNamedGetter(self.descriptor, templateValues)).define() +
+ "}\n") % (self.descriptor.concreteType)
+ else:
+ getNamed = ""
+
+ return """//MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
+ //"Should not have a XrayWrapper here");
+
+%s
+let mut found = false;
+if !GetPropertyOnPrototype(cx, proxy, id, &mut found, vp) {
+ return false;
+}
+
+if found {
+ return true;
+}
+%s
+*vp = UndefinedValue();
+return true;""" % (getIndexedOrExpando, getNamed)
+
+ def definition_body(self):
+ return CGGeneric(self.getBody())
+
+class CGDOMJSProxyHandler_obj_toString(CGAbstractExternMethod):
+ def __init__(self, descriptor):
+ args = [Argument('*mut JSContext', 'cx'), Argument('*mut JSObject', 'proxy')]
+ CGAbstractExternMethod.__init__(self, descriptor, "obj_toString", "*mut JSString", args)
+ self.descriptor = descriptor
+ def getBody(self):
+ stringifier = self.descriptor.operations['Stringifier']
+ if stringifier:
+ nativeName = MakeNativeName(stringifier.identifier.name)
+ signature = stringifier.signatures()[0]
+ returnType = signature[0]
+ extendedAttributes = self.descriptor.getExtendedAttributes(stringifier)
+ infallible = 'infallible' in extendedAttributes
+ if not infallible:
+ error = CGGeneric(
+ ('ThrowMethodFailedWithDetails(cx, rv, "%s", "toString");\n' +
+ "return NULL;") % self.descriptor.interface.identifier.name)
+ else:
+ error = None
+ call = CGCallGenerator(error, [], "", returnType, extendedAttributes, self.descriptor, nativeName, False, object="UnwrapProxy(proxy)")
+ return call.define() + """
+
+JSString* jsresult;
+return xpc_qsStringToJsstring(cx, result, &jsresult) ? jsresult : NULL;"""
+
+ return """let s = "%s".to_c_str();
+ _obj_toString(cx, s.as_ptr())""" % self.descriptor.name
+
+ def definition_body(self):
+ return CGGeneric(self.getBody())
+
+class CGAbstractClassHook(CGAbstractExternMethod):
+ """
+ Meant for implementing JSClass hooks, like Finalize or Trace. Does very raw
+ 'this' unwrapping as it assumes that the unwrapped type is always known.
+ """
+ def __init__(self, descriptor, name, returnType, args):
+ CGAbstractExternMethod.__init__(self, descriptor, name, returnType,
+ args)
+
+ def definition_body_prologue(self):
+ return CGGeneric("""\
+let this: *const %s = unwrap::<%s>(obj);
+""" % (self.descriptor.concreteType, self.descriptor.concreteType))
+
+ def definition_body(self):
+ return CGList([
+ self.definition_body_prologue(),
+ self.generate_code(),
+ ])
+
+ def generate_code(self):
+ # Override me
+ assert(False)
+
+def finalizeHook(descriptor, hookName, context):
+ release = """let val = JS_GetReservedSlot(obj, dom_object_slot(obj));
+let _: Box<%s> = mem::transmute(val.to_private());
+debug!("%s finalize: {:p}", this);
+""" % (descriptor.concreteType, descriptor.concreteType)
+ return release
+
+class CGClassTraceHook(CGAbstractClassHook):
+ """
+ A hook to trace through our native object; used for GC and CC
+ """
+ def __init__(self, descriptor):
+ args = [Argument('*mut JSTracer', 'trc'), Argument('*mut JSObject', 'obj')]
+ CGAbstractClassHook.__init__(self, descriptor, TRACE_HOOK_NAME, 'void',
+ args)
+
+ def generate_code(self):
+ return CGGeneric("(*this).trace(%s);" % self.args[0].name)
+
+class CGClassConstructHook(CGAbstractExternMethod):
+ """
+ JS-visible constructor for our objects
+ """
+ def __init__(self, descriptor):
+ args = [Argument('*mut JSContext', 'cx'), Argument('u32', 'argc'), Argument('*mut JSVal', 'vp')]
+ CGAbstractExternMethod.__init__(self, descriptor, CONSTRUCT_HOOK_NAME,
+ 'JSBool', args)
+ self._ctor = self.descriptor.interface.ctor()
+
+ def define(self):
+ if not self._ctor:
+ return ""
+ return CGAbstractExternMethod.define(self)
+
+ def definition_body(self):
+ preamble = CGGeneric("""\
+let global = global_object_for_js_object(JS_CALLEE(cx, vp).to_object());
+let global = global.root();
+""")
+ nativeName = MakeNativeName(self._ctor.identifier.name)
+ callGenerator = CGMethodCall(["&global.root_ref()"], nativeName, True,
+ self.descriptor, self._ctor)
+ return CGList([preamble, callGenerator])
+
+class CGClassFinalizeHook(CGAbstractClassHook):
+ """
+ A hook for finalize, used to release our native object.
+ """
+ def __init__(self, descriptor):
+ args = [Argument('*mut JSFreeOp', 'fop'), Argument('*mut JSObject', 'obj')]
+ CGAbstractClassHook.__init__(self, descriptor, FINALIZE_HOOK_NAME,
+ 'void', args)
+
+ def generate_code(self):
+ return CGGeneric(finalizeHook(self.descriptor, self.name, self.args[0].name))
+
+class CGDOMJSProxyHandlerDOMClass(CGThing):
+ def __init__(self, descriptor):
+ CGThing.__init__(self)
+ self.descriptor = descriptor
+
+ def define(self):
+ return """
+static Class: DOMClass = """ + DOMClass(self.descriptor) + """;
+
+"""
+
+
+class CGInterfaceTrait(CGThing):
+ def __init__(self, descriptor):
+ CGThing.__init__(self)
+
+ def argument_type(ty, optional=False, defaultValue=None, variadic=False):
+ _, _, declType, _ = getJSToNativeConversionTemplate(
+ ty, descriptor, isArgument=True)
+
+ if variadic:
+ declType = CGWrapper(declType, pre="Vec<", post=">")
+ elif optional and not defaultValue:
+ declType = CGWrapper(declType, pre="Option<", post=">")
+
+ if ty.isGeckoInterface() and not (ty.nullable() or optional):
+ declType = CGWrapper(declType, pre="&")
+ elif ty.isDictionary():
+ declType = CGWrapper(declType, pre="&")
+
+ return declType.define()
+
+ def attribute_arguments(needCx, argument=None):
+ if needCx:
+ yield "cx", "*mut JSContext"
+
+ if argument:
+ yield "value", argument_type(argument)
+
+ def method_arguments(returnType, arguments, trailing=None):
+ if needCx(returnType, arguments, True):
+ yield "cx", "*mut JSContext"
+
+ for argument in arguments:
+ ty = argument_type(argument.type, argument.optional,
+ argument.defaultValue, argument.variadic)
+ yield CGDictionary.makeMemberName(argument.identifier.name), ty
+
+ if trailing:
+ yield trailing
+
+ def return_type(rettype, infallible):
+ result = getRetvalDeclarationForType(rettype, descriptor)
+ if not infallible:
+ result = CGWrapper(result, pre="Fallible<", post=">")
+ return result.define()
+
+ def members():
+ for m in descriptor.interface.members:
+ if m.isMethod() and not m.isStatic():
+ name = CGSpecializedMethod.makeNativeName(descriptor, m)
+ infallible = 'infallible' in descriptor.getExtendedAttributes(m)
+ for idx, (rettype, arguments) in enumerate(m.signatures()):
+ arguments = method_arguments(rettype, arguments)
+ rettype = return_type(rettype, infallible)
+ yield name + ('_' * idx), arguments, rettype
+ elif m.isAttr() and not m.isStatic():
+ name = CGSpecializedGetter.makeNativeName(descriptor, m)
+ infallible = 'infallible' in descriptor.getExtendedAttributes(m, getter=True)
+ needCx = typeNeedsCx(m.type)
+ yield name, attribute_arguments(needCx), return_type(m.type, infallible)
+
+ if not m.readonly:
+ name = CGSpecializedSetter.makeNativeName(descriptor, m)
+ infallible = 'infallible' in descriptor.getExtendedAttributes(m, setter=True)
+ if infallible:
+ rettype = "()"
+ else:
+ rettype = "ErrorResult"
+ yield name, attribute_arguments(needCx, m.type), rettype
+
+ if descriptor.proxy:
+ for name, operation in descriptor.operations.iteritems():
+ if not operation:
+ continue
+
+ assert len(operation.signatures()) == 1
+ rettype, arguments = operation.signatures()[0]
+
+ infallible = 'infallible' in descriptor.getExtendedAttributes(operation)
+ arguments = method_arguments(rettype, arguments, ("found", "&mut bool"))
+ rettype = return_type(rettype, infallible)
+ yield name, arguments, rettype
+
+ def fmt(arguments):
+ return "".join(", %s: %s" % argument for argument in arguments)
+
+ methods = CGList([
+ CGGeneric("fn %s(&self%s) -> %s;\n" % (name, fmt(arguments), rettype))
+ for name, arguments, rettype in members()
+ ], "")
+ self.cgRoot = CGWrapper(CGIndenter(methods),
+ pre="pub trait %sMethods {\n" % descriptor.interface.identifier.name,
+ post="}")
+
+ def define(self):
+ return self.cgRoot.define()
+
+
+class CGDescriptor(CGThing):
+ def __init__(self, descriptor):
+ CGThing.__init__(self)
+
+ assert not descriptor.interface.isCallback()
+
+ cgThings = []
+ cgThings.append(CGGetProtoObjectMethod(descriptor))
+ if descriptor.interface.hasInterfaceObject():
+ # https://github.com/mozilla/servo/issues/2665
+ # cgThings.append(CGGetConstructorObjectMethod(descriptor))
+ pass
+
+ (hasMethod, hasGetter, hasLenientGetter,
+ hasSetter, hasLenientSetter) = False, False, False, False, False
+ for m in descriptor.interface.members:
+ if m.isMethod() and not m.isIdentifierLess():
+ if m.isStatic():
+ assert descriptor.interface.hasInterfaceObject()
+ cgThings.append(CGStaticMethod(descriptor, m))
+ else:
+ cgThings.append(CGSpecializedMethod(descriptor, m))
+ cgThings.append(CGMemberJITInfo(descriptor, m))
+ hasMethod = True
+ elif m.isAttr():
+ if m.isStatic():
+ assert descriptor.interface.hasInterfaceObject()
+ cgThings.append(CGStaticGetter(descriptor, m))
+ else:
+ cgThings.append(CGSpecializedGetter(descriptor, m))
+ if m.hasLenientThis():
+ hasLenientGetter = True
+ else:
+ hasGetter = True
+
+ if not m.readonly:
+ if m.isStatic():
+ assert descriptor.interface.hasInterfaceObject()
+ cgThings.append(CGStaticSetter(descriptor, m))
+ else:
+ cgThings.append(CGSpecializedSetter(descriptor, m))
+ if m.hasLenientThis():
+ hasLenientSetter = True
+ else:
+ hasSetter = True
+
+ if not m.isStatic():
+ cgThings.append(CGMemberJITInfo(descriptor, m))
+ if hasMethod:
+ cgThings.append(CGGenericMethod(descriptor))
+ if hasGetter:
+ cgThings.append(CGGenericGetter(descriptor))
+ if hasLenientGetter:
+ pass
+ if hasSetter:
+ cgThings.append(CGGenericSetter(descriptor))
+ if hasLenientSetter:
+ pass
+
+ if descriptor.concrete:
+ cgThings.append(CGClassFinalizeHook(descriptor))
+ cgThings.append(CGClassTraceHook(descriptor))
+
+ if descriptor.interface.hasInterfaceObject():
+ cgThings.append(CGClassConstructHook(descriptor))
+ cgThings.append(CGInterfaceObjectJSClass(descriptor))
+
+ cgThings.append(CGPrototypeJSClass(descriptor))
+
+ properties = PropertyArrays(descriptor)
+ cgThings.append(CGGeneric(str(properties)))
+ cgThings.append(CGNativeProperties(descriptor, properties))
+ cgThings.append(CGNativePropertyHooks(descriptor, properties))
+ cgThings.append(CGCreateInterfaceObjectsMethod(descriptor, properties))
+
+ cgThings.append(CGNamespace.build([descriptor.name + "Constants"],
+ CGConstant(m for m in descriptor.interface.members if m.isConst()),
+ public=True))
+
+ if descriptor.interface.hasInterfaceObject():
+ cgThings.append(CGDefineDOMInterfaceMethod(descriptor))
+
+ if descriptor.proxy:
+ cgThings.append(CGDefineProxyHandler(descriptor))
+
+ if descriptor.concrete:
+ if descriptor.proxy:
+ #cgThings.append(CGProxyIsProxy(descriptor))
+ cgThings.append(CGProxyUnwrap(descriptor))
+ cgThings.append(CGDOMJSProxyHandlerDOMClass(descriptor))
+ cgThings.append(CGDOMJSProxyHandler_getOwnPropertyDescriptor(descriptor))
+ cgThings.append(CGDOMJSProxyHandler_obj_toString(descriptor))
+ cgThings.append(CGDOMJSProxyHandler_get(descriptor))
+ cgThings.append(CGDOMJSProxyHandler_hasOwn(descriptor))
+ if descriptor.operations['IndexedSetter'] or descriptor.operations['NamedSetter']:
+ cgThings.append(CGDOMJSProxyHandler_defineProperty(descriptor))
+
+ #cgThings.append(CGDOMJSProxyHandler(descriptor))
+ #cgThings.append(CGIsMethod(descriptor))
+ pass
+ else:
+ cgThings.append(CGDOMJSClass(descriptor))
+ pass
+
+ cgThings.append(CGWrapMethod(descriptor))
+
+ cgThings.append(CGIDLInterface(descriptor))
+ cgThings.append(CGInterfaceTrait(descriptor))
+
+ cgThings = CGList(cgThings, "\n")
+ cgThings = CGWrapper(cgThings, pre='\n', post='\n')
+ #self.cgRoot = CGWrapper(CGNamespace(toBindingNamespace(descriptor.name),
+ # cgThings),
+ # post='\n')
+ self.cgRoot = cgThings
+
+ def define(self):
+ return self.cgRoot.define()
+
+class CGNamespacedEnum(CGThing):
+ def __init__(self, namespace, enumName, names, values, comment="", deriving=""):
+
+ if not values:
+ values = []
+
+ # Account for explicit enum values.
+ entries = []
+ for i in range(0, len(names)):
+ if len(values) > i and values[i] is not None:
+ entry = "%s = %s" % (names[i], values[i])
+ else:
+ entry = names[i]
+ entries.append(entry)
+
+ # Append a Count.
+ entries.append(enumName + 'Count = ' + str(len(entries)))
+
+ # Indent.
+ entries = [' ' + e for e in entries]
+
+ # Build the enum body.
+ enumstr = comment + 'pub enum %s {\n%s\n}\n' % (enumName, ',\n'.join(entries))
+ if deriving:
+ enumstr = ('#[deriving(%s)]\n' % deriving) + enumstr
+ curr = CGGeneric(enumstr)
+
+ # Add some whitespace padding.
+ curr = CGWrapper(curr, pre='\n',post='\n')
+
+ # Add the namespace.
+ curr = CGNamespace(namespace, curr, public=True)
+
+ # Add the typedef
+ #typedef = '\ntypedef %s::%s %s;\n\n' % (namespace, enumName, enumName)
+ #curr = CGList([curr, CGGeneric(typedef)])
+
+ # Save the result.
+ self.node = curr
+
+ def define(self):
+ return self.node.define()
+
+class CGDictionary(CGThing):
+ def __init__(self, dictionary, descriptorProvider):
+ self.dictionary = dictionary;
+ if all(CGDictionary(d, descriptorProvider).generatable for
+ d in CGDictionary.getDictionaryDependencies(dictionary)):
+ self.generatable = True
+ else:
+ self.generatable = False
+ # Nothing else to do here
+ return
+ self.memberInfo = [
+ (member,
+ getJSToNativeConversionTemplate(member.type,
+ descriptorProvider,
+ isMember="Dictionary",
+ defaultValue=member.defaultValue,
+ failureCode="return Err(());",
+ exceptionCode="return Err(());"))
+ for member in dictionary.members ]
+
+ def define(self):
+ if not self.generatable:
+ return ""
+ return self.struct() + "\n" + self.impl()
+
+ def struct(self):
+ d = self.dictionary
+ if d.parent:
+ inheritance = " pub parent: %s::%s<'a, 'b>,\n" % (self.makeModuleName(d.parent),
+ self.makeClassName(d.parent))
+ else:
+ inheritance = ""
+ memberDecls = [" pub %s: %s," %
+ (self.makeMemberName(m[0].identifier.name), self.getMemberType(m))
+ for m in self.memberInfo]
+
+ return (string.Template(
+ "pub struct ${selfName}<'a, 'b> {\n" +
+ "${inheritance}" +
+ "\n".join(memberDecls) + "\n" +
+ "}").substitute( { "selfName": self.makeClassName(d),
+ "inheritance": inheritance }))
+
+ def impl(self):
+ d = self.dictionary
+ if d.parent:
+ initParent = ("parent: match %s::%s::new(cx, val) {\n"
+ " Ok(parent) => parent,\n"
+ " Err(_) => return Err(()),\n"
+ "},\n") % (self.makeModuleName(d.parent),
+ self.makeClassName(d.parent))
+ else:
+ initParent = ""
+
+ def memberInit(memberInfo):
+ member, _ = memberInfo
+ name = self.makeMemberName(member.identifier.name)
+ conversion = self.getMemberConversion(memberInfo)
+ return CGGeneric("%s: %s,\n" % (name, conversion.define()))
+
+ memberInits = CGList([memberInit(m) for m in self.memberInfo])
+
+ return string.Template(
+ "impl<'a, 'b> ${selfName}<'a, 'b> {\n"
+ " pub fn empty() -> ${selfName}<'a, 'b> {\n"
+ " ${selfName}::new(ptr::mut_null(), NullValue()).unwrap()\n"
+ " }\n"
+ " pub fn new(cx: *mut JSContext, val: JSVal) -> Result<${selfName}<'a, 'b>, ()> {\n"
+ " let object = if val.is_null_or_undefined() {\n"
+ " ptr::mut_null()\n"
+ " } else if val.is_object() {\n"
+ " val.to_object()\n"
+ " } else {\n"
+ " throw_type_error(cx, \"Value not an object.\");\n"
+ " return Err(());\n"
+ " };\n"
+ " Ok(${selfName} {\n"
+ "${initParent}"
+ "${initMembers}"
+ " })\n"
+ " }\n"
+ "}").substitute({
+ "selfName": self.makeClassName(d),
+ "initParent": CGIndenter(CGGeneric(initParent), indentLevel=6).define(),
+ "initMembers": CGIndenter(memberInits, indentLevel=6).define(),
+ })
+
+ @staticmethod
+ def makeDictionaryName(dictionary):
+ return dictionary.identifier.name
+
+ def makeClassName(self, dictionary):
+ return self.makeDictionaryName(dictionary)
+
+ @staticmethod
+ def makeModuleName(dictionary):
+ name = dictionary.identifier.name
+ if name.endswith('Init'):
+ return toBindingNamespace(name.replace('Init', ''))
+ #XXXjdm This breaks on the test webidl files, sigh.
+ #raise TypeError("No idea how to find this dictionary's definition: " + name)
+ return "/* uh oh */ %s" % name
+
+ def getMemberType(self, memberInfo):
+ member, (_, _, declType, _) = memberInfo
+ if not member.defaultValue:
+ declType = CGWrapper(declType, pre="Option<", post=">")
+ return declType.define()
+
+ def getMemberConversion(self, memberInfo):
+ def indent(s):
+ return CGIndenter(CGGeneric(s), 8).define()
+
+ member, (templateBody, default, declType, _) = memberInfo
+ replacements = { "val": "value" }
+ conversion = string.Template(templateBody).substitute(replacements)
+
+ assert (member.defaultValue is None) == (default is None)
+ if not default:
+ default = "None"
+ conversion = "Some(%s)" % conversion
+
+ conversion = (
+ "match get_dictionary_property(cx, object, \"%s\") {\n"
+ " Err(()) => return Err(()),\n"
+ " Ok(Some(value)) => {\n"
+ "%s\n"
+ " },\n"
+ " Ok(None) => {\n"
+ "%s\n"
+ " },\n"
+ "}") % (member.identifier.name, indent(conversion), indent(default))
+
+ return CGGeneric(conversion)
+
+ @staticmethod
+ def makeIdName(name):
+ return name + "_id"
+
+ @staticmethod
+ def makeMemberName(name):
+ # Can't use Rust keywords as member names.
+ if name == "type":
+ return name + "_"
+ return name
+
+ @staticmethod
+ def getDictionaryDependencies(dictionary):
+ deps = set();
+ if dictionary.parent:
+ deps.add(dictionary.parent)
+ for member in dictionary.members:
+ if member.type.isDictionary():
+ deps.add(member.type.unroll().inner)
+ return deps
+
+class CGRegisterProtos(CGAbstractMethod):
+ def __init__(self, config):
+ arguments = [
+ Argument('*mut JSContext', 'cx'),
+ Argument('*mut JSObject', 'global'),
+ ]
+ CGAbstractMethod.__init__(self, None, 'Register', 'void', arguments,
+ unsafe=False, pub=True)
+ self.config = config
+
+ def definition_body(self):
+ return CGList([
+ CGGeneric("codegen::Bindings::%sBinding::DefineDOMInterface(cx, global);" % desc.name)
+ for desc in self.config.getDescriptors(hasInterfaceObject=True, register=True)
+ ], "\n")
+
+
+class CGRegisterProxyHandlersMethod(CGAbstractMethod):
+ def __init__(self, descriptors):
+ CGAbstractMethod.__init__(self, None, 'RegisterProxyHandlers', 'void', [],
+ unsafe=True, pub=True)
+ self.descriptors = descriptors
+
+ def definition_body(self):
+ return CGList([
+ CGGeneric("proxy_handlers[proxies::%s as uint] = codegen::Bindings::%sBinding::DefineProxyHandler();" % (desc.name, desc.name))
+ for desc in self.descriptors
+ ], "\n")
+
+
+class CGRegisterProxyHandlers(CGThing):
+ def __init__(self, config):
+ descriptors = config.getDescriptors(proxy=True)
+ length = len(descriptors)
+ self.root = CGList([
+ CGGeneric("pub static mut proxy_handlers: [*const libc::c_void, ..%d] = [0 as *const libc::c_void, ..%d];" % (length, length)),
+ CGRegisterProxyHandlersMethod(descriptors),
+ ], "\n")
+
+ def define(self):
+ return self.root.define()
+
+
+class CGBindingRoot(CGThing):
+ """
+ Root codegen class for binding generation. Instantiate the class, and call
+ declare or define to generate header or cpp code (respectively).
+ """
+ def __init__(self, config, prefix, webIDLFile):
+ descriptors = config.getDescriptors(webIDLFile=webIDLFile,
+ isCallback=False)
+ dictionaries = config.getDictionaries(webIDLFile=webIDLFile)
+
+ cgthings = []
+
+ mainCallbacks = config.getCallbacks(webIDLFile=webIDLFile)
+ callbackDescriptors = config.getDescriptors(webIDLFile=webIDLFile,
+ isCallback=True)
+
+ # Do codegen for all the enums
+ cgthings = [CGEnum(e) for e in config.getEnums(webIDLFile)]
+
+ cgthings.extend([CGDictionary(d, config.getDescriptorProvider())
+ for d in dictionaries])
+
+ # Do codegen for all the callbacks.
+ cgthings.extend(CGList([CGCallbackFunction(c, config.getDescriptorProvider()),
+ CGCallbackFunctionImpl(c)], "\n")
+ for c in mainCallbacks)
+
+ # Do codegen for all the descriptors
+ cgthings.extend([CGDescriptor(x) for x in descriptors])
+
+ # Do codegen for all the callback interfaces.
+ cgthings.extend(CGList([CGCallbackInterface(x),
+ CGCallbackFunctionImpl(x)], "\n")
+ for x in callbackDescriptors)
+
+ # And make sure we have the right number of newlines at the end
+ curr = CGWrapper(CGList(cgthings, "\n\n"), post="\n\n")
+
+ # Wrap all of that in our namespaces.
+ #curr = CGNamespace.build(['dom'],
+ # CGWrapper(curr, pre="\n"))
+
+ # Add imports
+ #XXXjdm This should only import the namespace for the current binding,
+ # not every binding ever.
+ curr = CGImports(curr, descriptors, [
+ 'js',
+ 'js::{JS_ARGV, JS_CALLEE, JS_THIS_OBJECT}',
+ 'js::{JSCLASS_GLOBAL_SLOT_COUNT, JSCLASS_IS_DOMJSCLASS}',
+ 'js::{JSCLASS_IS_GLOBAL, JSCLASS_RESERVED_SLOTS_SHIFT}',
+ 'js::{JSCLASS_RESERVED_SLOTS_MASK, JSID_VOID, JSJitInfo}',
+ 'js::{JSPROP_ENUMERATE, JSPROP_NATIVE_ACCESSORS, JSPROP_SHARED}',
+ 'js::{JSRESOLVE_ASSIGNING, JSRESOLVE_QUALIFIED}',
+ 'js::jsapi::{JS_CallFunctionValue, JS_GetClass, JS_GetGlobalForObject}',
+ 'js::jsapi::{JS_GetObjectPrototype, JS_GetProperty, JS_GetPropertyById}',
+ 'js::jsapi::{JS_GetPropertyDescriptorById, JS_GetReservedSlot}',
+ 'js::jsapi::{JS_HasProperty, JS_HasPropertyById, JS_IsExceptionPending}',
+ 'js::jsapi::{JS_NewObject, JS_ObjectIsCallable, JS_SetPrototype}',
+ 'js::jsapi::{JS_SetReservedSlot, JS_WrapValue, JSBool, JSContext}',
+ 'js::jsapi::{JSClass, JSFreeOp, JSFunctionSpec, JSHandleObject, jsid}',
+ 'js::jsapi::{JSNativeWrapper, JSObject, JSPropertyDescriptor, JS_ArrayIterator}',
+ 'js::jsapi::{JSPropertyOpWrapper, JSPropertySpec, JS_PropertyStub}',
+ 'js::jsapi::{JSStrictPropertyOpWrapper, JSString, JSTracer, JS_ConvertStub}',
+ 'js::jsapi::{JS_StrictPropertyStub, JS_EnumerateStub, JS_ResolveStub}',
+ 'js::jsval::JSVal',
+ 'js::jsval::{ObjectValue, ObjectOrNullValue, PrivateValue}',
+ 'js::jsval::{NullValue, UndefinedValue}',
+ 'js::glue::{CallJitMethodOp, CallJitPropertyOp, CreateProxyHandler}',
+ 'js::glue::{GetProxyPrivate, NewProxyObject, ProxyTraps}',
+ 'js::glue::{RUST_FUNCTION_VALUE_TO_JITINFO}',
+ 'js::glue::{RUST_JS_NumberValue, RUST_JSID_IS_STRING}',
+ 'js::rust::with_compartment',
+ 'dom::types::*',
+ 'dom::bindings',
+ 'dom::bindings::global::GlobalRef',
+ 'dom::bindings::js::{JS, JSRef, Root, RootedReference, Temporary}',
+ 'dom::bindings::js::{OptionalRootable, OptionalRootedRootable, ResultRootable}',
+ 'dom::bindings::js::{OptionalRootedReference, OptionalOptionalRootedRootable}',
+ 'dom::bindings::utils::{CreateDOMGlobal, CreateInterfaceObjects2}',
+ 'dom::bindings::utils::{ConstantSpec, cx_for_dom_object}',
+ 'dom::bindings::utils::{dom_object_slot, DOM_OBJECT_SLOT, DOMClass}',
+ 'dom::bindings::utils::{DOMJSClass, JSCLASS_DOM_GLOBAL}',
+ 'dom::bindings::utils::{FindEnumStringIndex, GetArrayIndexFromId}',
+ 'dom::bindings::utils::{GetPropertyOnPrototype, GetProtoOrIfaceArray}',
+ 'dom::bindings::utils::{HasPropertyOnPrototype, IntVal}',
+ 'dom::bindings::utils::{jsid_to_str}',
+ 'dom::bindings::utils::global_object_for_js_object',
+ 'dom::bindings::utils::{Reflectable}',
+ 'dom::bindings::utils::{squirrel_away_unique}',
+ 'dom::bindings::utils::{ThrowingConstructor, unwrap, unwrap_jsmanaged}',
+ 'dom::bindings::utils::VoidVal',
+ 'dom::bindings::utils::get_dictionary_property',
+ 'dom::bindings::utils::{NativeProperties, NativePropertyHooks}',
+ 'dom::bindings::trace::JSTraceable',
+ 'dom::bindings::callback::{CallbackContainer,CallbackInterface,CallbackFunction}',
+ 'dom::bindings::callback::{CallSetup,ExceptionHandling}',
+ 'dom::bindings::callback::{WrapCallThisObject}',
+ 'dom::bindings::conversions::{FromJSValConvertible, ToJSValConvertible}',
+ 'dom::bindings::conversions::IDLInterface',
+ 'dom::bindings::conversions::{Default, Empty}',
+ 'dom::bindings::codegen::*',
+ 'dom::bindings::codegen::Bindings::*',
+ 'dom::bindings::codegen::RegisterBindings',
+ 'dom::bindings::codegen::UnionTypes::*',
+ 'dom::bindings::error::{FailureUnknown, Fallible, Error, ErrorResult}',
+ 'dom::bindings::error::throw_dom_exception',
+ 'dom::bindings::error::throw_type_error',
+ 'dom::bindings::proxyhandler',
+ 'dom::bindings::proxyhandler::{_obj_toString, defineProperty}',
+ 'dom::bindings::proxyhandler::{FillPropertyDescriptor, GetExpandoObject}',
+ 'dom::bindings::proxyhandler::{delete_, getPropertyDescriptor}',
+ 'dom::bindings::str::ByteString',
+ 'page::JSPageInfo',
+ 'libc',
+ 'servo_util::str::DOMString',
+ 'std::mem',
+ 'std::cmp',
+ 'std::ptr',
+ 'std::str',
+ 'std::num',
+ ])
+
+ # Add the auto-generated comment.
+ curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
+
+ # Store the final result.
+ self.root = curr
+
+ def define(self):
+ return stripTrailingWhitespace(self.root.define())
+
+class CGNativeMember(ClassMethod):
+ def __init__(self, descriptorProvider, member, name, signature, extendedAttrs,
+ breakAfter=True, passJSBitsAsNeeded=True, visibility="public",
+ jsObjectsArePtr=False, variadicIsSequence=False):
+ """
+ If jsObjectsArePtr is true, typed arrays and "object" will be
+ passed as JSObject*.
+
+ If passJSBitsAsNeeded is false, we don't automatically pass in a
+ JSContext* or a JSObject* based on the return and argument types.
+ """
+ self.descriptorProvider = descriptorProvider
+ self.member = member
+ self.extendedAttrs = extendedAttrs
+ self.passJSBitsAsNeeded = passJSBitsAsNeeded
+ self.jsObjectsArePtr = jsObjectsArePtr
+ self.variadicIsSequence = variadicIsSequence
+ breakAfterSelf = "\n" if breakAfter else ""
+ ClassMethod.__init__(self, name,
+ self.getReturnType(signature[0], False),
+ self.getArgs(signature[0], signature[1]),
+ static=member.isStatic(),
+ # Mark our getters, which are attrs that
+ # have a non-void return type, as const.
+ const=(not member.isStatic() and member.isAttr() and
+ not signature[0].isVoid()),
+ breakAfterReturnDecl=" ",
+ breakAfterSelf=breakAfterSelf,
+ visibility=visibility)
+
+ def getReturnType(self, type, isMember):
+ return self.getRetvalInfo(type, isMember)[0]
+
+ def getRetvalInfo(self, type, isMember):
+ """
+ Returns a tuple:
+
+ The first element is the type declaration for the retval
+
+ The second element is a template for actually returning a value stored in
+ "${declName}". This means actually returning it if
+ we're not outparam, else assigning to the "retval" outparam. If
+ isMember is true, this can be None, since in that case the caller will
+ never examine this value.
+ """
+ if type.isVoid():
+ typeDecl, template = "", ""
+ elif type.isPrimitive() and type.tag() in builtinNames:
+ result = CGGeneric(builtinNames[type.tag()])
+ if type.nullable():
+ raise TypeError("Nullable primitives are not supported here.")
+
+ typeDecl, template = result.define(), "return Ok(${declName});"
+ elif type.isDOMString():
+ if isMember:
+ # No need for a third element in the isMember case
+ typeDecl, template = "nsString", None
+ # Outparam
+ else:
+ typeDecl, template = "void", "retval = ${declName};"
+ elif type.isByteString():
+ if isMember:
+ # No need for a third element in the isMember case
+ typeDecl, template = "nsCString", None
+ # Outparam
+ typeDecl, template = "void", "retval = ${declName};"
+ elif type.isEnum():
+ enumName = type.unroll().inner.identifier.name
+ if type.nullable():
+ enumName = CGTemplatedType("Nullable",
+ CGGeneric(enumName)).define()
+ typeDecl, template = enumName, "return ${declName};"
+ elif type.isGeckoInterface():
+ iface = type.unroll().inner;
+ nativeType = self.descriptorProvider.getDescriptor(
+ iface.identifier.name).nativeType
+ # Now trim off unnecessary namespaces
+ nativeType = nativeType.split("::")
+ if nativeType[0] == "mozilla":
+ nativeType.pop(0)
+ if nativeType[0] == "dom":
+ nativeType.pop(0)
+ result = CGWrapper(CGGeneric("::".join(nativeType)), post="*")
+ # Since we always force an owning type for callback return values,
+ # our ${declName} is an OwningNonNull or nsRefPtr. So we can just
+ # .forget() to get our already_AddRefed.
+ typeDecl, template = result.define(), "return ${declName}.forget();"
+ elif type.isCallback():
+ typeDecl, template = \
+ ("already_AddRefed<%s>" % type.unroll().identifier.name,
+ "return ${declName}.forget();")
+ elif type.isAny():
+ typeDecl, template = "JSVal", "return Ok(${declName});"
+ elif type.isObject():
+ typeDecl, template = "JSObject*", "return ${declName};"
+ elif type.isSpiderMonkeyInterface():
+ if type.nullable():
+ returnCode = "return ${declName}.IsNull() ? nullptr : ${declName}.Value().Obj();"
+ else:
+ returnCode = "return ${declName}.Obj();"
+ typeDecl, template = "JSObject*", returnCode
+ elif type.isSequence():
+ # If we want to handle sequence-of-sequences return values, we're
+ # going to need to fix example codegen to not produce nsTArray<void>
+ # for the relevant argument...
+ assert not isMember
+ # Outparam.
+ if type.nullable():
+ returnCode = ("if (${declName}.IsNull()) {\n"
+ " retval.SetNull();\n"
+ "} else {\n"
+ " retval.SetValue().SwapElements(${declName}.Value());\n"
+ "}")
+ else:
+ returnCode = "retval.SwapElements(${declName});"
+ typeDecl, template = "void", returnCode
+ elif type.isDate():
+ result = CGGeneric("Date")
+ if type.nullable():
+ result = CGTemplatedType("Nullable", result)
+ typeDecl, template = result.define(), "return ${declName};"
+ else:
+ raise TypeError("Don't know how to declare return value for %s" % type)
+
+ if not 'infallible' in self.extendedAttrs:
+ if typeDecl:
+ typeDecl = "Fallible<%s>" % typeDecl
+ else:
+ typeDecl = "ErrorResult"
+ if not template:
+ template = "return Ok(());"
+ return typeDecl, template
+
+ def getArgs(self, returnType, argList):
+ args = [self.getArg(arg) for arg in argList]
+ # Now the outparams
+ if returnType.isDOMString():
+ args.append(Argument("nsString&", "retval"))
+ if returnType.isByteString():
+ args.append(Argument("nsCString&", "retval"))
+ elif returnType.isSequence():
+ nullable = returnType.nullable()
+ if nullable:
+ returnType = returnType.inner
+ # And now the actual underlying type
+ elementDecl = self.getReturnType(returnType.inner, True)
+ type = CGTemplatedType("nsTArray", CGGeneric(elementDecl))
+ if nullable:
+ type = CGTemplatedType("Nullable", type)
+ args.append(Argument("%s&" % type.define(), "retval"))
+ # The legacycaller thisval
+ if self.member.isMethod() and self.member.isLegacycaller():
+ # If it has an identifier, we can't deal with it yet
+ assert self.member.isIdentifierLess()
+ args.insert(0, Argument("JS::Value", "aThisVal"))
+ # And jscontext bits.
+ if needCx(returnType, argList, self.passJSBitsAsNeeded):
+ args.insert(0, Argument("JSContext*", "cx"))
+ # And if we're static, a global
+ if self.member.isStatic():
+ args.insert(0, Argument("const GlobalObject&", "global"))
+ return args
+
+ def doGetArgType(self, type, optional, isMember):
+ """
+ The main work of getArgType. Returns a string type decl, whether this
+ is a const ref, as well as whether the type should be wrapped in
+ Nullable as needed.
+
+ isMember can be false or one of the strings "Sequence" or "Variadic"
+ """
+ if type.isArray():
+ raise TypeError("Can't handle array arguments yet")
+
+ if type.isSequence():
+ nullable = type.nullable()
+ if nullable:
+ type = type.inner
+ elementType = type.inner
+ argType = self.getArgType(elementType, False, "Sequence")[0]
+ decl = CGTemplatedType("Sequence", argType)
+ return decl.define(), True, True
+
+ if type.isUnion():
+ if type.nullable():
+ type = type.inner
+ return str(type) + "::" + str(type), False, True
+
+ if type.isGeckoInterface() and not type.isCallbackInterface():
+ iface = type.unroll().inner
+ argIsPointer = type.nullable()
+ forceOwningType = iface.isCallback() or isMember
+ if argIsPointer:
+ if (optional or isMember) and forceOwningType:
+ typeDecl = "nsRefPtr<%s>"
+ else:
+ typeDecl = "*%s"
+ else:
+ if optional or isMember:
+ if forceOwningType:
+ typeDecl = "OwningNonNull<%s>"
+ else:
+ typeDecl = "NonNull<%s>"
+ else:
+ typeDecl = "%s"
+ descriptor = self.descriptorProvider.getDescriptor(iface.identifier.name)
+ return (typeDecl % descriptor.argumentType,
+ False, False)
+
+ if type.isSpiderMonkeyInterface():
+ if self.jsObjectsArePtr:
+ return "JSObject*", False, False
+
+ return type.name, True, True
+
+ if type.isDOMString():
+ declType = "DOMString"
+ return declType, True, False
+
+ if type.isByteString():
+ declType = "nsCString"
+ return declType, True, False
+
+ if type.isEnum():
+ return type.unroll().inner.identifier.name, False, True
+
+ if type.isCallback() or type.isCallbackInterface():
+ forceOwningType = optional or isMember
+ if type.nullable():
+ if forceOwningType:
+ declType = "nsRefPtr<%s>"
+ else:
+ declType = "%s*"
+ else:
+ if forceOwningType:
+ declType = "OwningNonNull<%s>"
+ else:
+ declType = "%s&"
+ if type.isCallback():
+ name = type.unroll().identifier.name
+ else:
+ name = type.unroll().inner.identifier.name
+ return declType % name, False, False
+
+ if type.isAny():
+ # Don't do the rooting stuff for variadics for now
+ if isMember:
+ declType = "JS::Value"
+ else:
+ declType = "JSVal"
+ return declType, False, False
+
+ if type.isObject():
+ if isMember:
+ declType = "JSObject*"
+ else:
+ declType = "JS::Handle<JSObject*>"
+ return declType, False, False
+
+ if type.isDictionary():
+ typeName = CGDictionary.makeDictionaryName(type.inner)
+ return typeName, True, True
+
+ if type.isDate():
+ return "Date", False, True
+
+ assert type.isPrimitive()
+
+ return builtinNames[type.tag()], False, True
+
+ def getArgType(self, type, optional, isMember):
+ """
+ Get the type of an argument declaration. Returns the type CGThing, and
+ whether this should be a const ref.
+
+ isMember can be False, "Sequence", or "Variadic"
+ """
+ (decl, ref, handleNullable) = self.doGetArgType(type, optional,
+ isMember)
+ decl = CGGeneric(decl)
+ if handleNullable and type.nullable():
+ decl = CGTemplatedType("Nullable", decl)
+ ref = True
+ if isMember == "Variadic":
+ arrayType = "Sequence" if self.variadicIsSequence else "nsTArray"
+ decl = CGTemplatedType(arrayType, decl)
+ ref = True
+ elif optional:
+ # Note: All variadic args claim to be optional, but we can just use
+ # empty arrays to represent them not being present.
+ decl = CGTemplatedType("Option", decl)
+ ref = False
+ return (decl, ref)
+
+ def getArg(self, arg):
+ """
+ Get the full argument declaration for an argument
+ """
+ (decl, ref) = self.getArgType(arg.type,
+ arg.optional and not arg.defaultValue,
+ "Variadic" if arg.variadic else False)
+ if ref:
+ decl = CGWrapper(decl, pre="&")
+
+ return Argument(decl.define(), arg.identifier.name)
+
+class CGCallback(CGClass):
+ def __init__(self, idlObject, descriptorProvider, baseName, methods,
+ getters=[], setters=[]):
+ self.baseName = baseName
+ self._deps = idlObject.getDeps()
+ name = idlObject.identifier.name
+ # For our public methods that needThisHandling we want most of the
+ # same args and the same return type as what CallbackMember
+ # generates. So we want to take advantage of all its
+ # CGNativeMember infrastructure, but that infrastructure can't deal
+ # with templates and most especially template arguments. So just
+ # cheat and have CallbackMember compute all those things for us.
+ realMethods = []
+ for method in methods:
+ if not method.needThisHandling:
+ realMethods.append(method)
+ else:
+ realMethods.extend(self.getMethodImpls(method))
+ CGClass.__init__(self, name,
+ bases=[ClassBase(baseName)],
+ constructors=self.getConstructors(),
+ methods=realMethods+getters+setters,
+ decorators="#[deriving(PartialEq,Clone,Encodable)]")
+
+ def getConstructors(self):
+ return [ClassConstructor(
+ [Argument("*mut JSObject", "aCallback")],
+ bodyInHeader=True,
+ visibility="pub",
+ explicit=False,
+ baseConstructors=[
+ "%s::new(aCallback)" % self.baseName
+ ])]
+
+ def getMethodImpls(self, method):
+ assert method.needThisHandling
+ args = list(method.args)
+ # Strip out the JSContext*/JSObject* args
+ # that got added.
+ assert args[0].name == "cx" and args[0].argType == "*mut JSContext"
+ assert args[1].name == "aThisObj" and args[1].argType == "*mut JSObject"
+ args = args[2:]
+ # Record the names of all the arguments, so we can use them when we call
+ # the private method.
+ argnames = [arg.name for arg in args]
+ argnamesWithThis = ["s.GetContext()", "thisObjJS"] + argnames
+ argnamesWithoutThis = ["s.GetContext()", "ptr::mut_null()"] + argnames
+ # Now that we've recorded the argnames for our call to our private
+ # method, insert our optional argument for deciding whether the
+ # CallSetup should re-throw exceptions on aRv.
+ args.append(Argument("ExceptionHandling", "aExceptionHandling",
+ "ReportExceptions"))
+
+ args[0] = Argument('&' + args[0].argType, args[0].name, args[0].default)
+ method.args[2] = args[0]
+
+ # And now insert our template argument.
+ argsWithoutThis = list(args)
+ args.insert(0, Argument("&JSRef<T>", "thisObj"))
+
+ # And the self argument
+ method.args.insert(0, Argument(None, "&self"))
+ args.insert(0, Argument(None, "&self"))
+ argsWithoutThis.insert(0, Argument(None, "&self"))
+
+ setupCall = ("let s = CallSetup::new(self, aExceptionHandling);\n"
+ "if s.GetContext().is_null() {\n"
+ " return Err(FailureUnknown);\n"
+ "}\n")
+
+ bodyWithThis = string.Template(
+ setupCall+
+ "let thisObjJS = WrapCallThisObject(s.GetContext(), thisObj);\n"
+ "if thisObjJS.is_null() {\n"
+ " return Err(FailureUnknown);\n"
+ "}\n"
+ "return ${methodName}(${callArgs});").substitute({
+ "callArgs" : ", ".join(argnamesWithThis),
+ "methodName": 'self.' + method.name,
+ })
+ bodyWithoutThis = string.Template(
+ setupCall +
+ "return ${methodName}(${callArgs});").substitute({
+ "callArgs" : ", ".join(argnamesWithoutThis),
+ "methodName": 'self.' + method.name,
+ })
+ return [ClassMethod(method.name+'_', method.returnType, args,
+ bodyInHeader=True,
+ templateArgs=["T: Reflectable"],
+ body=bodyWithThis,
+ visibility='pub'),
+ ClassMethod(method.name+'__', method.returnType, argsWithoutThis,
+ bodyInHeader=True,
+ body=bodyWithoutThis,
+ visibility='pub'),
+ method]
+
+ def deps(self):
+ return self._deps
+
+# We're always fallible
+def callbackGetterName(attr):
+ return "Get" + MakeNativeName(attr.identifier.name)
+
+def callbackSetterName(attr):
+ return "Set" + MakeNativeName(attr.identifier.name)
+
+class CGCallbackFunction(CGCallback):
+ def __init__(self, callback, descriptorProvider):
+ CGCallback.__init__(self, callback, descriptorProvider,
+ "CallbackFunction",
+ methods=[CallCallback(callback, descriptorProvider)])
+
+ def getConstructors(self):
+ return CGCallback.getConstructors(self)
+
+class CGCallbackFunctionImpl(CGGeneric):
+ def __init__(self, callback):
+ impl = string.Template("""impl CallbackContainer for ${type} {
+ fn new(callback: *mut JSObject) -> ${type} {
+ ${type}::new(callback)
+ }
+
+ fn callback(&self) -> *mut JSObject {
+ self.parent.callback()
+ }
+}
+
+impl ToJSValConvertible for ${type} {
+ fn to_jsval(&self, cx: *mut JSContext) -> JSVal {
+ self.callback().to_jsval(cx)
+ }
+}
+""").substitute({"type": callback.name})
+ CGGeneric.__init__(self, impl)
+
+class CGCallbackInterface(CGCallback):
+ def __init__(self, descriptor):
+ iface = descriptor.interface
+ attrs = [m for m in iface.members if m.isAttr() and not m.isStatic()]
+ getters = [CallbackGetter(a, descriptor) for a in attrs]
+ setters = [CallbackSetter(a, descriptor) for a in attrs
+ if not a.readonly]
+ methods = [m for m in iface.members
+ if m.isMethod() and not m.isStatic() and not m.isIdentifierLess()]
+ methods = [CallbackOperation(m, sig, descriptor) for m in methods
+ for sig in m.signatures()]
+ assert not iface.isJSImplemented() or not iface.ctor()
+ CGCallback.__init__(self, iface, descriptor, "CallbackInterface",
+ methods, getters=getters, setters=setters)
+
+class FakeMember():
+ def __init__(self):
+ self.treatNullAs = "Default"
+ def isStatic(self):
+ return False
+ def isAttr(self):
+ return False
+ def isMethod(self):
+ return False
+ def getExtendedAttribute(self, name):
+ return None
+
+class CallbackMember(CGNativeMember):
+ def __init__(self, sig, name, descriptorProvider, needThisHandling, rethrowContentException=False):
+ """
+ needThisHandling is True if we need to be able to accept a specified
+ thisObj, False otherwise.
+ """
+ assert not rethrowContentException or not needThisHandling
+
+ self.retvalType = sig[0]
+ self.originalSig = sig
+ args = sig[1]
+ self.argCount = len(args)
+ if self.argCount > 0:
+ # Check for variadic arguments
+ lastArg = args[self.argCount-1]
+ if lastArg.variadic:
+ self.argCountStr = (
+ "(%d - 1) + %s.Length()" % (self.argCount,
+ lastArg.identifier.name))
+ else:
+ self.argCountStr = "%d" % self.argCount
+ self.needThisHandling = needThisHandling
+ # If needThisHandling, we generate ourselves as private and the caller
+ # will handle generating public versions that handle the "this" stuff.
+ visibility = "priv" if needThisHandling else "pub"
+ self.rethrowContentException = rethrowContentException
+ # We don't care, for callback codegen, whether our original member was
+ # a method or attribute or whatnot. Just always pass FakeMember()
+ # here.
+ CGNativeMember.__init__(self, descriptorProvider, FakeMember(),
+ name, (self.retvalType, args),
+ extendedAttrs={},
+ passJSBitsAsNeeded=False,
+ visibility=visibility,
+ jsObjectsArePtr=True)
+ # We have to do all the generation of our body now, because
+ # the caller relies on us throwing if we can't manage it.
+ self.exceptionCode= "return Err(FailureUnknown);\n"
+ self.body = self.getImpl()
+
+ def getImpl(self):
+ replacements = {
+ "declRval": self.getRvalDecl(),
+ "returnResult": self.getResultConversion(),
+ "convertArgs": self.getArgConversions(),
+ "doCall": self.getCall(),
+ "setupCall": self.getCallSetup(),
+ }
+ if self.argCount > 0:
+ replacements["argCount"] = self.argCountStr
+ replacements["argvDecl"] = string.Template(
+ "let mut argv = Vec::from_elem(${argCount}, UndefinedValue());\n"
+ ).substitute(replacements)
+ else:
+ # Avoid weird 0-sized arrays
+ replacements["argvDecl"] = ""
+
+ # Newlines and semicolons are in the values
+ pre = string.Template(
+ "${setupCall}"
+ "${declRval}"
+ "${argvDecl}").substitute(replacements)
+ body = string.Template(
+ "${convertArgs}"
+ "${doCall}"
+ "${returnResult}").substitute(replacements)
+ return CGList([
+ CGGeneric(pre),
+ CGWrapper(CGIndenter(CGGeneric(body)),
+ pre="with_compartment(cx, self.parent.callback(), || {\n",
+ post="})")
+ ], "\n").define()
+
+ def getResultConversion(self):
+ replacements = {
+ "val": "rval",
+ "declName": "rvalDecl",
+ }
+
+ template, _, declType, needsRooting = getJSToNativeConversionTemplate(
+ self.retvalType,
+ self.descriptorProvider,
+ exceptionCode=self.exceptionCode,
+ isCallbackReturnValue="Callback",
+ # XXXbz we should try to do better here
+ sourceDescription="return value")
+
+ convertType = instantiateJSToNativeConversionTemplate(
+ template, replacements, declType, "rvalDecl", needsRooting)
+
+ assignRetval = string.Template(
+ self.getRetvalInfo(self.retvalType,
+ False)[1]).substitute(replacements)
+ return convertType.define() + "\n" + assignRetval + "\n"
+
+ def getArgConversions(self):
+ # Just reget the arglist from self.originalSig, because our superclasses
+ # just have way to many members they like to clobber, so I can't find a
+ # safe member name to store it in.
+ argConversions = [self.getArgConversion(i, arg) for (i, arg)
+ in enumerate(self.originalSig[1])]
+ # Do them back to front, so our argc modifications will work
+ # correctly, because we examine trailing arguments first.
+ argConversions.reverse();
+ # Wrap each one in a scope so that any locals it has don't leak out, and
+ # also so that we can just "break;" for our successCode.
+ argConversions = [CGWrapper(CGIndenter(CGGeneric(c)),
+ pre="loop {\n",
+ post="\nbreak;}\n")
+ for c in argConversions]
+ if self.argCount > 0:
+ argConversions.insert(0, self.getArgcDecl())
+ # And slap them together.
+ return CGList(argConversions, "\n\n").define() + "\n\n"
+
+ def getArgConversion(self, i, arg):
+ argval = arg.identifier.name
+
+ if arg.variadic:
+ argval = argval + "[idx]"
+ jsvalIndex = "%d + idx" % i
+ else:
+ jsvalIndex = "%d" % i
+ if arg.optional and not arg.defaultValue:
+ argval += ".clone().unwrap()"
+
+ conversion = wrapForType("*argv.get_mut(%s)" % jsvalIndex,
+ result=argval,
+ successCode="continue;" if arg.variadic else "break;")
+ if arg.variadic:
+ conversion = string.Template(
+ "for (uint32_t idx = 0; idx < ${arg}.Length(); ++idx) {\n" +
+ CGIndenter(CGGeneric(conversion)).define() + "\n"
+ "}\n"
+ "break;").substitute({ "arg": arg.identifier.name })
+ elif arg.optional and not arg.defaultValue:
+ conversion = (
+ CGIfWrapper(CGGeneric(conversion),
+ "%s.is_some()" % arg.identifier.name).define() +
+ " else if (argc == %d) {\n"
+ " // This is our current trailing argument; reduce argc\n"
+ " argc -= 1;\n"
+ "} else {\n"
+ " *argv.get_mut(%d) = UndefinedValue();\n"
+ "}" % (i+1, i))
+ return conversion
+
+ def getArgs(self, returnType, argList):
+ args = CGNativeMember.getArgs(self, returnType, argList)
+ if not self.needThisHandling:
+ # Since we don't need this handling, we're the actual method that
+ # will be called, so we need an aRethrowExceptions argument.
+ if self.rethrowContentException:
+ args.append(Argument("JSCompartment*", "aCompartment", "nullptr"))
+ else:
+ args.append(Argument("ExceptionHandling", "aExceptionHandling",
+ "ReportExceptions"))
+ return args
+ # We want to allow the caller to pass in a "this" object, as
+ # well as a JSContext.
+ return [Argument("*mut JSContext", "cx"),
+ Argument("*mut JSObject", "aThisObj")] + args
+
+ def getCallSetup(self):
+ if self.needThisHandling:
+ # It's been done for us already
+ return ""
+ callSetup = "CallSetup s(CallbackPreserveColor(), aRv"
+ if self.rethrowContentException:
+ # getArgs doesn't add the aExceptionHandling argument but does add
+ # aCompartment for us.
+ callSetup += ", RethrowContentExceptions, aCompartment"
+ else:
+ callSetup += ", aExceptionHandling"
+ callSetup += ");"
+ return string.Template(
+ "${callSetup}\n"
+ "JSContext* cx = s.GetContext();\n"
+ "if (!cx) {\n"
+ " return Err(FailureUnknown);\n"
+ "}\n").substitute({
+ "callSetup": callSetup,
+ })
+
+ def getArgcDecl(self):
+ return CGGeneric("let mut argc = %su32;" % self.argCountStr);
+
+ @staticmethod
+ def ensureASCIIName(idlObject):
+ type = "attribute" if idlObject.isAttr() else "operation"
+ if re.match("[^\x20-\x7E]", idlObject.identifier.name):
+ raise SyntaxError('Callback %s name "%s" contains non-ASCII '
+ "characters. We can't handle that. %s" %
+ (type, idlObject.identifier.name,
+ idlObject.location))
+ if re.match('"', idlObject.identifier.name):
+ raise SyntaxError("Callback %s name '%s' contains "
+ "double-quote character. We can't handle "
+ "that. %s" %
+ (type, idlObject.identifier.name,
+ idlObject.location))
+
+class CallbackMethod(CallbackMember):
+ def __init__(self, sig, name, descriptorProvider, needThisHandling, rethrowContentException=False):
+ CallbackMember.__init__(self, sig, name, descriptorProvider,
+ needThisHandling, rethrowContentException)
+ def getRvalDecl(self):
+ return "let mut rval = UndefinedValue();\n"
+
+ def getCall(self):
+ replacements = {
+ "thisObj": self.getThisObj(),
+ "getCallable": self.getCallableDecl()
+ }
+ if self.argCount > 0:
+ replacements["argv"] = "argv.as_mut_ptr()"
+ replacements["argc"] = "argc"
+ else:
+ replacements["argv"] = "nullptr"
+ replacements["argc"] = "0"
+ return string.Template("${getCallable}"
+ "let ok = unsafe {\n"
+ " JS_CallFunctionValue(cx, ${thisObj}, callable,\n"
+ " ${argc}, ${argv}, &mut rval)\n"
+ "};\n"
+ "if ok == 0 {\n"
+ " return Err(FailureUnknown);\n"
+ "}\n").substitute(replacements)
+
+class CallCallback(CallbackMethod):
+ def __init__(self, callback, descriptorProvider):
+ CallbackMethod.__init__(self, callback.signatures()[0], "Call",
+ descriptorProvider, needThisHandling=True)
+
+ def getThisObj(self):
+ return "aThisObj"
+
+ def getCallableDecl(self):
+ return "let callable = ObjectValue(unsafe {&*self.parent.callback()});\n";
+
+class CallbackOperationBase(CallbackMethod):
+ """
+ Common class for implementing various callback operations.
+ """
+ def __init__(self, signature, jsName, nativeName, descriptor, singleOperation, rethrowContentException=False):
+ self.singleOperation = singleOperation
+ self.methodName = jsName
+ CallbackMethod.__init__(self, signature, nativeName, descriptor, singleOperation, rethrowContentException)
+
+ def getThisObj(self):
+ if not self.singleOperation:
+ return "self.parent.callback()"
+ # This relies on getCallableDecl declaring a boolean
+ # isCallable in the case when we're a single-operation
+ # interface.
+ return "if isCallable { aThisObj } else { self.parent.callback() }"
+
+ def getCallableDecl(self):
+ replacements = {
+ "methodName": self.methodName
+ }
+ getCallableFromProp = string.Template(
+ 'match self.parent.GetCallableProperty(cx, "${methodName}") {\n'
+ ' Err(_) => return Err(FailureUnknown),\n'
+ ' Ok(callable) => callable,\n'
+ '}').substitute(replacements)
+ if not self.singleOperation:
+ return 'JS::Rooted<JS::Value> callable(cx);\n' + getCallableFromProp
+ return (
+ 'let isCallable = unsafe { JS_ObjectIsCallable(cx, self.parent.callback()) != 0 };\n'
+ 'let callable =\n' +
+ CGIndenter(
+ CGIfElseWrapper('isCallable',
+ CGGeneric('unsafe { ObjectValue(&*self.parent.callback()) }'),
+ CGGeneric(getCallableFromProp))).define() + ';\n')
+
+class CallbackOperation(CallbackOperationBase):
+ """
+ Codegen actual WebIDL operations on callback interfaces.
+ """
+ def __init__(self, method, signature, descriptor):
+ self.ensureASCIIName(method)
+ jsName = method.identifier.name
+ CallbackOperationBase.__init__(self, signature,
+ jsName, MakeNativeName(jsName),
+ descriptor, descriptor.interface.isSingleOperationInterface(),
+ rethrowContentException=descriptor.interface.isJSImplemented())
+
+class CallbackGetter(CallbackMember):
+ def __init__(self, attr, descriptor):
+ self.ensureASCIIName(attr)
+ self.attrName = attr.identifier.name
+ CallbackMember.__init__(self,
+ (attr.type, []),
+ callbackGetterName(attr),
+ descriptor,
+ needThisHandling=False,
+ rethrowContentException=descriptor.interface.isJSImplemented())
+
+ def getRvalDecl(self):
+ return "JS::Rooted<JS::Value> rval(cx, JS::UndefinedValue());\n"
+
+ def getCall(self):
+ replacements = {
+ "attrName": self.attrName
+ }
+ return string.Template(
+ 'if (!JS_GetProperty(cx, mCallback, "${attrName}", &rval)) {\n'
+ ' return Err(FailureUnknown);\n'
+ '}\n').substitute(replacements);
+
+class CallbackSetter(CallbackMember):
+ def __init__(self, attr, descriptor):
+ self.ensureASCIIName(attr)
+ self.attrName = attr.identifier.name
+ CallbackMember.__init__(self,
+ (BuiltinTypes[IDLBuiltinType.Types.void],
+ [FakeArgument(attr.type, attr)]),
+ callbackSetterName(attr),
+ descriptor,
+ needThisHandling=False,
+ rethrowContentException=descriptor.interface.isJSImplemented())
+
+ def getRvalDecl(self):
+ # We don't need an rval
+ return ""
+
+ def getCall(self):
+ replacements = {
+ "attrName": self.attrName,
+ "argv": "argv.handleAt(0)",
+ }
+ return string.Template(
+ 'MOZ_ASSERT(argv.length() == 1);\n'
+ 'if (!JS_SetProperty(cx, mCallback, "${attrName}", ${argv})) {\n'
+ ' return Err(FailureUnknown);\n'
+ '}\n').substitute(replacements)
+
+ def getArgcDecl(self):
+ return None
+
+class GlobalGenRoots():
+ """
+ Roots for global codegen.
+
+ To generate code, call the method associated with the target, and then
+ call the appropriate define/declare method.
+ """
+
+ @staticmethod
+ def PrototypeList(config):
+ # Prototype ID enum.
+ protos = [d.name for d in config.getDescriptors(isCallback=False)]
+ proxies = [d.name for d in config.getDescriptors(proxy=True)]
+
+ return CGList([
+ CGGeneric(AUTOGENERATED_WARNING_COMMENT),
+ CGGeneric("pub static MAX_PROTO_CHAIN_LENGTH: uint = %d;\n\n" % config.maxProtoChainLength),
+ CGNamespacedEnum('id', 'ID', protos, [0], deriving="PartialEq"),
+ CGNamespacedEnum('proxies', 'Proxy', proxies, [0], deriving="PartialEq"),
+ ])
+
+
+ @staticmethod
+ def RegisterBindings(config):
+ # TODO - Generate the methods we want
+ code = CGList([
+ CGRegisterProtos(config),
+ CGRegisterProxyHandlers(config),
+ ], "\n")
+
+ return CGImports(code, [], [
+ 'dom::bindings::codegen',
+ 'dom::bindings::codegen::PrototypeList::proxies',
+ 'js::jsapi::JSContext',
+ 'js::jsapi::JSObject',
+ 'libc',
+ ])
+
+ @staticmethod
+ def InterfaceTypes(config):
+ descriptors = [d.name for d in config.getDescriptors(register=True, isCallback=False)]
+ curr = CGList([CGGeneric("pub use dom::%s::%s;\n" % (name.lower(), name)) for name in descriptors])
+ curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
+ return curr
+
+ @staticmethod
+ def Bindings(config):
+
+ descriptors = (set(d.name + "Binding" for d in config.getDescriptors(register=True)) |
+ set(d.unroll().module() for d in config.callbacks))
+ curr = CGList([CGGeneric("pub mod %s;\n" % name) for name in sorted(descriptors)])
+ curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
+ return curr
+
+ @staticmethod
+ def InheritTypes(config):
+
+ descriptors = config.getDescriptors(register=True, isCallback=False)
+ allprotos = [CGGeneric("#![allow(unused_imports)]\n"),
+ CGGeneric("use dom::types::*;\n"),
+ CGGeneric("use dom::bindings::js::{JS, JSRef, Temporary};\n"),
+ CGGeneric("use dom::bindings::trace::JSTraceable;\n"),
+ CGGeneric("use dom::bindings::utils::Reflectable;\n"),
+ CGGeneric("use serialize::{Encodable, Encoder};\n"),
+ CGGeneric("use js::jsapi::JSTracer;\n\n")]
+ for descriptor in descriptors:
+ name = descriptor.name
+ protos = [CGGeneric('pub trait %s {}\n' % (name + 'Base'))]
+ for proto in descriptor.prototypeChain:
+ protos += [CGGeneric('impl %s for %s {}\n' % (proto + 'Base',
+ descriptor.concreteType))]
+ derived = [CGGeneric('pub trait %s { fn %s(&self) -> bool; }\n' %
+ (name + 'Derived', 'is_' + name.lower()))]
+ for protoName in descriptor.prototypeChain[1:-1]:
+ protoDescriptor = config.getDescriptor(protoName)
+ delegate = string.Template('''impl ${selfName} for ${baseName} {
+ fn ${fname}(&self) -> bool {
+ self.${parentName}.${fname}()
+ }
+}
+''').substitute({'fname': 'is_' + name.lower(),
+ 'selfName': name + 'Derived',
+ 'baseName': protoDescriptor.concreteType,
+ 'parentName': protoDescriptor.prototypeChain[-2].lower()})
+ derived += [CGGeneric(delegate)]
+ derived += [CGGeneric('\n')]
+
+ cast = [CGGeneric(string.Template('''pub trait ${castTraitName} {
+ #[inline(always)]
+ fn to_ref<'a, 'b, T: ${toBound}+Reflectable>(base: &'a JSRef<'b, T>) -> Option<&'a JSRef<'b, Self>> {
+ match base.deref().${checkFn}() {
+ true => unsafe { Some(base.transmute()) },
+ false => None
+ }
+ }
+
+ #[inline(always)]
+ fn to_mut_ref<'a, 'b, T: ${toBound}+Reflectable>(base: &'a mut JSRef<'b, T>) -> Option<&'a mut JSRef<'b, Self>> {
+ match base.deref().${checkFn}() {
+ true => unsafe { Some(base.transmute_mut()) },
+ false => None
+ }
+ }
+
+ #[inline(always)]
+ fn from_ref<'a, 'b, T: ${fromBound}>(derived: &'a JSRef<'b, T>) -> &'a JSRef<'b, Self> {
+ unsafe { derived.transmute() }
+ }
+
+ #[inline(always)]
+ fn from_mut_ref<'a, 'b, T: ${fromBound}>(derived: &'a mut JSRef<'b, T>) -> &'a mut JSRef<'b, Self> {
+ unsafe { derived.transmute_mut() }
+ }
+
+ #[inline(always)]
+ fn from_temporary<T: ${fromBound}+Reflectable>(derived: Temporary<T>) -> Temporary<Self> {
+ unsafe { derived.transmute() }
+ }
+}
+''').substitute({'checkFn': 'is_' + name.lower(),
+ 'castTraitName': name + 'Cast',
+ 'fromBound': name + 'Base',
+ 'toBound': name + 'Derived'})),
+ CGGeneric("impl %s for %s {}\n\n" % (name + 'Cast', name))]
+
+ trace = [CGGeneric(string.Template('''impl JSTraceable for ${name} {
+ fn trace(&self, tracer: *mut JSTracer) {
+ unsafe {
+ self.encode(&mut *tracer).ok().expect("failed to encode");
+ }
+ }
+}
+''').substitute({'name': name}))]
+
+ allprotos += protos + derived + cast + trace
+
+ curr = CGList(allprotos)
+ curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
+ return curr
+
+ @staticmethod
+ def UnionTypes(config):
+
+ curr = UnionTypes(config.getDescriptors(),
+ config.getDictionaries(),
+ config.getCallbacks(),
+ config)
+
+ # Add the auto-generated comment.
+ curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
+
+ # Done.
+ return curr
diff --git a/components/script/dom/bindings/codegen/Configuration.py b/components/script/dom/bindings/codegen/Configuration.py
new file mode 100644
index 00000000000..d9be43fd2e6
--- /dev/null
+++ b/components/script/dom/bindings/codegen/Configuration.py
@@ -0,0 +1,341 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from WebIDL import IDLInterface
+
+autogenerated_comment = "/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n"
+
+class Configuration:
+ """
+ Represents global configuration state based on IDL parse data and
+ the configuration file.
+ """
+ def __init__(self, filename, parseData):
+ # Read the configuration file.
+ glbl = {}
+ execfile(filename, glbl)
+ config = glbl['DOMInterfaces']
+
+ # Build descriptors for all the interfaces we have in the parse data.
+ # This allows callers to specify a subset of interfaces by filtering
+ # |parseData|.
+ self.descriptors = []
+ self.interfaces = {}
+ self.maxProtoChainLength = 0;
+ for thing in parseData:
+ # Some toplevel things are sadly types, and those have an
+ # isInterface that doesn't mean the same thing as IDLObject's
+ # isInterface()...
+ if not isinstance(thing, IDLInterface):
+ continue
+
+ iface = thing
+ self.interfaces[iface.identifier.name] = iface
+ if iface.identifier.name not in config:
+ # Completely skip consequential interfaces with no descriptor
+ # if they have no interface object because chances are we
+ # don't need to do anything interesting with them.
+ if iface.isConsequential() and not iface.hasInterfaceObject():
+ continue
+ entry = {}
+ else:
+ entry = config[iface.identifier.name]
+ if not isinstance(entry, list):
+ assert isinstance(entry, dict)
+ entry = [entry]
+ self.descriptors.extend([Descriptor(self, iface, x) for x in entry])
+
+ # Mark the descriptors for which only a single nativeType implements
+ # an interface.
+ for descriptor in self.descriptors:
+ intefaceName = descriptor.interface.identifier.name
+ otherDescriptors = [d for d in self.descriptors
+ if d.interface.identifier.name == intefaceName]
+ descriptor.uniqueImplementation = len(otherDescriptors) == 1
+
+ self.enums = [e for e in parseData if e.isEnum()]
+ self.dictionaries = [d for d in parseData if d.isDictionary()]
+ self.callbacks = [c for c in parseData if
+ c.isCallback() and not c.isInterface()]
+
+ # Keep the descriptor list sorted for determinism.
+ self.descriptors.sort(lambda x,y: cmp(x.name, y.name))
+
+ def getInterface(self, ifname):
+ return self.interfaces[ifname]
+ def getDescriptors(self, **filters):
+ """Gets the descriptors that match the given filters."""
+ curr = self.descriptors
+ for key, val in filters.iteritems():
+ if key == 'webIDLFile':
+ getter = lambda x: x.interface.filename()
+ elif key == 'hasInterfaceObject':
+ getter = lambda x: x.interface.hasInterfaceObject()
+ elif key == 'isCallback':
+ getter = lambda x: x.interface.isCallback()
+ elif key == 'isJSImplemented':
+ getter = lambda x: x.interface.isJSImplemented()
+ else:
+ getter = lambda x: getattr(x, key)
+ curr = filter(lambda x: getter(x) == val, curr)
+ return curr
+ def getEnums(self, webIDLFile):
+ return filter(lambda e: e.filename() == webIDLFile, self.enums)
+
+ @staticmethod
+ def _filterForFile(items, webIDLFile=""):
+ """Gets the items that match the given filters."""
+ if not webIDLFile:
+ return items
+
+ return filter(lambda x: x.filename() == webIDLFile, items)
+
+ def getDictionaries(self, webIDLFile=""):
+ return self._filterForFile(self.dictionaries, webIDLFile=webIDLFile)
+ def getCallbacks(self, webIDLFile=""):
+ return self._filterForFile(self.callbacks, webIDLFile=webIDLFile)
+
+ def getDescriptor(self, interfaceName):
+ """
+ Gets the appropriate descriptor for the given interface name.
+ """
+ iface = self.getInterface(interfaceName)
+ descriptors = self.getDescriptors(interface=iface)
+
+ # We should have exactly one result.
+ if len(descriptors) is not 1:
+ raise NoSuchDescriptorError("For " + interfaceName + " found " +
+ str(len(matches)) + " matches");
+ return descriptors[0]
+ def getDescriptorProvider(self):
+ """
+ Gets a descriptor provider that can provide descriptors as needed.
+ """
+ return DescriptorProvider(self)
+
+class NoSuchDescriptorError(TypeError):
+ def __init__(self, str):
+ TypeError.__init__(self, str)
+
+class DescriptorProvider:
+ """
+ A way of getting descriptors for interface names
+ """
+ def __init__(self, config):
+ self.config = config
+
+ def getDescriptor(self, interfaceName):
+ """
+ Gets the appropriate descriptor for the given interface name given the
+ context of the current descriptor.
+ """
+ return self.config.getDescriptor(interfaceName)
+
+class Descriptor(DescriptorProvider):
+ """
+ Represents a single descriptor for an interface. See Bindings.conf.
+ """
+ def __init__(self, config, interface, desc):
+ DescriptorProvider.__init__(self, config)
+ self.interface = interface
+
+ # Read the desc, and fill in the relevant defaults.
+ ifaceName = self.interface.identifier.name
+
+ # Callback types do not use JS smart pointers, so we should not use the
+ # built-in rooting mechanisms for them.
+ if self.interface.isCallback():
+ self.needsRooting = False
+ else:
+ self.needsRooting = True
+
+ self.returnType = desc.get('returnType', "Temporary<%s>" % ifaceName)
+ self.argumentType = "JSRef<%s>" % ifaceName
+ self.memberType = "Root<'a, 'b, %s>" % ifaceName
+ self.nativeType = desc.get('nativeType', 'JS<%s>' % ifaceName)
+ self.concreteType = desc.get('concreteType', ifaceName)
+ self.register = desc.get('register', True)
+ self.outerObjectHook = desc.get('outerObjectHook', 'None')
+
+ # If we're concrete, we need to crawl our ancestor interfaces and mark
+ # them as having a concrete descendant.
+ self.concrete = desc.get('concrete', True)
+ if self.concrete:
+ self.proxy = False
+ operations = {
+ 'IndexedGetter': None,
+ 'IndexedSetter': None,
+ 'IndexedCreator': None,
+ 'IndexedDeleter': None,
+ 'NamedGetter': None,
+ 'NamedSetter': None,
+ 'NamedCreator': None,
+ 'NamedDeleter': None,
+ 'Stringifier': None
+ }
+ iface = self.interface
+ while iface:
+ for m in iface.members:
+ if not m.isMethod():
+ continue
+
+ def addOperation(operation, m):
+ if not operations[operation]:
+ operations[operation] = m
+ def addIndexedOrNamedOperation(operation, m):
+ self.proxy = True
+ if m.isIndexed():
+ operation = 'Indexed' + operation
+ else:
+ assert m.isNamed()
+ operation = 'Named' + operation
+ addOperation(operation, m)
+
+ if m.isStringifier():
+ addOperation('Stringifier', m)
+ else:
+ if m.isGetter():
+ addIndexedOrNamedOperation('Getter', m)
+ if m.isSetter():
+ addIndexedOrNamedOperation('Setter', m)
+ if m.isCreator():
+ addIndexedOrNamedOperation('Creator', m)
+ if m.isDeleter():
+ addIndexedOrNamedOperation('Deleter', m)
+ raise TypeError("deleter specified on %s but we "
+ "don't support deleters yet" %
+ self.interface.identifier.name)
+
+ iface.setUserData('hasConcreteDescendant', True)
+ iface = iface.parent
+
+ if self.proxy:
+ self.operations = operations
+ iface = self.interface
+ while iface:
+ iface.setUserData('hasProxyDescendant', True)
+ iface = iface.parent
+
+ self.name = interface.identifier.name
+
+ # self.extendedAttributes is a dict of dicts, keyed on
+ # all/getterOnly/setterOnly and then on member name. Values are an
+ # array of extended attributes.
+ self.extendedAttributes = { 'all': {}, 'getterOnly': {}, 'setterOnly': {} }
+
+ def addExtendedAttribute(attribute, config):
+ def add(key, members, attribute):
+ for member in members:
+ self.extendedAttributes[key].setdefault(member, []).append(attribute)
+
+ if isinstance(config, dict):
+ for key in ['all', 'getterOnly', 'setterOnly']:
+ add(key, config.get(key, []), attribute)
+ elif isinstance(config, list):
+ add('all', config, attribute)
+ else:
+ assert isinstance(config, str)
+ if config == '*':
+ iface = self.interface
+ while iface:
+ add('all', map(lambda m: m.name, iface.members), attribute)
+ iface = iface.parent
+ else:
+ add('all', [config], attribute)
+
+ # Build the prototype chain.
+ self.prototypeChain = []
+ parent = interface
+ while parent:
+ self.prototypeChain.insert(0, parent.identifier.name)
+ parent = parent.parent
+ config.maxProtoChainLength = max(config.maxProtoChainLength,
+ len(self.prototypeChain))
+
+ def getExtendedAttributes(self, member, getter=False, setter=False):
+ def maybeAppendInfallibleToAttrs(attrs, throws):
+ if throws is None:
+ attrs.append("infallible")
+ elif throws is True:
+ pass
+ else:
+ raise TypeError("Unknown value for 'Throws'")
+
+ name = member.identifier.name
+ if member.isMethod():
+ attrs = self.extendedAttributes['all'].get(name, [])
+ throws = member.getExtendedAttribute("Throws")
+ maybeAppendInfallibleToAttrs(attrs, throws)
+ return attrs
+
+ assert member.isAttr()
+ assert bool(getter) != bool(setter)
+ key = 'getterOnly' if getter else 'setterOnly'
+ attrs = self.extendedAttributes['all'].get(name, []) + self.extendedAttributes[key].get(name, [])
+ throws = member.getExtendedAttribute("Throws")
+ if throws is None:
+ throwsAttr = "GetterThrows" if getter else "SetterThrows"
+ throws = member.getExtendedAttribute(throwsAttr)
+ maybeAppendInfallibleToAttrs(attrs, throws)
+ return attrs
+
+ def isGlobal(self):
+ """
+ Returns true if this is the primary interface for a global object
+ of some sort.
+ """
+ return (self.interface.getExtendedAttribute("Global") or
+ self.interface.getExtendedAttribute("PrimaryGlobal"))
+
+
+# Some utility methods
+def getTypesFromDescriptor(descriptor):
+ """
+ Get all argument and return types for all members of the descriptor
+ """
+ members = [m for m in descriptor.interface.members]
+ if descriptor.interface.ctor():
+ members.append(descriptor.interface.ctor())
+ members.extend(descriptor.interface.namedConstructors)
+ signatures = [s for m in members if m.isMethod() for s in m.signatures()]
+ types = []
+ for s in signatures:
+ assert len(s) == 2
+ (returnType, arguments) = s
+ types.append(returnType)
+ types.extend(a.type for a in arguments)
+
+ types.extend(a.type for a in members if a.isAttr())
+ return types
+
+def getFlatTypes(types):
+ retval = set()
+ for type in types:
+ type = type.unroll()
+ if type.isUnion():
+ retval |= set(type.flatMemberTypes)
+ else:
+ retval.add(type)
+ return retval
+
+def getTypesFromDictionary(dictionary):
+ """
+ Get all member types for this dictionary
+ """
+ types = []
+ curDict = dictionary
+ while curDict:
+ types.extend([m.type for m in curDict.members])
+ curDict = curDict.parent
+ return types
+
+def getTypesFromCallback(callback):
+ """
+ Get the types this callback depends on: its return type and the
+ types of its arguments.
+ """
+ sig = callback.signatures()[0]
+ types = [sig[0]] # Return type
+ types.extend(arg.type for arg in sig[1]) # Arguments
+ return types
diff --git a/components/script/dom/bindings/codegen/DOMJSClass.h b/components/script/dom/bindings/codegen/DOMJSClass.h
new file mode 100644
index 00000000000..151960b5901
--- /dev/null
+++ b/components/script/dom/bindings/codegen/DOMJSClass.h
@@ -0,0 +1,114 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_DOMJSClass_h
+#define mozilla_dom_DOMJSClass_h
+
+#include "jsapi.h"
+#include "jsfriendapi.h"
+
+#include "mozilla/dom/PrototypeList.h" // auto-generated
+
+// We use slot 0 for holding the raw object. This is safe for both
+// globals and non-globals.
+#define DOM_OBJECT_SLOT 0
+
+// All DOM globals must have a slot at DOM_PROTOTYPE_SLOT. We have to
+// start at 1 past JSCLASS_GLOBAL_SLOT_COUNT because XPConnect uses
+// that one.
+#define DOM_PROTOTYPE_SLOT (JSCLASS_GLOBAL_SLOT_COUNT + 1)
+
+// We use these flag bits for the new bindings.
+#define JSCLASS_DOM_GLOBAL JSCLASS_USERBIT1
+
+// NOTE: This is baked into the Ion JIT as 0 in codegen for LGetDOMProperty and
+// LSetDOMProperty. Those constants need to be changed accordingly if this value
+// changes.
+#define DOM_PROTO_INSTANCE_CLASS_SLOT 0
+
+namespace mozilla {
+namespace dom {
+
+typedef bool
+(* ResolveProperty)(JSContext* cx, JSObject* wrapper, jsid id, bool set,
+ JSPropertyDescriptor* desc);
+typedef bool
+(* EnumerateProperties)(JSContext* cx, JSObject* wrapper,
+ JS::AutoIdVector& props);
+
+struct NativePropertyHooks
+{
+ ResolveProperty mResolveOwnProperty;
+ ResolveProperty mResolveProperty;
+ EnumerateProperties mEnumerateOwnProperties;
+ EnumerateProperties mEnumerateProperties;
+
+ const NativePropertyHooks *mProtoHooks;
+};
+
+struct DOMClass
+{
+ // A list of interfaces that this object implements, in order of decreasing
+ // derivedness.
+ const prototypes::ID mInterfaceChain[prototypes::id::_ID_Count];
+
+ // We store the DOM object in reserved slot with index DOM_OBJECT_SLOT or in
+ // the proxy private if we use a proxy object.
+ // Sometimes it's an nsISupports and sometimes it's not; this class tells
+ // us which it is.
+ const bool mDOMObjectIsISupports;
+
+ const NativePropertyHooks* mNativeHooks;
+};
+
+// Special JSClass for reflected DOM objects.
+struct DOMJSClass
+{
+ // It would be nice to just inherit from JSClass, but that precludes pure
+ // compile-time initialization of the form |DOMJSClass = {...};|, since C++
+ // only allows brace initialization for aggregate/POD types.
+ JSClass mBase;
+
+ DOMClass mClass;
+
+ static DOMJSClass* FromJSClass(JSClass* base) {
+ MOZ_ASSERT(base->flags & JSCLASS_IS_DOMJSCLASS);
+ return reinterpret_cast<DOMJSClass*>(base);
+ }
+ static const DOMJSClass* FromJSClass(const JSClass* base) {
+ MOZ_ASSERT(base->flags & JSCLASS_IS_DOMJSCLASS);
+ return reinterpret_cast<const DOMJSClass*>(base);
+ }
+
+ static DOMJSClass* FromJSClass(js::Class* base) {
+ return FromJSClass(Jsvalify(base));
+ }
+ static const DOMJSClass* FromJSClass(const js::Class* base) {
+ return FromJSClass(Jsvalify(base));
+ }
+
+ JSClass* ToJSClass() { return &mBase; }
+};
+
+inline bool
+HasProtoOrIfaceArray(JSObject* global)
+{
+ MOZ_ASSERT(js::GetObjectClass(global)->flags & JSCLASS_DOM_GLOBAL);
+ // This can be undefined if we GC while creating the global
+ return !js::GetReservedSlot(global, DOM_PROTOTYPE_SLOT).isUndefined();
+}
+
+inline JSObject**
+GetProtoOrIfaceArray(JSObject* global)
+{
+ MOZ_ASSERT(js::GetObjectClass(global)->flags & JSCLASS_DOM_GLOBAL);
+ return static_cast<JSObject**>(
+ js::GetReservedSlot(global, DOM_PROTOTYPE_SLOT).toPrivate());
+}
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_DOMJSClass_h */
diff --git a/components/script/dom/bindings/codegen/DOMJSProxyHandler.cpp b/components/script/dom/bindings/codegen/DOMJSProxyHandler.cpp
new file mode 100644
index 00000000000..af45cc6ed1a
--- /dev/null
+++ b/components/script/dom/bindings/codegen/DOMJSProxyHandler.cpp
@@ -0,0 +1,247 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=2 sw=2 et tw=99 ft=cpp: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/Util.h"
+
+#include "DOMJSProxyHandler.h"
+#include "xpcpublic.h"
+#include "xpcprivate.h"
+#include "XPCQuickStubs.h"
+#include "XPCWrapper.h"
+#include "WrapperFactory.h"
+#include "nsDOMClassInfo.h"
+#include "nsGlobalWindow.h"
+#include "nsWrapperCacheInlines.h"
+#include "mozilla/dom/BindingUtils.h"
+
+#include "jsapi.h"
+
+using namespace JS;
+
+namespace mozilla {
+namespace dom {
+
+jsid s_length_id = JSID_VOID;
+
+bool
+DefineStaticJSVals(JSContext* cx)
+{
+ JSAutoRequest ar(cx);
+
+ return InternJSString(cx, s_length_id, "length");
+}
+
+
+int HandlerFamily;
+
+// Store the information for the specialized ICs.
+struct SetListBaseInformation
+{
+ SetListBaseInformation() {
+ js::SetListBaseInformation((void*) &HandlerFamily, js::JSSLOT_PROXY_EXTRA + JSPROXYSLOT_EXPANDO);
+ }
+};
+
+SetListBaseInformation gSetListBaseInformation;
+
+
+bool
+DefineConstructor(JSContext* cx, JSObject* obj, DefineInterface aDefine, nsresult* aResult)
+{
+ bool enabled;
+ bool defined = aDefine(cx, obj, &enabled);
+ MOZ_ASSERT(!defined || enabled,
+ "We defined a constructor but the new bindings are disabled?");
+ *aResult = defined ? NS_OK : NS_ERROR_FAILURE;
+ return enabled;
+}
+
+// static
+JSObject*
+DOMProxyHandler::EnsureExpandoObject(JSContext* cx, JSObject* obj)
+{
+ NS_ASSERTION(IsDOMProxy(obj), "expected a DOM proxy object");
+ JSObject* expando = GetExpandoObject(obj);
+ if (!expando) {
+ expando = JS_NewObjectWithGivenProto(cx, nullptr, nullptr,
+ js::GetObjectParent(obj));
+ if (!expando) {
+ return NULL;
+ }
+
+ xpc::CompartmentPrivate* priv = xpc::GetCompartmentPrivate(obj);
+ if (!priv->RegisterDOMExpandoObject(obj)) {
+ return NULL;
+ }
+
+ nsWrapperCache* cache;
+ CallQueryInterface(UnwrapDOMObject<nsISupports>(obj, eProxyDOMObject), &cache);
+ cache->SetPreservingWrapper(true);
+
+ js::SetProxyExtra(obj, JSPROXYSLOT_EXPANDO, ObjectValue(*expando));
+ }
+ return expando;
+}
+
+bool
+DOMProxyHandler::getPropertyDescriptor(JSContext* cx, JSObject* proxy, jsid id, bool set,
+ JSPropertyDescriptor* desc)
+{
+ if (!getOwnPropertyDescriptor(cx, proxy, id, set, desc)) {
+ return false;
+ }
+ if (desc->obj) {
+ return true;
+ }
+
+ JSObject* proto;
+ if (!js::GetObjectProto(cx, proxy, &proto)) {
+ return false;
+ }
+ if (!proto) {
+ desc->obj = NULL;
+ return true;
+ }
+
+ return JS_GetPropertyDescriptorById(cx, proto, id, JSRESOLVE_QUALIFIED, desc);
+}
+
+bool
+DOMProxyHandler::defineProperty(JSContext* cx, JSObject* proxy, jsid id,
+ JSPropertyDescriptor* desc)
+{
+ if ((desc->attrs & JSPROP_GETTER) && desc->setter == JS_StrictPropertyStub) {
+ return JS_ReportErrorFlagsAndNumber(cx,
+ JSREPORT_WARNING | JSREPORT_STRICT |
+ JSREPORT_STRICT_MODE_ERROR,
+ js_GetErrorMessage, NULL,
+ JSMSG_GETTER_ONLY);
+ }
+
+ if (xpc::WrapperFactory::IsXrayWrapper(proxy)) {
+ return true;
+ }
+
+ JSObject* expando = EnsureExpandoObject(cx, proxy);
+ if (!expando) {
+ return false;
+ }
+
+ return JS_DefinePropertyById(cx, expando, id, desc->value, desc->getter, desc->setter,
+ desc->attrs);
+}
+
+bool
+DOMProxyHandler::delete_(JSContext* cx, JSObject* proxy, jsid id, bool* bp)
+{
+ JSBool b = true;
+
+ JSObject* expando;
+ if (!xpc::WrapperFactory::IsXrayWrapper(proxy) && (expando = GetExpandoObject(proxy))) {
+ Value v;
+ if (!JS_DeletePropertyById2(cx, expando, id, &v) || !JS_ValueToBoolean(cx, v, &b)) {
+ return false;
+ }
+ }
+
+ *bp = !!b;
+ return true;
+}
+
+bool
+DOMProxyHandler::enumerate(JSContext* cx, JSObject* proxy, AutoIdVector& props)
+{
+ JSObject* proto;
+ if (!JS_GetPrototype(cx, proxy, &proto)) {
+ return false;
+ }
+ return getOwnPropertyNames(cx, proxy, props) &&
+ (!proto || js::GetPropertyNames(cx, proto, 0, &props));
+}
+
+bool
+DOMProxyHandler::fix(JSContext* cx, JSObject* proxy, Value* vp)
+{
+ vp->setUndefined();
+ return true;
+}
+
+bool
+DOMProxyHandler::has(JSContext* cx, JSObject* proxy, jsid id, bool* bp)
+{
+ if (!hasOwn(cx, proxy, id, bp)) {
+ return false;
+ }
+
+ if (*bp) {
+ // We have the property ourselves; no need to worry about our prototype
+ // chain.
+ return true;
+ }
+
+ // OK, now we have to look at the proto
+ JSObject *proto;
+ if (!js::GetObjectProto(cx, proxy, &proto)) {
+ return false;
+ }
+ if (!proto) {
+ return true;
+ }
+ JSBool protoHasProp;
+ bool ok = JS_HasPropertyById(cx, proto, id, &protoHasProp);
+ if (ok) {
+ *bp = protoHasProp;
+ }
+ return ok;
+}
+
+// static
+JSString*
+DOMProxyHandler::obj_toString(JSContext* cx, const char* className)
+{
+ size_t nchars = sizeof("[object ]") - 1 + strlen(className);
+ jschar* chars = static_cast<jschar*>(JS_malloc(cx, (nchars + 1) * sizeof(jschar)));
+ if (!chars) {
+ return NULL;
+ }
+
+ const char* prefix = "[object ";
+ nchars = 0;
+ while ((chars[nchars] = (jschar)*prefix) != 0) {
+ nchars++, prefix++;
+ }
+ while ((chars[nchars] = (jschar)*className) != 0) {
+ nchars++, className++;
+ }
+ chars[nchars++] = ']';
+ chars[nchars] = 0;
+
+ JSString* str = JS_NewUCString(cx, chars, nchars);
+ if (!str) {
+ JS_free(cx, chars);
+ }
+ return str;
+}
+
+int32_t
+IdToInt32(JSContext* cx, jsid id)
+{
+ JSAutoRequest ar(cx);
+
+ jsval idval;
+ double array_index;
+ int32_t i;
+ if (!::JS_IdToValue(cx, id, &idval) ||
+ !::JS_ValueToNumber(cx, idval, &array_index) ||
+ !::JS_DoubleIsInt32(array_index, &i)) {
+ return -1;
+ }
+
+ return i;
+}
+
+} // namespace dom
+} // namespace mozilla
diff --git a/components/script/dom/bindings/codegen/DOMJSProxyHandler.h b/components/script/dom/bindings/codegen/DOMJSProxyHandler.h
new file mode 100644
index 00000000000..394e2dc4d2f
--- /dev/null
+++ b/components/script/dom/bindings/codegen/DOMJSProxyHandler.h
@@ -0,0 +1,109 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_DOMJSProxyHandler_h
+#define mozilla_dom_DOMJSProxyHandler_h
+
+#include "jsapi.h"
+#include "jsfriendapi.h"
+#include "jsproxy.h"
+#include "xpcpublic.h"
+#include "nsString.h"
+#include "mozilla/Likely.h"
+
+#define DOM_PROXY_OBJECT_SLOT js::JSSLOT_PROXY_PRIVATE
+
+namespace mozilla {
+namespace dom {
+
+enum {
+ JSPROXYSLOT_EXPANDO = 0
+};
+
+template<typename T> struct Prefable;
+
+class DOMProxyHandler : public DOMBaseProxyHandler
+{
+public:
+ DOMProxyHandler(const DOMClass& aClass)
+ : DOMBaseProxyHandler(true),
+ mClass(aClass)
+ {
+ }
+
+ bool getPropertyDescriptor(JSContext* cx, JSObject* proxy, jsid id, bool set,
+ JSPropertyDescriptor* desc);
+ bool defineProperty(JSContext* cx, JSObject* proxy, jsid id,
+ JSPropertyDescriptor* desc);
+ bool delete_(JSContext* cx, JSObject* proxy, jsid id, bool* bp);
+ bool enumerate(JSContext* cx, JSObject* proxy, JS::AutoIdVector& props);
+ bool fix(JSContext* cx, JSObject* proxy, JS::Value* vp);
+ bool has(JSContext* cx, JSObject* proxy, jsid id, bool* bp);
+ using js::BaseProxyHandler::obj_toString;
+
+ static JSObject* GetExpandoObject(JSObject* obj)
+ {
+ MOZ_ASSERT(IsDOMProxy(obj), "expected a DOM proxy object");
+ JS::Value v = js::GetProxyExtra(obj, JSPROXYSLOT_EXPANDO);
+ return v.isUndefined() ? NULL : v.toObjectOrNull();
+ }
+ static JSObject* EnsureExpandoObject(JSContext* cx, JSObject* obj);
+
+ const DOMClass& mClass;
+
+protected:
+ static JSString* obj_toString(JSContext* cx, const char* className);
+};
+
+extern jsid s_length_id;
+
+int32_t IdToInt32(JSContext* cx, jsid id);
+
+inline int32_t
+GetArrayIndexFromId(JSContext* cx, jsid id)
+{
+ if (MOZ_LIKELY(JSID_IS_INT(id))) {
+ return JSID_TO_INT(id);
+ }
+ if (MOZ_LIKELY(id == s_length_id)) {
+ return -1;
+ }
+ if (MOZ_LIKELY(JSID_IS_ATOM(id))) {
+ JSAtom* atom = JSID_TO_ATOM(id);
+ jschar s = *js::GetAtomChars(atom);
+ if (MOZ_LIKELY((unsigned)s >= 'a' && (unsigned)s <= 'z'))
+ return -1;
+
+ uint32_t i;
+ JSLinearString* str = js::AtomToLinearString(JSID_TO_ATOM(id));
+ return js::StringIsArrayIndex(str, &i) ? i : -1;
+ }
+ return IdToInt32(cx, id);
+}
+
+inline void
+FillPropertyDescriptor(JSPropertyDescriptor* desc, JSObject* obj, bool readonly)
+{
+ desc->obj = obj;
+ desc->attrs = (readonly ? JSPROP_READONLY : 0) | JSPROP_ENUMERATE;
+ desc->getter = NULL;
+ desc->setter = NULL;
+ desc->shortid = 0;
+}
+
+inline void
+FillPropertyDescriptor(JSPropertyDescriptor* desc, JSObject* obj, jsval v, bool readonly)
+{
+ desc->value = v;
+ FillPropertyDescriptor(desc, obj, readonly);
+}
+
+JSObject*
+EnsureExpandoObject(JSContext* cx, JSObject* obj);
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_DOMProxyHandler_h */
diff --git a/components/script/dom/bindings/codegen/ErrorResult.h b/components/script/dom/bindings/codegen/ErrorResult.h
new file mode 100644
index 00000000000..bbd9404a865
--- /dev/null
+++ b/components/script/dom/bindings/codegen/ErrorResult.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
+/* vim: set ts=2 sw=2 et tw=79: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * A struct for tracking exceptions that need to be thrown to JS.
+ */
+
+#ifndef mozilla_ErrorResult_h
+#define mozilla_ErrorResult_h
+
+#include "nscore.h"
+#include "mozilla/Assertions.h"
+
+namespace mozilla {
+
+class ErrorResult {
+public:
+ ErrorResult() {
+ mResult = NS_OK;
+ }
+
+ void Throw(nsresult rv) {
+ MOZ_ASSERT(NS_FAILED(rv), "Please don't try throwing success");
+ mResult = rv;
+ }
+
+ // In the future, we can add overloads of Throw that take more
+ // interesting things, like strings or DOM exception types or
+ // something if desired.
+
+ // Backwards-compat to make conversion simpler. We don't call
+ // Throw() here because people can easily pass success codes to
+ // this.
+ void operator=(nsresult rv) {
+ mResult = rv;
+ }
+
+ bool Failed() const {
+ return NS_FAILED(mResult);
+ }
+
+ nsresult ErrorCode() const {
+ return mResult;
+ }
+
+private:
+ nsresult mResult;
+
+ // Not to be implemented, to make sure people always pass this by
+ // reference, not by value.
+ ErrorResult(const ErrorResult&) MOZ_DELETE;
+};
+
+} // namespace mozilla
+
+#endif /* mozilla_ErrorResult_h */
diff --git a/components/script/dom/bindings/codegen/Errors.msg b/components/script/dom/bindings/codegen/Errors.msg
new file mode 100644
index 00000000000..81d6624cec8
--- /dev/null
+++ b/components/script/dom/bindings/codegen/Errors.msg
@@ -0,0 +1,30 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * The format for each error message is:
+ *
+ * MSG_DEF(<SYMBOLIC_NAME>, <ARGUMENT_COUNT>, <FORMAT_STRING>)
+ *
+ * where
+ *
+ * <SYMBOLIC_NAME> is a legal C++ identifer that will be used in the source.
+ *
+ * <ARGUMENT_COUNT> is an integer literal specifying the total number of
+ * replaceable arguments in the following format string.
+ *
+ * <FORMAT_STRING> is a string literal, containing <ARGUMENT_COUNT> sequences
+ * {X} where X is an integer representing the argument number that will
+ * be replaced with a string value when the error is reported.
+ */
+
+MSG_DEF(MSG_INVALID_ENUM_VALUE, 2, "Value '{0}' is not a valid value for enumeration {1}.")
+MSG_DEF(MSG_MISSING_ARGUMENTS, 1, "Not enough arguments to {0}.")
+MSG_DEF(MSG_NOT_OBJECT, 0, "Value not an object.")
+MSG_DEF(MSG_DOES_NOT_IMPLEMENT_INTERFACE, 1, "Value does not implement interface {0}.")
+MSG_DEF(MSG_NOT_IN_UNION, 1, "Value could not be converted to any of: {0}.")
+MSG_DEF(MSG_ILLEGAL_CONSTRUCTOR, 0, "Illegal constructor.")
+MSG_DEF(MSG_NO_PROPERTY_SETTER, 1, "{0} doesn't have an indexed property setter.")
+MSG_DEF(MSG_ENFORCE_RANGE_NON_FINITE, 1, "Non-finite value is out of range for {0}.")
+MSG_DEF(MSG_ENFORCE_RANGE_OUT_OF_RANGE, 1, "Value is out of range for {0}.")
diff --git a/components/script/dom/bindings/codegen/GenerateCSS2PropertiesWebIDL.py b/components/script/dom/bindings/codegen/GenerateCSS2PropertiesWebIDL.py
new file mode 100644
index 00000000000..1bb50afaee7
--- /dev/null
+++ b/components/script/dom/bindings/codegen/GenerateCSS2PropertiesWebIDL.py
@@ -0,0 +1,26 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import sys
+import string
+
+propList = eval(sys.stdin.read())
+props = ""
+for [prop, pref] in propList:
+ extendedAttrs = ["Throws", "TreatNullAs=EmptyString"]
+ if pref is not "":
+ extendedAttrs.append("Pref=%s" % pref)
+ if not prop.startswith("Moz"):
+ prop = prop[0].lower() + prop[1:]
+ # Unfortunately, even some of the getters here are fallible
+ # (e.g. on nsComputedDOMStyle).
+ props += " [%s] attribute DOMString %s;\n" % (", ".join(extendedAttrs),
+ prop)
+
+idlFile = open(sys.argv[1], "r");
+idlTemplate = idlFile.read();
+idlFile.close();
+
+print ("/* THIS IS AN AUTOGENERATED FILE. DO NOT EDIT */\n\n" +
+ string.Template(idlTemplate).substitute({ "props": props }))
diff --git a/components/script/dom/bindings/codegen/GlobalGen.py b/components/script/dom/bindings/codegen/GlobalGen.py
new file mode 100644
index 00000000000..cdca464e029
--- /dev/null
+++ b/components/script/dom/bindings/codegen/GlobalGen.py
@@ -0,0 +1,83 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at http://mozilla.org/MPL/2.0/.
+
+# We do one global pass over all the WebIDL to generate our prototype enum
+# and generate information for subsequent phases.
+
+import sys
+sys.path.append("./parser/")
+sys.path.append("./ply/")
+import os
+import cStringIO
+import WebIDL
+import cPickle
+from Configuration import *
+from CodegenRust import GlobalGenRoots, replaceFileIfChanged
+# import Codegen in general, so we can set a variable on it
+import Codegen
+
+def generate_file(config, name, filename):
+ root = getattr(GlobalGenRoots, name)(config)
+ code = root.define()
+
+ if replaceFileIfChanged(filename, code):
+ print "Generating %s" % (filename)
+ else:
+ print "%s hasn't changed - not touching it" % (filename)
+
+def main():
+ # Parse arguments.
+ from optparse import OptionParser
+ usageString = "usage: %prog [options] webidldir [files]"
+ o = OptionParser(usage=usageString)
+ o.add_option("--cachedir", dest='cachedir', default=None,
+ help="Directory in which to cache lex/parse tables.")
+ o.add_option("--verbose-errors", action='store_true', default=False,
+ help="When an error happens, display the Python traceback.")
+ (options, args) = o.parse_args()
+
+ if len(args) < 2:
+ o.error(usageString)
+
+ configFile = args[0]
+ baseDir = args[1]
+ fileList = args[2:]
+
+ # Parse the WebIDL.
+ parser = WebIDL.Parser(options.cachedir)
+ for filename in fileList:
+ fullPath = os.path.normpath(os.path.join(baseDir, filename))
+ f = open(fullPath, 'rb')
+ lines = f.readlines()
+ f.close()
+ parser.parse(''.join(lines), fullPath)
+ parserResults = parser.finish()
+
+ # Write the parser results out to a pickle.
+ resultsFile = open('ParserResults.pkl', 'wb')
+ cPickle.dump(parserResults, resultsFile, -1)
+ resultsFile.close()
+
+ # Load the configuration.
+ config = Configuration(configFile, parserResults)
+
+ # Generate the prototype list.
+ generate_file(config, 'PrototypeList', 'PrototypeList.rs')
+
+ # Generate the common code.
+ generate_file(config, 'RegisterBindings', 'RegisterBindings.rs')
+
+ # Generate the type list.
+ generate_file(config, 'InterfaceTypes', 'InterfaceTypes.rs')
+
+ # Generate the type list.
+ generate_file(config, 'InheritTypes', 'InheritTypes.rs')
+
+ # Generate the module declarations.
+ generate_file(config, 'Bindings', 'Bindings/mod.rs')
+
+ generate_file(config, 'UnionTypes', 'UnionTypes.rs')
+
+if __name__ == '__main__':
+ main()
diff --git a/components/script/dom/bindings/codegen/Makefile.in b/components/script/dom/bindings/codegen/Makefile.in
new file mode 100644
index 00000000000..5fef1e77218
--- /dev/null
+++ b/components/script/dom/bindings/codegen/Makefile.in
@@ -0,0 +1,165 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DEPTH = @DEPTH@
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+FAIL_ON_WARNINGS := 1
+
+MODULE = dom
+LIBRARY_NAME = dombindings_s
+LIBXUL_LIBRARY = 1
+FORCE_STATIC_LIB = 1
+EXPORT_LIBRARY = 1
+
+include $(topsrcdir)/config/config.mk
+
+# Need this to find all our DOM source files.
+include $(topsrcdir)/dom/dom-config.mk
+
+include $(topsrcdir)/dom/webidl/WebIDL.mk
+
+binding_include_path := mozilla/dom
+all_webidl_files = $(webidl_files) $(generated_webidl_files)
+# Set exported_binding_headers before adding the test IDL to the mix
+exported_binding_headers := $(subst .webidl,Binding.h,$(all_webidl_files))
+# Set linked_binding_cpp_files before adding the test IDL to the mix
+linked_binding_cpp_files := $(subst .webidl,Binding.cpp,$(all_webidl_files))
+
+all_webidl_files += $(test_webidl_files)
+
+binding_header_files := $(subst .webidl,Binding.h,$(all_webidl_files))
+binding_cpp_files := $(subst .webidl,Binding.cpp,$(all_webidl_files))
+
+globalgen_targets := \
+ PrototypeList.h \
+ RegisterBindings.h \
+ RegisterBindings.cpp \
+ UnionTypes.h \
+ UnionConversions.h \
+ $(NULL)
+
+CPPSRCS = \
+ $(linked_binding_cpp_files) \
+ $(filter %.cpp, $(globalgen_targets)) \
+ BindingUtils.cpp \
+ DOMJSProxyHandler.cpp \
+ $(NULL)
+
+EXPORTS_NAMESPACES = $(binding_include_path) mozilla
+
+EXPORTS_mozilla = \
+ ErrorResult.h \
+ $(NULL)
+
+EXPORTS_$(binding_include_path) = \
+ BindingUtils.h \
+ DOMJSClass.h \
+ DOMJSProxyHandler.h \
+ Errors.msg \
+ Nullable.h \
+ PrimitiveConversions.h \
+ PrototypeList.h \
+ RegisterBindings.h \
+ TypedArray.h \
+ UnionConversions.h \
+ UnionTypes.h \
+ $(exported_binding_headers) \
+ $(NULL)
+
+LOCAL_INCLUDES += -I$(topsrcdir)/js/xpconnect/src \
+ -I$(topsrcdir)/js/xpconnect/wrappers \
+ -I$(topsrcdir)/content/canvas/src \
+ -I$(topsrcdir)/content/html/content/src
+
+include $(topsrcdir)/config/rules.mk
+
+# If you change bindinggen_dependencies here, change it in
+# dom/bindings/test/Makefile.in too.
+bindinggen_dependencies := \
+ BindingGen.py \
+ Bindings.conf \
+ Configuration.py \
+ Codegen.py \
+ parser/WebIDL.py \
+ ParserResults.pkl \
+ $(GLOBAL_DEPS) \
+ $(NULL)
+
+CSS2Properties.webidl: $(topsrcdir)/layout/style/nsCSSPropList.h \
+ $(topsrcdir)/layout/style/nsCSSPropAliasList.h \
+ $(webidl_base)/CSS2Properties.webidl.in \
+ $(webidl_base)/CSS2PropertiesProps.h \
+ $(srcdir)/GenerateCSS2PropertiesWebIDL.py \
+ $(GLOBAL_DEPS)
+ $(CPP) $(DEFINES) $(ACDEFINES) -I$(topsrcdir)/layout/style $(webidl_base)/CSS2PropertiesProps.h | \
+ $(PYTHON) \
+ $(srcdir)/GenerateCSS2PropertiesWebIDL.py $(webidl_base)/CSS2Properties.webidl.in > CSS2Properties.webidl
+
+$(webidl_files): %: $(webidl_base)/%
+ $(INSTALL) $(IFLAGS1) $(webidl_base)/$* .
+
+$(test_webidl_files): %: $(srcdir)/test/%
+ $(INSTALL) $(IFLAGS1) $(srcdir)/test/$* .
+
+$(binding_header_files): %Binding.h: $(bindinggen_dependencies) \
+ %.webidl \
+ $(NULL)
+ $(PYTHON) $(topsrcdir)/config/pythonpath.py \
+ $(PLY_INCLUDE) -I$(srcdir)/parser \
+ $(srcdir)/BindingGen.py header \
+ $(srcdir)/Bindings.conf $*Binding \
+ $*.webidl
+
+$(binding_cpp_files): %Binding.cpp: $(bindinggen_dependencies) \
+ %.webidl \
+ $(NULL)
+ $(PYTHON) $(topsrcdir)/config/pythonpath.py \
+ $(PLY_INCLUDE) -I$(srcdir)/parser \
+ $(srcdir)/BindingGen.py cpp \
+ $(srcdir)/Bindings.conf $*Binding \
+ $*.webidl
+
+$(globalgen_targets): ParserResults.pkl
+
+CACHE_DIR = _cache
+
+globalgen_dependencies := \
+ GlobalGen.py \
+ Bindings.conf \
+ Configuration.py \
+ Codegen.py \
+ parser/WebIDL.py \
+ $(CACHE_DIR)/.done \
+ $(GLOBAL_DEPS) \
+ $(NULL)
+
+$(CACHE_DIR)/.done:
+ $(MKDIR) -p $(CACHE_DIR)
+ @$(TOUCH) $@
+
+ParserResults.pkl: $(globalgen_dependencies) \
+ $(all_webidl_files)
+ $(PYTHON) $(topsrcdir)/config/pythonpath.py \
+ $(PLY_INCLUDE) -I$(srcdir)/parser \
+ $(srcdir)/GlobalGen.py $(srcdir)/Bindings.conf . \
+ --cachedir=$(CACHE_DIR) \
+ $(all_webidl_files)
+
+GARBAGE += \
+ $(binding_header_files) \
+ $(binding_cpp_files) \
+ $(all_webidl_files) \
+ $(globalgen_targets) \
+ ParserResults.pkl \
+ webidlyacc.py \
+ parser.out \
+ $(NULL)
+
+# Make sure all binding header files are created during the export stage, so we
+# don't have issues with .cpp files being compiled before we've generated the
+# headers they depend on. This is really only needed for the test files, since
+# the non-test headers are all exported above anyway.
+export:: $(binding_header_files)
diff --git a/components/script/dom/bindings/codegen/Nullable.h b/components/script/dom/bindings/codegen/Nullable.h
new file mode 100644
index 00000000000..8b2cc08642b
--- /dev/null
+++ b/components/script/dom/bindings/codegen/Nullable.h
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
+/* vim: set ts=2 sw=2 et tw=79: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_Nullable_h
+#define mozilla_dom_Nullable_h
+
+#include "mozilla/Assertions.h"
+
+namespace mozilla {
+namespace dom {
+
+// Support for nullable types
+template <typename T>
+struct Nullable
+{
+private:
+ T mValue;
+ bool mIsNull;
+
+public:
+ Nullable()
+ : mIsNull(true)
+ {}
+
+ Nullable(T aValue)
+ : mValue(aValue)
+ , mIsNull(false)
+ {}
+
+ void SetValue(T aValue) {
+ mValue = aValue;
+ mIsNull = false;
+ }
+
+ // For cases when |T| is some type with nontrivial copy behavior, we may want
+ // to get a reference to our internal copy of T and work with it directly
+ // instead of relying on the copying version of SetValue().
+ T& SetValue() {
+ mIsNull = false;
+ return mValue;
+ }
+
+ void SetNull() {
+ mIsNull = true;
+ }
+
+ const T& Value() const {
+ MOZ_ASSERT(!mIsNull);
+ return mValue;
+ }
+
+ T& Value() {
+ MOZ_ASSERT(!mIsNull);
+ return mValue;
+ }
+
+ bool IsNull() const {
+ return mIsNull;
+ }
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_Nullable_h */
diff --git a/components/script/dom/bindings/codegen/PrimitiveConversions.h b/components/script/dom/bindings/codegen/PrimitiveConversions.h
new file mode 100644
index 00000000000..40c27425772
--- /dev/null
+++ b/components/script/dom/bindings/codegen/PrimitiveConversions.h
@@ -0,0 +1,350 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
+/* vim: set ts=2 sw=2 et tw=79: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Conversions from jsval to primitive values
+ */
+
+#ifndef mozilla_dom_PrimitiveConversions_h
+#define mozilla_dom_PrimitiveConversions_h
+
+#include <limits>
+#include <math.h>
+#include "mozilla/Assertions.h"
+#include "mozilla/dom/BindingUtils.h"
+#include "mozilla/FloatingPoint.h"
+#include "xpcpublic.h"
+
+namespace mozilla {
+namespace dom {
+
+template<typename T>
+struct TypeName {
+};
+
+template<>
+struct TypeName<int8_t> {
+ static const char* value() {
+ return "byte";
+ }
+};
+template<>
+struct TypeName<uint8_t> {
+ static const char* value() {
+ return "octet";
+ }
+};
+template<>
+struct TypeName<int16_t> {
+ static const char* value() {
+ return "short";
+ }
+};
+template<>
+struct TypeName<uint16_t> {
+ static const char* value() {
+ return "unsigned short";
+ }
+};
+template<>
+struct TypeName<int32_t> {
+ static const char* value() {
+ return "long";
+ }
+};
+template<>
+struct TypeName<uint32_t> {
+ static const char* value() {
+ return "unsigned long";
+ }
+};
+template<>
+struct TypeName<int64_t> {
+ static const char* value() {
+ return "long long";
+ }
+};
+template<>
+struct TypeName<uint64_t> {
+ static const char* value() {
+ return "unsigned long long";
+ }
+};
+
+
+enum ConversionBehavior {
+ eDefault,
+ eEnforceRange,
+ eClamp
+};
+
+template<typename T, ConversionBehavior B>
+struct PrimitiveConversionTraits {
+};
+
+template<typename T>
+struct DisallowedConversion {
+ typedef int jstype;
+ typedef int intermediateType;
+
+private:
+ static inline bool converter(JSContext* cx, JS::Value v, jstype* retval) {
+ MOZ_NOT_REACHED("This should never be instantiated!");
+ return false;
+ }
+};
+
+struct PrimitiveConversionTraits_smallInt {
+ // The output of JS::ToInt32 is determined as follows:
+ // 1) The value is converted to a double
+ // 2) Anything that's not a finite double returns 0
+ // 3) The double is rounded towards zero to the nearest integer
+ // 4) The resulting integer is reduced mod 2^32. The output of this
+ // operation is an integer in the range [0, 2^32).
+ // 5) If the resulting number is >= 2^31, 2^32 is subtracted from it.
+ //
+ // The result of all this is a number in the range [-2^31, 2^31)
+ //
+ // WebIDL conversions for the 8-bit, 16-bit, and 32-bit integer types
+ // are defined in the same way, except that step 4 uses reduction mod
+ // 2^8 and 2^16 for the 8-bit and 16-bit types respectively, and step 5
+ // is only done for the signed types.
+ //
+ // C/C++ define integer conversion semantics to unsigned types as taking
+ // your input integer mod (1 + largest value representable in the
+ // unsigned type). Since 2^32 is zero mod 2^8, 2^16, and 2^32,
+ // converting to the unsigned int of the relevant width will correctly
+ // perform step 4; in particular, the 2^32 possibly subtracted in step 5
+ // will become 0.
+ //
+ // Once we have step 4 done, we're just going to assume 2s-complement
+ // representation and cast directly to the type we really want.
+ //
+ // So we can cast directly for all unsigned types and for int32_t; for
+ // the smaller-width signed types we need to cast through the
+ // corresponding unsigned type.
+ typedef int32_t jstype;
+ typedef int32_t intermediateType;
+ static inline bool converter(JSContext* cx, JS::Value v, jstype* retval) {
+ return JS::ToInt32(cx, v, retval);
+ }
+};
+template<>
+struct PrimitiveConversionTraits<int8_t, eDefault> : PrimitiveConversionTraits_smallInt {
+ typedef uint8_t intermediateType;
+};
+template<>
+struct PrimitiveConversionTraits<uint8_t, eDefault> : PrimitiveConversionTraits_smallInt {
+};
+template<>
+struct PrimitiveConversionTraits<int16_t, eDefault> : PrimitiveConversionTraits_smallInt {
+ typedef uint16_t intermediateType;
+};
+template<>
+struct PrimitiveConversionTraits<uint16_t, eDefault> : PrimitiveConversionTraits_smallInt {
+};
+template<>
+struct PrimitiveConversionTraits<int32_t, eDefault> : PrimitiveConversionTraits_smallInt {
+};
+template<>
+struct PrimitiveConversionTraits<uint32_t, eDefault> : PrimitiveConversionTraits_smallInt {
+};
+
+template<>
+struct PrimitiveConversionTraits<int64_t, eDefault> {
+ typedef int64_t jstype;
+ typedef int64_t intermediateType;
+ static inline bool converter(JSContext* cx, JS::Value v, jstype* retval) {
+ return JS::ToInt64(cx, v, retval);
+ }
+};
+
+template<>
+struct PrimitiveConversionTraits<uint64_t, eDefault> {
+ typedef uint64_t jstype;
+ typedef uint64_t intermediateType;
+ static inline bool converter(JSContext* cx, JS::Value v, jstype* retval) {
+ return JS::ToUint64(cx, v, retval);
+ }
+};
+
+template<typename T>
+struct PrimitiveConversionTraits_Limits {
+ static inline T min() {
+ return std::numeric_limits<T>::min();
+ }
+ static inline T max() {
+ return std::numeric_limits<T>::max();
+ }
+};
+
+template<>
+struct PrimitiveConversionTraits_Limits<int64_t> {
+ static inline int64_t min() {
+ return -(1LL << 53) + 1;
+ }
+ static inline int64_t max() {
+ return (1LL << 53) - 1;
+ }
+};
+
+template<>
+struct PrimitiveConversionTraits_Limits<uint64_t> {
+ static inline uint64_t min() {
+ return 0;
+ }
+ static inline uint64_t max() {
+ return (1LL << 53) - 1;
+ }
+};
+
+template<typename T, bool (*Enforce)(JSContext* cx, const double& d, T* retval)>
+struct PrimitiveConversionTraits_ToCheckedIntHelper {
+ typedef T jstype;
+ typedef T intermediateType;
+
+ static inline bool converter(JSContext* cx, JS::Value v, jstype* retval) {
+ double intermediate;
+ if (!JS::ToNumber(cx, v, &intermediate)) {
+ return false;
+ }
+
+ return Enforce(cx, intermediate, retval);
+ }
+};
+
+template<typename T>
+inline bool
+PrimitiveConversionTraits_EnforceRange(JSContext* cx, const double& d, T* retval)
+{
+ MOZ_STATIC_ASSERT(std::numeric_limits<T>::is_integer,
+ "This can only be applied to integers!");
+
+ if (!MOZ_DOUBLE_IS_FINITE(d)) {
+ return ThrowErrorMessage(cx, MSG_ENFORCE_RANGE_NON_FINITE, TypeName<T>::value());
+ }
+
+ bool neg = (d < 0);
+ double rounded = floor(neg ? -d : d);
+ rounded = neg ? -rounded : rounded;
+ if (rounded < PrimitiveConversionTraits_Limits<T>::min() ||
+ rounded > PrimitiveConversionTraits_Limits<T>::max()) {
+ return ThrowErrorMessage(cx, MSG_ENFORCE_RANGE_OUT_OF_RANGE, TypeName<T>::value());
+ }
+
+ *retval = static_cast<T>(rounded);
+ return true;
+}
+
+template<typename T>
+struct PrimitiveConversionTraits<T, eEnforceRange> :
+ public PrimitiveConversionTraits_ToCheckedIntHelper<T, PrimitiveConversionTraits_EnforceRange<T> > {
+};
+
+template<typename T>
+inline bool
+PrimitiveConversionTraits_Clamp(JSContext* cx, const double& d, T* retval)
+{
+ MOZ_STATIC_ASSERT(std::numeric_limits<T>::is_integer,
+ "This can only be applied to integers!");
+
+ if (MOZ_DOUBLE_IS_NaN(d)) {
+ *retval = 0;
+ return true;
+ }
+ if (d >= PrimitiveConversionTraits_Limits<T>::max()) {
+ *retval = PrimitiveConversionTraits_Limits<T>::max();
+ return true;
+ }
+ if (d <= PrimitiveConversionTraits_Limits<T>::min()) {
+ *retval = PrimitiveConversionTraits_Limits<T>::min();
+ return true;
+ }
+
+ MOZ_ASSERT(MOZ_DOUBLE_IS_FINITE(d));
+
+ // Banker's rounding (round ties towards even).
+ // We move away from 0 by 0.5f and then truncate. That gets us the right
+ // answer for any starting value except plus or minus N.5. With a starting
+ // value of that form, we now have plus or minus N+1. If N is odd, this is
+ // the correct result. If N is even, plus or minus N is the correct result.
+ double toTruncate = (d < 0) ? d - 0.5 : d + 0.5;
+
+ T truncated(toTruncate);
+
+ if (truncated == toTruncate) {
+ /*
+ * It was a tie (since moving away from 0 by 0.5 gave us the exact integer
+ * we want). Since we rounded away from 0, we either already have an even
+ * number or we have an odd number but the number we want is one closer to
+ * 0. So just unconditionally masking out the ones bit should do the trick
+ * to get us the value we want.
+ */
+ truncated &= ~1;
+ }
+
+ *retval = truncated;
+ return true;
+}
+
+template<typename T>
+struct PrimitiveConversionTraits<T, eClamp> :
+ public PrimitiveConversionTraits_ToCheckedIntHelper<T, PrimitiveConversionTraits_Clamp<T> > {
+};
+
+
+template<ConversionBehavior B>
+struct PrimitiveConversionTraits<bool, B> : public DisallowedConversion<bool> {};
+
+template<>
+struct PrimitiveConversionTraits<bool, eDefault> {
+ typedef JSBool jstype;
+ typedef bool intermediateType;
+ static inline bool converter(JSContext* /* unused */, JS::Value v, jstype* retval) {
+ *retval = JS::ToBoolean(v);
+ return true;
+ }
+};
+
+
+template<ConversionBehavior B>
+struct PrimitiveConversionTraits<float, B> : public DisallowedConversion<float> {};
+
+template<ConversionBehavior B>
+struct PrimitiveConversionTraits<double, B> : public DisallowedConversion<double> {};
+
+struct PrimitiveConversionTraits_float {
+ typedef double jstype;
+ typedef double intermediateType;
+ static inline bool converter(JSContext* cx, JS::Value v, jstype* retval) {
+ return JS::ToNumber(cx, v, retval);
+ }
+};
+
+template<>
+struct PrimitiveConversionTraits<float, eDefault> : PrimitiveConversionTraits_float {
+};
+template<>
+struct PrimitiveConversionTraits<double, eDefault> : PrimitiveConversionTraits_float {
+};
+
+
+template<typename T, ConversionBehavior B>
+bool ValueToPrimitive(JSContext* cx, JS::Value v, T* retval)
+{
+ typename PrimitiveConversionTraits<T, B>::jstype t;
+ if (!PrimitiveConversionTraits<T, B>::converter(cx, v, &t))
+ return false;
+
+ *retval =
+ static_cast<typename PrimitiveConversionTraits<T, B>::intermediateType>(t);
+ return true;
+}
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_PrimitiveConversions_h */
diff --git a/components/script/dom/bindings/codegen/RegisterBindings.h b/components/script/dom/bindings/codegen/RegisterBindings.h
new file mode 100644
index 00000000000..7d83a747cc3
--- /dev/null
+++ b/components/script/dom/bindings/codegen/RegisterBindings.h
@@ -0,0 +1,14 @@
+#ifndef mozilla_dom_RegisterBindings_h__
+#define mozilla_dom_RegisterBindings_h__
+
+
+namespace mozilla {
+namespace dom {
+void
+Register(nsScriptNameSpaceManager* aNameSpaceManager);
+
+} // namespace dom
+} // namespace mozilla
+
+
+#endif // mozilla_dom_RegisterBindings_h__
diff --git a/components/script/dom/bindings/codegen/TypedArray.h b/components/script/dom/bindings/codegen/TypedArray.h
new file mode 100644
index 00000000000..2a6f17bcb96
--- /dev/null
+++ b/components/script/dom/bindings/codegen/TypedArray.h
@@ -0,0 +1,121 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
+/* vim: set ts=2 sw=2 et tw=79: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_TypedArray_h
+#define mozilla_dom_TypedArray_h
+
+#include "jsfriendapi.h"
+
+namespace mozilla {
+namespace dom {
+
+/*
+ * Various typed array classes for argument conversion. We have a base class
+ * that has a way of initializing a TypedArray from an existing typed array, and
+ * a subclass of the base class that supports creation of a relevant typed array
+ * or array buffer object.
+ */
+template<typename T,
+ JSObject* UnboxArray(JSContext*, JSObject*, uint32_t*, T**)>
+struct TypedArray_base {
+ TypedArray_base(JSContext* cx, JSObject* obj)
+ {
+ mObj = UnboxArray(cx, obj, &mLength, &mData);
+ }
+
+private:
+ T* mData;
+ uint32_t mLength;
+ JSObject* mObj;
+
+public:
+ inline bool inited() const {
+ return !!mObj;
+ }
+
+ inline T *Data() const {
+ MOZ_ASSERT(inited());
+ return mData;
+ }
+
+ inline uint32_t Length() const {
+ MOZ_ASSERT(inited());
+ return mLength;
+ }
+
+ inline JSObject *Obj() const {
+ MOZ_ASSERT(inited());
+ return mObj;
+ }
+};
+
+
+template<typename T,
+ T* GetData(JSObject*, JSContext*),
+ JSObject* UnboxArray(JSContext*, JSObject*, uint32_t*, T**),
+ JSObject* CreateNew(JSContext*, uint32_t)>
+struct TypedArray : public TypedArray_base<T,UnboxArray> {
+ TypedArray(JSContext* cx, JSObject* obj) :
+ TypedArray_base<T,UnboxArray>(cx, obj)
+ {}
+
+ static inline JSObject*
+ Create(JSContext* cx, nsWrapperCache* creator, uint32_t length,
+ const T* data = NULL) {
+ JSObject* creatorWrapper;
+ Maybe<JSAutoCompartment> ac;
+ if (creator && (creatorWrapper = creator->GetWrapperPreserveColor())) {
+ ac.construct(cx, creatorWrapper);
+ }
+ JSObject* obj = CreateNew(cx, length);
+ if (!obj) {
+ return NULL;
+ }
+ if (data) {
+ T* buf = static_cast<T*>(GetData(obj, cx));
+ memcpy(buf, data, length*sizeof(T));
+ }
+ return obj;
+ }
+};
+
+typedef TypedArray<int8_t, JS_GetInt8ArrayData, JS_GetObjectAsInt8Array,
+ JS_NewInt8Array>
+ Int8Array;
+typedef TypedArray<uint8_t, JS_GetUint8ArrayData,
+ JS_GetObjectAsUint8Array, JS_NewUint8Array>
+ Uint8Array;
+typedef TypedArray<uint8_t, JS_GetUint8ClampedArrayData,
+ JS_GetObjectAsUint8ClampedArray, JS_NewUint8ClampedArray>
+ Uint8ClampedArray;
+typedef TypedArray<int16_t, JS_GetInt16ArrayData,
+ JS_GetObjectAsInt16Array, JS_NewInt16Array>
+ Int16Array;
+typedef TypedArray<uint16_t, JS_GetUint16ArrayData,
+ JS_GetObjectAsUint16Array, JS_NewUint16Array>
+ Uint16Array;
+typedef TypedArray<int32_t, JS_GetInt32ArrayData,
+ JS_GetObjectAsInt32Array, JS_NewInt32Array>
+ Int32Array;
+typedef TypedArray<uint32_t, JS_GetUint32ArrayData,
+ JS_GetObjectAsUint32Array, JS_NewUint32Array>
+ Uint32Array;
+typedef TypedArray<float, JS_GetFloat32ArrayData,
+ JS_GetObjectAsFloat32Array, JS_NewFloat32Array>
+ Float32Array;
+typedef TypedArray<double, JS_GetFloat64ArrayData,
+ JS_GetObjectAsFloat64Array, JS_NewFloat64Array>
+ Float64Array;
+typedef TypedArray_base<uint8_t, JS_GetObjectAsArrayBufferView>
+ ArrayBufferView;
+typedef TypedArray<uint8_t, JS_GetArrayBufferData,
+ JS_GetObjectAsArrayBuffer, JS_NewArrayBuffer>
+ ArrayBuffer;
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_TypedArray_h */
diff --git a/components/script/dom/bindings/codegen/crashtests/769464.html b/components/script/dom/bindings/codegen/crashtests/769464.html
new file mode 100644
index 00000000000..84d6dbc08b4
--- /dev/null
+++ b/components/script/dom/bindings/codegen/crashtests/769464.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<script>
+
+function boom()
+{
+ window.getComputedStyle(new Worker("404.js"));
+}
+
+window.addEventListener("load", boom, false);
+
+</script>
diff --git a/components/script/dom/bindings/codegen/crashtests/crashtests.list b/components/script/dom/bindings/codegen/crashtests/crashtests.list
new file mode 100644
index 00000000000..cb954bd91fc
--- /dev/null
+++ b/components/script/dom/bindings/codegen/crashtests/crashtests.list
@@ -0,0 +1 @@
+asserts-if(cocoaWidget,0-1) load 769464.html
diff --git a/components/script/dom/bindings/codegen/parser/README b/components/script/dom/bindings/codegen/parser/README
new file mode 100644
index 00000000000..94b64b88459
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/README
@@ -0,0 +1 @@
+A WebIDL parser written in Python to be used in Mozilla. \ No newline at end of file
diff --git a/components/script/dom/bindings/codegen/parser/UPSTREAM b/components/script/dom/bindings/codegen/parser/UPSTREAM
new file mode 100644
index 00000000000..7ac5899379e
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/UPSTREAM
@@ -0,0 +1 @@
+http://dev.w3.org/cvsweb/~checkout~/2006/webapi/WebIDL/Overview.html?rev=1.409;content-type=text%2Fhtml%3b+charset=utf-8 \ No newline at end of file
diff --git a/components/script/dom/bindings/codegen/parser/WebIDL.py b/components/script/dom/bindings/codegen/parser/WebIDL.py
new file mode 100644
index 00000000000..32f80e82c56
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/WebIDL.py
@@ -0,0 +1,5583 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+""" A WebIDL parser. """
+
+from ply import lex, yacc
+import re
+import os
+import traceback
+import math
+from collections import defaultdict
+
+# Machinery
+
+def parseInt(literal):
+ string = literal
+ sign = 0
+ base = 0
+
+ if string[0] == '-':
+ sign = -1
+ string = string[1:]
+ else:
+ sign = 1
+
+ if string[0] == '0' and len(string) > 1:
+ if string[1] == 'x' or string[1] == 'X':
+ base = 16
+ string = string[2:]
+ else:
+ base = 8
+ string = string[1:]
+ else:
+ base = 10
+
+ value = int(string, base)
+ return value * sign
+
+# Magic for creating enums
+def M_add_class_attribs(attribs, start):
+ def foo(name, bases, dict_):
+ for v, k in enumerate(attribs):
+ dict_[k] = start + v
+ assert 'length' not in dict_
+ dict_['length'] = start + len(attribs)
+ return type(name, bases, dict_)
+ return foo
+
+def enum(*names, **kw):
+ if len(kw) == 1:
+ base = kw['base'].__class__
+ start = base.length
+ else:
+ assert len(kw) == 0
+ base = object
+ start = 0
+ class Foo(base):
+ __metaclass__ = M_add_class_attribs(names, start)
+ def __setattr__(self, name, value): # this makes it read-only
+ raise NotImplementedError
+ return Foo()
+
+class WebIDLError(Exception):
+ def __init__(self, message, locations, warning=False):
+ self.message = message
+ self.locations = [str(loc) for loc in locations]
+ self.warning = warning
+
+ def __str__(self):
+ return "%s: %s%s%s" % (self.warning and 'warning' or 'error',
+ self.message,
+ ", " if len(self.locations) != 0 else "",
+ "\n".join(self.locations))
+
+class Location(object):
+ def __init__(self, lexer, lineno, lexpos, filename):
+ self._line = None
+ self._lineno = lineno
+ self._lexpos = lexpos
+ self._lexdata = lexer.lexdata
+ self._file = filename if filename else "<unknown>"
+
+ def __eq__(self, other):
+ return self._lexpos == other._lexpos and \
+ self._file == other._file
+
+ def filename(self):
+ return self._file
+
+ def resolve(self):
+ if self._line:
+ return
+
+ startofline = self._lexdata.rfind('\n', 0, self._lexpos) + 1
+ endofline = self._lexdata.find('\n', self._lexpos, self._lexpos + 80)
+ if endofline != -1:
+ self._line = self._lexdata[startofline:endofline]
+ else:
+ self._line = self._lexdata[startofline:]
+ self._colno = self._lexpos - startofline
+
+ # Our line number seems to point to the start of self._lexdata
+ self._lineno += self._lexdata.count('\n', 0, startofline)
+
+ def get(self):
+ self.resolve()
+ return "%s line %s:%s" % (self._file, self._lineno, self._colno)
+
+ def _pointerline(self):
+ return " " * self._colno + "^"
+
+ def __str__(self):
+ self.resolve()
+ return "%s line %s:%s\n%s\n%s" % (self._file, self._lineno, self._colno,
+ self._line, self._pointerline())
+
+class BuiltinLocation(object):
+ def __init__(self, text):
+ self.msg = text + "\n"
+
+ def __eq__(self, other):
+ return isinstance(other, BuiltinLocation) and \
+ self.msg == other.msg
+
+ def filename(self):
+ return '<builtin>'
+
+ def resolve(self):
+ pass
+
+ def get(self):
+ return self.msg
+
+ def __str__(self):
+ return self.get()
+
+
+# Data Model
+
+class IDLObject(object):
+ def __init__(self, location):
+ self.location = location
+ self.userData = dict()
+
+ def filename(self):
+ return self.location.filename()
+
+ def isInterface(self):
+ return False
+
+ def isEnum(self):
+ return False
+
+ def isCallback(self):
+ return False
+
+ def isType(self):
+ return False
+
+ def isDictionary(self):
+ return False;
+
+ def isUnion(self):
+ return False
+
+ def getUserData(self, key, default):
+ return self.userData.get(key, default)
+
+ def setUserData(self, key, value):
+ self.userData[key] = value
+
+ def addExtendedAttributes(self, attrs):
+ assert False # Override me!
+
+ def handleExtendedAttribute(self, attr):
+ assert False # Override me!
+
+ def _getDependentObjects(self):
+ assert False # Override me!
+
+ def getDeps(self, visited=None):
+ """ Return a set of files that this object depends on. If any of
+ these files are changed the parser needs to be rerun to regenerate
+ a new IDLObject.
+
+ The visited argument is a set of all the objects already visited.
+ We must test to see if we are in it, and if so, do nothing. This
+ prevents infinite recursion."""
+
+ # NB: We can't use visited=set() above because the default value is
+ # evaluated when the def statement is evaluated, not when the function
+ # is executed, so there would be one set for all invocations.
+ if visited == None:
+ visited = set()
+
+ if self in visited:
+ return set()
+
+ visited.add(self)
+
+ deps = set()
+ if self.filename() != "<builtin>":
+ deps.add(self.filename())
+
+ for d in self._getDependentObjects():
+ deps = deps.union(d.getDeps(visited))
+
+ return deps
+
+class IDLScope(IDLObject):
+ def __init__(self, location, parentScope, identifier):
+ IDLObject.__init__(self, location)
+
+ self.parentScope = parentScope
+ if identifier:
+ assert isinstance(identifier, IDLIdentifier)
+ self._name = identifier
+ else:
+ self._name = None
+
+ self._dict = {}
+ self.globalNames = set()
+ # A mapping from global name to the set of global interfaces
+ # that have that global name.
+ self.globalNameMapping = defaultdict(set)
+ self.primaryGlobalAttr = None
+ self.primaryGlobalName = None
+
+ def __str__(self):
+ return self.QName()
+
+ def QName(self):
+ if self._name:
+ return self._name.QName() + "::"
+ return "::"
+
+ def ensureUnique(self, identifier, object):
+ """
+ Ensure that there is at most one 'identifier' in scope ('self').
+ Note that object can be None. This occurs if we end up here for an
+ interface type we haven't seen yet.
+ """
+ assert isinstance(identifier, IDLUnresolvedIdentifier)
+ assert not object or isinstance(object, IDLObjectWithIdentifier)
+ assert not object or object.identifier == identifier
+
+ if identifier.name in self._dict:
+ if not object:
+ return
+
+ # ensureUnique twice with the same object is not allowed
+ assert id(object) != id(self._dict[identifier.name])
+
+ replacement = self.resolveIdentifierConflict(self, identifier,
+ self._dict[identifier.name],
+ object)
+ self._dict[identifier.name] = replacement
+ return
+
+ assert object
+
+ self._dict[identifier.name] = object
+
+ def resolveIdentifierConflict(self, scope, identifier, originalObject, newObject):
+ if isinstance(originalObject, IDLExternalInterface) and \
+ isinstance(newObject, IDLExternalInterface) and \
+ originalObject.identifier.name == newObject.identifier.name:
+ return originalObject
+
+ if (isinstance(originalObject, IDLExternalInterface) or
+ isinstance(newObject, IDLExternalInterface)):
+ raise WebIDLError(
+ "Name collision between "
+ "interface declarations for identifier '%s' at '%s' and '%s'"
+ % (identifier.name,
+ originalObject.location, newObject.location), [])
+
+ if (isinstance(originalObject, IDLDictionary) or
+ isinstance(newObject, IDLDictionary)):
+ raise WebIDLError(
+ "Name collision between dictionary declarations for "
+ "identifier '%s'.\n%s\n%s"
+ % (identifier.name,
+ originalObject.location, newObject.location), [])
+
+ # We do the merging of overloads here as opposed to in IDLInterface
+ # because we need to merge overloads of NamedConstructors and we need to
+ # detect conflicts in those across interfaces. See also the comment in
+ # IDLInterface.addExtendedAttributes for "NamedConstructor".
+ if originalObject.tag == IDLInterfaceMember.Tags.Method and \
+ newObject.tag == IDLInterfaceMember.Tags.Method:
+ return originalObject.addOverload(newObject)
+
+ # Default to throwing, derived classes can override.
+ conflictdesc = "\n\t%s at %s\n\t%s at %s" % \
+ (originalObject, originalObject.location, newObject, newObject.location)
+
+ raise WebIDLError(
+ "Multiple unresolvable definitions of identifier '%s' in scope '%s%s"
+ % (identifier.name, str(self), conflictdesc), [])
+
+ def _lookupIdentifier(self, identifier):
+ return self._dict[identifier.name]
+
+ def lookupIdentifier(self, identifier):
+ assert isinstance(identifier, IDLIdentifier)
+ assert identifier.scope == self
+ return self._lookupIdentifier(identifier)
+
+class IDLIdentifier(IDLObject):
+ def __init__(self, location, scope, name):
+ IDLObject.__init__(self, location)
+
+ self.name = name
+ assert isinstance(scope, IDLScope)
+ self.scope = scope
+
+ def __str__(self):
+ return self.QName()
+
+ def QName(self):
+ return self.scope.QName() + self.name
+
+ def __hash__(self):
+ return self.QName().__hash__()
+
+ def __eq__(self, other):
+ return self.QName() == other.QName()
+
+ def object(self):
+ return self.scope.lookupIdentifier(self)
+
+class IDLUnresolvedIdentifier(IDLObject):
+ def __init__(self, location, name, allowDoubleUnderscore = False,
+ allowForbidden = False):
+ IDLObject.__init__(self, location)
+
+ assert len(name) > 0
+
+ if name[:2] == "__" and name != "__content" and name != "___noSuchMethod__" and not allowDoubleUnderscore:
+ raise WebIDLError("Identifiers beginning with __ are reserved",
+ [location])
+ if name[0] == '_' and not allowDoubleUnderscore:
+ name = name[1:]
+ # TODO: Bug 872377, Restore "toJSON" to below list.
+ # We sometimes need custom serialization, so allow toJSON for now.
+ if (name in ["constructor", "toString"] and
+ not allowForbidden):
+ raise WebIDLError("Cannot use reserved identifier '%s'" % (name),
+ [location])
+
+ self.name = name
+
+ def __str__(self):
+ return self.QName()
+
+ def QName(self):
+ return "<unresolved scope>::" + self.name
+
+ def resolve(self, scope, object):
+ assert isinstance(scope, IDLScope)
+ assert not object or isinstance(object, IDLObjectWithIdentifier)
+ assert not object or object.identifier == self
+
+ scope.ensureUnique(self, object)
+
+ identifier = IDLIdentifier(self.location, scope, self.name)
+ if object:
+ object.identifier = identifier
+ return identifier
+
+ def finish(self):
+ assert False # Should replace with a resolved identifier first.
+
+class IDLObjectWithIdentifier(IDLObject):
+ def __init__(self, location, parentScope, identifier):
+ IDLObject.__init__(self, location)
+
+ assert isinstance(identifier, IDLUnresolvedIdentifier)
+
+ self.identifier = identifier
+
+ if parentScope:
+ self.resolve(parentScope)
+
+ self.treatNullAs = "Default"
+
+ def resolve(self, parentScope):
+ assert isinstance(parentScope, IDLScope)
+ assert isinstance(self.identifier, IDLUnresolvedIdentifier)
+ self.identifier.resolve(parentScope, self)
+
+ def checkForStringHandlingExtendedAttributes(self, attrs,
+ isDictionaryMember=False,
+ isOptional=False):
+ """
+ A helper function to deal with TreatNullAs. Returns the list
+ of attrs it didn't handle itself.
+ """
+ assert isinstance(self, IDLArgument) or isinstance(self, IDLAttribute)
+ unhandledAttrs = list()
+ for attr in attrs:
+ if not attr.hasValue():
+ unhandledAttrs.append(attr)
+ continue
+
+ identifier = attr.identifier()
+ value = attr.value()
+ if identifier == "TreatNullAs":
+ if not self.type.isDOMString() or self.type.nullable():
+ raise WebIDLError("[TreatNullAs] is only allowed on "
+ "arguments or attributes whose type is "
+ "DOMString",
+ [self.location])
+ if isDictionaryMember:
+ raise WebIDLError("[TreatNullAs] is not allowed for "
+ "dictionary members", [self.location])
+ if value != 'EmptyString':
+ raise WebIDLError("[TreatNullAs] must take the identifier "
+ "'EmptyString', not '%s'" % value,
+ [self.location])
+ self.treatNullAs = value
+ else:
+ unhandledAttrs.append(attr)
+
+ return unhandledAttrs
+
+class IDLObjectWithScope(IDLObjectWithIdentifier, IDLScope):
+ def __init__(self, location, parentScope, identifier):
+ assert isinstance(identifier, IDLUnresolvedIdentifier)
+
+ IDLObjectWithIdentifier.__init__(self, location, parentScope, identifier)
+ IDLScope.__init__(self, location, parentScope, self.identifier)
+
+class IDLIdentifierPlaceholder(IDLObjectWithIdentifier):
+ def __init__(self, location, identifier):
+ assert isinstance(identifier, IDLUnresolvedIdentifier)
+ IDLObjectWithIdentifier.__init__(self, location, None, identifier)
+
+ def finish(self, scope):
+ try:
+ scope._lookupIdentifier(self.identifier)
+ except:
+ raise WebIDLError("Unresolved type '%s'." % self.identifier,
+ [self.location])
+
+ obj = self.identifier.resolve(scope, None)
+ return scope.lookupIdentifier(obj)
+
+class IDLExternalInterface(IDLObjectWithIdentifier):
+ def __init__(self, location, parentScope, identifier):
+ raise WebIDLError("Servo does not support external interfaces.",
+ [self.location])
+
+class IDLPartialInterface(IDLObject):
+ def __init__(self, location, name, members, nonPartialInterface):
+ assert isinstance(name, IDLUnresolvedIdentifier)
+
+ IDLObject.__init__(self, location)
+ self.identifier = name
+ self.members = members
+ # propagatedExtendedAttrs are the ones that should get
+ # propagated to our non-partial interface.
+ self.propagatedExtendedAttrs = []
+ self._nonPartialInterface = nonPartialInterface
+ self._finished = False
+ nonPartialInterface.addPartialInterface(self)
+
+ def addExtendedAttributes(self, attrs):
+ for attr in attrs:
+ identifier = attr.identifier()
+
+ if identifier in ["Constructor", "NamedConstructor"]:
+ self.propagatedExtendedAttrs.append(attr)
+ elif identifier == "Exposed":
+ # This just gets propagated to all our members.
+ for member in self.members:
+ if len(member._exposureGlobalNames) != 0:
+ raise WebIDLError("[Exposed] specified on both a "
+ "partial interface member and on the "
+ "partial interface itself",
+ [member.location, attr.location])
+ member.addExtendedAttributes([attr])
+ else:
+ raise WebIDLError("Unknown extended attribute %s on partial "
+ "interface" % identifier,
+ [attr.location])
+
+ def finish(self, scope):
+ if self._finished:
+ return
+ self._finished = True
+ # Need to make sure our non-partial interface gets finished so it can
+ # report cases when we only have partial interfaces.
+ self._nonPartialInterface.finish(scope)
+
+ def validate(self):
+ pass
+
+
+def convertExposedAttrToGlobalNameSet(exposedAttr, targetSet):
+ assert len(targetSet) == 0
+ if exposedAttr.hasValue():
+ targetSet.add(exposedAttr.value())
+ else:
+ assert exposedAttr.hasArgs()
+ targetSet.update(exposedAttr.args())
+
+def globalNameSetToExposureSet(globalScope, nameSet, exposureSet):
+ for name in nameSet:
+ exposureSet.update(globalScope.globalNameMapping[name])
+
+class IDLInterface(IDLObjectWithScope):
+ def __init__(self, location, parentScope, name, parent, members,
+ isKnownNonPartial):
+ assert isinstance(parentScope, IDLScope)
+ assert isinstance(name, IDLUnresolvedIdentifier)
+ assert isKnownNonPartial or not parent
+ assert isKnownNonPartial or len(members) == 0
+
+ self.parent = None
+ self._callback = False
+ self._finished = False
+ self.members = []
+ self._partialInterfaces = []
+ self._extendedAttrDict = {}
+ # namedConstructors needs deterministic ordering because bindings code
+ # outputs the constructs in the order that namedConstructors enumerates
+ # them.
+ self.namedConstructors = list()
+ self.implementedInterfaces = set()
+ self._consequential = False
+ self._isKnownNonPartial = False
+ # self.interfacesBasedOnSelf is the set of interfaces that inherit from
+ # self or have self as a consequential interface, including self itself.
+ # Used for distinguishability checking.
+ self.interfacesBasedOnSelf = set([self])
+ # self.interfacesImplementingSelf is the set of interfaces that directly
+ # have self as a consequential interface
+ self.interfacesImplementingSelf = set()
+ self._hasChildInterfaces = False
+ self._isOnGlobalProtoChain = False
+ # Tracking of the number of reserved slots we need for our
+ # members and those of ancestor interfaces.
+ self.totalMembersInSlots = 0
+ # Tracking of the number of own own members we have in slots
+ self._ownMembersInSlots = 0
+ # _exposureGlobalNames are the global names listed in our [Exposed]
+ # extended attribute. exposureSet is the exposure set as defined in the
+ # Web IDL spec: it contains interface names.
+ self._exposureGlobalNames = set()
+ self.exposureSet = set()
+
+ IDLObjectWithScope.__init__(self, location, parentScope, name)
+
+ if isKnownNonPartial:
+ self.setNonPartial(location, parent, members)
+
+ def __str__(self):
+ return "Interface '%s'" % self.identifier.name
+
+ def ctor(self):
+ identifier = IDLUnresolvedIdentifier(self.location, "constructor",
+ allowForbidden=True)
+ try:
+ return self._lookupIdentifier(identifier)
+ except:
+ return None
+
+ def resolveIdentifierConflict(self, scope, identifier, originalObject, newObject):
+ assert isinstance(scope, IDLScope)
+ assert isinstance(originalObject, IDLInterfaceMember)
+ assert isinstance(newObject, IDLInterfaceMember)
+
+ retval = IDLScope.resolveIdentifierConflict(self, scope, identifier,
+ originalObject, newObject)
+
+ # Might be a ctor, which isn't in self.members
+ if newObject in self.members:
+ self.members.remove(newObject)
+ return retval
+
+ def finish(self, scope):
+ if self._finished:
+ return
+
+ self._finished = True
+
+ if not self._isKnownNonPartial:
+ raise WebIDLError("Interface %s does not have a non-partial "
+ "declaration" % self.identifier.name,
+ [self.location])
+
+ # Verify that our [Exposed] value, if any, makes sense.
+ for globalName in self._exposureGlobalNames:
+ if globalName not in scope.globalNames:
+ raise WebIDLError("Unknown [Exposed] value %s" % globalName,
+ [self.location])
+
+ if len(self._exposureGlobalNames) == 0:
+ self._exposureGlobalNames.add(scope.primaryGlobalName)
+
+ globalNameSetToExposureSet(scope, self._exposureGlobalNames,
+ self.exposureSet)
+
+ # Now go ahead and merge in our partial interfaces.
+ for partial in self._partialInterfaces:
+ partial.finish(scope)
+ self.addExtendedAttributes(partial.propagatedExtendedAttrs)
+ self.members.extend(partial.members)
+
+ # Now that we've merged in our partial interfaces, set the
+ # _exposureGlobalNames on any members that don't have it set yet. Note
+ # that any partial interfaces that had [Exposed] set have already set up
+ # _exposureGlobalNames on all the members coming from them, so this is
+ # just implementing the "members default to interface that defined them"
+ # and "partial interfaces default to interface they're a partial for"
+ # rules from the spec.
+ for m in self.members:
+ # If m, or the partial interface m came from, had [Exposed]
+ # specified, it already has a nonempty exposure global names set.
+ if len(m._exposureGlobalNames) == 0:
+ m._exposureGlobalNames.update(self._exposureGlobalNames)
+
+ assert not self.parent or isinstance(self.parent, IDLIdentifierPlaceholder)
+ parent = self.parent.finish(scope) if self.parent else None
+ if parent and isinstance(parent, IDLExternalInterface):
+ raise WebIDLError("%s inherits from %s which does not have "
+ "a definition" %
+ (self.identifier.name,
+ self.parent.identifier.name),
+ [self.location])
+ assert not parent or isinstance(parent, IDLInterface)
+
+ self.parent = parent
+
+ assert iter(self.members)
+
+ if self.parent:
+ self.parent.finish(scope)
+
+ self.parent._hasChildInterfaces = True
+
+ self.totalMembersInSlots = self.parent.totalMembersInSlots
+
+ # Interfaces with [Global] or [PrimaryGlobal] must not
+ # have anything inherit from them
+ if (self.parent.getExtendedAttribute("Global") or
+ self.parent.getExtendedAttribute("PrimaryGlobal")):
+ # Note: This is not a self.parent.isOnGlobalProtoChain() check
+ # because ancestors of a [Global] interface can have other
+ # descendants.
+ raise WebIDLError("[Global] interface has another interface "
+ "inheriting from it",
+ [self.location, self.parent.location])
+
+ # Make sure that we're not exposed in places where our parent is not
+ if not self.exposureSet.issubset(self.parent.exposureSet):
+ raise WebIDLError("Interface %s is exposed in globals where its "
+ "parent interface %s is not exposed." %
+ (self.identifier.name,
+ self.parent.identifier.name),
+ [self.location, self.parent.location])
+
+ # Callbacks must not inherit from non-callbacks or inherit from
+ # anything that has consequential interfaces.
+ # XXXbz Can non-callbacks inherit from callbacks? Spec issue pending.
+ # XXXbz Can callbacks have consequential interfaces? Spec issue pending
+ if self.isCallback():
+ if not self.parent.isCallback():
+ raise WebIDLError("Callback interface %s inheriting from "
+ "non-callback interface %s" %
+ (self.identifier.name,
+ self.parent.identifier.name),
+ [self.location, self.parent.location])
+ elif self.parent.isCallback():
+ raise WebIDLError("Non-callback interface %s inheriting from "
+ "callback interface %s" %
+ (self.identifier.name,
+ self.parent.identifier.name),
+ [self.location, self.parent.location])
+
+ for iface in self.implementedInterfaces:
+ iface.finish(scope)
+
+ cycleInGraph = self.findInterfaceLoopPoint(self)
+ if cycleInGraph:
+ raise WebIDLError("Interface %s has itself as ancestor or "
+ "implemented interface" % self.identifier.name,
+ [self.location, cycleInGraph.location])
+
+ if self.isCallback():
+ # "implements" should have made sure we have no
+ # consequential interfaces.
+ assert len(self.getConsequentialInterfaces()) == 0
+ # And that we're not consequential.
+ assert not self.isConsequential()
+
+ # Now resolve() and finish() our members before importing the
+ # ones from our implemented interfaces.
+
+ # resolve() will modify self.members, so we need to iterate
+ # over a copy of the member list here.
+ for member in list(self.members):
+ member.resolve(self)
+
+ for member in self.members:
+ member.finish(scope)
+
+ # Now that we've finished our members, which has updated their exposure
+ # sets, make sure they aren't exposed in places where we are not.
+ for member in self.members:
+ if not member.exposureSet.issubset(self.exposureSet):
+ raise WebIDLError("Interface member has larger exposure set "
+ "than the interface itself",
+ [member.location, self.location])
+
+ ctor = self.ctor()
+ if ctor is not None:
+ ctor.finish(scope)
+
+ for ctor in self.namedConstructors:
+ ctor.finish(scope)
+
+ # Make a copy of our member list, so things that implement us
+ # can get those without all the stuff we implement ourselves
+ # admixed.
+ self.originalMembers = list(self.members)
+
+ # Import everything from our consequential interfaces into
+ # self.members. Sort our consequential interfaces by name
+ # just so we have a consistent order.
+ for iface in sorted(self.getConsequentialInterfaces(),
+ cmp=cmp,
+ key=lambda x: x.identifier.name):
+ # Flag the interface as being someone's consequential interface
+ iface.setIsConsequentialInterfaceOf(self)
+ # Verify that we're not exposed somewhere where iface is not exposed
+ if not self.exposureSet.issubset(iface.exposureSet):
+ raise WebIDLError("Interface %s is exposed in globals where its "
+ "consequential interface %s is not exposed." %
+ (self.identifier.name, iface.identifier.name),
+ [self.location, iface.location])
+ additionalMembers = iface.originalMembers;
+ for additionalMember in additionalMembers:
+ for member in self.members:
+ if additionalMember.identifier.name == member.identifier.name:
+ raise WebIDLError(
+ "Multiple definitions of %s on %s coming from 'implements' statements" %
+ (member.identifier.name, self),
+ [additionalMember.location, member.location])
+ self.members.extend(additionalMembers)
+ iface.interfacesImplementingSelf.add(self)
+
+ for ancestor in self.getInheritedInterfaces():
+ ancestor.interfacesBasedOnSelf.add(self)
+ for ancestorConsequential in ancestor.getConsequentialInterfaces():
+ ancestorConsequential.interfacesBasedOnSelf.add(self)
+
+ # Deal with interfaces marked [Unforgeable], now that we have our full
+ # member list, except unforgeables pulled in from parents. We want to
+ # do this before we set "originatingInterface" on our unforgeable
+ # members.
+ if self.getExtendedAttribute("Unforgeable"):
+ # Check that the interface already has all the things the
+ # spec would otherwise require us to synthesize and is
+ # missing the ones we plan to synthesize.
+ if not any(m.isMethod() and m.isStringifier() for m in self.members):
+ raise WebIDLError("Unforgeable interface %s does not have a "
+ "stringifier" % self.identifier.name,
+ [self.location])
+
+ for m in self.members:
+ if ((m.isMethod() and m.isJsonifier()) or
+ m.identifier.name == "toJSON"):
+ raise WebIDLError("Unforgeable interface %s has a "
+ "jsonifier so we won't be able to add "
+ "one ourselves" % self.identifier.name,
+ [self.location, m.location])
+
+ if m.identifier.name == "valueOf" and not m.isStatic():
+ raise WebIDLError("Unforgeable interface %s has a valueOf "
+ "member so we won't be able to add one "
+ "ourselves" % self.identifier.name,
+ [self.location, m.location])
+
+ for member in self.members:
+ if ((member.isAttr() or member.isMethod()) and
+ member.isUnforgeable() and
+ not hasattr(member, "originatingInterface")):
+ member.originatingInterface = self
+
+ # Compute slot indices for our members before we pull in
+ # unforgeable members from our parent.
+ for member in self.members:
+ if (member.isAttr() and
+ (member.getExtendedAttribute("StoreInSlot") or
+ member.getExtendedAttribute("Cached"))):
+ member.slotIndex = self.totalMembersInSlots
+ self.totalMembersInSlots += 1
+ if member.getExtendedAttribute("StoreInSlot"):
+ self._ownMembersInSlots += 1
+
+ if self.parent:
+ # Make sure we don't shadow any of the [Unforgeable] attributes on
+ # our ancestor interfaces. We don't have to worry about
+ # consequential interfaces here, because those have already been
+ # imported into the relevant .members lists. And we don't have to
+ # worry about anything other than our parent, because it has already
+ # imported its ancestors unforgeable attributes into its member
+ # list.
+ for unforgeableMember in (member for member in self.parent.members if
+ (member.isAttr() or member.isMethod()) and
+ member.isUnforgeable()):
+ shadows = [ m for m in self.members if
+ (m.isAttr() or m.isMethod()) and
+ not m.isStatic() and
+ m.identifier.name == unforgeableMember.identifier.name ]
+ if len(shadows) != 0:
+ locs = [unforgeableMember.location] + [ s.location for s
+ in shadows ]
+ raise WebIDLError("Interface %s shadows [Unforgeable] "
+ "members of %s" %
+ (self.identifier.name,
+ ancestor.identifier.name),
+ locs)
+ # And now just stick it in our members, since we won't be
+ # inheriting this down the proto chain. If we really cared we
+ # could try to do something where we set up the unforgeable
+ # attributes/methods of ancestor interfaces, with their
+ # corresponding getters, on our interface, but that gets pretty
+ # complicated and seems unnecessary.
+ self.members.append(unforgeableMember)
+
+ # Ensure that there's at most one of each {named,indexed}
+ # {getter,setter,creator,deleter}, at most one stringifier,
+ # and at most one legacycaller. Note that this last is not
+ # quite per spec, but in practice no one overloads
+ # legacycallers.
+ specialMembersSeen = {}
+ for member in self.members:
+ if not member.isMethod():
+ continue
+
+ if member.isGetter():
+ memberType = "getters"
+ elif member.isSetter():
+ memberType = "setters"
+ elif member.isCreator():
+ memberType = "creators"
+ elif member.isDeleter():
+ memberType = "deleters"
+ elif member.isStringifier():
+ memberType = "stringifiers"
+ elif member.isJsonifier():
+ memberType = "jsonifiers"
+ elif member.isLegacycaller():
+ memberType = "legacycallers"
+ else:
+ continue
+
+ if (memberType != "stringifiers" and memberType != "legacycallers" and
+ memberType != "jsonifiers"):
+ if member.isNamed():
+ memberType = "named " + memberType
+ else:
+ assert member.isIndexed()
+ memberType = "indexed " + memberType
+
+ if memberType in specialMembersSeen:
+ raise WebIDLError("Multiple " + memberType + " on %s" % (self),
+ [self.location,
+ specialMembersSeen[memberType].location,
+ member.location])
+
+ specialMembersSeen[memberType] = member
+
+ if self._isOnGlobalProtoChain:
+ # Make sure we have no named setters, creators, or deleters
+ for memberType in ["setter", "creator", "deleter"]:
+ memberId = "named " + memberType + "s"
+ if memberId in specialMembersSeen:
+ raise WebIDLError("Interface with [Global] has a named %s" %
+ memberType,
+ [self.location,
+ specialMembersSeen[memberId].location])
+ # Make sure we're not [OverrideBuiltins]
+ if self.getExtendedAttribute("OverrideBuiltins"):
+ raise WebIDLError("Interface with [Global] also has "
+ "[OverrideBuiltins]",
+ [self.location])
+ # Mark all of our ancestors as being on the global's proto chain too
+ parent = self.parent
+ while parent:
+ # Must not inherit from an interface with [OverrideBuiltins]
+ if parent.getExtendedAttribute("OverrideBuiltins"):
+ raise WebIDLError("Interface with [Global] inherits from "
+ "interface with [OverrideBuiltins]",
+ [self.location, parent.location])
+ parent._isOnGlobalProtoChain = True
+ parent = parent.parent
+
+ def validate(self):
+ # We don't support consequential unforgeable interfaces. Need to check
+ # this here, becaue in finish() an interface might not know yet that
+ # it's consequential.
+ if self.getExtendedAttribute("Unforgeable") and self.isConsequential():
+ raise WebIDLError(
+ "%s is an unforgeable consequential interface" %
+ self.identifier.name,
+ [self.location] +
+ list(i.location for i in
+ (self.interfacesBasedOnSelf - { self }) ))
+
+ # We also don't support inheriting from unforgeable interfaces.
+ if self.getExtendedAttribute("Unforgeable") and self.hasChildInterfaces():
+ raise WebIDLError("%s is an unforgeable ancestor interface" %
+ self.identifier.name,
+ [self.location] +
+ list(i.location for i in
+ self.interfacesBasedOnSelf if i.parent == self))
+
+
+ for member in self.members:
+ member.validate()
+
+ # Check that PutForwards refers to another attribute and that no
+ # cycles exist in forwarded assignments.
+ if member.isAttr():
+ iface = self
+ attr = member
+ putForwards = attr.getExtendedAttribute("PutForwards")
+ if putForwards and self.isCallback():
+ raise WebIDLError("[PutForwards] used on an attribute "
+ "on interface %s which is a callback "
+ "interface" % self.identifier.name,
+ [self.location, member.location])
+
+ while putForwards is not None:
+ forwardIface = attr.type.unroll().inner
+ fowardAttr = None
+
+ for forwardedMember in forwardIface.members:
+ if (not forwardedMember.isAttr() or
+ forwardedMember.identifier.name != putForwards[0]):
+ continue
+ if forwardedMember == member:
+ raise WebIDLError("Cycle detected in forwarded "
+ "assignments for attribute %s on "
+ "%s" %
+ (member.identifier.name, self),
+ [member.location])
+ fowardAttr = forwardedMember
+ break
+
+ if fowardAttr is None:
+ raise WebIDLError("Attribute %s on %s forwards to "
+ "missing attribute %s" %
+ (attr.identifier.name, iface, putForwards),
+ [attr.location])
+
+ iface = forwardIface
+ attr = fowardAttr
+ putForwards = attr.getExtendedAttribute("PutForwards")
+
+ if (self.getExtendedAttribute("Pref") and
+ self._exposureGlobalNames != set([self.parentScope.primaryGlobalName])):
+ raise WebIDLError("[Pref] used on an member that is not %s-only" %
+ self.parentScope.primaryGlobalName,
+ [self.location])
+
+
+ def isInterface(self):
+ return True
+
+ def isExternal(self):
+ return False
+
+ def setIsConsequentialInterfaceOf(self, other):
+ self._consequential = True
+ self.interfacesBasedOnSelf.add(other)
+
+ def isConsequential(self):
+ return self._consequential
+
+ def setCallback(self, value):
+ self._callback = value
+
+ def isCallback(self):
+ return self._callback
+
+ def isSingleOperationInterface(self):
+ assert self.isCallback() or self.isJSImplemented()
+ return (
+ # JS-implemented things should never need the
+ # this-handling weirdness of single-operation interfaces.
+ not self.isJSImplemented() and
+ # Not inheriting from another interface
+ not self.parent and
+ # No consequential interfaces
+ len(self.getConsequentialInterfaces()) == 0 and
+ # No attributes of any kinds
+ not any(m.isAttr() for m in self.members) and
+ # There is at least one regular operation, and all regular
+ # operations have the same identifier
+ len(set(m.identifier.name for m in self.members if
+ m.isMethod() and not m.isStatic())) == 1)
+
+ def inheritanceDepth(self):
+ depth = 0
+ parent = self.parent
+ while parent:
+ depth = depth + 1
+ parent = parent.parent
+ return depth
+
+ def hasConstants(self):
+ return any(m.isConst() for m in self.members)
+
+ def hasInterfaceObject(self):
+ if self.isCallback():
+ return self.hasConstants()
+ return not hasattr(self, "_noInterfaceObject")
+
+ def hasInterfacePrototypeObject(self):
+ return not self.isCallback() and self.getUserData('hasConcreteDescendant', False)
+
+ def addExtendedAttributes(self, attrs):
+ for attr in attrs:
+ identifier = attr.identifier()
+
+ # Special cased attrs
+ if identifier == "TreatNonCallableAsNull":
+ raise WebIDLError("TreatNonCallableAsNull cannot be specified on interfaces",
+ [attr.location, self.location])
+ if identifier == "TreatNonObjectAsNull":
+ raise WebIDLError("TreatNonObjectAsNull cannot be specified on interfaces",
+ [attr.location, self.location])
+ elif identifier == "NoInterfaceObject":
+ if not attr.noArguments():
+ raise WebIDLError("[NoInterfaceObject] must take no arguments",
+ [attr.location])
+
+ if self.ctor():
+ raise WebIDLError("Constructor and NoInterfaceObject are incompatible",
+ [self.location])
+
+ self._noInterfaceObject = True
+ elif identifier == "Constructor" or identifier == "NamedConstructor" or identifier == "ChromeConstructor":
+ if identifier == "Constructor" and not self.hasInterfaceObject():
+ raise WebIDLError(str(identifier) + " and NoInterfaceObject are incompatible",
+ [self.location])
+
+ if identifier == "NamedConstructor" and not attr.hasValue():
+ raise WebIDLError("NamedConstructor must either take an identifier or take a named argument list",
+ [attr.location])
+
+ if identifier == "ChromeConstructor" and not self.hasInterfaceObject():
+ raise WebIDLError(str(identifier) + " and NoInterfaceObject are incompatible",
+ [self.location])
+
+ args = attr.args() if attr.hasArgs() else []
+
+ retType = IDLWrapperType(self.location, self)
+
+ if identifier == "Constructor" or identifier == "ChromeConstructor":
+ name = "constructor"
+ allowForbidden = True
+ else:
+ name = attr.value()
+ allowForbidden = False
+
+ methodIdentifier = IDLUnresolvedIdentifier(self.location, name,
+ allowForbidden=allowForbidden)
+
+ method = IDLMethod(self.location, methodIdentifier, retType,
+ args, static=True)
+ # Constructors are always NewObject and are always
+ # assumed to be able to throw (since there's no way to
+ # indicate otherwise) and never have any other
+ # extended attributes.
+ method.addExtendedAttributes(
+ [IDLExtendedAttribute(self.location, ("NewObject",)),
+ IDLExtendedAttribute(self.location, ("Throws",))])
+ if identifier == "ChromeConstructor":
+ method.addExtendedAttributes(
+ [IDLExtendedAttribute(self.location, ("ChromeOnly",))])
+
+ if identifier == "Constructor" or identifier == "ChromeConstructor":
+ method.resolve(self)
+ else:
+ # We need to detect conflicts for NamedConstructors across
+ # interfaces. We first call resolve on the parentScope,
+ # which will merge all NamedConstructors with the same
+ # identifier accross interfaces as overloads.
+ method.resolve(self.parentScope)
+
+ # Then we look up the identifier on the parentScope. If the
+ # result is the same as the method we're adding then it
+ # hasn't been added as an overload and it's the first time
+ # we've encountered a NamedConstructor with that identifier.
+ # If the result is not the same as the method we're adding
+ # then it has been added as an overload and we need to check
+ # whether the result is actually one of our existing
+ # NamedConstructors.
+ newMethod = self.parentScope.lookupIdentifier(method.identifier)
+ if newMethod == method:
+ self.namedConstructors.append(method)
+ elif not newMethod in self.namedConstructors:
+ raise WebIDLError("NamedConstructor conflicts with a NamedConstructor of a different interface",
+ [method.location, newMethod.location])
+ elif (identifier == "ArrayClass"):
+ if not attr.noArguments():
+ raise WebIDLError("[ArrayClass] must take no arguments",
+ [attr.location])
+ if self.parent:
+ raise WebIDLError("[ArrayClass] must not be specified on "
+ "an interface with inherited interfaces",
+ [attr.location, self.location])
+ elif (identifier == "ExceptionClass"):
+ if not attr.noArguments():
+ raise WebIDLError("[ExceptionClass] must take no arguments",
+ [attr.location])
+ if self.parent:
+ raise WebIDLError("[ExceptionClass] must not be specified on "
+ "an interface with inherited interfaces",
+ [attr.location, self.location])
+ elif identifier == "Global":
+ if attr.hasValue():
+ self.globalNames = [ attr.value() ]
+ elif attr.hasArgs():
+ self.globalNames = attr.args()
+ else:
+ self.globalNames = [ self.identifier.name ]
+ self.parentScope.globalNames.update(self.globalNames)
+ for globalName in self.globalNames:
+ self.parentScope.globalNameMapping[globalName].add(self.identifier.name)
+ self._isOnGlobalProtoChain = True
+ elif identifier == "PrimaryGlobal":
+ if not attr.noArguments():
+ raise WebIDLError("[PrimaryGlobal] must take no arguments",
+ [attr.location])
+ if self.parentScope.primaryGlobalAttr is not None:
+ raise WebIDLError(
+ "[PrimaryGlobal] specified twice",
+ [attr.location,
+ self.parentScope.primaryGlobalAttr.location])
+ self.parentScope.primaryGlobalAttr = attr
+ self.parentScope.primaryGlobalName = self.identifier.name
+ self.parentScope.globalNames.add(self.identifier.name)
+ self.parentScope.globalNameMapping[self.identifier.name].add(self.identifier.name)
+ self._isOnGlobalProtoChain = True
+ elif (identifier == "NeedNewResolve" or
+ identifier == "OverrideBuiltins" or
+ identifier == "ChromeOnly" or
+ identifier == "Unforgeable" or
+ identifier == "LegacyEventInit"):
+ # Known extended attributes that do not take values
+ if not attr.noArguments():
+ raise WebIDLError("[%s] must take no arguments" % identifier,
+ [attr.location])
+ elif identifier == "Exposed":
+ convertExposedAttrToGlobalNameSet(attr,
+ self._exposureGlobalNames)
+ elif (identifier == "Pref" or
+ identifier == "JSImplementation" or
+ identifier == "HeaderFile" or
+ identifier == "NavigatorProperty" or
+ identifier == "AvailableIn" or
+ identifier == "Func" or
+ identifier == "CheckPermissions"):
+ # Known extended attributes that take a string value
+ if not attr.hasValue():
+ raise WebIDLError("[%s] must have a value" % identifier,
+ [attr.location])
+ else:
+ raise WebIDLError("Unknown extended attribute %s on interface" % identifier,
+ [attr.location])
+
+ attrlist = attr.listValue()
+ self._extendedAttrDict[identifier] = attrlist if len(attrlist) else True
+
+ def addImplementedInterface(self, implementedInterface):
+ assert(isinstance(implementedInterface, IDLInterface))
+ self.implementedInterfaces.add(implementedInterface)
+
+ def getInheritedInterfaces(self):
+ """
+ Returns a list of the interfaces this interface inherits from
+ (not including this interface itself). The list is in order
+ from most derived to least derived.
+ """
+ assert(self._finished)
+ if not self.parent:
+ return []
+ parentInterfaces = self.parent.getInheritedInterfaces()
+ parentInterfaces.insert(0, self.parent)
+ return parentInterfaces
+
+ def getConsequentialInterfaces(self):
+ assert(self._finished)
+ # The interfaces we implement directly
+ consequentialInterfaces = set(self.implementedInterfaces)
+
+ # And their inherited interfaces
+ for iface in self.implementedInterfaces:
+ consequentialInterfaces |= set(iface.getInheritedInterfaces())
+
+ # And now collect up the consequential interfaces of all of those
+ temp = set()
+ for iface in consequentialInterfaces:
+ temp |= iface.getConsequentialInterfaces()
+
+ return consequentialInterfaces | temp
+
+ def findInterfaceLoopPoint(self, otherInterface):
+ """
+ Finds an interface, amongst our ancestors and consequential interfaces,
+ that inherits from otherInterface or implements otherInterface
+ directly. If there is no such interface, returns None.
+ """
+ if self.parent:
+ if self.parent == otherInterface:
+ return self
+ loopPoint = self.parent.findInterfaceLoopPoint(otherInterface)
+ if loopPoint:
+ return loopPoint
+ if otherInterface in self.implementedInterfaces:
+ return self
+ for iface in self.implementedInterfaces:
+ loopPoint = iface.findInterfaceLoopPoint(otherInterface)
+ if loopPoint:
+ return loopPoint
+ return None
+
+ def getExtendedAttribute(self, name):
+ return self._extendedAttrDict.get(name, None)
+
+ def setNonPartial(self, location, parent, members):
+ assert not parent or isinstance(parent, IDLIdentifierPlaceholder)
+ if self._isKnownNonPartial:
+ raise WebIDLError("Two non-partial definitions for the "
+ "same interface",
+ [location, self.location])
+ self._isKnownNonPartial = True
+ # Now make it look like we were parsed at this new location, since
+ # that's the place where the interface is "really" defined
+ self.location = location
+ assert not self.parent
+ self.parent = parent
+ # Put the new members at the beginning
+ self.members = members + self.members
+
+ def addPartialInterface(self, partial):
+ assert self.identifier.name == partial.identifier.name
+ self._partialInterfaces.append(partial)
+
+ def getJSImplementation(self):
+ classId = self.getExtendedAttribute("JSImplementation")
+ if not classId:
+ return classId
+ assert isinstance(classId, list)
+ assert len(classId) == 1
+ return classId[0]
+
+ def isJSImplemented(self):
+ return bool(self.getJSImplementation())
+
+ def getNavigatorProperty(self):
+ naviProp = self.getExtendedAttribute("NavigatorProperty")
+ if not naviProp:
+ return None
+ assert len(naviProp) == 1
+ assert isinstance(naviProp, list)
+ assert len(naviProp[0]) != 0
+ return naviProp[0]
+
+ def hasChildInterfaces(self):
+ return self._hasChildInterfaces
+
+ def isOnGlobalProtoChain(self):
+ return self._isOnGlobalProtoChain
+
+ def _getDependentObjects(self):
+ deps = set(self.members)
+ deps.union(self.implementedInterfaces)
+ if self.parent:
+ deps.add(self.parent)
+ return deps
+
+ def hasMembersInSlots(self):
+ return self._ownMembersInSlots != 0
+
+class IDLDictionary(IDLObjectWithScope):
+ def __init__(self, location, parentScope, name, parent, members):
+ assert isinstance(parentScope, IDLScope)
+ assert isinstance(name, IDLUnresolvedIdentifier)
+ assert not parent or isinstance(parent, IDLIdentifierPlaceholder)
+
+ self.parent = parent
+ self._finished = False
+ self.members = list(members)
+
+ IDLObjectWithScope.__init__(self, location, parentScope, name)
+
+ def __str__(self):
+ return "Dictionary '%s'" % self.identifier.name
+
+ def isDictionary(self):
+ return True;
+
+ def finish(self, scope):
+ if self._finished:
+ return
+
+ self._finished = True
+
+ if self.parent:
+ assert isinstance(self.parent, IDLIdentifierPlaceholder)
+ oldParent = self.parent
+ self.parent = self.parent.finish(scope)
+ if not isinstance(self.parent, IDLDictionary):
+ raise WebIDLError("Dictionary %s has parent that is not a dictionary" %
+ self.identifier.name,
+ [oldParent.location, self.parent.location])
+
+ # Make sure the parent resolves all its members before we start
+ # looking at them.
+ self.parent.finish(scope)
+
+ for member in self.members:
+ member.resolve(self)
+ if not member.isComplete():
+ member.complete(scope)
+ assert member.type.isComplete()
+
+ # Members of a dictionary are sorted in lexicographic order
+ self.members.sort(cmp=cmp, key=lambda x: x.identifier.name)
+
+ inheritedMembers = []
+ ancestor = self.parent
+ while ancestor:
+ if ancestor == self:
+ raise WebIDLError("Dictionary %s has itself as an ancestor" %
+ self.identifier.name,
+ [self.identifier.location])
+ inheritedMembers.extend(ancestor.members)
+ ancestor = ancestor.parent
+
+ # Catch name duplication
+ for inheritedMember in inheritedMembers:
+ for member in self.members:
+ if member.identifier.name == inheritedMember.identifier.name:
+ raise WebIDLError("Dictionary %s has two members with name %s" %
+ (self.identifier.name, member.identifier.name),
+ [member.location, inheritedMember.location])
+
+ def validate(self):
+ def typeContainsDictionary(memberType, dictionary):
+ """
+ Returns a tuple whose:
+
+ - First element is a Boolean value indicating whether
+ memberType contains dictionary.
+
+ - Second element is:
+ A list of locations that leads from the type that was passed in
+ the memberType argument, to the dictionary being validated,
+ if the boolean value in the first element is True.
+
+ None, if the boolean value in the first element is False.
+ """
+
+ if (memberType.nullable() or
+ memberType.isArray() or
+ memberType.isSequence() or
+ memberType.isMozMap()):
+ return typeContainsDictionary(memberType.inner, dictionary)
+
+ if memberType.isDictionary():
+ if memberType.inner == dictionary:
+ return (True, [memberType.location])
+
+ (contains, locations) = dictionaryContainsDictionary(memberType.inner, \
+ dictionary)
+ if contains:
+ return (True, [memberType.location] + locations)
+
+ if memberType.isUnion():
+ for member in memberType.flatMemberTypes:
+ (contains, locations) = typeContainsDictionary(member, dictionary)
+ if contains:
+ return (True, locations)
+
+ return (False, None)
+
+ def dictionaryContainsDictionary(dictMember, dictionary):
+ for member in dictMember.members:
+ (contains, locations) = typeContainsDictionary(member.type, dictionary)
+ if contains:
+ return (True, [member.location] + locations)
+
+ if dictMember.parent:
+ if dictMember.parent == dictionary:
+ return (True, [dictMember.location])
+ else:
+ (contains, locations) = dictionaryContainsDictionary(dictMember.parent, dictionary)
+ if contains:
+ return (True, [dictMember.location] + locations)
+
+ return (False, None)
+
+ for member in self.members:
+ if member.type.isDictionary() and member.type.nullable():
+ raise WebIDLError("Dictionary %s has member with nullable "
+ "dictionary type" % self.identifier.name,
+ [member.location])
+ (contains, locations) = typeContainsDictionary(member.type, self)
+ if contains:
+ raise WebIDLError("Dictionary %s has member with itself as type." %
+ self.identifier.name,
+ [member.location] + locations)
+
+ def addExtendedAttributes(self, attrs):
+ assert len(attrs) == 0
+
+ def _getDependentObjects(self):
+ deps = set(self.members)
+ if (self.parent):
+ deps.add(self.parent)
+ return deps
+
+class IDLEnum(IDLObjectWithIdentifier):
+ def __init__(self, location, parentScope, name, values):
+ assert isinstance(parentScope, IDLScope)
+ assert isinstance(name, IDLUnresolvedIdentifier)
+
+ if len(values) != len(set(values)):
+ raise WebIDLError("Enum %s has multiple identical strings" % name.name,
+ [location])
+
+ IDLObjectWithIdentifier.__init__(self, location, parentScope, name)
+ self._values = values
+
+ def values(self):
+ return self._values
+
+ def finish(self, scope):
+ pass
+
+ def validate(self):
+ pass
+
+ def isEnum(self):
+ return True
+
+ def addExtendedAttributes(self, attrs):
+ assert len(attrs) == 0
+
+ def _getDependentObjects(self):
+ return set()
+
+class IDLType(IDLObject):
+ Tags = enum(
+ # The integer types
+ 'int8',
+ 'uint8',
+ 'int16',
+ 'uint16',
+ 'int32',
+ 'uint32',
+ 'int64',
+ 'uint64',
+ # Additional primitive types
+ 'bool',
+ 'unrestricted_float',
+ 'float',
+ 'unrestricted_double',
+ # "double" last primitive type to match IDLBuiltinType
+ 'double',
+ # Other types
+ 'any',
+ 'domstring',
+ 'bytestring',
+ 'scalarvaluestring',
+ 'object',
+ 'date',
+ 'void',
+ # Funny stuff
+ 'interface',
+ 'dictionary',
+ 'enum',
+ 'callback',
+ 'union',
+ 'sequence',
+ 'mozmap',
+ 'array'
+ )
+
+ def __init__(self, location, name):
+ IDLObject.__init__(self, location)
+ self.name = name
+ self.builtin = False
+
+ def __eq__(self, other):
+ return other and self.builtin == other.builtin and self.name == other.name
+
+ def __ne__(self, other):
+ return not self == other
+
+ def __str__(self):
+ return str(self.name)
+
+ def isType(self):
+ return True
+
+ def nullable(self):
+ return False
+
+ def isPrimitive(self):
+ return False
+
+ def isBoolean(self):
+ return False
+
+ def isNumeric(self):
+ return False
+
+ def isString(self):
+ return False
+
+ def isByteString(self):
+ return False
+
+ def isDOMString(self):
+ return False
+
+ def isScalarValueString(self):
+ return False
+
+ def isVoid(self):
+ return self.name == "Void"
+
+ def isSequence(self):
+ return False
+
+ def isMozMap(self):
+ return False
+
+ def isArray(self):
+ return False
+
+ def isArrayBuffer(self):
+ return False
+
+ def isArrayBufferView(self):
+ return False
+
+ def isTypedArray(self):
+ return False
+
+ def isCallbackInterface(self):
+ return False
+
+ def isNonCallbackInterface(self):
+ return False
+
+ def isGeckoInterface(self):
+ """ Returns a boolean indicating whether this type is an 'interface'
+ type that is implemented in Gecko. At the moment, this returns
+ true for all interface types that are not types from the TypedArray
+ spec."""
+ return self.isInterface() and not self.isSpiderMonkeyInterface()
+
+ def isSpiderMonkeyInterface(self):
+ """ Returns a boolean indicating whether this type is an 'interface'
+ type that is implemented in Spidermonkey. At the moment, this
+ only returns true for the types from the TypedArray spec. """
+ return self.isInterface() and (self.isArrayBuffer() or \
+ self.isArrayBufferView() or \
+ self.isTypedArray())
+
+ def isDictionary(self):
+ return False
+
+ def isInterface(self):
+ return False
+
+ def isAny(self):
+ return self.tag() == IDLType.Tags.any
+
+ def isDate(self):
+ return self.tag() == IDLType.Tags.date
+
+ def isObject(self):
+ return self.tag() == IDLType.Tags.object
+
+ def isPromise(self):
+ return False
+
+ def isComplete(self):
+ return True
+
+ def includesRestrictedFloat(self):
+ return False
+
+ def isFloat(self):
+ return False
+
+ def isUnrestricted(self):
+ # Should only call this on float types
+ assert self.isFloat()
+
+ def isSerializable(self):
+ return False
+
+ def tag(self):
+ assert False # Override me!
+
+ def treatNonCallableAsNull(self):
+ assert self.tag() == IDLType.Tags.callback
+ return self.nullable() and self.inner._treatNonCallableAsNull
+
+ def treatNonObjectAsNull(self):
+ assert self.tag() == IDLType.Tags.callback
+ return self.nullable() and self.inner._treatNonObjectAsNull
+
+ def addExtendedAttributes(self, attrs):
+ assert len(attrs) == 0
+
+ def resolveType(self, parentScope):
+ pass
+
+ def unroll(self):
+ return self
+
+ def isDistinguishableFrom(self, other):
+ raise TypeError("Can't tell whether a generic type is or is not "
+ "distinguishable from other things")
+
+ def isExposedInAllOf(self, exposureSet):
+ return True
+
+class IDLUnresolvedType(IDLType):
+ """
+ Unresolved types are interface types
+ """
+
+ def __init__(self, location, name, promiseInnerType=None):
+ IDLType.__init__(self, location, name)
+ self._promiseInnerType = promiseInnerType
+
+ def isComplete(self):
+ return False
+
+ def complete(self, scope):
+ obj = None
+ try:
+ obj = scope._lookupIdentifier(self.name)
+ except:
+ raise WebIDLError("Unresolved type '%s'." % self.name,
+ [self.location])
+
+ assert obj
+ if obj.isType():
+ # obj itself might not be complete; deal with that.
+ assert obj != self
+ if not obj.isComplete():
+ obj = obj.complete(scope)
+ return obj
+
+ if self._promiseInnerType and not self._promiseInnerType.isComplete():
+ self._promiseInnerType = self._promiseInnerType.complete(scope)
+
+ name = self.name.resolve(scope, None)
+ return IDLWrapperType(self.location, obj, self._promiseInnerType)
+
+ def isDistinguishableFrom(self, other):
+ raise TypeError("Can't tell whether an unresolved type is or is not "
+ "distinguishable from other things")
+
+class IDLNullableType(IDLType):
+ def __init__(self, location, innerType):
+ assert not innerType.isVoid()
+ assert not innerType == BuiltinTypes[IDLBuiltinType.Types.any]
+
+ IDLType.__init__(self, location, innerType.name)
+ self.inner = innerType
+ self.builtin = False
+
+ def __eq__(self, other):
+ return isinstance(other, IDLNullableType) and self.inner == other.inner
+
+ def __str__(self):
+ return self.inner.__str__() + "OrNull"
+
+ def nullable(self):
+ return True
+
+ def isCallback(self):
+ return self.inner.isCallback()
+
+ def isPrimitive(self):
+ return self.inner.isPrimitive()
+
+ def isBoolean(self):
+ return self.inner.isBoolean()
+
+ def isNumeric(self):
+ return self.inner.isNumeric()
+
+ def isString(self):
+ return self.inner.isString()
+
+ def isByteString(self):
+ return self.inner.isByteString()
+
+ def isDOMString(self):
+ return self.inner.isDOMString()
+
+ def isScalarValueString(self):
+ return self.inner.isScalarValueString()
+
+ def isFloat(self):
+ return self.inner.isFloat()
+
+ def isUnrestricted(self):
+ return self.inner.isUnrestricted()
+
+ def includesRestrictedFloat(self):
+ return self.inner.includesRestrictedFloat()
+
+ def isInteger(self):
+ return self.inner.isInteger()
+
+ def isVoid(self):
+ return False
+
+ def isSequence(self):
+ return self.inner.isSequence()
+
+ def isMozMap(self):
+ return self.inner.isMozMap()
+
+ def isArray(self):
+ return self.inner.isArray()
+
+ def isArrayBuffer(self):
+ return self.inner.isArrayBuffer()
+
+ def isArrayBufferView(self):
+ return self.inner.isArrayBufferView()
+
+ def isTypedArray(self):
+ return self.inner.isTypedArray()
+
+ def isDictionary(self):
+ return self.inner.isDictionary()
+
+ def isInterface(self):
+ return self.inner.isInterface()
+
+ def isCallbackInterface(self):
+ return self.inner.isCallbackInterface()
+
+ def isNonCallbackInterface(self):
+ return self.inner.isNonCallbackInterface()
+
+ def isEnum(self):
+ return self.inner.isEnum()
+
+ def isUnion(self):
+ return self.inner.isUnion()
+
+ def isSerializable(self):
+ return self.inner.isSerializable()
+
+ def tag(self):
+ return self.inner.tag()
+
+ def resolveType(self, parentScope):
+ assert isinstance(parentScope, IDLScope)
+ self.inner.resolveType(parentScope)
+
+ def isComplete(self):
+ return self.inner.isComplete()
+
+ def complete(self, scope):
+ self.inner = self.inner.complete(scope)
+ if self.inner.nullable():
+ raise WebIDLError("The inner type of a nullable type must not be "
+ "a nullable type",
+ [self.location, self.inner.location])
+ if self.inner.isUnion():
+ if self.inner.hasNullableType:
+ raise WebIDLError("The inner type of a nullable type must not "
+ "be a union type that itself has a nullable "
+ "type as a member type", [self.location])
+
+ self.name = self.inner.name
+ return self
+
+ def unroll(self):
+ return self.inner.unroll()
+
+ def isDistinguishableFrom(self, other):
+ if (other.nullable() or (other.isUnion() and other.hasNullableType) or
+ other.isDictionary()):
+ # Can't tell which type null should become
+ return False
+ return self.inner.isDistinguishableFrom(other)
+
+ def _getDependentObjects(self):
+ return self.inner._getDependentObjects()
+
+class IDLSequenceType(IDLType):
+ def __init__(self, location, parameterType):
+ assert not parameterType.isVoid()
+
+ IDLType.__init__(self, location, parameterType.name)
+ self.inner = parameterType
+ self.builtin = False
+
+ def __eq__(self, other):
+ return isinstance(other, IDLSequenceType) and self.inner == other.inner
+
+ def __str__(self):
+ return self.inner.__str__() + "Sequence"
+
+ def nullable(self):
+ return False
+
+ def isPrimitive(self):
+ return False;
+
+ def isString(self):
+ return False;
+
+ def isByteString(self):
+ return False
+
+ def isDOMString(self):
+ return False
+
+ def isScalarValueString(self):
+ return False
+
+ def isVoid(self):
+ return False
+
+ def isSequence(self):
+ return True
+
+ def isArray(self):
+ return False
+
+ def isDictionary(self):
+ return False
+
+ def isInterface(self):
+ return False
+
+ def isEnum(self):
+ return False
+
+ def isSerializable(self):
+ return self.inner.isSerializable()
+
+ def includesRestrictedFloat(self):
+ return self.inner.includesRestrictedFloat()
+
+ def tag(self):
+ return IDLType.Tags.sequence
+
+ def resolveType(self, parentScope):
+ assert isinstance(parentScope, IDLScope)
+ self.inner.resolveType(parentScope)
+
+ def isComplete(self):
+ return self.inner.isComplete()
+
+ def complete(self, scope):
+ self.inner = self.inner.complete(scope)
+ self.name = self.inner.name
+ return self
+
+ def unroll(self):
+ return self.inner.unroll()
+
+ def isDistinguishableFrom(self, other):
+ if other.isUnion():
+ # Just forward to the union; it'll deal
+ return other.isDistinguishableFrom(self)
+ return (other.isPrimitive() or other.isString() or other.isEnum() or
+ other.isDate() or other.isNonCallbackInterface() or other.isMozMap())
+
+ def _getDependentObjects(self):
+ return self.inner._getDependentObjects()
+
+class IDLMozMapType(IDLType):
+ # XXXbz This is pretty similar to IDLSequenceType in various ways.
+ # And maybe to IDLNullableType. Should we have a superclass for
+ # "type containing this other type"? Bug 1015318.
+ def __init__(self, location, parameterType):
+ assert not parameterType.isVoid()
+
+ IDLType.__init__(self, location, parameterType.name)
+ self.inner = parameterType
+ self.builtin = False
+
+ def __eq__(self, other):
+ return isinstance(other, IDLMozMapType) and self.inner == other.inner
+
+ def __str__(self):
+ return self.inner.__str__() + "MozMap"
+
+ def isMozMap(self):
+ return True
+
+ def includesRestrictedFloat(self):
+ return self.inner.includesRestrictedFloat()
+
+ def tag(self):
+ return IDLType.Tags.mozmap
+
+ def resolveType(self, parentScope):
+ assert isinstance(parentScope, IDLScope)
+ self.inner.resolveType(parentScope)
+
+ def isComplete(self):
+ return self.inner.isComplete()
+
+ def complete(self, scope):
+ self.inner = self.inner.complete(scope)
+ self.name = self.inner.name
+ return self
+
+ def unroll(self):
+ # We do not unroll our inner. Just stop at ourselves. That
+ # lets us add headers for both ourselves and our inner as
+ # needed.
+ return self
+
+ def isDistinguishableFrom(self, other):
+ if other.isUnion():
+ # Just forward to the union; it'll deal
+ return other.isDistinguishableFrom(self)
+ return (other.isPrimitive() or other.isString() or other.isEnum() or
+ other.isDate() or other.isNonCallbackInterface() or other.isSequence())
+
+ def isExposedInAllOf(self, exposureSet):
+ return self.inner.unroll().isExposedInAllOf(exposureSet)
+
+ def _getDependentObjects(self):
+ return self.inner._getDependentObjects()
+
+class IDLUnionType(IDLType):
+ def __init__(self, location, memberTypes):
+ IDLType.__init__(self, location, "")
+ self.memberTypes = memberTypes
+ self.hasNullableType = False
+ self.hasDictionaryType = False
+ self.flatMemberTypes = None
+ self.builtin = False
+
+ def __eq__(self, other):
+ return isinstance(other, IDLUnionType) and self.memberTypes == other.memberTypes
+
+ def isVoid(self):
+ return False
+
+ def isUnion(self):
+ return True
+
+ def isSerializable(self):
+ return all(m.isSerializable() for m in self.memberTypes)
+
+ def includesRestrictedFloat(self):
+ return any(t.includesRestrictedFloat() for t in self.memberTypes)
+
+ def tag(self):
+ return IDLType.Tags.union
+
+ def resolveType(self, parentScope):
+ assert isinstance(parentScope, IDLScope)
+ for t in self.memberTypes:
+ t.resolveType(parentScope)
+
+ def isComplete(self):
+ return self.flatMemberTypes is not None
+
+ def complete(self, scope):
+ def typeName(type):
+ if isinstance(type, IDLNullableType):
+ return typeName(type.inner) + "OrNull"
+ if isinstance(type, IDLWrapperType):
+ return typeName(type._identifier.object())
+ if isinstance(type, IDLObjectWithIdentifier):
+ return typeName(type.identifier)
+ if (isinstance(type, IDLType) and
+ (type.isArray() or type.isSequence() or type.isMozMap)):
+ return str(type)
+ return type.name
+
+ for (i, type) in enumerate(self.memberTypes):
+ if not type.isComplete():
+ self.memberTypes[i] = type.complete(scope)
+
+ self.name = "Or".join(typeName(type) for type in self.memberTypes)
+ self.flatMemberTypes = list(self.memberTypes)
+ i = 0
+ while i < len(self.flatMemberTypes):
+ if self.flatMemberTypes[i].nullable():
+ if self.hasNullableType:
+ raise WebIDLError("Can't have more than one nullable types in a union",
+ [nullableType.location, self.flatMemberTypes[i].location])
+ if self.hasDictionaryType:
+ raise WebIDLError("Can't have a nullable type and a "
+ "dictionary type in a union",
+ [dictionaryType.location,
+ self.flatMemberTypes[i].location])
+ self.hasNullableType = True
+ nullableType = self.flatMemberTypes[i]
+ self.flatMemberTypes[i] = self.flatMemberTypes[i].inner
+ continue
+ if self.flatMemberTypes[i].isDictionary():
+ if self.hasNullableType:
+ raise WebIDLError("Can't have a nullable type and a "
+ "dictionary type in a union",
+ [nullableType.location,
+ self.flatMemberTypes[i].location])
+ self.hasDictionaryType = True
+ dictionaryType = self.flatMemberTypes[i]
+ elif self.flatMemberTypes[i].isUnion():
+ self.flatMemberTypes[i:i + 1] = self.flatMemberTypes[i].memberTypes
+ continue
+ i += 1
+
+ for (i, t) in enumerate(self.flatMemberTypes[:-1]):
+ for u in self.flatMemberTypes[i + 1:]:
+ if not t.isDistinguishableFrom(u):
+ raise WebIDLError("Flat member types of a union should be "
+ "distinguishable, " + str(t) + " is not "
+ "distinguishable from " + str(u),
+ [self.location, t.location, u.location])
+
+ return self
+
+ def isDistinguishableFrom(self, other):
+ if self.hasNullableType and other.nullable():
+ # Can't tell which type null should become
+ return False
+ if other.isUnion():
+ otherTypes = other.unroll().memberTypes
+ else:
+ otherTypes = [other]
+ # For every type in otherTypes, check that it's distinguishable from
+ # every type in our types
+ for u in otherTypes:
+ if any(not t.isDistinguishableFrom(u) for t in self.memberTypes):
+ return False
+ return True
+
+ def isExposedInAllOf(self, exposureSet):
+ # We could have different member types in different globals. Just make sure that each thing in exposureSet has one of our member types exposed in it.
+ for globalName in exposureSet:
+ if not any(t.unroll().isExposedInAllOf(set([globalName])) for t
+ in self.flatMemberTypes):
+ return False
+ return True
+
+ def _getDependentObjects(self):
+ return set(self.memberTypes)
+
+class IDLArrayType(IDLType):
+ def __init__(self, location, parameterType):
+ assert not parameterType.isVoid()
+ if parameterType.isSequence():
+ raise WebIDLError("Array type cannot parameterize over a sequence type",
+ [location])
+ if parameterType.isMozMap():
+ raise WebIDLError("Array type cannot parameterize over a MozMap type",
+ [location])
+ if parameterType.isDictionary():
+ raise WebIDLError("Array type cannot parameterize over a dictionary type",
+ [location])
+
+ IDLType.__init__(self, location, parameterType.name)
+ self.inner = parameterType
+ self.builtin = False
+
+ def __eq__(self, other):
+ return isinstance(other, IDLArrayType) and self.inner == other.inner
+
+ def __str__(self):
+ return self.inner.__str__() + "Array"
+
+ def nullable(self):
+ return False
+
+ def isPrimitive(self):
+ return False
+
+ def isString(self):
+ return False
+
+ def isByteString(self):
+ return False
+
+ def isDOMString(self):
+ return False
+
+ def isScalarValueString(self):
+ return False
+
+ def isVoid(self):
+ return False
+
+ def isSequence(self):
+ assert not self.inner.isSequence()
+ return False
+
+ def isArray(self):
+ return True
+
+ def isDictionary(self):
+ assert not self.inner.isDictionary()
+ return False
+
+ def isInterface(self):
+ return False
+
+ def isEnum(self):
+ return False
+
+ def tag(self):
+ return IDLType.Tags.array
+
+ def resolveType(self, parentScope):
+ assert isinstance(parentScope, IDLScope)
+ self.inner.resolveType(parentScope)
+
+ def isComplete(self):
+ return self.inner.isComplete()
+
+ def complete(self, scope):
+ self.inner = self.inner.complete(scope)
+ self.name = self.inner.name
+
+ if self.inner.isDictionary():
+ raise WebIDLError("Array type must not contain "
+ "dictionary as element type.",
+ [self.inner.location])
+
+ assert not self.inner.isSequence()
+
+ return self
+
+ def unroll(self):
+ return self.inner.unroll()
+
+ def isDistinguishableFrom(self, other):
+ if other.isUnion():
+ # Just forward to the union; it'll deal
+ return other.isDistinguishableFrom(self)
+ return (other.isPrimitive() or other.isString() or other.isEnum() or
+ other.isDate() or other.isNonCallbackInterface())
+
+ def _getDependentObjects(self):
+ return self.inner._getDependentObjects()
+
+class IDLTypedefType(IDLType, IDLObjectWithIdentifier):
+ def __init__(self, location, innerType, name):
+ IDLType.__init__(self, location, innerType.name)
+
+ identifier = IDLUnresolvedIdentifier(location, name)
+
+ IDLObjectWithIdentifier.__init__(self, location, None, identifier)
+
+ self.inner = innerType
+ self.name = name
+ self.builtin = False
+
+ def __eq__(self, other):
+ return isinstance(other, IDLTypedefType) and self.inner == other.inner
+
+ def __str__(self):
+ return self.identifier.name
+
+ def nullable(self):
+ return self.inner.nullable()
+
+ def isPrimitive(self):
+ return self.inner.isPrimitive()
+
+ def isBoolean(self):
+ return self.inner.isBoolean()
+
+ def isNumeric(self):
+ return self.inner.isNumeric()
+
+ def isString(self):
+ return self.inner.isString()
+
+ def isByteString(self):
+ return self.inner.isByteString()
+
+ def isDOMString(self):
+ return self.inner.isDOMString()
+
+ def isScalarValueString(self):
+ return self.inner.isScalarValueString()
+
+ def isVoid(self):
+ return self.inner.isVoid()
+
+ def isSequence(self):
+ return self.inner.isSequence()
+
+ def isMozMap(self):
+ return self.inner.isMozMap()
+
+ def isArray(self):
+ return self.inner.isArray()
+
+ def isDictionary(self):
+ return self.inner.isDictionary()
+
+ def isArrayBuffer(self):
+ return self.inner.isArrayBuffer()
+
+ def isArrayBufferView(self):
+ return self.inner.isArrayBufferView()
+
+ def isTypedArray(self):
+ return self.inner.isTypedArray()
+
+ def isInterface(self):
+ return self.inner.isInterface()
+
+ def isCallbackInterface(self):
+ return self.inner.isCallbackInterface()
+
+ def isNonCallbackInterface(self):
+ return self.inner.isNonCallbackInterface()
+
+ def isComplete(self):
+ return False
+
+ def complete(self, parentScope):
+ if not self.inner.isComplete():
+ self.inner = self.inner.complete(parentScope)
+ assert self.inner.isComplete()
+ return self.inner
+
+ def finish(self, parentScope):
+ # Maybe the IDLObjectWithIdentifier for the typedef should be
+ # a separate thing from the type? If that happens, we can
+ # remove some hackery around avoiding isInterface() in
+ # Configuration.py.
+ self.complete(parentScope)
+
+ def validate(self):
+ pass
+
+ # Do we need a resolveType impl? I don't think it's particularly useful....
+
+ def tag(self):
+ return self.inner.tag()
+
+ def unroll(self):
+ return self.inner.unroll()
+
+ def isDistinguishableFrom(self, other):
+ return self.inner.isDistinguishableFrom(other)
+
+ def _getDependentObjects(self):
+ return self.inner._getDependentObjects()
+
+class IDLWrapperType(IDLType):
+ def __init__(self, location, inner, promiseInnerType=None):
+ IDLType.__init__(self, location, inner.identifier.name)
+ self.inner = inner
+ self._identifier = inner.identifier
+ self.builtin = False
+ assert not promiseInnerType or inner.identifier.name == "Promise"
+ self._promiseInnerType = promiseInnerType
+
+ def __eq__(self, other):
+ return isinstance(other, IDLWrapperType) and \
+ self._identifier == other._identifier and \
+ self.builtin == other.builtin
+
+ def __str__(self):
+ return str(self.name) + " (Wrapper)"
+
+ def nullable(self):
+ return False
+
+ def isPrimitive(self):
+ return False
+
+ def isString(self):
+ return False
+
+ def isByteString(self):
+ return False
+
+ def isDOMString(self):
+ return False
+
+ def isScalarValueString(self):
+ return False
+
+ def isVoid(self):
+ return False
+
+ def isSequence(self):
+ return False
+
+ def isArray(self):
+ return False
+
+ def isDictionary(self):
+ return isinstance(self.inner, IDLDictionary)
+
+ def isInterface(self):
+ return isinstance(self.inner, IDLInterface) or \
+ isinstance(self.inner, IDLExternalInterface)
+
+ def isCallbackInterface(self):
+ return self.isInterface() and self.inner.isCallback()
+
+ def isNonCallbackInterface(self):
+ return self.isInterface() and not self.inner.isCallback()
+
+ def isEnum(self):
+ return isinstance(self.inner, IDLEnum)
+
+ def isPromise(self):
+ return isinstance(self.inner, IDLInterface) and \
+ self.inner.identifier.name == "Promise"
+
+ def isSerializable(self):
+ if self.isInterface():
+ if self.inner.isExternal():
+ return False
+ return any(m.isMethod() and m.isJsonifier() for m in self.inner.members)
+ elif self.isEnum():
+ return True
+ elif self.isDictionary():
+ return all(m.type.isSerializable() for m in self.inner.members)
+ else:
+ raise WebIDLError("IDLWrapperType wraps type %s that we don't know if "
+ "is serializable" % type(self.inner), [self.location])
+
+ def resolveType(self, parentScope):
+ assert isinstance(parentScope, IDLScope)
+ self.inner.resolve(parentScope)
+
+ def isComplete(self):
+ return True
+
+ def tag(self):
+ if self.isInterface():
+ return IDLType.Tags.interface
+ elif self.isEnum():
+ return IDLType.Tags.enum
+ elif self.isDictionary():
+ return IDLType.Tags.dictionary
+ else:
+ assert False
+
+ def isDistinguishableFrom(self, other):
+ if other.isUnion():
+ # Just forward to the union; it'll deal
+ return other.isDistinguishableFrom(self)
+ assert self.isInterface() or self.isEnum() or self.isDictionary()
+ if self.isEnum():
+ return (other.isPrimitive() or other.isInterface() or other.isObject() or
+ other.isCallback() or other.isDictionary() or
+ other.isSequence() or other.isMozMap() or other.isArray() or
+ other.isDate())
+ if self.isDictionary() and other.nullable():
+ return False
+ if other.isPrimitive() or other.isString() or other.isEnum() or other.isDate():
+ return True
+ if self.isDictionary():
+ return other.isNonCallbackInterface()
+
+ assert self.isInterface()
+ if other.isInterface():
+ if other.isSpiderMonkeyInterface():
+ # Just let |other| handle things
+ return other.isDistinguishableFrom(self)
+ assert self.isGeckoInterface() and other.isGeckoInterface()
+ if self.inner.isExternal() or other.unroll().inner.isExternal():
+ return self != other
+ return (len(self.inner.interfacesBasedOnSelf &
+ other.unroll().inner.interfacesBasedOnSelf) == 0 and
+ (self.isNonCallbackInterface() or
+ other.isNonCallbackInterface()))
+ if (other.isDictionary() or other.isCallback() or
+ other.isSequence() or other.isMozMap() or other.isArray()):
+ return self.isNonCallbackInterface()
+
+ # Not much else |other| can be
+ assert other.isObject()
+ return False
+
+ def isExposedInAllOf(self, exposureSet):
+ if not self.isInterface():
+ return True
+ iface = self.inner
+ if iface.isExternal():
+ # Let's say true, though ideally we'd only do this when
+ # exposureSet contains the primary global's name.
+ return True
+ if (iface.identifier.name == "Promise" and
+ # Check the internal type
+ not self._promiseInnerType.unroll().isExposedInAllOf(exposureSet)):
+ return False
+ return iface.exposureSet.issuperset(exposureSet)
+
+ def _getDependentObjects(self):
+ # NB: The codegen for an interface type depends on
+ # a) That the identifier is in fact an interface (as opposed to
+ # a dictionary or something else).
+ # b) The native type of the interface.
+ # If we depend on the interface object we will also depend on
+ # anything the interface depends on which is undesirable. We
+ # considered implementing a dependency just on the interface type
+ # file, but then every modification to an interface would cause this
+ # to be regenerated which is still undesirable. We decided not to
+ # depend on anything, reasoning that:
+ # 1) Changing the concrete type of the interface requires modifying
+ # Bindings.conf, which is still a global dependency.
+ # 2) Changing an interface to a dictionary (or vice versa) with the
+ # same identifier should be incredibly rare.
+ return set()
+
+class IDLBuiltinType(IDLType):
+
+ Types = enum(
+ # The integer types
+ 'byte',
+ 'octet',
+ 'short',
+ 'unsigned_short',
+ 'long',
+ 'unsigned_long',
+ 'long_long',
+ 'unsigned_long_long',
+ # Additional primitive types
+ 'boolean',
+ 'unrestricted_float',
+ 'float',
+ 'unrestricted_double',
+ # IMPORTANT: "double" must be the last primitive type listed
+ 'double',
+ # Other types
+ 'any',
+ 'domstring',
+ 'bytestring',
+ 'scalarvaluestring',
+ 'object',
+ 'date',
+ 'void',
+ # Funny stuff
+ 'ArrayBuffer',
+ 'ArrayBufferView',
+ 'Int8Array',
+ 'Uint8Array',
+ 'Uint8ClampedArray',
+ 'Int16Array',
+ 'Uint16Array',
+ 'Int32Array',
+ 'Uint32Array',
+ 'Float32Array',
+ 'Float64Array'
+ )
+
+ TagLookup = {
+ Types.byte: IDLType.Tags.int8,
+ Types.octet: IDLType.Tags.uint8,
+ Types.short: IDLType.Tags.int16,
+ Types.unsigned_short: IDLType.Tags.uint16,
+ Types.long: IDLType.Tags.int32,
+ Types.unsigned_long: IDLType.Tags.uint32,
+ Types.long_long: IDLType.Tags.int64,
+ Types.unsigned_long_long: IDLType.Tags.uint64,
+ Types.boolean: IDLType.Tags.bool,
+ Types.unrestricted_float: IDLType.Tags.unrestricted_float,
+ Types.float: IDLType.Tags.float,
+ Types.unrestricted_double: IDLType.Tags.unrestricted_double,
+ Types.double: IDLType.Tags.double,
+ Types.any: IDLType.Tags.any,
+ Types.domstring: IDLType.Tags.domstring,
+ Types.bytestring: IDLType.Tags.bytestring,
+ Types.scalarvaluestring: IDLType.Tags.scalarvaluestring,
+ Types.object: IDLType.Tags.object,
+ Types.date: IDLType.Tags.date,
+ Types.void: IDLType.Tags.void,
+ Types.ArrayBuffer: IDLType.Tags.interface,
+ Types.ArrayBufferView: IDLType.Tags.interface,
+ Types.Int8Array: IDLType.Tags.interface,
+ Types.Uint8Array: IDLType.Tags.interface,
+ Types.Uint8ClampedArray: IDLType.Tags.interface,
+ Types.Int16Array: IDLType.Tags.interface,
+ Types.Uint16Array: IDLType.Tags.interface,
+ Types.Int32Array: IDLType.Tags.interface,
+ Types.Uint32Array: IDLType.Tags.interface,
+ Types.Float32Array: IDLType.Tags.interface,
+ Types.Float64Array: IDLType.Tags.interface
+ }
+
+ def __init__(self, location, name, type):
+ IDLType.__init__(self, location, name)
+ self.builtin = True
+ self._typeTag = type
+
+ def isPrimitive(self):
+ return self._typeTag <= IDLBuiltinType.Types.double
+
+ def isBoolean(self):
+ return self._typeTag == IDLBuiltinType.Types.boolean
+
+ def isNumeric(self):
+ return self.isPrimitive() and not self.isBoolean()
+
+ def isString(self):
+ return self._typeTag == IDLBuiltinType.Types.domstring or \
+ self._typeTag == IDLBuiltinType.Types.bytestring or \
+ self._typeTag == IDLBuiltinType.Types.scalarvaluestring
+
+ def isByteString(self):
+ return self._typeTag == IDLBuiltinType.Types.bytestring
+
+ def isDOMString(self):
+ return self._typeTag == IDLBuiltinType.Types.domstring
+
+ def isScalarValueString(self):
+ return self._typeTag == IDLBuiltinType.Types.scalarvaluestring
+
+ def isInteger(self):
+ return self._typeTag <= IDLBuiltinType.Types.unsigned_long_long
+
+ def isArrayBuffer(self):
+ return self._typeTag == IDLBuiltinType.Types.ArrayBuffer
+
+ def isArrayBufferView(self):
+ return self._typeTag == IDLBuiltinType.Types.ArrayBufferView
+
+ def isTypedArray(self):
+ return self._typeTag >= IDLBuiltinType.Types.Int8Array and \
+ self._typeTag <= IDLBuiltinType.Types.Float64Array
+
+ def isInterface(self):
+ # TypedArray things are interface types per the TypedArray spec,
+ # but we handle them as builtins because SpiderMonkey implements
+ # all of it internally.
+ return self.isArrayBuffer() or \
+ self.isArrayBufferView() or \
+ self.isTypedArray()
+
+ def isNonCallbackInterface(self):
+ # All the interfaces we can be are non-callback
+ return self.isInterface()
+
+ def isFloat(self):
+ return self._typeTag == IDLBuiltinType.Types.float or \
+ self._typeTag == IDLBuiltinType.Types.double or \
+ self._typeTag == IDLBuiltinType.Types.unrestricted_float or \
+ self._typeTag == IDLBuiltinType.Types.unrestricted_double
+
+ def isUnrestricted(self):
+ assert self.isFloat()
+ return self._typeTag == IDLBuiltinType.Types.unrestricted_float or \
+ self._typeTag == IDLBuiltinType.Types.unrestricted_double
+
+ def isSerializable(self):
+ return self.isPrimitive() or self.isDOMString() or self.isDate()
+
+ def includesRestrictedFloat(self):
+ return self.isFloat() and not self.isUnrestricted()
+
+ def tag(self):
+ return IDLBuiltinType.TagLookup[self._typeTag]
+
+ def isDistinguishableFrom(self, other):
+ if other.isUnion():
+ # Just forward to the union; it'll deal
+ return other.isDistinguishableFrom(self)
+ if self.isBoolean():
+ return (other.isNumeric() or other.isString() or other.isEnum() or
+ other.isInterface() or other.isObject() or
+ other.isCallback() or other.isDictionary() or
+ other.isSequence() or other.isMozMap() or other.isArray() or
+ other.isDate())
+ if self.isNumeric():
+ return (other.isBoolean() or other.isString() or other.isEnum() or
+ other.isInterface() or other.isObject() or
+ other.isCallback() or other.isDictionary() or
+ other.isSequence() or other.isMozMap() or other.isArray() or
+ other.isDate())
+ if self.isString():
+ return (other.isPrimitive() or other.isInterface() or
+ other.isObject() or
+ other.isCallback() or other.isDictionary() or
+ other.isSequence() or other.isMozMap() or other.isArray() or
+ other.isDate())
+ if self.isAny():
+ # Can't tell "any" apart from anything
+ return False
+ if self.isObject():
+ return other.isPrimitive() or other.isString() or other.isEnum()
+ if self.isDate():
+ return (other.isPrimitive() or other.isString() or other.isEnum() or
+ other.isInterface() or other.isCallback() or
+ other.isDictionary() or other.isSequence() or
+ other.isMozMap() or other.isArray())
+ if self.isVoid():
+ return not other.isVoid()
+ # Not much else we could be!
+ assert self.isSpiderMonkeyInterface()
+ # Like interfaces, but we know we're not a callback
+ return (other.isPrimitive() or other.isString() or other.isEnum() or
+ other.isCallback() or other.isDictionary() or
+ other.isSequence() or other.isMozMap() or other.isArray() or
+ other.isDate() or
+ (other.isInterface() and (
+ # ArrayBuffer is distinguishable from everything
+ # that's not an ArrayBuffer or a callback interface
+ (self.isArrayBuffer() and not other.isArrayBuffer()) or
+ # ArrayBufferView is distinguishable from everything
+ # that's not an ArrayBufferView or typed array.
+ (self.isArrayBufferView() and not other.isArrayBufferView() and
+ not other.isTypedArray()) or
+ # Typed arrays are distinguishable from everything
+ # except ArrayBufferView and the same type of typed
+ # array
+ (self.isTypedArray() and not other.isArrayBufferView() and not
+ (other.isTypedArray() and other.name == self.name)))))
+
+ def _getDependentObjects(self):
+ return set()
+
+BuiltinTypes = {
+ IDLBuiltinType.Types.byte:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Byte",
+ IDLBuiltinType.Types.byte),
+ IDLBuiltinType.Types.octet:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Octet",
+ IDLBuiltinType.Types.octet),
+ IDLBuiltinType.Types.short:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Short",
+ IDLBuiltinType.Types.short),
+ IDLBuiltinType.Types.unsigned_short:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "UnsignedShort",
+ IDLBuiltinType.Types.unsigned_short),
+ IDLBuiltinType.Types.long:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Long",
+ IDLBuiltinType.Types.long),
+ IDLBuiltinType.Types.unsigned_long:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "UnsignedLong",
+ IDLBuiltinType.Types.unsigned_long),
+ IDLBuiltinType.Types.long_long:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "LongLong",
+ IDLBuiltinType.Types.long_long),
+ IDLBuiltinType.Types.unsigned_long_long:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "UnsignedLongLong",
+ IDLBuiltinType.Types.unsigned_long_long),
+ IDLBuiltinType.Types.boolean:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Boolean",
+ IDLBuiltinType.Types.boolean),
+ IDLBuiltinType.Types.float:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Float",
+ IDLBuiltinType.Types.float),
+ IDLBuiltinType.Types.unrestricted_float:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "UnrestrictedFloat",
+ IDLBuiltinType.Types.unrestricted_float),
+ IDLBuiltinType.Types.double:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Double",
+ IDLBuiltinType.Types.double),
+ IDLBuiltinType.Types.unrestricted_double:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "UnrestrictedDouble",
+ IDLBuiltinType.Types.unrestricted_double),
+ IDLBuiltinType.Types.any:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Any",
+ IDLBuiltinType.Types.any),
+ IDLBuiltinType.Types.domstring:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "String",
+ IDLBuiltinType.Types.domstring),
+ IDLBuiltinType.Types.bytestring:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "ByteString",
+ IDLBuiltinType.Types.bytestring),
+ IDLBuiltinType.Types.scalarvaluestring:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "ScalarValueString",
+ IDLBuiltinType.Types.scalarvaluestring),
+ IDLBuiltinType.Types.object:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Object",
+ IDLBuiltinType.Types.object),
+ IDLBuiltinType.Types.date:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Date",
+ IDLBuiltinType.Types.date),
+ IDLBuiltinType.Types.void:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Void",
+ IDLBuiltinType.Types.void),
+ IDLBuiltinType.Types.ArrayBuffer:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "ArrayBuffer",
+ IDLBuiltinType.Types.ArrayBuffer),
+ IDLBuiltinType.Types.ArrayBufferView:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "ArrayBufferView",
+ IDLBuiltinType.Types.ArrayBufferView),
+ IDLBuiltinType.Types.Int8Array:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Int8Array",
+ IDLBuiltinType.Types.Int8Array),
+ IDLBuiltinType.Types.Uint8Array:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Uint8Array",
+ IDLBuiltinType.Types.Uint8Array),
+ IDLBuiltinType.Types.Uint8ClampedArray:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Uint8ClampedArray",
+ IDLBuiltinType.Types.Uint8ClampedArray),
+ IDLBuiltinType.Types.Int16Array:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Int16Array",
+ IDLBuiltinType.Types.Int16Array),
+ IDLBuiltinType.Types.Uint16Array:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Uint16Array",
+ IDLBuiltinType.Types.Uint16Array),
+ IDLBuiltinType.Types.Int32Array:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Int32Array",
+ IDLBuiltinType.Types.Int32Array),
+ IDLBuiltinType.Types.Uint32Array:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Uint32Array",
+ IDLBuiltinType.Types.Uint32Array),
+ IDLBuiltinType.Types.Float32Array:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Float32Array",
+ IDLBuiltinType.Types.Float32Array),
+ IDLBuiltinType.Types.Float64Array:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Float64Array",
+ IDLBuiltinType.Types.Float64Array)
+ }
+
+
+integerTypeSizes = {
+ IDLBuiltinType.Types.byte: (-128, 127),
+ IDLBuiltinType.Types.octet: (0, 255),
+ IDLBuiltinType.Types.short: (-32768, 32767),
+ IDLBuiltinType.Types.unsigned_short: (0, 65535),
+ IDLBuiltinType.Types.long: (-2147483648, 2147483647),
+ IDLBuiltinType.Types.unsigned_long: (0, 4294967295),
+ IDLBuiltinType.Types.long_long: (-9223372036854775808,
+ 9223372036854775807),
+ IDLBuiltinType.Types.unsigned_long_long: (0, 18446744073709551615)
+ }
+
+def matchIntegerValueToType(value):
+ for type, extremes in integerTypeSizes.items():
+ (min, max) = extremes
+ if value <= max and value >= min:
+ return BuiltinTypes[type]
+
+ return None
+
+class IDLValue(IDLObject):
+ def __init__(self, location, type, value):
+ IDLObject.__init__(self, location)
+ self.type = type
+ assert isinstance(type, IDLType)
+
+ self.value = value
+
+ def coerceToType(self, type, location):
+ if type == self.type:
+ return self # Nothing to do
+
+ # We first check for unions to ensure that even if the union is nullable
+ # we end up with the right flat member type, not the union's type.
+ if type.isUnion():
+ # We use the flat member types here, because if we have a nullable
+ # member type, or a nested union, we want the type the value
+ # actually coerces to, not the nullable or nested union type.
+ for subtype in type.unroll().flatMemberTypes:
+ try:
+ coercedValue = self.coerceToType(subtype, location)
+ # Create a new IDLValue to make sure that we have the
+ # correct float/double type. This is necessary because we
+ # use the value's type when it is a default value of a
+ # union, and the union cares about the exact float type.
+ return IDLValue(self.location, subtype, coercedValue.value)
+ except:
+ pass
+ # If the type allows null, rerun this matching on the inner type, except
+ # nullable enums. We handle those specially, because we want our
+ # default string values to stay strings even when assigned to a nullable
+ # enum.
+ elif type.nullable() and not type.isEnum():
+ innerValue = self.coerceToType(type.inner, location)
+ return IDLValue(self.location, type, innerValue.value)
+
+ elif self.type.isInteger() and type.isInteger():
+ # We're both integer types. See if we fit.
+
+ (min, max) = integerTypeSizes[type._typeTag]
+ if self.value <= max and self.value >= min:
+ # Promote
+ return IDLValue(self.location, type, self.value)
+ else:
+ raise WebIDLError("Value %s is out of range for type %s." %
+ (self.value, type), [location])
+ elif self.type.isInteger() and type.isFloat():
+ # Convert an integer literal into float
+ if -2**24 <= self.value <= 2**24:
+ floatType = BuiltinTypes[IDLBuiltinType.Types.float]
+ return IDLValue(self.location, floatType, float(self.value))
+ else:
+ raise WebIDLError("Converting value %s to %s will lose precision." %
+ (self.value, type), [location])
+ elif self.type.isString() and type.isEnum():
+ # Just keep our string, but make sure it's a valid value for this enum
+ enum = type.unroll().inner
+ if self.value not in enum.values():
+ raise WebIDLError("'%s' is not a valid default value for enum %s"
+ % (self.value, enum.identifier.name),
+ [location, enum.location])
+ return self
+ elif self.type.isFloat() and type.isFloat():
+ if (not type.isUnrestricted() and
+ (self.value == float("inf") or self.value == float("-inf") or
+ math.isnan(self.value))):
+ raise WebIDLError("Trying to convert unrestricted value %s to non-unrestricted"
+ % self.value, [location]);
+ return self
+ elif self.type.isString() and type.isScalarValueString():
+ # Allow ScalarValueStrings to use default value just like
+ # DOMString. No coercion is required in this case as Codegen.py
+ # treats ScalarValueString just like DOMString, but with an
+ # extra normalization step.
+ assert self.type.isDOMString()
+ return self
+ raise WebIDLError("Cannot coerce type %s to type %s." %
+ (self.type, type), [location])
+
+ def _getDependentObjects(self):
+ return set()
+
+class IDLNullValue(IDLObject):
+ def __init__(self, location):
+ IDLObject.__init__(self, location)
+ self.type = None
+ self.value = None
+
+ def coerceToType(self, type, location):
+ if (not isinstance(type, IDLNullableType) and
+ not (type.isUnion() and type.hasNullableType) and
+ not (type.isUnion() and type.hasDictionaryType) and
+ not type.isDictionary() and
+ not type.isAny()):
+ raise WebIDLError("Cannot coerce null value to type %s." % type,
+ [location])
+
+ nullValue = IDLNullValue(self.location)
+ if type.isUnion() and not type.nullable() and type.hasDictionaryType:
+ # We're actually a default value for the union's dictionary member.
+ # Use its type.
+ for t in type.flatMemberTypes:
+ if t.isDictionary():
+ nullValue.type = t
+ return nullValue
+ nullValue.type = type
+ return nullValue
+
+ def _getDependentObjects(self):
+ return set()
+
+class IDLEmptySequenceValue(IDLObject):
+ def __init__(self, location):
+ IDLObject.__init__(self, location)
+ self.type = None
+ self.value = None
+
+ def coerceToType(self, type, location):
+ if type.isUnion():
+ # We use the flat member types here, because if we have a nullable
+ # member type, or a nested union, we want the type the value
+ # actually coerces to, not the nullable or nested union type.
+ for subtype in type.unroll().flatMemberTypes:
+ try:
+ return self.coerceToType(subtype, location)
+ except:
+ pass
+
+ if not type.isSequence():
+ raise WebIDLError("Cannot coerce empty sequence value to type %s." % type,
+ [location])
+
+ emptySequenceValue = IDLEmptySequenceValue(self.location)
+ emptySequenceValue.type = type
+ return emptySequenceValue
+
+ def _getDependentObjects(self):
+ return set()
+
+class IDLUndefinedValue(IDLObject):
+ def __init__(self, location):
+ IDLObject.__init__(self, location)
+ self.type = None
+ self.value = None
+
+ def coerceToType(self, type, location):
+ if not type.isAny():
+ raise WebIDLError("Cannot coerce undefined value to type %s." % type,
+ [location])
+
+ undefinedValue = IDLUndefinedValue(self.location)
+ undefinedValue.type = type
+ return undefinedValue
+
+ def _getDependentObjects(self):
+ return set()
+
+class IDLInterfaceMember(IDLObjectWithIdentifier):
+
+ Tags = enum(
+ 'Const',
+ 'Attr',
+ 'Method'
+ )
+
+ Special = enum(
+ 'Static',
+ 'Stringifier'
+ )
+
+ def __init__(self, location, identifier, tag):
+ IDLObjectWithIdentifier.__init__(self, location, None, identifier)
+ self.tag = tag
+ self._extendedAttrDict = {}
+ # _exposureGlobalNames are the global names listed in our [Exposed]
+ # extended attribute. exposureSet is the exposure set as defined in the
+ # Web IDL spec: it contains interface names.
+ self._exposureGlobalNames = set()
+ self.exposureSet = set()
+
+ def isMethod(self):
+ return self.tag == IDLInterfaceMember.Tags.Method
+
+ def isAttr(self):
+ return self.tag == IDLInterfaceMember.Tags.Attr
+
+ def isConst(self):
+ return self.tag == IDLInterfaceMember.Tags.Const
+
+ def addExtendedAttributes(self, attrs):
+ for attr in attrs:
+ self.handleExtendedAttribute(attr)
+ attrlist = attr.listValue()
+ self._extendedAttrDict[attr.identifier()] = attrlist if len(attrlist) else True
+
+ def handleExtendedAttribute(self, attr):
+ pass
+
+ def getExtendedAttribute(self, name):
+ return self._extendedAttrDict.get(name, None)
+
+ def finish(self, scope):
+ for globalName in self._exposureGlobalNames:
+ if globalName not in scope.globalNames:
+ raise WebIDLError("Unknown [Exposed] value %s" % globalName,
+ [self.location])
+ globalNameSetToExposureSet(scope, self._exposureGlobalNames,
+ self.exposureSet)
+ self._scope = scope
+
+ def validate(self):
+ if (self.getExtendedAttribute("Pref") and
+ self.exposureSet != set([self._scope.primaryGlobalName])):
+ raise WebIDLError("[Pref] used on an interface member that is not "
+ "%s-only" % self._scope.primaryGlobalName,
+ [self.location])
+
+class IDLConst(IDLInterfaceMember):
+ def __init__(self, location, identifier, type, value):
+ IDLInterfaceMember.__init__(self, location, identifier,
+ IDLInterfaceMember.Tags.Const)
+
+ assert isinstance(type, IDLType)
+ if type.isDictionary():
+ raise WebIDLError("A constant cannot be of a dictionary type",
+ [self.location])
+ self.type = type
+ self.value = value
+
+ if identifier.name == "prototype":
+ raise WebIDLError("The identifier of a constant must not be 'prototype'",
+ [location])
+
+ def __str__(self):
+ return "'%s' const '%s'" % (self.type, self.identifier)
+
+ def finish(self, scope):
+ IDLInterfaceMember.finish(self, scope)
+
+ if not self.type.isComplete():
+ type = self.type.complete(scope)
+ if not type.isPrimitive() and not type.isString():
+ locations = [self.type.location, type.location]
+ try:
+ locations.append(type.inner.location)
+ except:
+ pass
+ raise WebIDLError("Incorrect type for constant", locations)
+ self.type = type
+
+ # The value might not match the type
+ coercedValue = self.value.coerceToType(self.type, self.location)
+ assert coercedValue
+
+ self.value = coercedValue
+
+ def validate(self):
+ IDLInterfaceMember.validate(self)
+
+ def handleExtendedAttribute(self, attr):
+ identifier = attr.identifier()
+ if identifier == "Exposed":
+ convertExposedAttrToGlobalNameSet(attr, self._exposureGlobalNames)
+ elif (identifier == "Pref" or
+ identifier == "ChromeOnly" or
+ identifier == "Func" or
+ identifier == "AvailableIn" or
+ identifier == "CheckPermissions"):
+ # Known attributes that we don't need to do anything with here
+ pass
+ else:
+ raise WebIDLError("Unknown extended attribute %s on constant" % identifier,
+ [attr.location])
+ IDLInterfaceMember.handleExtendedAttribute(self, attr)
+
+ def _getDependentObjects(self):
+ return set([self.type, self.value])
+
+class IDLAttribute(IDLInterfaceMember):
+ def __init__(self, location, identifier, type, readonly, inherit=False,
+ static=False, stringifier=False):
+ IDLInterfaceMember.__init__(self, location, identifier,
+ IDLInterfaceMember.Tags.Attr)
+
+ assert isinstance(type, IDLType)
+ self.type = type
+ self.readonly = readonly
+ self.inherit = inherit
+ self.static = static
+ self.lenientThis = False
+ self._unforgeable = False
+ self.stringifier = stringifier
+ self.enforceRange = False
+ self.clamp = False
+ self.slotIndex = None
+
+ if static and identifier.name == "prototype":
+ raise WebIDLError("The identifier of a static attribute must not be 'prototype'",
+ [location])
+
+ if readonly and inherit:
+ raise WebIDLError("An attribute cannot be both 'readonly' and 'inherit'",
+ [self.location])
+
+ def isStatic(self):
+ return self.static
+
+ def __str__(self):
+ return "'%s' attribute '%s'" % (self.type, self.identifier)
+
+ def finish(self, scope):
+ IDLInterfaceMember.finish(self, scope)
+
+ if not self.type.isComplete():
+ t = self.type.complete(scope)
+
+ assert not isinstance(t, IDLUnresolvedType)
+ assert not isinstance(t, IDLTypedefType)
+ assert not isinstance(t.name, IDLUnresolvedIdentifier)
+ self.type = t
+
+ if self.type.isDictionary() and not self.getExtendedAttribute("Cached"):
+ raise WebIDLError("An attribute cannot be of a dictionary type",
+ [self.location])
+ if self.type.isSequence() and not self.getExtendedAttribute("Cached"):
+ raise WebIDLError("A non-cached attribute cannot be of a sequence "
+ "type", [self.location])
+ if self.type.isMozMap() and not self.getExtendedAttribute("Cached"):
+ raise WebIDLError("A non-cached attribute cannot be of a MozMap "
+ "type", [self.location])
+ if self.type.isUnion():
+ for f in self.type.unroll().flatMemberTypes:
+ if f.isDictionary():
+ raise WebIDLError("An attribute cannot be of a union "
+ "type if one of its member types (or "
+ "one of its member types's member "
+ "types, and so on) is a dictionary "
+ "type", [self.location, f.location])
+ if f.isSequence():
+ raise WebIDLError("An attribute cannot be of a union "
+ "type if one of its member types (or "
+ "one of its member types's member "
+ "types, and so on) is a sequence "
+ "type", [self.location, f.location])
+ if f.isMozMap():
+ raise WebIDLError("An attribute cannot be of a union "
+ "type if one of its member types (or "
+ "one of its member types's member "
+ "types, and so on) is a MozMap "
+ "type", [self.location, f.location])
+ if not self.type.isInterface() and self.getExtendedAttribute("PutForwards"):
+ raise WebIDLError("An attribute with [PutForwards] must have an "
+ "interface type as its type", [self.location])
+
+ if not self.type.isInterface() and self.getExtendedAttribute("SameObject"):
+ raise WebIDLError("An attribute with [SameObject] must have an "
+ "interface type as its type", [self.location])
+
+ def validate(self):
+ IDLInterfaceMember.validate(self)
+
+ if ((self.getExtendedAttribute("Cached") or
+ self.getExtendedAttribute("StoreInSlot")) and
+ not self.getExtendedAttribute("Constant") and
+ not self.getExtendedAttribute("Pure")):
+ raise WebIDLError("Cached attributes and attributes stored in "
+ "slots must be constant or pure, since the "
+ "getter won't always be called.",
+ [self.location])
+ if self.getExtendedAttribute("Frozen"):
+ if (not self.type.isSequence() and not self.type.isDictionary() and
+ not self.type.isMozMap()):
+ raise WebIDLError("[Frozen] is only allowed on "
+ "sequence-valued, dictionary-valued, and "
+ "MozMap-valued attributes",
+ [self.location])
+ if not self.type.unroll().isExposedInAllOf(self.exposureSet):
+ raise WebIDLError("Attribute returns a type that is not exposed "
+ "everywhere where the attribute is exposed",
+ [self.location])
+
+ def handleExtendedAttribute(self, attr):
+ identifier = attr.identifier()
+ if identifier == "SetterThrows" and self.readonly:
+ raise WebIDLError("Readonly attributes must not be flagged as "
+ "[SetterThrows]",
+ [self.location])
+ elif (((identifier == "Throws" or identifier == "GetterThrows") and
+ self.getExtendedAttribute("StoreInSlot")) or
+ (identifier == "StoreInSlot" and
+ (self.getExtendedAttribute("Throws") or
+ self.getExtendedAttribute("GetterThrows")))):
+ raise WebIDLError("Throwing things can't be [Pure] or [Constant] "
+ "or [SameObject] or [StoreInSlot]",
+ [attr.location])
+ elif identifier == "LenientThis":
+ if not attr.noArguments():
+ raise WebIDLError("[LenientThis] must take no arguments",
+ [attr.location])
+ if self.isStatic():
+ raise WebIDLError("[LenientThis] is only allowed on non-static "
+ "attributes", [attr.location, self.location])
+ if self.getExtendedAttribute("CrossOriginReadable"):
+ raise WebIDLError("[LenientThis] is not allowed in combination "
+ "with [CrossOriginReadable]",
+ [attr.location, self.location])
+ if self.getExtendedAttribute("CrossOriginWritable"):
+ raise WebIDLError("[LenientThis] is not allowed in combination "
+ "with [CrossOriginWritable]",
+ [attr.location, self.location])
+ self.lenientThis = True
+ elif identifier == "Unforgeable":
+ if self.isStatic():
+ raise WebIDLError("[Unforgeable] is only allowed on non-static "
+ "attributes", [attr.location, self.location])
+ self._unforgeable = True
+ elif identifier == "SameObject" and not self.readonly:
+ raise WebIDLError("[SameObject] only allowed on readonly attributes",
+ [attr.location, self.location])
+ elif identifier == "Constant" and not self.readonly:
+ raise WebIDLError("[Constant] only allowed on readonly attributes",
+ [attr.location, self.location])
+ elif identifier == "PutForwards":
+ if not self.readonly:
+ raise WebIDLError("[PutForwards] is only allowed on readonly "
+ "attributes", [attr.location, self.location])
+ if self.isStatic():
+ raise WebIDLError("[PutForwards] is only allowed on non-static "
+ "attributes", [attr.location, self.location])
+ if self.getExtendedAttribute("Replaceable") is not None:
+ raise WebIDLError("[PutForwards] and [Replaceable] can't both "
+ "appear on the same attribute",
+ [attr.location, self.location])
+ if not attr.hasValue():
+ raise WebIDLError("[PutForwards] takes an identifier",
+ [attr.location, self.location])
+ elif identifier == "Replaceable":
+ if self.getExtendedAttribute("PutForwards") is not None:
+ raise WebIDLError("[PutForwards] and [Replaceable] can't both "
+ "appear on the same attribute",
+ [attr.location, self.location])
+ elif identifier == "LenientFloat":
+ if self.readonly:
+ raise WebIDLError("[LenientFloat] used on a readonly attribute",
+ [attr.location, self.location])
+ if not self.type.includesRestrictedFloat():
+ raise WebIDLError("[LenientFloat] used on an attribute with a "
+ "non-restricted-float type",
+ [attr.location, self.location])
+ elif identifier == "EnforceRange":
+ if self.readonly:
+ raise WebIDLError("[EnforceRange] used on a readonly attribute",
+ [attr.location, self.location])
+ self.enforceRange = True
+ elif identifier == "Clamp":
+ if self.readonly:
+ raise WebIDLError("[Clamp] used on a readonly attribute",
+ [attr.location, self.location])
+ self.clamp = True
+ elif identifier == "StoreInSlot":
+ if self.getExtendedAttribute("Cached"):
+ raise WebIDLError("[StoreInSlot] and [Cached] must not be "
+ "specified on the same attribute",
+ [attr.location, self.location])
+ elif identifier == "Cached":
+ if self.getExtendedAttribute("StoreInSlot"):
+ raise WebIDLError("[Cached] and [StoreInSlot] must not be "
+ "specified on the same attribute",
+ [attr.location, self.location])
+ elif (identifier == "CrossOriginReadable" or
+ identifier == "CrossOriginWritable"):
+ if not attr.noArguments() and identifier == "CrossOriginReadable":
+ raise WebIDLError("[%s] must take no arguments" % identifier,
+ [attr.location])
+ if self.isStatic():
+ raise WebIDLError("[%s] is only allowed on non-static "
+ "attributes" % identifier,
+ [attr.location, self.location])
+ if self.getExtendedAttribute("LenientThis"):
+ raise WebIDLError("[LenientThis] is not allowed in combination "
+ "with [%s]" % identifier,
+ [attr.location, self.location])
+ elif identifier == "Exposed":
+ convertExposedAttrToGlobalNameSet(attr, self._exposureGlobalNames)
+ elif (identifier == "Pref" or
+ identifier == "SetterThrows" or
+ identifier == "Pure" or
+ identifier == "Throws" or
+ identifier == "GetterThrows" or
+ identifier == "ChromeOnly" or
+ identifier == "SameObject" or
+ identifier == "Constant" or
+ identifier == "Func" or
+ identifier == "Frozen" or
+ identifier == "AvailableIn" or
+ identifier == "NewObject" or
+ identifier == "CheckPermissions"):
+ # Known attributes that we don't need to do anything with here
+ pass
+ else:
+ raise WebIDLError("Unknown extended attribute %s on attribute" % identifier,
+ [attr.location])
+ IDLInterfaceMember.handleExtendedAttribute(self, attr)
+
+ def resolve(self, parentScope):
+ assert isinstance(parentScope, IDLScope)
+ self.type.resolveType(parentScope)
+ IDLObjectWithIdentifier.resolve(self, parentScope)
+
+ def addExtendedAttributes(self, attrs):
+ attrs = self.checkForStringHandlingExtendedAttributes(attrs)
+ IDLInterfaceMember.addExtendedAttributes(self, attrs)
+
+ def hasLenientThis(self):
+ return self.lenientThis
+
+ def isUnforgeable(self):
+ return self._unforgeable
+
+ def _getDependentObjects(self):
+ return set([self.type])
+
+class IDLArgument(IDLObjectWithIdentifier):
+ def __init__(self, location, identifier, type, optional=False, defaultValue=None, variadic=False, dictionaryMember=False):
+ IDLObjectWithIdentifier.__init__(self, location, None, identifier)
+
+ assert isinstance(type, IDLType)
+ self.type = type
+
+ self.optional = optional
+ self.defaultValue = defaultValue
+ self.variadic = variadic
+ self.dictionaryMember = dictionaryMember
+ self._isComplete = False
+ self.enforceRange = False
+ self.clamp = False
+ self._allowTreatNonCallableAsNull = False
+
+ assert not variadic or optional
+
+ def addExtendedAttributes(self, attrs):
+ attrs = self.checkForStringHandlingExtendedAttributes(
+ attrs,
+ isDictionaryMember=self.dictionaryMember,
+ isOptional=self.optional)
+ for attribute in attrs:
+ identifier = attribute.identifier()
+ if identifier == "Clamp":
+ if not attribute.noArguments():
+ raise WebIDLError("[Clamp] must take no arguments",
+ [attribute.location])
+ if self.enforceRange:
+ raise WebIDLError("[EnforceRange] and [Clamp] are mutually exclusive",
+ [self.location]);
+ self.clamp = True
+ elif identifier == "EnforceRange":
+ if not attribute.noArguments():
+ raise WebIDLError("[EnforceRange] must take no arguments",
+ [attribute.location])
+ if self.clamp:
+ raise WebIDLError("[EnforceRange] and [Clamp] are mutually exclusive",
+ [self.location]);
+ self.enforceRange = True
+ elif identifier == "TreatNonCallableAsNull":
+ self._allowTreatNonCallableAsNull = True
+ else:
+ raise WebIDLError("Unhandled extended attribute on an argument",
+ [attribute.location])
+
+ def isComplete(self):
+ return self._isComplete
+
+ def complete(self, scope):
+ if self._isComplete:
+ return
+
+ self._isComplete = True
+
+ if not self.type.isComplete():
+ type = self.type.complete(scope)
+ assert not isinstance(type, IDLUnresolvedType)
+ assert not isinstance(type, IDLTypedefType)
+ assert not isinstance(type.name, IDLUnresolvedIdentifier)
+ self.type = type
+
+ if ((self.type.isDictionary() or
+ self.type.isUnion() and self.type.unroll().hasDictionaryType) and
+ self.optional and not self.defaultValue):
+ # Default optional dictionaries to null, for simplicity,
+ # so the codegen doesn't have to special-case this.
+ self.defaultValue = IDLNullValue(self.location)
+ elif self.type.isAny():
+ assert (self.defaultValue is None or
+ isinstance(self.defaultValue, IDLNullValue))
+ # optional 'any' values always have a default value
+ if self.optional and not self.defaultValue and not self.variadic:
+ # Set the default value to undefined, for simplicity, so the
+ # codegen doesn't have to special-case this.
+ self.defaultValue = IDLUndefinedValue(self.location)
+
+ # Now do the coercing thing; this needs to happen after the
+ # above creation of a default value.
+ if self.defaultValue:
+ self.defaultValue = self.defaultValue.coerceToType(self.type,
+ self.location)
+ assert self.defaultValue
+
+ def allowTreatNonCallableAsNull(self):
+ return self._allowTreatNonCallableAsNull
+
+ def _getDependentObjects(self):
+ deps = set([self.type])
+ if self.defaultValue:
+ deps.add(self.defaultValue)
+ return deps
+
+class IDLCallbackType(IDLType, IDLObjectWithScope):
+ def __init__(self, location, parentScope, identifier, returnType, arguments):
+ assert isinstance(returnType, IDLType)
+
+ IDLType.__init__(self, location, identifier.name)
+
+ self._returnType = returnType
+ # Clone the list
+ self._arguments = list(arguments)
+
+ IDLObjectWithScope.__init__(self, location, parentScope, identifier)
+
+ for (returnType, arguments) in self.signatures():
+ for argument in arguments:
+ argument.resolve(self)
+
+ self._treatNonCallableAsNull = False
+ self._treatNonObjectAsNull = False
+
+ def module(self):
+ return self.location.filename().split('/')[-1].split('.webidl')[0] + 'Binding'
+
+ def isCallback(self):
+ return True
+
+ def signatures(self):
+ return [(self._returnType, self._arguments)]
+
+ def tag(self):
+ return IDLType.Tags.callback
+
+ def finish(self, scope):
+ if not self._returnType.isComplete():
+ type = self._returnType.complete(scope)
+
+ assert not isinstance(type, IDLUnresolvedType)
+ assert not isinstance(type, IDLTypedefType)
+ assert not isinstance(type.name, IDLUnresolvedIdentifier)
+ self._returnType = type
+
+ for argument in self._arguments:
+ if argument.type.isComplete():
+ continue
+
+ type = argument.type.complete(scope)
+
+ assert not isinstance(type, IDLUnresolvedType)
+ assert not isinstance(type, IDLTypedefType)
+ assert not isinstance(type.name, IDLUnresolvedIdentifier)
+ argument.type = type
+
+ def validate(self):
+ pass
+
+ def isDistinguishableFrom(self, other):
+ if other.isUnion():
+ # Just forward to the union; it'll deal
+ return other.isDistinguishableFrom(self)
+ return (other.isPrimitive() or other.isString() or other.isEnum() or
+ other.isNonCallbackInterface() or other.isDate())
+
+ def addExtendedAttributes(self, attrs):
+ unhandledAttrs = []
+ for attr in attrs:
+ if attr.identifier() == "TreatNonCallableAsNull":
+ self._treatNonCallableAsNull = True
+ elif attr.identifier() == "TreatNonObjectAsNull":
+ self._treatNonObjectAsNull = True
+ else:
+ unhandledAttrs.append(attr)
+ if self._treatNonCallableAsNull and self._treatNonObjectAsNull:
+ raise WebIDLError("Cannot specify both [TreatNonCallableAsNull] "
+ "and [TreatNonObjectAsNull]", [self.location])
+ if len(unhandledAttrs) != 0:
+ IDLType.addExtendedAttributes(self, unhandledAttrs)
+
+ def _getDependentObjects(self):
+ return set([self._returnType] + self._arguments)
+
+class IDLMethodOverload:
+ """
+ A class that represents a single overload of a WebIDL method. This is not
+ quite the same as an element of the "effective overload set" in the spec,
+ because separate IDLMethodOverloads are not created based on arguments being
+ optional. Rather, when multiple methods have the same name, there is an
+ IDLMethodOverload for each one, all hanging off an IDLMethod representing
+ the full set of overloads.
+ """
+ def __init__(self, returnType, arguments, location):
+ self.returnType = returnType
+ # Clone the list of arguments, just in case
+ self.arguments = list(arguments)
+ self.location = location
+
+ def _getDependentObjects(self):
+ deps = set(self.arguments)
+ deps.add(self.returnType)
+ return deps
+
+class IDLMethod(IDLInterfaceMember, IDLScope):
+
+ Special = enum(
+ 'Getter',
+ 'Setter',
+ 'Creator',
+ 'Deleter',
+ 'LegacyCaller',
+ base=IDLInterfaceMember.Special
+ )
+
+ TypeSuffixModifier = enum(
+ 'None',
+ 'QMark',
+ 'Brackets'
+ )
+
+ NamedOrIndexed = enum(
+ 'Neither',
+ 'Named',
+ 'Indexed'
+ )
+
+ def __init__(self, location, identifier, returnType, arguments,
+ static=False, getter=False, setter=False, creator=False,
+ deleter=False, specialType=NamedOrIndexed.Neither,
+ legacycaller=False, stringifier=False, jsonifier=False):
+ # REVIEW: specialType is NamedOrIndexed -- wow, this is messed up.
+ IDLInterfaceMember.__init__(self, location, identifier,
+ IDLInterfaceMember.Tags.Method)
+
+ self._hasOverloads = False
+
+ assert isinstance(returnType, IDLType)
+
+ # self._overloads is a list of IDLMethodOverloads
+ self._overloads = [IDLMethodOverload(returnType, arguments, location)]
+
+ assert isinstance(static, bool)
+ self._static = static
+ assert isinstance(getter, bool)
+ self._getter = getter
+ assert isinstance(setter, bool)
+ self._setter = setter
+ assert isinstance(creator, bool)
+ self._creator = creator
+ assert isinstance(deleter, bool)
+ self._deleter = deleter
+ assert isinstance(legacycaller, bool)
+ self._legacycaller = legacycaller
+ assert isinstance(stringifier, bool)
+ self._stringifier = stringifier
+ assert isinstance(jsonifier, bool)
+ self._jsonifier = jsonifier
+ self._specialType = specialType
+ self._unforgeable = False
+
+ if static and identifier.name == "prototype":
+ raise WebIDLError("The identifier of a static operation must not be 'prototype'",
+ [location])
+
+ self.assertSignatureConstraints()
+
+ def __str__(self):
+ return "Method '%s'" % self.identifier
+
+ def assertSignatureConstraints(self):
+ if self._getter or self._deleter:
+ assert len(self._overloads) == 1
+ overload = self._overloads[0]
+ arguments = overload.arguments
+ assert len(arguments) == 1
+ assert arguments[0].type == BuiltinTypes[IDLBuiltinType.Types.domstring] or \
+ arguments[0].type == BuiltinTypes[IDLBuiltinType.Types.unsigned_long]
+ assert not arguments[0].optional and not arguments[0].variadic
+ assert not self._getter or not overload.returnType.isVoid()
+
+ if self._setter or self._creator:
+ assert len(self._overloads) == 1
+ arguments = self._overloads[0].arguments
+ assert len(arguments) == 2
+ assert arguments[0].type == BuiltinTypes[IDLBuiltinType.Types.domstring] or \
+ arguments[0].type == BuiltinTypes[IDLBuiltinType.Types.unsigned_long]
+ assert not arguments[0].optional and not arguments[0].variadic
+ assert not arguments[1].optional and not arguments[1].variadic
+
+ if self._stringifier:
+ assert len(self._overloads) == 1
+ overload = self._overloads[0]
+ assert len(overload.arguments) == 0
+ assert overload.returnType == BuiltinTypes[IDLBuiltinType.Types.domstring]
+
+ if self._jsonifier:
+ assert len(self._overloads) == 1
+ overload = self._overloads[0]
+ assert len(overload.arguments) == 0
+ assert overload.returnType == BuiltinTypes[IDLBuiltinType.Types.object]
+
+ def isStatic(self):
+ return self._static
+
+ def isGetter(self):
+ return self._getter
+
+ def isSetter(self):
+ return self._setter
+
+ def isCreator(self):
+ return self._creator
+
+ def isDeleter(self):
+ return self._deleter
+
+ def isNamed(self):
+ assert self._specialType == IDLMethod.NamedOrIndexed.Named or \
+ self._specialType == IDLMethod.NamedOrIndexed.Indexed
+ return self._specialType == IDLMethod.NamedOrIndexed.Named
+
+ def isIndexed(self):
+ assert self._specialType == IDLMethod.NamedOrIndexed.Named or \
+ self._specialType == IDLMethod.NamedOrIndexed.Indexed
+ return self._specialType == IDLMethod.NamedOrIndexed.Indexed
+
+ def isLegacycaller(self):
+ return self._legacycaller
+
+ def isStringifier(self):
+ return self._stringifier
+
+ def isJsonifier(self):
+ return self._jsonifier
+
+ def hasOverloads(self):
+ return self._hasOverloads
+
+ def isIdentifierLess(self):
+ return self.identifier.name[:2] == "__" and self.identifier.name != "__noSuchMethod__"
+
+ def resolve(self, parentScope):
+ assert isinstance(parentScope, IDLScope)
+ IDLObjectWithIdentifier.resolve(self, parentScope)
+ IDLScope.__init__(self, self.location, parentScope, self.identifier)
+ for (returnType, arguments) in self.signatures():
+ for argument in arguments:
+ argument.resolve(self)
+
+ def addOverload(self, method):
+ assert len(method._overloads) == 1
+
+ if self._extendedAttrDict != method ._extendedAttrDict:
+ raise WebIDLError("Extended attributes differ on different "
+ "overloads of %s" % method.identifier,
+ [self.location, method.location])
+
+ self._overloads.extend(method._overloads)
+
+ self._hasOverloads = True
+
+ if self.isStatic() != method.isStatic():
+ raise WebIDLError("Overloaded identifier %s appears with different values of the 'static' attribute" % method.identifier,
+ [method.location])
+
+ if self.isLegacycaller() != method.isLegacycaller():
+ raise WebIDLError("Overloaded identifier %s appears with different values of the 'legacycaller' attribute" % method.identifier,
+ [method.location])
+
+ # Can't overload special things!
+ assert not self.isGetter()
+ assert not method.isGetter()
+ assert not self.isSetter()
+ assert not method.isSetter()
+ assert not self.isCreator()
+ assert not method.isCreator()
+ assert not self.isDeleter()
+ assert not method.isDeleter()
+ assert not self.isStringifier()
+ assert not method.isStringifier()
+ assert not self.isJsonifier()
+ assert not method.isJsonifier()
+
+ return self
+
+ def signatures(self):
+ return [(overload.returnType, overload.arguments) for overload in
+ self._overloads]
+
+ def finish(self, scope):
+ IDLInterfaceMember.finish(self, scope)
+
+ overloadWithPromiseReturnType = None
+ overloadWithoutPromiseReturnType = None
+ for overload in self._overloads:
+ variadicArgument = None
+
+ arguments = overload.arguments
+ for (idx, argument) in enumerate(arguments):
+ if not argument.isComplete():
+ argument.complete(scope)
+ assert argument.type.isComplete()
+
+ if (argument.type.isDictionary() or
+ (argument.type.isUnion() and
+ argument.type.unroll().hasDictionaryType)):
+ # Dictionaries and unions containing dictionaries at the
+ # end of the list or followed by optional arguments must be
+ # optional.
+ if (not argument.optional and
+ all(arg.optional for arg in arguments[idx+1:])):
+ raise WebIDLError("Dictionary argument or union "
+ "argument containing a dictionary "
+ "not followed by a required argument "
+ "must be optional",
+ [argument.location])
+
+ # An argument cannot be a Nullable Dictionary
+ if argument.type.nullable():
+ raise WebIDLError("An argument cannot be a nullable "
+ "dictionary or nullable union "
+ "containing a dictionary",
+ [argument.location])
+
+ # Only the last argument can be variadic
+ if variadicArgument:
+ raise WebIDLError("Variadic argument is not last argument",
+ [variadicArgument.location])
+ if argument.variadic:
+ variadicArgument = argument
+
+ returnType = overload.returnType
+ if not returnType.isComplete():
+ returnType = returnType.complete(scope)
+ assert not isinstance(returnType, IDLUnresolvedType)
+ assert not isinstance(returnType, IDLTypedefType)
+ assert not isinstance(returnType.name, IDLUnresolvedIdentifier)
+ overload.returnType = returnType
+
+ if returnType.isPromise():
+ overloadWithPromiseReturnType = overload
+ else:
+ overloadWithoutPromiseReturnType = overload
+
+ # Make sure either all our overloads return Promises or none do
+ if overloadWithPromiseReturnType and overloadWithoutPromiseReturnType:
+ raise WebIDLError("We have overloads with both Promise and "
+ "non-Promise return types",
+ [overloadWithPromiseReturnType.location,
+ overloadWithoutPromiseReturnType.location])
+
+ if overloadWithPromiseReturnType and self._legacycaller:
+ raise WebIDLError("May not have a Promise return type for a "
+ "legacycaller.",
+ [overloadWithPromiseReturnType.location])
+
+ # Now compute various information that will be used by the
+ # WebIDL overload resolution algorithm.
+ self.maxArgCount = max(len(s[1]) for s in self.signatures())
+ self.allowedArgCounts = [ i for i in range(self.maxArgCount+1)
+ if len(self.signaturesForArgCount(i)) != 0 ]
+
+ def validate(self):
+ IDLInterfaceMember.validate(self)
+
+ # Make sure our overloads are properly distinguishable and don't have
+ # different argument types before the distinguishing args.
+ for argCount in self.allowedArgCounts:
+ possibleOverloads = self.overloadsForArgCount(argCount)
+ if len(possibleOverloads) == 1:
+ continue
+ distinguishingIndex = self.distinguishingIndexForArgCount(argCount)
+ for idx in range(distinguishingIndex):
+ firstSigType = possibleOverloads[0].arguments[idx].type
+ for overload in possibleOverloads[1:]:
+ if overload.arguments[idx].type != firstSigType:
+ raise WebIDLError(
+ "Signatures for method '%s' with %d arguments have "
+ "different types of arguments at index %d, which "
+ "is before distinguishing index %d" %
+ (self.identifier.name, argCount, idx,
+ distinguishingIndex),
+ [self.location, overload.location])
+
+ for overload in self._overloads:
+ if not overload.returnType.unroll().isExposedInAllOf(self.exposureSet):
+ raise WebIDLError("Overload returns a type that is not exposed "
+ "everywhere where the method is exposed",
+ [overload.location])
+
+ def overloadsForArgCount(self, argc):
+ return [overload for overload in self._overloads if
+ len(overload.arguments) == argc or
+ (len(overload.arguments) > argc and
+ all(arg.optional for arg in overload.arguments[argc:])) or
+ (len(overload.arguments) < argc and
+ len(overload.arguments) > 0 and
+ overload.arguments[-1].variadic)]
+
+ def signaturesForArgCount(self, argc):
+ return [(overload.returnType, overload.arguments) for overload
+ in self.overloadsForArgCount(argc)]
+
+ def locationsForArgCount(self, argc):
+ return [overload.location for overload in self.overloadsForArgCount(argc)]
+
+ def distinguishingIndexForArgCount(self, argc):
+ def isValidDistinguishingIndex(idx, signatures):
+ for (firstSigIndex, (firstRetval, firstArgs)) in enumerate(signatures[:-1]):
+ for (secondRetval, secondArgs) in signatures[firstSigIndex+1:]:
+ if idx < len(firstArgs):
+ firstType = firstArgs[idx].type
+ else:
+ assert(firstArgs[-1].variadic)
+ firstType = firstArgs[-1].type
+ if idx < len(secondArgs):
+ secondType = secondArgs[idx].type
+ else:
+ assert(secondArgs[-1].variadic)
+ secondType = secondArgs[-1].type
+ if not firstType.isDistinguishableFrom(secondType):
+ return False
+ return True
+ signatures = self.signaturesForArgCount(argc)
+ for idx in range(argc):
+ if isValidDistinguishingIndex(idx, signatures):
+ return idx
+ # No valid distinguishing index. Time to throw
+ locations = self.locationsForArgCount(argc)
+ raise WebIDLError("Signatures with %d arguments for method '%s' are not "
+ "distinguishable" % (argc, self.identifier.name),
+ locations)
+
+ def handleExtendedAttribute(self, attr):
+ identifier = attr.identifier()
+ if identifier == "GetterThrows":
+ raise WebIDLError("Methods must not be flagged as "
+ "[GetterThrows]",
+ [attr.location, self.location])
+ elif identifier == "SetterThrows":
+ raise WebIDLError("Methods must not be flagged as "
+ "[SetterThrows]",
+ [attr.location, self.location])
+ elif identifier == "Unforgeable":
+ if self.isStatic():
+ raise WebIDLError("[Unforgeable] is only allowed on non-static "
+ "methods", [attr.location, self.location])
+ self._unforgeable = True
+ elif identifier == "SameObject":
+ raise WebIDLError("Methods must not be flagged as [SameObject]",
+ [attr.location, self.location]);
+ elif identifier == "Constant":
+ raise WebIDLError("Methods must not be flagged as [Constant]",
+ [attr.location, self.location]);
+ elif identifier == "PutForwards":
+ raise WebIDLError("Only attributes support [PutForwards]",
+ [attr.location, self.location])
+ elif identifier == "LenientFloat":
+ # This is called before we've done overload resolution
+ assert len(self.signatures()) == 1
+ sig = self.signatures()[0]
+ if not sig[0].isVoid():
+ raise WebIDLError("[LenientFloat] used on a non-void method",
+ [attr.location, self.location])
+ if not any(arg.type.includesRestrictedFloat() for arg in sig[1]):
+ raise WebIDLError("[LenientFloat] used on an operation with no "
+ "restricted float type arguments",
+ [attr.location, self.location])
+ elif identifier == "Exposed":
+ convertExposedAttrToGlobalNameSet(attr, self._exposureGlobalNames)
+ elif (identifier == "Pure" or
+ identifier == "CrossOriginCallable" or
+ identifier == "WebGLHandlesContextLoss"):
+ # Known no-argument attributes.
+ if not attr.noArguments():
+ raise WebIDLError("[%s] must take no arguments" % identifier,
+ [attr.location])
+ elif (identifier == "Throws" or
+ identifier == "NewObject" or
+ identifier == "ChromeOnly" or
+ identifier == "Pref" or
+ identifier == "Func" or
+ identifier == "AvailableIn" or
+ identifier == "CheckPermissions"):
+ # Known attributes that we don't need to do anything with here
+ pass
+ else:
+ raise WebIDLError("Unknown extended attribute %s on method" % identifier,
+ [attr.location])
+ IDLInterfaceMember.handleExtendedAttribute(self, attr)
+
+ def returnsPromise(self):
+ return self._overloads[0].returnType.isPromise()
+
+ def isUnforgeable(self):
+ return self._unforgeable
+
+ def _getDependentObjects(self):
+ deps = set()
+ for overload in self._overloads:
+ deps.union(overload._getDependentObjects())
+ return deps
+
+class IDLImplementsStatement(IDLObject):
+ def __init__(self, location, implementor, implementee):
+ IDLObject.__init__(self, location)
+ self.implementor = implementor;
+ self.implementee = implementee
+
+ def finish(self, scope):
+ assert(isinstance(self.implementor, IDLIdentifierPlaceholder))
+ assert(isinstance(self.implementee, IDLIdentifierPlaceholder))
+ implementor = self.implementor.finish(scope)
+ implementee = self.implementee.finish(scope)
+ # NOTE: we depend on not setting self.implementor and
+ # self.implementee here to keep track of the original
+ # locations.
+ if not isinstance(implementor, IDLInterface):
+ raise WebIDLError("Left-hand side of 'implements' is not an "
+ "interface",
+ [self.implementor.location])
+ if implementor.isCallback():
+ raise WebIDLError("Left-hand side of 'implements' is a callback "
+ "interface",
+ [self.implementor.location])
+ if not isinstance(implementee, IDLInterface):
+ raise WebIDLError("Right-hand side of 'implements' is not an "
+ "interface",
+ [self.implementee.location])
+ if implementee.isCallback():
+ raise WebIDLError("Right-hand side of 'implements' is a callback "
+ "interface",
+ [self.implementee.location])
+ implementor.addImplementedInterface(implementee)
+
+ def validate(self):
+ pass
+
+ def addExtendedAttributes(self, attrs):
+ assert len(attrs) == 0
+
+class IDLExtendedAttribute(IDLObject):
+ """
+ A class to represent IDL extended attributes so we can give them locations
+ """
+ def __init__(self, location, tuple):
+ IDLObject.__init__(self, location)
+ self._tuple = tuple
+
+ def identifier(self):
+ return self._tuple[0]
+
+ def noArguments(self):
+ return len(self._tuple) == 1
+
+ def hasValue(self):
+ return len(self._tuple) >= 2 and isinstance(self._tuple[1], str)
+
+ def value(self):
+ assert(self.hasValue())
+ return self._tuple[1]
+
+ def hasArgs(self):
+ return (len(self._tuple) == 2 and isinstance(self._tuple[1], list) or
+ len(self._tuple) == 3)
+
+ def args(self):
+ assert(self.hasArgs())
+ # Our args are our last element
+ return self._tuple[-1]
+
+ def listValue(self):
+ """
+ Backdoor for storing random data in _extendedAttrDict
+ """
+ return list(self._tuple)[1:]
+
+# Parser
+
+class Tokenizer(object):
+ tokens = [
+ "INTEGER",
+ "FLOATLITERAL",
+ "IDENTIFIER",
+ "STRING",
+ "WHITESPACE",
+ "OTHER"
+ ]
+
+ def t_FLOATLITERAL(self, t):
+ r'(-?(([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)([Ee][+-]?[0-9]+)?|[0-9]+[Ee][+-]?[0-9]+|Infinity))|NaN'
+ t.value = float(t.value)
+ return t
+
+ def t_INTEGER(self, t):
+ r'-?(0([0-7]+|[Xx][0-9A-Fa-f]+)?|[1-9][0-9]*)'
+ try:
+ # Can't use int(), because that doesn't handle octal properly.
+ t.value = parseInt(t.value)
+ except:
+ raise WebIDLError("Invalid integer literal",
+ [Location(lexer=self.lexer,
+ lineno=self.lexer.lineno,
+ lexpos=self.lexer.lexpos,
+ filename=self._filename)])
+ return t
+
+ def t_IDENTIFIER(self, t):
+ r'[A-Z_a-z][0-9A-Z_a-z-]*'
+ t.type = self.keywords.get(t.value, 'IDENTIFIER')
+ return t
+
+ def t_STRING(self, t):
+ r'"[^"]*"'
+ t.value = t.value[1:-1]
+ return t
+
+ def t_WHITESPACE(self, t):
+ r'[\t\n\r ]+|[\t\n\r ]*((//[^\n]*|/\*.*?\*/)[\t\n\r ]*)+'
+ pass
+
+ def t_ELLIPSIS(self, t):
+ r'\.\.\.'
+ t.type = self.keywords.get(t.value)
+ return t
+
+ def t_OTHER(self, t):
+ r'[^\t\n\r 0-9A-Z_a-z]'
+ t.type = self.keywords.get(t.value, 'OTHER')
+ return t
+
+ keywords = {
+ "module": "MODULE",
+ "interface": "INTERFACE",
+ "partial": "PARTIAL",
+ "dictionary": "DICTIONARY",
+ "exception": "EXCEPTION",
+ "enum": "ENUM",
+ "callback": "CALLBACK",
+ "typedef": "TYPEDEF",
+ "implements": "IMPLEMENTS",
+ "const": "CONST",
+ "null": "NULL",
+ "true": "TRUE",
+ "false": "FALSE",
+ "serializer": "SERIALIZER",
+ "stringifier": "STRINGIFIER",
+ "jsonifier": "JSONIFIER",
+ "unrestricted": "UNRESTRICTED",
+ "attribute": "ATTRIBUTE",
+ "readonly": "READONLY",
+ "inherit": "INHERIT",
+ "static": "STATIC",
+ "getter": "GETTER",
+ "setter": "SETTER",
+ "creator": "CREATOR",
+ "deleter": "DELETER",
+ "legacycaller": "LEGACYCALLER",
+ "optional": "OPTIONAL",
+ "...": "ELLIPSIS",
+ "::": "SCOPE",
+ "Date": "DATE",
+ "DOMString": "DOMSTRING",
+ "ByteString": "BYTESTRING",
+ "ScalarValueString": "SCALARVALUESTRING",
+ "any": "ANY",
+ "boolean": "BOOLEAN",
+ "byte": "BYTE",
+ "double": "DOUBLE",
+ "float": "FLOAT",
+ "long": "LONG",
+ "object": "OBJECT",
+ "octet": "OCTET",
+ "optional": "OPTIONAL",
+ "Promise": "PROMISE",
+ "sequence": "SEQUENCE",
+ "MozMap": "MOZMAP",
+ "short": "SHORT",
+ "unsigned": "UNSIGNED",
+ "void": "VOID",
+ ":": "COLON",
+ ";": "SEMICOLON",
+ "{": "LBRACE",
+ "}": "RBRACE",
+ "(": "LPAREN",
+ ")": "RPAREN",
+ "[": "LBRACKET",
+ "]": "RBRACKET",
+ "?": "QUESTIONMARK",
+ ",": "COMMA",
+ "=": "EQUALS",
+ "<": "LT",
+ ">": "GT",
+ "ArrayBuffer": "ARRAYBUFFER",
+ "or": "OR"
+ }
+
+ tokens.extend(keywords.values())
+
+ def t_error(self, t):
+ raise WebIDLError("Unrecognized Input",
+ [Location(lexer=self.lexer,
+ lineno=self.lexer.lineno,
+ lexpos=self.lexer.lexpos,
+ filename = self.filename)])
+
+ def __init__(self, outputdir, lexer=None):
+ if lexer:
+ self.lexer = lexer
+ else:
+ self.lexer = lex.lex(object=self,
+ outputdir=outputdir,
+ lextab='webidllex',
+ reflags=re.DOTALL)
+
+class SqueakyCleanLogger(object):
+ errorWhitelist = [
+ # Web IDL defines the WHITESPACE token, but doesn't actually
+ # use it ... so far.
+ "Token 'WHITESPACE' defined, but not used",
+ # And that means we have an unused token
+ "There is 1 unused token",
+ # Web IDL defines a OtherOrComma rule that's only used in
+ # ExtendedAttributeInner, which we don't use yet.
+ "Rule 'OtherOrComma' defined, but not used",
+ # And an unused rule
+ "There is 1 unused rule",
+ # And the OtherOrComma grammar symbol is unreachable.
+ "Symbol 'OtherOrComma' is unreachable",
+ # Which means the Other symbol is unreachable.
+ "Symbol 'Other' is unreachable",
+ ]
+ def __init__(self):
+ self.errors = []
+ def debug(self, msg, *args, **kwargs):
+ pass
+ info = debug
+ def warning(self, msg, *args, **kwargs):
+ if msg == "%s:%d: Rule '%s' defined, but not used":
+ # Munge things so we don't have to hardcode filenames and
+ # line numbers in our whitelist.
+ whitelistmsg = "Rule '%s' defined, but not used"
+ whitelistargs = args[2:]
+ else:
+ whitelistmsg = msg
+ whitelistargs = args
+ if (whitelistmsg % whitelistargs) not in SqueakyCleanLogger.errorWhitelist:
+ self.errors.append(msg % args)
+ error = warning
+
+ def reportGrammarErrors(self):
+ if self.errors:
+ raise WebIDLError("\n".join(self.errors), [])
+
+class Parser(Tokenizer):
+ def getLocation(self, p, i):
+ return Location(self.lexer, p.lineno(i), p.lexpos(i), self._filename)
+
+ def globalScope(self):
+ return self._globalScope
+
+ # The p_Foo functions here must match the WebIDL spec's grammar.
+ # It's acceptable to split things at '|' boundaries.
+ def p_Definitions(self, p):
+ """
+ Definitions : ExtendedAttributeList Definition Definitions
+ """
+ if p[2]:
+ p[0] = [p[2]]
+ p[2].addExtendedAttributes(p[1])
+ else:
+ assert not p[1]
+ p[0] = []
+
+ p[0].extend(p[3])
+
+ def p_DefinitionsEmpty(self, p):
+ """
+ Definitions :
+ """
+ p[0] = []
+
+ def p_Definition(self, p):
+ """
+ Definition : CallbackOrInterface
+ | PartialInterface
+ | Dictionary
+ | Exception
+ | Enum
+ | Typedef
+ | ImplementsStatement
+ """
+ p[0] = p[1]
+ assert p[1] # We might not have implemented something ...
+
+ def p_CallbackOrInterfaceCallback(self, p):
+ """
+ CallbackOrInterface : CALLBACK CallbackRestOrInterface
+ """
+ if p[2].isInterface():
+ assert isinstance(p[2], IDLInterface)
+ p[2].setCallback(True)
+
+ p[0] = p[2]
+
+ def p_CallbackOrInterfaceInterface(self, p):
+ """
+ CallbackOrInterface : Interface
+ """
+ p[0] = p[1]
+
+ def p_CallbackRestOrInterface(self, p):
+ """
+ CallbackRestOrInterface : CallbackRest
+ | Interface
+ """
+ assert p[1]
+ p[0] = p[1]
+
+ def p_Interface(self, p):
+ """
+ Interface : INTERFACE IDENTIFIER Inheritance LBRACE InterfaceMembers RBRACE SEMICOLON
+ """
+ location = self.getLocation(p, 1)
+ identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
+ members = p[5]
+ parent = p[3]
+
+ try:
+ existingObj = self.globalScope()._lookupIdentifier(identifier)
+ if existingObj:
+ p[0] = existingObj
+ if not isinstance(p[0], IDLInterface):
+ raise WebIDLError("Interface has the same name as "
+ "non-interface object",
+ [location, p[0].location])
+ p[0].setNonPartial(location, parent, members)
+ return
+ except Exception, ex:
+ if isinstance(ex, WebIDLError):
+ raise ex
+ pass
+
+ p[0] = IDLInterface(location, self.globalScope(), identifier, parent,
+ members, isKnownNonPartial=True)
+
+ def p_InterfaceForwardDecl(self, p):
+ """
+ Interface : INTERFACE IDENTIFIER SEMICOLON
+ """
+ location = self.getLocation(p, 1)
+ identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
+
+ try:
+ if self.globalScope()._lookupIdentifier(identifier):
+ p[0] = self.globalScope()._lookupIdentifier(identifier)
+ if not isinstance(p[0], IDLExternalInterface):
+ raise WebIDLError("Name collision between external "
+ "interface declaration for identifier "
+ "%s and %s" % (identifier.name, p[0]),
+ [location, p[0].location])
+ return
+ except Exception, ex:
+ if isinstance(ex, WebIDLError):
+ raise ex
+ pass
+
+ p[0] = IDLExternalInterface(location, self.globalScope(), identifier)
+
+ def p_PartialInterface(self, p):
+ """
+ PartialInterface : PARTIAL INTERFACE IDENTIFIER LBRACE InterfaceMembers RBRACE SEMICOLON
+ """
+ location = self.getLocation(p, 2)
+ identifier = IDLUnresolvedIdentifier(self.getLocation(p, 3), p[3])
+ members = p[5]
+
+ nonPartialInterface = None
+ try:
+ nonPartialInterface = self.globalScope()._lookupIdentifier(identifier)
+ if nonPartialInterface:
+ if not isinstance(nonPartialInterface, IDLInterface):
+ raise WebIDLError("Partial interface has the same name as "
+ "non-interface object",
+ [location, nonPartialInterface.location])
+ except Exception, ex:
+ if isinstance(ex, WebIDLError):
+ raise ex
+ pass
+
+ if not nonPartialInterface:
+ nonPartialInterface = IDLInterface(location, self.globalScope(),
+ identifier, None,
+ [], isKnownNonPartial=False)
+ partialInterface = IDLPartialInterface(location, identifier, members,
+ nonPartialInterface)
+ p[0] = partialInterface
+
+ def p_Inheritance(self, p):
+ """
+ Inheritance : COLON ScopedName
+ """
+ p[0] = IDLIdentifierPlaceholder(self.getLocation(p, 2), p[2])
+
+ def p_InheritanceEmpty(self, p):
+ """
+ Inheritance :
+ """
+ pass
+
+ def p_InterfaceMembers(self, p):
+ """
+ InterfaceMembers : ExtendedAttributeList InterfaceMember InterfaceMembers
+ """
+ p[0] = [p[2]] if p[2] else []
+
+ assert not p[1] or p[2]
+ p[2].addExtendedAttributes(p[1])
+
+ p[0].extend(p[3])
+
+ def p_InterfaceMembersEmpty(self, p):
+ """
+ InterfaceMembers :
+ """
+ p[0] = []
+
+ def p_InterfaceMember(self, p):
+ """
+ InterfaceMember : Const
+ | AttributeOrOperation
+ """
+ p[0] = p[1]
+
+ def p_Dictionary(self, p):
+ """
+ Dictionary : DICTIONARY IDENTIFIER Inheritance LBRACE DictionaryMembers RBRACE SEMICOLON
+ """
+ location = self.getLocation(p, 1)
+ identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
+ members = p[5]
+ p[0] = IDLDictionary(location, self.globalScope(), identifier, p[3], members)
+
+ def p_DictionaryMembers(self, p):
+ """
+ DictionaryMembers : ExtendedAttributeList DictionaryMember DictionaryMembers
+ |
+ """
+ if len(p) == 1:
+ # We're at the end of the list
+ p[0] = []
+ return
+ # Add our extended attributes
+ p[2].addExtendedAttributes(p[1])
+ p[0] = [p[2]]
+ p[0].extend(p[3])
+
+ def p_DictionaryMember(self, p):
+ """
+ DictionaryMember : Type IDENTIFIER Default SEMICOLON
+ """
+ # These quack a lot like optional arguments, so just treat them that way.
+ t = p[1]
+ assert isinstance(t, IDLType)
+ identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
+ defaultValue = p[3]
+
+ p[0] = IDLArgument(self.getLocation(p, 2), identifier, t, optional=True,
+ defaultValue=defaultValue, variadic=False,
+ dictionaryMember=True)
+
+ def p_Default(self, p):
+ """
+ Default : EQUALS DefaultValue
+ |
+ """
+ if len(p) > 1:
+ p[0] = p[2]
+ else:
+ p[0] = None
+
+ def p_DefaultValue(self, p):
+ """
+ DefaultValue : ConstValue
+ | LBRACKET RBRACKET
+ """
+ if len(p) == 2:
+ p[0] = p[1]
+ else:
+ assert len(p) == 3 # Must be []
+ p[0] = IDLEmptySequenceValue(self.getLocation(p, 1))
+
+ def p_Exception(self, p):
+ """
+ Exception : EXCEPTION IDENTIFIER Inheritance LBRACE ExceptionMembers RBRACE SEMICOLON
+ """
+ pass
+
+ def p_Enum(self, p):
+ """
+ Enum : ENUM IDENTIFIER LBRACE EnumValueList RBRACE SEMICOLON
+ """
+ location = self.getLocation(p, 1)
+ identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
+
+ values = p[4]
+ assert values
+ p[0] = IDLEnum(location, self.globalScope(), identifier, values)
+
+ def p_EnumValueList(self, p):
+ """
+ EnumValueList : STRING EnumValueListComma
+ """
+ p[0] = [p[1]]
+ p[0].extend(p[2])
+
+ def p_EnumValueListComma(self, p):
+ """
+ EnumValueListComma : COMMA EnumValueListString
+ """
+ p[0] = p[2]
+
+ def p_EnumValueListCommaEmpty(self, p):
+ """
+ EnumValueListComma :
+ """
+ p[0] = []
+
+ def p_EnumValueListString(self, p):
+ """
+ EnumValueListString : STRING EnumValueListComma
+ """
+ p[0] = [p[1]]
+ p[0].extend(p[2])
+
+ def p_EnumValueListStringEmpty(self, p):
+ """
+ EnumValueListString :
+ """
+ p[0] = []
+
+ def p_CallbackRest(self, p):
+ """
+ CallbackRest : IDENTIFIER EQUALS ReturnType LPAREN ArgumentList RPAREN SEMICOLON
+ """
+ identifier = IDLUnresolvedIdentifier(self.getLocation(p, 1), p[1])
+ p[0] = IDLCallbackType(self.getLocation(p, 1), self.globalScope(),
+ identifier, p[3], p[5])
+
+ def p_ExceptionMembers(self, p):
+ """
+ ExceptionMembers : ExtendedAttributeList ExceptionMember ExceptionMembers
+ |
+ """
+ pass
+
+ def p_Typedef(self, p):
+ """
+ Typedef : TYPEDEF Type IDENTIFIER SEMICOLON
+ """
+ typedef = IDLTypedefType(self.getLocation(p, 1), p[2], p[3])
+ typedef.resolve(self.globalScope())
+ p[0] = typedef
+
+ def p_ImplementsStatement(self, p):
+ """
+ ImplementsStatement : ScopedName IMPLEMENTS ScopedName SEMICOLON
+ """
+ assert(p[2] == "implements")
+ implementor = IDLIdentifierPlaceholder(self.getLocation(p, 1), p[1])
+ implementee = IDLIdentifierPlaceholder(self.getLocation(p, 3), p[3])
+ p[0] = IDLImplementsStatement(self.getLocation(p, 1), implementor,
+ implementee)
+
+ def p_Const(self, p):
+ """
+ Const : CONST ConstType IDENTIFIER EQUALS ConstValue SEMICOLON
+ """
+ location = self.getLocation(p, 1)
+ type = p[2]
+ identifier = IDLUnresolvedIdentifier(self.getLocation(p, 3), p[3])
+ value = p[5]
+ p[0] = IDLConst(location, identifier, type, value)
+
+ def p_ConstValueBoolean(self, p):
+ """
+ ConstValue : BooleanLiteral
+ """
+ location = self.getLocation(p, 1)
+ booleanType = BuiltinTypes[IDLBuiltinType.Types.boolean]
+ p[0] = IDLValue(location, booleanType, p[1])
+
+ def p_ConstValueInteger(self, p):
+ """
+ ConstValue : INTEGER
+ """
+ location = self.getLocation(p, 1)
+
+ # We don't know ahead of time what type the integer literal is.
+ # Determine the smallest type it could possibly fit in and use that.
+ integerType = matchIntegerValueToType(p[1])
+ if integerType == None:
+ raise WebIDLError("Integer literal out of range", [location])
+
+ p[0] = IDLValue(location, integerType, p[1])
+
+ def p_ConstValueFloat(self, p):
+ """
+ ConstValue : FLOATLITERAL
+ """
+ location = self.getLocation(p, 1)
+ p[0] = IDLValue(location, BuiltinTypes[IDLBuiltinType.Types.unrestricted_float], p[1])
+
+ def p_ConstValueString(self, p):
+ """
+ ConstValue : STRING
+ """
+ location = self.getLocation(p, 1)
+ stringType = BuiltinTypes[IDLBuiltinType.Types.domstring]
+ p[0] = IDLValue(location, stringType, p[1])
+
+ def p_ConstValueNull(self, p):
+ """
+ ConstValue : NULL
+ """
+ p[0] = IDLNullValue(self.getLocation(p, 1))
+
+ def p_BooleanLiteralTrue(self, p):
+ """
+ BooleanLiteral : TRUE
+ """
+ p[0] = True
+
+ def p_BooleanLiteralFalse(self, p):
+ """
+ BooleanLiteral : FALSE
+ """
+ p[0] = False
+
+ def p_AttributeOrOperation(self, p):
+ """
+ AttributeOrOperation : Attribute
+ | Operation
+ """
+ p[0] = p[1]
+
+ def p_AttributeWithQualifier(self, p):
+ """
+ Attribute : Qualifier AttributeRest
+ """
+ static = IDLInterfaceMember.Special.Static in p[1]
+ stringifier = IDLInterfaceMember.Special.Stringifier in p[1]
+ (location, identifier, type, readonly) = p[2]
+ p[0] = IDLAttribute(location, identifier, type, readonly, static=static,
+ stringifier=stringifier)
+
+ def p_Attribute(self, p):
+ """
+ Attribute : Inherit AttributeRest
+ """
+ (location, identifier, type, readonly) = p[2]
+ p[0] = IDLAttribute(location, identifier, type, readonly, inherit=p[1])
+
+ def p_AttributeRest(self, p):
+ """
+ AttributeRest : ReadOnly ATTRIBUTE Type IDENTIFIER SEMICOLON
+ """
+ location = self.getLocation(p, 2)
+ readonly = p[1]
+ t = p[3]
+ identifier = IDLUnresolvedIdentifier(self.getLocation(p, 4), p[4])
+ p[0] = (location, identifier, t, readonly)
+
+ def p_ReadOnly(self, p):
+ """
+ ReadOnly : READONLY
+ """
+ p[0] = True
+
+ def p_ReadOnlyEmpty(self, p):
+ """
+ ReadOnly :
+ """
+ p[0] = False
+
+ def p_Inherit(self, p):
+ """
+ Inherit : INHERIT
+ """
+ p[0] = True
+
+ def p_InheritEmpty(self, p):
+ """
+ Inherit :
+ """
+ p[0] = False
+
+ def p_Operation(self, p):
+ """
+ Operation : Qualifiers OperationRest
+ """
+ qualifiers = p[1]
+
+ # Disallow duplicates in the qualifier set
+ if not len(set(qualifiers)) == len(qualifiers):
+ raise WebIDLError("Duplicate qualifiers are not allowed",
+ [self.getLocation(p, 1)])
+
+ static = IDLInterfaceMember.Special.Static in p[1]
+ # If static is there that's all that's allowed. This is disallowed
+ # by the parser, so we can assert here.
+ assert not static or len(qualifiers) == 1
+
+ stringifier = IDLInterfaceMember.Special.Stringifier in p[1]
+ # If stringifier is there that's all that's allowed. This is disallowed
+ # by the parser, so we can assert here.
+ assert not stringifier or len(qualifiers) == 1
+
+ getter = True if IDLMethod.Special.Getter in p[1] else False
+ setter = True if IDLMethod.Special.Setter in p[1] else False
+ creator = True if IDLMethod.Special.Creator in p[1] else False
+ deleter = True if IDLMethod.Special.Deleter in p[1] else False
+ legacycaller = True if IDLMethod.Special.LegacyCaller in p[1] else False
+
+ if getter or deleter:
+ if setter or creator:
+ raise WebIDLError("getter and deleter are incompatible with setter and creator",
+ [self.getLocation(p, 1)])
+
+ (returnType, identifier, arguments) = p[2]
+
+ assert isinstance(returnType, IDLType)
+
+ specialType = IDLMethod.NamedOrIndexed.Neither
+
+ if getter or deleter:
+ if len(arguments) != 1:
+ raise WebIDLError("%s has wrong number of arguments" %
+ ("getter" if getter else "deleter"),
+ [self.getLocation(p, 2)])
+ argType = arguments[0].type
+ if argType == BuiltinTypes[IDLBuiltinType.Types.domstring]:
+ specialType = IDLMethod.NamedOrIndexed.Named
+ elif argType == BuiltinTypes[IDLBuiltinType.Types.unsigned_long]:
+ specialType = IDLMethod.NamedOrIndexed.Indexed
+ else:
+ raise WebIDLError("%s has wrong argument type (must be DOMString or UnsignedLong)" %
+ ("getter" if getter else "deleter"),
+ [arguments[0].location])
+ if arguments[0].optional or arguments[0].variadic:
+ raise WebIDLError("%s cannot have %s argument" %
+ ("getter" if getter else "deleter",
+ "optional" if arguments[0].optional else "variadic"),
+ [arguments[0].location])
+ if getter:
+ if returnType.isVoid():
+ raise WebIDLError("getter cannot have void return type",
+ [self.getLocation(p, 2)])
+ if setter or creator:
+ if len(arguments) != 2:
+ raise WebIDLError("%s has wrong number of arguments" %
+ ("setter" if setter else "creator"),
+ [self.getLocation(p, 2)])
+ argType = arguments[0].type
+ if argType == BuiltinTypes[IDLBuiltinType.Types.domstring]:
+ specialType = IDLMethod.NamedOrIndexed.Named
+ elif argType == BuiltinTypes[IDLBuiltinType.Types.unsigned_long]:
+ specialType = IDLMethod.NamedOrIndexed.Indexed
+ else:
+ raise WebIDLError("%s has wrong argument type (must be DOMString or UnsignedLong)" %
+ ("setter" if setter else "creator"),
+ [arguments[0].location])
+ if arguments[0].optional or arguments[0].variadic:
+ raise WebIDLError("%s cannot have %s argument" %
+ ("setter" if setter else "creator",
+ "optional" if arguments[0].optional else "variadic"),
+ [arguments[0].location])
+ if arguments[1].optional or arguments[1].variadic:
+ raise WebIDLError("%s cannot have %s argument" %
+ ("setter" if setter else "creator",
+ "optional" if arguments[1].optional else "variadic"),
+ [arguments[1].location])
+
+ if stringifier:
+ if len(arguments) != 0:
+ raise WebIDLError("stringifier has wrong number of arguments",
+ [self.getLocation(p, 2)])
+ if not returnType.isDOMString():
+ raise WebIDLError("stringifier must have DOMString return type",
+ [self.getLocation(p, 2)])
+
+ # identifier might be None. This is only permitted for special methods.
+ if not identifier:
+ if not getter and not setter and not creator and \
+ not deleter and not legacycaller and not stringifier:
+ raise WebIDLError("Identifier required for non-special methods",
+ [self.getLocation(p, 2)])
+
+ location = BuiltinLocation("<auto-generated-identifier>")
+ identifier = IDLUnresolvedIdentifier(location, "__%s%s%s%s%s%s%s" %
+ ("named" if specialType == IDLMethod.NamedOrIndexed.Named else \
+ "indexed" if specialType == IDLMethod.NamedOrIndexed.Indexed else "",
+ "getter" if getter else "",
+ "setter" if setter else "",
+ "deleter" if deleter else "",
+ "creator" if creator else "",
+ "legacycaller" if legacycaller else "",
+ "stringifier" if stringifier else ""), allowDoubleUnderscore=True)
+
+ method = IDLMethod(self.getLocation(p, 2), identifier, returnType, arguments,
+ static=static, getter=getter, setter=setter, creator=creator,
+ deleter=deleter, specialType=specialType,
+ legacycaller=legacycaller, stringifier=stringifier)
+ p[0] = method
+
+ def p_Stringifier(self, p):
+ """
+ Operation : STRINGIFIER SEMICOLON
+ """
+ identifier = IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"),
+ "__stringifier",
+ allowDoubleUnderscore=True)
+ method = IDLMethod(self.getLocation(p, 1),
+ identifier,
+ returnType=BuiltinTypes[IDLBuiltinType.Types.domstring],
+ arguments=[],
+ stringifier=True)
+ p[0] = method
+
+ def p_Jsonifier(self, p):
+ """
+ Operation : JSONIFIER SEMICOLON
+ """
+ identifier = IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"),
+ "__jsonifier", allowDoubleUnderscore=True)
+ method = IDLMethod(self.getLocation(p, 1),
+ identifier,
+ returnType=BuiltinTypes[IDLBuiltinType.Types.object],
+ arguments=[],
+ jsonifier=True)
+ p[0] = method
+
+ def p_QualifierStatic(self, p):
+ """
+ Qualifier : STATIC
+ """
+ p[0] = [IDLInterfaceMember.Special.Static]
+
+ def p_QualifierStringifier(self, p):
+ """
+ Qualifier : STRINGIFIER
+ """
+ p[0] = [IDLInterfaceMember.Special.Stringifier]
+
+ def p_Qualifiers(self, p):
+ """
+ Qualifiers : Qualifier
+ | Specials
+ """
+ p[0] = p[1]
+
+ def p_Specials(self, p):
+ """
+ Specials : Special Specials
+ """
+ p[0] = [p[1]]
+ p[0].extend(p[2])
+
+ def p_SpecialsEmpty(self, p):
+ """
+ Specials :
+ """
+ p[0] = []
+
+ def p_SpecialGetter(self, p):
+ """
+ Special : GETTER
+ """
+ p[0] = IDLMethod.Special.Getter
+
+ def p_SpecialSetter(self, p):
+ """
+ Special : SETTER
+ """
+ p[0] = IDLMethod.Special.Setter
+
+ def p_SpecialCreator(self, p):
+ """
+ Special : CREATOR
+ """
+ p[0] = IDLMethod.Special.Creator
+
+ def p_SpecialDeleter(self, p):
+ """
+ Special : DELETER
+ """
+ p[0] = IDLMethod.Special.Deleter
+
+ def p_SpecialLegacyCaller(self, p):
+ """
+ Special : LEGACYCALLER
+ """
+ p[0] = IDLMethod.Special.LegacyCaller
+
+ def p_OperationRest(self, p):
+ """
+ OperationRest : ReturnType OptionalIdentifier LPAREN ArgumentList RPAREN SEMICOLON
+ """
+ p[0] = (p[1], p[2], p[4])
+
+ def p_OptionalIdentifier(self, p):
+ """
+ OptionalIdentifier : IDENTIFIER
+ """
+ p[0] = IDLUnresolvedIdentifier(self.getLocation(p, 1), p[1])
+
+ def p_OptionalIdentifierEmpty(self, p):
+ """
+ OptionalIdentifier :
+ """
+ pass
+
+ def p_ArgumentList(self, p):
+ """
+ ArgumentList : Argument Arguments
+ """
+ p[0] = [p[1]] if p[1] else []
+ p[0].extend(p[2])
+
+ def p_ArgumentListEmpty(self, p):
+ """
+ ArgumentList :
+ """
+ p[0] = []
+
+ def p_Arguments(self, p):
+ """
+ Arguments : COMMA Argument Arguments
+ """
+ p[0] = [p[2]] if p[2] else []
+ p[0].extend(p[3])
+
+ def p_ArgumentsEmpty(self, p):
+ """
+ Arguments :
+ """
+ p[0] = []
+
+ def p_Argument(self, p):
+ """
+ Argument : ExtendedAttributeList Optional Type Ellipsis ArgumentName Default
+ """
+ t = p[3]
+ assert isinstance(t, IDLType)
+ identifier = IDLUnresolvedIdentifier(self.getLocation(p, 5), p[5])
+
+ optional = p[2]
+ variadic = p[4]
+ defaultValue = p[6]
+
+ if not optional and defaultValue:
+ raise WebIDLError("Mandatory arguments can't have a default value.",
+ [self.getLocation(p, 6)])
+
+ # We can't test t.isAny() here and give it a default value as needed,
+ # since at this point t is not a fully resolved type yet (e.g. it might
+ # be a typedef). We'll handle the 'any' case in IDLArgument.complete.
+
+ if variadic:
+ if optional:
+ raise WebIDLError("Variadic arguments should not be marked optional.",
+ [self.getLocation(p, 2)])
+ optional = variadic
+
+ p[0] = IDLArgument(self.getLocation(p, 5), identifier, t, optional, defaultValue, variadic)
+ p[0].addExtendedAttributes(p[1])
+
+ def p_ArgumentName(self, p):
+ """
+ ArgumentName : IDENTIFIER
+ | ATTRIBUTE
+ | CALLBACK
+ | CONST
+ | CREATOR
+ | DELETER
+ | DICTIONARY
+ | ENUM
+ | EXCEPTION
+ | GETTER
+ | IMPLEMENTS
+ | INHERIT
+ | INTERFACE
+ | LEGACYCALLER
+ | PARTIAL
+ | SERIALIZER
+ | SETTER
+ | STATIC
+ | STRINGIFIER
+ | JSONIFIER
+ | TYPEDEF
+ | UNRESTRICTED
+ """
+ p[0] = p[1]
+
+ def p_Optional(self, p):
+ """
+ Optional : OPTIONAL
+ """
+ p[0] = True
+
+ def p_OptionalEmpty(self, p):
+ """
+ Optional :
+ """
+ p[0] = False
+
+ def p_Ellipsis(self, p):
+ """
+ Ellipsis : ELLIPSIS
+ """
+ p[0] = True
+
+ def p_EllipsisEmpty(self, p):
+ """
+ Ellipsis :
+ """
+ p[0] = False
+
+ def p_ExceptionMember(self, p):
+ """
+ ExceptionMember : Const
+ | ExceptionField
+ """
+ pass
+
+ def p_ExceptionField(self, p):
+ """
+ ExceptionField : Type IDENTIFIER SEMICOLON
+ """
+ pass
+
+ def p_ExtendedAttributeList(self, p):
+ """
+ ExtendedAttributeList : LBRACKET ExtendedAttribute ExtendedAttributes RBRACKET
+ """
+ p[0] = [p[2]]
+ if p[3]:
+ p[0].extend(p[3])
+
+ def p_ExtendedAttributeListEmpty(self, p):
+ """
+ ExtendedAttributeList :
+ """
+ p[0] = []
+
+ def p_ExtendedAttribute(self, p):
+ """
+ ExtendedAttribute : ExtendedAttributeNoArgs
+ | ExtendedAttributeArgList
+ | ExtendedAttributeIdent
+ | ExtendedAttributeNamedArgList
+ | ExtendedAttributeIdentList
+ """
+ p[0] = IDLExtendedAttribute(self.getLocation(p, 1), p[1])
+
+ def p_ExtendedAttributeEmpty(self, p):
+ """
+ ExtendedAttribute :
+ """
+ pass
+
+ def p_ExtendedAttributes(self, p):
+ """
+ ExtendedAttributes : COMMA ExtendedAttribute ExtendedAttributes
+ """
+ p[0] = [p[2]] if p[2] else []
+ p[0].extend(p[3])
+
+ def p_ExtendedAttributesEmpty(self, p):
+ """
+ ExtendedAttributes :
+ """
+ p[0] = []
+
+ def p_Other(self, p):
+ """
+ Other : INTEGER
+ | FLOATLITERAL
+ | IDENTIFIER
+ | STRING
+ | OTHER
+ | ELLIPSIS
+ | COLON
+ | SCOPE
+ | SEMICOLON
+ | LT
+ | EQUALS
+ | GT
+ | QUESTIONMARK
+ | DATE
+ | DOMSTRING
+ | BYTESTRING
+ | SCALARVALUESTRING
+ | ANY
+ | ATTRIBUTE
+ | BOOLEAN
+ | BYTE
+ | LEGACYCALLER
+ | CONST
+ | CREATOR
+ | DELETER
+ | DOUBLE
+ | EXCEPTION
+ | FALSE
+ | FLOAT
+ | GETTER
+ | IMPLEMENTS
+ | INHERIT
+ | INTERFACE
+ | LONG
+ | MODULE
+ | NULL
+ | OBJECT
+ | OCTET
+ | OPTIONAL
+ | SEQUENCE
+ | MOZMAP
+ | SETTER
+ | SHORT
+ | STATIC
+ | STRINGIFIER
+ | JSONIFIER
+ | TRUE
+ | TYPEDEF
+ | UNSIGNED
+ | VOID
+ """
+ pass
+
+ def p_OtherOrComma(self, p):
+ """
+ OtherOrComma : Other
+ | COMMA
+ """
+ pass
+
+ def p_TypeSingleType(self, p):
+ """
+ Type : SingleType
+ """
+ p[0] = p[1]
+
+ def p_TypeUnionType(self, p):
+ """
+ Type : UnionType TypeSuffix
+ """
+ p[0] = self.handleModifiers(p[1], p[2])
+
+ def p_SingleTypeNonAnyType(self, p):
+ """
+ SingleType : NonAnyType
+ """
+ p[0] = p[1]
+
+ def p_SingleTypeAnyType(self, p):
+ """
+ SingleType : ANY TypeSuffixStartingWithArray
+ """
+ p[0] = self.handleModifiers(BuiltinTypes[IDLBuiltinType.Types.any], p[2])
+
+ def p_UnionType(self, p):
+ """
+ UnionType : LPAREN UnionMemberType OR UnionMemberType UnionMemberTypes RPAREN
+ """
+ types = [p[2], p[4]]
+ types.extend(p[5])
+ p[0] = IDLUnionType(self.getLocation(p, 1), types)
+
+ def p_UnionMemberTypeNonAnyType(self, p):
+ """
+ UnionMemberType : NonAnyType
+ """
+ p[0] = p[1]
+
+ def p_UnionMemberTypeArrayOfAny(self, p):
+ """
+ UnionMemberTypeArrayOfAny : ANY LBRACKET RBRACKET
+ """
+ p[0] = IDLArrayType(self.getLocation(p, 2),
+ BuiltinTypes[IDLBuiltinType.Types.any])
+
+ def p_UnionMemberType(self, p):
+ """
+ UnionMemberType : UnionType TypeSuffix
+ | UnionMemberTypeArrayOfAny TypeSuffix
+ """
+ p[0] = self.handleModifiers(p[1], p[2])
+
+ def p_UnionMemberTypes(self, p):
+ """
+ UnionMemberTypes : OR UnionMemberType UnionMemberTypes
+ """
+ p[0] = [p[2]]
+ p[0].extend(p[3])
+
+ def p_UnionMemberTypesEmpty(self, p):
+ """
+ UnionMemberTypes :
+ """
+ p[0] = []
+
+ def p_NonAnyType(self, p):
+ """
+ NonAnyType : PrimitiveOrStringType TypeSuffix
+ | ARRAYBUFFER TypeSuffix
+ | OBJECT TypeSuffix
+ """
+ if p[1] == "object":
+ type = BuiltinTypes[IDLBuiltinType.Types.object]
+ elif p[1] == "ArrayBuffer":
+ type = BuiltinTypes[IDLBuiltinType.Types.ArrayBuffer]
+ else:
+ type = BuiltinTypes[p[1]]
+
+ p[0] = self.handleModifiers(type, p[2])
+
+ def p_NonAnyTypeSequenceType(self, p):
+ """
+ NonAnyType : SEQUENCE LT Type GT Null
+ """
+ innerType = p[3]
+ type = IDLSequenceType(self.getLocation(p, 1), innerType)
+ if p[5]:
+ type = IDLNullableType(self.getLocation(p, 5), type)
+ p[0] = type
+
+ # Note: Promise<void> is allowed, so we want to parametrize on
+ # ReturnType, not Type. Also, we want this to end up picking up
+ # the Promise interface for now, hence the games with IDLUnresolvedType.
+ def p_NonAnyTypePromiseType(self, p):
+ """
+ NonAnyType : PROMISE LT ReturnType GT Null
+ """
+ innerType = p[3]
+ promiseIdent = IDLUnresolvedIdentifier(self.getLocation(p, 1), "Promise")
+ type = IDLUnresolvedType(self.getLocation(p, 1), promiseIdent, p[3])
+ if p[5]:
+ type = IDLNullableType(self.getLocation(p, 5), type)
+ p[0] = type
+
+ def p_NonAnyTypeMozMapType(self, p):
+ """
+ NonAnyType : MOZMAP LT Type GT Null
+ """
+ innerType = p[3]
+ type = IDLMozMapType(self.getLocation(p, 1), innerType)
+ if p[5]:
+ type = IDLNullableType(self.getLocation(p, 5), type)
+ p[0] = type
+
+ def p_NonAnyTypeScopedName(self, p):
+ """
+ NonAnyType : ScopedName TypeSuffix
+ """
+ assert isinstance(p[1], IDLUnresolvedIdentifier)
+
+ if p[1].name == "Promise":
+ raise WebIDLError("Promise used without saying what it's "
+ "parametrized over",
+ [self.getLocation(p, 1)])
+
+ type = None
+
+ try:
+ if self.globalScope()._lookupIdentifier(p[1]):
+ obj = self.globalScope()._lookupIdentifier(p[1])
+ if obj.isType():
+ type = obj
+ else:
+ type = IDLWrapperType(self.getLocation(p, 1), p[1])
+ p[0] = self.handleModifiers(type, p[2])
+ return
+ except:
+ pass
+
+ type = IDLUnresolvedType(self.getLocation(p, 1), p[1])
+ p[0] = self.handleModifiers(type, p[2])
+
+ def p_NonAnyTypeDate(self, p):
+ """
+ NonAnyType : DATE TypeSuffix
+ """
+ p[0] = self.handleModifiers(BuiltinTypes[IDLBuiltinType.Types.date],
+ p[2])
+
+ def p_ConstType(self, p):
+ """
+ ConstType : PrimitiveOrStringType Null
+ """
+ type = BuiltinTypes[p[1]]
+ if p[2]:
+ type = IDLNullableType(self.getLocation(p, 1), type)
+ p[0] = type
+
+ def p_ConstTypeIdentifier(self, p):
+ """
+ ConstType : IDENTIFIER Null
+ """
+ identifier = IDLUnresolvedIdentifier(self.getLocation(p, 1), p[1])
+
+ type = IDLUnresolvedType(self.getLocation(p, 1), identifier)
+ if p[2]:
+ type = IDLNullableType(self.getLocation(p, 1), type)
+ p[0] = type
+
+ def p_PrimitiveOrStringTypeUint(self, p):
+ """
+ PrimitiveOrStringType : UnsignedIntegerType
+ """
+ p[0] = p[1]
+
+ def p_PrimitiveOrStringTypeBoolean(self, p):
+ """
+ PrimitiveOrStringType : BOOLEAN
+ """
+ p[0] = IDLBuiltinType.Types.boolean
+
+ def p_PrimitiveOrStringTypeByte(self, p):
+ """
+ PrimitiveOrStringType : BYTE
+ """
+ p[0] = IDLBuiltinType.Types.byte
+
+ def p_PrimitiveOrStringTypeOctet(self, p):
+ """
+ PrimitiveOrStringType : OCTET
+ """
+ p[0] = IDLBuiltinType.Types.octet
+
+ def p_PrimitiveOrStringTypeFloat(self, p):
+ """
+ PrimitiveOrStringType : FLOAT
+ """
+ p[0] = IDLBuiltinType.Types.float
+
+ def p_PrimitiveOrStringTypeUnrestictedFloat(self, p):
+ """
+ PrimitiveOrStringType : UNRESTRICTED FLOAT
+ """
+ p[0] = IDLBuiltinType.Types.unrestricted_float
+
+ def p_PrimitiveOrStringTypeDouble(self, p):
+ """
+ PrimitiveOrStringType : DOUBLE
+ """
+ p[0] = IDLBuiltinType.Types.double
+
+ def p_PrimitiveOrStringTypeUnrestictedDouble(self, p):
+ """
+ PrimitiveOrStringType : UNRESTRICTED DOUBLE
+ """
+ p[0] = IDLBuiltinType.Types.unrestricted_double
+
+ def p_PrimitiveOrStringTypeDOMString(self, p):
+ """
+ PrimitiveOrStringType : DOMSTRING
+ """
+ p[0] = IDLBuiltinType.Types.domstring
+
+ def p_PrimitiveOrStringTypeBytestring(self, p):
+ """
+ PrimitiveOrStringType : BYTESTRING
+ """
+ p[0] = IDLBuiltinType.Types.bytestring
+
+ def p_PrimitiveOrStringTypeScalarValueString(self, p):
+ """
+ PrimitiveOrStringType : SCALARVALUESTRING
+ """
+ p[0] = IDLBuiltinType.Types.scalarvaluestring
+
+ def p_UnsignedIntegerTypeUnsigned(self, p):
+ """
+ UnsignedIntegerType : UNSIGNED IntegerType
+ """
+ p[0] = p[2] + 1 # Adding one to a given signed integer type
+ # gets you the unsigned type.
+
+ def p_UnsignedIntegerType(self, p):
+ """
+ UnsignedIntegerType : IntegerType
+ """
+ p[0] = p[1]
+
+ def p_IntegerTypeShort(self, p):
+ """
+ IntegerType : SHORT
+ """
+ p[0] = IDLBuiltinType.Types.short
+
+ def p_IntegerTypeLong(self, p):
+ """
+ IntegerType : LONG OptionalLong
+ """
+ if p[2]:
+ p[0] = IDLBuiltinType.Types.long_long
+ else:
+ p[0] = IDLBuiltinType.Types.long
+
+ def p_OptionalLong(self, p):
+ """
+ OptionalLong : LONG
+ """
+ p[0] = True
+
+ def p_OptionalLongEmpty(self, p):
+ """
+ OptionalLong :
+ """
+ p[0] = False
+
+ def p_TypeSuffixBrackets(self, p):
+ """
+ TypeSuffix : LBRACKET RBRACKET TypeSuffix
+ """
+ p[0] = [(IDLMethod.TypeSuffixModifier.Brackets, self.getLocation(p, 1))]
+ p[0].extend(p[3])
+
+ def p_TypeSuffixQMark(self, p):
+ """
+ TypeSuffix : QUESTIONMARK TypeSuffixStartingWithArray
+ """
+ p[0] = [(IDLMethod.TypeSuffixModifier.QMark, self.getLocation(p, 1))]
+ p[0].extend(p[2])
+
+ def p_TypeSuffixEmpty(self, p):
+ """
+ TypeSuffix :
+ """
+ p[0] = []
+
+ def p_TypeSuffixStartingWithArray(self, p):
+ """
+ TypeSuffixStartingWithArray : LBRACKET RBRACKET TypeSuffix
+ """
+ p[0] = [(IDLMethod.TypeSuffixModifier.Brackets, self.getLocation(p, 1))]
+ p[0].extend(p[3])
+
+ def p_TypeSuffixStartingWithArrayEmpty(self, p):
+ """
+ TypeSuffixStartingWithArray :
+ """
+ p[0] = []
+
+ def p_Null(self, p):
+ """
+ Null : QUESTIONMARK
+ |
+ """
+ if len(p) > 1:
+ p[0] = True
+ else:
+ p[0] = False
+
+ def p_ReturnTypeType(self, p):
+ """
+ ReturnType : Type
+ """
+ p[0] = p[1]
+
+ def p_ReturnTypeVoid(self, p):
+ """
+ ReturnType : VOID
+ """
+ p[0] = BuiltinTypes[IDLBuiltinType.Types.void]
+
+ def p_ScopedName(self, p):
+ """
+ ScopedName : AbsoluteScopedName
+ | RelativeScopedName
+ """
+ p[0] = p[1]
+
+ def p_AbsoluteScopedName(self, p):
+ """
+ AbsoluteScopedName : SCOPE IDENTIFIER ScopedNameParts
+ """
+ assert False
+ pass
+
+ def p_RelativeScopedName(self, p):
+ """
+ RelativeScopedName : IDENTIFIER ScopedNameParts
+ """
+ assert not p[2] # Not implemented!
+
+ p[0] = IDLUnresolvedIdentifier(self.getLocation(p, 1), p[1])
+
+ def p_ScopedNameParts(self, p):
+ """
+ ScopedNameParts : SCOPE IDENTIFIER ScopedNameParts
+ """
+ assert False
+ pass
+
+ def p_ScopedNamePartsEmpty(self, p):
+ """
+ ScopedNameParts :
+ """
+ p[0] = None
+
+ def p_ExtendedAttributeNoArgs(self, p):
+ """
+ ExtendedAttributeNoArgs : IDENTIFIER
+ """
+ p[0] = (p[1],)
+
+ def p_ExtendedAttributeArgList(self, p):
+ """
+ ExtendedAttributeArgList : IDENTIFIER LPAREN ArgumentList RPAREN
+ """
+ p[0] = (p[1], p[3])
+
+ def p_ExtendedAttributeIdent(self, p):
+ """
+ ExtendedAttributeIdent : IDENTIFIER EQUALS STRING
+ | IDENTIFIER EQUALS IDENTIFIER
+ """
+ p[0] = (p[1], p[3])
+
+ def p_ExtendedAttributeNamedArgList(self, p):
+ """
+ ExtendedAttributeNamedArgList : IDENTIFIER EQUALS IDENTIFIER LPAREN ArgumentList RPAREN
+ """
+ p[0] = (p[1], p[3], p[5])
+
+ def p_ExtendedAttributeIdentList(self, p):
+ """
+ ExtendedAttributeIdentList : IDENTIFIER EQUALS LPAREN IdentifierList RPAREN
+ """
+ p[0] = (p[1], p[4])
+
+ def p_IdentifierList(self, p):
+ """
+ IdentifierList : IDENTIFIER Identifiers
+ """
+ idents = list(p[2])
+ idents.insert(0, p[1])
+ p[0] = idents
+
+ def p_IdentifiersList(self, p):
+ """
+ Identifiers : COMMA IDENTIFIER Identifiers
+ """
+ idents = list(p[3])
+ idents.insert(0, p[2])
+ p[0] = idents
+
+ def p_IdentifiersEmpty(self, p):
+ """
+ Identifiers :
+ """
+ p[0] = []
+
+ def p_error(self, p):
+ if not p:
+ raise WebIDLError("Syntax Error at end of file. Possibly due to missing semicolon(;), braces(}) or both",
+ [self._filename])
+ else:
+ raise WebIDLError("invalid syntax", [Location(self.lexer, p.lineno, p.lexpos, self._filename)])
+
+ def __init__(self, outputdir='', lexer=None):
+ Tokenizer.__init__(self, outputdir, lexer)
+
+ logger = SqueakyCleanLogger()
+ self.parser = yacc.yacc(module=self,
+ outputdir=outputdir,
+ tabmodule='webidlyacc',
+ errorlog=logger
+ # Pickling the grammar is a speedup in
+ # some cases (older Python?) but a
+ # significant slowdown in others.
+ # We're not pickling for now, until it
+ # becomes a speedup again.
+ # , picklefile='WebIDLGrammar.pkl'
+ )
+ logger.reportGrammarErrors()
+
+ self._globalScope = IDLScope(BuiltinLocation("<Global Scope>"), None, None)
+ # To make our test harness work, pretend like we have a primary global already. Note that we _don't_ set _globalScope.primaryGlobalAttr, so we'll still be able to detect multiple PrimaryGlobal extended attributes.
+ self._globalScope.primaryGlobalName = "FakeTestPrimaryGlobal"
+ self._globalScope.globalNames.add("FakeTestPrimaryGlobal")
+ self._globalScope.globalNameMapping["FakeTestPrimaryGlobal"].add("FakeTestPrimaryGlobal")
+ self._installBuiltins(self._globalScope)
+ self._productions = []
+
+ self._filename = "<builtin>"
+ self.lexer.input(Parser._builtins)
+ self._filename = None
+
+ self.parser.parse(lexer=self.lexer,tracking=True)
+
+ def _installBuiltins(self, scope):
+ assert isinstance(scope, IDLScope)
+
+ # xrange omits the last value.
+ for x in xrange(IDLBuiltinType.Types.ArrayBuffer, IDLBuiltinType.Types.Float64Array + 1):
+ builtin = BuiltinTypes[x]
+ name = builtin.name
+
+ typedef = IDLTypedefType(BuiltinLocation("<builtin type>"), builtin, name)
+ typedef.resolve(scope)
+
+ @ staticmethod
+ def handleModifiers(type, modifiers):
+ for (modifier, modifierLocation) in modifiers:
+ assert modifier == IDLMethod.TypeSuffixModifier.QMark or \
+ modifier == IDLMethod.TypeSuffixModifier.Brackets
+
+ if modifier == IDLMethod.TypeSuffixModifier.QMark:
+ type = IDLNullableType(modifierLocation, type)
+ elif modifier == IDLMethod.TypeSuffixModifier.Brackets:
+ type = IDLArrayType(modifierLocation, type)
+
+ return type
+
+ def parse(self, t, filename=None):
+ self.lexer.input(t)
+
+ #for tok in iter(self.lexer.token, None):
+ # print tok
+
+ self._filename = filename
+ self._productions.extend(self.parser.parse(lexer=self.lexer,tracking=True))
+ self._filename = None
+
+ def finish(self):
+ # First, finish all the IDLImplementsStatements. In particular, we
+ # have to make sure we do those before we do the IDLInterfaces.
+ # XXX khuey hates this bit and wants to nuke it from orbit.
+ implementsStatements = [ p for p in self._productions if
+ isinstance(p, IDLImplementsStatement)]
+ otherStatements = [ p for p in self._productions if
+ not isinstance(p, IDLImplementsStatement)]
+ for production in implementsStatements:
+ production.finish(self.globalScope())
+ for production in otherStatements:
+ production.finish(self.globalScope())
+
+ # Do any post-finish validation we need to do
+ for production in self._productions:
+ production.validate()
+
+ # De-duplicate self._productions, without modifying its order.
+ seen = set()
+ result = []
+ for p in self._productions:
+ if p not in seen:
+ seen.add(p)
+ result.append(p)
+ return result
+
+ def reset(self):
+ return Parser(lexer=self.lexer)
+
+ # Builtin IDL defined by WebIDL
+ _builtins = """
+ typedef unsigned long long DOMTimeStamp;
+ """
+
+def main():
+ # Parse arguments.
+ from optparse import OptionParser
+ usageString = "usage: %prog [options] files"
+ o = OptionParser(usage=usageString)
+ o.add_option("--cachedir", dest='cachedir', default=None,
+ help="Directory in which to cache lex/parse tables.")
+ o.add_option("--verbose-errors", action='store_true', default=False,
+ help="When an error happens, display the Python traceback.")
+ (options, args) = o.parse_args()
+
+ if len(args) < 1:
+ o.error(usageString)
+
+ fileList = args
+ baseDir = os.getcwd()
+
+ # Parse the WebIDL.
+ parser = Parser(options.cachedir)
+ try:
+ for filename in fileList:
+ fullPath = os.path.normpath(os.path.join(baseDir, filename))
+ f = open(fullPath, 'rb')
+ lines = f.readlines()
+ f.close()
+ print fullPath
+ parser.parse(''.join(lines), fullPath)
+ parser.finish()
+ except WebIDLError, e:
+ if options.verbose_errors:
+ traceback.print_exc()
+ else:
+ print e
+
+if __name__ == '__main__':
+ main()
diff --git a/components/script/dom/bindings/codegen/parser/external.patch b/components/script/dom/bindings/codegen/parser/external.patch
new file mode 100644
index 00000000000..9464511a9d0
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/external.patch
@@ -0,0 +1,49 @@
+--- WebIDL.py
++++ WebIDL.py
+@@ -450,44 +450,8 @@ class IDLIdentifierPlaceholder(IDLObjectWithIdentifier):
+
+ class IDLExternalInterface(IDLObjectWithIdentifier):
+ def __init__(self, location, parentScope, identifier):
+- assert isinstance(identifier, IDLUnresolvedIdentifier)
+- assert isinstance(parentScope, IDLScope)
+- self.parent = None
+- IDLObjectWithIdentifier.__init__(self, location, parentScope, identifier)
+- IDLObjectWithIdentifier.resolve(self, parentScope)
+-
+- def finish(self, scope):
+- pass
+-
+- def validate(self):
+- pass
+-
+- def isExternal(self):
+- return True
+-
+- def isInterface(self):
+- return True
+-
+- def isConsequential(self):
+- return False
+-
+- def addExtendedAttributes(self, attrs):
+- assert len(attrs) == 0
+-
+- def resolve(self, parentScope):
+- pass
+-
+- def getJSImplementation(self):
+- return None
+-
+- def isJSImplemented(self):
+- return False
+-
+- def getNavigatorProperty(self):
+- return None
+-
+- def _getDependentObjects(self):
+- return set()
++ raise WebIDLError("Servo does not support external interfaces.",
++ [self.location])
+
+ class IDLPartialInterface(IDLObject):
+ def __init__(self, location, name, members, nonPartialInterface):
diff --git a/components/script/dom/bindings/codegen/parser/module.patch b/components/script/dom/bindings/codegen/parser/module.patch
new file mode 100644
index 00000000000..977947b4c63
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/module.patch
@@ -0,0 +1,12 @@
+--- WebIDL.py
++++ WebIDL.py
+@@ -3398,6 +3398,9 @@ class IDLCallbackType(IDLType, IDLObjectWithScope):
+ self._treatNonCallableAsNull = False
+ self._treatNonObjectAsNull = False
+
++ def module(self):
++ return self.location.filename().split('/')[-1].split('.webidl')[0] + 'Binding'
++
+ def isCallback(self):
+ return True
+
diff --git a/components/script/dom/bindings/codegen/parser/runtests.py b/components/script/dom/bindings/codegen/parser/runtests.py
new file mode 100644
index 00000000000..98a7d2b81d3
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/runtests.py
@@ -0,0 +1,79 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import os, sys
+import glob
+import optparse
+import traceback
+import WebIDL
+
+class TestHarness(object):
+ def __init__(self, test, verbose):
+ self.test = test
+ self.verbose = verbose
+ self.printed_intro = False
+
+ def start(self):
+ if self.verbose:
+ self.maybe_print_intro()
+
+ def finish(self):
+ if self.verbose or self.printed_intro:
+ print "Finished test %s" % self.test
+
+ def maybe_print_intro(self):
+ if not self.printed_intro:
+ print "Starting test %s" % self.test
+ self.printed_intro = True
+
+ def test_pass(self, msg):
+ if self.verbose:
+ print "TEST-PASS | %s" % msg
+
+ def test_fail(self, msg):
+ self.maybe_print_intro()
+ print "TEST-UNEXPECTED-FAIL | %s" % msg
+
+ def ok(self, condition, msg):
+ if condition:
+ self.test_pass(msg)
+ else:
+ self.test_fail(msg)
+
+ def check(self, a, b, msg):
+ if a == b:
+ self.test_pass(msg)
+ else:
+ self.test_fail(msg)
+ print "\tGot %s expected %s" % (a, b)
+
+def run_tests(tests, verbose):
+ testdir = os.path.join(os.path.dirname(__file__), 'tests')
+ if not tests:
+ tests = glob.iglob(os.path.join(testdir, "*.py"))
+ sys.path.append(testdir)
+
+ for test in tests:
+ (testpath, ext) = os.path.splitext(os.path.basename(test))
+ _test = __import__(testpath, globals(), locals(), ['WebIDLTest'])
+
+ harness = TestHarness(test, verbose)
+ harness.start()
+ try:
+ _test.WebIDLTest.__call__(WebIDL.Parser(), harness)
+ except Exception, ex:
+ print "TEST-UNEXPECTED-FAIL | Unhandled exception in test %s: %s" % (testpath, ex)
+ traceback.print_exc()
+ finally:
+ harness.finish()
+
+if __name__ == '__main__':
+ usage = """%prog [OPTIONS] [TESTS]
+ Where TESTS are relative to the tests directory."""
+ parser = optparse.OptionParser(usage=usage)
+ parser.add_option('-q', '--quiet', action='store_false', dest='verbose', default=True,
+ help="Don't print passing tests.")
+ options, tests = parser.parse_args()
+
+ run_tests(tests, verbose=options.verbose)
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_any_null.py b/components/script/dom/bindings/codegen/parser/tests/test_any_null.py
new file mode 100644
index 00000000000..e3b690bf6f1
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_any_null.py
@@ -0,0 +1,14 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface DoubleNull {
+ attribute any? foo;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_argument_identifier_conflicts.py b/components/script/dom/bindings/codegen/parser/tests/test_argument_identifier_conflicts.py
new file mode 100644
index 00000000000..eb1f6d3c92e
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_argument_identifier_conflicts.py
@@ -0,0 +1,14 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface ArgumentIdentifierConflict {
+ void foo(boolean arg1, boolean arg1);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_argument_novoid.py b/components/script/dom/bindings/codegen/parser/tests/test_argument_novoid.py
new file mode 100644
index 00000000000..ef8c2229aed
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_argument_novoid.py
@@ -0,0 +1,14 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface VoidArgument1 {
+ void foo(void arg2);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_array_of_interface.py b/components/script/dom/bindings/codegen/parser/tests/test_array_of_interface.py
new file mode 100644
index 00000000000..26528984595
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_array_of_interface.py
@@ -0,0 +1,13 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface A {
+ attribute long a;
+ };
+
+ interface B {
+ attribute A[] b;
+ };
+ """);
+ parser.finish()
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_arraybuffer.py b/components/script/dom/bindings/codegen/parser/tests/test_arraybuffer.py
new file mode 100644
index 00000000000..5b8e56f86ca
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_arraybuffer.py
@@ -0,0 +1,84 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface TestArrayBuffer {
+ attribute ArrayBuffer bufferAttr;
+ void bufferMethod(ArrayBuffer arg1, ArrayBuffer? arg2, ArrayBuffer[] arg3, sequence<ArrayBuffer> arg4);
+
+ attribute ArrayBufferView viewAttr;
+ void viewMethod(ArrayBufferView arg1, ArrayBufferView? arg2, ArrayBufferView[] arg3, sequence<ArrayBufferView> arg4);
+
+ attribute Int8Array int8ArrayAttr;
+ void int8ArrayMethod(Int8Array arg1, Int8Array? arg2, Int8Array[] arg3, sequence<Int8Array> arg4);
+
+ attribute Uint8Array uint8ArrayAttr;
+ void uint8ArrayMethod(Uint8Array arg1, Uint8Array? arg2, Uint8Array[] arg3, sequence<Uint8Array> arg4);
+
+ attribute Uint8ClampedArray uint8ClampedArrayAttr;
+ void uint8ClampedArrayMethod(Uint8ClampedArray arg1, Uint8ClampedArray? arg2, Uint8ClampedArray[] arg3, sequence<Uint8ClampedArray> arg4);
+
+ attribute Int16Array int16ArrayAttr;
+ void int16ArrayMethod(Int16Array arg1, Int16Array? arg2, Int16Array[] arg3, sequence<Int16Array> arg4);
+
+ attribute Uint16Array uint16ArrayAttr;
+ void uint16ArrayMethod(Uint16Array arg1, Uint16Array? arg2, Uint16Array[] arg3, sequence<Uint16Array> arg4);
+
+ attribute Int32Array int32ArrayAttr;
+ void int32ArrayMethod(Int32Array arg1, Int32Array? arg2, Int32Array[] arg3, sequence<Int32Array> arg4);
+
+ attribute Uint32Array uint32ArrayAttr;
+ void uint32ArrayMethod(Uint32Array arg1, Uint32Array? arg2, Uint32Array[] arg3, sequence<Uint32Array> arg4);
+
+ attribute Float32Array float32ArrayAttr;
+ void float32ArrayMethod(Float32Array arg1, Float32Array? arg2, Float32Array[] arg3, sequence<Float32Array> arg4);
+
+ attribute Float64Array float64ArrayAttr;
+ void float64ArrayMethod(Float64Array arg1, Float64Array? arg2, Float64Array[] arg3, sequence<Float64Array> arg4);
+ };
+ """)
+
+ results = parser.finish()
+
+ iface = results[0]
+
+ harness.ok(True, "TestArrayBuffer interface parsed without error")
+ harness.check(len(iface.members), 22, "Interface should have twenty two members")
+
+ members = iface.members
+
+ def checkStuff(attr, method, t):
+ harness.ok(isinstance(attr, WebIDL.IDLAttribute), "Expect an IDLAttribute")
+ harness.ok(isinstance(method, WebIDL.IDLMethod), "Expect an IDLMethod")
+
+ harness.check(str(attr.type), t, "Expect an ArrayBuffer type")
+ harness.ok(attr.type.isSpiderMonkeyInterface(), "Should test as a js interface")
+
+ (retType, arguments) = method.signatures()[0]
+ harness.ok(retType.isVoid(), "Should have a void return type")
+ harness.check(len(arguments), 4, "Expect 4 arguments")
+
+ harness.check(str(arguments[0].type), t, "Expect an ArrayBuffer type")
+ harness.ok(arguments[0].type.isSpiderMonkeyInterface(), "Should test as a js interface")
+
+ harness.check(str(arguments[1].type), t + "OrNull", "Expect an ArrayBuffer type")
+ harness.ok(arguments[1].type.inner.isSpiderMonkeyInterface(), "Should test as a js interface")
+
+ harness.check(str(arguments[2].type), t + "Array", "Expect an ArrayBuffer type")
+ harness.ok(arguments[2].type.inner.isSpiderMonkeyInterface(), "Should test as a js interface")
+
+ harness.check(str(arguments[3].type), t + "Sequence", "Expect an ArrayBuffer type")
+ harness.ok(arguments[3].type.inner.isSpiderMonkeyInterface(), "Should test as a js interface")
+
+
+ checkStuff(members[0], members[1], "ArrayBuffer")
+ checkStuff(members[2], members[3], "ArrayBufferView")
+ checkStuff(members[4], members[5], "Int8Array")
+ checkStuff(members[6], members[7], "Uint8Array")
+ checkStuff(members[8], members[9], "Uint8ClampedArray")
+ checkStuff(members[10], members[11], "Int16Array")
+ checkStuff(members[12], members[13], "Uint16Array")
+ checkStuff(members[14], members[15], "Int32Array")
+ checkStuff(members[16], members[17], "Uint32Array")
+ checkStuff(members[18], members[19], "Float32Array")
+ checkStuff(members[20], members[21], "Float64Array")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_attr.py b/components/script/dom/bindings/codegen/parser/tests/test_attr.py
new file mode 100644
index 00000000000..6b6142b6243
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_attr.py
@@ -0,0 +1,302 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ testData = [("::TestAttr%s::b", "b", "Byte%s", False),
+ ("::TestAttr%s::rb", "rb", "Byte%s", True),
+ ("::TestAttr%s::o", "o", "Octet%s", False),
+ ("::TestAttr%s::ro", "ro", "Octet%s", True),
+ ("::TestAttr%s::s", "s", "Short%s", False),
+ ("::TestAttr%s::rs", "rs", "Short%s", True),
+ ("::TestAttr%s::us", "us", "UnsignedShort%s", False),
+ ("::TestAttr%s::rus", "rus", "UnsignedShort%s", True),
+ ("::TestAttr%s::l", "l", "Long%s", False),
+ ("::TestAttr%s::rl", "rl", "Long%s", True),
+ ("::TestAttr%s::ul", "ul", "UnsignedLong%s", False),
+ ("::TestAttr%s::rul", "rul", "UnsignedLong%s", True),
+ ("::TestAttr%s::ll", "ll", "LongLong%s", False),
+ ("::TestAttr%s::rll", "rll", "LongLong%s", True),
+ ("::TestAttr%s::ull", "ull", "UnsignedLongLong%s", False),
+ ("::TestAttr%s::rull", "rull", "UnsignedLongLong%s", True),
+ ("::TestAttr%s::str", "str", "String%s", False),
+ ("::TestAttr%s::rstr", "rstr", "String%s", True),
+ ("::TestAttr%s::obj", "obj", "Object%s", False),
+ ("::TestAttr%s::robj", "robj", "Object%s", True),
+ ("::TestAttr%s::object", "object", "Object%s", False),
+ ("::TestAttr%s::f", "f", "Float%s", False),
+ ("::TestAttr%s::rf", "rf", "Float%s", True)]
+
+ parser.parse("""
+ interface TestAttr {
+ attribute byte b;
+ readonly attribute byte rb;
+ attribute octet o;
+ readonly attribute octet ro;
+ attribute short s;
+ readonly attribute short rs;
+ attribute unsigned short us;
+ readonly attribute unsigned short rus;
+ attribute long l;
+ readonly attribute long rl;
+ attribute unsigned long ul;
+ readonly attribute unsigned long rul;
+ attribute long long ll;
+ readonly attribute long long rll;
+ attribute unsigned long long ull;
+ readonly attribute unsigned long long rull;
+ attribute DOMString str;
+ readonly attribute DOMString rstr;
+ attribute object obj;
+ readonly attribute object robj;
+ attribute object _object;
+ attribute float f;
+ readonly attribute float rf;
+ };
+
+ interface TestAttrNullable {
+ attribute byte? b;
+ readonly attribute byte? rb;
+ attribute octet? o;
+ readonly attribute octet? ro;
+ attribute short? s;
+ readonly attribute short? rs;
+ attribute unsigned short? us;
+ readonly attribute unsigned short? rus;
+ attribute long? l;
+ readonly attribute long? rl;
+ attribute unsigned long? ul;
+ readonly attribute unsigned long? rul;
+ attribute long long? ll;
+ readonly attribute long long? rll;
+ attribute unsigned long long? ull;
+ readonly attribute unsigned long long? rull;
+ attribute DOMString? str;
+ readonly attribute DOMString? rstr;
+ attribute object? obj;
+ readonly attribute object? robj;
+ attribute object? _object;
+ attribute float? f;
+ readonly attribute float? rf;
+ };
+
+ interface TestAttrArray {
+ attribute byte[] b;
+ readonly attribute byte[] rb;
+ attribute octet[] o;
+ readonly attribute octet[] ro;
+ attribute short[] s;
+ readonly attribute short[] rs;
+ attribute unsigned short[] us;
+ readonly attribute unsigned short[] rus;
+ attribute long[] l;
+ readonly attribute long[] rl;
+ attribute unsigned long[] ul;
+ readonly attribute unsigned long[] rul;
+ attribute long long[] ll;
+ readonly attribute long long[] rll;
+ attribute unsigned long long[] ull;
+ readonly attribute unsigned long long[] rull;
+ attribute DOMString[] str;
+ readonly attribute DOMString[] rstr;
+ attribute object[] obj;
+ readonly attribute object[] robj;
+ attribute object[] _object;
+ attribute float[] f;
+ readonly attribute float[] rf;
+ };
+
+ interface TestAttrNullableArray {
+ attribute byte[]? b;
+ readonly attribute byte[]? rb;
+ attribute octet[]? o;
+ readonly attribute octet[]? ro;
+ attribute short[]? s;
+ readonly attribute short[]? rs;
+ attribute unsigned short[]? us;
+ readonly attribute unsigned short[]? rus;
+ attribute long[]? l;
+ readonly attribute long[]? rl;
+ attribute unsigned long[]? ul;
+ readonly attribute unsigned long[]? rul;
+ attribute long long[]? ll;
+ readonly attribute long long[]? rll;
+ attribute unsigned long long[]? ull;
+ readonly attribute unsigned long long[]? rull;
+ attribute DOMString[]? str;
+ readonly attribute DOMString[]? rstr;
+ attribute object[]? obj;
+ readonly attribute object[]? robj;
+ attribute object[]? _object;
+ attribute float[]? f;
+ readonly attribute float[]? rf;
+ };
+
+ interface TestAttrArrayOfNullableTypes {
+ attribute byte?[] b;
+ readonly attribute byte?[] rb;
+ attribute octet?[] o;
+ readonly attribute octet?[] ro;
+ attribute short?[] s;
+ readonly attribute short?[] rs;
+ attribute unsigned short?[] us;
+ readonly attribute unsigned short?[] rus;
+ attribute long?[] l;
+ readonly attribute long?[] rl;
+ attribute unsigned long?[] ul;
+ readonly attribute unsigned long?[] rul;
+ attribute long long?[] ll;
+ readonly attribute long long?[] rll;
+ attribute unsigned long long?[] ull;
+ readonly attribute unsigned long long?[] rull;
+ attribute DOMString?[] str;
+ readonly attribute DOMString?[] rstr;
+ attribute object?[] obj;
+ readonly attribute object?[] robj;
+ attribute object?[] _object;
+ attribute float?[] f;
+ readonly attribute float?[] rf;
+ };
+
+ interface TestAttrNullableArrayOfNullableTypes {
+ attribute byte?[]? b;
+ readonly attribute byte?[]? rb;
+ attribute octet?[]? o;
+ readonly attribute octet?[]? ro;
+ attribute short?[]? s;
+ readonly attribute short?[]? rs;
+ attribute unsigned short?[]? us;
+ readonly attribute unsigned short?[]? rus;
+ attribute long?[]? l;
+ readonly attribute long?[]? rl;
+ attribute unsigned long?[]? ul;
+ readonly attribute unsigned long?[]? rul;
+ attribute long long?[]? ll;
+ readonly attribute long long?[]? rll;
+ attribute unsigned long long?[]? ull;
+ readonly attribute unsigned long long?[]? rull;
+ attribute DOMString?[]? str;
+ readonly attribute DOMString?[]? rstr;
+ attribute object?[]? obj;
+ readonly attribute object?[]? robj;
+ attribute object?[]? _object;
+ attribute float?[]? f;
+ readonly attribute float?[]? rf;
+ };
+ """)
+
+ results = parser.finish()
+
+ def checkAttr(attr, QName, name, type, readonly):
+ harness.ok(isinstance(attr, WebIDL.IDLAttribute),
+ "Should be an IDLAttribute")
+ harness.ok(attr.isAttr(), "Attr is an Attr")
+ harness.ok(not attr.isMethod(), "Attr is not an method")
+ harness.ok(not attr.isConst(), "Attr is not a const")
+ harness.check(attr.identifier.QName(), QName, "Attr has the right QName")
+ harness.check(attr.identifier.name, name, "Attr has the right name")
+ harness.check(str(attr.type), type, "Attr has the right type")
+ harness.check(attr.readonly, readonly, "Attr's readonly state is correct")
+
+ harness.ok(True, "TestAttr interface parsed without error.")
+ harness.check(len(results), 6, "Should be six productions.")
+ iface = results[0]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ harness.check(iface.identifier.QName(), "::TestAttr", "Interface has the right QName")
+ harness.check(iface.identifier.name, "TestAttr", "Interface has the right name")
+ harness.check(len(iface.members), len(testData), "Expect %s members" % len(testData))
+
+ attrs = iface.members
+
+ for i in range(len(attrs)):
+ data = testData[i]
+ attr = attrs[i]
+ (QName, name, type, readonly) = data
+ checkAttr(attr, QName % "", name, type % "", readonly)
+
+ iface = results[1]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ harness.check(iface.identifier.QName(), "::TestAttrNullable", "Interface has the right QName")
+ harness.check(iface.identifier.name, "TestAttrNullable", "Interface has the right name")
+ harness.check(len(iface.members), len(testData), "Expect %s members" % len(testData))
+
+ attrs = iface.members
+
+ for i in range(len(attrs)):
+ data = testData[i]
+ attr = attrs[i]
+ (QName, name, type, readonly) = data
+ checkAttr(attr, QName % "Nullable", name, type % "OrNull", readonly)
+
+ iface = results[2]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ harness.check(iface.identifier.QName(), "::TestAttrArray", "Interface has the right QName")
+ harness.check(iface.identifier.name, "TestAttrArray", "Interface has the right name")
+ harness.check(len(iface.members), len(testData), "Expect %s members" % len(testData))
+
+ attrs = iface.members
+
+ for i in range(len(attrs)):
+ data = testData[i]
+ attr = attrs[i]
+ (QName, name, type, readonly) = data
+ checkAttr(attr, QName % "Array", name, type % "Array", readonly)
+
+ iface = results[3]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ harness.check(iface.identifier.QName(), "::TestAttrNullableArray", "Interface has the right QName")
+ harness.check(iface.identifier.name, "TestAttrNullableArray", "Interface has the right name")
+ harness.check(len(iface.members), len(testData), "Expect %s members" % len(testData))
+
+ attrs = iface.members
+
+ for i in range(len(attrs)):
+ data = testData[i]
+ attr = attrs[i]
+ (QName, name, type, readonly) = data
+ checkAttr(attr, QName % "NullableArray", name, type % "ArrayOrNull", readonly)
+
+ iface = results[4]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ harness.check(iface.identifier.QName(), "::TestAttrArrayOfNullableTypes", "Interface has the right QName")
+ harness.check(iface.identifier.name, "TestAttrArrayOfNullableTypes", "Interface has the right name")
+ harness.check(len(iface.members), len(testData), "Expect %s members" % len(testData))
+
+ attrs = iface.members
+
+ for i in range(len(attrs)):
+ data = testData[i]
+ attr = attrs[i]
+ (QName, name, type, readonly) = data
+ checkAttr(attr, QName % "ArrayOfNullableTypes", name, type % "OrNullArray", readonly)
+
+ iface = results[5]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ harness.check(iface.identifier.QName(), "::TestAttrNullableArrayOfNullableTypes", "Interface has the right QName")
+ harness.check(iface.identifier.name, "TestAttrNullableArrayOfNullableTypes", "Interface has the right name")
+ harness.check(len(iface.members), len(testData), "Expect %s members" % len(testData))
+
+ attrs = iface.members
+
+ for i in range(len(attrs)):
+ data = testData[i]
+ attr = attrs[i]
+ (QName, name, type, readonly) = data
+ checkAttr(attr, QName % "NullableArrayOfNullableTypes", name, type % "OrNullArrayOrNull", readonly)
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A {
+ [SetterInfallible] readonly attribute boolean foo;
+ };
+ """)
+ results = parser.finish()
+ except Exception, x:
+ threw = True
+ harness.ok(threw, "Should not allow [SetterInfallible] on readonly attributes")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_attr_sequence_type.py b/components/script/dom/bindings/codegen/parser/tests/test_attr_sequence_type.py
new file mode 100644
index 00000000000..fb1b97812bc
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_attr_sequence_type.py
@@ -0,0 +1,67 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface AttrSequenceType {
+ attribute sequence<object> foo;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Attribute type must not be a sequence type")
+
+ parser.reset()
+
+ threw = False
+ try:
+ parser.parse("""
+ interface AttrUnionWithSequenceType {
+ attribute (sequence<object> or DOMString) foo;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Attribute type must not be a union with a sequence member type")
+
+ parser.reset()
+
+ threw = False
+ try:
+ parser.parse("""
+ interface AttrNullableUnionWithSequenceType {
+ attribute (sequence<object>? or DOMString) foo;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Attribute type must not be a union with a nullable sequence "
+ "member type")
+
+ parser.reset()
+
+ threw = False
+ try:
+ parser.parse("""
+ interface AttrUnionWithUnionWithSequenceType {
+ attribute ((sequence<object> or DOMString) or AttrUnionWithUnionWithSequenceType) foo;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Attribute type must not be a union type with a union member "
+ "type that has a sequence member type")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_builtin_filename.py b/components/script/dom/bindings/codegen/parser/tests/test_builtin_filename.py
new file mode 100644
index 00000000000..631e52eba0b
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_builtin_filename.py
@@ -0,0 +1,11 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface Test {
+ attribute long b;
+ };
+ """);
+
+ attr = parser.finish()[0].members[0]
+ harness.check(attr.type.filename(), '<builtin>', 'Filename on builtin type')
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_builtins.py b/components/script/dom/bindings/codegen/parser/tests/test_builtins.py
new file mode 100644
index 00000000000..f8563fc2d9b
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_builtins.py
@@ -0,0 +1,41 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface TestBuiltins {
+ attribute boolean b;
+ attribute byte s8;
+ attribute octet u8;
+ attribute short s16;
+ attribute unsigned short u16;
+ attribute long s32;
+ attribute unsigned long u32;
+ attribute long long s64;
+ attribute unsigned long long u64;
+ attribute DOMTimeStamp ts;
+ };
+ """)
+
+ results = parser.finish()
+
+ harness.ok(True, "TestBuiltins interface parsed without error.")
+ harness.check(len(results), 1, "Should be one production")
+ harness.ok(isinstance(results[0], WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ iface = results[0]
+ harness.check(iface.identifier.QName(), "::TestBuiltins", "Interface has the right QName")
+ harness.check(iface.identifier.name, "TestBuiltins", "Interface has the right name")
+ harness.check(iface.parent, None, "Interface has no parent")
+
+ members = iface.members
+ harness.check(len(members), 10, "Should be one production")
+
+ names = ["b", "s8", "u8", "s16", "u16", "s32", "u32", "s64", "u64", "ts"]
+ types = ["Boolean", "Byte", "Octet", "Short", "UnsignedShort", "Long", "UnsignedLong", "LongLong", "UnsignedLongLong", "UnsignedLongLong"]
+ for i in range(10):
+ attr = members[i]
+ harness.ok(isinstance(attr, WebIDL.IDLAttribute), "Should be an IDLAttribute")
+ harness.check(attr.identifier.QName(), "::TestBuiltins::" + names[i], "Attr has correct QName")
+ harness.check(attr.identifier.name, names[i], "Attr has correct name")
+ harness.check(str(attr.type), types[i], "Attr type is the correct name")
+ harness.ok(attr.type.isPrimitive(), "Should be a primitive type")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_callback.py b/components/script/dom/bindings/codegen/parser/tests/test_callback.py
new file mode 100644
index 00000000000..267d27dc087
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_callback.py
@@ -0,0 +1,34 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface TestCallback {
+ attribute CallbackType? listener;
+ };
+
+ callback CallbackType = boolean (unsigned long arg);
+ """)
+
+ results = parser.finish()
+
+ harness.ok(True, "TestCallback interface parsed without error.")
+ harness.check(len(results), 2, "Should be one production.")
+ iface = results[0]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ harness.check(iface.identifier.QName(), "::TestCallback", "Interface has the right QName")
+ harness.check(iface.identifier.name, "TestCallback", "Interface has the right name")
+ harness.check(len(iface.members), 1, "Expect %s members" % 1)
+
+ attr = iface.members[0]
+ harness.ok(isinstance(attr, WebIDL.IDLAttribute),
+ "Should be an IDLAttribute")
+ harness.ok(attr.isAttr(), "Should be an attribute")
+ harness.ok(not attr.isMethod(), "Attr is not an method")
+ harness.ok(not attr.isConst(), "Attr is not a const")
+ harness.check(attr.identifier.QName(), "::TestCallback::listener", "Attr has the right QName")
+ harness.check(attr.identifier.name, "listener", "Attr has the right name")
+ t = attr.type
+ harness.ok(not isinstance(t, WebIDL.IDLWrapperType), "Attr has the right type")
+ harness.ok(isinstance(t, WebIDL.IDLNullableType), "Attr has the right type")
+ harness.ok(t.isCallback(), "Attr has the right type")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_callback_interface.py b/components/script/dom/bindings/codegen/parser/tests/test_callback_interface.py
new file mode 100644
index 00000000000..80896ca1edb
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_callback_interface.py
@@ -0,0 +1,47 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ callback interface TestCallbackInterface {
+ attribute boolean bool;
+ };
+ """)
+
+ results = parser.finish()
+
+ iface = results[0]
+
+ harness.ok(iface.isCallback(), "Interface should be a callback")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface TestInterface {
+ };
+ callback interface TestCallbackInterface : TestInterface {
+ attribute boolean bool;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow non-callback parent of callback interface")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface TestInterface : TestCallbackInterface {
+ };
+ callback interface TestCallbackInterface {
+ attribute boolean bool;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow callback parent of non-callback interface")
+
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_const.py b/components/script/dom/bindings/codegen/parser/tests/test_const.py
new file mode 100644
index 00000000000..12f411363fb
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_const.py
@@ -0,0 +1,64 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface TestConsts {
+ const byte zero = 0;
+ const byte b = -1;
+ const octet o = 2;
+ const short s = -3;
+ const unsigned short us = 0x4;
+ const long l = -0X5;
+ const unsigned long ul = 6;
+ const unsigned long long ull = 7;
+ const long long ll = -010;
+ const boolean t = true;
+ const boolean f = false;
+ const boolean? n = null;
+ const boolean? nt = true;
+ const boolean? nf = false;
+ };
+ """)
+
+ results = parser.finish()
+
+ harness.ok(True, "TestConsts interface parsed without error.")
+ harness.check(len(results), 1, "Should be one production.")
+ iface = results[0]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ harness.check(iface.identifier.QName(), "::TestConsts", "Interface has the right QName")
+ harness.check(iface.identifier.name, "TestConsts", "Interface has the right name")
+ harness.check(len(iface.members), 14, "Expect 14 members")
+
+ consts = iface.members
+
+ def checkConst(const, QName, name, type, value):
+ harness.ok(isinstance(const, WebIDL.IDLConst),
+ "Should be an IDLConst")
+ harness.ok(const.isConst(), "Const is a const")
+ harness.ok(not const.isAttr(), "Const is not an attr")
+ harness.ok(not const.isMethod(), "Const is not a method")
+ harness.check(const.identifier.QName(), QName, "Const has the right QName")
+ harness.check(const.identifier.name, name, "Const has the right name")
+ harness.check(str(const.type), type, "Const has the right type")
+ harness.ok(const.type.isPrimitive(), "All consts should be primitive")
+ harness.check(str(const.value.type), str(const.type),
+ "Const's value has the same type as the type")
+ harness.check(const.value.value, value, "Const value has the right value.")
+
+ checkConst(consts[0], "::TestConsts::zero", "zero", "Byte", 0)
+ checkConst(consts[1], "::TestConsts::b", "b", "Byte", -1)
+ checkConst(consts[2], "::TestConsts::o", "o", "Octet", 2)
+ checkConst(consts[3], "::TestConsts::s", "s", "Short", -3)
+ checkConst(consts[4], "::TestConsts::us", "us", "UnsignedShort", 4)
+ checkConst(consts[5], "::TestConsts::l", "l", "Long", -5)
+ checkConst(consts[6], "::TestConsts::ul", "ul", "UnsignedLong", 6)
+ checkConst(consts[7], "::TestConsts::ull", "ull", "UnsignedLongLong", 7)
+ checkConst(consts[8], "::TestConsts::ll", "ll", "LongLong", -8)
+ checkConst(consts[9], "::TestConsts::t", "t", "Boolean", True)
+ checkConst(consts[10], "::TestConsts::f", "f", "Boolean", False)
+ checkConst(consts[11], "::TestConsts::n", "n", "BooleanOrNull", None)
+ checkConst(consts[12], "::TestConsts::nt", "nt", "BooleanOrNull", True)
+ checkConst(consts[13], "::TestConsts::nf", "nf", "BooleanOrNull", False)
+
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_constructor.py b/components/script/dom/bindings/codegen/parser/tests/test_constructor.py
new file mode 100644
index 00000000000..6ec1be1871b
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_constructor.py
@@ -0,0 +1,75 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ def checkArgument(argument, QName, name, type, optional, variadic):
+ harness.ok(isinstance(argument, WebIDL.IDLArgument),
+ "Should be an IDLArgument")
+ harness.check(argument.identifier.QName(), QName, "Argument has the right QName")
+ harness.check(argument.identifier.name, name, "Argument has the right name")
+ harness.check(str(argument.type), type, "Argument has the right return type")
+ harness.check(argument.optional, optional, "Argument has the right optional value")
+ harness.check(argument.variadic, variadic, "Argument has the right variadic value")
+
+ def checkMethod(method, QName, name, signatures,
+ static=False, getter=False, setter=False, creator=False,
+ deleter=False, legacycaller=False, stringifier=False):
+ harness.ok(isinstance(method, WebIDL.IDLMethod),
+ "Should be an IDLMethod")
+ harness.ok(method.isMethod(), "Method is a method")
+ harness.ok(not method.isAttr(), "Method is not an attr")
+ harness.ok(not method.isConst(), "Method is not a const")
+ harness.check(method.identifier.QName(), QName, "Method has the right QName")
+ harness.check(method.identifier.name, name, "Method has the right name")
+ harness.check(method.isStatic(), static, "Method has the correct static value")
+ harness.check(method.isGetter(), getter, "Method has the correct getter value")
+ harness.check(method.isSetter(), setter, "Method has the correct setter value")
+ harness.check(method.isCreator(), creator, "Method has the correct creator value")
+ harness.check(method.isDeleter(), deleter, "Method has the correct deleter value")
+ harness.check(method.isLegacycaller(), legacycaller, "Method has the correct legacycaller value")
+ harness.check(method.isStringifier(), stringifier, "Method has the correct stringifier value")
+ harness.check(len(method.signatures()), len(signatures), "Method has the correct number of signatures")
+
+ sigpairs = zip(method.signatures(), signatures)
+ for (gotSignature, expectedSignature) in sigpairs:
+ (gotRetType, gotArgs) = gotSignature
+ (expectedRetType, expectedArgs) = expectedSignature
+
+ harness.check(str(gotRetType), expectedRetType,
+ "Method has the expected return type.")
+
+ for i in range(0, len(gotArgs)):
+ (QName, name, type, optional, variadic) = expectedArgs[i]
+ checkArgument(gotArgs[i], QName, name, type, optional, variadic)
+
+ parser.parse("""
+ [Constructor]
+ interface TestConstructorNoArgs {
+ };
+
+ [Constructor(DOMString name)]
+ interface TestConstructorWithArgs {
+ };
+
+ [Constructor(object foo), Constructor(boolean bar)]
+ interface TestConstructorOverloads {
+ };
+ """)
+ results = parser.finish()
+ harness.check(len(results), 3, "Should be two productions")
+ harness.ok(isinstance(results[0], WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ harness.ok(isinstance(results[1], WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+
+ checkMethod(results[0].ctor(), "::TestConstructorNoArgs::constructor",
+ "constructor", [("TestConstructorNoArgs (Wrapper)", [])])
+ checkMethod(results[1].ctor(), "::TestConstructorWithArgs::constructor",
+ "constructor",
+ [("TestConstructorWithArgs (Wrapper)",
+ [("::TestConstructorWithArgs::constructor::name", "name", "String", False, False)])])
+ checkMethod(results[2].ctor(), "::TestConstructorOverloads::constructor",
+ "constructor",
+ [("TestConstructorOverloads (Wrapper)",
+ [("::TestConstructorOverloads::constructor::foo", "foo", "Object", False, False)]),
+ ("TestConstructorOverloads (Wrapper)",
+ [("::TestConstructorOverloads::constructor::bar", "bar", "Boolean", False, False)])])
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_constructor_no_interface_object.py b/components/script/dom/bindings/codegen/parser/tests/test_constructor_no_interface_object.py
new file mode 100644
index 00000000000..192c5f6f97b
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_constructor_no_interface_object.py
@@ -0,0 +1,28 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ [Constructor, NoInterfaceObject]
+ interface TestConstructorNoInterfaceObject {
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ [NoInterfaceObject, Constructor]
+ interface TestConstructorNoInterfaceObject {
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_deduplicate.py b/components/script/dom/bindings/codegen/parser/tests/test_deduplicate.py
new file mode 100644
index 00000000000..6249d36fb8f
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_deduplicate.py
@@ -0,0 +1,15 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface Foo;
+ interface Bar;
+ interface Foo;
+ """);
+
+ results = parser.finish()
+
+ # There should be no duplicate interfaces in the result.
+ expectedNames = sorted(['Foo', 'Bar'])
+ actualNames = sorted(map(lambda iface: iface.identifier.name, results))
+ harness.check(actualNames, expectedNames, "Parser shouldn't output duplicate names.")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_dictionary.py b/components/script/dom/bindings/codegen/parser/tests/test_dictionary.py
new file mode 100644
index 00000000000..9ae9eb2b66f
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_dictionary.py
@@ -0,0 +1,198 @@
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ dictionary Dict2 : Dict1 {
+ long child = 5;
+ Dict1 aaandAnother;
+ };
+ dictionary Dict1 {
+ long parent;
+ double otherParent;
+ };
+ """)
+ results = parser.finish()
+
+ dict1 = results[1];
+ dict2 = results[0];
+
+ harness.check(len(dict1.members), 2, "Dict1 has two members")
+ harness.check(len(dict2.members), 2, "Dict2 has four members")
+
+ harness.check(dict1.members[0].identifier.name, "otherParent",
+ "'o' comes before 'p'")
+ harness.check(dict1.members[1].identifier.name, "parent",
+ "'o' really comes before 'p'")
+ harness.check(dict2.members[0].identifier.name, "aaandAnother",
+ "'a' comes before 'c'")
+ harness.check(dict2.members[1].identifier.name, "child",
+ "'a' really comes before 'c'")
+
+ # Now reset our parser
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary Dict {
+ long prop = 5;
+ long prop;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow name duplication in a dictionary")
+
+ # Now reset our parser again
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary Dict1 : Dict2 {
+ long prop = 5;
+ };
+ dictionary Dict2 : Dict3 {
+ long prop2;
+ };
+ dictionary Dict3 {
+ double prop;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow name duplication in a dictionary and "
+ "its ancestor")
+
+ # More reset
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface Iface {};
+ dictionary Dict : Iface {
+ long prop;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow non-dictionary parents for dictionaries")
+
+ # Even more reset
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary A : B {};
+ dictionary B : A {};
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow cycles in dictionary inheritance chains")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary A {
+ [TreatNullAs=EmptyString] DOMString foo;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow [TreatNullAs] on dictionary members");
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary A {
+ [TreatUndefinedAs=EmptyString] DOMString foo;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow [TreatUndefinedAs] on dictionary members");
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary A {
+ };
+ interface X {
+ void doFoo(A arg);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Trailing dictionary arg must be optional")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary A {
+ };
+ interface X {
+ void doFoo(A arg1, optional long arg2);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Dictionary arg followed by optional arg must be optional")
+
+ parser = parser.reset()
+ parser.parse("""
+ dictionary A {
+ };
+ interface X {
+ void doFoo(A arg1, long arg2);
+ };
+ """)
+ results = parser.finish()
+ harness.ok(True, "Dictionary arg followed by required arg can be required")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary A {
+ };
+ interface X {
+ void doFoo(optional A? arg1);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Dictionary arg must not be nullable")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary A {
+ };
+ interface X {
+ void doFoo((A or long)? arg1);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Dictionary arg must not be in a nullable union")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_distinguishability.py b/components/script/dom/bindings/codegen/parser/tests/test_distinguishability.py
new file mode 100644
index 00000000000..86847800631
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_distinguishability.py
@@ -0,0 +1,150 @@
+def firstArgType(method):
+ return method.signatures()[0][1][0].type
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ dictionary Dict {
+ };
+ callback interface Foo {
+ };
+ interface Bar {
+ // Bit of a pain to get things that have dictionary types
+ void passDict(optional Dict arg);
+ void passFoo(Foo arg);
+ void passNullableUnion((object? or DOMString) arg);
+ void passNullable(Foo? arg);
+ };
+ """)
+ results = parser.finish()
+
+ iface = results[2]
+ harness.ok(iface.isInterface(), "Should have interface")
+ dictMethod = iface.members[0]
+ ifaceMethod = iface.members[1]
+ nullableUnionMethod = iface.members[2]
+ nullableIfaceMethod = iface.members[3]
+
+ dictType = firstArgType(dictMethod)
+ ifaceType = firstArgType(ifaceMethod)
+
+ harness.ok(dictType.isDictionary(), "Should have dictionary type");
+ harness.ok(ifaceType.isInterface(), "Should have interface type");
+ harness.ok(ifaceType.isCallbackInterface(), "Should have callback interface type");
+
+ harness.ok(not dictType.isDistinguishableFrom(ifaceType),
+ "Dictionary not distinguishable from callback interface")
+ harness.ok(not ifaceType.isDistinguishableFrom(dictType),
+ "Callback interface not distinguishable from dictionary")
+
+ nullableUnionType = firstArgType(nullableUnionMethod)
+ nullableIfaceType = firstArgType(nullableIfaceMethod)
+
+ harness.ok(nullableUnionType.isUnion(), "Should have union type");
+ harness.ok(nullableIfaceType.isInterface(), "Should have interface type");
+ harness.ok(nullableIfaceType.nullable(), "Should have nullable type");
+
+ harness.ok(not nullableUnionType.isDistinguishableFrom(nullableIfaceType),
+ "Nullable type not distinguishable from union with nullable "
+ "member type")
+ harness.ok(not nullableIfaceType.isDistinguishableFrom(nullableUnionType),
+ "Union with nullable member type not distinguishable from "
+ "nullable type")
+
+ parser = parser.reset()
+ parser.parse("""
+ interface TestIface {
+ void passKid(Kid arg);
+ void passParent(Parent arg);
+ void passGrandparent(Grandparent arg);
+ void passImplemented(Implemented arg);
+ void passImplementedParent(ImplementedParent arg);
+ void passUnrelated1(Unrelated1 arg);
+ void passUnrelated2(Unrelated2 arg);
+ void passArrayBuffer(ArrayBuffer arg);
+ void passArrayBuffer(ArrayBufferView arg);
+ };
+
+ interface Kid : Parent {};
+ interface Parent : Grandparent {};
+ interface Grandparent {};
+ interface Implemented : ImplementedParent {};
+ Parent implements Implemented;
+ interface ImplementedParent {};
+ interface Unrelated1 {};
+ interface Unrelated2 {};
+ """)
+ results = parser.finish()
+
+ iface = results[0]
+ harness.ok(iface.isInterface(), "Should have interface")
+ argTypes = [firstArgType(method) for method in iface.members]
+ unrelatedTypes = [firstArgType(method) for method in iface.members[-3:]]
+
+ for type1 in argTypes:
+ for type2 in argTypes:
+ distinguishable = (type1 is not type2 and
+ (type1 in unrelatedTypes or
+ type2 in unrelatedTypes))
+
+ harness.check(type1.isDistinguishableFrom(type2),
+ distinguishable,
+ "Type %s should %sbe distinguishable from type %s" %
+ (type1, "" if distinguishable else "not ", type2))
+ harness.check(type2.isDistinguishableFrom(type1),
+ distinguishable,
+ "Type %s should %sbe distinguishable from type %s" %
+ (type2, "" if distinguishable else "not ", type1))
+
+ parser = parser.reset()
+ parser.parse("""
+ interface Dummy {};
+ interface TestIface {
+ void method(long arg1, TestIface arg2);
+ void method(long arg1, long arg2);
+ void method(long arg1, Dummy arg2);
+ void method(DOMString arg1, DOMString arg2, DOMString arg3);
+ };
+ """)
+ results = parser.finish()
+ harness.check(len(results[1].members), 1,
+ "Should look like we have one method")
+ harness.check(len(results[1].members[0].signatures()), 4,
+ "Should have four signatures")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface Dummy {};
+ interface TestIface {
+ void method(long arg1, TestIface arg2);
+ void method(long arg1, long arg2);
+ void method(any arg1, Dummy arg2);
+ void method(DOMString arg1, DOMString arg2, DOMString arg3);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Should throw when args before the distinguishing arg are not "
+ "all the same type")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface Dummy {};
+ interface TestIface {
+ void method(long arg1, TestIface arg2);
+ void method(long arg1, long arg2);
+ void method(any arg1, DOMString arg2);
+ void method(DOMString arg1, DOMString arg2, DOMString arg3);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should throw when there is no distinguishing index")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_double_null.py b/components/script/dom/bindings/codegen/parser/tests/test_double_null.py
new file mode 100644
index 00000000000..700c7eade00
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_double_null.py
@@ -0,0 +1,14 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface DoubleNull {
+ attribute byte?? foo;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_duplicate_qualifiers.py b/components/script/dom/bindings/codegen/parser/tests/test_duplicate_qualifiers.py
new file mode 100644
index 00000000000..799f2e0e0ed
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_duplicate_qualifiers.py
@@ -0,0 +1,84 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface DuplicateQualifiers1 {
+ getter getter byte foo(unsigned long index);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface DuplicateQualifiers2 {
+ setter setter byte foo(unsigned long index, byte value);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface DuplicateQualifiers3 {
+ creator creator byte foo(unsigned long index, byte value);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface DuplicateQualifiers4 {
+ deleter deleter byte foo(unsigned long index);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface DuplicateQualifiers5 {
+ getter deleter getter byte foo(unsigned long index);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ results = parser.parse("""
+ interface DuplicateQualifiers6 {
+ creator setter creator byte foo(unsigned long index, byte value);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_empty_enum.py b/components/script/dom/bindings/codegen/parser/tests/test_empty_enum.py
new file mode 100644
index 00000000000..ee0079f06da
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_empty_enum.py
@@ -0,0 +1,14 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ try:
+ parser.parse("""
+ enum TestEmptyEnum {
+ };
+ """)
+
+ harness.ok(False, "Should have thrown!")
+ except:
+ harness.ok(True, "Parsing TestEmptyEnum enum should fail")
+
+ results = parser.finish()
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_enum.py b/components/script/dom/bindings/codegen/parser/tests/test_enum.py
new file mode 100644
index 00000000000..69a6932062d
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_enum.py
@@ -0,0 +1,81 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ enum TestEnum {
+ "",
+ "foo",
+ "bar"
+ };
+
+ interface TestEnumInterface {
+ TestEnum doFoo(boolean arg);
+ readonly attribute TestEnum foo;
+ };
+ """)
+
+ results = parser.finish()
+
+ harness.ok(True, "TestEnumInterfaces interface parsed without error.")
+ harness.check(len(results), 2, "Should be one production")
+ harness.ok(isinstance(results[0], WebIDL.IDLEnum),
+ "Should be an IDLEnum")
+ harness.ok(isinstance(results[1], WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+
+ enum = results[0]
+ harness.check(enum.identifier.QName(), "::TestEnum", "Enum has the right QName")
+ harness.check(enum.identifier.name, "TestEnum", "Enum has the right name")
+ harness.check(enum.values(), ["", "foo", "bar"], "Enum has the right values")
+
+ iface = results[1]
+
+ harness.check(iface.identifier.QName(), "::TestEnumInterface", "Interface has the right QName")
+ harness.check(iface.identifier.name, "TestEnumInterface", "Interface has the right name")
+ harness.check(iface.parent, None, "Interface has no parent")
+
+ members = iface.members
+ harness.check(len(members), 2, "Should be one production")
+ harness.ok(isinstance(members[0], WebIDL.IDLMethod),
+ "Should be an IDLMethod")
+ method = members[0]
+ harness.check(method.identifier.QName(), "::TestEnumInterface::doFoo",
+ "Method has correct QName")
+ harness.check(method.identifier.name, "doFoo", "Method has correct name")
+
+ signatures = method.signatures()
+ harness.check(len(signatures), 1, "Expect one signature")
+
+ (returnType, arguments) = signatures[0]
+ harness.check(str(returnType), "TestEnum (Wrapper)", "Method type is the correct name")
+ harness.check(len(arguments), 1, "Method has the right number of arguments")
+ arg = arguments[0]
+ harness.ok(isinstance(arg, WebIDL.IDLArgument), "Should be an IDLArgument")
+ harness.check(str(arg.type), "Boolean", "Argument has the right type")
+
+ attr = members[1]
+ harness.check(attr.identifier.QName(), "::TestEnumInterface::foo",
+ "Attr has correct QName")
+ harness.check(attr.identifier.name, "foo", "Attr has correct name")
+
+ harness.check(str(attr.type), "TestEnum (Wrapper)", "Attr type is the correct name")
+
+ # Now reset our parser
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ enum Enum {
+ "a",
+ "b",
+ "c"
+ };
+ interface TestInterface {
+ void foo(optional Enum e = "d");
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow a bogus default value for an enum")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_enum_duplicate_values.py b/components/script/dom/bindings/codegen/parser/tests/test_enum_duplicate_values.py
new file mode 100644
index 00000000000..51205d209e7
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_enum_duplicate_values.py
@@ -0,0 +1,13 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ try:
+ parser.parse("""
+ enum TestEnumDuplicateValue {
+ "",
+ ""
+ };
+ """)
+ harness.ok(False, "Should have thrown!")
+ except:
+ harness.ok(True, "Enum TestEnumDuplicateValue should throw")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_error_colno.py b/components/script/dom/bindings/codegen/parser/tests/test_error_colno.py
new file mode 100644
index 00000000000..ca0674aec04
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_error_colno.py
@@ -0,0 +1,20 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ # Check that error messages put the '^' in the right place.
+
+ threw = False
+ input = 'interface ?'
+ try:
+ parser.parse(input)
+ results = parser.finish()
+ except WebIDL.WebIDLError, e:
+ threw = True
+ lines = str(e).split('\n')
+
+ harness.check(len(lines), 3, 'Expected number of lines in error message')
+ harness.check(lines[1], input, 'Second line shows error')
+ harness.check(lines[2], ' ' * (len(input) - 1) + '^',
+ 'Correct column pointer in error message')
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_error_lineno.py b/components/script/dom/bindings/codegen/parser/tests/test_error_lineno.py
new file mode 100644
index 00000000000..f11222e7a4d
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_error_lineno.py
@@ -0,0 +1,28 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ # Check that error messages put the '^' in the right place.
+
+ threw = False
+ input = """\
+// This is a comment.
+interface Foo {
+};
+
+/* This is also a comment. */
+interface ?"""
+ try:
+ parser.parse(input)
+ results = parser.finish()
+ except WebIDL.WebIDLError, e:
+ threw = True
+ lines = str(e).split('\n')
+
+ harness.check(len(lines), 3, 'Expected number of lines in error message')
+ harness.ok(lines[0].endswith('line 6:10'), 'First line of error should end with "line 6:10", but was "%s".' % lines[0])
+ harness.check(lines[1], 'interface ?', 'Second line of error message is the line which caused the error.')
+ harness.check(lines[2], ' ' * (len('interface ?') - 1) + '^',
+ 'Correct column pointer in error message.')
+
+ harness.ok(threw, "Should have thrown.")
+
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_extended_attributes.py b/components/script/dom/bindings/codegen/parser/tests/test_extended_attributes.py
new file mode 100644
index 00000000000..5c6887331e7
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_extended_attributes.py
@@ -0,0 +1,107 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ [Flippety]
+ interface TestExtendedAttr {
+ [Foopy] attribute byte b;
+ };
+ """)
+
+ results = parser.finish()
+
+ parser = parser.reset()
+ parser.parse("""
+ [Flippety="foo.bar",Floppety=flop]
+ interface TestExtendedAttr {
+ [Foopy="foo.bar"] attribute byte b;
+ };
+ """)
+
+ results = parser.finish()
+
+ parser = parser.reset()
+ parser.parse("""
+ interface TestLenientThis {
+ [LenientThis] attribute byte b;
+ };
+ """)
+
+ results = parser.finish()
+ harness.ok(results[0].members[0].hasLenientThis(),
+ "Should have a lenient this")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface TestLenientThis2 {
+ [LenientThis=something] attribute byte b;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "[LenientThis] must take no arguments")
+
+ parser = parser.reset()
+ parser.parse("""
+ interface TestClamp {
+ void testClamp([Clamp] long foo);
+ void testNotClamp(long foo);
+ };
+ """)
+
+ results = parser.finish()
+ # Pull out the first argument out of the arglist of the first (and
+ # only) signature.
+ harness.ok(results[0].members[0].signatures()[0][1][0].clamp,
+ "Should be clamped")
+ harness.ok(not results[0].members[1].signatures()[0][1][0].clamp,
+ "Should not be clamped")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface TestClamp2 {
+ void testClamp([Clamp=something] long foo);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "[Clamp] must take no arguments")
+
+ parser = parser.reset()
+ parser.parse("""
+ interface TestEnforceRange {
+ void testEnforceRange([EnforceRange] long foo);
+ void testNotEnforceRange(long foo);
+ };
+ """)
+
+ results = parser.finish()
+ # Pull out the first argument out of the arglist of the first (and
+ # only) signature.
+ harness.ok(results[0].members[0].signatures()[0][1][0].enforceRange,
+ "Should be enforceRange")
+ harness.ok(not results[0].members[1].signatures()[0][1][0].enforceRange,
+ "Should not be enforceRange")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface TestEnforceRange2 {
+ void testEnforceRange([EnforceRange=something] long foo);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "[EnforceRange] must take no arguments")
+
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_forward_decl.py b/components/script/dom/bindings/codegen/parser/tests/test_forward_decl.py
new file mode 100644
index 00000000000..cac24c832cc
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_forward_decl.py
@@ -0,0 +1,15 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface ForwardDeclared;
+ interface ForwardDeclared;
+
+ interface TestForwardDecl {
+ attribute ForwardDeclared foo;
+ };
+ """)
+
+ results = parser.finish()
+
+ harness.ok(True, "TestForwardDeclared interface parsed without error.")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_implements.py b/components/script/dom/bindings/codegen/parser/tests/test_implements.py
new file mode 100644
index 00000000000..04c47d92abe
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_implements.py
@@ -0,0 +1,216 @@
+# Import the WebIDL module, so we can do isinstance checks and whatnot
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ # Basic functionality
+ threw = False
+ try:
+ parser.parse("""
+ A implements B;
+ interface B {
+ attribute long x;
+ };
+ interface A {
+ attribute long y;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(not threw, "Should not have thrown on implements statement "
+ "before interfaces")
+ harness.check(len(results), 3, "We have three statements")
+ harness.ok(isinstance(results[1], WebIDL.IDLInterface), "B is an interface")
+ harness.check(len(results[1].members), 1, "B has one member")
+ A = results[2]
+ harness.ok(isinstance(A, WebIDL.IDLInterface), "A is an interface")
+ harness.check(len(A.members), 2, "A has two members")
+ harness.check(A.members[0].identifier.name, "y", "First member is 'y'")
+ harness.check(A.members[1].identifier.name, "x", "Second member is 'x'")
+
+ # Duplicated member names not allowed
+ threw = False
+ try:
+ parser.parse("""
+ C implements D;
+ interface D {
+ attribute long x;
+ };
+ interface C {
+ attribute long x;
+ };
+ """)
+ parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown on implemented interface duplicating "
+ "a name on base interface")
+
+ # Same, but duplicated across implemented interfaces
+ threw = False
+ try:
+ parser.parse("""
+ E implements F;
+ E implements G;
+ interface F {
+ attribute long x;
+ };
+ interface G {
+ attribute long x;
+ };
+ interface E {};
+ """)
+ parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown on implemented interfaces "
+ "duplicating each other's member names")
+
+ # Same, but duplicated across indirectly implemented interfaces
+ threw = False
+ try:
+ parser.parse("""
+ H implements I;
+ H implements J;
+ I implements K;
+ interface K {
+ attribute long x;
+ };
+ interface L {
+ attribute long x;
+ };
+ interface I {};
+ interface J : L {};
+ interface H {};
+ """)
+ parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown on indirectly implemented interfaces "
+ "duplicating each other's member names")
+
+ # Same, but duplicated across an implemented interface and its parent
+ threw = False
+ try:
+ parser.parse("""
+ M implements N;
+ interface O {
+ attribute long x;
+ };
+ interface N : O {
+ attribute long x;
+ };
+ interface M {};
+ """)
+ parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown on implemented interface and its "
+ "ancestor duplicating member names")
+
+ # Reset the parser so we can actually find things where we expect
+ # them in the list
+ parser = parser.reset()
+
+ # Diamonds should be allowed
+ threw = False
+ try:
+ parser.parse("""
+ P implements Q;
+ P implements R;
+ Q implements S;
+ R implements S;
+ interface Q {};
+ interface R {};
+ interface S {
+ attribute long x;
+ };
+ interface P {};
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(not threw, "Diamond inheritance is fine")
+ harness.check(results[6].identifier.name, "S", "We should be looking at 'S'")
+ harness.check(len(results[6].members), 1, "S should have one member")
+ harness.check(results[6].members[0].identifier.name, "x",
+ "S's member should be 'x'")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface TestInterface {
+ };
+ callback interface TestCallbackInterface {
+ };
+ TestInterface implements TestCallbackInterface;
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Should not allow callback interfaces on the right-hand side "
+ "of 'implements'")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface TestInterface {
+ };
+ callback interface TestCallbackInterface {
+ };
+ TestCallbackInterface implements TestInterface;
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Should not allow callback interfaces on the left-hand side of "
+ "'implements'")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface TestInterface {
+ };
+ dictionary Dict {
+ };
+ Dict implements TestInterface;
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Should not allow non-interfaces on the left-hand side "
+ "of 'implements'")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface TestInterface {
+ };
+ dictionary Dict {
+ };
+ TestInterface implements Dict;
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Should not allow non-interfaces on the right-hand side "
+ "of 'implements'")
+
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_incomplete_parent.py b/components/script/dom/bindings/codegen/parser/tests/test_incomplete_parent.py
new file mode 100644
index 00000000000..1f520a28e16
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_incomplete_parent.py
@@ -0,0 +1,18 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface TestIncompleteParent : NotYetDefined {
+ void foo();
+ };
+
+ interface NotYetDefined : EvenHigherOnTheChain {
+ };
+
+ interface EvenHigherOnTheChain {
+ };
+ """)
+
+ parser.finish()
+
+ harness.ok(True, "TestIncompleteParent interface parsed without error.")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_incomplete_types.py b/components/script/dom/bindings/codegen/parser/tests/test_incomplete_types.py
new file mode 100644
index 00000000000..fdc39604070
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_incomplete_types.py
@@ -0,0 +1,44 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface TestIncompleteTypes {
+ attribute FooInterface attr1;
+
+ FooInterface method1(FooInterface arg);
+ };
+
+ interface FooInterface {
+ };
+ """)
+
+ results = parser.finish()
+
+ harness.ok(True, "TestIncompleteTypes interface parsed without error.")
+ harness.check(len(results), 2, "Should be two productions.")
+ iface = results[0]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ harness.check(iface.identifier.QName(), "::TestIncompleteTypes", "Interface has the right QName")
+ harness.check(iface.identifier.name, "TestIncompleteTypes", "Interface has the right name")
+ harness.check(len(iface.members), 2, "Expect 2 members")
+
+ attr = iface.members[0]
+ harness.ok(isinstance(attr, WebIDL.IDLAttribute),
+ "Should be an IDLAttribute")
+ method = iface.members[1]
+ harness.ok(isinstance(method, WebIDL.IDLMethod),
+ "Should be an IDLMethod")
+
+ harness.check(attr.identifier.QName(), "::TestIncompleteTypes::attr1",
+ "Attribute has the right QName")
+ harness.check(attr.type.name, "FooInterface",
+ "Previously unresolved type has the right name")
+
+ harness.check(method.identifier.QName(), "::TestIncompleteTypes::method1",
+ "Attribute has the right QName")
+ (returnType, args) = method.signatures()[0]
+ harness.check(returnType.name, "FooInterface",
+ "Previously unresolved type has the right name")
+ harness.check(args[0].type.name, "FooInterface",
+ "Previously unresolved type has the right name")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_interface.py b/components/script/dom/bindings/codegen/parser/tests/test_interface.py
new file mode 100644
index 00000000000..5b07172c636
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_interface.py
@@ -0,0 +1,188 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("interface Foo { };")
+ results = parser.finish()
+ harness.ok(True, "Empty interface parsed without error.")
+ harness.check(len(results), 1, "Should be one production")
+ harness.ok(isinstance(results[0], WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ iface = results[0]
+ harness.check(iface.identifier.QName(), "::Foo", "Interface has the right QName")
+ harness.check(iface.identifier.name, "Foo", "Interface has the right name")
+ harness.check(iface.parent, None, "Interface has no parent")
+
+ parser.parse("interface Bar : Foo { };")
+ results = parser.finish()
+ harness.ok(True, "Empty interface parsed without error.")
+ harness.check(len(results), 2, "Should be two productions")
+ harness.ok(isinstance(results[1], WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ iface = results[1]
+ harness.check(iface.identifier.QName(), "::Bar", "Interface has the right QName")
+ harness.check(iface.identifier.name, "Bar", "Interface has the right name")
+ harness.ok(isinstance(iface.parent, WebIDL.IDLInterface),
+ "Interface has a parent")
+
+ parser = parser.reset()
+ parser.parse("""
+ interface QNameBase {
+ attribute long foo;
+ };
+
+ interface QNameDerived : QNameBase {
+ attribute long long foo;
+ attribute byte bar;
+ };
+ """)
+ results = parser.finish()
+ harness.check(len(results), 2, "Should be two productions")
+ harness.ok(isinstance(results[0], WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ harness.ok(isinstance(results[1], WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ harness.check(results[1].parent, results[0], "Inheritance chain is right")
+ harness.check(len(results[0].members), 1, "Expect 1 productions")
+ harness.check(len(results[1].members), 2, "Expect 2 productions")
+ base = results[0]
+ derived = results[1]
+ harness.check(base.members[0].identifier.QName(), "::QNameBase::foo",
+ "Member has the right QName")
+ harness.check(derived.members[0].identifier.QName(), "::QNameDerived::foo",
+ "Member has the right QName")
+ harness.check(derived.members[1].identifier.QName(), "::QNameDerived::bar",
+ "Member has the right QName")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A : B {};
+ interface B : A {};
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow cycles in interface inheritance chains")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A : C {};
+ interface C : B {};
+ interface B : A {};
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow indirect cycles in interface inheritance chains")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A {};
+ interface B {};
+ A implements B;
+ B implements A;
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow cycles via implements")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A {};
+ interface C {};
+ interface B {};
+ A implements C;
+ C implements B;
+ B implements A;
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow indirect cycles via implements")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A : B {};
+ interface B {};
+ B implements A;
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow inheriting from an interface that implements us")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A : B {};
+ interface B {};
+ interface C {};
+ B implements C;
+ C implements A;
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow inheriting from an interface that indirectly implements us")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A : B {};
+ interface B : C {};
+ interface C {};
+ C implements A;
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow indirectly inheriting from an interface that implements us")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A : B {};
+ interface B : C {};
+ interface C {};
+ interface D {};
+ C implements D;
+ D implements A;
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow indirectly inheriting from an interface that indirectly implements us")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A;
+ interface B : A {};
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow inheriting from an interface that is only forward declared")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_interface_const_identifier_conflicts.py b/components/script/dom/bindings/codegen/parser/tests/test_interface_const_identifier_conflicts.py
new file mode 100644
index 00000000000..db944e7aaf7
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_interface_const_identifier_conflicts.py
@@ -0,0 +1,15 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface IdentifierConflict {
+ const byte thing1 = 1;
+ const unsigned long thing1 = 1;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_interface_identifier_conflicts_across_members.py b/components/script/dom/bindings/codegen/parser/tests/test_interface_identifier_conflicts_across_members.py
new file mode 100644
index 00000000000..1a73fb917ed
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_interface_identifier_conflicts_across_members.py
@@ -0,0 +1,60 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface IdentifierConflictAcrossMembers1 {
+ const byte thing1 = 1;
+ readonly attribute long thing1;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface IdentifierConflictAcrossMembers2 {
+ readonly attribute long thing1;
+ const byte thing1 = 1;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface IdentifierConflictAcrossMembers3 {
+ getter boolean thing1(DOMString name);
+ readonly attribute long thing1;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface IdentifierConflictAcrossMembers1 {
+ const byte thing1 = 1;
+ long thing1();
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_method.py b/components/script/dom/bindings/codegen/parser/tests/test_method.py
new file mode 100644
index 00000000000..40b2d2cf8b9
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_method.py
@@ -0,0 +1,145 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface TestMethods {
+ void basic();
+ static void basicStatic();
+ void basicWithSimpleArgs(boolean arg1, byte arg2, unsigned long arg3);
+ boolean basicBoolean();
+ static boolean basicStaticBoolean();
+ boolean basicBooleanWithSimpleArgs(boolean arg1, byte arg2, unsigned long arg3);
+ void optionalArg(optional byte? arg1, optional sequence<byte> arg2);
+ void variadicArg(byte?... arg1);
+ void crazyTypes(sequence<long?[]>? arg1, boolean?[][]? arg2);
+ object getObject();
+ void setObject(object arg1);
+ void setAny(any arg1);
+ float doFloats(float arg1);
+ };
+ """)
+
+ results = parser.finish()
+
+ harness.ok(True, "TestMethods interface parsed without error.")
+ harness.check(len(results), 1, "Should be one production.")
+ iface = results[0]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ harness.check(iface.identifier.QName(), "::TestMethods", "Interface has the right QName")
+ harness.check(iface.identifier.name, "TestMethods", "Interface has the right name")
+ harness.check(len(iface.members), 13, "Expect 13 members")
+
+ methods = iface.members
+
+ def checkArgument(argument, QName, name, type, optional, variadic):
+ harness.ok(isinstance(argument, WebIDL.IDLArgument),
+ "Should be an IDLArgument")
+ harness.check(argument.identifier.QName(), QName, "Argument has the right QName")
+ harness.check(argument.identifier.name, name, "Argument has the right name")
+ harness.check(str(argument.type), type, "Argument has the right return type")
+ harness.check(argument.optional, optional, "Argument has the right optional value")
+ harness.check(argument.variadic, variadic, "Argument has the right variadic value")
+
+ def checkMethod(method, QName, name, signatures,
+ static=False, getter=False, setter=False, creator=False,
+ deleter=False, legacycaller=False, stringifier=False):
+ harness.ok(isinstance(method, WebIDL.IDLMethod),
+ "Should be an IDLMethod")
+ harness.ok(method.isMethod(), "Method is a method")
+ harness.ok(not method.isAttr(), "Method is not an attr")
+ harness.ok(not method.isConst(), "Method is not a const")
+ harness.check(method.identifier.QName(), QName, "Method has the right QName")
+ harness.check(method.identifier.name, name, "Method has the right name")
+ harness.check(method.isStatic(), static, "Method has the correct static value")
+ harness.check(method.isGetter(), getter, "Method has the correct getter value")
+ harness.check(method.isSetter(), setter, "Method has the correct setter value")
+ harness.check(method.isCreator(), creator, "Method has the correct creator value")
+ harness.check(method.isDeleter(), deleter, "Method has the correct deleter value")
+ harness.check(method.isLegacycaller(), legacycaller, "Method has the correct legacycaller value")
+ harness.check(method.isStringifier(), stringifier, "Method has the correct stringifier value")
+ harness.check(len(method.signatures()), len(signatures), "Method has the correct number of signatures")
+
+ sigpairs = zip(method.signatures(), signatures)
+ for (gotSignature, expectedSignature) in sigpairs:
+ (gotRetType, gotArgs) = gotSignature
+ (expectedRetType, expectedArgs) = expectedSignature
+
+ harness.check(str(gotRetType), expectedRetType,
+ "Method has the expected return type.")
+
+ for i in range(0, len(gotArgs)):
+ (QName, name, type, optional, variadic) = expectedArgs[i]
+ checkArgument(gotArgs[i], QName, name, type, optional, variadic)
+
+ checkMethod(methods[0], "::TestMethods::basic", "basic", [("Void", [])])
+ checkMethod(methods[1], "::TestMethods::basicStatic", "basicStatic",
+ [("Void", [])], static=True)
+ checkMethod(methods[2], "::TestMethods::basicWithSimpleArgs",
+ "basicWithSimpleArgs",
+ [("Void",
+ [("::TestMethods::basicWithSimpleArgs::arg1", "arg1", "Boolean", False, False),
+ ("::TestMethods::basicWithSimpleArgs::arg2", "arg2", "Byte", False, False),
+ ("::TestMethods::basicWithSimpleArgs::arg3", "arg3", "UnsignedLong", False, False)])])
+ checkMethod(methods[3], "::TestMethods::basicBoolean", "basicBoolean", [("Boolean", [])])
+ checkMethod(methods[4], "::TestMethods::basicStaticBoolean", "basicStaticBoolean", [("Boolean", [])], static=True)
+ checkMethod(methods[5], "::TestMethods::basicBooleanWithSimpleArgs",
+ "basicBooleanWithSimpleArgs",
+ [("Boolean",
+ [("::TestMethods::basicBooleanWithSimpleArgs::arg1", "arg1", "Boolean", False, False),
+ ("::TestMethods::basicBooleanWithSimpleArgs::arg2", "arg2", "Byte", False, False),
+ ("::TestMethods::basicBooleanWithSimpleArgs::arg3", "arg3", "UnsignedLong", False, False)])])
+ checkMethod(methods[6], "::TestMethods::optionalArg",
+ "optionalArg",
+ [("Void",
+ [("::TestMethods::optionalArg::arg1", "arg1", "ByteOrNull", True, False),
+ ("::TestMethods::optionalArg::arg2", "arg2", "ByteSequence", True, False)])])
+ checkMethod(methods[7], "::TestMethods::variadicArg",
+ "variadicArg",
+ [("Void",
+ [("::TestMethods::variadicArg::arg1", "arg1", "ByteOrNull", True, True)])])
+ checkMethod(methods[8], "::TestMethods::crazyTypes",
+ "crazyTypes",
+ [("Void",
+ [("::TestMethods::crazyTypes::arg1", "arg1", "LongOrNullArraySequenceOrNull", False, False),
+ ("::TestMethods::crazyTypes::arg2", "arg2", "BooleanOrNullArrayArrayOrNull", False, False)])])
+ checkMethod(methods[9], "::TestMethods::getObject",
+ "getObject", [("Object", [])])
+ checkMethod(methods[10], "::TestMethods::setObject",
+ "setObject",
+ [("Void",
+ [("::TestMethods::setObject::arg1", "arg1", "Object", False, False)])])
+ checkMethod(methods[11], "::TestMethods::setAny",
+ "setAny",
+ [("Void",
+ [("::TestMethods::setAny::arg1", "arg1", "Any", False, False)])])
+ checkMethod(methods[12], "::TestMethods::doFloats",
+ "doFloats",
+ [("Float",
+ [("::TestMethods::doFloats::arg1", "arg1", "Float", False, False)])])
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A {
+ [GetterInfallible] void foo();
+ };
+ """)
+ results = parser.finish()
+ except Exception, x:
+ threw = True
+ harness.ok(threw, "Should not allow [GetterInfallible] on methods")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A {
+ [SetterInfallible] void foo();
+ };
+ """)
+ results = parser.finish()
+ except Exception, x:
+ threw = True
+ harness.ok(threw, "Should not allow [SetterInfallible] on methods")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_nullable_equivalency.py b/components/script/dom/bindings/codegen/parser/tests/test_nullable_equivalency.py
new file mode 100644
index 00000000000..3366b9fbbbd
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_nullable_equivalency.py
@@ -0,0 +1,126 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface TestNullableEquivalency1 {
+ attribute long a;
+ attribute long? b;
+ };
+
+ interface TestNullableEquivalency2 {
+ attribute ArrayBuffer a;
+ attribute ArrayBuffer? b;
+ };
+
+ /* Can't have dictionary-valued attributes, so can't test that here */
+
+ enum TestNullableEquivalency4Enum {
+ "Foo",
+ "Bar"
+ };
+
+ interface TestNullableEquivalency4 {
+ attribute TestNullableEquivalency4Enum a;
+ attribute TestNullableEquivalency4Enum? b;
+ };
+
+ interface TestNullableEquivalency5 {
+ attribute TestNullableEquivalency4 a;
+ attribute TestNullableEquivalency4? b;
+ };
+
+ interface TestNullableEquivalency6 {
+ attribute boolean a;
+ attribute boolean? b;
+ };
+
+ interface TestNullableEquivalency7 {
+ attribute DOMString a;
+ attribute DOMString? b;
+ };
+
+ /* Not implemented. */
+ /*interface TestNullableEquivalency8 {
+ attribute float a;
+ attribute float? b;
+ };*/
+
+ interface TestNullableEquivalency8 {
+ attribute double a;
+ attribute double? b;
+ };
+
+ interface TestNullableEquivalency9 {
+ attribute object a;
+ attribute object? b;
+ };
+
+ interface TestNullableEquivalency10 {
+ attribute double[] a;
+ attribute double[]? b;
+ };
+
+ interface TestNullableEquivalency11 {
+ attribute TestNullableEquivalency9[] a;
+ attribute TestNullableEquivalency9[]? b;
+ };
+ """)
+
+ for decl in parser.finish():
+ if decl.isInterface():
+ checkEquivalent(decl, harness)
+
+def checkEquivalent(iface, harness):
+ type1 = iface.members[0].type
+ type2 = iface.members[1].type
+
+ harness.check(type1.nullable(), False, 'attr1 should not be nullable')
+ harness.check(type2.nullable(), True, 'attr2 should be nullable')
+
+ # We don't know about type1, but type2, the nullable type, definitely
+ # shouldn't be builtin.
+ harness.check(type2.builtin, False, 'attr2 should not be builtin')
+
+ # Ensure that all attributes of type2 match those in type1, except for:
+ # - names on an ignore list,
+ # - names beginning with '_',
+ # - functions which throw when called with no args, and
+ # - class-level non-callables ("static variables").
+ #
+ # Yes, this is an ugly, fragile hack. But it finds bugs...
+ for attr in dir(type1):
+ if attr.startswith('_') or \
+ attr in ['nullable', 'builtin', 'filename', 'location',
+ 'inner', 'QName'] or \
+ (hasattr(type(type1), attr) and not callable(getattr(type1, attr))):
+ continue
+
+ a1 = getattr(type1, attr)
+
+ if callable(a1):
+ try:
+ v1 = a1()
+ except:
+ # Can't call a1 with no args, so skip this attriute.
+ continue
+
+ try:
+ a2 = getattr(type2, attr)
+ except:
+ harness.ok(False, 'Missing %s attribute on type %s in %s' % (attr, type2, iface))
+ continue
+
+ if not callable(a2):
+ harness.ok(False, "%s attribute on type %s in %s wasn't callable" % (attr, type2, iface))
+ continue
+
+ v2 = a2()
+ harness.check(v2, v1, '%s method return value' % attr)
+ else:
+ try:
+ a2 = getattr(type2, attr)
+ except:
+ harness.ok(False, 'Missing %s attribute on type %s in %s' % (attr, type2, iface))
+ continue
+
+ harness.check(a2, a1, '%s attribute should match' % attr)
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_nullable_void.py b/components/script/dom/bindings/codegen/parser/tests/test_nullable_void.py
new file mode 100644
index 00000000000..961ff825e9f
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_nullable_void.py
@@ -0,0 +1,14 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface NullableVoid {
+ void? foo();
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_optional_constraints.py b/components/script/dom/bindings/codegen/parser/tests/test_optional_constraints.py
new file mode 100644
index 00000000000..1dcdc7fb8a5
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_optional_constraints.py
@@ -0,0 +1,14 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface OptionalConstraints1 {
+ void foo(optional byte arg1, byte arg2);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_overload.py b/components/script/dom/bindings/codegen/parser/tests/test_overload.py
new file mode 100644
index 00000000000..59d9be54e53
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_overload.py
@@ -0,0 +1,47 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface TestOverloads {
+ void basic();
+ void basic(long arg1);
+ boolean abitharder(TestOverloads foo);
+ boolean abitharder(boolean foo);
+ void abitharder(ArrayBuffer? foo);
+ };
+ """)
+
+ results = parser.finish()
+
+ harness.ok(True, "TestOverloads interface parsed without error.")
+ harness.check(len(results), 1, "Should be one production.")
+ iface = results[0]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ harness.check(iface.identifier.QName(), "::TestOverloads", "Interface has the right QName")
+ harness.check(iface.identifier.name, "TestOverloads", "Interface has the right name")
+ harness.check(len(iface.members), 2, "Expect %s members" % 2)
+
+ member = iface.members[0]
+ harness.check(member.identifier.QName(), "::TestOverloads::basic", "Method has the right QName")
+ harness.check(member.identifier.name, "basic", "Method has the right name")
+ harness.check(member.hasOverloads(), True, "Method has overloads")
+
+ signatures = member.signatures()
+ harness.check(len(signatures), 2, "Method should have 2 signatures")
+
+ (retval, argumentSet) = signatures[0]
+
+ harness.check(str(retval), "Void", "Expect a void retval")
+ harness.check(len(argumentSet), 0, "Expect an empty argument set")
+
+ (retval, argumentSet) = signatures[1]
+ harness.check(str(retval), "Void", "Expect a void retval")
+ harness.check(len(argumentSet), 1, "Expect an argument set with one argument")
+
+ argument = argumentSet[0]
+ harness.ok(isinstance(argument, WebIDL.IDLArgument),
+ "Should be an IDLArgument")
+ harness.check(argument.identifier.QName(), "::TestOverloads::basic::arg1", "Argument has the right QName")
+ harness.check(argument.identifier.name, "arg1", "Argument has the right name")
+ harness.check(str(argument.type), "Long", "Argument has the right type")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_sanity.py b/components/script/dom/bindings/codegen/parser/tests/test_sanity.py
new file mode 100644
index 00000000000..d3184c00731
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_sanity.py
@@ -0,0 +1,7 @@
+def WebIDLTest(parser, harness):
+ parser.parse("")
+ parser.finish()
+ harness.ok(True, "Parsing nothing doesn't throw.")
+ parser.parse("interface Foo {};")
+ parser.finish()
+ harness.ok(True, "Parsing a silly interface doesn't throw.")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_special_method_signature_mismatch.py b/components/script/dom/bindings/codegen/parser/tests/test_special_method_signature_mismatch.py
new file mode 100644
index 00000000000..5ea1743d36a
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_special_method_signature_mismatch.py
@@ -0,0 +1,294 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch1 {
+ getter long long foo(long index);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch2 {
+ getter void foo(unsigned long index);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch3 {
+ getter boolean foo(unsigned long index, boolean extraArg);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch4 {
+ getter boolean foo(unsigned long... index);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch5 {
+ getter boolean foo(optional unsigned long index);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch6 {
+ getter boolean foo();
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch7 {
+ deleter long long foo(long index);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch9 {
+ deleter boolean foo(unsigned long index, boolean extraArg);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch10 {
+ deleter boolean foo(unsigned long... index);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch11 {
+ deleter boolean foo(optional unsigned long index);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch12 {
+ deleter boolean foo();
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch13 {
+ setter long long foo(long index, long long value);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch15 {
+ setter boolean foo(unsigned long index, boolean value, long long extraArg);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch16 {
+ setter boolean foo(unsigned long index, boolean... value);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch17 {
+ setter boolean foo(unsigned long index, optional boolean value);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch18 {
+ setter boolean foo();
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch20 {
+ creator long long foo(long index, long long value);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch22 {
+ creator boolean foo(unsigned long index, boolean value, long long extraArg);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch23 {
+ creator boolean foo(unsigned long index, boolean... value);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch24 {
+ creator boolean foo(unsigned long index, optional boolean value);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch25 {
+ creator boolean foo();
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_special_methods.py b/components/script/dom/bindings/codegen/parser/tests/test_special_methods.py
new file mode 100644
index 00000000000..695cfe4f250
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_special_methods.py
@@ -0,0 +1,73 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface SpecialMethods {
+ getter long long (unsigned long index);
+ setter long long (unsigned long index, long long value);
+ creator long long (unsigned long index, long long value);
+ deleter long long (unsigned long index);
+ getter boolean (DOMString name);
+ setter boolean (DOMString name, boolean value);
+ creator boolean (DOMString name, boolean value);
+ deleter boolean (DOMString name);
+ };
+
+ interface SpecialMethodsCombination {
+ getter deleter long long (unsigned long index);
+ setter creator long long (unsigned long index, long long value);
+ getter deleter boolean (DOMString name);
+ setter creator boolean (DOMString name, boolean value);
+ };
+ """)
+
+ results = parser.finish()
+
+ def checkMethod(method, QName, name,
+ static=False, getter=False, setter=False, creator=False,
+ deleter=False, legacycaller=False, stringifier=False):
+ harness.ok(isinstance(method, WebIDL.IDLMethod),
+ "Should be an IDLMethod")
+ harness.check(method.identifier.QName(), QName, "Method has the right QName")
+ harness.check(method.identifier.name, name, "Method has the right name")
+ harness.check(method.isStatic(), static, "Method has the correct static value")
+ harness.check(method.isGetter(), getter, "Method has the correct getter value")
+ harness.check(method.isSetter(), setter, "Method has the correct setter value")
+ harness.check(method.isCreator(), creator, "Method has the correct creator value")
+ harness.check(method.isDeleter(), deleter, "Method has the correct deleter value")
+ harness.check(method.isLegacycaller(), legacycaller, "Method has the correct legacycaller value")
+ harness.check(method.isStringifier(), stringifier, "Method has the correct stringifier value")
+
+ harness.check(len(results), 2, "Expect 2 interfaces")
+
+ iface = results[0]
+ harness.check(len(iface.members), 8, "Expect 8 members")
+
+ checkMethod(iface.members[0], "::SpecialMethods::__indexedgetter", "__indexedgetter",
+ getter=True)
+ checkMethod(iface.members[1], "::SpecialMethods::__indexedsetter", "__indexedsetter",
+ setter=True)
+ checkMethod(iface.members[2], "::SpecialMethods::__indexedcreator", "__indexedcreator",
+ creator=True)
+ checkMethod(iface.members[3], "::SpecialMethods::__indexeddeleter", "__indexeddeleter",
+ deleter=True)
+ checkMethod(iface.members[4], "::SpecialMethods::__namedgetter", "__namedgetter",
+ getter=True)
+ checkMethod(iface.members[5], "::SpecialMethods::__namedsetter", "__namedsetter",
+ setter=True)
+ checkMethod(iface.members[6], "::SpecialMethods::__namedcreator", "__namedcreator",
+ creator=True)
+ checkMethod(iface.members[7], "::SpecialMethods::__nameddeleter", "__nameddeleter",
+ deleter=True)
+
+ iface = results[1]
+ harness.check(len(iface.members), 4, "Expect 4 members")
+
+ checkMethod(iface.members[0], "::SpecialMethodsCombination::__indexedgetterdeleter",
+ "__indexedgetterdeleter", getter=True, deleter=True)
+ checkMethod(iface.members[1], "::SpecialMethodsCombination::__indexedsettercreator",
+ "__indexedsettercreator", setter=True, creator=True)
+ checkMethod(iface.members[2], "::SpecialMethodsCombination::__namedgetterdeleter",
+ "__namedgetterdeleter", getter=True, deleter=True)
+ checkMethod(iface.members[3], "::SpecialMethodsCombination::__namedsettercreator",
+ "__namedsettercreator", setter=True, creator=True)
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_special_methods_uniqueness.py b/components/script/dom/bindings/codegen/parser/tests/test_special_methods_uniqueness.py
new file mode 100644
index 00000000000..42e2c5bb71b
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_special_methods_uniqueness.py
@@ -0,0 +1,62 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodUniqueness1 {
+ getter deleter boolean (DOMString name);
+ getter boolean (DOMString name);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodUniqueness1 {
+ deleter boolean (DOMString name);
+ getter deleter boolean (DOMString name);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodUniqueness1 {
+ setter creator boolean (DOMString name);
+ creator boolean (DOMString name);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodUniqueness1 {
+ setter boolean (DOMString name);
+ creator setter boolean (DOMString name);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_treatNonCallableAsNull.py b/components/script/dom/bindings/codegen/parser/tests/test_treatNonCallableAsNull.py
new file mode 100644
index 00000000000..3d0e5ca479f
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_treatNonCallableAsNull.py
@@ -0,0 +1,56 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ callback Function = any(any... arguments);
+
+ interface TestTreatNonCallableAsNull1 {
+ [TreatNonCallableAsNull] attribute Function? onfoo;
+ attribute Function? onbar;
+ };
+ """)
+
+ results = parser.finish()
+
+ iface = results[1]
+ attr = iface.members[0]
+ harness.check(attr.type.treatNonCallableAsNull(), True, "Got the expected value")
+ attr = iface.members[1]
+ harness.check(attr.type.treatNonCallableAsNull(), False, "Got the expected value")
+
+ parser = parser.reset()
+
+ threw = False
+ try:
+ parser.parse("""
+ callback Function = any(any... arguments);
+
+ interface TestTreatNonCallableAsNull2 {
+ [TreatNonCallableAsNull] attribute Function onfoo;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset()
+
+ threw = False
+ try:
+ parser.parse("""
+ callback Function = any(any... arguments);
+
+ [TreatNonCallableAsNull]
+ interface TestTreatNonCallableAsNull3 {
+ attribute Function onfoo;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_typedef.py b/components/script/dom/bindings/codegen/parser/tests/test_typedef.py
new file mode 100644
index 00000000000..9d2f3b3c2ce
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_typedef.py
@@ -0,0 +1,76 @@
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ typedef long mylong;
+ typedef long? mynullablelong;
+ interface Foo {
+ const mylong X = 5;
+ const mynullablelong Y = 7;
+ const mynullablelong Z = null;
+ void foo(mylong arg);
+ };
+ """)
+
+ results = parser.finish()
+
+ harness.check(results[2].members[1].type.name, "Long",
+ "Should expand typedefs")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ typedef long? mynullablelong;
+ interface Foo {
+ void foo(mynullablelong? Y);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown on nullable inside nullable arg.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ typedef long? mynullablelong;
+ interface Foo {
+ const mynullablelong? X = 5;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown on nullable inside nullable const.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface Foo {
+ const mynullablelong? X = 5;
+ };
+ typedef long? mynullablelong;
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Should have thrown on nullable inside nullable const typedef "
+ "after interface.")
+
+ parser = parser.reset()
+ parser.parse("""
+ interface Foo {
+ const mylong X = 5;
+ };
+ typedef long mylong;
+ """)
+
+ results = parser.finish()
+
+ harness.check(results[0].members[0].type.name, "Long",
+ "Should expand typedefs that come before interface")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_union.py b/components/script/dom/bindings/codegen/parser/tests/test_union.py
new file mode 100644
index 00000000000..68c2bcade8c
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_union.py
@@ -0,0 +1,169 @@
+import WebIDL
+import itertools
+import string
+
+# We'd like to use itertools.chain but it's 2.6 or higher.
+def chain(*iterables):
+ # chain('ABC', 'DEF') --> A B C D E F
+ for it in iterables:
+ for element in it:
+ yield element
+
+# We'd like to use itertools.combinations but it's 2.6 or higher.
+def combinations(iterable, r):
+ # combinations('ABCD', 2) --> AB AC AD BC BD CD
+ # combinations(range(4), 3) --> 012 013 023 123
+ pool = tuple(iterable)
+ n = len(pool)
+ if r > n:
+ return
+ indices = range(r)
+ yield tuple(pool[i] for i in indices)
+ while True:
+ for i in reversed(range(r)):
+ if indices[i] != i + n - r:
+ break
+ else:
+ return
+ indices[i] += 1
+ for j in range(i+1, r):
+ indices[j] = indices[j-1] + 1
+ yield tuple(pool[i] for i in indices)
+
+# We'd like to use itertools.combinations_with_replacement but it's 2.7 or
+# higher.
+def combinations_with_replacement(iterable, r):
+ # combinations_with_replacement('ABC', 2) --> AA AB AC BB BC CC
+ pool = tuple(iterable)
+ n = len(pool)
+ if not n and r:
+ return
+ indices = [0] * r
+ yield tuple(pool[i] for i in indices)
+ while True:
+ for i in reversed(range(r)):
+ if indices[i] != n - 1:
+ break
+ else:
+ return
+ indices[i:] = [indices[i] + 1] * (r - i)
+ yield tuple(pool[i] for i in indices)
+
+def WebIDLTest(parser, harness):
+ types = ["float",
+ "double",
+ "short",
+ "unsigned short",
+ "long",
+ "unsigned long",
+ "long long",
+ "unsigned long long",
+ "boolean",
+ "byte",
+ "octet",
+ "DOMString",
+ #"sequence<float>",
+ "object",
+ "ArrayBuffer",
+ #"Date",
+ "TestInterface1",
+ "TestInterface2"]
+
+ testPre = """
+ interface TestInterface1 {
+ };
+ interface TestInterface2 {
+ };
+ """
+
+ interface = testPre + """
+ interface PrepareForTest {
+ """
+ for (i, type) in enumerate(types):
+ interface += string.Template("""
+ readonly attribute ${type} attr${i};
+ """).substitute(i=i, type=type)
+ interface += """
+ };
+ """
+
+ parser.parse(interface)
+ results = parser.finish()
+
+ iface = results[2]
+
+ parser = parser.reset()
+
+ def typesAreDistinguishable(t):
+ return all(u[0].isDistinguishableFrom(u[1]) for u in combinations(t, 2))
+ def typesAreNotDistinguishable(t):
+ return any(not u[0].isDistinguishableFrom(u[1]) for u in combinations(t, 2))
+ def unionTypeName(t):
+ if len(t) > 2:
+ t[0:2] = [unionTypeName(t[0:2])]
+ return "(" + " or ".join(t) + ")"
+
+ # typeCombinations is an iterable of tuples containing the name of the type
+ # as a string and the parsed IDL type.
+ def unionTypes(typeCombinations, predicate):
+ for c in typeCombinations:
+ if predicate(t[1] for t in c):
+ yield unionTypeName([t[0] for t in c])
+
+ # We limit invalid union types with a union member type to the subset of 3
+ # types with one invalid combination.
+ # typeCombinations is an iterable of tuples containing the name of the type
+ # as a string and the parsed IDL type.
+ def invalidUnionWithUnion(typeCombinations):
+ for c in typeCombinations:
+ if (typesAreNotDistinguishable((c[0][1], c[1][1])) and
+ typesAreDistinguishable((c[1][1], c[2][1])) and
+ typesAreDistinguishable((c[0][1], c[2][1]))):
+ yield unionTypeName([t[0] for t in c])
+
+ # Create a list of tuples containing the name of the type as a string and
+ # the parsed IDL type.
+ types = zip(types, (a.type for a in iface.members))
+
+ validUnionTypes = chain(unionTypes(combinations(types, 2), typesAreDistinguishable),
+ unionTypes(combinations(types, 3), typesAreDistinguishable))
+ invalidUnionTypes = chain(unionTypes(combinations_with_replacement(types, 2), typesAreNotDistinguishable),
+ invalidUnionWithUnion(combinations(types, 3)))
+ interface = testPre + """
+ interface TestUnion {
+ """
+ for (i, type) in enumerate(validUnionTypes):
+ interface += string.Template("""
+ void method${i}(${type} arg);
+ ${type} returnMethod${i}();
+ attribute ${type} attr${i};
+ void arrayMethod${i}(${type}[] arg);
+ ${type}[] arrayReturnMethod${i}();
+ attribute ${type}[] arrayAttr${i};
+ void optionalMethod${i}(${type}? arg);
+ """).substitute(i=i, type=type)
+ interface += """
+ };
+ """
+ parser.parse(interface)
+ results = parser.finish()
+
+ parser = parser.reset()
+
+ for invalid in invalidUnionTypes:
+ interface = testPre + string.Template("""
+ interface TestUnion {
+ void method(${type} arg);
+ };
+ """).substitute(type=invalid)
+
+ threw = False
+ try:
+ parser.parse(interface)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset()
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_union_any.py b/components/script/dom/bindings/codegen/parser/tests/test_union_any.py
new file mode 100644
index 00000000000..e34cadab470
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_union_any.py
@@ -0,0 +1,14 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface AnyNotInUnion {
+ void foo((any or DOMString) arg);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_union_nullable.py b/components/script/dom/bindings/codegen/parser/tests/test_union_nullable.py
new file mode 100644
index 00000000000..08430a94a2e
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_union_nullable.py
@@ -0,0 +1,53 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface OneNullableInUnion {
+ void foo((object? or DOMString?) arg);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Two nullable member types of a union should have thrown.")
+
+ parser.reset()
+ threw = False
+
+ try:
+ parser.parse("""
+ interface NullableInNullableUnion {
+ void foo((object? or DOMString)? arg);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "A nullable union type with a nullable member type should have "
+ "thrown.")
+
+ parser.reset()
+ threw = False
+
+ try:
+ parser.parse("""
+ interface NullableInUnionNullableUnionHelper {
+ };
+ interface NullableInUnionNullableUnion {
+ void foo(((object? or DOMString) or NullableInUnionNullableUnionHelper)? arg);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "A nullable union type with a nullable member type should have "
+ "thrown.")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_variadic_callback.py b/components/script/dom/bindings/codegen/parser/tests/test_variadic_callback.py
new file mode 100644
index 00000000000..d9a78db2043
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_variadic_callback.py
@@ -0,0 +1,10 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ callback TestVariadicCallback = any(any... arguments);
+ """)
+
+ results = parser.finish()
+
+ harness.ok(True, "TestVariadicCallback callback parsed without error.")
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_variadic_constraints.py b/components/script/dom/bindings/codegen/parser/tests/test_variadic_constraints.py
new file mode 100644
index 00000000000..9cba22c5842
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_variadic_constraints.py
@@ -0,0 +1,39 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ results = parser.parse("""
+ interface VariadicConstraints1 {
+ void foo(byte... arg1, byte arg2);
+ };
+ """)
+
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ results = parser.parse("""
+ interface VariadicConstraints2 {
+ void foo(byte... arg1, optional byte arg2);
+ };
+ """)
+
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ results = parser.parse("""
+ interface VariadicConstraints3 {
+ void foo(optional byte... arg1);
+ };
+ """)
+
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/components/script/dom/bindings/codegen/parser/update.sh b/components/script/dom/bindings/codegen/parser/update.sh
new file mode 100755
index 00000000000..5dd513812e1
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/update.sh
@@ -0,0 +1,3 @@
+wget https://mxr.mozilla.org/mozilla-central/source/dom/bindings/parser/WebIDL.py?raw=1 -O WebIDL.py
+patch < external.patch
+patch < module.patch
diff --git a/components/script/dom/bindings/codegen/ply/COPYING b/components/script/dom/bindings/codegen/ply/COPYING
new file mode 100644
index 00000000000..3b107de4508
--- /dev/null
+++ b/components/script/dom/bindings/codegen/ply/COPYING
@@ -0,0 +1,28 @@
+Copyright (C) 2001-2009,
+David M. Beazley (Dabeaz LLC)
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+* Neither the name of the David Beazley or Dabeaz LLC may be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/components/script/dom/bindings/codegen/ply/README b/components/script/dom/bindings/codegen/ply/README
new file mode 100644
index 00000000000..2459c490197
--- /dev/null
+++ b/components/script/dom/bindings/codegen/ply/README
@@ -0,0 +1,9 @@
+David Beazley's PLY (Python Lex-Yacc)
+http://www.dabeaz.com/ply/
+
+Licensed under BSD.
+
+This directory contains just the code and license from PLY version 3.3;
+the full distribution (see the URL) also contains examples, tests,
+documentation, and a longer README.
+
diff --git a/components/script/dom/bindings/codegen/ply/ply/__init__.py b/components/script/dom/bindings/codegen/ply/ply/__init__.py
new file mode 100644
index 00000000000..853a985542b
--- /dev/null
+++ b/components/script/dom/bindings/codegen/ply/ply/__init__.py
@@ -0,0 +1,4 @@
+# PLY package
+# Author: David Beazley (dave@dabeaz.com)
+
+__all__ = ['lex','yacc']
diff --git a/components/script/dom/bindings/codegen/ply/ply/lex.py b/components/script/dom/bindings/codegen/ply/ply/lex.py
new file mode 100644
index 00000000000..267ec100fc2
--- /dev/null
+++ b/components/script/dom/bindings/codegen/ply/ply/lex.py
@@ -0,0 +1,1058 @@
+# -----------------------------------------------------------------------------
+# ply: lex.py
+#
+# Copyright (C) 2001-2009,
+# David M. Beazley (Dabeaz LLC)
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# * Neither the name of the David Beazley or Dabeaz LLC may be used to
+# endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+# -----------------------------------------------------------------------------
+
+__version__ = "3.3"
+__tabversion__ = "3.2" # Version of table file used
+
+import re, sys, types, copy, os
+
+# This tuple contains known string types
+try:
+ # Python 2.6
+ StringTypes = (types.StringType, types.UnicodeType)
+except AttributeError:
+ # Python 3.0
+ StringTypes = (str, bytes)
+
+# Extract the code attribute of a function. Different implementations
+# are for Python 2/3 compatibility.
+
+if sys.version_info[0] < 3:
+ def func_code(f):
+ return f.func_code
+else:
+ def func_code(f):
+ return f.__code__
+
+# This regular expression is used to match valid token names
+_is_identifier = re.compile(r'^[a-zA-Z0-9_]+$')
+
+# Exception thrown when invalid token encountered and no default error
+# handler is defined.
+
+class LexError(Exception):
+ def __init__(self,message,s):
+ self.args = (message,)
+ self.text = s
+
+# Token class. This class is used to represent the tokens produced.
+class LexToken(object):
+ def __str__(self):
+ return "LexToken(%s,%r,%d,%d)" % (self.type,self.value,self.lineno,self.lexpos)
+ def __repr__(self):
+ return str(self)
+
+# This object is a stand-in for a logging object created by the
+# logging module.
+
+class PlyLogger(object):
+ def __init__(self,f):
+ self.f = f
+ def critical(self,msg,*args,**kwargs):
+ self.f.write((msg % args) + "\n")
+
+ def warning(self,msg,*args,**kwargs):
+ self.f.write("WARNING: "+ (msg % args) + "\n")
+
+ def error(self,msg,*args,**kwargs):
+ self.f.write("ERROR: " + (msg % args) + "\n")
+
+ info = critical
+ debug = critical
+
+# Null logger is used when no output is generated. Does nothing.
+class NullLogger(object):
+ def __getattribute__(self,name):
+ return self
+ def __call__(self,*args,**kwargs):
+ return self
+
+# -----------------------------------------------------------------------------
+# === Lexing Engine ===
+#
+# The following Lexer class implements the lexer runtime. There are only
+# a few public methods and attributes:
+#
+# input() - Store a new string in the lexer
+# token() - Get the next token
+# clone() - Clone the lexer
+#
+# lineno - Current line number
+# lexpos - Current position in the input string
+# -----------------------------------------------------------------------------
+
+class Lexer:
+ def __init__(self):
+ self.lexre = None # Master regular expression. This is a list of
+ # tuples (re,findex) where re is a compiled
+ # regular expression and findex is a list
+ # mapping regex group numbers to rules
+ self.lexretext = None # Current regular expression strings
+ self.lexstatere = {} # Dictionary mapping lexer states to master regexs
+ self.lexstateretext = {} # Dictionary mapping lexer states to regex strings
+ self.lexstaterenames = {} # Dictionary mapping lexer states to symbol names
+ self.lexstate = "INITIAL" # Current lexer state
+ self.lexstatestack = [] # Stack of lexer states
+ self.lexstateinfo = None # State information
+ self.lexstateignore = {} # Dictionary of ignored characters for each state
+ self.lexstateerrorf = {} # Dictionary of error functions for each state
+ self.lexreflags = 0 # Optional re compile flags
+ self.lexdata = None # Actual input data (as a string)
+ self.lexpos = 0 # Current position in input text
+ self.lexlen = 0 # Length of the input text
+ self.lexerrorf = None # Error rule (if any)
+ self.lextokens = None # List of valid tokens
+ self.lexignore = "" # Ignored characters
+ self.lexliterals = "" # Literal characters that can be passed through
+ self.lexmodule = None # Module
+ self.lineno = 1 # Current line number
+ self.lexoptimize = 0 # Optimized mode
+
+ def clone(self,object=None):
+ c = copy.copy(self)
+
+ # If the object parameter has been supplied, it means we are attaching the
+ # lexer to a new object. In this case, we have to rebind all methods in
+ # the lexstatere and lexstateerrorf tables.
+
+ if object:
+ newtab = { }
+ for key, ritem in self.lexstatere.items():
+ newre = []
+ for cre, findex in ritem:
+ newfindex = []
+ for f in findex:
+ if not f or not f[0]:
+ newfindex.append(f)
+ continue
+ newfindex.append((getattr(object,f[0].__name__),f[1]))
+ newre.append((cre,newfindex))
+ newtab[key] = newre
+ c.lexstatere = newtab
+ c.lexstateerrorf = { }
+ for key, ef in self.lexstateerrorf.items():
+ c.lexstateerrorf[key] = getattr(object,ef.__name__)
+ c.lexmodule = object
+ return c
+
+ # ------------------------------------------------------------
+ # writetab() - Write lexer information to a table file
+ # ------------------------------------------------------------
+ def writetab(self,tabfile,outputdir=""):
+ if isinstance(tabfile,types.ModuleType):
+ return
+ basetabfilename = tabfile.split(".")[-1]
+ filename = os.path.join(outputdir,basetabfilename)+".py"
+ tf = open(filename,"w")
+ tf.write("# %s.py. This file automatically created by PLY (version %s). Don't edit!\n" % (tabfile,__version__))
+ tf.write("_tabversion = %s\n" % repr(__version__))
+ tf.write("_lextokens = %s\n" % repr(self.lextokens))
+ tf.write("_lexreflags = %s\n" % repr(self.lexreflags))
+ tf.write("_lexliterals = %s\n" % repr(self.lexliterals))
+ tf.write("_lexstateinfo = %s\n" % repr(self.lexstateinfo))
+
+ tabre = { }
+ # Collect all functions in the initial state
+ initial = self.lexstatere["INITIAL"]
+ initialfuncs = []
+ for part in initial:
+ for f in part[1]:
+ if f and f[0]:
+ initialfuncs.append(f)
+
+ for key, lre in self.lexstatere.items():
+ titem = []
+ for i in range(len(lre)):
+ titem.append((self.lexstateretext[key][i],_funcs_to_names(lre[i][1],self.lexstaterenames[key][i])))
+ tabre[key] = titem
+
+ tf.write("_lexstatere = %s\n" % repr(tabre))
+ tf.write("_lexstateignore = %s\n" % repr(self.lexstateignore))
+
+ taberr = { }
+ for key, ef in self.lexstateerrorf.items():
+ if ef:
+ taberr[key] = ef.__name__
+ else:
+ taberr[key] = None
+ tf.write("_lexstateerrorf = %s\n" % repr(taberr))
+ tf.close()
+
+ # ------------------------------------------------------------
+ # readtab() - Read lexer information from a tab file
+ # ------------------------------------------------------------
+ def readtab(self,tabfile,fdict):
+ if isinstance(tabfile,types.ModuleType):
+ lextab = tabfile
+ else:
+ if sys.version_info[0] < 3:
+ exec("import %s as lextab" % tabfile)
+ else:
+ env = { }
+ exec("import %s as lextab" % tabfile, env,env)
+ lextab = env['lextab']
+
+ if getattr(lextab,"_tabversion","0.0") != __version__:
+ raise ImportError("Inconsistent PLY version")
+
+ self.lextokens = lextab._lextokens
+ self.lexreflags = lextab._lexreflags
+ self.lexliterals = lextab._lexliterals
+ self.lexstateinfo = lextab._lexstateinfo
+ self.lexstateignore = lextab._lexstateignore
+ self.lexstatere = { }
+ self.lexstateretext = { }
+ for key,lre in lextab._lexstatere.items():
+ titem = []
+ txtitem = []
+ for i in range(len(lre)):
+ titem.append((re.compile(lre[i][0],lextab._lexreflags | re.VERBOSE),_names_to_funcs(lre[i][1],fdict)))
+ txtitem.append(lre[i][0])
+ self.lexstatere[key] = titem
+ self.lexstateretext[key] = txtitem
+ self.lexstateerrorf = { }
+ for key,ef in lextab._lexstateerrorf.items():
+ self.lexstateerrorf[key] = fdict[ef]
+ self.begin('INITIAL')
+
+ # ------------------------------------------------------------
+ # input() - Push a new string into the lexer
+ # ------------------------------------------------------------
+ def input(self,s):
+ # Pull off the first character to see if s looks like a string
+ c = s[:1]
+ if not isinstance(c,StringTypes):
+ raise ValueError("Expected a string")
+ self.lexdata = s
+ self.lexpos = 0
+ self.lexlen = len(s)
+
+ # ------------------------------------------------------------
+ # begin() - Changes the lexing state
+ # ------------------------------------------------------------
+ def begin(self,state):
+ if not state in self.lexstatere:
+ raise ValueError("Undefined state")
+ self.lexre = self.lexstatere[state]
+ self.lexretext = self.lexstateretext[state]
+ self.lexignore = self.lexstateignore.get(state,"")
+ self.lexerrorf = self.lexstateerrorf.get(state,None)
+ self.lexstate = state
+
+ # ------------------------------------------------------------
+ # push_state() - Changes the lexing state and saves old on stack
+ # ------------------------------------------------------------
+ def push_state(self,state):
+ self.lexstatestack.append(self.lexstate)
+ self.begin(state)
+
+ # ------------------------------------------------------------
+ # pop_state() - Restores the previous state
+ # ------------------------------------------------------------
+ def pop_state(self):
+ self.begin(self.lexstatestack.pop())
+
+ # ------------------------------------------------------------
+ # current_state() - Returns the current lexing state
+ # ------------------------------------------------------------
+ def current_state(self):
+ return self.lexstate
+
+ # ------------------------------------------------------------
+ # skip() - Skip ahead n characters
+ # ------------------------------------------------------------
+ def skip(self,n):
+ self.lexpos += n
+
+ # ------------------------------------------------------------
+ # opttoken() - Return the next token from the Lexer
+ #
+ # Note: This function has been carefully implemented to be as fast
+ # as possible. Don't make changes unless you really know what
+ # you are doing
+ # ------------------------------------------------------------
+ def token(self):
+ # Make local copies of frequently referenced attributes
+ lexpos = self.lexpos
+ lexlen = self.lexlen
+ lexignore = self.lexignore
+ lexdata = self.lexdata
+
+ while lexpos < lexlen:
+ # This code provides some short-circuit code for whitespace, tabs, and other ignored characters
+ if lexdata[lexpos] in lexignore:
+ lexpos += 1
+ continue
+
+ # Look for a regular expression match
+ for lexre,lexindexfunc in self.lexre:
+ m = lexre.match(lexdata,lexpos)
+ if not m: continue
+
+ # Create a token for return
+ tok = LexToken()
+ tok.value = m.group()
+ tok.lineno = self.lineno
+ tok.lexpos = lexpos
+
+ i = m.lastindex
+ func,tok.type = lexindexfunc[i]
+
+ if not func:
+ # If no token type was set, it's an ignored token
+ if tok.type:
+ self.lexpos = m.end()
+ return tok
+ else:
+ lexpos = m.end()
+ break
+
+ lexpos = m.end()
+
+ # If token is processed by a function, call it
+
+ tok.lexer = self # Set additional attributes useful in token rules
+ self.lexmatch = m
+ self.lexpos = lexpos
+
+ newtok = func(tok)
+
+ # Every function must return a token, if nothing, we just move to next token
+ if not newtok:
+ lexpos = self.lexpos # This is here in case user has updated lexpos.
+ lexignore = self.lexignore # This is here in case there was a state change
+ break
+
+ # Verify type of the token. If not in the token map, raise an error
+ if not self.lexoptimize:
+ if not newtok.type in self.lextokens:
+ raise LexError("%s:%d: Rule '%s' returned an unknown token type '%s'" % (
+ func_code(func).co_filename, func_code(func).co_firstlineno,
+ func.__name__, newtok.type),lexdata[lexpos:])
+
+ return newtok
+ else:
+ # No match, see if in literals
+ if lexdata[lexpos] in self.lexliterals:
+ tok = LexToken()
+ tok.value = lexdata[lexpos]
+ tok.lineno = self.lineno
+ tok.type = tok.value
+ tok.lexpos = lexpos
+ self.lexpos = lexpos + 1
+ return tok
+
+ # No match. Call t_error() if defined.
+ if self.lexerrorf:
+ tok = LexToken()
+ tok.value = self.lexdata[lexpos:]
+ tok.lineno = self.lineno
+ tok.type = "error"
+ tok.lexer = self
+ tok.lexpos = lexpos
+ self.lexpos = lexpos
+ newtok = self.lexerrorf(tok)
+ if lexpos == self.lexpos:
+ # Error method didn't change text position at all. This is an error.
+ raise LexError("Scanning error. Illegal character '%s'" % (lexdata[lexpos]), lexdata[lexpos:])
+ lexpos = self.lexpos
+ if not newtok: continue
+ return newtok
+
+ self.lexpos = lexpos
+ raise LexError("Illegal character '%s' at index %d" % (lexdata[lexpos],lexpos), lexdata[lexpos:])
+
+ self.lexpos = lexpos + 1
+ if self.lexdata is None:
+ raise RuntimeError("No input string given with input()")
+ return None
+
+ # Iterator interface
+ def __iter__(self):
+ return self
+
+ def next(self):
+ t = self.token()
+ if t is None:
+ raise StopIteration
+ return t
+
+ __next__ = next
+
+# -----------------------------------------------------------------------------
+# ==== Lex Builder ===
+#
+# The functions and classes below are used to collect lexing information
+# and build a Lexer object from it.
+# -----------------------------------------------------------------------------
+
+# -----------------------------------------------------------------------------
+# get_caller_module_dict()
+#
+# This function returns a dictionary containing all of the symbols defined within
+# a caller further down the call stack. This is used to get the environment
+# associated with the yacc() call if none was provided.
+# -----------------------------------------------------------------------------
+
+def get_caller_module_dict(levels):
+ try:
+ raise RuntimeError
+ except RuntimeError:
+ e,b,t = sys.exc_info()
+ f = t.tb_frame
+ while levels > 0:
+ f = f.f_back
+ levels -= 1
+ ldict = f.f_globals.copy()
+ if f.f_globals != f.f_locals:
+ ldict.update(f.f_locals)
+
+ return ldict
+
+# -----------------------------------------------------------------------------
+# _funcs_to_names()
+#
+# Given a list of regular expression functions, this converts it to a list
+# suitable for output to a table file
+# -----------------------------------------------------------------------------
+
+def _funcs_to_names(funclist,namelist):
+ result = []
+ for f,name in zip(funclist,namelist):
+ if f and f[0]:
+ result.append((name, f[1]))
+ else:
+ result.append(f)
+ return result
+
+# -----------------------------------------------------------------------------
+# _names_to_funcs()
+#
+# Given a list of regular expression function names, this converts it back to
+# functions.
+# -----------------------------------------------------------------------------
+
+def _names_to_funcs(namelist,fdict):
+ result = []
+ for n in namelist:
+ if n and n[0]:
+ result.append((fdict[n[0]],n[1]))
+ else:
+ result.append(n)
+ return result
+
+# -----------------------------------------------------------------------------
+# _form_master_re()
+#
+# This function takes a list of all of the regex components and attempts to
+# form the master regular expression. Given limitations in the Python re
+# module, it may be necessary to break the master regex into separate expressions.
+# -----------------------------------------------------------------------------
+
+def _form_master_re(relist,reflags,ldict,toknames):
+ if not relist: return []
+ regex = "|".join(relist)
+ try:
+ lexre = re.compile(regex,re.VERBOSE | reflags)
+
+ # Build the index to function map for the matching engine
+ lexindexfunc = [ None ] * (max(lexre.groupindex.values())+1)
+ lexindexnames = lexindexfunc[:]
+
+ for f,i in lexre.groupindex.items():
+ handle = ldict.get(f,None)
+ if type(handle) in (types.FunctionType, types.MethodType):
+ lexindexfunc[i] = (handle,toknames[f])
+ lexindexnames[i] = f
+ elif handle is not None:
+ lexindexnames[i] = f
+ if f.find("ignore_") > 0:
+ lexindexfunc[i] = (None,None)
+ else:
+ lexindexfunc[i] = (None, toknames[f])
+
+ return [(lexre,lexindexfunc)],[regex],[lexindexnames]
+ except Exception:
+ m = int(len(relist)/2)
+ if m == 0: m = 1
+ llist, lre, lnames = _form_master_re(relist[:m],reflags,ldict,toknames)
+ rlist, rre, rnames = _form_master_re(relist[m:],reflags,ldict,toknames)
+ return llist+rlist, lre+rre, lnames+rnames
+
+# -----------------------------------------------------------------------------
+# def _statetoken(s,names)
+#
+# Given a declaration name s of the form "t_" and a dictionary whose keys are
+# state names, this function returns a tuple (states,tokenname) where states
+# is a tuple of state names and tokenname is the name of the token. For example,
+# calling this with s = "t_foo_bar_SPAM" might return (('foo','bar'),'SPAM')
+# -----------------------------------------------------------------------------
+
+def _statetoken(s,names):
+ nonstate = 1
+ parts = s.split("_")
+ for i in range(1,len(parts)):
+ if not parts[i] in names and parts[i] != 'ANY': break
+ if i > 1:
+ states = tuple(parts[1:i])
+ else:
+ states = ('INITIAL',)
+
+ if 'ANY' in states:
+ states = tuple(names)
+
+ tokenname = "_".join(parts[i:])
+ return (states,tokenname)
+
+
+# -----------------------------------------------------------------------------
+# LexerReflect()
+#
+# This class represents information needed to build a lexer as extracted from a
+# user's input file.
+# -----------------------------------------------------------------------------
+class LexerReflect(object):
+ def __init__(self,ldict,log=None,reflags=0):
+ self.ldict = ldict
+ self.error_func = None
+ self.tokens = []
+ self.reflags = reflags
+ self.stateinfo = { 'INITIAL' : 'inclusive'}
+ self.files = {}
+ self.error = 0
+
+ if log is None:
+ self.log = PlyLogger(sys.stderr)
+ else:
+ self.log = log
+
+ # Get all of the basic information
+ def get_all(self):
+ self.get_tokens()
+ self.get_literals()
+ self.get_states()
+ self.get_rules()
+
+ # Validate all of the information
+ def validate_all(self):
+ self.validate_tokens()
+ self.validate_literals()
+ self.validate_rules()
+ return self.error
+
+ # Get the tokens map
+ def get_tokens(self):
+ tokens = self.ldict.get("tokens",None)
+ if not tokens:
+ self.log.error("No token list is defined")
+ self.error = 1
+ return
+
+ if not isinstance(tokens,(list, tuple)):
+ self.log.error("tokens must be a list or tuple")
+ self.error = 1
+ return
+
+ if not tokens:
+ self.log.error("tokens is empty")
+ self.error = 1
+ return
+
+ self.tokens = tokens
+
+ # Validate the tokens
+ def validate_tokens(self):
+ terminals = {}
+ for n in self.tokens:
+ if not _is_identifier.match(n):
+ self.log.error("Bad token name '%s'",n)
+ self.error = 1
+ if n in terminals:
+ self.log.warning("Token '%s' multiply defined", n)
+ terminals[n] = 1
+
+ # Get the literals specifier
+ def get_literals(self):
+ self.literals = self.ldict.get("literals","")
+
+ # Validate literals
+ def validate_literals(self):
+ try:
+ for c in self.literals:
+ if not isinstance(c,StringTypes) or len(c) > 1:
+ self.log.error("Invalid literal %s. Must be a single character", repr(c))
+ self.error = 1
+ continue
+
+ except TypeError:
+ self.log.error("Invalid literals specification. literals must be a sequence of characters")
+ self.error = 1
+
+ def get_states(self):
+ self.states = self.ldict.get("states",None)
+ # Build statemap
+ if self.states:
+ if not isinstance(self.states,(tuple,list)):
+ self.log.error("states must be defined as a tuple or list")
+ self.error = 1
+ else:
+ for s in self.states:
+ if not isinstance(s,tuple) or len(s) != 2:
+ self.log.error("Invalid state specifier %s. Must be a tuple (statename,'exclusive|inclusive')",repr(s))
+ self.error = 1
+ continue
+ name, statetype = s
+ if not isinstance(name,StringTypes):
+ self.log.error("State name %s must be a string", repr(name))
+ self.error = 1
+ continue
+ if not (statetype == 'inclusive' or statetype == 'exclusive'):
+ self.log.error("State type for state %s must be 'inclusive' or 'exclusive'",name)
+ self.error = 1
+ continue
+ if name in self.stateinfo:
+ self.log.error("State '%s' already defined",name)
+ self.error = 1
+ continue
+ self.stateinfo[name] = statetype
+
+ # Get all of the symbols with a t_ prefix and sort them into various
+ # categories (functions, strings, error functions, and ignore characters)
+
+ def get_rules(self):
+ tsymbols = [f for f in self.ldict if f[:2] == 't_' ]
+
+ # Now build up a list of functions and a list of strings
+
+ self.toknames = { } # Mapping of symbols to token names
+ self.funcsym = { } # Symbols defined as functions
+ self.strsym = { } # Symbols defined as strings
+ self.ignore = { } # Ignore strings by state
+ self.errorf = { } # Error functions by state
+
+ for s in self.stateinfo:
+ self.funcsym[s] = []
+ self.strsym[s] = []
+
+ if len(tsymbols) == 0:
+ self.log.error("No rules of the form t_rulename are defined")
+ self.error = 1
+ return
+
+ for f in tsymbols:
+ t = self.ldict[f]
+ states, tokname = _statetoken(f,self.stateinfo)
+ self.toknames[f] = tokname
+
+ if hasattr(t,"__call__"):
+ if tokname == 'error':
+ for s in states:
+ self.errorf[s] = t
+ elif tokname == 'ignore':
+ line = func_code(t).co_firstlineno
+ file = func_code(t).co_filename
+ self.log.error("%s:%d: Rule '%s' must be defined as a string",file,line,t.__name__)
+ self.error = 1
+ else:
+ for s in states:
+ self.funcsym[s].append((f,t))
+ elif isinstance(t, StringTypes):
+ if tokname == 'ignore':
+ for s in states:
+ self.ignore[s] = t
+ if "\\" in t:
+ self.log.warning("%s contains a literal backslash '\\'",f)
+
+ elif tokname == 'error':
+ self.log.error("Rule '%s' must be defined as a function", f)
+ self.error = 1
+ else:
+ for s in states:
+ self.strsym[s].append((f,t))
+ else:
+ self.log.error("%s not defined as a function or string", f)
+ self.error = 1
+
+ # Sort the functions by line number
+ for f in self.funcsym.values():
+ if sys.version_info[0] < 3:
+ f.sort(lambda x,y: cmp(func_code(x[1]).co_firstlineno,func_code(y[1]).co_firstlineno))
+ else:
+ # Python 3.0
+ f.sort(key=lambda x: func_code(x[1]).co_firstlineno)
+
+ # Sort the strings by regular expression length
+ for s in self.strsym.values():
+ if sys.version_info[0] < 3:
+ s.sort(lambda x,y: (len(x[1]) < len(y[1])) - (len(x[1]) > len(y[1])))
+ else:
+ # Python 3.0
+ s.sort(key=lambda x: len(x[1]),reverse=True)
+
+ # Validate all of the t_rules collected
+ def validate_rules(self):
+ for state in self.stateinfo:
+ # Validate all rules defined by functions
+
+
+
+ for fname, f in self.funcsym[state]:
+ line = func_code(f).co_firstlineno
+ file = func_code(f).co_filename
+ self.files[file] = 1
+
+ tokname = self.toknames[fname]
+ if isinstance(f, types.MethodType):
+ reqargs = 2
+ else:
+ reqargs = 1
+ nargs = func_code(f).co_argcount
+ if nargs > reqargs:
+ self.log.error("%s:%d: Rule '%s' has too many arguments",file,line,f.__name__)
+ self.error = 1
+ continue
+
+ if nargs < reqargs:
+ self.log.error("%s:%d: Rule '%s' requires an argument", file,line,f.__name__)
+ self.error = 1
+ continue
+
+ if not f.__doc__:
+ self.log.error("%s:%d: No regular expression defined for rule '%s'",file,line,f.__name__)
+ self.error = 1
+ continue
+
+ try:
+ c = re.compile("(?P<%s>%s)" % (fname,f.__doc__), re.VERBOSE | self.reflags)
+ if c.match(""):
+ self.log.error("%s:%d: Regular expression for rule '%s' matches empty string", file,line,f.__name__)
+ self.error = 1
+ except re.error:
+ _etype, e, _etrace = sys.exc_info()
+ self.log.error("%s:%d: Invalid regular expression for rule '%s'. %s", file,line,f.__name__,e)
+ if '#' in f.__doc__:
+ self.log.error("%s:%d. Make sure '#' in rule '%s' is escaped with '\\#'",file,line, f.__name__)
+ self.error = 1
+
+ # Validate all rules defined by strings
+ for name,r in self.strsym[state]:
+ tokname = self.toknames[name]
+ if tokname == 'error':
+ self.log.error("Rule '%s' must be defined as a function", name)
+ self.error = 1
+ continue
+
+ if not tokname in self.tokens and tokname.find("ignore_") < 0:
+ self.log.error("Rule '%s' defined for an unspecified token %s",name,tokname)
+ self.error = 1
+ continue
+
+ try:
+ c = re.compile("(?P<%s>%s)" % (name,r),re.VERBOSE | self.reflags)
+ if (c.match("")):
+ self.log.error("Regular expression for rule '%s' matches empty string",name)
+ self.error = 1
+ except re.error:
+ _etype, e, _etrace = sys.exc_info()
+ self.log.error("Invalid regular expression for rule '%s'. %s",name,e)
+ if '#' in r:
+ self.log.error("Make sure '#' in rule '%s' is escaped with '\\#'",name)
+ self.error = 1
+
+ if not self.funcsym[state] and not self.strsym[state]:
+ self.log.error("No rules defined for state '%s'",state)
+ self.error = 1
+
+ # Validate the error function
+ efunc = self.errorf.get(state,None)
+ if efunc:
+ f = efunc
+ line = func_code(f).co_firstlineno
+ file = func_code(f).co_filename
+ self.files[file] = 1
+
+ if isinstance(f, types.MethodType):
+ reqargs = 2
+ else:
+ reqargs = 1
+ nargs = func_code(f).co_argcount
+ if nargs > reqargs:
+ self.log.error("%s:%d: Rule '%s' has too many arguments",file,line,f.__name__)
+ self.error = 1
+
+ if nargs < reqargs:
+ self.log.error("%s:%d: Rule '%s' requires an argument", file,line,f.__name__)
+ self.error = 1
+
+ for f in self.files:
+ self.validate_file(f)
+
+
+ # -----------------------------------------------------------------------------
+ # validate_file()
+ #
+ # This checks to see if there are duplicated t_rulename() functions or strings
+ # in the parser input file. This is done using a simple regular expression
+ # match on each line in the given file.
+ # -----------------------------------------------------------------------------
+
+ def validate_file(self,filename):
+ import os.path
+ base,ext = os.path.splitext(filename)
+ if ext != '.py': return # No idea what the file is. Return OK
+
+ try:
+ f = open(filename)
+ lines = f.readlines()
+ f.close()
+ except IOError:
+ return # Couldn't find the file. Don't worry about it
+
+ fre = re.compile(r'\s*def\s+(t_[a-zA-Z_0-9]*)\(')
+ sre = re.compile(r'\s*(t_[a-zA-Z_0-9]*)\s*=')
+
+ counthash = { }
+ linen = 1
+ for l in lines:
+ m = fre.match(l)
+ if not m:
+ m = sre.match(l)
+ if m:
+ name = m.group(1)
+ prev = counthash.get(name)
+ if not prev:
+ counthash[name] = linen
+ else:
+ self.log.error("%s:%d: Rule %s redefined. Previously defined on line %d",filename,linen,name,prev)
+ self.error = 1
+ linen += 1
+
+# -----------------------------------------------------------------------------
+# lex(module)
+#
+# Build all of the regular expression rules from definitions in the supplied module
+# -----------------------------------------------------------------------------
+def lex(module=None,object=None,debug=0,optimize=0,lextab="lextab",reflags=0,nowarn=0,outputdir="", debuglog=None, errorlog=None):
+ global lexer
+ ldict = None
+ stateinfo = { 'INITIAL' : 'inclusive'}
+ lexobj = Lexer()
+ lexobj.lexoptimize = optimize
+ global token,input
+
+ if errorlog is None:
+ errorlog = PlyLogger(sys.stderr)
+
+ if debug:
+ if debuglog is None:
+ debuglog = PlyLogger(sys.stderr)
+
+ # Get the module dictionary used for the lexer
+ if object: module = object
+
+ if module:
+ _items = [(k,getattr(module,k)) for k in dir(module)]
+ ldict = dict(_items)
+ else:
+ ldict = get_caller_module_dict(2)
+
+ # Collect parser information from the dictionary
+ linfo = LexerReflect(ldict,log=errorlog,reflags=reflags)
+ linfo.get_all()
+ if not optimize:
+ if linfo.validate_all():
+ raise SyntaxError("Can't build lexer")
+
+ if optimize and lextab:
+ try:
+ lexobj.readtab(lextab,ldict)
+ token = lexobj.token
+ input = lexobj.input
+ lexer = lexobj
+ return lexobj
+
+ except ImportError:
+ pass
+
+ # Dump some basic debugging information
+ if debug:
+ debuglog.info("lex: tokens = %r", linfo.tokens)
+ debuglog.info("lex: literals = %r", linfo.literals)
+ debuglog.info("lex: states = %r", linfo.stateinfo)
+
+ # Build a dictionary of valid token names
+ lexobj.lextokens = { }
+ for n in linfo.tokens:
+ lexobj.lextokens[n] = 1
+
+ # Get literals specification
+ if isinstance(linfo.literals,(list,tuple)):
+ lexobj.lexliterals = type(linfo.literals[0])().join(linfo.literals)
+ else:
+ lexobj.lexliterals = linfo.literals
+
+ # Get the stateinfo dictionary
+ stateinfo = linfo.stateinfo
+
+ regexs = { }
+ # Build the master regular expressions
+ for state in stateinfo:
+ regex_list = []
+
+ # Add rules defined by functions first
+ for fname, f in linfo.funcsym[state]:
+ line = func_code(f).co_firstlineno
+ file = func_code(f).co_filename
+ regex_list.append("(?P<%s>%s)" % (fname,f.__doc__))
+ if debug:
+ debuglog.info("lex: Adding rule %s -> '%s' (state '%s')",fname,f.__doc__, state)
+
+ # Now add all of the simple rules
+ for name,r in linfo.strsym[state]:
+ regex_list.append("(?P<%s>%s)" % (name,r))
+ if debug:
+ debuglog.info("lex: Adding rule %s -> '%s' (state '%s')",name,r, state)
+
+ regexs[state] = regex_list
+
+ # Build the master regular expressions
+
+ if debug:
+ debuglog.info("lex: ==== MASTER REGEXS FOLLOW ====")
+
+ for state in regexs:
+ lexre, re_text, re_names = _form_master_re(regexs[state],reflags,ldict,linfo.toknames)
+ lexobj.lexstatere[state] = lexre
+ lexobj.lexstateretext[state] = re_text
+ lexobj.lexstaterenames[state] = re_names
+ if debug:
+ for i in range(len(re_text)):
+ debuglog.info("lex: state '%s' : regex[%d] = '%s'",state, i, re_text[i])
+
+ # For inclusive states, we need to add the regular expressions from the INITIAL state
+ for state,stype in stateinfo.items():
+ if state != "INITIAL" and stype == 'inclusive':
+ lexobj.lexstatere[state].extend(lexobj.lexstatere['INITIAL'])
+ lexobj.lexstateretext[state].extend(lexobj.lexstateretext['INITIAL'])
+ lexobj.lexstaterenames[state].extend(lexobj.lexstaterenames['INITIAL'])
+
+ lexobj.lexstateinfo = stateinfo
+ lexobj.lexre = lexobj.lexstatere["INITIAL"]
+ lexobj.lexretext = lexobj.lexstateretext["INITIAL"]
+ lexobj.lexreflags = reflags
+
+ # Set up ignore variables
+ lexobj.lexstateignore = linfo.ignore
+ lexobj.lexignore = lexobj.lexstateignore.get("INITIAL","")
+
+ # Set up error functions
+ lexobj.lexstateerrorf = linfo.errorf
+ lexobj.lexerrorf = linfo.errorf.get("INITIAL",None)
+ if not lexobj.lexerrorf:
+ errorlog.warning("No t_error rule is defined")
+
+ # Check state information for ignore and error rules
+ for s,stype in stateinfo.items():
+ if stype == 'exclusive':
+ if not s in linfo.errorf:
+ errorlog.warning("No error rule is defined for exclusive state '%s'", s)
+ if not s in linfo.ignore and lexobj.lexignore:
+ errorlog.warning("No ignore rule is defined for exclusive state '%s'", s)
+ elif stype == 'inclusive':
+ if not s in linfo.errorf:
+ linfo.errorf[s] = linfo.errorf.get("INITIAL",None)
+ if not s in linfo.ignore:
+ linfo.ignore[s] = linfo.ignore.get("INITIAL","")
+
+ # Create global versions of the token() and input() functions
+ token = lexobj.token
+ input = lexobj.input
+ lexer = lexobj
+
+ # If in optimize mode, we write the lextab
+ if lextab and optimize:
+ lexobj.writetab(lextab,outputdir)
+
+ return lexobj
+
+# -----------------------------------------------------------------------------
+# runmain()
+#
+# This runs the lexer as a main program
+# -----------------------------------------------------------------------------
+
+def runmain(lexer=None,data=None):
+ if not data:
+ try:
+ filename = sys.argv[1]
+ f = open(filename)
+ data = f.read()
+ f.close()
+ except IndexError:
+ sys.stdout.write("Reading from standard input (type EOF to end):\n")
+ data = sys.stdin.read()
+
+ if lexer:
+ _input = lexer.input
+ else:
+ _input = input
+ _input(data)
+ if lexer:
+ _token = lexer.token
+ else:
+ _token = token
+
+ while 1:
+ tok = _token()
+ if not tok: break
+ sys.stdout.write("(%s,%r,%d,%d)\n" % (tok.type, tok.value, tok.lineno,tok.lexpos))
+
+# -----------------------------------------------------------------------------
+# @TOKEN(regex)
+#
+# This decorator function can be used to set the regex expression on a function
+# when its docstring might need to be set in an alternative way
+# -----------------------------------------------------------------------------
+
+def TOKEN(r):
+ def set_doc(f):
+ if hasattr(r,"__call__"):
+ f.__doc__ = r.__doc__
+ else:
+ f.__doc__ = r
+ return f
+ return set_doc
+
+# Alternative spelling of the TOKEN decorator
+Token = TOKEN
+
diff --git a/components/script/dom/bindings/codegen/ply/ply/yacc.py b/components/script/dom/bindings/codegen/ply/ply/yacc.py
new file mode 100644
index 00000000000..e9f5c657551
--- /dev/null
+++ b/components/script/dom/bindings/codegen/ply/ply/yacc.py
@@ -0,0 +1,3276 @@
+# -----------------------------------------------------------------------------
+# ply: yacc.py
+#
+# Copyright (C) 2001-2009,
+# David M. Beazley (Dabeaz LLC)
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# * Neither the name of the David Beazley or Dabeaz LLC may be used to
+# endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+# -----------------------------------------------------------------------------
+#
+# This implements an LR parser that is constructed from grammar rules defined
+# as Python functions. The grammer is specified by supplying the BNF inside
+# Python documentation strings. The inspiration for this technique was borrowed
+# from John Aycock's Spark parsing system. PLY might be viewed as cross between
+# Spark and the GNU bison utility.
+#
+# The current implementation is only somewhat object-oriented. The
+# LR parser itself is defined in terms of an object (which allows multiple
+# parsers to co-exist). However, most of the variables used during table
+# construction are defined in terms of global variables. Users shouldn't
+# notice unless they are trying to define multiple parsers at the same
+# time using threads (in which case they should have their head examined).
+#
+# This implementation supports both SLR and LALR(1) parsing. LALR(1)
+# support was originally implemented by Elias Ioup (ezioup@alumni.uchicago.edu),
+# using the algorithm found in Aho, Sethi, and Ullman "Compilers: Principles,
+# Techniques, and Tools" (The Dragon Book). LALR(1) has since been replaced
+# by the more efficient DeRemer and Pennello algorithm.
+#
+# :::::::: WARNING :::::::
+#
+# Construction of LR parsing tables is fairly complicated and expensive.
+# To make this module run fast, a *LOT* of work has been put into
+# optimization---often at the expensive of readability and what might
+# consider to be good Python "coding style." Modify the code at your
+# own risk!
+# ----------------------------------------------------------------------------
+
+__version__ = "3.3"
+__tabversion__ = "3.2" # Table version
+
+#-----------------------------------------------------------------------------
+# === User configurable parameters ===
+#
+# Change these to modify the default behavior of yacc (if you wish)
+#-----------------------------------------------------------------------------
+
+yaccdebug = 1 # Debugging mode. If set, yacc generates a
+ # a 'parser.out' file in the current directory
+
+debug_file = 'parser.out' # Default name of the debugging file
+tab_module = 'parsetab' # Default name of the table module
+default_lr = 'LALR' # Default LR table generation method
+
+error_count = 3 # Number of symbols that must be shifted to leave recovery mode
+
+yaccdevel = 0 # Set to True if developing yacc. This turns off optimized
+ # implementations of certain functions.
+
+resultlimit = 40 # Size limit of results when running in debug mode.
+
+pickle_protocol = 0 # Protocol to use when writing pickle files
+
+import re, types, sys, os.path
+
+# Compatibility function for python 2.6/3.0
+if sys.version_info[0] < 3:
+ def func_code(f):
+ return f.func_code
+else:
+ def func_code(f):
+ return f.__code__
+
+# Compatibility
+try:
+ MAXINT = sys.maxint
+except AttributeError:
+ MAXINT = sys.maxsize
+
+# Python 2.x/3.0 compatibility.
+def load_ply_lex():
+ if sys.version_info[0] < 3:
+ import lex
+ else:
+ import ply.lex as lex
+ return lex
+
+# This object is a stand-in for a logging object created by the
+# logging module. PLY will use this by default to create things
+# such as the parser.out file. If a user wants more detailed
+# information, they can create their own logging object and pass
+# it into PLY.
+
+class PlyLogger(object):
+ def __init__(self,f):
+ self.f = f
+ def debug(self,msg,*args,**kwargs):
+ self.f.write((msg % args) + "\n")
+ info = debug
+
+ def warning(self,msg,*args,**kwargs):
+ self.f.write("WARNING: "+ (msg % args) + "\n")
+
+ def error(self,msg,*args,**kwargs):
+ self.f.write("ERROR: " + (msg % args) + "\n")
+
+ critical = debug
+
+# Null logger is used when no output is generated. Does nothing.
+class NullLogger(object):
+ def __getattribute__(self,name):
+ return self
+ def __call__(self,*args,**kwargs):
+ return self
+
+# Exception raised for yacc-related errors
+class YaccError(Exception): pass
+
+# Format the result message that the parser produces when running in debug mode.
+def format_result(r):
+ repr_str = repr(r)
+ if '\n' in repr_str: repr_str = repr(repr_str)
+ if len(repr_str) > resultlimit:
+ repr_str = repr_str[:resultlimit]+" ..."
+ result = "<%s @ 0x%x> (%s)" % (type(r).__name__,id(r),repr_str)
+ return result
+
+
+# Format stack entries when the parser is running in debug mode
+def format_stack_entry(r):
+ repr_str = repr(r)
+ if '\n' in repr_str: repr_str = repr(repr_str)
+ if len(repr_str) < 16:
+ return repr_str
+ else:
+ return "<%s @ 0x%x>" % (type(r).__name__,id(r))
+
+#-----------------------------------------------------------------------------
+# === LR Parsing Engine ===
+#
+# The following classes are used for the LR parser itself. These are not
+# used during table construction and are independent of the actual LR
+# table generation algorithm
+#-----------------------------------------------------------------------------
+
+# This class is used to hold non-terminal grammar symbols during parsing.
+# It normally has the following attributes set:
+# .type = Grammar symbol type
+# .value = Symbol value
+# .lineno = Starting line number
+# .endlineno = Ending line number (optional, set automatically)
+# .lexpos = Starting lex position
+# .endlexpos = Ending lex position (optional, set automatically)
+
+class YaccSymbol:
+ def __str__(self): return self.type
+ def __repr__(self): return str(self)
+
+# This class is a wrapper around the objects actually passed to each
+# grammar rule. Index lookup and assignment actually assign the
+# .value attribute of the underlying YaccSymbol object.
+# The lineno() method returns the line number of a given
+# item (or 0 if not defined). The linespan() method returns
+# a tuple of (startline,endline) representing the range of lines
+# for a symbol. The lexspan() method returns a tuple (lexpos,endlexpos)
+# representing the range of positional information for a symbol.
+
+class YaccProduction:
+ def __init__(self,s,stack=None):
+ self.slice = s
+ self.stack = stack
+ self.lexer = None
+ self.parser= None
+ def __getitem__(self,n):
+ if n >= 0: return self.slice[n].value
+ else: return self.stack[n].value
+
+ def __setitem__(self,n,v):
+ self.slice[n].value = v
+
+ def __getslice__(self,i,j):
+ return [s.value for s in self.slice[i:j]]
+
+ def __len__(self):
+ return len(self.slice)
+
+ def lineno(self,n):
+ return getattr(self.slice[n],"lineno",0)
+
+ def set_lineno(self,n,lineno):
+ self.slice[n].lineno = lineno
+
+ def linespan(self,n):
+ startline = getattr(self.slice[n],"lineno",0)
+ endline = getattr(self.slice[n],"endlineno",startline)
+ return startline,endline
+
+ def lexpos(self,n):
+ return getattr(self.slice[n],"lexpos",0)
+
+ def lexspan(self,n):
+ startpos = getattr(self.slice[n],"lexpos",0)
+ endpos = getattr(self.slice[n],"endlexpos",startpos)
+ return startpos,endpos
+
+ def error(self):
+ raise SyntaxError
+
+
+# -----------------------------------------------------------------------------
+# == LRParser ==
+#
+# The LR Parsing engine.
+# -----------------------------------------------------------------------------
+
+class LRParser:
+ def __init__(self,lrtab,errorf):
+ self.productions = lrtab.lr_productions
+ self.action = lrtab.lr_action
+ self.goto = lrtab.lr_goto
+ self.errorfunc = errorf
+
+ def errok(self):
+ self.errorok = 1
+
+ def restart(self):
+ del self.statestack[:]
+ del self.symstack[:]
+ sym = YaccSymbol()
+ sym.type = '$end'
+ self.symstack.append(sym)
+ self.statestack.append(0)
+
+ def parse(self,input=None,lexer=None,debug=0,tracking=0,tokenfunc=None):
+ if debug or yaccdevel:
+ if isinstance(debug,int):
+ debug = PlyLogger(sys.stderr)
+ return self.parsedebug(input,lexer,debug,tracking,tokenfunc)
+ elif tracking:
+ return self.parseopt(input,lexer,debug,tracking,tokenfunc)
+ else:
+ return self.parseopt_notrack(input,lexer,debug,tracking,tokenfunc)
+
+
+ # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ # parsedebug().
+ #
+ # This is the debugging enabled version of parse(). All changes made to the
+ # parsing engine should be made here. For the non-debugging version,
+ # copy this code to a method parseopt() and delete all of the sections
+ # enclosed in:
+ #
+ # #--! DEBUG
+ # statements
+ # #--! DEBUG
+ #
+ # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+ def parsedebug(self,input=None,lexer=None,debug=None,tracking=0,tokenfunc=None):
+ lookahead = None # Current lookahead symbol
+ lookaheadstack = [ ] # Stack of lookahead symbols
+ actions = self.action # Local reference to action table (to avoid lookup on self.)
+ goto = self.goto # Local reference to goto table (to avoid lookup on self.)
+ prod = self.productions # Local reference to production list (to avoid lookup on self.)
+ pslice = YaccProduction(None) # Production object passed to grammar rules
+ errorcount = 0 # Used during error recovery
+
+ # --! DEBUG
+ debug.info("PLY: PARSE DEBUG START")
+ # --! DEBUG
+
+ # If no lexer was given, we will try to use the lex module
+ if not lexer:
+ lex = load_ply_lex()
+ lexer = lex.lexer
+
+ # Set up the lexer and parser objects on pslice
+ pslice.lexer = lexer
+ pslice.parser = self
+
+ # If input was supplied, pass to lexer
+ if input is not None:
+ lexer.input(input)
+
+ if tokenfunc is None:
+ # Tokenize function
+ get_token = lexer.token
+ else:
+ get_token = tokenfunc
+
+ # Set up the state and symbol stacks
+
+ statestack = [ ] # Stack of parsing states
+ self.statestack = statestack
+ symstack = [ ] # Stack of grammar symbols
+ self.symstack = symstack
+
+ pslice.stack = symstack # Put in the production
+ errtoken = None # Err token
+
+ # The start state is assumed to be (0,$end)
+
+ statestack.append(0)
+ sym = YaccSymbol()
+ sym.type = "$end"
+ symstack.append(sym)
+ state = 0
+ while 1:
+ # Get the next symbol on the input. If a lookahead symbol
+ # is already set, we just use that. Otherwise, we'll pull
+ # the next token off of the lookaheadstack or from the lexer
+
+ # --! DEBUG
+ debug.debug('')
+ debug.debug('State : %s', state)
+ # --! DEBUG
+
+ if not lookahead:
+ if not lookaheadstack:
+ lookahead = get_token() # Get the next token
+ else:
+ lookahead = lookaheadstack.pop()
+ if not lookahead:
+ lookahead = YaccSymbol()
+ lookahead.type = "$end"
+
+ # --! DEBUG
+ debug.debug('Stack : %s',
+ ("%s . %s" % (" ".join([xx.type for xx in symstack][1:]), str(lookahead))).lstrip())
+ # --! DEBUG
+
+ # Check the action table
+ ltype = lookahead.type
+ t = actions[state].get(ltype)
+
+ if t is not None:
+ if t > 0:
+ # shift a symbol on the stack
+ statestack.append(t)
+ state = t
+
+ # --! DEBUG
+ debug.debug("Action : Shift and goto state %s", t)
+ # --! DEBUG
+
+ symstack.append(lookahead)
+ lookahead = None
+
+ # Decrease error count on successful shift
+ if errorcount: errorcount -=1
+ continue
+
+ if t < 0:
+ # reduce a symbol on the stack, emit a production
+ p = prod[-t]
+ pname = p.name
+ plen = p.len
+
+ # Get production function
+ sym = YaccSymbol()
+ sym.type = pname # Production name
+ sym.value = None
+
+ # --! DEBUG
+ if plen:
+ debug.info("Action : Reduce rule [%s] with %s and goto state %d", p.str, "["+",".join([format_stack_entry(_v.value) for _v in symstack[-plen:]])+"]",-t)
+ else:
+ debug.info("Action : Reduce rule [%s] with %s and goto state %d", p.str, [],-t)
+
+ # --! DEBUG
+
+ if plen:
+ targ = symstack[-plen-1:]
+ targ[0] = sym
+
+ # --! TRACKING
+ if tracking:
+ t1 = targ[1]
+ sym.lineno = t1.lineno
+ sym.lexpos = t1.lexpos
+ t1 = targ[-1]
+ sym.endlineno = getattr(t1,"endlineno",t1.lineno)
+ sym.endlexpos = getattr(t1,"endlexpos",t1.lexpos)
+
+ # --! TRACKING
+
+ # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ # The code enclosed in this section is duplicated
+ # below as a performance optimization. Make sure
+ # changes get made in both locations.
+
+ pslice.slice = targ
+
+ try:
+ # Call the grammar rule with our special slice object
+ del symstack[-plen:]
+ del statestack[-plen:]
+ p.callable(pslice)
+ # --! DEBUG
+ debug.info("Result : %s", format_result(pslice[0]))
+ # --! DEBUG
+ symstack.append(sym)
+ state = goto[statestack[-1]][pname]
+ statestack.append(state)
+ except SyntaxError:
+ # If an error was set. Enter error recovery state
+ lookaheadstack.append(lookahead)
+ symstack.pop()
+ statestack.pop()
+ state = statestack[-1]
+ sym.type = 'error'
+ lookahead = sym
+ errorcount = error_count
+ self.errorok = 0
+ continue
+ # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+ else:
+
+ # --! TRACKING
+ if tracking:
+ sym.lineno = lexer.lineno
+ sym.lexpos = lexer.lexpos
+ # --! TRACKING
+
+ targ = [ sym ]
+
+ # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ # The code enclosed in this section is duplicated
+ # above as a performance optimization. Make sure
+ # changes get made in both locations.
+
+ pslice.slice = targ
+
+ try:
+ # Call the grammar rule with our special slice object
+ p.callable(pslice)
+ # --! DEBUG
+ debug.info("Result : %s", format_result(pslice[0]))
+ # --! DEBUG
+ symstack.append(sym)
+ state = goto[statestack[-1]][pname]
+ statestack.append(state)
+ except SyntaxError:
+ # If an error was set. Enter error recovery state
+ lookaheadstack.append(lookahead)
+ symstack.pop()
+ statestack.pop()
+ state = statestack[-1]
+ sym.type = 'error'
+ lookahead = sym
+ errorcount = error_count
+ self.errorok = 0
+ continue
+ # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+ if t == 0:
+ n = symstack[-1]
+ result = getattr(n,"value",None)
+ # --! DEBUG
+ debug.info("Done : Returning %s", format_result(result))
+ debug.info("PLY: PARSE DEBUG END")
+ # --! DEBUG
+ return result
+
+ if t == None:
+
+ # --! DEBUG
+ debug.error('Error : %s',
+ ("%s . %s" % (" ".join([xx.type for xx in symstack][1:]), str(lookahead))).lstrip())
+ # --! DEBUG
+
+ # We have some kind of parsing error here. To handle
+ # this, we are going to push the current token onto
+ # the tokenstack and replace it with an 'error' token.
+ # If there are any synchronization rules, they may
+ # catch it.
+ #
+ # In addition to pushing the error token, we call call
+ # the user defined p_error() function if this is the
+ # first syntax error. This function is only called if
+ # errorcount == 0.
+ if errorcount == 0 or self.errorok:
+ errorcount = error_count
+ self.errorok = 0
+ errtoken = lookahead
+ if errtoken.type == "$end":
+ errtoken = None # End of file!
+ if self.errorfunc:
+ global errok,token,restart
+ errok = self.errok # Set some special functions available in error recovery
+ token = get_token
+ restart = self.restart
+ if errtoken and not hasattr(errtoken,'lexer'):
+ errtoken.lexer = lexer
+ tok = self.errorfunc(errtoken)
+ del errok, token, restart # Delete special functions
+
+ if self.errorok:
+ # User must have done some kind of panic
+ # mode recovery on their own. The
+ # returned token is the next lookahead
+ lookahead = tok
+ errtoken = None
+ continue
+ else:
+ if errtoken:
+ if hasattr(errtoken,"lineno"): lineno = lookahead.lineno
+ else: lineno = 0
+ if lineno:
+ sys.stderr.write("yacc: Syntax error at line %d, token=%s\n" % (lineno, errtoken.type))
+ else:
+ sys.stderr.write("yacc: Syntax error, token=%s" % errtoken.type)
+ else:
+ sys.stderr.write("yacc: Parse error in input. EOF\n")
+ return
+
+ else:
+ errorcount = error_count
+
+ # case 1: the statestack only has 1 entry on it. If we're in this state, the
+ # entire parse has been rolled back and we're completely hosed. The token is
+ # discarded and we just keep going.
+
+ if len(statestack) <= 1 and lookahead.type != "$end":
+ lookahead = None
+ errtoken = None
+ state = 0
+ # Nuke the pushback stack
+ del lookaheadstack[:]
+ continue
+
+ # case 2: the statestack has a couple of entries on it, but we're
+ # at the end of the file. nuke the top entry and generate an error token
+
+ # Start nuking entries on the stack
+ if lookahead.type == "$end":
+ # Whoa. We're really hosed here. Bail out
+ return
+
+ if lookahead.type != 'error':
+ sym = symstack[-1]
+ if sym.type == 'error':
+ # Hmmm. Error is on top of stack, we'll just nuke input
+ # symbol and continue
+ lookahead = None
+ continue
+ t = YaccSymbol()
+ t.type = 'error'
+ if hasattr(lookahead,"lineno"):
+ t.lineno = lookahead.lineno
+ t.value = lookahead
+ lookaheadstack.append(lookahead)
+ lookahead = t
+ else:
+ symstack.pop()
+ statestack.pop()
+ state = statestack[-1] # Potential bug fix
+
+ continue
+
+ # Call an error function here
+ raise RuntimeError("yacc: internal parser error!!!\n")
+
+ # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ # parseopt().
+ #
+ # Optimized version of parse() method. DO NOT EDIT THIS CODE DIRECTLY.
+ # Edit the debug version above, then copy any modifications to the method
+ # below while removing #--! DEBUG sections.
+ # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+
+ def parseopt(self,input=None,lexer=None,debug=0,tracking=0,tokenfunc=None):
+ lookahead = None # Current lookahead symbol
+ lookaheadstack = [ ] # Stack of lookahead symbols
+ actions = self.action # Local reference to action table (to avoid lookup on self.)
+ goto = self.goto # Local reference to goto table (to avoid lookup on self.)
+ prod = self.productions # Local reference to production list (to avoid lookup on self.)
+ pslice = YaccProduction(None) # Production object passed to grammar rules
+ errorcount = 0 # Used during error recovery
+
+ # If no lexer was given, we will try to use the lex module
+ if not lexer:
+ lex = load_ply_lex()
+ lexer = lex.lexer
+
+ # Set up the lexer and parser objects on pslice
+ pslice.lexer = lexer
+ pslice.parser = self
+
+ # If input was supplied, pass to lexer
+ if input is not None:
+ lexer.input(input)
+
+ if tokenfunc is None:
+ # Tokenize function
+ get_token = lexer.token
+ else:
+ get_token = tokenfunc
+
+ # Set up the state and symbol stacks
+
+ statestack = [ ] # Stack of parsing states
+ self.statestack = statestack
+ symstack = [ ] # Stack of grammar symbols
+ self.symstack = symstack
+
+ pslice.stack = symstack # Put in the production
+ errtoken = None # Err token
+
+ # The start state is assumed to be (0,$end)
+
+ statestack.append(0)
+ sym = YaccSymbol()
+ sym.type = '$end'
+ symstack.append(sym)
+ state = 0
+ while 1:
+ # Get the next symbol on the input. If a lookahead symbol
+ # is already set, we just use that. Otherwise, we'll pull
+ # the next token off of the lookaheadstack or from the lexer
+
+ if not lookahead:
+ if not lookaheadstack:
+ lookahead = get_token() # Get the next token
+ else:
+ lookahead = lookaheadstack.pop()
+ if not lookahead:
+ lookahead = YaccSymbol()
+ lookahead.type = '$end'
+
+ # Check the action table
+ ltype = lookahead.type
+ t = actions[state].get(ltype)
+
+ if t is not None:
+ if t > 0:
+ # shift a symbol on the stack
+ statestack.append(t)
+ state = t
+
+ symstack.append(lookahead)
+ lookahead = None
+
+ # Decrease error count on successful shift
+ if errorcount: errorcount -=1
+ continue
+
+ if t < 0:
+ # reduce a symbol on the stack, emit a production
+ p = prod[-t]
+ pname = p.name
+ plen = p.len
+
+ # Get production function
+ sym = YaccSymbol()
+ sym.type = pname # Production name
+ sym.value = None
+
+ if plen:
+ targ = symstack[-plen-1:]
+ targ[0] = sym
+
+ # --! TRACKING
+ if tracking:
+ t1 = targ[1]
+ sym.lineno = t1.lineno
+ sym.lexpos = t1.lexpos
+ t1 = targ[-1]
+ sym.endlineno = getattr(t1,"endlineno",t1.lineno)
+ sym.endlexpos = getattr(t1,"endlexpos",t1.lexpos)
+
+ # --! TRACKING
+
+ # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ # The code enclosed in this section is duplicated
+ # below as a performance optimization. Make sure
+ # changes get made in both locations.
+
+ pslice.slice = targ
+
+ try:
+ # Call the grammar rule with our special slice object
+ del symstack[-plen:]
+ del statestack[-plen:]
+ p.callable(pslice)
+ symstack.append(sym)
+ state = goto[statestack[-1]][pname]
+ statestack.append(state)
+ except SyntaxError:
+ # If an error was set. Enter error recovery state
+ lookaheadstack.append(lookahead)
+ symstack.pop()
+ statestack.pop()
+ state = statestack[-1]
+ sym.type = 'error'
+ lookahead = sym
+ errorcount = error_count
+ self.errorok = 0
+ continue
+ # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+ else:
+
+ # --! TRACKING
+ if tracking:
+ sym.lineno = lexer.lineno
+ sym.lexpos = lexer.lexpos
+ # --! TRACKING
+
+ targ = [ sym ]
+
+ # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ # The code enclosed in this section is duplicated
+ # above as a performance optimization. Make sure
+ # changes get made in both locations.
+
+ pslice.slice = targ
+
+ try:
+ # Call the grammar rule with our special slice object
+ p.callable(pslice)
+ symstack.append(sym)
+ state = goto[statestack[-1]][pname]
+ statestack.append(state)
+ except SyntaxError:
+ # If an error was set. Enter error recovery state
+ lookaheadstack.append(lookahead)
+ symstack.pop()
+ statestack.pop()
+ state = statestack[-1]
+ sym.type = 'error'
+ lookahead = sym
+ errorcount = error_count
+ self.errorok = 0
+ continue
+ # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+ if t == 0:
+ n = symstack[-1]
+ return getattr(n,"value",None)
+
+ if t == None:
+
+ # We have some kind of parsing error here. To handle
+ # this, we are going to push the current token onto
+ # the tokenstack and replace it with an 'error' token.
+ # If there are any synchronization rules, they may
+ # catch it.
+ #
+ # In addition to pushing the error token, we call call
+ # the user defined p_error() function if this is the
+ # first syntax error. This function is only called if
+ # errorcount == 0.
+ if errorcount == 0 or self.errorok:
+ errorcount = error_count
+ self.errorok = 0
+ errtoken = lookahead
+ if errtoken.type == '$end':
+ errtoken = None # End of file!
+ if self.errorfunc:
+ global errok,token,restart
+ errok = self.errok # Set some special functions available in error recovery
+ token = get_token
+ restart = self.restart
+ if errtoken and not hasattr(errtoken,'lexer'):
+ errtoken.lexer = lexer
+ tok = self.errorfunc(errtoken)
+ del errok, token, restart # Delete special functions
+
+ if self.errorok:
+ # User must have done some kind of panic
+ # mode recovery on their own. The
+ # returned token is the next lookahead
+ lookahead = tok
+ errtoken = None
+ continue
+ else:
+ if errtoken:
+ if hasattr(errtoken,"lineno"): lineno = lookahead.lineno
+ else: lineno = 0
+ if lineno:
+ sys.stderr.write("yacc: Syntax error at line %d, token=%s\n" % (lineno, errtoken.type))
+ else:
+ sys.stderr.write("yacc: Syntax error, token=%s" % errtoken.type)
+ else:
+ sys.stderr.write("yacc: Parse error in input. EOF\n")
+ return
+
+ else:
+ errorcount = error_count
+
+ # case 1: the statestack only has 1 entry on it. If we're in this state, the
+ # entire parse has been rolled back and we're completely hosed. The token is
+ # discarded and we just keep going.
+
+ if len(statestack) <= 1 and lookahead.type != '$end':
+ lookahead = None
+ errtoken = None
+ state = 0
+ # Nuke the pushback stack
+ del lookaheadstack[:]
+ continue
+
+ # case 2: the statestack has a couple of entries on it, but we're
+ # at the end of the file. nuke the top entry and generate an error token
+
+ # Start nuking entries on the stack
+ if lookahead.type == '$end':
+ # Whoa. We're really hosed here. Bail out
+ return
+
+ if lookahead.type != 'error':
+ sym = symstack[-1]
+ if sym.type == 'error':
+ # Hmmm. Error is on top of stack, we'll just nuke input
+ # symbol and continue
+ lookahead = None
+ continue
+ t = YaccSymbol()
+ t.type = 'error'
+ if hasattr(lookahead,"lineno"):
+ t.lineno = lookahead.lineno
+ t.value = lookahead
+ lookaheadstack.append(lookahead)
+ lookahead = t
+ else:
+ symstack.pop()
+ statestack.pop()
+ state = statestack[-1] # Potential bug fix
+
+ continue
+
+ # Call an error function here
+ raise RuntimeError("yacc: internal parser error!!!\n")
+
+ # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ # parseopt_notrack().
+ #
+ # Optimized version of parseopt() with line number tracking removed.
+ # DO NOT EDIT THIS CODE DIRECTLY. Copy the optimized version and remove
+ # code in the #--! TRACKING sections
+ # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+ def parseopt_notrack(self,input=None,lexer=None,debug=0,tracking=0,tokenfunc=None):
+ lookahead = None # Current lookahead symbol
+ lookaheadstack = [ ] # Stack of lookahead symbols
+ actions = self.action # Local reference to action table (to avoid lookup on self.)
+ goto = self.goto # Local reference to goto table (to avoid lookup on self.)
+ prod = self.productions # Local reference to production list (to avoid lookup on self.)
+ pslice = YaccProduction(None) # Production object passed to grammar rules
+ errorcount = 0 # Used during error recovery
+
+ # If no lexer was given, we will try to use the lex module
+ if not lexer:
+ lex = load_ply_lex()
+ lexer = lex.lexer
+
+ # Set up the lexer and parser objects on pslice
+ pslice.lexer = lexer
+ pslice.parser = self
+
+ # If input was supplied, pass to lexer
+ if input is not None:
+ lexer.input(input)
+
+ if tokenfunc is None:
+ # Tokenize function
+ get_token = lexer.token
+ else:
+ get_token = tokenfunc
+
+ # Set up the state and symbol stacks
+
+ statestack = [ ] # Stack of parsing states
+ self.statestack = statestack
+ symstack = [ ] # Stack of grammar symbols
+ self.symstack = symstack
+
+ pslice.stack = symstack # Put in the production
+ errtoken = None # Err token
+
+ # The start state is assumed to be (0,$end)
+
+ statestack.append(0)
+ sym = YaccSymbol()
+ sym.type = '$end'
+ symstack.append(sym)
+ state = 0
+ while 1:
+ # Get the next symbol on the input. If a lookahead symbol
+ # is already set, we just use that. Otherwise, we'll pull
+ # the next token off of the lookaheadstack or from the lexer
+
+ if not lookahead:
+ if not lookaheadstack:
+ lookahead = get_token() # Get the next token
+ else:
+ lookahead = lookaheadstack.pop()
+ if not lookahead:
+ lookahead = YaccSymbol()
+ lookahead.type = '$end'
+
+ # Check the action table
+ ltype = lookahead.type
+ t = actions[state].get(ltype)
+
+ if t is not None:
+ if t > 0:
+ # shift a symbol on the stack
+ statestack.append(t)
+ state = t
+
+ symstack.append(lookahead)
+ lookahead = None
+
+ # Decrease error count on successful shift
+ if errorcount: errorcount -=1
+ continue
+
+ if t < 0:
+ # reduce a symbol on the stack, emit a production
+ p = prod[-t]
+ pname = p.name
+ plen = p.len
+
+ # Get production function
+ sym = YaccSymbol()
+ sym.type = pname # Production name
+ sym.value = None
+
+ if plen:
+ targ = symstack[-plen-1:]
+ targ[0] = sym
+
+ # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ # The code enclosed in this section is duplicated
+ # below as a performance optimization. Make sure
+ # changes get made in both locations.
+
+ pslice.slice = targ
+
+ try:
+ # Call the grammar rule with our special slice object
+ del symstack[-plen:]
+ del statestack[-plen:]
+ p.callable(pslice)
+ symstack.append(sym)
+ state = goto[statestack[-1]][pname]
+ statestack.append(state)
+ except SyntaxError:
+ # If an error was set. Enter error recovery state
+ lookaheadstack.append(lookahead)
+ symstack.pop()
+ statestack.pop()
+ state = statestack[-1]
+ sym.type = 'error'
+ lookahead = sym
+ errorcount = error_count
+ self.errorok = 0
+ continue
+ # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+ else:
+
+ targ = [ sym ]
+
+ # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ # The code enclosed in this section is duplicated
+ # above as a performance optimization. Make sure
+ # changes get made in both locations.
+
+ pslice.slice = targ
+
+ try:
+ # Call the grammar rule with our special slice object
+ p.callable(pslice)
+ symstack.append(sym)
+ state = goto[statestack[-1]][pname]
+ statestack.append(state)
+ except SyntaxError:
+ # If an error was set. Enter error recovery state
+ lookaheadstack.append(lookahead)
+ symstack.pop()
+ statestack.pop()
+ state = statestack[-1]
+ sym.type = 'error'
+ lookahead = sym
+ errorcount = error_count
+ self.errorok = 0
+ continue
+ # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+ if t == 0:
+ n = symstack[-1]
+ return getattr(n,"value",None)
+
+ if t == None:
+
+ # We have some kind of parsing error here. To handle
+ # this, we are going to push the current token onto
+ # the tokenstack and replace it with an 'error' token.
+ # If there are any synchronization rules, they may
+ # catch it.
+ #
+ # In addition to pushing the error token, we call call
+ # the user defined p_error() function if this is the
+ # first syntax error. This function is only called if
+ # errorcount == 0.
+ if errorcount == 0 or self.errorok:
+ errorcount = error_count
+ self.errorok = 0
+ errtoken = lookahead
+ if errtoken.type == '$end':
+ errtoken = None # End of file!
+ if self.errorfunc:
+ global errok,token,restart
+ errok = self.errok # Set some special functions available in error recovery
+ token = get_token
+ restart = self.restart
+ if errtoken and not hasattr(errtoken,'lexer'):
+ errtoken.lexer = lexer
+ tok = self.errorfunc(errtoken)
+ del errok, token, restart # Delete special functions
+
+ if self.errorok:
+ # User must have done some kind of panic
+ # mode recovery on their own. The
+ # returned token is the next lookahead
+ lookahead = tok
+ errtoken = None
+ continue
+ else:
+ if errtoken:
+ if hasattr(errtoken,"lineno"): lineno = lookahead.lineno
+ else: lineno = 0
+ if lineno:
+ sys.stderr.write("yacc: Syntax error at line %d, token=%s\n" % (lineno, errtoken.type))
+ else:
+ sys.stderr.write("yacc: Syntax error, token=%s" % errtoken.type)
+ else:
+ sys.stderr.write("yacc: Parse error in input. EOF\n")
+ return
+
+ else:
+ errorcount = error_count
+
+ # case 1: the statestack only has 1 entry on it. If we're in this state, the
+ # entire parse has been rolled back and we're completely hosed. The token is
+ # discarded and we just keep going.
+
+ if len(statestack) <= 1 and lookahead.type != '$end':
+ lookahead = None
+ errtoken = None
+ state = 0
+ # Nuke the pushback stack
+ del lookaheadstack[:]
+ continue
+
+ # case 2: the statestack has a couple of entries on it, but we're
+ # at the end of the file. nuke the top entry and generate an error token
+
+ # Start nuking entries on the stack
+ if lookahead.type == '$end':
+ # Whoa. We're really hosed here. Bail out
+ return
+
+ if lookahead.type != 'error':
+ sym = symstack[-1]
+ if sym.type == 'error':
+ # Hmmm. Error is on top of stack, we'll just nuke input
+ # symbol and continue
+ lookahead = None
+ continue
+ t = YaccSymbol()
+ t.type = 'error'
+ if hasattr(lookahead,"lineno"):
+ t.lineno = lookahead.lineno
+ t.value = lookahead
+ lookaheadstack.append(lookahead)
+ lookahead = t
+ else:
+ symstack.pop()
+ statestack.pop()
+ state = statestack[-1] # Potential bug fix
+
+ continue
+
+ # Call an error function here
+ raise RuntimeError("yacc: internal parser error!!!\n")
+
+# -----------------------------------------------------------------------------
+# === Grammar Representation ===
+#
+# The following functions, classes, and variables are used to represent and
+# manipulate the rules that make up a grammar.
+# -----------------------------------------------------------------------------
+
+import re
+
+# regex matching identifiers
+_is_identifier = re.compile(r'^[a-zA-Z0-9_-]+$')
+
+# -----------------------------------------------------------------------------
+# class Production:
+#
+# This class stores the raw information about a single production or grammar rule.
+# A grammar rule refers to a specification such as this:
+#
+# expr : expr PLUS term
+#
+# Here are the basic attributes defined on all productions
+#
+# name - Name of the production. For example 'expr'
+# prod - A list of symbols on the right side ['expr','PLUS','term']
+# prec - Production precedence level
+# number - Production number.
+# func - Function that executes on reduce
+# file - File where production function is defined
+# lineno - Line number where production function is defined
+#
+# The following attributes are defined or optional.
+#
+# len - Length of the production (number of symbols on right hand side)
+# usyms - Set of unique symbols found in the production
+# -----------------------------------------------------------------------------
+
+class Production(object):
+ reduced = 0
+ def __init__(self,number,name,prod,precedence=('right',0),func=None,file='',line=0):
+ self.name = name
+ self.prod = tuple(prod)
+ self.number = number
+ self.func = func
+ self.callable = None
+ self.file = file
+ self.line = line
+ self.prec = precedence
+
+ # Internal settings used during table construction
+
+ self.len = len(self.prod) # Length of the production
+
+ # Create a list of unique production symbols used in the production
+ self.usyms = [ ]
+ for s in self.prod:
+ if s not in self.usyms:
+ self.usyms.append(s)
+
+ # List of all LR items for the production
+ self.lr_items = []
+ self.lr_next = None
+
+ # Create a string representation
+ if self.prod:
+ self.str = "%s -> %s" % (self.name," ".join(self.prod))
+ else:
+ self.str = "%s -> <empty>" % self.name
+
+ def __str__(self):
+ return self.str
+
+ def __repr__(self):
+ return "Production("+str(self)+")"
+
+ def __len__(self):
+ return len(self.prod)
+
+ def __nonzero__(self):
+ return 1
+
+ def __getitem__(self,index):
+ return self.prod[index]
+
+ # Return the nth lr_item from the production (or None if at the end)
+ def lr_item(self,n):
+ if n > len(self.prod): return None
+ p = LRItem(self,n)
+
+ # Precompute the list of productions immediately following. Hack. Remove later
+ try:
+ p.lr_after = Prodnames[p.prod[n+1]]
+ except (IndexError,KeyError):
+ p.lr_after = []
+ try:
+ p.lr_before = p.prod[n-1]
+ except IndexError:
+ p.lr_before = None
+
+ return p
+
+ # Bind the production function name to a callable
+ def bind(self,pdict):
+ if self.func:
+ self.callable = pdict[self.func]
+
+# This class serves as a minimal standin for Production objects when
+# reading table data from files. It only contains information
+# actually used by the LR parsing engine, plus some additional
+# debugging information.
+class MiniProduction(object):
+ def __init__(self,str,name,len,func,file,line):
+ self.name = name
+ self.len = len
+ self.func = func
+ self.callable = None
+ self.file = file
+ self.line = line
+ self.str = str
+ def __str__(self):
+ return self.str
+ def __repr__(self):
+ return "MiniProduction(%s)" % self.str
+
+ # Bind the production function name to a callable
+ def bind(self,pdict):
+ if self.func:
+ self.callable = pdict[self.func]
+
+
+# -----------------------------------------------------------------------------
+# class LRItem
+#
+# This class represents a specific stage of parsing a production rule. For
+# example:
+#
+# expr : expr . PLUS term
+#
+# In the above, the "." represents the current location of the parse. Here
+# basic attributes:
+#
+# name - Name of the production. For example 'expr'
+# prod - A list of symbols on the right side ['expr','.', 'PLUS','term']
+# number - Production number.
+#
+# lr_next Next LR item. Example, if we are ' expr -> expr . PLUS term'
+# then lr_next refers to 'expr -> expr PLUS . term'
+# lr_index - LR item index (location of the ".") in the prod list.
+# lookaheads - LALR lookahead symbols for this item
+# len - Length of the production (number of symbols on right hand side)
+# lr_after - List of all productions that immediately follow
+# lr_before - Grammar symbol immediately before
+# -----------------------------------------------------------------------------
+
+class LRItem(object):
+ def __init__(self,p,n):
+ self.name = p.name
+ self.prod = list(p.prod)
+ self.number = p.number
+ self.lr_index = n
+ self.lookaheads = { }
+ self.prod.insert(n,".")
+ self.prod = tuple(self.prod)
+ self.len = len(self.prod)
+ self.usyms = p.usyms
+
+ def __str__(self):
+ if self.prod:
+ s = "%s -> %s" % (self.name," ".join(self.prod))
+ else:
+ s = "%s -> <empty>" % self.name
+ return s
+
+ def __repr__(self):
+ return "LRItem("+str(self)+")"
+
+# -----------------------------------------------------------------------------
+# rightmost_terminal()
+#
+# Return the rightmost terminal from a list of symbols. Used in add_production()
+# -----------------------------------------------------------------------------
+def rightmost_terminal(symbols, terminals):
+ i = len(symbols) - 1
+ while i >= 0:
+ if symbols[i] in terminals:
+ return symbols[i]
+ i -= 1
+ return None
+
+# -----------------------------------------------------------------------------
+# === GRAMMAR CLASS ===
+#
+# The following class represents the contents of the specified grammar along
+# with various computed properties such as first sets, follow sets, LR items, etc.
+# This data is used for critical parts of the table generation process later.
+# -----------------------------------------------------------------------------
+
+class GrammarError(YaccError): pass
+
+class Grammar(object):
+ def __init__(self,terminals):
+ self.Productions = [None] # A list of all of the productions. The first
+ # entry is always reserved for the purpose of
+ # building an augmented grammar
+
+ self.Prodnames = { } # A dictionary mapping the names of nonterminals to a list of all
+ # productions of that nonterminal.
+
+ self.Prodmap = { } # A dictionary that is only used to detect duplicate
+ # productions.
+
+ self.Terminals = { } # A dictionary mapping the names of terminal symbols to a
+ # list of the rules where they are used.
+
+ for term in terminals:
+ self.Terminals[term] = []
+
+ self.Terminals['error'] = []
+
+ self.Nonterminals = { } # A dictionary mapping names of nonterminals to a list
+ # of rule numbers where they are used.
+
+ self.First = { } # A dictionary of precomputed FIRST(x) symbols
+
+ self.Follow = { } # A dictionary of precomputed FOLLOW(x) symbols
+
+ self.Precedence = { } # Precedence rules for each terminal. Contains tuples of the
+ # form ('right',level) or ('nonassoc', level) or ('left',level)
+
+ self.UsedPrecedence = { } # Precedence rules that were actually used by the grammer.
+ # This is only used to provide error checking and to generate
+ # a warning about unused precedence rules.
+
+ self.Start = None # Starting symbol for the grammar
+
+
+ def __len__(self):
+ return len(self.Productions)
+
+ def __getitem__(self,index):
+ return self.Productions[index]
+
+ # -----------------------------------------------------------------------------
+ # set_precedence()
+ #
+ # Sets the precedence for a given terminal. assoc is the associativity such as
+ # 'left','right', or 'nonassoc'. level is a numeric level.
+ #
+ # -----------------------------------------------------------------------------
+
+ def set_precedence(self,term,assoc,level):
+ assert self.Productions == [None],"Must call set_precedence() before add_production()"
+ if term in self.Precedence:
+ raise GrammarError("Precedence already specified for terminal '%s'" % term)
+ if assoc not in ['left','right','nonassoc']:
+ raise GrammarError("Associativity must be one of 'left','right', or 'nonassoc'")
+ self.Precedence[term] = (assoc,level)
+
+ # -----------------------------------------------------------------------------
+ # add_production()
+ #
+ # Given an action function, this function assembles a production rule and
+ # computes its precedence level.
+ #
+ # The production rule is supplied as a list of symbols. For example,
+ # a rule such as 'expr : expr PLUS term' has a production name of 'expr' and
+ # symbols ['expr','PLUS','term'].
+ #
+ # Precedence is determined by the precedence of the right-most non-terminal
+ # or the precedence of a terminal specified by %prec.
+ #
+ # A variety of error checks are performed to make sure production symbols
+ # are valid and that %prec is used correctly.
+ # -----------------------------------------------------------------------------
+
+ def add_production(self,prodname,syms,func=None,file='',line=0):
+
+ if prodname in self.Terminals:
+ raise GrammarError("%s:%d: Illegal rule name '%s'. Already defined as a token" % (file,line,prodname))
+ if prodname == 'error':
+ raise GrammarError("%s:%d: Illegal rule name '%s'. error is a reserved word" % (file,line,prodname))
+ if not _is_identifier.match(prodname):
+ raise GrammarError("%s:%d: Illegal rule name '%s'" % (file,line,prodname))
+
+ # Look for literal tokens
+ for n,s in enumerate(syms):
+ if s[0] in "'\"":
+ try:
+ c = eval(s)
+ if (len(c) > 1):
+ raise GrammarError("%s:%d: Literal token %s in rule '%s' may only be a single character" % (file,line,s, prodname))
+ if not c in self.Terminals:
+ self.Terminals[c] = []
+ syms[n] = c
+ continue
+ except SyntaxError:
+ pass
+ if not _is_identifier.match(s) and s != '%prec':
+ raise GrammarError("%s:%d: Illegal name '%s' in rule '%s'" % (file,line,s, prodname))
+
+ # Determine the precedence level
+ if '%prec' in syms:
+ if syms[-1] == '%prec':
+ raise GrammarError("%s:%d: Syntax error. Nothing follows %%prec" % (file,line))
+ if syms[-2] != '%prec':
+ raise GrammarError("%s:%d: Syntax error. %%prec can only appear at the end of a grammar rule" % (file,line))
+ precname = syms[-1]
+ prodprec = self.Precedence.get(precname,None)
+ if not prodprec:
+ raise GrammarError("%s:%d: Nothing known about the precedence of '%s'" % (file,line,precname))
+ else:
+ self.UsedPrecedence[precname] = 1
+ del syms[-2:] # Drop %prec from the rule
+ else:
+ # If no %prec, precedence is determined by the rightmost terminal symbol
+ precname = rightmost_terminal(syms,self.Terminals)
+ prodprec = self.Precedence.get(precname,('right',0))
+
+ # See if the rule is already in the rulemap
+ map = "%s -> %s" % (prodname,syms)
+ if map in self.Prodmap:
+ m = self.Prodmap[map]
+ raise GrammarError("%s:%d: Duplicate rule %s. " % (file,line, m) +
+ "Previous definition at %s:%d" % (m.file, m.line))
+
+ # From this point on, everything is valid. Create a new Production instance
+ pnumber = len(self.Productions)
+ if not prodname in self.Nonterminals:
+ self.Nonterminals[prodname] = [ ]
+
+ # Add the production number to Terminals and Nonterminals
+ for t in syms:
+ if t in self.Terminals:
+ self.Terminals[t].append(pnumber)
+ else:
+ if not t in self.Nonterminals:
+ self.Nonterminals[t] = [ ]
+ self.Nonterminals[t].append(pnumber)
+
+ # Create a production and add it to the list of productions
+ p = Production(pnumber,prodname,syms,prodprec,func,file,line)
+ self.Productions.append(p)
+ self.Prodmap[map] = p
+
+ # Add to the global productions list
+ try:
+ self.Prodnames[prodname].append(p)
+ except KeyError:
+ self.Prodnames[prodname] = [ p ]
+ return 0
+
+ # -----------------------------------------------------------------------------
+ # set_start()
+ #
+ # Sets the starting symbol and creates the augmented grammar. Production
+ # rule 0 is S' -> start where start is the start symbol.
+ # -----------------------------------------------------------------------------
+
+ def set_start(self,start=None):
+ if not start:
+ start = self.Productions[1].name
+ if start not in self.Nonterminals:
+ raise GrammarError("start symbol %s undefined" % start)
+ self.Productions[0] = Production(0,"S'",[start])
+ self.Nonterminals[start].append(0)
+ self.Start = start
+
+ # -----------------------------------------------------------------------------
+ # find_unreachable()
+ #
+ # Find all of the nonterminal symbols that can't be reached from the starting
+ # symbol. Returns a list of nonterminals that can't be reached.
+ # -----------------------------------------------------------------------------
+
+ def find_unreachable(self):
+
+ # Mark all symbols that are reachable from a symbol s
+ def mark_reachable_from(s):
+ if reachable[s]:
+ # We've already reached symbol s.
+ return
+ reachable[s] = 1
+ for p in self.Prodnames.get(s,[]):
+ for r in p.prod:
+ mark_reachable_from(r)
+
+ reachable = { }
+ for s in list(self.Terminals) + list(self.Nonterminals):
+ reachable[s] = 0
+
+ mark_reachable_from( self.Productions[0].prod[0] )
+
+ return [s for s in list(self.Nonterminals)
+ if not reachable[s]]
+
+ # -----------------------------------------------------------------------------
+ # infinite_cycles()
+ #
+ # This function looks at the various parsing rules and tries to detect
+ # infinite recursion cycles (grammar rules where there is no possible way
+ # to derive a string of only terminals).
+ # -----------------------------------------------------------------------------
+
+ def infinite_cycles(self):
+ terminates = {}
+
+ # Terminals:
+ for t in self.Terminals:
+ terminates[t] = 1
+
+ terminates['$end'] = 1
+
+ # Nonterminals:
+
+ # Initialize to false:
+ for n in self.Nonterminals:
+ terminates[n] = 0
+
+ # Then propagate termination until no change:
+ while 1:
+ some_change = 0
+ for (n,pl) in self.Prodnames.items():
+ # Nonterminal n terminates iff any of its productions terminates.
+ for p in pl:
+ # Production p terminates iff all of its rhs symbols terminate.
+ for s in p.prod:
+ if not terminates[s]:
+ # The symbol s does not terminate,
+ # so production p does not terminate.
+ p_terminates = 0
+ break
+ else:
+ # didn't break from the loop,
+ # so every symbol s terminates
+ # so production p terminates.
+ p_terminates = 1
+
+ if p_terminates:
+ # symbol n terminates!
+ if not terminates[n]:
+ terminates[n] = 1
+ some_change = 1
+ # Don't need to consider any more productions for this n.
+ break
+
+ if not some_change:
+ break
+
+ infinite = []
+ for (s,term) in terminates.items():
+ if not term:
+ if not s in self.Prodnames and not s in self.Terminals and s != 'error':
+ # s is used-but-not-defined, and we've already warned of that,
+ # so it would be overkill to say that it's also non-terminating.
+ pass
+ else:
+ infinite.append(s)
+
+ return infinite
+
+
+ # -----------------------------------------------------------------------------
+ # undefined_symbols()
+ #
+ # Find all symbols that were used the grammar, but not defined as tokens or
+ # grammar rules. Returns a list of tuples (sym, prod) where sym in the symbol
+ # and prod is the production where the symbol was used.
+ # -----------------------------------------------------------------------------
+ def undefined_symbols(self):
+ result = []
+ for p in self.Productions:
+ if not p: continue
+
+ for s in p.prod:
+ if not s in self.Prodnames and not s in self.Terminals and s != 'error':
+ result.append((s,p))
+ return result
+
+ # -----------------------------------------------------------------------------
+ # unused_terminals()
+ #
+ # Find all terminals that were defined, but not used by the grammar. Returns
+ # a list of all symbols.
+ # -----------------------------------------------------------------------------
+ def unused_terminals(self):
+ unused_tok = []
+ for s,v in self.Terminals.items():
+ if s != 'error' and not v:
+ unused_tok.append(s)
+
+ return unused_tok
+
+ # ------------------------------------------------------------------------------
+ # unused_rules()
+ #
+ # Find all grammar rules that were defined, but not used (maybe not reachable)
+ # Returns a list of productions.
+ # ------------------------------------------------------------------------------
+
+ def unused_rules(self):
+ unused_prod = []
+ for s,v in self.Nonterminals.items():
+ if not v:
+ p = self.Prodnames[s][0]
+ unused_prod.append(p)
+ return unused_prod
+
+ # -----------------------------------------------------------------------------
+ # unused_precedence()
+ #
+ # Returns a list of tuples (term,precedence) corresponding to precedence
+ # rules that were never used by the grammar. term is the name of the terminal
+ # on which precedence was applied and precedence is a string such as 'left' or
+ # 'right' corresponding to the type of precedence.
+ # -----------------------------------------------------------------------------
+
+ def unused_precedence(self):
+ unused = []
+ for termname in self.Precedence:
+ if not (termname in self.Terminals or termname in self.UsedPrecedence):
+ unused.append((termname,self.Precedence[termname][0]))
+
+ return unused
+
+ # -------------------------------------------------------------------------
+ # _first()
+ #
+ # Compute the value of FIRST1(beta) where beta is a tuple of symbols.
+ #
+ # During execution of compute_first1, the result may be incomplete.
+ # Afterward (e.g., when called from compute_follow()), it will be complete.
+ # -------------------------------------------------------------------------
+ def _first(self,beta):
+
+ # We are computing First(x1,x2,x3,...,xn)
+ result = [ ]
+ for x in beta:
+ x_produces_empty = 0
+
+ # Add all the non-<empty> symbols of First[x] to the result.
+ for f in self.First[x]:
+ if f == '<empty>':
+ x_produces_empty = 1
+ else:
+ if f not in result: result.append(f)
+
+ if x_produces_empty:
+ # We have to consider the next x in beta,
+ # i.e. stay in the loop.
+ pass
+ else:
+ # We don't have to consider any further symbols in beta.
+ break
+ else:
+ # There was no 'break' from the loop,
+ # so x_produces_empty was true for all x in beta,
+ # so beta produces empty as well.
+ result.append('<empty>')
+
+ return result
+
+ # -------------------------------------------------------------------------
+ # compute_first()
+ #
+ # Compute the value of FIRST1(X) for all symbols
+ # -------------------------------------------------------------------------
+ def compute_first(self):
+ if self.First:
+ return self.First
+
+ # Terminals:
+ for t in self.Terminals:
+ self.First[t] = [t]
+
+ self.First['$end'] = ['$end']
+
+ # Nonterminals:
+
+ # Initialize to the empty set:
+ for n in self.Nonterminals:
+ self.First[n] = []
+
+ # Then propagate symbols until no change:
+ while 1:
+ some_change = 0
+ for n in self.Nonterminals:
+ for p in self.Prodnames[n]:
+ for f in self._first(p.prod):
+ if f not in self.First[n]:
+ self.First[n].append( f )
+ some_change = 1
+ if not some_change:
+ break
+
+ return self.First
+
+ # ---------------------------------------------------------------------
+ # compute_follow()
+ #
+ # Computes all of the follow sets for every non-terminal symbol. The
+ # follow set is the set of all symbols that might follow a given
+ # non-terminal. See the Dragon book, 2nd Ed. p. 189.
+ # ---------------------------------------------------------------------
+ def compute_follow(self,start=None):
+ # If already computed, return the result
+ if self.Follow:
+ return self.Follow
+
+ # If first sets not computed yet, do that first.
+ if not self.First:
+ self.compute_first()
+
+ # Add '$end' to the follow list of the start symbol
+ for k in self.Nonterminals:
+ self.Follow[k] = [ ]
+
+ if not start:
+ start = self.Productions[1].name
+
+ self.Follow[start] = [ '$end' ]
+
+ while 1:
+ didadd = 0
+ for p in self.Productions[1:]:
+ # Here is the production set
+ for i in range(len(p.prod)):
+ B = p.prod[i]
+ if B in self.Nonterminals:
+ # Okay. We got a non-terminal in a production
+ fst = self._first(p.prod[i+1:])
+ hasempty = 0
+ for f in fst:
+ if f != '<empty>' and f not in self.Follow[B]:
+ self.Follow[B].append(f)
+ didadd = 1
+ if f == '<empty>':
+ hasempty = 1
+ if hasempty or i == (len(p.prod)-1):
+ # Add elements of follow(a) to follow(b)
+ for f in self.Follow[p.name]:
+ if f not in self.Follow[B]:
+ self.Follow[B].append(f)
+ didadd = 1
+ if not didadd: break
+ return self.Follow
+
+
+ # -----------------------------------------------------------------------------
+ # build_lritems()
+ #
+ # This function walks the list of productions and builds a complete set of the
+ # LR items. The LR items are stored in two ways: First, they are uniquely
+ # numbered and placed in the list _lritems. Second, a linked list of LR items
+ # is built for each production. For example:
+ #
+ # E -> E PLUS E
+ #
+ # Creates the list
+ #
+ # [E -> . E PLUS E, E -> E . PLUS E, E -> E PLUS . E, E -> E PLUS E . ]
+ # -----------------------------------------------------------------------------
+
+ def build_lritems(self):
+ for p in self.Productions:
+ lastlri = p
+ i = 0
+ lr_items = []
+ while 1:
+ if i > len(p):
+ lri = None
+ else:
+ lri = LRItem(p,i)
+ # Precompute the list of productions immediately following
+ try:
+ lri.lr_after = self.Prodnames[lri.prod[i+1]]
+ except (IndexError,KeyError):
+ lri.lr_after = []
+ try:
+ lri.lr_before = lri.prod[i-1]
+ except IndexError:
+ lri.lr_before = None
+
+ lastlri.lr_next = lri
+ if not lri: break
+ lr_items.append(lri)
+ lastlri = lri
+ i += 1
+ p.lr_items = lr_items
+
+# -----------------------------------------------------------------------------
+# == Class LRTable ==
+#
+# This basic class represents a basic table of LR parsing information.
+# Methods for generating the tables are not defined here. They are defined
+# in the derived class LRGeneratedTable.
+# -----------------------------------------------------------------------------
+
+class VersionError(YaccError): pass
+
+class LRTable(object):
+ def __init__(self):
+ self.lr_action = None
+ self.lr_goto = None
+ self.lr_productions = None
+ self.lr_method = None
+
+ def read_table(self,module):
+ if isinstance(module,types.ModuleType):
+ parsetab = module
+ else:
+ if sys.version_info[0] < 3:
+ exec("import %s as parsetab" % module)
+ else:
+ env = { }
+ exec("import %s as parsetab" % module, env, env)
+ parsetab = env['parsetab']
+
+ if parsetab._tabversion != __tabversion__:
+ raise VersionError("yacc table file version is out of date")
+
+ self.lr_action = parsetab._lr_action
+ self.lr_goto = parsetab._lr_goto
+
+ self.lr_productions = []
+ for p in parsetab._lr_productions:
+ self.lr_productions.append(MiniProduction(*p))
+
+ self.lr_method = parsetab._lr_method
+ return parsetab._lr_signature
+
+ def read_pickle(self,filename):
+ try:
+ import cPickle as pickle
+ except ImportError:
+ import pickle
+
+ in_f = open(filename,"rb")
+
+ tabversion = pickle.load(in_f)
+ if tabversion != __tabversion__:
+ raise VersionError("yacc table file version is out of date")
+ self.lr_method = pickle.load(in_f)
+ signature = pickle.load(in_f)
+ self.lr_action = pickle.load(in_f)
+ self.lr_goto = pickle.load(in_f)
+ productions = pickle.load(in_f)
+
+ self.lr_productions = []
+ for p in productions:
+ self.lr_productions.append(MiniProduction(*p))
+
+ in_f.close()
+ return signature
+
+ # Bind all production function names to callable objects in pdict
+ def bind_callables(self,pdict):
+ for p in self.lr_productions:
+ p.bind(pdict)
+
+# -----------------------------------------------------------------------------
+# === LR Generator ===
+#
+# The following classes and functions are used to generate LR parsing tables on
+# a grammar.
+# -----------------------------------------------------------------------------
+
+# -----------------------------------------------------------------------------
+# digraph()
+# traverse()
+#
+# The following two functions are used to compute set valued functions
+# of the form:
+#
+# F(x) = F'(x) U U{F(y) | x R y}
+#
+# This is used to compute the values of Read() sets as well as FOLLOW sets
+# in LALR(1) generation.
+#
+# Inputs: X - An input set
+# R - A relation
+# FP - Set-valued function
+# ------------------------------------------------------------------------------
+
+def digraph(X,R,FP):
+ N = { }
+ for x in X:
+ N[x] = 0
+ stack = []
+ F = { }
+ for x in X:
+ if N[x] == 0: traverse(x,N,stack,F,X,R,FP)
+ return F
+
+def traverse(x,N,stack,F,X,R,FP):
+ stack.append(x)
+ d = len(stack)
+ N[x] = d
+ F[x] = FP(x) # F(X) <- F'(x)
+
+ rel = R(x) # Get y's related to x
+ for y in rel:
+ if N[y] == 0:
+ traverse(y,N,stack,F,X,R,FP)
+ N[x] = min(N[x],N[y])
+ for a in F.get(y,[]):
+ if a not in F[x]: F[x].append(a)
+ if N[x] == d:
+ N[stack[-1]] = MAXINT
+ F[stack[-1]] = F[x]
+ element = stack.pop()
+ while element != x:
+ N[stack[-1]] = MAXINT
+ F[stack[-1]] = F[x]
+ element = stack.pop()
+
+class LALRError(YaccError): pass
+
+# -----------------------------------------------------------------------------
+# == LRGeneratedTable ==
+#
+# This class implements the LR table generation algorithm. There are no
+# public methods except for write()
+# -----------------------------------------------------------------------------
+
+class LRGeneratedTable(LRTable):
+ def __init__(self,grammar,method='LALR',log=None):
+ if method not in ['SLR','LALR']:
+ raise LALRError("Unsupported method %s" % method)
+
+ self.grammar = grammar
+ self.lr_method = method
+
+ # Set up the logger
+ if not log:
+ log = NullLogger()
+ self.log = log
+
+ # Internal attributes
+ self.lr_action = {} # Action table
+ self.lr_goto = {} # Goto table
+ self.lr_productions = grammar.Productions # Copy of grammar Production array
+ self.lr_goto_cache = {} # Cache of computed gotos
+ self.lr0_cidhash = {} # Cache of closures
+
+ self._add_count = 0 # Internal counter used to detect cycles
+
+ # Diagonistic information filled in by the table generator
+ self.sr_conflict = 0
+ self.rr_conflict = 0
+ self.conflicts = [] # List of conflicts
+
+ self.sr_conflicts = []
+ self.rr_conflicts = []
+
+ # Build the tables
+ self.grammar.build_lritems()
+ self.grammar.compute_first()
+ self.grammar.compute_follow()
+ self.lr_parse_table()
+
+ # Compute the LR(0) closure operation on I, where I is a set of LR(0) items.
+
+ def lr0_closure(self,I):
+ self._add_count += 1
+
+ # Add everything in I to J
+ J = I[:]
+ didadd = 1
+ while didadd:
+ didadd = 0
+ for j in J:
+ for x in j.lr_after:
+ if getattr(x,"lr0_added",0) == self._add_count: continue
+ # Add B --> .G to J
+ J.append(x.lr_next)
+ x.lr0_added = self._add_count
+ didadd = 1
+
+ return J
+
+ # Compute the LR(0) goto function goto(I,X) where I is a set
+ # of LR(0) items and X is a grammar symbol. This function is written
+ # in a way that guarantees uniqueness of the generated goto sets
+ # (i.e. the same goto set will never be returned as two different Python
+ # objects). With uniqueness, we can later do fast set comparisons using
+ # id(obj) instead of element-wise comparison.
+
+ def lr0_goto(self,I,x):
+ # First we look for a previously cached entry
+ g = self.lr_goto_cache.get((id(I),x),None)
+ if g: return g
+
+ # Now we generate the goto set in a way that guarantees uniqueness
+ # of the result
+
+ s = self.lr_goto_cache.get(x,None)
+ if not s:
+ s = { }
+ self.lr_goto_cache[x] = s
+
+ gs = [ ]
+ for p in I:
+ n = p.lr_next
+ if n and n.lr_before == x:
+ s1 = s.get(id(n),None)
+ if not s1:
+ s1 = { }
+ s[id(n)] = s1
+ gs.append(n)
+ s = s1
+ g = s.get('$end',None)
+ if not g:
+ if gs:
+ g = self.lr0_closure(gs)
+ s['$end'] = g
+ else:
+ s['$end'] = gs
+ self.lr_goto_cache[(id(I),x)] = g
+ return g
+
+ # Compute the LR(0) sets of item function
+ def lr0_items(self):
+
+ C = [ self.lr0_closure([self.grammar.Productions[0].lr_next]) ]
+ i = 0
+ for I in C:
+ self.lr0_cidhash[id(I)] = i
+ i += 1
+
+ # Loop over the items in C and each grammar symbols
+ i = 0
+ while i < len(C):
+ I = C[i]
+ i += 1
+
+ # Collect all of the symbols that could possibly be in the goto(I,X) sets
+ asyms = { }
+ for ii in I:
+ for s in ii.usyms:
+ asyms[s] = None
+
+ for x in asyms:
+ g = self.lr0_goto(I,x)
+ if not g: continue
+ if id(g) in self.lr0_cidhash: continue
+ self.lr0_cidhash[id(g)] = len(C)
+ C.append(g)
+
+ return C
+
+ # -----------------------------------------------------------------------------
+ # ==== LALR(1) Parsing ====
+ #
+ # LALR(1) parsing is almost exactly the same as SLR except that instead of
+ # relying upon Follow() sets when performing reductions, a more selective
+ # lookahead set that incorporates the state of the LR(0) machine is utilized.
+ # Thus, we mainly just have to focus on calculating the lookahead sets.
+ #
+ # The method used here is due to DeRemer and Pennelo (1982).
+ #
+ # DeRemer, F. L., and T. J. Pennelo: "Efficient Computation of LALR(1)
+ # Lookahead Sets", ACM Transactions on Programming Languages and Systems,
+ # Vol. 4, No. 4, Oct. 1982, pp. 615-649
+ #
+ # Further details can also be found in:
+ #
+ # J. Tremblay and P. Sorenson, "The Theory and Practice of Compiler Writing",
+ # McGraw-Hill Book Company, (1985).
+ #
+ # -----------------------------------------------------------------------------
+
+ # -----------------------------------------------------------------------------
+ # compute_nullable_nonterminals()
+ #
+ # Creates a dictionary containing all of the non-terminals that might produce
+ # an empty production.
+ # -----------------------------------------------------------------------------
+
+ def compute_nullable_nonterminals(self):
+ nullable = {}
+ num_nullable = 0
+ while 1:
+ for p in self.grammar.Productions[1:]:
+ if p.len == 0:
+ nullable[p.name] = 1
+ continue
+ for t in p.prod:
+ if not t in nullable: break
+ else:
+ nullable[p.name] = 1
+ if len(nullable) == num_nullable: break
+ num_nullable = len(nullable)
+ return nullable
+
+ # -----------------------------------------------------------------------------
+ # find_nonterminal_trans(C)
+ #
+ # Given a set of LR(0) items, this functions finds all of the non-terminal
+ # transitions. These are transitions in which a dot appears immediately before
+ # a non-terminal. Returns a list of tuples of the form (state,N) where state
+ # is the state number and N is the nonterminal symbol.
+ #
+ # The input C is the set of LR(0) items.
+ # -----------------------------------------------------------------------------
+
+ def find_nonterminal_transitions(self,C):
+ trans = []
+ for state in range(len(C)):
+ for p in C[state]:
+ if p.lr_index < p.len - 1:
+ t = (state,p.prod[p.lr_index+1])
+ if t[1] in self.grammar.Nonterminals:
+ if t not in trans: trans.append(t)
+ state = state + 1
+ return trans
+
+ # -----------------------------------------------------------------------------
+ # dr_relation()
+ #
+ # Computes the DR(p,A) relationships for non-terminal transitions. The input
+ # is a tuple (state,N) where state is a number and N is a nonterminal symbol.
+ #
+ # Returns a list of terminals.
+ # -----------------------------------------------------------------------------
+
+ def dr_relation(self,C,trans,nullable):
+ dr_set = { }
+ state,N = trans
+ terms = []
+
+ g = self.lr0_goto(C[state],N)
+ for p in g:
+ if p.lr_index < p.len - 1:
+ a = p.prod[p.lr_index+1]
+ if a in self.grammar.Terminals:
+ if a not in terms: terms.append(a)
+
+ # This extra bit is to handle the start state
+ if state == 0 and N == self.grammar.Productions[0].prod[0]:
+ terms.append('$end')
+
+ return terms
+
+ # -----------------------------------------------------------------------------
+ # reads_relation()
+ #
+ # Computes the READS() relation (p,A) READS (t,C).
+ # -----------------------------------------------------------------------------
+
+ def reads_relation(self,C, trans, empty):
+ # Look for empty transitions
+ rel = []
+ state, N = trans
+
+ g = self.lr0_goto(C[state],N)
+ j = self.lr0_cidhash.get(id(g),-1)
+ for p in g:
+ if p.lr_index < p.len - 1:
+ a = p.prod[p.lr_index + 1]
+ if a in empty:
+ rel.append((j,a))
+
+ return rel
+
+ # -----------------------------------------------------------------------------
+ # compute_lookback_includes()
+ #
+ # Determines the lookback and includes relations
+ #
+ # LOOKBACK:
+ #
+ # This relation is determined by running the LR(0) state machine forward.
+ # For example, starting with a production "N : . A B C", we run it forward
+ # to obtain "N : A B C ." We then build a relationship between this final
+ # state and the starting state. These relationships are stored in a dictionary
+ # lookdict.
+ #
+ # INCLUDES:
+ #
+ # Computes the INCLUDE() relation (p,A) INCLUDES (p',B).
+ #
+ # This relation is used to determine non-terminal transitions that occur
+ # inside of other non-terminal transition states. (p,A) INCLUDES (p', B)
+ # if the following holds:
+ #
+ # B -> LAT, where T -> epsilon and p' -L-> p
+ #
+ # L is essentially a prefix (which may be empty), T is a suffix that must be
+ # able to derive an empty string. State p' must lead to state p with the string L.
+ #
+ # -----------------------------------------------------------------------------
+
+ def compute_lookback_includes(self,C,trans,nullable):
+
+ lookdict = {} # Dictionary of lookback relations
+ includedict = {} # Dictionary of include relations
+
+ # Make a dictionary of non-terminal transitions
+ dtrans = {}
+ for t in trans:
+ dtrans[t] = 1
+
+ # Loop over all transitions and compute lookbacks and includes
+ for state,N in trans:
+ lookb = []
+ includes = []
+ for p in C[state]:
+ if p.name != N: continue
+
+ # Okay, we have a name match. We now follow the production all the way
+ # through the state machine until we get the . on the right hand side
+
+ lr_index = p.lr_index
+ j = state
+ while lr_index < p.len - 1:
+ lr_index = lr_index + 1
+ t = p.prod[lr_index]
+
+ # Check to see if this symbol and state are a non-terminal transition
+ if (j,t) in dtrans:
+ # Yes. Okay, there is some chance that this is an includes relation
+ # the only way to know for certain is whether the rest of the
+ # production derives empty
+
+ li = lr_index + 1
+ while li < p.len:
+ if p.prod[li] in self.grammar.Terminals: break # No forget it
+ if not p.prod[li] in nullable: break
+ li = li + 1
+ else:
+ # Appears to be a relation between (j,t) and (state,N)
+ includes.append((j,t))
+
+ g = self.lr0_goto(C[j],t) # Go to next set
+ j = self.lr0_cidhash.get(id(g),-1) # Go to next state
+
+ # When we get here, j is the final state, now we have to locate the production
+ for r in C[j]:
+ if r.name != p.name: continue
+ if r.len != p.len: continue
+ i = 0
+ # This look is comparing a production ". A B C" with "A B C ."
+ while i < r.lr_index:
+ if r.prod[i] != p.prod[i+1]: break
+ i = i + 1
+ else:
+ lookb.append((j,r))
+ for i in includes:
+ if not i in includedict: includedict[i] = []
+ includedict[i].append((state,N))
+ lookdict[(state,N)] = lookb
+
+ return lookdict,includedict
+
+ # -----------------------------------------------------------------------------
+ # compute_read_sets()
+ #
+ # Given a set of LR(0) items, this function computes the read sets.
+ #
+ # Inputs: C = Set of LR(0) items
+ # ntrans = Set of nonterminal transitions
+ # nullable = Set of empty transitions
+ #
+ # Returns a set containing the read sets
+ # -----------------------------------------------------------------------------
+
+ def compute_read_sets(self,C, ntrans, nullable):
+ FP = lambda x: self.dr_relation(C,x,nullable)
+ R = lambda x: self.reads_relation(C,x,nullable)
+ F = digraph(ntrans,R,FP)
+ return F
+
+ # -----------------------------------------------------------------------------
+ # compute_follow_sets()
+ #
+ # Given a set of LR(0) items, a set of non-terminal transitions, a readset,
+ # and an include set, this function computes the follow sets
+ #
+ # Follow(p,A) = Read(p,A) U U {Follow(p',B) | (p,A) INCLUDES (p',B)}
+ #
+ # Inputs:
+ # ntrans = Set of nonterminal transitions
+ # readsets = Readset (previously computed)
+ # inclsets = Include sets (previously computed)
+ #
+ # Returns a set containing the follow sets
+ # -----------------------------------------------------------------------------
+
+ def compute_follow_sets(self,ntrans,readsets,inclsets):
+ FP = lambda x: readsets[x]
+ R = lambda x: inclsets.get(x,[])
+ F = digraph(ntrans,R,FP)
+ return F
+
+ # -----------------------------------------------------------------------------
+ # add_lookaheads()
+ #
+ # Attaches the lookahead symbols to grammar rules.
+ #
+ # Inputs: lookbacks - Set of lookback relations
+ # followset - Computed follow set
+ #
+ # This function directly attaches the lookaheads to productions contained
+ # in the lookbacks set
+ # -----------------------------------------------------------------------------
+
+ def add_lookaheads(self,lookbacks,followset):
+ for trans,lb in lookbacks.items():
+ # Loop over productions in lookback
+ for state,p in lb:
+ if not state in p.lookaheads:
+ p.lookaheads[state] = []
+ f = followset.get(trans,[])
+ for a in f:
+ if a not in p.lookaheads[state]: p.lookaheads[state].append(a)
+
+ # -----------------------------------------------------------------------------
+ # add_lalr_lookaheads()
+ #
+ # This function does all of the work of adding lookahead information for use
+ # with LALR parsing
+ # -----------------------------------------------------------------------------
+
+ def add_lalr_lookaheads(self,C):
+ # Determine all of the nullable nonterminals
+ nullable = self.compute_nullable_nonterminals()
+
+ # Find all non-terminal transitions
+ trans = self.find_nonterminal_transitions(C)
+
+ # Compute read sets
+ readsets = self.compute_read_sets(C,trans,nullable)
+
+ # Compute lookback/includes relations
+ lookd, included = self.compute_lookback_includes(C,trans,nullable)
+
+ # Compute LALR FOLLOW sets
+ followsets = self.compute_follow_sets(trans,readsets,included)
+
+ # Add all of the lookaheads
+ self.add_lookaheads(lookd,followsets)
+
+ # -----------------------------------------------------------------------------
+ # lr_parse_table()
+ #
+ # This function constructs the parse tables for SLR or LALR
+ # -----------------------------------------------------------------------------
+ def lr_parse_table(self):
+ Productions = self.grammar.Productions
+ Precedence = self.grammar.Precedence
+ goto = self.lr_goto # Goto array
+ action = self.lr_action # Action array
+ log = self.log # Logger for output
+
+ actionp = { } # Action production array (temporary)
+
+ log.info("Parsing method: %s", self.lr_method)
+
+ # Step 1: Construct C = { I0, I1, ... IN}, collection of LR(0) items
+ # This determines the number of states
+
+ C = self.lr0_items()
+
+ if self.lr_method == 'LALR':
+ self.add_lalr_lookaheads(C)
+
+ # Build the parser table, state by state
+ st = 0
+ for I in C:
+ # Loop over each production in I
+ actlist = [ ] # List of actions
+ st_action = { }
+ st_actionp = { }
+ st_goto = { }
+ log.info("")
+ log.info("state %d", st)
+ log.info("")
+ for p in I:
+ log.info(" (%d) %s", p.number, str(p))
+ log.info("")
+
+ for p in I:
+ if p.len == p.lr_index + 1:
+ if p.name == "S'":
+ # Start symbol. Accept!
+ st_action["$end"] = 0
+ st_actionp["$end"] = p
+ else:
+ # We are at the end of a production. Reduce!
+ if self.lr_method == 'LALR':
+ laheads = p.lookaheads[st]
+ else:
+ laheads = self.grammar.Follow[p.name]
+ for a in laheads:
+ actlist.append((a,p,"reduce using rule %d (%s)" % (p.number,p)))
+ r = st_action.get(a,None)
+ if r is not None:
+ # Whoa. Have a shift/reduce or reduce/reduce conflict
+ if r > 0:
+ # Need to decide on shift or reduce here
+ # By default we favor shifting. Need to add
+ # some precedence rules here.
+ sprec,slevel = Productions[st_actionp[a].number].prec
+ rprec,rlevel = Precedence.get(a,('right',0))
+ if (slevel < rlevel) or ((slevel == rlevel) and (rprec == 'left')):
+ # We really need to reduce here.
+ st_action[a] = -p.number
+ st_actionp[a] = p
+ if not slevel and not rlevel:
+ log.info(" ! shift/reduce conflict for %s resolved as reduce",a)
+ self.sr_conflicts.append((st,a,'reduce'))
+ Productions[p.number].reduced += 1
+ elif (slevel == rlevel) and (rprec == 'nonassoc'):
+ st_action[a] = None
+ else:
+ # Hmmm. Guess we'll keep the shift
+ if not rlevel:
+ log.info(" ! shift/reduce conflict for %s resolved as shift",a)
+ self.sr_conflicts.append((st,a,'shift'))
+ elif r < 0:
+ # Reduce/reduce conflict. In this case, we favor the rule
+ # that was defined first in the grammar file
+ oldp = Productions[-r]
+ pp = Productions[p.number]
+ if oldp.line > pp.line:
+ st_action[a] = -p.number
+ st_actionp[a] = p
+ chosenp,rejectp = pp,oldp
+ Productions[p.number].reduced += 1
+ Productions[oldp.number].reduced -= 1
+ else:
+ chosenp,rejectp = oldp,pp
+ self.rr_conflicts.append((st,chosenp,rejectp))
+ log.info(" ! reduce/reduce conflict for %s resolved using rule %d (%s)", a,st_actionp[a].number, st_actionp[a])
+ else:
+ raise LALRError("Unknown conflict in state %d" % st)
+ else:
+ st_action[a] = -p.number
+ st_actionp[a] = p
+ Productions[p.number].reduced += 1
+ else:
+ i = p.lr_index
+ a = p.prod[i+1] # Get symbol right after the "."
+ if a in self.grammar.Terminals:
+ g = self.lr0_goto(I,a)
+ j = self.lr0_cidhash.get(id(g),-1)
+ if j >= 0:
+ # We are in a shift state
+ actlist.append((a,p,"shift and go to state %d" % j))
+ r = st_action.get(a,None)
+ if r is not None:
+ # Whoa have a shift/reduce or shift/shift conflict
+ if r > 0:
+ if r != j:
+ raise LALRError("Shift/shift conflict in state %d" % st)
+ elif r < 0:
+ # Do a precedence check.
+ # - if precedence of reduce rule is higher, we reduce.
+ # - if precedence of reduce is same and left assoc, we reduce.
+ # - otherwise we shift
+ rprec,rlevel = Productions[st_actionp[a].number].prec
+ sprec,slevel = Precedence.get(a,('right',0))
+ if (slevel > rlevel) or ((slevel == rlevel) and (rprec == 'right')):
+ # We decide to shift here... highest precedence to shift
+ Productions[st_actionp[a].number].reduced -= 1
+ st_action[a] = j
+ st_actionp[a] = p
+ if not rlevel:
+ log.info(" ! shift/reduce conflict for %s resolved as shift",a)
+ self.sr_conflicts.append((st,a,'shift'))
+ elif (slevel == rlevel) and (rprec == 'nonassoc'):
+ st_action[a] = None
+ else:
+ # Hmmm. Guess we'll keep the reduce
+ if not slevel and not rlevel:
+ log.info(" ! shift/reduce conflict for %s resolved as reduce",a)
+ self.sr_conflicts.append((st,a,'reduce'))
+
+ else:
+ raise LALRError("Unknown conflict in state %d" % st)
+ else:
+ st_action[a] = j
+ st_actionp[a] = p
+
+ # Print the actions associated with each terminal
+ _actprint = { }
+ for a,p,m in actlist:
+ if a in st_action:
+ if p is st_actionp[a]:
+ log.info(" %-15s %s",a,m)
+ _actprint[(a,m)] = 1
+ log.info("")
+ # Print the actions that were not used. (debugging)
+ not_used = 0
+ for a,p,m in actlist:
+ if a in st_action:
+ if p is not st_actionp[a]:
+ if not (a,m) in _actprint:
+ log.debug(" ! %-15s [ %s ]",a,m)
+ not_used = 1
+ _actprint[(a,m)] = 1
+ if not_used:
+ log.debug("")
+
+ # Construct the goto table for this state
+
+ nkeys = { }
+ for ii in I:
+ for s in ii.usyms:
+ if s in self.grammar.Nonterminals:
+ nkeys[s] = None
+ for n in nkeys:
+ g = self.lr0_goto(I,n)
+ j = self.lr0_cidhash.get(id(g),-1)
+ if j >= 0:
+ st_goto[n] = j
+ log.info(" %-30s shift and go to state %d",n,j)
+
+ action[st] = st_action
+ actionp[st] = st_actionp
+ goto[st] = st_goto
+ st += 1
+
+
+ # -----------------------------------------------------------------------------
+ # write()
+ #
+ # This function writes the LR parsing tables to a file
+ # -----------------------------------------------------------------------------
+
+ def write_table(self,modulename,outputdir='',signature=""):
+ basemodulename = modulename.split(".")[-1]
+ filename = os.path.join(outputdir,basemodulename) + ".py"
+ try:
+ f = open(filename,"w")
+
+ f.write("""
+# %s
+# This file is automatically generated. Do not edit.
+_tabversion = %r
+
+_lr_method = %r
+
+_lr_signature = %r
+ """ % (filename, __tabversion__, self.lr_method, signature))
+
+ # Change smaller to 0 to go back to original tables
+ smaller = 1
+
+ # Factor out names to try and make smaller
+ if smaller:
+ items = { }
+
+ for s,nd in self.lr_action.items():
+ for name,v in nd.items():
+ i = items.get(name)
+ if not i:
+ i = ([],[])
+ items[name] = i
+ i[0].append(s)
+ i[1].append(v)
+
+ f.write("\n_lr_action_items = {")
+ for k,v in items.items():
+ f.write("%r:([" % k)
+ for i in v[0]:
+ f.write("%r," % i)
+ f.write("],[")
+ for i in v[1]:
+ f.write("%r," % i)
+
+ f.write("]),")
+ f.write("}\n")
+
+ f.write("""
+_lr_action = { }
+for _k, _v in _lr_action_items.items():
+ for _x,_y in zip(_v[0],_v[1]):
+ if not _x in _lr_action: _lr_action[_x] = { }
+ _lr_action[_x][_k] = _y
+del _lr_action_items
+""")
+
+ else:
+ f.write("\n_lr_action = { ");
+ for k,v in self.lr_action.items():
+ f.write("(%r,%r):%r," % (k[0],k[1],v))
+ f.write("}\n");
+
+ if smaller:
+ # Factor out names to try and make smaller
+ items = { }
+
+ for s,nd in self.lr_goto.items():
+ for name,v in nd.items():
+ i = items.get(name)
+ if not i:
+ i = ([],[])
+ items[name] = i
+ i[0].append(s)
+ i[1].append(v)
+
+ f.write("\n_lr_goto_items = {")
+ for k,v in items.items():
+ f.write("%r:([" % k)
+ for i in v[0]:
+ f.write("%r," % i)
+ f.write("],[")
+ for i in v[1]:
+ f.write("%r," % i)
+
+ f.write("]),")
+ f.write("}\n")
+
+ f.write("""
+_lr_goto = { }
+for _k, _v in _lr_goto_items.items():
+ for _x,_y in zip(_v[0],_v[1]):
+ if not _x in _lr_goto: _lr_goto[_x] = { }
+ _lr_goto[_x][_k] = _y
+del _lr_goto_items
+""")
+ else:
+ f.write("\n_lr_goto = { ");
+ for k,v in self.lr_goto.items():
+ f.write("(%r,%r):%r," % (k[0],k[1],v))
+ f.write("}\n");
+
+ # Write production table
+ f.write("_lr_productions = [\n")
+ for p in self.lr_productions:
+ if p.func:
+ f.write(" (%r,%r,%d,%r,%r,%d),\n" % (p.str,p.name, p.len, p.func,p.file,p.line))
+ else:
+ f.write(" (%r,%r,%d,None,None,None),\n" % (str(p),p.name, p.len))
+ f.write("]\n")
+ f.close()
+
+ except IOError:
+ e = sys.exc_info()[1]
+ sys.stderr.write("Unable to create '%s'\n" % filename)
+ sys.stderr.write(str(e)+"\n")
+ return
+
+
+ # -----------------------------------------------------------------------------
+ # pickle_table()
+ #
+ # This function pickles the LR parsing tables to a supplied file object
+ # -----------------------------------------------------------------------------
+
+ def pickle_table(self,filename,signature=""):
+ try:
+ import cPickle as pickle
+ except ImportError:
+ import pickle
+ outf = open(filename,"wb")
+ pickle.dump(__tabversion__,outf,pickle_protocol)
+ pickle.dump(self.lr_method,outf,pickle_protocol)
+ pickle.dump(signature,outf,pickle_protocol)
+ pickle.dump(self.lr_action,outf,pickle_protocol)
+ pickle.dump(self.lr_goto,outf,pickle_protocol)
+
+ outp = []
+ for p in self.lr_productions:
+ if p.func:
+ outp.append((p.str,p.name, p.len, p.func,p.file,p.line))
+ else:
+ outp.append((str(p),p.name,p.len,None,None,None))
+ pickle.dump(outp,outf,pickle_protocol)
+ outf.close()
+
+# -----------------------------------------------------------------------------
+# === INTROSPECTION ===
+#
+# The following functions and classes are used to implement the PLY
+# introspection features followed by the yacc() function itself.
+# -----------------------------------------------------------------------------
+
+# -----------------------------------------------------------------------------
+# get_caller_module_dict()
+#
+# This function returns a dictionary containing all of the symbols defined within
+# a caller further down the call stack. This is used to get the environment
+# associated with the yacc() call if none was provided.
+# -----------------------------------------------------------------------------
+
+def get_caller_module_dict(levels):
+ try:
+ raise RuntimeError
+ except RuntimeError:
+ e,b,t = sys.exc_info()
+ f = t.tb_frame
+ while levels > 0:
+ f = f.f_back
+ levels -= 1
+ ldict = f.f_globals.copy()
+ if f.f_globals != f.f_locals:
+ ldict.update(f.f_locals)
+
+ return ldict
+
+# -----------------------------------------------------------------------------
+# parse_grammar()
+#
+# This takes a raw grammar rule string and parses it into production data
+# -----------------------------------------------------------------------------
+def parse_grammar(doc,file,line):
+ grammar = []
+ # Split the doc string into lines
+ pstrings = doc.splitlines()
+ lastp = None
+ dline = line
+ for ps in pstrings:
+ dline += 1
+ p = ps.split()
+ if not p: continue
+ try:
+ if p[0] == '|':
+ # This is a continuation of a previous rule
+ if not lastp:
+ raise SyntaxError("%s:%d: Misplaced '|'" % (file,dline))
+ prodname = lastp
+ syms = p[1:]
+ else:
+ prodname = p[0]
+ lastp = prodname
+ syms = p[2:]
+ assign = p[1]
+ if assign != ':' and assign != '::=':
+ raise SyntaxError("%s:%d: Syntax error. Expected ':'" % (file,dline))
+
+ grammar.append((file,dline,prodname,syms))
+ except SyntaxError:
+ raise
+ except Exception:
+ raise SyntaxError("%s:%d: Syntax error in rule '%s'" % (file,dline,ps.strip()))
+
+ return grammar
+
+# -----------------------------------------------------------------------------
+# ParserReflect()
+#
+# This class represents information extracted for building a parser including
+# start symbol, error function, tokens, precedence list, action functions,
+# etc.
+# -----------------------------------------------------------------------------
+class ParserReflect(object):
+ def __init__(self,pdict,log=None):
+ self.pdict = pdict
+ self.start = None
+ self.error_func = None
+ self.tokens = None
+ self.files = {}
+ self.grammar = []
+ self.error = 0
+
+ if log is None:
+ self.log = PlyLogger(sys.stderr)
+ else:
+ self.log = log
+
+ # Get all of the basic information
+ def get_all(self):
+ self.get_start()
+ self.get_error_func()
+ self.get_tokens()
+ self.get_precedence()
+ self.get_pfunctions()
+
+ # Validate all of the information
+ def validate_all(self):
+ self.validate_start()
+ self.validate_error_func()
+ self.validate_tokens()
+ self.validate_precedence()
+ self.validate_pfunctions()
+ self.validate_files()
+ return self.error
+
+ # Compute a signature over the grammar
+ def signature(self):
+ try:
+ from hashlib import md5
+ except ImportError:
+ from md5 import md5
+ try:
+ sig = md5()
+ if self.start:
+ sig.update(self.start.encode('latin-1'))
+ if self.prec:
+ sig.update("".join(["".join(p) for p in self.prec]).encode('latin-1'))
+ if self.tokens:
+ sig.update(" ".join(self.tokens).encode('latin-1'))
+ for f in self.pfuncs:
+ if f[3]:
+ sig.update(f[3].encode('latin-1'))
+ except (TypeError,ValueError):
+ pass
+ return sig.digest()
+
+ # -----------------------------------------------------------------------------
+ # validate_file()
+ #
+ # This method checks to see if there are duplicated p_rulename() functions
+ # in the parser module file. Without this function, it is really easy for
+ # users to make mistakes by cutting and pasting code fragments (and it's a real
+ # bugger to try and figure out why the resulting parser doesn't work). Therefore,
+ # we just do a little regular expression pattern matching of def statements
+ # to try and detect duplicates.
+ # -----------------------------------------------------------------------------
+
+ def validate_files(self):
+ # Match def p_funcname(
+ fre = re.compile(r'\s*def\s+(p_[a-zA-Z_0-9]*)\(')
+
+ for filename in self.files.keys():
+ base,ext = os.path.splitext(filename)
+ if ext != '.py': return 1 # No idea. Assume it's okay.
+
+ try:
+ f = open(filename)
+ lines = f.readlines()
+ f.close()
+ except IOError:
+ continue
+
+ counthash = { }
+ for linen,l in enumerate(lines):
+ linen += 1
+ m = fre.match(l)
+ if m:
+ name = m.group(1)
+ prev = counthash.get(name)
+ if not prev:
+ counthash[name] = linen
+ else:
+ self.log.warning("%s:%d: Function %s redefined. Previously defined on line %d", filename,linen,name,prev)
+
+ # Get the start symbol
+ def get_start(self):
+ self.start = self.pdict.get('start')
+
+ # Validate the start symbol
+ def validate_start(self):
+ if self.start is not None:
+ if not isinstance(self.start,str):
+ self.log.error("'start' must be a string")
+
+ # Look for error handler
+ def get_error_func(self):
+ self.error_func = self.pdict.get('p_error')
+
+ # Validate the error function
+ def validate_error_func(self):
+ if self.error_func:
+ if isinstance(self.error_func,types.FunctionType):
+ ismethod = 0
+ elif isinstance(self.error_func, types.MethodType):
+ ismethod = 1
+ else:
+ self.log.error("'p_error' defined, but is not a function or method")
+ self.error = 1
+ return
+
+ eline = func_code(self.error_func).co_firstlineno
+ efile = func_code(self.error_func).co_filename
+ self.files[efile] = 1
+
+ if (func_code(self.error_func).co_argcount != 1+ismethod):
+ self.log.error("%s:%d: p_error() requires 1 argument",efile,eline)
+ self.error = 1
+
+ # Get the tokens map
+ def get_tokens(self):
+ tokens = self.pdict.get("tokens",None)
+ if not tokens:
+ self.log.error("No token list is defined")
+ self.error = 1
+ return
+
+ if not isinstance(tokens,(list, tuple)):
+ self.log.error("tokens must be a list or tuple")
+ self.error = 1
+ return
+
+ if not tokens:
+ self.log.error("tokens is empty")
+ self.error = 1
+ return
+
+ self.tokens = tokens
+
+ # Validate the tokens
+ def validate_tokens(self):
+ # Validate the tokens.
+ if 'error' in self.tokens:
+ self.log.error("Illegal token name 'error'. Is a reserved word")
+ self.error = 1
+ return
+
+ terminals = {}
+ for n in self.tokens:
+ if n in terminals:
+ self.log.warning("Token '%s' multiply defined", n)
+ terminals[n] = 1
+
+ # Get the precedence map (if any)
+ def get_precedence(self):
+ self.prec = self.pdict.get("precedence",None)
+
+ # Validate and parse the precedence map
+ def validate_precedence(self):
+ preclist = []
+ if self.prec:
+ if not isinstance(self.prec,(list,tuple)):
+ self.log.error("precedence must be a list or tuple")
+ self.error = 1
+ return
+ for level,p in enumerate(self.prec):
+ if not isinstance(p,(list,tuple)):
+ self.log.error("Bad precedence table")
+ self.error = 1
+ return
+
+ if len(p) < 2:
+ self.log.error("Malformed precedence entry %s. Must be (assoc, term, ..., term)",p)
+ self.error = 1
+ return
+ assoc = p[0]
+ if not isinstance(assoc,str):
+ self.log.error("precedence associativity must be a string")
+ self.error = 1
+ return
+ for term in p[1:]:
+ if not isinstance(term,str):
+ self.log.error("precedence items must be strings")
+ self.error = 1
+ return
+ preclist.append((term,assoc,level+1))
+ self.preclist = preclist
+
+ # Get all p_functions from the grammar
+ def get_pfunctions(self):
+ p_functions = []
+ for name, item in self.pdict.items():
+ if name[:2] != 'p_': continue
+ if name == 'p_error': continue
+ if isinstance(item,(types.FunctionType,types.MethodType)):
+ line = func_code(item).co_firstlineno
+ file = func_code(item).co_filename
+ p_functions.append((line,file,name,item.__doc__))
+
+ # Sort all of the actions by line number
+ p_functions.sort()
+ self.pfuncs = p_functions
+
+
+ # Validate all of the p_functions
+ def validate_pfunctions(self):
+ grammar = []
+ # Check for non-empty symbols
+ if len(self.pfuncs) == 0:
+ self.log.error("no rules of the form p_rulename are defined")
+ self.error = 1
+ return
+
+ for line, file, name, doc in self.pfuncs:
+ func = self.pdict[name]
+ if isinstance(func, types.MethodType):
+ reqargs = 2
+ else:
+ reqargs = 1
+ if func_code(func).co_argcount > reqargs:
+ self.log.error("%s:%d: Rule '%s' has too many arguments",file,line,func.__name__)
+ self.error = 1
+ elif func_code(func).co_argcount < reqargs:
+ self.log.error("%s:%d: Rule '%s' requires an argument",file,line,func.__name__)
+ self.error = 1
+ elif not func.__doc__:
+ self.log.warning("%s:%d: No documentation string specified in function '%s' (ignored)",file,line,func.__name__)
+ else:
+ try:
+ parsed_g = parse_grammar(doc,file,line)
+ for g in parsed_g:
+ grammar.append((name, g))
+ except SyntaxError:
+ e = sys.exc_info()[1]
+ self.log.error(str(e))
+ self.error = 1
+
+ # Looks like a valid grammar rule
+ # Mark the file in which defined.
+ self.files[file] = 1
+
+ # Secondary validation step that looks for p_ definitions that are not functions
+ # or functions that look like they might be grammar rules.
+
+ for n,v in self.pdict.items():
+ if n[0:2] == 'p_' and isinstance(v, (types.FunctionType, types.MethodType)): continue
+ if n[0:2] == 't_': continue
+ if n[0:2] == 'p_' and n != 'p_error':
+ self.log.warning("'%s' not defined as a function", n)
+ if ((isinstance(v,types.FunctionType) and func_code(v).co_argcount == 1) or
+ (isinstance(v,types.MethodType) and func_code(v).co_argcount == 2)):
+ try:
+ doc = v.__doc__.split(" ")
+ if doc[1] == ':':
+ self.log.warning("%s:%d: Possible grammar rule '%s' defined without p_ prefix",
+ func_code(v).co_filename, func_code(v).co_firstlineno,n)
+ except Exception:
+ pass
+
+ self.grammar = grammar
+
+# -----------------------------------------------------------------------------
+# yacc(module)
+#
+# Build a parser
+# -----------------------------------------------------------------------------
+
+def yacc(method='LALR', debug=yaccdebug, module=None, tabmodule=tab_module, start=None,
+ check_recursion=1, optimize=0, write_tables=1, debugfile=debug_file,outputdir='',
+ debuglog=None, errorlog = None, picklefile=None):
+
+ global parse # Reference to the parsing method of the last built parser
+
+ # If pickling is enabled, table files are not created
+
+ if picklefile:
+ write_tables = 0
+
+ if errorlog is None:
+ errorlog = PlyLogger(sys.stderr)
+
+ # Get the module dictionary used for the parser
+ if module:
+ _items = [(k,getattr(module,k)) for k in dir(module)]
+ pdict = dict(_items)
+ else:
+ pdict = get_caller_module_dict(2)
+
+ # Collect parser information from the dictionary
+ pinfo = ParserReflect(pdict,log=errorlog)
+ pinfo.get_all()
+
+ if pinfo.error:
+ raise YaccError("Unable to build parser")
+
+ # Check signature against table files (if any)
+ signature = pinfo.signature()
+
+ # Read the tables
+ try:
+ lr = LRTable()
+ if picklefile:
+ read_signature = lr.read_pickle(picklefile)
+ else:
+ read_signature = lr.read_table(tabmodule)
+ if optimize or (read_signature == signature):
+ try:
+ lr.bind_callables(pinfo.pdict)
+ parser = LRParser(lr,pinfo.error_func)
+ parse = parser.parse
+ return parser
+ except Exception:
+ e = sys.exc_info()[1]
+ errorlog.warning("There was a problem loading the table file: %s", repr(e))
+ except VersionError:
+ e = sys.exc_info()
+ errorlog.warning(str(e))
+ except Exception:
+ pass
+
+ if debuglog is None:
+ if debug:
+ debuglog = PlyLogger(open(debugfile,"w"))
+ else:
+ debuglog = NullLogger()
+
+ debuglog.info("Created by PLY version %s (http://www.dabeaz.com/ply)", __version__)
+
+
+ errors = 0
+
+ # Validate the parser information
+ if pinfo.validate_all():
+ raise YaccError("Unable to build parser")
+
+ if not pinfo.error_func:
+ errorlog.warning("no p_error() function is defined")
+
+ # Create a grammar object
+ grammar = Grammar(pinfo.tokens)
+
+ # Set precedence level for terminals
+ for term, assoc, level in pinfo.preclist:
+ try:
+ grammar.set_precedence(term,assoc,level)
+ except GrammarError:
+ e = sys.exc_info()[1]
+ errorlog.warning("%s",str(e))
+
+ # Add productions to the grammar
+ for funcname, gram in pinfo.grammar:
+ file, line, prodname, syms = gram
+ try:
+ grammar.add_production(prodname,syms,funcname,file,line)
+ except GrammarError:
+ e = sys.exc_info()[1]
+ errorlog.error("%s",str(e))
+ errors = 1
+
+ # Set the grammar start symbols
+ try:
+ if start is None:
+ grammar.set_start(pinfo.start)
+ else:
+ grammar.set_start(start)
+ except GrammarError:
+ e = sys.exc_info()[1]
+ errorlog.error(str(e))
+ errors = 1
+
+ if errors:
+ raise YaccError("Unable to build parser")
+
+ # Verify the grammar structure
+ undefined_symbols = grammar.undefined_symbols()
+ for sym, prod in undefined_symbols:
+ errorlog.error("%s:%d: Symbol '%s' used, but not defined as a token or a rule",prod.file,prod.line,sym)
+ errors = 1
+
+ unused_terminals = grammar.unused_terminals()
+ if unused_terminals:
+ debuglog.info("")
+ debuglog.info("Unused terminals:")
+ debuglog.info("")
+ for term in unused_terminals:
+ errorlog.warning("Token '%s' defined, but not used", term)
+ debuglog.info(" %s", term)
+
+ # Print out all productions to the debug log
+ if debug:
+ debuglog.info("")
+ debuglog.info("Grammar")
+ debuglog.info("")
+ for n,p in enumerate(grammar.Productions):
+ debuglog.info("Rule %-5d %s", n, p)
+
+ # Find unused non-terminals
+ unused_rules = grammar.unused_rules()
+ for prod in unused_rules:
+ errorlog.warning("%s:%d: Rule '%s' defined, but not used", prod.file, prod.line, prod.name)
+
+ if len(unused_terminals) == 1:
+ errorlog.warning("There is 1 unused token")
+ if len(unused_terminals) > 1:
+ errorlog.warning("There are %d unused tokens", len(unused_terminals))
+
+ if len(unused_rules) == 1:
+ errorlog.warning("There is 1 unused rule")
+ if len(unused_rules) > 1:
+ errorlog.warning("There are %d unused rules", len(unused_rules))
+
+ if debug:
+ debuglog.info("")
+ debuglog.info("Terminals, with rules where they appear")
+ debuglog.info("")
+ terms = list(grammar.Terminals)
+ terms.sort()
+ for term in terms:
+ debuglog.info("%-20s : %s", term, " ".join([str(s) for s in grammar.Terminals[term]]))
+
+ debuglog.info("")
+ debuglog.info("Nonterminals, with rules where they appear")
+ debuglog.info("")
+ nonterms = list(grammar.Nonterminals)
+ nonterms.sort()
+ for nonterm in nonterms:
+ debuglog.info("%-20s : %s", nonterm, " ".join([str(s) for s in grammar.Nonterminals[nonterm]]))
+ debuglog.info("")
+
+ if check_recursion:
+ unreachable = grammar.find_unreachable()
+ for u in unreachable:
+ errorlog.warning("Symbol '%s' is unreachable",u)
+
+ infinite = grammar.infinite_cycles()
+ for inf in infinite:
+ errorlog.error("Infinite recursion detected for symbol '%s'", inf)
+ errors = 1
+
+ unused_prec = grammar.unused_precedence()
+ for term, assoc in unused_prec:
+ errorlog.error("Precedence rule '%s' defined for unknown symbol '%s'", assoc, term)
+ errors = 1
+
+ if errors:
+ raise YaccError("Unable to build parser")
+
+ # Run the LRGeneratedTable on the grammar
+ if debug:
+ errorlog.debug("Generating %s tables", method)
+
+ lr = LRGeneratedTable(grammar,method,debuglog)
+
+ if debug:
+ num_sr = len(lr.sr_conflicts)
+
+ # Report shift/reduce and reduce/reduce conflicts
+ if num_sr == 1:
+ errorlog.warning("1 shift/reduce conflict")
+ elif num_sr > 1:
+ errorlog.warning("%d shift/reduce conflicts", num_sr)
+
+ num_rr = len(lr.rr_conflicts)
+ if num_rr == 1:
+ errorlog.warning("1 reduce/reduce conflict")
+ elif num_rr > 1:
+ errorlog.warning("%d reduce/reduce conflicts", num_rr)
+
+ # Write out conflicts to the output file
+ if debug and (lr.sr_conflicts or lr.rr_conflicts):
+ debuglog.warning("")
+ debuglog.warning("Conflicts:")
+ debuglog.warning("")
+
+ for state, tok, resolution in lr.sr_conflicts:
+ debuglog.warning("shift/reduce conflict for %s in state %d resolved as %s", tok, state, resolution)
+
+ already_reported = {}
+ for state, rule, rejected in lr.rr_conflicts:
+ if (state,id(rule),id(rejected)) in already_reported:
+ continue
+ debuglog.warning("reduce/reduce conflict in state %d resolved using rule (%s)", state, rule)
+ debuglog.warning("rejected rule (%s) in state %d", rejected,state)
+ errorlog.warning("reduce/reduce conflict in state %d resolved using rule (%s)", state, rule)
+ errorlog.warning("rejected rule (%s) in state %d", rejected, state)
+ already_reported[state,id(rule),id(rejected)] = 1
+
+ warned_never = []
+ for state, rule, rejected in lr.rr_conflicts:
+ if not rejected.reduced and (rejected not in warned_never):
+ debuglog.warning("Rule (%s) is never reduced", rejected)
+ errorlog.warning("Rule (%s) is never reduced", rejected)
+ warned_never.append(rejected)
+
+ # Write the table file if requested
+ if write_tables:
+ lr.write_table(tabmodule,outputdir,signature)
+
+ # Write a pickled version of the tables
+ if picklefile:
+ lr.pickle_table(picklefile,signature)
+
+ # Build the parser
+ lr.bind_callables(pinfo.pdict)
+ parser = LRParser(lr,pinfo.error_func)
+
+ parse = parser.parse
+ return parser
diff --git a/components/script/dom/bindings/codegen/pythonpath.py b/components/script/dom/bindings/codegen/pythonpath.py
new file mode 100644
index 00000000000..49b2d2f740f
--- /dev/null
+++ b/components/script/dom/bindings/codegen/pythonpath.py
@@ -0,0 +1,60 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+"""
+Run a python script, adding extra directories to the python path.
+"""
+
+
+def main(args):
+ def usage():
+ print >>sys.stderr, "pythonpath.py -I directory script.py [args...]"
+ sys.exit(150)
+
+ paths = []
+
+ while True:
+ try:
+ arg = args[0]
+ except IndexError:
+ usage()
+
+ if arg == '-I':
+ args.pop(0)
+ try:
+ path = args.pop(0)
+ except IndexError:
+ usage()
+
+ paths.append(os.path.abspath(path))
+ continue
+
+ if arg.startswith('-I'):
+ paths.append(os.path.abspath(args.pop(0)[2:]))
+ continue
+
+ if arg.startswith('-D'):
+ os.chdir(args.pop(0)[2:])
+ continue
+
+ break
+
+ script = args[0]
+
+ sys.path[0:0] = [os.path.abspath(os.path.dirname(script))] + paths
+ sys.argv = args
+ sys.argc = len(args)
+
+ frozenglobals['__name__'] = '__main__'
+ frozenglobals['__file__'] = script
+
+ execfile(script, frozenglobals)
+
+# Freeze scope here ... why this makes things work I have no idea ...
+frozenglobals = globals()
+
+import sys, os
+
+if __name__ == '__main__':
+ main(sys.argv[1:])
diff --git a/components/script/dom/bindings/codegen/stubgenerator/Skeleton.cpp b/components/script/dom/bindings/codegen/stubgenerator/Skeleton.cpp
new file mode 100644
index 00000000000..dfa17d23400
--- /dev/null
+++ b/components/script/dom/bindings/codegen/stubgenerator/Skeleton.cpp
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "Skeleton.h"
+#include "mozilla/dom/SkeletonBinding.h"
+#include "nsContentUtils.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(Skeleton)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(Skeleton)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(Skeleton)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Skeleton)
+ NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+Skeleton::Skeleton()
+{
+ SetIsDOMBinding();
+}
+
+Skeleton::~Skeleton()
+{
+}
+
+JSObject*
+Skeleton::WrapObject(JSContext* aCx, JSObject* aScope,
+ bool* aTriedToWrap)
+{
+ return SkeletonBinding::Wrap(aCx, aScope, this, aTriedToWrap);
+}
+
+}
+}
+
diff --git a/components/script/dom/bindings/codegen/stubgenerator/Skeleton.h b/components/script/dom/bindings/codegen/stubgenerator/Skeleton.h
new file mode 100644
index 00000000000..286cff9af4a
--- /dev/null
+++ b/components/script/dom/bindings/codegen/stubgenerator/Skeleton.h
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#pragma once
+
+#include "nsWrapperCache.h"
+#include "nsCycleCollectionParticipant.h"
+#include "mozilla/Attributes.h"
+
+struct JSContext;
+
+namespace mozilla {
+namespace dom {
+
+class Skeleton MOZ_FINAL : public nsISupports,
+ public nsWrapperCache
+{
+public:
+ Skeleton();
+ ~Skeleton();
+
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Skeleton)
+
+ void* GetParentObject() const
+ {
+ // TODO: return something sensible here, and change the return type
+ return somethingSensible;
+ }
+
+ virtual JSObject* WrapObject(JSContext* aCx, JSObject* aScope,
+ bool* aTriedToWrap);
+};
+
+}
+}
+
diff --git a/components/script/dom/bindings/codegen/stubgenerator/generate.sh b/components/script/dom/bindings/codegen/stubgenerator/generate.sh
new file mode 100644
index 00000000000..52577f6f42f
--- /dev/null
+++ b/components/script/dom/bindings/codegen/stubgenerator/generate.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+# This script creates a skeleton implementation for a C++ class which
+# implements a Web IDL interface.
+
+# This script is released into the public domain.
+
+if [ -z "$1" ]; then
+ echo usage: ./generate.sh ClassName
+ exit 1
+fi
+
+expression="s/Skeleton/$1/g"
+
+sed "$expression" < Skeleton.h > "$1.h"
+sed "$expression" < Skeleton.cpp > "$1.cpp"
+
diff --git a/components/script/dom/bindings/codegen/test/Makefile.in b/components/script/dom/bindings/codegen/test/Makefile.in
new file mode 100644
index 00000000000..d8104db5ffd
--- /dev/null
+++ b/components/script/dom/bindings/codegen/test/Makefile.in
@@ -0,0 +1,87 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DEPTH = @DEPTH@
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+relativesrcdir = @relativesrcdir@
+
+MODULE = dom
+LIBRARY_NAME = dombindings_test_s
+LIBXUL_LIBRARY = 1
+FORCE_STATIC_LIB = 1
+# Do NOT export this library. We don't actually want our test code
+# being added to libxul or anything.
+
+include $(DEPTH)/config/autoconf.mk
+
+# Need this to find all our DOM source files.
+include $(topsrcdir)/dom/dom-config.mk
+
+# And need this for $(test_webidl_files)
+include $(topsrcdir)/dom/webidl/WebIDL.mk
+
+# But the webidl actually lives in our parent dir
+test_webidl_files := $(addprefix ../,$(test_webidl_files))
+
+CPPSRCS := $(subst .webidl,Binding.cpp,$(test_webidl_files))
+
+LOCAL_INCLUDES += \
+ -I$(topsrcdir)/js/xpconnect/src \
+ -I$(topsrcdir)/js/xpconnect/wrappers \
+ -I$(topsrcdir)/dom/bindings \
+ $(NULL)
+
+
+# If you change bindinggen_dependencies here, change it in
+# dom/bindings/Makefile.in too. But note that we include ../Makefile
+# here manually, since $(GLOBAL_DEPS) won't cover it.
+bindinggen_dependencies := \
+ ../BindingGen.py \
+ ../Bindings.conf \
+ ../Configuration.py \
+ ../Codegen.py \
+ ../parser/WebIDL.py \
+ ../ParserResults.pkl \
+ ../Makefile \
+ $(GLOBAL_DEPS) \
+ $(NULL)
+
+MOCHITEST_FILES := \
+ test_bug773326.html \
+ test_enums.html \
+ test_integers.html \
+ test_interfaceToString.html \
+ test_lookupGetter.html \
+ test_InstanceOf.html \
+ test_traceProtos.html \
+ test_forOf.html \
+ forOf_iframe.html \
+ test_sequence_wrapping.html \
+ file_bug775543.html \
+ test_bug788369.html \
+ $(NULL)
+
+MOCHITEST_CHROME_FILES = \
+ test_bug775543.html \
+ $(NULL)
+
+# Include rules.mk before any of our targets so our first target is coming from
+# rules.mk and running make with no target in this dir does the right thing.
+include $(topsrcdir)/config/rules.mk
+
+$(CPPSRCS): ../%Binding.cpp: $(bindinggen_dependencies) \
+ ../%.webidl \
+ $(NULL)
+ $(MAKE) -C .. $*Binding.h
+ $(MAKE) -C .. $*Binding.cpp
+
+check::
+ $(PYTHON) $(topsrcdir)/config/pythonpath.py \
+ $(PLY_INCLUDE) $(srcdir)/../parser/runtests.py
+
+check-interactive:
+ $(PYTHON) $(topsrcdir)/config/pythonpath.py \
+ $(PLY_INCLUDE) $(srcdir)/../parser/runtests.py -q
diff --git a/components/script/dom/bindings/codegen/test/TestBindingHeader.h b/components/script/dom/bindings/codegen/test/TestBindingHeader.h
new file mode 100644
index 00000000000..1fbab0a9fb8
--- /dev/null
+++ b/components/script/dom/bindings/codegen/test/TestBindingHeader.h
@@ -0,0 +1,653 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef TestBindingHeader_h
+#define TestBindingHeader_h
+
+#include "nsWrapperCache.h"
+#include "mozilla/ErrorResult.h"
+#include "mozilla/dom/BindingUtils.h"
+#include "mozilla/dom/TypedArray.h"
+#include "nsCOMPtr.h"
+// We don't export TestCodeGenBinding.h, but it's right in our parent dir.
+#include "../TestCodeGenBinding.h"
+#include "mozilla/dom/UnionTypes.h"
+
+namespace mozilla {
+namespace dom {
+
+// IID for the TestNonCastableInterface
+#define NS_TEST_NONCASTABLE_INTERFACE_IID \
+{ 0x7c9f8ee2, 0xc9bf, 0x46ca, \
+ { 0xa0, 0xa9, 0x03, 0xa8, 0xd6, 0x30, 0x0e, 0xde } }
+
+class TestNonCastableInterface : public nsISupports,
+ public nsWrapperCache
+{
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_TEST_NONCASTABLE_INTERFACE_IID)
+ NS_DECL_ISUPPORTS
+
+ // We need a GetParentObject to make binding codegen happy
+ virtual nsISupports* GetParentObject();
+};
+
+// IID for the IndirectlyImplementedInterface
+#define NS_INDIRECTLY_IMPLEMENTED_INTERFACE_IID \
+{ 0xfed55b69, 0x7012, 0x4849, \
+ { 0xaf, 0x56, 0x4b, 0xa9, 0xee, 0x41, 0x30, 0x89 } }
+
+class IndirectlyImplementedInterface : public nsISupports,
+ public nsWrapperCache
+{
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_INDIRECTLY_IMPLEMENTED_INTERFACE_IID)
+ NS_DECL_ISUPPORTS
+
+ // We need a GetParentObject to make binding codegen happy
+ virtual nsISupports* GetParentObject();
+
+ bool IndirectlyImplementedProperty();
+ void IndirectlyImplementedProperty(bool);
+ void IndirectlyImplementedMethod();
+};
+
+// IID for the TestExternalInterface
+#define NS_TEST_EXTERNAL_INTERFACE_IID \
+{ 0xd5ba0c99, 0x9b1d, 0x4e71, \
+ { 0x8a, 0x94, 0x56, 0x38, 0x6c, 0xa3, 0xda, 0x3d } }
+class TestExternalInterface : public nsISupports
+{
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_TEST_EXTERNAL_INTERFACE_IID)
+ NS_DECL_ISUPPORTS
+};
+
+// IID for the TestCallbackInterface
+#define NS_TEST_CALLBACK_INTERFACE_IID \
+{ 0xbf711ba4, 0xc8f6, 0x46cf, \
+ { 0xba, 0x5b, 0xaa, 0xe2, 0x78, 0x18, 0xe6, 0x4a } }
+class TestCallbackInterface : public nsISupports
+{
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_TEST_CALLBACK_INTERFACE_IID)
+ NS_DECL_ISUPPORTS
+};
+
+class TestNonWrapperCacheInterface : public nsISupports
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ virtual JSObject* WrapObject(JSContext* cx, JSObject* scope);
+};
+
+class OnlyForUseInConstructor : public nsISupports,
+ public nsWrapperCache
+{
+public:
+ NS_DECL_ISUPPORTS
+ // We need a GetParentObject to make binding codegen happy
+ virtual nsISupports* GetParentObject();
+};
+
+class TestInterface : public nsISupports,
+ public nsWrapperCache
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ // We need a GetParentObject to make binding codegen happy
+ virtual nsISupports* GetParentObject();
+
+ // And now our actual WebIDL API
+ // Constructors
+ static
+ already_AddRefed<TestInterface> Constructor(nsISupports*, ErrorResult&);
+ static
+ already_AddRefed<TestInterface> Constructor(nsISupports*, const nsAString&,
+ ErrorResult&);
+ static
+ already_AddRefed<TestInterface> Constructor(nsISupports*, uint32_t,
+ Nullable<bool>&, ErrorResult&);
+ static
+ already_AddRefed<TestInterface> Constructor(nsISupports*, TestInterface*,
+ ErrorResult&);
+ static
+ already_AddRefed<TestInterface> Constructor(nsISupports*,
+ TestNonCastableInterface&,
+ ErrorResult&);
+ /* static
+ already_AddRefed<TestInterface> Constructor(nsISupports*,
+ uint32_t, uint32_t,
+ const TestInterfaceOrOnlyForUseInConstructor&,
+ ErrorResult&);
+ */
+
+ // Integer types
+ int8_t ReadonlyByte();
+ int8_t WritableByte();
+ void SetWritableByte(int8_t);
+ void PassByte(int8_t);
+ int8_t ReceiveByte();
+ void PassOptionalByte(const Optional<int8_t>&);
+ void PassOptionalByteWithDefault(int8_t);
+ void PassNullableByte(Nullable<int8_t>&);
+ void PassOptionalNullableByte(const Optional< Nullable<int8_t> >&);
+
+ int16_t ReadonlyShort();
+ int16_t WritableShort();
+ void SetWritableShort(int16_t);
+ void PassShort(int16_t);
+ int16_t ReceiveShort();
+ void PassOptionalShort(const Optional<int16_t>&);
+ void PassOptionalShortWithDefault(int16_t);
+
+ int32_t ReadonlyLong();
+ int32_t WritableLong();
+ void SetWritableLong(int32_t);
+ void PassLong(int32_t);
+ int16_t ReceiveLong();
+ void PassOptionalLong(const Optional<int32_t>&);
+ void PassOptionalLongWithDefault(int32_t);
+
+ int64_t ReadonlyLongLong();
+ int64_t WritableLongLong();
+ void SetWritableLongLong(int64_t);
+ void PassLongLong(int64_t);
+ int64_t ReceiveLongLong();
+ void PassOptionalLongLong(const Optional<int64_t>&);
+ void PassOptionalLongLongWithDefault(int64_t);
+
+ uint8_t ReadonlyOctet();
+ uint8_t WritableOctet();
+ void SetWritableOctet(uint8_t);
+ void PassOctet(uint8_t);
+ uint8_t ReceiveOctet();
+ void PassOptionalOctet(const Optional<uint8_t>&);
+ void PassOptionalOctetWithDefault(uint8_t);
+
+ uint16_t ReadonlyUnsignedShort();
+ uint16_t WritableUnsignedShort();
+ void SetWritableUnsignedShort(uint16_t);
+ void PassUnsignedShort(uint16_t);
+ uint16_t ReceiveUnsignedShort();
+ void PassOptionalUnsignedShort(const Optional<uint16_t>&);
+ void PassOptionalUnsignedShortWithDefault(uint16_t);
+
+ uint32_t ReadonlyUnsignedLong();
+ uint32_t WritableUnsignedLong();
+ void SetWritableUnsignedLong(uint32_t);
+ void PassUnsignedLong(uint32_t);
+ uint32_t ReceiveUnsignedLong();
+ void PassOptionalUnsignedLong(const Optional<uint32_t>&);
+ void PassOptionalUnsignedLongWithDefault(uint32_t);
+
+ uint64_t ReadonlyUnsignedLongLong();
+ uint64_t WritableUnsignedLongLong();
+ void SetWritableUnsignedLongLong(uint64_t);
+ void PassUnsignedLongLong(uint64_t);
+ uint64_t ReceiveUnsignedLongLong();
+ void PassOptionalUnsignedLongLong(const Optional<uint64_t>&);
+ void PassOptionalUnsignedLongLongWithDefault(uint64_t);
+
+ // Interface types
+ already_AddRefed<TestInterface> ReceiveSelf();
+ already_AddRefed<TestInterface> ReceiveNullableSelf();
+ TestInterface* ReceiveWeakSelf();
+ TestInterface* ReceiveWeakNullableSelf();
+ void PassSelf(TestInterface&);
+ void PassSelf2(NonNull<TestInterface>&);
+ void PassNullableSelf(TestInterface*);
+ already_AddRefed<TestInterface> NonNullSelf();
+ void SetNonNullSelf(TestInterface&);
+ already_AddRefed<TestInterface> GetNullableSelf();
+ void SetNullableSelf(TestInterface*);
+ void PassOptionalSelf(const Optional<TestInterface*> &);
+ void PassOptionalNonNullSelf(const Optional<NonNull<TestInterface> >&);
+ void PassOptionalSelfWithDefault(TestInterface*);
+
+ already_AddRefed<TestNonWrapperCacheInterface> ReceiveNonWrapperCacheInterface();
+ already_AddRefed<TestNonWrapperCacheInterface> ReceiveNullableNonWrapperCacheInterface();
+ void ReceiveNonWrapperCacheInterfaceSequence(nsTArray<nsRefPtr<TestNonWrapperCacheInterface> >&);
+ void ReceiveNullableNonWrapperCacheInterfaceSequence(nsTArray<nsRefPtr<TestNonWrapperCacheInterface> >&);
+ void ReceiveNonWrapperCacheInterfaceNullableSequence(Nullable<nsTArray<nsRefPtr<TestNonWrapperCacheInterface> > >&);
+ void ReceiveNullableNonWrapperCacheInterfaceNullableSequence(Nullable<nsTArray<nsRefPtr<TestNonWrapperCacheInterface> > >&);
+
+ already_AddRefed<TestNonCastableInterface> ReceiveOther();
+ already_AddRefed<TestNonCastableInterface> ReceiveNullableOther();
+ TestNonCastableInterface* ReceiveWeakOther();
+ TestNonCastableInterface* ReceiveWeakNullableOther();
+ void PassOther(TestNonCastableInterface&);
+ void PassOther2(NonNull<TestNonCastableInterface>&);
+ void PassNullableOther(TestNonCastableInterface*);
+ already_AddRefed<TestNonCastableInterface> NonNullOther();
+ void SetNonNullOther(TestNonCastableInterface&);
+ already_AddRefed<TestNonCastableInterface> GetNullableOther();
+ void SetNullableOther(TestNonCastableInterface*);
+ void PassOptionalOther(const Optional<TestNonCastableInterface*>&);
+ void PassOptionalNonNullOther(const Optional<NonNull<TestNonCastableInterface> >&);
+ void PassOptionalOtherWithDefault(TestNonCastableInterface*);
+
+ already_AddRefed<TestExternalInterface> ReceiveExternal();
+ already_AddRefed<TestExternalInterface> ReceiveNullableExternal();
+ TestExternalInterface* ReceiveWeakExternal();
+ TestExternalInterface* ReceiveWeakNullableExternal();
+ void PassExternal(TestExternalInterface*);
+ void PassExternal2(TestExternalInterface*);
+ void PassNullableExternal(TestExternalInterface*);
+ already_AddRefed<TestExternalInterface> NonNullExternal();
+ void SetNonNullExternal(TestExternalInterface*);
+ already_AddRefed<TestExternalInterface> GetNullableExternal();
+ void SetNullableExternal(TestExternalInterface*);
+ void PassOptionalExternal(const Optional<TestExternalInterface*>&);
+ void PassOptionalNonNullExternal(const Optional<TestExternalInterface*>&);
+ void PassOptionalExternalWithDefault(TestExternalInterface*);
+
+ already_AddRefed<TestCallbackInterface> ReceiveCallbackInterface();
+ already_AddRefed<TestCallbackInterface> ReceiveNullableCallbackInterface();
+ TestCallbackInterface* ReceiveWeakCallbackInterface();
+ TestCallbackInterface* ReceiveWeakNullableCallbackInterface();
+ void PassCallbackInterface(TestCallbackInterface&);
+ void PassCallbackInterface2(OwningNonNull<TestCallbackInterface>);
+ void PassNullableCallbackInterface(TestCallbackInterface*);
+ already_AddRefed<TestCallbackInterface> NonNullCallbackInterface();
+ void SetNonNullCallbackInterface(TestCallbackInterface&);
+ already_AddRefed<TestCallbackInterface> GetNullableCallbackInterface();
+ void SetNullableCallbackInterface(TestCallbackInterface*);
+ void PassOptionalCallbackInterface(const Optional<nsRefPtr<TestCallbackInterface> >&);
+ void PassOptionalNonNullCallbackInterface(const Optional<OwningNonNull<TestCallbackInterface> >&);
+ void PassOptionalCallbackInterfaceWithDefault(TestCallbackInterface*);
+
+ already_AddRefed<IndirectlyImplementedInterface> ReceiveConsequentialInterface();
+ void PassConsequentialInterface(IndirectlyImplementedInterface&);
+
+ // Sequence types
+ void ReceiveSequence(nsTArray<int32_t>&);
+ void ReceiveNullableSequence(Nullable< nsTArray<int32_t> >&);
+ void ReceiveSequenceOfNullableInts(nsTArray< Nullable<int32_t> >&);
+ void ReceiveNullableSequenceOfNullableInts(Nullable< nsTArray< Nullable<int32_t> > >&);
+ void PassSequence(const Sequence<int32_t> &);
+ void PassNullableSequence(const Nullable< Sequence<int32_t> >&);
+ void PassSequenceOfNullableInts(const Sequence<Nullable<int32_t> >&);
+ void PassOptionalSequenceOfNullableInts(const Optional<Sequence<Nullable<int32_t> > > &);
+ void PassOptionalNullableSequenceOfNullableInts(const Optional<Nullable<Sequence<Nullable<int32_t> > > > &);
+ void ReceiveCastableObjectSequence(nsTArray< nsRefPtr<TestInterface> > &);
+ void ReceiveNullableCastableObjectSequence(nsTArray< nsRefPtr<TestInterface> > &);
+ void ReceiveCastableObjectNullableSequence(Nullable< nsTArray< nsRefPtr<TestInterface> > >&);
+ void ReceiveNullableCastableObjectNullableSequence(Nullable< nsTArray< nsRefPtr<TestInterface> > >&);
+ void ReceiveWeakCastableObjectSequence(nsTArray<TestInterface*> &);
+ void ReceiveWeakNullableCastableObjectSequence(nsTArray<TestInterface*> &);
+ void ReceiveWeakCastableObjectNullableSequence(Nullable< nsTArray<TestInterface*> >&);
+ void ReceiveWeakNullableCastableObjectNullableSequence(Nullable< nsTArray<TestInterface*> >&);
+ void PassCastableObjectSequence(const Sequence< OwningNonNull<TestInterface> >&);
+ void PassNullableCastableObjectSequence(const Sequence< nsRefPtr<TestInterface> > &);
+ void PassCastableObjectNullableSequence(const Nullable< Sequence< OwningNonNull<TestInterface> > >&);
+ void PassNullableCastableObjectNullableSequence(const Nullable< Sequence< nsRefPtr<TestInterface> > >&);
+ void PassOptionalSequence(const Optional<Sequence<int32_t> >&);
+ void PassOptionalNullableSequence(const Optional<Nullable<Sequence<int32_t> > >&);
+ void PassOptionalNullableSequenceWithDefaultValue(const Nullable< Sequence<int32_t> >&);
+ void PassOptionalObjectSequence(const Optional<Sequence<OwningNonNull<TestInterface> > >&);
+
+ void ReceiveStringSequence(nsTArray<nsString>&);
+ void PassStringSequence(const Sequence<nsString>&);
+
+ void ReceiveAnySequence(JSContext*, nsTArray<JS::Value>&);
+ void ReceiveNullableAnySequence(JSContext*, Nullable<nsTArray<JS::Value> >);
+
+ // Typed array types
+ void PassArrayBuffer(ArrayBuffer&);
+ void PassNullableArrayBuffer(ArrayBuffer*);
+ void PassOptionalArrayBuffer(const Optional<ArrayBuffer>&);
+ void PassOptionalNullableArrayBuffer(const Optional<ArrayBuffer*>&);
+ void PassOptionalNullableArrayBufferWithDefaultValue(ArrayBuffer*);
+ void PassArrayBufferView(ArrayBufferView&);
+ void PassInt8Array(Int8Array&);
+ void PassInt16Array(Int16Array&);
+ void PassInt32Array(Int32Array&);
+ void PassUint8Array(Uint8Array&);
+ void PassUint16Array(Uint16Array&);
+ void PassUint32Array(Uint32Array&);
+ void PassUint8ClampedArray(Uint8ClampedArray&);
+ void PassFloat32Array(Float32Array&);
+ void PassFloat64Array(Float64Array&);
+ JSObject* ReceiveUint8Array(JSContext*);
+
+ // String types
+ void PassString(const nsAString&);
+ void PassNullableString(const nsAString&);
+ void PassOptionalString(const Optional<nsAString>&);
+ void PassOptionalStringWithDefaultValue(const nsAString&);
+ void PassOptionalNullableString(const Optional<nsAString>&);
+ void PassOptionalNullableStringWithDefaultValue(const nsAString&);
+
+ // Enumarated types
+ void PassEnum(TestEnum);
+ void PassOptionalEnum(const Optional<TestEnum>&);
+ void PassEnumWithDefault(TestEnum);
+ TestEnum ReceiveEnum();
+ TestEnum EnumAttribute();
+ TestEnum ReadonlyEnumAttribute();
+ void SetEnumAttribute(TestEnum);
+
+ // Callback types
+ void PassCallback(JSContext*, JSObject*);
+ void PassNullableCallback(JSContext*, JSObject*);
+ void PassOptionalCallback(JSContext*, const Optional<JSObject*>&);
+ void PassOptionalNullableCallback(JSContext*, const Optional<JSObject*>&);
+ void PassOptionalNullableCallbackWithDefaultValue(JSContext*, JSObject*);
+ JSObject* ReceiveCallback(JSContext*);
+ JSObject* ReceiveNullableCallback(JSContext*);
+
+ // Any types
+ void PassAny(JSContext*, JS::Value);
+ void PassOptionalAny(JSContext*, const Optional<JS::Value>&);
+ void PassAnyDefaultNull(JSContext*, JS::Value);
+ JS::Value ReceiveAny(JSContext*);
+
+ // object types
+ void PassObject(JSContext*, JSObject&);
+ void PassNullableObject(JSContext*, JSObject*);
+ void PassOptionalObject(JSContext*, const Optional<NonNull<JSObject> >&);
+ void PassOptionalNullableObject(JSContext*, const Optional<JSObject*>&);
+ void PassOptionalNullableObjectWithDefaultValue(JSContext*, JSObject*);
+ JSObject* ReceiveObject(JSContext*);
+ JSObject* ReceiveNullableObject(JSContext*);
+
+ // Union types
+ void PassUnion(JSContext*, const ObjectOrLong& arg);
+ void PassUnionWithNullable(JSContext*, const ObjectOrNullOrLong& arg)
+ {
+ ObjectOrLong returnValue;
+ if (arg.IsNull()) {
+ } else if (arg.IsObject()) {
+ JSObject& obj = (JSObject&)arg.GetAsObject();
+ JS_GetClass(&obj);
+ //returnValue.SetAsObject(&obj);
+ } else {
+ int32_t i = arg.GetAsLong();
+ i += 1;
+ }
+ }
+ void PassNullableUnion(JSContext*, const Nullable<ObjectOrLong>&);
+ void PassOptionalUnion(JSContext*, const Optional<ObjectOrLong>&);
+ void PassOptionalNullableUnion(JSContext*, const Optional<Nullable<ObjectOrLong> >&);
+ void PassOptionalNullableUnionWithDefaultValue(JSContext*, const Nullable<ObjectOrLong>&);
+ //void PassUnionWithInterfaces(const TestInterfaceOrTestExternalInterface& arg);
+ //void PassUnionWithInterfacesAndNullable(const TestInterfaceOrNullOrTestExternalInterface& arg);
+ void PassUnionWithArrayBuffer(const ArrayBufferOrLong&);
+ void PassUnionWithString(JSContext*, const StringOrObject&);
+ //void PassUnionWithEnum(JSContext*, const TestEnumOrObject&);
+ void PassUnionWithCallback(JSContext*, const TestCallbackOrLong&);
+ void PassUnionWithObject(JSContext*, const ObjectOrLong&);
+
+ // binaryNames tests
+ void MethodRenamedTo();
+ void MethodRenamedTo(int8_t);
+ int8_t AttributeGetterRenamedTo();
+ int8_t AttributeRenamedTo();
+ void SetAttributeRenamedTo(int8_t);
+
+ // Dictionary tests
+ void PassDictionary(const Dict&);
+ void PassOtherDictionary(const GrandparentDict&);
+ void PassSequenceOfDictionaries(const Sequence<Dict>&);
+ void PassDictionaryOrLong(const Dict&);
+ void PassDictionaryOrLong(int32_t);
+ void PassDictContainingDict(const DictContainingDict&);
+ void PassDictContainingSequence(const DictContainingSequence&);
+
+ // Typedefs
+ void ExerciseTypedefInterfaces1(TestInterface&);
+ already_AddRefed<TestInterface> ExerciseTypedefInterfaces2(TestInterface*);
+ void ExerciseTypedefInterfaces3(TestInterface&);
+
+ // Miscellania
+ int32_t AttrWithLenientThis();
+ void SetAttrWithLenientThis(int32_t);
+
+ // Methods and properties imported via "implements"
+ bool ImplementedProperty();
+ void SetImplementedProperty(bool);
+ void ImplementedMethod();
+ bool ImplementedParentProperty();
+ void SetImplementedParentProperty(bool);
+ void ImplementedParentMethod();
+ bool IndirectlyImplementedProperty();
+ void SetIndirectlyImplementedProperty(bool);
+ void IndirectlyImplementedMethod();
+ uint32_t DiamondImplementedProperty();
+
+ // Test EnforceRange/Clamp
+ void DontEnforceRangeOrClamp(int8_t);
+ void DoEnforceRange(int8_t);
+ void DoClamp(int8_t);
+
+private:
+ // We add signatures here that _could_ start matching if the codegen
+ // got data types wrong. That way if it ever does we'll have a call
+ // to these private deleted methods and compilation will fail.
+ void SetReadonlyByte(int8_t) MOZ_DELETE;
+ template<typename T>
+ void SetWritableByte(T) MOZ_DELETE;
+ template<typename T>
+ void PassByte(T) MOZ_DELETE;
+ template<typename T>
+ void PassOptionalByte(const Optional<T>&) MOZ_DELETE;
+ template<typename T>
+ void PassOptionalByteWithDefault(T) MOZ_DELETE;
+
+ void SetReadonlyShort(int16_t) MOZ_DELETE;
+ template<typename T>
+ void SetWritableShort(T) MOZ_DELETE;
+ template<typename T>
+ void PassShort(T) MOZ_DELETE;
+ template<typename T>
+ void PassOptionalShort(const Optional<T>&) MOZ_DELETE;
+ template<typename T>
+ void PassOptionalShortWithDefault(T) MOZ_DELETE;
+
+ void SetReadonlyLong(int32_t) MOZ_DELETE;
+ template<typename T>
+ void SetWritableLong(T) MOZ_DELETE;
+ template<typename T>
+ void PassLong(T) MOZ_DELETE;
+ template<typename T>
+ void PassOptionalLong(const Optional<T>&) MOZ_DELETE;
+ template<typename T>
+ void PassOptionalLongWithDefault(T) MOZ_DELETE;
+
+ void SetReadonlyLongLong(int64_t) MOZ_DELETE;
+ template<typename T>
+ void SetWritableLongLong(T) MOZ_DELETE;
+ template<typename T>
+ void PassLongLong(T) MOZ_DELETE;
+ template<typename T>
+ void PassOptionalLongLong(const Optional<T>&) MOZ_DELETE;
+ template<typename T>
+ void PassOptionalLongLongWithDefault(T) MOZ_DELETE;
+
+ void SetReadonlyOctet(uint8_t) MOZ_DELETE;
+ template<typename T>
+ void SetWritableOctet(T) MOZ_DELETE;
+ template<typename T>
+ void PassOctet(T) MOZ_DELETE;
+ template<typename T>
+ void PassOptionalOctet(const Optional<T>&) MOZ_DELETE;
+ template<typename T>
+ void PassOptionalOctetWithDefault(T) MOZ_DELETE;
+
+ void SetReadonlyUnsignedShort(uint16_t) MOZ_DELETE;
+ template<typename T>
+ void SetWritableUnsignedShort(T) MOZ_DELETE;
+ template<typename T>
+ void PassUnsignedShort(T) MOZ_DELETE;
+ template<typename T>
+ void PassOptionalUnsignedShort(const Optional<T>&) MOZ_DELETE;
+ template<typename T>
+ void PassOptionalUnsignedShortWithDefault(T) MOZ_DELETE;
+
+ void SetReadonlyUnsignedLong(uint32_t) MOZ_DELETE;
+ template<typename T>
+ void SetWritableUnsignedLong(T) MOZ_DELETE;
+ template<typename T>
+ void PassUnsignedLong(T) MOZ_DELETE;
+ template<typename T>
+ void PassOptionalUnsignedLong(const Optional<T>&) MOZ_DELETE;
+ template<typename T>
+ void PassOptionalUnsignedLongWithDefault(T) MOZ_DELETE;
+
+ void SetReadonlyUnsignedLongLong(uint64_t) MOZ_DELETE;
+ template<typename T>
+ void SetWritableUnsignedLongLong(T) MOZ_DELETE;
+ template<typename T>
+ void PassUnsignedLongLong(T) MOZ_DELETE;
+ template<typename T>
+ void PassOptionalUnsignedLongLong(const Optional<T>&) MOZ_DELETE;
+ template<typename T>
+ void PassOptionalUnsignedLongLongWithDefault(T) MOZ_DELETE;
+
+ // Enforce that only const things are passed for sequences
+ void PassSequence(Sequence<int32_t> &) MOZ_DELETE;
+ void PassNullableSequence(Nullable< Sequence<int32_t> >&) MOZ_DELETE;
+ void PassOptionalNullableSequenceWithDefaultValue(Nullable< Sequence<int32_t> >&) MOZ_DELETE;
+
+ // Enforce that only const things are passed for optional
+ void PassOptionalByte(Optional<int8_t>&) MOZ_DELETE;
+ void PassOptionalNullableByte(Optional<Nullable<int8_t> >&) MOZ_DELETE;
+ void PassOptionalShort(Optional<int16_t>&) MOZ_DELETE;
+ void PassOptionalLong(Optional<int32_t>&) MOZ_DELETE;
+ void PassOptionalLongLong(Optional<int64_t>&) MOZ_DELETE;
+ void PassOptionalOctet(Optional<uint8_t>&) MOZ_DELETE;
+ void PassOptionalUnsignedShort(Optional<uint16_t>&) MOZ_DELETE;
+ void PassOptionalUnsignedLong(Optional<uint32_t>&) MOZ_DELETE;
+ void PassOptionalUnsignedLongLong(Optional<uint64_t>&) MOZ_DELETE;
+ void PassOptionalSelf(Optional<TestInterface*> &) MOZ_DELETE;
+ void PassOptionalNonNullSelf(Optional<NonNull<TestInterface> >&) MOZ_DELETE;
+ void PassOptionalOther(Optional<TestNonCastableInterface*>&);
+ void PassOptionalNonNullOther(Optional<NonNull<TestNonCastableInterface> >&);
+ void PassOptionalExternal(Optional<TestExternalInterface*>&) MOZ_DELETE;
+ void PassOptionalNonNullExternal(Optional<TestExternalInterface*>&) MOZ_DELETE;
+ void PassOptionalSequence(Optional<Sequence<int32_t> >&) MOZ_DELETE;
+ void PassOptionalNullableSequence(Optional<Nullable<Sequence<int32_t> > >&) MOZ_DELETE;
+ void PassOptionalObjectSequence(Optional<Sequence<OwningNonNull<TestInterface> > >&) MOZ_DELETE;
+ void PassOptionalArrayBuffer(Optional<ArrayBuffer>&) MOZ_DELETE;
+ void PassOptionalNullableArrayBuffer(Optional<ArrayBuffer*>&) MOZ_DELETE;
+ void PassOptionalEnum(Optional<TestEnum>&) MOZ_DELETE;
+ void PassOptionalCallback(JSContext*, Optional<JSObject*>&) MOZ_DELETE;
+ void PassOptionalNullableCallback(JSContext*, Optional<JSObject*>&) MOZ_DELETE;
+ void PassOptionalAny(Optional<JS::Value>&) MOZ_DELETE;
+
+ // And test that string stuff is always const
+ void PassString(nsAString&) MOZ_DELETE;
+ void PassNullableString(nsAString&) MOZ_DELETE;
+ void PassOptionalString(Optional<nsAString>&) MOZ_DELETE;
+ void PassOptionalStringWithDefaultValue(nsAString&) MOZ_DELETE;
+ void PassOptionalNullableString(Optional<nsAString>&) MOZ_DELETE;
+ void PassOptionalNullableStringWithDefaultValue(nsAString&) MOZ_DELETE;
+
+};
+
+class TestIndexedGetterInterface : public nsISupports,
+ public nsWrapperCache
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ // We need a GetParentObject to make binding codegen happy
+ virtual nsISupports* GetParentObject();
+
+ uint32_t IndexedGetter(uint32_t, bool&);
+ uint32_t IndexedGetter(uint32_t&) MOZ_DELETE;
+ uint32_t Item(uint32_t&);
+ uint32_t Item(uint32_t, bool&) MOZ_DELETE;
+ uint32_t Length();
+};
+
+class TestNamedGetterInterface : public nsISupports,
+ public nsWrapperCache
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ // We need a GetParentObject to make binding codegen happy
+ virtual nsISupports* GetParentObject();
+
+ void NamedGetter(const nsAString&, bool&, nsAString&);
+};
+
+class TestIndexedAndNamedGetterInterface : public nsISupports,
+ public nsWrapperCache
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ // We need a GetParentObject to make binding codegen happy
+ virtual nsISupports* GetParentObject();
+
+ uint32_t IndexedGetter(uint32_t, bool&);
+ void NamedGetter(const nsAString&, bool&, nsAString&);
+ void NamedItem(const nsAString&, nsAString&);
+ uint32_t Length();
+};
+
+class TestIndexedSetterInterface : public nsISupports,
+ public nsWrapperCache
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ // We need a GetParentObject to make binding codegen happy
+ virtual nsISupports* GetParentObject();
+
+ void IndexedSetter(uint32_t, const nsAString&);
+ void SetItem(uint32_t, const nsAString&);
+};
+
+class TestNamedSetterInterface : public nsISupports,
+ public nsWrapperCache
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ // We need a GetParentObject to make binding codegen happy
+ virtual nsISupports* GetParentObject();
+
+ void NamedSetter(const nsAString&, TestIndexedSetterInterface&);
+};
+
+class TestIndexedAndNamedSetterInterface : public nsISupports,
+ public nsWrapperCache
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ // We need a GetParentObject to make binding codegen happy
+ virtual nsISupports* GetParentObject();
+
+ void IndexedSetter(uint32_t, TestIndexedSetterInterface&);
+ void NamedSetter(const nsAString&, TestIndexedSetterInterface&);
+ void SetNamedItem(const nsAString&, TestIndexedSetterInterface&);
+};
+
+class TestIndexedAndNamedGetterAndSetterInterface : public TestIndexedSetterInterface
+{
+public:
+ uint32_t IndexedGetter(uint32_t, bool&);
+ uint32_t Item(uint32_t);
+ void NamedGetter(const nsAString&, bool&, nsAString&);
+ void NamedItem(const nsAString&, nsAString&);
+ void IndexedSetter(uint32_t, int32_t&);
+ void IndexedSetter(uint32_t, const nsAString&) MOZ_DELETE;
+ void NamedSetter(const nsAString&, const nsAString&);
+ void Stringify(nsAString&);
+ uint32_t Length();
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* TestBindingHeader_h */
diff --git a/components/script/dom/bindings/codegen/test/TestCodeGen.webidl b/components/script/dom/bindings/codegen/test/TestCodeGen.webidl
new file mode 100644
index 00000000000..8c2b3c1b6b4
--- /dev/null
+++ b/components/script/dom/bindings/codegen/test/TestCodeGen.webidl
@@ -0,0 +1,442 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+typedef long myLong;
+typedef TestInterface AnotherNameForTestInterface;
+typedef TestInterface? NullableTestInterface;
+
+interface TestExternalInterface;
+
+interface TestNonCastableInterface {
+};
+
+callback interface TestCallbackInterface {
+ readonly attribute long foo;
+ void doSomething();
+};
+
+enum TestEnum {
+ "a",
+ "b"
+};
+
+callback TestCallback = void();
+
+TestInterface implements ImplementedInterface;
+
+// This interface is only for use in the constructor below
+interface OnlyForUseInConstructor {
+};
+
+[Constructor,
+ Constructor(DOMString str),
+ Constructor(unsigned long num, boolean? bool),
+ Constructor(TestInterface? iface),
+ Constructor(TestNonCastableInterface iface)
+ // , Constructor(long arg1, long arg2, (TestInterface or OnlyForUseInConstructor) arg3)
+ ]
+interface TestInterface {
+ // Integer types
+ // XXXbz add tests for throwing versions of all the integer stuff
+ readonly attribute byte readonlyByte;
+ attribute byte writableByte;
+ void passByte(byte arg);
+ byte receiveByte();
+ void passOptionalByte(optional byte arg);
+ void passOptionalByteWithDefault(optional byte arg = 0);
+ void passNullableByte(byte? arg);
+ void passOptionalNullableByte(optional byte? arg);
+
+ readonly attribute short readonlyShort;
+ attribute short writableShort;
+ void passShort(short arg);
+ short receiveShort();
+ void passOptionalShort(optional short arg);
+ void passOptionalShortWithDefault(optional short arg = 5);
+
+ readonly attribute long readonlyLong;
+ attribute long writableLong;
+ void passLong(long arg);
+ long receiveLong();
+ void passOptionalLong(optional long arg);
+ void passOptionalLongWithDefault(optional long arg = 7);
+
+ readonly attribute long long readonlyLongLong;
+ attribute long long writableLongLong;
+ void passLongLong(long long arg);
+ long long receiveLongLong();
+ void passOptionalLongLong(optional long long arg);
+ void passOptionalLongLongWithDefault(optional long long arg = -12);
+
+ readonly attribute octet readonlyOctet;
+ attribute octet writableOctet;
+ void passOctet(octet arg);
+ octet receiveOctet();
+ void passOptionalOctet(optional octet arg);
+ void passOptionalOctetWithDefault(optional octet arg = 19);
+
+ readonly attribute unsigned short readonlyUnsignedShort;
+ attribute unsigned short writableUnsignedShort;
+ void passUnsignedShort(unsigned short arg);
+ unsigned short receiveUnsignedShort();
+ void passOptionalUnsignedShort(optional unsigned short arg);
+ void passOptionalUnsignedShortWithDefault(optional unsigned short arg = 2);
+
+ readonly attribute unsigned long readonlyUnsignedLong;
+ attribute unsigned long writableUnsignedLong;
+ void passUnsignedLong(unsigned long arg);
+ unsigned long receiveUnsignedLong();
+ void passOptionalUnsignedLong(optional unsigned long arg);
+ void passOptionalUnsignedLongWithDefault(optional unsigned long arg = 6);
+
+ readonly attribute unsigned long long readonlyUnsignedLongLong;
+ attribute unsigned long long writableUnsignedLongLong;
+ void passUnsignedLongLong(unsigned long long arg);
+ unsigned long long receiveUnsignedLongLong();
+ void passOptionalUnsignedLongLong(optional unsigned long long arg);
+ void passOptionalUnsignedLongLongWithDefault(optional unsigned long long arg = 17);
+
+ // Castable interface types
+ // XXXbz add tests for throwing versions of all the castable interface stuff
+ TestInterface receiveSelf();
+ TestInterface? receiveNullableSelf();
+ TestInterface receiveWeakSelf();
+ TestInterface? receiveWeakNullableSelf();
+ // A verstion to test for casting to TestInterface&
+ void passSelf(TestInterface arg);
+ // A version we can use to test for the exact type passed in
+ void passSelf2(TestInterface arg);
+ void passNullableSelf(TestInterface? arg);
+ attribute TestInterface nonNullSelf;
+ attribute TestInterface? nullableSelf;
+ // Optional arguments
+ void passOptionalSelf(optional TestInterface? arg);
+ void passOptionalNonNullSelf(optional TestInterface arg);
+ void passOptionalSelfWithDefault(optional TestInterface? arg = null);
+
+ // Non-wrapper-cache interface types
+ [Creator]
+ TestNonWrapperCacheInterface receiveNonWrapperCacheInterface();
+ [Creator]
+ TestNonWrapperCacheInterface? receiveNullableNonWrapperCacheInterface();
+ [Creator]
+ sequence<TestNonWrapperCacheInterface> receiveNonWrapperCacheInterfaceSequence();
+ [Creator]
+ sequence<TestNonWrapperCacheInterface?> receiveNullableNonWrapperCacheInterfaceSequence();
+ [Creator]
+ sequence<TestNonWrapperCacheInterface>? receiveNonWrapperCacheInterfaceNullableSequence();
+ [Creator]
+ sequence<TestNonWrapperCacheInterface?>? receiveNullableNonWrapperCacheInterfaceNullableSequence();
+
+ // Non-castable interface types
+ TestNonCastableInterface receiveOther();
+ TestNonCastableInterface? receiveNullableOther();
+ TestNonCastableInterface receiveWeakOther();
+ TestNonCastableInterface? receiveWeakNullableOther();
+ // A verstion to test for casting to TestNonCastableInterface&
+ void passOther(TestNonCastableInterface arg);
+ // A version we can use to test for the exact type passed in
+ void passOther2(TestNonCastableInterface arg);
+ void passNullableOther(TestNonCastableInterface? arg);
+ attribute TestNonCastableInterface nonNullOther;
+ attribute TestNonCastableInterface? nullableOther;
+ // Optional arguments
+ void passOptionalOther(optional TestNonCastableInterface? arg);
+ void passOptionalNonNullOther(optional TestNonCastableInterface arg);
+ void passOptionalOtherWithDefault(optional TestNonCastableInterface? arg = null);
+
+ // External interface types
+ TestExternalInterface receiveExternal();
+ TestExternalInterface? receiveNullableExternal();
+ TestExternalInterface receiveWeakExternal();
+ TestExternalInterface? receiveWeakNullableExternal();
+ // A verstion to test for casting to TestExternalInterface&
+ void passExternal(TestExternalInterface arg);
+ // A version we can use to test for the exact type passed in
+ void passExternal2(TestExternalInterface arg);
+ void passNullableExternal(TestExternalInterface? arg);
+ attribute TestExternalInterface nonNullExternal;
+ attribute TestExternalInterface? nullableExternal;
+ // Optional arguments
+ void passOptionalExternal(optional TestExternalInterface? arg);
+ void passOptionalNonNullExternal(optional TestExternalInterface arg);
+ void passOptionalExternalWithDefault(optional TestExternalInterface? arg = null);
+
+ // Callback interface types
+ TestCallbackInterface receiveCallbackInterface();
+ TestCallbackInterface? receiveNullableCallbackInterface();
+ TestCallbackInterface receiveWeakCallbackInterface();
+ TestCallbackInterface? receiveWeakNullableCallbackInterface();
+ // A verstion to test for casting to TestCallbackInterface&
+ void passCallbackInterface(TestCallbackInterface arg);
+ // A version we can use to test for the exact type passed in
+ void passCallbackInterface2(TestCallbackInterface arg);
+ void passNullableCallbackInterface(TestCallbackInterface? arg);
+ attribute TestCallbackInterface nonNullCallbackInterface;
+ attribute TestCallbackInterface? nullableCallbackInterface;
+ // Optional arguments
+ void passOptionalCallbackInterface(optional TestCallbackInterface? arg);
+ void passOptionalNonNullCallbackInterface(optional TestCallbackInterface arg);
+ void passOptionalCallbackInterfaceWithDefault(optional TestCallbackInterface? arg = null);
+
+ // Miscellaneous interface tests
+ IndirectlyImplementedInterface receiveConsequentialInterface();
+ void passConsequentialInterface(IndirectlyImplementedInterface arg);
+
+ // Sequence types
+ sequence<long> receiveSequence();
+ sequence<long>? receiveNullableSequence();
+ sequence<long?> receiveSequenceOfNullableInts();
+ sequence<long?>? receiveNullableSequenceOfNullableInts();
+ void passSequence(sequence<long> arg);
+ void passNullableSequence(sequence<long>? arg);
+ void passSequenceOfNullableInts(sequence<long?> arg);
+ void passOptionalSequenceOfNullableInts(optional sequence<long?> arg);
+ void passOptionalNullableSequenceOfNullableInts(optional sequence<long?>? arg);
+ sequence<TestInterface> receiveCastableObjectSequence();
+ sequence<TestInterface?> receiveNullableCastableObjectSequence();
+ sequence<TestInterface>? receiveCastableObjectNullableSequence();
+ sequence<TestInterface?>? receiveNullableCastableObjectNullableSequence();
+ sequence<TestInterface> receiveWeakCastableObjectSequence();
+ sequence<TestInterface?> receiveWeakNullableCastableObjectSequence();
+ sequence<TestInterface>? receiveWeakCastableObjectNullableSequence();
+ sequence<TestInterface?>? receiveWeakNullableCastableObjectNullableSequence();
+ void passCastableObjectSequence(sequence<TestInterface> arg);
+ void passNullableCastableObjectSequence(sequence<TestInterface?> arg);
+ void passCastableObjectNullableSequence(sequence<TestInterface>? arg);
+ void passNullableCastableObjectNullableSequence(sequence<TestInterface?>? arg);
+ void passOptionalSequence(optional sequence<long> arg);
+ void passOptionalNullableSequence(optional sequence<long>? arg);
+ void passOptionalNullableSequenceWithDefaultValue(optional sequence<long>? arg = null);
+ void passOptionalObjectSequence(optional sequence<TestInterface> arg);
+
+ sequence<DOMString> receiveStringSequence();
+ void passStringSequence(sequence<DOMString> arg);
+
+ sequence<any> receiveAnySequence();
+ sequence<any>? receiveNullableAnySequence();
+
+ // Typed array types
+ void passArrayBuffer(ArrayBuffer arg);
+ void passNullableArrayBuffer(ArrayBuffer? arg);
+ void passOptionalArrayBuffer(optional ArrayBuffer arg);
+ void passOptionalNullableArrayBuffer(optional ArrayBuffer? arg);
+ void passOptionalNullableArrayBufferWithDefaultValue(optional ArrayBuffer? arg= null);
+ void passArrayBufferView(ArrayBufferView arg);
+ void passInt8Array(Int8Array arg);
+ void passInt16Array(Int16Array arg);
+ void passInt32Array(Int32Array arg);
+ void passUint8Array(Uint8Array arg);
+ void passUint16Array(Uint16Array arg);
+ void passUint32Array(Uint32Array arg);
+ void passUint8ClampedArray(Uint8ClampedArray arg);
+ void passFloat32Array(Float32Array arg);
+ void passFloat64Array(Float64Array arg);
+ Uint8Array receiveUint8Array();
+
+ // String types
+ void passString(DOMString arg);
+ void passNullableString(DOMString? arg);
+ void passOptionalString(optional DOMString arg);
+ void passOptionalStringWithDefaultValue(optional DOMString arg = "abc");
+ void passOptionalNullableString(optional DOMString? arg);
+ void passOptionalNullableStringWithDefaultValue(optional DOMString? arg = null);
+
+ // Enumerated types
+ void passEnum(TestEnum arg);
+ // No support for nullable enums yet
+ // void passNullableEnum(TestEnum? arg);
+ void passOptionalEnum(optional TestEnum arg);
+ void passEnumWithDefault(optional TestEnum arg = "a");
+ // void passOptionalNullableEnum(optional TestEnum? arg);
+ // void passOptionalNullableEnumWithDefaultValue(optional TestEnum? arg = null);
+ TestEnum receiveEnum();
+ attribute TestEnum enumAttribute;
+ readonly attribute TestEnum readonlyEnumAttribute;
+
+ // Callback types
+ void passCallback(TestCallback arg);
+ void passNullableCallback(TestCallback? arg);
+ void passOptionalCallback(optional TestCallback arg);
+ void passOptionalNullableCallback(optional TestCallback? arg);
+ void passOptionalNullableCallbackWithDefaultValue(optional TestCallback? arg = null);
+ TestCallback receiveCallback();
+ TestCallback? receiveNullableCallback();
+
+ // Any types
+ void passAny(any arg);
+ void passOptionalAny(optional any arg);
+ void passAnyDefaultNull(optional any arg = null);
+ any receiveAny();
+
+ // object types
+ void passObject(object arg);
+ void passNullableObject(object? arg);
+ void passOptionalObject(optional object arg);
+ void passOptionalNullableObject(optional object? arg);
+ void passOptionalNullableObjectWithDefaultValue(optional object? arg = null);
+ object receiveObject();
+ object? receiveNullableObject();
+
+ // Union types
+ void passUnion((object or long) arg);
+ void passUnionWithNullable((object? or long) arg);
+ void passNullableUnion((object or long)? arg);
+ void passOptionalUnion(optional (object or long) arg);
+ void passOptionalNullableUnion(optional (object or long)? arg);
+ void passOptionalNullableUnionWithDefaultValue(optional (object or long)? arg = null);
+ //void passUnionWithInterfaces((TestInterface or TestExternalInterface) arg);
+ //void passUnionWithInterfacesAndNullable((TestInterface? or TestExternalInterface) arg);
+ //void passUnionWithSequence((sequence<object> or long) arg);
+ void passUnionWithArrayBuffer((ArrayBuffer or long) arg);
+ void passUnionWithString((DOMString or object) arg);
+ //void passUnionWithEnum((TestEnum or object) arg);
+ void passUnionWithCallback((TestCallback or long) arg);
+ void passUnionWithObject((object or long) arg);
+ //void passUnionWithDict((Dict or long) arg);
+
+ // binaryNames tests
+ void methodRenamedFrom();
+ void methodRenamedFrom(byte argument);
+ readonly attribute byte attributeGetterRenamedFrom;
+ attribute byte attributeRenamedFrom;
+
+ void passDictionary(optional Dict x);
+ void passOtherDictionary(optional GrandparentDict x);
+ void passSequenceOfDictionaries(sequence<Dict> x);
+ void passDictionaryOrLong(optional Dict x);
+ void passDictionaryOrLong(long x);
+
+ void passDictContainingDict(optional DictContainingDict arg);
+ void passDictContainingSequence(optional DictContainingSequence arg);
+
+ // EnforceRange/Clamp tests
+ void dontEnforceRangeOrClamp(byte arg);
+ void doEnforceRange([EnforceRange] byte arg);
+ void doClamp([Clamp] byte arg);
+
+ // Typedefs
+ const myLong myLongConstant = 5;
+ void exerciseTypedefInterfaces1(AnotherNameForTestInterface arg);
+ AnotherNameForTestInterface exerciseTypedefInterfaces2(NullableTestInterface arg);
+ void exerciseTypedefInterfaces3(YetAnotherNameForTestInterface arg);
+
+ // Miscellania
+ [LenientThis] attribute long attrWithLenientThis;
+};
+
+interface TestNonWrapperCacheInterface {
+};
+
+interface ImplementedInterfaceParent {
+ void implementedParentMethod();
+ attribute boolean implementedParentProperty;
+
+ const long implementedParentConstant = 8;
+};
+
+ImplementedInterfaceParent implements IndirectlyImplementedInterface;
+
+[NoInterfaceObject]
+interface IndirectlyImplementedInterface {
+ void indirectlyImplementedMethod();
+ attribute boolean indirectlyImplementedProperty;
+
+ const long indirectlyImplementedConstant = 9;
+};
+
+interface ImplementedInterface : ImplementedInterfaceParent {
+ void implementedMethod();
+ attribute boolean implementedProperty;
+
+ const long implementedConstant = 5;
+};
+
+interface DiamondImplements {
+ readonly attribute long diamondImplementedProperty;
+};
+interface DiamondBranch1A {
+};
+interface DiamondBranch1B {
+};
+interface DiamondBranch2A : DiamondImplements {
+};
+interface DiamondBranch2B : DiamondImplements {
+};
+TestInterface implements DiamondBranch1A;
+TestInterface implements DiamondBranch1B;
+TestInterface implements DiamondBranch2A;
+TestInterface implements DiamondBranch2B;
+DiamondBranch1A implements DiamondImplements;
+DiamondBranch1B implements DiamondImplements;
+
+dictionary Dict : ParentDict {
+ TestEnum someEnum;
+ long x;
+ long a;
+ long b = 8;
+ long z = 9;
+ DOMString str;
+ DOMString empty = "";
+ TestEnum otherEnum = "b";
+ DOMString otherStr = "def";
+ DOMString? yetAnotherStr = null;
+};
+
+dictionary ParentDict : GrandparentDict {
+ long c = 5;
+ TestInterface someInterface;
+ TestExternalInterface someExternalInterface;
+};
+
+dictionary DictContainingDict {
+ Dict memberDict;
+};
+
+dictionary DictContainingSequence {
+ sequence<long> ourSequence;
+};
+
+interface TestIndexedGetterInterface {
+ getter long item(unsigned long index);
+ [Infallible]
+ readonly attribute unsigned long length;
+};
+
+interface TestNamedGetterInterface {
+ getter DOMString (DOMString name);
+};
+
+interface TestIndexedAndNamedGetterInterface {
+ getter long (unsigned long index);
+ getter DOMString namedItem(DOMString name);
+ [Infallible]
+ readonly attribute unsigned long length;
+};
+
+interface TestIndexedSetterInterface {
+ setter creator void setItem(unsigned long index, DOMString item);
+};
+
+interface TestNamedSetterInterface {
+ setter creator void (DOMString name, TestIndexedSetterInterface item);
+};
+
+interface TestIndexedAndNamedSetterInterface {
+ setter creator void (unsigned long index, TestIndexedSetterInterface item);
+ setter creator void setNamedItem(DOMString name, TestIndexedSetterInterface item);
+};
+
+interface TestIndexedAndNamedGetterAndSetterInterface : TestIndexedSetterInterface {
+ getter long item(unsigned long index);
+ getter DOMString namedItem(DOMString name);
+ setter creator void (unsigned long index, long item);
+ setter creator void (DOMString name, DOMString item);
+ [Infallible]
+ stringifier DOMString ();
+ [Infallible]
+ readonly attribute unsigned long length;
+};
diff --git a/components/script/dom/bindings/codegen/test/TestDictionary.webidl b/components/script/dom/bindings/codegen/test/TestDictionary.webidl
new file mode 100644
index 00000000000..3dd91bd6500
--- /dev/null
+++ b/components/script/dom/bindings/codegen/test/TestDictionary.webidl
@@ -0,0 +1,9 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+dictionary GrandparentDict {
+ double someNum;
+}; \ No newline at end of file
diff --git a/components/script/dom/bindings/codegen/test/TestTypedef.webidl b/components/script/dom/bindings/codegen/test/TestTypedef.webidl
new file mode 100644
index 00000000000..7f758c79e8f
--- /dev/null
+++ b/components/script/dom/bindings/codegen/test/TestTypedef.webidl
@@ -0,0 +1,7 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+typedef TestInterface YetAnotherNameForTestInterface;
diff --git a/components/script/dom/bindings/codegen/test/file_bug775543.html b/components/script/dom/bindings/codegen/test/file_bug775543.html
new file mode 100644
index 00000000000..ee8c14c4d9c
--- /dev/null
+++ b/components/script/dom/bindings/codegen/test/file_bug775543.html
@@ -0,0 +1,5 @@
+<body>
+<script>
+worker = new Worker("a");
+</script>
+</body>
diff --git a/components/script/dom/bindings/codegen/test/forOf_iframe.html b/components/script/dom/bindings/codegen/test/forOf_iframe.html
new file mode 100644
index 00000000000..91417aba0e8
--- /dev/null
+++ b/components/script/dom/bindings/codegen/test/forOf_iframe.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>iframe content for test_forOf_iframe.html</title>
+</head>
+<body>
+ <div id="basket">
+ <span id="egg0"></span>
+ <span id="egg1"><span id="duckling1"></span></span>
+ <span id="egg2"></span>
+ </div>
+</body>
+</html>
diff --git a/components/script/dom/bindings/codegen/test/test_InstanceOf.html b/components/script/dom/bindings/codegen/test/test_InstanceOf.html
new file mode 100644
index 00000000000..3a5a76b1b21
--- /dev/null
+++ b/components/script/dom/bindings/codegen/test/test_InstanceOf.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=748983
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 748983</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=748983">Mozilla Bug 748983</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 748983 **/
+ok(document instanceof EventTarget, "document is an event target")
+ok(new XMLHttpRequest() instanceof XMLHttpRequest, "instanceof should work on XHR");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/components/script/dom/bindings/codegen/test/test_bug773326.html b/components/script/dom/bindings/codegen/test/test_bug773326.html
new file mode 100644
index 00000000000..2e3b1ea304d
--- /dev/null
+++ b/components/script/dom/bindings/codegen/test/test_bug773326.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Test for Bug 773326</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+test(function() {
+ new Worker("data:text/javascript,new XMLHttpRequest(42)");
+}, "Should not crash")
+</script>
diff --git a/components/script/dom/bindings/codegen/test/test_bug775543.html b/components/script/dom/bindings/codegen/test/test_bug775543.html
new file mode 100644
index 00000000000..d8df05f630f
--- /dev/null
+++ b/components/script/dom/bindings/codegen/test/test_bug775543.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=775543
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 775543</title>
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=775543">Mozilla Bug 775543</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<iframe id="t" src="http://example.org/tests/dom/bindings/test/file_bug775543.html" onload="test();"></iframe>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 775543 **/
+
+function test()
+{
+ var a = XPCNativeWrapper(document.getElementById("t").contentWindow.wrappedJSObject.worker);
+ isnot(XPCNativeWrapper.unwrap(a), a, "XPCNativeWrapper(Worker) should be an Xray wrapper");
+ a.toString();
+ ok(true, "Shouldn't crash when calling a method on an Xray wrapper around a worker");
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/components/script/dom/bindings/codegen/test/test_bug788369.html b/components/script/dom/bindings/codegen/test/test_bug788369.html
new file mode 100644
index 00000000000..787bd28fe34
--- /dev/null
+++ b/components/script/dom/bindings/codegen/test/test_bug788369.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=788369
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 788369</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=788369">Mozilla Bug 788369</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 788369 **/
+try {
+ var xhr = new(window.ActiveXObject || XMLHttpRequest)("Microsoft.XMLHTTP");
+ ok(xhr instanceof XMLHttpRequest, "Should have an XHR object");
+} catch (e) {
+ ok(false, "Should not throw exception when constructing: " + e);
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/components/script/dom/bindings/codegen/test/test_enums.html b/components/script/dom/bindings/codegen/test/test_enums.html
new file mode 100644
index 00000000000..e5dc519a0c9
--- /dev/null
+++ b/components/script/dom/bindings/codegen/test/test_enums.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Enums</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+test(function() {
+ var xhr = new XMLHttpRequest();
+ xhr.open("get", "foo")
+ assert_equals(xhr.responseType, "");
+ xhr.responseType = "foo";
+ assert_equals(xhr.responseType, "");
+}, "Assigning an invalid value to an enum attribute should not throw.");
+</script>
diff --git a/components/script/dom/bindings/codegen/test/test_forOf.html b/components/script/dom/bindings/codegen/test/test_forOf.html
new file mode 100644
index 00000000000..b1a3032a385
--- /dev/null
+++ b/components/script/dom/bindings/codegen/test/test_forOf.html
@@ -0,0 +1,94 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=725907
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 725907</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=725907">Mozilla Bug 725907</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<div id="basket">
+ <span id="egg0"></span>
+ <span id="egg1"><span id="duckling1"></span></span>
+ <span id="egg2"></span>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 725907 **/
+
+function runTestsForDocument(document, msgSuffix) {
+ function is(a, b, msg) { SimpleTest.is(a, b, msg + msgSuffix); }
+ function isnot(a, b, msg) { SimpleTest.isnot(a, b, msg + msgSuffix); }
+
+ var basket = document.getElementById("basket");
+ var egg3 = document.createElement("span");
+ egg3.id = "egg3";
+
+ var log = '';
+ for (var x of basket.childNodes) {
+ if (x.nodeType != x.TEXT_NODE)
+ log += x.id + ";";
+ }
+ is(log, "egg0;egg1;egg2;", "'for (x of div.childNodes)' should iterate over child nodes");
+
+ log = '';
+ for (var x of basket.childNodes) {
+ if (x.nodeType != x.TEXT_NODE) {
+ log += x.id + ";";
+ if (x.id == "egg1")
+ basket.appendChild(egg3);
+ }
+ }
+ is(log, "egg0;egg1;egg2;egg3;", "'for (x of div.childNodes)' should see elements added during iteration");
+
+ var iter1 = basket.childNodes.iterator();
+ var iter2 = basket.childNodes.iterator();
+ isnot(iter1, iter2, "nodelist.iterator() returns a new iterator each time");
+
+ log = '';
+ basket.appendChild(document.createTextNode("some text"));
+ for (var x of basket.children)
+ log += x.id + ";";
+ is(log, "egg0;egg1;egg2;egg3;", "'for (x of div.children)' should iterate over child elements");
+
+ var iter1 = basket.children.iterator();
+ var iter2 = basket.children.iterator();
+ isnot(iter1, iter2, ".iterator() returns a new iterator each time");
+
+ var count = 0;
+ for (var x of document.getElementsByClassName("hazardous-materials"))
+ count++;
+ is(count, 0, "'for (x of emptyNodeList)' loop should run zero times");
+
+ var log = '';
+ for (var x of document.querySelectorAll("span"))
+ log += x.id + ";";
+ is(log, "egg0;egg1;duckling1;egg2;egg3;", "for-of loop should work with a querySelectorAll() NodeList");
+}
+
+/* All the tests run twice. First, in this document, so without any wrappers. */
+runTestsForDocument(document, "");
+
+/* And once using the document of an iframe, so working with cross-compartment wrappers. */
+SimpleTest.waitForExplicitFinish();
+function iframeLoaded(iframe) {
+ runTestsForDocument(iframe.contentWindow.document, " (in iframe)");
+ SimpleTest.finish();
+}
+
+</script>
+
+<iframe src="forOf_iframe.html" onload="iframeLoaded(this)"></iframe>
+
+</pre>
+</body>
+</html>
diff --git a/components/script/dom/bindings/codegen/test/test_integers.html b/components/script/dom/bindings/codegen/test/test_integers.html
new file mode 100644
index 00000000000..6799fd791a8
--- /dev/null
+++ b/components/script/dom/bindings/codegen/test/test_integers.html
@@ -0,0 +1,45 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <canvas id="c" width="1" height="1"></canvas>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+ function testInt64NonFinite(arg) {
+ // We can use a WebGLRenderingContext to test conversion to 64-bit signed
+ // ints edge cases.
+ try {
+ var gl = $("c").getContext("experimental-webgl");
+ } catch (ex) {
+ // No WebGL support on MacOS 10.5. Just skip this test
+ todo(false, "WebGL not supported");
+ return;
+ }
+ is(gl.getError(), 0, "Should not start in an error state");
+
+ var b = gl.createBuffer();
+ gl.bindBuffer(gl.ARRAY_BUFFER, b);
+
+ var a = new Float32Array(1);
+ gl.bufferData(gl.ARRAY_BUFFER, a, gl.STATIC_DRAW);
+
+ gl.bufferSubData(gl.ARRAY_BUFFER, arg, a);
+
+ is(gl.getError(), 0, "Should have treated non-finite double as 0");
+ }
+
+ testInt64NonFinite(NaN);
+ testInt64NonFinite(Infinity);
+ testInt64NonFinite(-Infinity);
+</script>
+</pre>
+</body>
+</html>
diff --git a/components/script/dom/bindings/codegen/test/test_interfaceToString.html b/components/script/dom/bindings/codegen/test/test_interfaceToString.html
new file mode 100644
index 00000000000..cf670bf2d54
--- /dev/null
+++ b/components/script/dom/bindings/codegen/test/test_interfaceToString.html
@@ -0,0 +1,38 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=742156
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 742156</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=742156">Mozilla Bug 742156</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 742156 **/
+
+var nativeToString = ("" + String.replace).replace("replace", "EventTarget");
+try {
+ var eventTargetToString = "" + EventTarget;
+ is(eventTargetToString, nativeToString,
+ "Stringifying a DOM interface object should return the same string" +
+ "as stringifying a native function.");
+}
+catch (e) {
+ ok(false, "Stringifying a DOM interface object shouldn't throw.");
+}
+
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/components/script/dom/bindings/codegen/test/test_lookupGetter.html b/components/script/dom/bindings/codegen/test/test_lookupGetter.html
new file mode 100644
index 00000000000..306ee4f643c
--- /dev/null
+++ b/components/script/dom/bindings/codegen/test/test_lookupGetter.html
@@ -0,0 +1,49 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=462428
+-->
+<head>
+ <title>Test for Bug 462428</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=462428">Mozilla Bug 462428</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 462428 **/
+var x = new XMLHttpRequest;
+x.open("GET", "");
+var getter = x.__lookupGetter__('readyState');
+ok(getter !== undefined, "But able to look it up the normal way");
+ok(!x.hasOwnProperty('readyState'), "property should still be on the prototype");
+
+var sawProp = false;
+for (var i in x) {
+ if (i === "readyState") {
+ sawProp = true;
+ }
+}
+
+ok(sawProp, "property should be enumerable");
+
+is(getter.call(x), 1, "the getter actually works");
+
+Object.getPrototypeOf(x).__defineSetter__('readyState', function() {});
+is(getter.call(x), 1, "the getter works after defineSetter");
+
+is(x.responseType, "", "Should have correct responseType up front");
+var setter = x.__lookupSetter__('responseType');
+setter.call(x, "document");
+is(x.responseType, "document", "the setter is bound correctly");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/components/script/dom/bindings/codegen/test/test_sequence_wrapping.html b/components/script/dom/bindings/codegen/test/test_sequence_wrapping.html
new file mode 100644
index 00000000000..e4f18f9986c
--- /dev/null
+++ b/components/script/dom/bindings/codegen/test/test_sequence_wrapping.html
@@ -0,0 +1,60 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=775852
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 775852</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=775852">Mozilla Bug 775852</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <canvas width="1" height="1" id="c"></canvas>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 775852 **/
+function doTest() {
+ try {
+ var gl = $("c").getContext("experimental-webgl");
+ } catch (e) {
+ // No WebGL support on MacOS 10.5. Just skip this test
+ todo(false, "WebGL not supported");
+ return;
+ }
+ var setterCalled = false;
+
+ extLength = gl.getSupportedExtensions().length;
+ ok(extLength > 0,
+ "This test won't work right if we have no supported extensions");
+
+ Object.defineProperty(Array.prototype, "0",
+ {
+ set: function(val) {
+ setterCalled = true;
+ }
+ });
+
+ // Test that our property got defined correctly
+ var arr = []
+ arr[0] = 5;
+ is(setterCalled, true, "Setter should be called when setting prop on array");
+
+ setterCalled = false;
+
+ is(gl.getSupportedExtensions().length, extLength,
+ "We should still have the same number of extensions");
+
+ is(setterCalled, false,
+ "Setter should not be called when getting supported extensions");
+}
+doTest();
+</script>
+</pre>
+</body>
+</html>
diff --git a/components/script/dom/bindings/codegen/test/test_traceProtos.html b/components/script/dom/bindings/codegen/test/test_traceProtos.html
new file mode 100644
index 00000000000..195876744d6
--- /dev/null
+++ b/components/script/dom/bindings/codegen/test/test_traceProtos.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=744772
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 744772</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=744772">Mozilla Bug 744772</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 744772 **/
+
+SimpleTest.waitForExplicitFinish();
+
+function callback() {
+ new XMLHttpRequest().upload;
+ ok(true, "Accessing unreferenced DOM interface objects shouldn't crash");
+ SimpleTest.finish();
+}
+
+delete window.XMLHttpRequestUpload;
+SpecialPowers.exactGC(window, callback);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/components/script/dom/bindings/conversions.rs b/components/script/dom/bindings/conversions.rs
new file mode 100644
index 00000000000..8ce5b55e9d3
--- /dev/null
+++ b/components/script/dom/bindings/conversions.rs
@@ -0,0 +1,378 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! Conversions of Rust values to and from `JSVal`.
+
+use dom::bindings::js::{JS, JSRef, Root};
+use dom::bindings::str::ByteString;
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::bindings::utils::jsstring_to_str;
+use dom::bindings::utils::unwrap_jsmanaged;
+use servo_util::str::DOMString;
+
+use js::jsapi::{JSBool, JSContext, JSObject};
+use js::jsapi::{JS_ValueToUint64, JS_ValueToInt64};
+use js::jsapi::{JS_ValueToECMAUint32, JS_ValueToECMAInt32};
+use js::jsapi::{JS_ValueToUint16, JS_ValueToNumber, JS_ValueToBoolean};
+use js::jsapi::{JS_ValueToString, JS_GetStringCharsAndLength};
+use js::jsapi::{JS_NewUCStringCopyN, JS_NewStringCopyN};
+use js::jsapi::{JS_WrapValue};
+use js::jsval::JSVal;
+use js::jsval::{UndefinedValue, NullValue, BooleanValue, Int32Value, UInt32Value};
+use js::jsval::{StringValue, ObjectValue, ObjectOrNullValue};
+use js::glue::RUST_JS_NumberValue;
+use libc;
+use std::default::Default;
+use std::slice;
+
+use dom::bindings::codegen::PrototypeList;
+
+// FIXME (https://github.com/rust-lang/rfcs/pull/4)
+// remove Option<Self> arguments.
+pub trait IDLInterface {
+ fn get_prototype_id(_: Option<Self>) -> PrototypeList::id::ID;
+ fn get_prototype_depth(_: Option<Self>) -> uint;
+}
+
+/// A trait to convert Rust types to `JSVal`s.
+pub trait ToJSValConvertible {
+ /// Convert `self` to a `JSVal`. JSAPI failure causes a task failure.
+ fn to_jsval(&self, cx: *mut JSContext) -> JSVal;
+}
+
+/// A trait to convert `JSVal`s to Rust types.
+pub trait FromJSValConvertible<T> {
+ /// Convert `val` to type `Self`.
+ /// Optional configuration of type `T` can be passed as the `option`
+ /// argument.
+ /// If it returns `Err(())`, a JSAPI exception is pending.
+ fn from_jsval(cx: *mut JSContext, val: JSVal, option: T) -> Result<Self, ()>;
+}
+
+
+impl ToJSValConvertible for () {
+ fn to_jsval(&self, _cx: *mut JSContext) -> JSVal {
+ UndefinedValue()
+ }
+}
+
+impl ToJSValConvertible for JSVal {
+ fn to_jsval(&self, cx: *mut JSContext) -> JSVal {
+ let mut value = *self;
+ if unsafe { JS_WrapValue(cx, &mut value) } == 0 {
+ fail!("JS_WrapValue failed.");
+ }
+ value
+ }
+}
+
+unsafe fn convert_from_jsval<T: Default>(
+ cx: *mut JSContext, value: JSVal,
+ convert_fn: unsafe extern "C" fn(*mut JSContext, JSVal, *mut T) -> JSBool) -> Result<T, ()> {
+ let mut ret = Default::default();
+ if convert_fn(cx, value, &mut ret) == 0 {
+ Err(())
+ } else {
+ Ok(ret)
+ }
+}
+
+
+impl ToJSValConvertible for bool {
+ fn to_jsval(&self, _cx: *mut JSContext) -> JSVal {
+ BooleanValue(*self)
+ }
+}
+
+impl FromJSValConvertible<()> for bool {
+ fn from_jsval(cx: *mut JSContext, val: JSVal, _option: ()) -> Result<bool, ()> {
+ let result = unsafe { convert_from_jsval(cx, val, JS_ValueToBoolean) };
+ result.map(|b| b != 0)
+ }
+}
+
+impl ToJSValConvertible for i8 {
+ fn to_jsval(&self, _cx: *mut JSContext) -> JSVal {
+ Int32Value(*self as i32)
+ }
+}
+
+impl FromJSValConvertible<()> for i8 {
+ fn from_jsval(cx: *mut JSContext, val: JSVal, _option: ()) -> Result<i8, ()> {
+ let result = unsafe { convert_from_jsval(cx, val, JS_ValueToECMAInt32) };
+ result.map(|v| v as i8)
+ }
+}
+
+impl ToJSValConvertible for u8 {
+ fn to_jsval(&self, _cx: *mut JSContext) -> JSVal {
+ Int32Value(*self as i32)
+ }
+}
+
+impl FromJSValConvertible<()> for u8 {
+ fn from_jsval(cx: *mut JSContext, val: JSVal, _option: ()) -> Result<u8, ()> {
+ let result = unsafe { convert_from_jsval(cx, val, JS_ValueToECMAInt32) };
+ result.map(|v| v as u8)
+ }
+}
+
+impl ToJSValConvertible for i16 {
+ fn to_jsval(&self, _cx: *mut JSContext) -> JSVal {
+ Int32Value(*self as i32)
+ }
+}
+
+impl FromJSValConvertible<()> for i16 {
+ fn from_jsval(cx: *mut JSContext, val: JSVal, _option: ()) -> Result<i16, ()> {
+ let result = unsafe { convert_from_jsval(cx, val, JS_ValueToECMAInt32) };
+ result.map(|v| v as i16)
+ }
+}
+
+impl ToJSValConvertible for u16 {
+ fn to_jsval(&self, _cx: *mut JSContext) -> JSVal {
+ Int32Value(*self as i32)
+ }
+}
+
+impl FromJSValConvertible<()> for u16 {
+ fn from_jsval(cx: *mut JSContext, val: JSVal, _option: ()) -> Result<u16, ()> {
+ unsafe { convert_from_jsval(cx, val, JS_ValueToUint16) }
+ }
+}
+
+impl ToJSValConvertible for i32 {
+ fn to_jsval(&self, _cx: *mut JSContext) -> JSVal {
+ Int32Value(*self)
+ }
+}
+
+impl FromJSValConvertible<()> for i32 {
+ fn from_jsval(cx: *mut JSContext, val: JSVal, _option: ()) -> Result<i32, ()> {
+ unsafe { convert_from_jsval(cx, val, JS_ValueToECMAInt32) }
+ }
+}
+
+impl ToJSValConvertible for u32 {
+ fn to_jsval(&self, _cx: *mut JSContext) -> JSVal {
+ UInt32Value(*self)
+ }
+}
+
+impl FromJSValConvertible<()> for u32 {
+ fn from_jsval(cx: *mut JSContext, val: JSVal, _option: ()) -> Result<u32, ()> {
+ unsafe { convert_from_jsval(cx, val, JS_ValueToECMAUint32) }
+ }
+}
+
+impl ToJSValConvertible for i64 {
+ fn to_jsval(&self, _cx: *mut JSContext) -> JSVal {
+ unsafe {
+ RUST_JS_NumberValue(*self as f64)
+ }
+ }
+}
+
+impl FromJSValConvertible<()> for i64 {
+ fn from_jsval(cx: *mut JSContext, val: JSVal, _option: ()) -> Result<i64, ()> {
+ unsafe { convert_from_jsval(cx, val, JS_ValueToInt64) }
+ }
+}
+
+impl ToJSValConvertible for u64 {
+ fn to_jsval(&self, _cx: *mut JSContext) -> JSVal {
+ unsafe {
+ RUST_JS_NumberValue(*self as f64)
+ }
+ }
+}
+
+impl FromJSValConvertible<()> for u64 {
+ fn from_jsval(cx: *mut JSContext, val: JSVal, _option: ()) -> Result<u64, ()> {
+ unsafe { convert_from_jsval(cx, val, JS_ValueToUint64) }
+ }
+}
+
+impl ToJSValConvertible for f32 {
+ fn to_jsval(&self, _cx: *mut JSContext) -> JSVal {
+ unsafe {
+ RUST_JS_NumberValue(*self as f64)
+ }
+ }
+}
+
+impl FromJSValConvertible<()> for f32 {
+ fn from_jsval(cx: *mut JSContext, val: JSVal, _option: ()) -> Result<f32, ()> {
+ let result = unsafe { convert_from_jsval(cx, val, JS_ValueToNumber) };
+ result.map(|f| f as f32)
+ }
+}
+
+impl ToJSValConvertible for f64 {
+ fn to_jsval(&self, _cx: *mut JSContext) -> JSVal {
+ unsafe {
+ RUST_JS_NumberValue(*self)
+ }
+ }
+}
+
+impl FromJSValConvertible<()> for f64 {
+ fn from_jsval(cx: *mut JSContext, val: JSVal, _option: ()) -> Result<f64, ()> {
+ unsafe { convert_from_jsval(cx, val, JS_ValueToNumber) }
+ }
+}
+
+impl ToJSValConvertible for DOMString {
+ fn to_jsval(&self, cx: *mut JSContext) -> JSVal {
+ unsafe {
+ let string_utf16: Vec<u16> = self.as_slice().utf16_units().collect();
+ let jsstr = JS_NewUCStringCopyN(cx, string_utf16.as_ptr(), string_utf16.len() as libc::size_t);
+ if jsstr.is_null() {
+ fail!("JS_NewUCStringCopyN failed");
+ }
+ StringValue(&*jsstr)
+ }
+ }
+}
+
+/// Behavior for stringification of `JSVal`s.
+#[deriving(PartialEq)]
+pub enum StringificationBehavior {
+ /// Convert `null` to the string `"null"`.
+ Default,
+ /// Convert `null` to the empty string.
+ Empty,
+}
+
+impl Default for StringificationBehavior {
+ fn default() -> StringificationBehavior {
+ Default
+ }
+}
+
+impl FromJSValConvertible<StringificationBehavior> for DOMString {
+ fn from_jsval(cx: *mut JSContext, value: JSVal, nullBehavior: StringificationBehavior) -> Result<DOMString, ()> {
+ if nullBehavior == Empty && value.is_null() {
+ Ok("".to_string())
+ } else {
+ let jsstr = unsafe { JS_ValueToString(cx, value) };
+ if jsstr.is_null() {
+ debug!("JS_ValueToString failed");
+ Err(())
+ } else {
+ Ok(jsstring_to_str(cx, jsstr))
+ }
+ }
+ }
+}
+
+impl ToJSValConvertible for ByteString {
+ fn to_jsval(&self, cx: *mut JSContext) -> JSVal {
+ unsafe {
+ let slice = self.as_slice();
+ let jsstr = JS_NewStringCopyN(cx, slice.as_ptr() as *const libc::c_char,
+ slice.len() as libc::size_t);
+ if jsstr.is_null() {
+ fail!("JS_NewStringCopyN failed");
+ }
+ StringValue(&*jsstr)
+ }
+ }
+}
+
+impl FromJSValConvertible<()> for ByteString {
+ fn from_jsval(cx: *mut JSContext, value: JSVal, _option: ()) -> Result<ByteString, ()> {
+ unsafe {
+ let string = JS_ValueToString(cx, value);
+ if string.is_null() {
+ debug!("JS_ValueToString failed");
+ return Err(());
+ }
+
+ let mut length = 0;
+ let chars = JS_GetStringCharsAndLength(cx, string, &mut length);
+ slice::raw::buf_as_slice(chars, length as uint, |char_vec| {
+ if char_vec.iter().any(|&c| c > 0xFF) {
+ // XXX Throw
+ Err(())
+ } else {
+ Ok(ByteString::new(char_vec.iter().map(|&c| c as u8).collect()))
+ }
+ })
+ }
+ }
+}
+
+impl ToJSValConvertible for Reflector {
+ fn to_jsval(&self, cx: *mut JSContext) -> JSVal {
+ let obj = self.get_jsobject();
+ assert!(obj.is_not_null());
+ let mut value = ObjectValue(unsafe { &*obj });
+ if unsafe { JS_WrapValue(cx, &mut value) } == 0 {
+ fail!("JS_WrapValue failed.");
+ }
+ value
+ }
+}
+
+impl<T: Reflectable+IDLInterface> FromJSValConvertible<()> for JS<T> {
+ fn from_jsval(_cx: *mut JSContext, value: JSVal, _option: ()) -> Result<JS<T>, ()> {
+ if !value.is_object() {
+ return Err(());
+ }
+ unwrap_jsmanaged(value.to_object(),
+ IDLInterface::get_prototype_id(None::<T>),
+ IDLInterface::get_prototype_depth(None::<T>))
+ }
+}
+
+impl<'a, 'b, T: Reflectable> ToJSValConvertible for Root<'a, 'b, T> {
+ fn to_jsval(&self, cx: *mut JSContext) -> JSVal {
+ self.reflector().to_jsval(cx)
+ }
+}
+
+impl<'a, T: Reflectable> ToJSValConvertible for JSRef<'a, T> {
+ fn to_jsval(&self, cx: *mut JSContext) -> JSVal {
+ self.reflector().to_jsval(cx)
+ }
+}
+
+impl<'a, T: Reflectable> ToJSValConvertible for JS<T> {
+ fn to_jsval(&self, cx: *mut JSContext) -> JSVal {
+ self.reflector().to_jsval(cx)
+ }
+}
+
+impl<T: ToJSValConvertible> ToJSValConvertible for Option<T> {
+ fn to_jsval(&self, cx: *mut JSContext) -> JSVal {
+ match self {
+ &Some(ref value) => value.to_jsval(cx),
+ &None => NullValue(),
+ }
+ }
+}
+
+impl<X: Default, T: FromJSValConvertible<X>> FromJSValConvertible<()> for Option<T> {
+ fn from_jsval(cx: *mut JSContext, value: JSVal, _: ()) -> Result<Option<T>, ()> {
+ if value.is_null_or_undefined() {
+ Ok(None)
+ } else {
+ let option: X = Default::default();
+ let result: Result<T, ()> = FromJSValConvertible::from_jsval(cx, value, option);
+ result.map(Some)
+ }
+ }
+}
+
+impl ToJSValConvertible for *mut JSObject {
+ fn to_jsval(&self, cx: *mut JSContext) -> JSVal {
+ let mut wrapped = ObjectOrNullValue(*self);
+ unsafe {
+ assert!(JS_WrapValue(cx, &mut wrapped) != 0);
+ }
+ wrapped
+ }
+}
diff --git a/components/script/dom/bindings/error.rs b/components/script/dom/bindings/error.rs
new file mode 100644
index 00000000000..cb39e4f0755
--- /dev/null
+++ b/components/script/dom/bindings/error.rs
@@ -0,0 +1,114 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! Utilities to throw exceptions from Rust bindings.
+
+use dom::bindings::conversions::ToJSValConvertible;
+use dom::bindings::global::GlobalRef;
+use dom::domexception::DOMException;
+
+use js::jsapi::{JSContext, JSBool, JSObject};
+use js::jsapi::{JS_IsExceptionPending, JS_SetPendingException, JS_ReportPendingException};
+use js::jsapi::{JS_ReportErrorNumber, JSErrorFormatString, JSEXN_TYPEERR};
+use js::jsapi::{JS_SaveFrameChain, JS_RestoreFrameChain};
+use js::glue::{ReportError};
+use js::rust::with_compartment;
+
+use libc;
+use std::ptr;
+
+/// DOM exceptions that can be thrown by a native DOM method.
+#[deriving(Show)]
+pub enum Error {
+ IndexSize,
+ FailureUnknown,
+ NotFound,
+ HierarchyRequest,
+ InvalidCharacter,
+ NotSupported,
+ InvalidState,
+ Syntax,
+ NamespaceError,
+ InvalidAccess,
+ Security,
+ Network,
+ Abort,
+ Timeout
+}
+
+/// The return type for IDL operations that can throw DOM exceptions.
+pub type Fallible<T> = Result<T, Error>;
+
+/// The return type for IDL operations that can throw DOM exceptions and
+/// return `()`.
+pub type ErrorResult = Fallible<()>;
+
+/// Set a pending DOM exception for the given `result` on `cx`.
+pub fn throw_dom_exception(cx: *mut JSContext, global: &GlobalRef,
+ result: Error) {
+ assert!(unsafe { JS_IsExceptionPending(cx) } == 0);
+ let exception = DOMException::new_from_error(global, result).root();
+ let thrown = exception.to_jsval(cx);
+ unsafe {
+ JS_SetPendingException(cx, thrown);
+ }
+}
+
+/// Report a pending exception, thereby clearing it.
+pub fn report_pending_exception(cx: *mut JSContext, obj: *mut JSObject) {
+ unsafe {
+ if JS_IsExceptionPending(cx) != 0 {
+ let saved = JS_SaveFrameChain(cx);
+ with_compartment(cx, obj, || {
+ JS_ReportPendingException(cx);
+ });
+ if saved != 0 {
+ JS_RestoreFrameChain(cx);
+ }
+ }
+ }
+}
+
+/// Throw an exception to signal that a `JSVal` can not be converted to any of
+/// the types in an IDL union type.
+pub fn throw_not_in_union(cx: *mut JSContext, names: &'static str) -> JSBool {
+ assert!(unsafe { JS_IsExceptionPending(cx) } == 0);
+ let message = format!("argument could not be converted to any of: {}", names);
+ message.with_c_str(|string| {
+ unsafe { ReportError(cx, string) };
+ });
+ return 0;
+}
+
+/// Format string used to throw `TypeError`s.
+static ERROR_FORMAT_STRING_STRING: [libc::c_char, ..4] = [
+ '{' as libc::c_char,
+ '0' as libc::c_char,
+ '}' as libc::c_char,
+ 0 as libc::c_char,
+];
+
+/// Format string struct used to throw `TypeError`s.
+static ERROR_FORMAT_STRING: JSErrorFormatString = JSErrorFormatString {
+ format: &ERROR_FORMAT_STRING_STRING as *const libc::c_char,
+ argCount: 1,
+ exnType: JSEXN_TYPEERR as i16,
+};
+
+/// Callback used to throw `TypeError`s.
+extern fn get_error_message(_user_ref: *mut libc::c_void,
+ _locale: *const libc::c_char,
+ error_number: libc::c_uint) -> *const JSErrorFormatString
+{
+ assert_eq!(error_number, 0);
+ &ERROR_FORMAT_STRING as *const JSErrorFormatString
+}
+
+/// Throw a `TypeError` with the given message.
+pub fn throw_type_error(cx: *mut JSContext, error: &str) {
+ let error = error.to_c_str();
+ unsafe {
+ JS_ReportErrorNumber(cx, Some(get_error_message), ptr::mut_null(), 0, error.as_ptr());
+ }
+}
diff --git a/components/script/dom/bindings/global.rs b/components/script/dom/bindings/global.rs
new file mode 100644
index 00000000000..35b94d0e472
--- /dev/null
+++ b/components/script/dom/bindings/global.rs
@@ -0,0 +1,121 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! Abstractions for global scopes.
+//!
+//! This module contains smart pointers to global scopes, to simplify writing
+//! code that works in workers as well as window scopes.
+
+use dom::bindings::js::{JS, JSRef, Root};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::workerglobalscope::WorkerGlobalScope;
+use dom::window::Window;
+use script_task::ScriptChan;
+
+use servo_net::resource_task::ResourceTask;
+
+use js::jsapi::JSContext;
+
+use url::Url;
+
+/// A freely-copyable reference to a rooted global object.
+pub enum GlobalRef<'a> {
+ Window(JSRef<'a, Window>),
+ Worker(JSRef<'a, WorkerGlobalScope>),
+}
+
+/// A stack-based rooted reference to a global object.
+pub enum GlobalRoot<'a, 'b> {
+ WindowRoot(Root<'a, 'b, Window>),
+ WorkerRoot(Root<'a, 'b, WorkerGlobalScope>),
+}
+
+/// A traced reference to a global object, for use in fields of traced Rust
+/// structures.
+#[deriving(Encodable)]
+pub enum GlobalField {
+ WindowField(JS<Window>),
+ WorkerField(JS<WorkerGlobalScope>),
+}
+
+impl<'a> GlobalRef<'a> {
+ /// Get the `JSContext` for the `JSRuntime` associated with the thread
+ /// this global object is on.
+ pub fn get_cx(&self) -> *mut JSContext {
+ match *self {
+ Window(ref window) => window.get_cx(),
+ Worker(ref worker) => worker.get_cx(),
+ }
+ }
+
+ /// Extract a `Window`, causing task failure if the global object is not
+ /// a `Window`.
+ pub fn as_window<'b>(&'b self) -> &'b JSRef<'b, Window> {
+ match *self {
+ Window(ref window) => window,
+ Worker(_) => fail!("expected a Window scope"),
+ }
+ }
+
+ pub fn resource_task(&self) -> ResourceTask {
+ match *self {
+ Window(ref window) => window.page().resource_task.deref().clone(),
+ Worker(ref worker) => worker.resource_task().clone(),
+ }
+ }
+
+ pub fn get_url(&self) -> Url {
+ match *self {
+ Window(ref window) => window.get_url(),
+ Worker(ref worker) => worker.get_url().clone(),
+ }
+ }
+
+ /// `ScriptChan` used to send messages to the event loop of this global's
+ /// thread.
+ pub fn script_chan<'b>(&'b self) -> &'b ScriptChan {
+ match *self {
+ Window(ref window) => &window.script_chan,
+ Worker(ref worker) => worker.script_chan(),
+ }
+ }
+}
+
+impl<'a> Reflectable for GlobalRef<'a> {
+ fn reflector<'b>(&'b self) -> &'b Reflector {
+ match *self {
+ Window(ref window) => window.reflector(),
+ Worker(ref worker) => worker.reflector(),
+ }
+ }
+}
+
+impl<'a, 'b> GlobalRoot<'a, 'b> {
+ /// Obtain a safe reference to the global object that cannot outlive the
+ /// lifetime of this root.
+ pub fn root_ref<'c>(&'c self) -> GlobalRef<'c> {
+ match *self {
+ WindowRoot(ref window) => Window(window.root_ref()),
+ WorkerRoot(ref worker) => Worker(worker.root_ref()),
+ }
+ }
+}
+
+impl GlobalField {
+ /// Create a new `GlobalField` from a rooted reference.
+ pub fn from_rooted(global: &GlobalRef) -> GlobalField {
+ match *global {
+ Window(ref window) => WindowField(JS::from_rooted(window)),
+ Worker(ref worker) => WorkerField(JS::from_rooted(worker)),
+ }
+ }
+
+ /// Create a stack-bounded root for this reference.
+ pub fn root(&self) -> GlobalRoot {
+ match *self {
+ WindowField(ref window) => WindowRoot(window.root()),
+ WorkerField(ref worker) => WorkerRoot(worker.root()),
+ }
+ }
+}
diff --git a/components/script/dom/bindings/js.rs b/components/script/dom/bindings/js.rs
new file mode 100644
index 00000000000..ab8b3e3c7f5
--- /dev/null
+++ b/components/script/dom/bindings/js.rs
@@ -0,0 +1,496 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! Smart pointers for the JS-managed DOM objects.
+//!
+//! The DOM is made up of Rust types whose lifetime is entirely controlled by the whims of
+//! the SpiderMonkey garbage collector. The types in this module are designed to ensure
+//! that any interactions with said Rust types only occur on values that will remain alive
+//! the entire time.
+//!
+//! Here is a brief overview of the important types:
+//!
+//! - `JSRef<T>`: a freely-copyable reference to a rooted value.
+//! - `Root<T>`: a stack-based reference to a rooted value.
+//! - `JS<T>`: a pointer to JS-owned memory that can automatically be traced by the GC when
+//! encountered as a field of a Rust structure.
+//! - `Temporary<T>`: a value that will remain rooted for the duration of its lifetime.
+//!
+//! The rule of thumb is as follows:
+//!
+//! - All methods return `Temporary<T>`, to ensure the value remains alive until it is stored
+//! somewhere that is reachable by the GC.
+//! - All functions take `&JSRef<T>` arguments, to ensure that they will remain uncollected for
+//! the duration of their usage.
+//! - All types contain `JS<T>` fields and derive the `Encodable` trait, to ensure that they are
+//! transitively marked as reachable by the GC if the enclosing value is reachable.
+//! - All methods for type `T` are implemented for `JSRef<T>`, to ensure that the self value
+//! will not be collected for the duration of the method call.
+//!
+//! Both `Temporary<T>` and `JS<T>` do not allow access to their inner value without explicitly
+//! creating a stack-based root via the `root` method. This returns a `Root<T>`, which causes
+//! the JS-owned value to be uncollectable for the duration of the `Root` object's lifetime.
+//! A `JSRef<T>` can be obtained from a `Root<T>` either by dereferencing the `Root<T>` (`*rooted`)
+//! or explicitly calling the `root_ref` method. These `JSRef<T>` values are not allowed to
+//! outlive their originating `Root<T>`, to ensure that all interactions with the enclosed value
+//! only occur when said value is uncollectable, and will cause static lifetime errors if
+//! misused.
+//!
+//! Other miscellaneous helper traits:
+//!
+//! - `OptionalRootable` and `OptionalRootedRootable`: make rooting `Option` values easy via a `root` method
+//! - `ResultRootable`: make rooting successful `Result` values easy
+//! - `TemporaryPushable`: allows mutating vectors of `JS<T>` with new elements of `JSRef`/`Temporary`
+//! - `OptionalSettable`: allows assigning `Option` values of `JSRef`/`Temporary` to fields of `Option<JS<T>>`
+//! - `RootedReference`: makes obtaining an `Option<JSRef<T>>` from an `Option<Root<T>>` easy
+
+use dom::bindings::utils::{Reflector, Reflectable};
+use dom::node::Node;
+use dom::xmlhttprequest::{XMLHttpRequest, TrustedXHRAddress};
+use dom::worker::{Worker, TrustedWorkerAddress};
+use js::jsapi::JSObject;
+use layout_interface::TrustedNodeAddress;
+use script_task::StackRoots;
+
+use std::cell::{Cell, RefCell};
+use std::kinds::marker::ContravariantLifetime;
+use std::mem;
+
+/// A type that represents a JS-owned value that is rooted for the lifetime of this value.
+/// Importantly, it requires explicit rooting in order to interact with the inner value.
+/// Can be assigned into JS-owned member fields (i.e. `JS<T>` types) safely via the
+/// `JS<T>::assign` method or `OptionalSettable::assign` (for `Option<JS<T>>` fields).
+pub struct Temporary<T> {
+ inner: JS<T>,
+ /// On-stack JS pointer to assuage conservative stack scanner
+ _js_ptr: *mut JSObject,
+}
+
+impl<T> PartialEq for Temporary<T> {
+ fn eq(&self, other: &Temporary<T>) -> bool {
+ self.inner == other.inner
+ }
+}
+
+impl<T: Reflectable> Temporary<T> {
+ /// Create a new `Temporary` value from a JS-owned value.
+ pub fn new(inner: JS<T>) -> Temporary<T> {
+ Temporary {
+ inner: inner,
+ _js_ptr: inner.reflector().get_jsobject(),
+ }
+ }
+
+ /// Create a new `Temporary` value from a rooted value.
+ pub fn from_rooted<'a>(root: &JSRef<'a, T>) -> Temporary<T> {
+ Temporary::new(JS::from_rooted(root))
+ }
+
+ /// Create a stack-bounded root for this value.
+ pub fn root<'a, 'b>(self) -> Root<'a, 'b, T> {
+ let collection = StackRoots.get().unwrap();
+ unsafe {
+ (**collection).new_root(&self.inner)
+ }
+ }
+
+ unsafe fn inner(&self) -> JS<T> {
+ self.inner.clone()
+ }
+
+ //XXXjdm It would be lovely if this could be private.
+ pub unsafe fn transmute<To>(self) -> Temporary<To> {
+ mem::transmute(self)
+ }
+}
+
+/// A rooted, JS-owned value. Must only be used as a field in other JS-owned types.
+pub struct JS<T> {
+ ptr: *const T
+}
+
+impl<T> PartialEq for JS<T> {
+ fn eq(&self, other: &JS<T>) -> bool {
+ self.ptr == other.ptr
+ }
+}
+
+impl <T> Clone for JS<T> {
+ #[inline]
+ fn clone(&self) -> JS<T> {
+ JS {
+ ptr: self.ptr.clone()
+ }
+ }
+}
+
+impl JS<Node> {
+ /// Create a new JS-owned value wrapped from an address known to be a `Node` pointer.
+ pub unsafe fn from_trusted_node_address(inner: TrustedNodeAddress) -> JS<Node> {
+ let TrustedNodeAddress(addr) = inner;
+ JS {
+ ptr: addr as *const Node
+ }
+ }
+}
+
+impl JS<XMLHttpRequest> {
+ pub unsafe fn from_trusted_xhr_address(inner: TrustedXHRAddress) -> JS<XMLHttpRequest> {
+ let TrustedXHRAddress(addr) = inner;
+ JS {
+ ptr: addr as *const XMLHttpRequest
+ }
+ }
+}
+
+impl JS<Worker> {
+ pub unsafe fn from_trusted_worker_address(inner: TrustedWorkerAddress) -> JS<Worker> {
+ let TrustedWorkerAddress(addr) = inner;
+ JS {
+ ptr: addr as *const Worker
+ }
+ }
+}
+
+impl<T: Reflectable> JS<T> {
+ /// Create a new JS-owned value wrapped from a raw Rust pointer.
+ pub unsafe fn from_raw(raw: *const T) -> JS<T> {
+ JS {
+ ptr: raw
+ }
+ }
+
+
+ /// Root this JS-owned value to prevent its collection as garbage.
+ pub fn root<'a, 'b>(&self) -> Root<'a, 'b, T> {
+ let collection = StackRoots.get().unwrap();
+ unsafe {
+ (**collection).new_root(self)
+ }
+ }
+}
+
+impl<T: Assignable<U>, U: Reflectable> JS<U> {
+ pub fn from_rooted(root: &T) -> JS<U> {
+ unsafe {
+ root.get_js()
+ }
+ }
+}
+
+//XXXjdm This is disappointing. This only gets called from trace hooks, in theory,
+// so it's safe to assume that self is rooted and thereby safe to access.
+impl<T: Reflectable> Reflectable for JS<T> {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ unsafe {
+ (*self.unsafe_get()).reflector()
+ }
+ }
+}
+
+impl<T: Reflectable> JS<T> {
+ /// Returns an unsafe pointer to the interior of this JS object without touching the borrow
+ /// flags. This is the only method that be safely accessed from layout. (The fact that this
+ /// is unsafe is what necessitates the layout wrappers.)
+ pub unsafe fn unsafe_get(&self) -> *mut T {
+ mem::transmute_copy(&self.ptr)
+ }
+
+ /// Store an unrooted value in this field. This is safe under the assumption that JS<T>
+ /// values are only used as fields in DOM types that are reachable in the GC graph,
+ /// so this unrooted value becomes transitively rooted for the lifetime of its new owner.
+ pub fn assign(&mut self, val: Temporary<T>) {
+ *self = unsafe { val.inner() };
+ }
+}
+
+impl<From, To> JS<From> {
+ //XXXjdm It would be lovely if this could be private.
+ pub unsafe fn transmute(self) -> JS<To> {
+ mem::transmute(self)
+ }
+
+ pub unsafe fn transmute_copy(&self) -> JS<To> {
+ mem::transmute_copy(self)
+ }
+}
+
+
+/// Get an `Option<JSRef<T>>` out of an `Option<Root<T>>`
+pub trait RootedReference<T> {
+ fn root_ref<'a>(&'a self) -> Option<JSRef<'a, T>>;
+}
+
+impl<'a, 'b, T: Reflectable> RootedReference<T> for Option<Root<'a, 'b, T>> {
+ fn root_ref<'a>(&'a self) -> Option<JSRef<'a, T>> {
+ self.as_ref().map(|root| root.root_ref())
+ }
+}
+
+/// Get an `Option<Option<JSRef<T>>>` out of an `Option<Option<Root<T>>>`
+pub trait OptionalRootedReference<T> {
+ fn root_ref<'a>(&'a self) -> Option<Option<JSRef<'a, T>>>;
+}
+
+impl<'a, 'b, T: Reflectable> OptionalRootedReference<T> for Option<Option<Root<'a, 'b, T>>> {
+ fn root_ref<'a>(&'a self) -> Option<Option<JSRef<'a, T>>> {
+ self.as_ref().map(|inner| inner.root_ref())
+ }
+}
+
+/// Trait that allows extracting a `JS<T>` value from a variety of rooting-related containers,
+/// which in general is an unsafe operation since they can outlive the rooted lifetime of the
+/// original value.
+/*definitely not public*/ trait Assignable<T> {
+ unsafe fn get_js(&self) -> JS<T>;
+}
+
+impl<T> Assignable<T> for JS<T> {
+ unsafe fn get_js(&self) -> JS<T> {
+ self.clone()
+ }
+}
+
+impl<'a, T> Assignable<T> for JSRef<'a, T> {
+ unsafe fn get_js(&self) -> JS<T> {
+ self.unrooted()
+ }
+}
+
+impl<T: Reflectable> Assignable<T> for Temporary<T> {
+ unsafe fn get_js(&self) -> JS<T> {
+ self.inner()
+ }
+}
+
+/// Assign an optional rootable value (either of `JS<T>` or `Temporary<T>`) to an optional
+/// field of a DOM type (ie. `Option<JS<T>>`)
+pub trait OptionalSettable<T> {
+ fn assign(&self, val: Option<T>);
+}
+
+impl<T: Assignable<U>, U: Reflectable> OptionalSettable<T> for Cell<Option<JS<U>>> {
+ fn assign(&self, val: Option<T>) {
+ self.set(val.map(|val| unsafe { val.get_js() }));
+ }
+}
+
+
+/// Root a rootable `Option` type (used for `Option<Temporary<T>>`)
+pub trait OptionalRootable<T> {
+ fn root<'a, 'b>(self) -> Option<Root<'a, 'b, T>>;
+}
+
+impl<T: Reflectable> OptionalRootable<T> for Option<Temporary<T>> {
+ fn root<'a, 'b>(self) -> Option<Root<'a, 'b, T>> {
+ self.map(|inner| inner.root())
+ }
+}
+
+/// Return an unrooted type for storing in optional DOM fields
+pub trait OptionalUnrootable<T> {
+ fn unrooted(&self) -> Option<JS<T>>;
+}
+
+impl<'a, T: Reflectable> OptionalUnrootable<T> for Option<JSRef<'a, T>> {
+ fn unrooted(&self) -> Option<JS<T>> {
+ self.as_ref().map(|inner| JS::from_rooted(inner))
+ }
+}
+
+/// Root a rootable `Option` type (used for `Option<JS<T>>`)
+pub trait OptionalRootedRootable<T> {
+ fn root<'a, 'b>(&self) -> Option<Root<'a, 'b, T>>;
+}
+
+impl<T: Reflectable> OptionalRootedRootable<T> for Option<JS<T>> {
+ fn root<'a, 'b>(&self) -> Option<Root<'a, 'b, T>> {
+ self.as_ref().map(|inner| inner.root())
+ }
+}
+
+/// Root a rootable `Option<Option>` type (used for `Option<Option<JS<T>>>`)
+pub trait OptionalOptionalRootedRootable<T> {
+ fn root<'a, 'b>(&self) -> Option<Option<Root<'a, 'b, T>>>;
+}
+
+impl<T: Reflectable> OptionalOptionalRootedRootable<T> for Option<Option<JS<T>>> {
+ fn root<'a, 'b>(&self) -> Option<Option<Root<'a, 'b, T>>> {
+ self.as_ref().map(|inner| inner.root())
+ }
+}
+
+
+/// Root a rootable `Result` type (any of `Temporary<T>` or `JS<T>`)
+pub trait ResultRootable<T,U> {
+ fn root<'a, 'b>(self) -> Result<Root<'a, 'b, T>, U>;
+}
+
+impl<T: Reflectable, U> ResultRootable<T, U> for Result<Temporary<T>, U> {
+ fn root<'a, 'b>(self) -> Result<Root<'a, 'b, T>, U> {
+ self.map(|inner| inner.root())
+ }
+}
+
+impl<T: Reflectable, U> ResultRootable<T, U> for Result<JS<T>, U> {
+ fn root<'a, 'b>(self) -> Result<Root<'a, 'b, T>, U> {
+ self.map(|inner| inner.root())
+ }
+}
+
+/// Provides a facility to push unrooted values onto lists of rooted values. This is safe
+/// under the assumption that said lists are reachable via the GC graph, and therefore the
+/// new values are transitively rooted for the lifetime of their new owner.
+pub trait TemporaryPushable<T> {
+ fn push_unrooted(&mut self, val: &T);
+ fn insert_unrooted(&mut self, index: uint, val: &T);
+}
+
+impl<T: Assignable<U>, U: Reflectable> TemporaryPushable<T> for Vec<JS<U>> {
+ fn push_unrooted(&mut self, val: &T) {
+ self.push(unsafe { val.get_js() });
+ }
+
+ fn insert_unrooted(&mut self, index: uint, val: &T) {
+ self.insert(index, unsafe { val.get_js() });
+ }
+}
+
+/// An opaque, LIFO rooting mechanism.
+pub struct RootCollection {
+ roots: RefCell<Vec<*mut JSObject>>,
+}
+
+impl RootCollection {
+ /// Create an empty collection of roots
+ pub fn new() -> RootCollection {
+ RootCollection {
+ roots: RefCell::new(vec!()),
+ }
+ }
+
+ /// Create a new stack-bounded root that will not outlive this collection
+ fn new_root<'a, 'b, T: Reflectable>(&'a self, unrooted: &JS<T>) -> Root<'a, 'b, T> {
+ Root::new(self, unrooted)
+ }
+
+ /// Track a stack-based root to ensure LIFO root ordering
+ fn root<'a, 'b, T: Reflectable>(&self, untracked: &Root<'a, 'b, T>) {
+ let mut roots = self.roots.borrow_mut();
+ roots.push(untracked.js_ptr);
+ debug!(" rooting {:?}", untracked.js_ptr);
+ }
+
+ /// Stop tracking a stack-based root, asserting if LIFO root ordering has been violated
+ fn unroot<'a, 'b, T: Reflectable>(&self, rooted: &Root<'a, 'b, T>) {
+ let mut roots = self.roots.borrow_mut();
+ debug!("unrooting {:?} (expecting {:?}", roots.last().unwrap(), rooted.js_ptr);
+ assert!(*roots.last().unwrap() == rooted.js_ptr);
+ roots.pop().unwrap();
+ }
+}
+
+/// A rooted JS value. The JS value is pinned for the duration of this object's lifetime;
+/// roots are additive, so this object's destruction will not invalidate other roots
+/// for the same JS value. `Root`s cannot outlive the associated `RootCollection` object.
+/// Attempts to transfer ownership of a `Root` via moving will trigger dynamic unrooting
+/// failures due to incorrect ordering.
+pub struct Root<'a, 'b, T> {
+ /// List that ensures correct dynamic root ordering
+ root_list: &'a RootCollection,
+ /// Reference to rooted value that must not outlive this container
+ jsref: JSRef<'b, T>,
+ /// On-stack JS pointer to assuage conservative stack scanner
+ js_ptr: *mut JSObject,
+}
+
+impl<'a, 'b, T: Reflectable> Root<'a, 'b, T> {
+ /// Create a new stack-bounded root for the provided JS-owned value.
+ /// It cannot not outlive its associated `RootCollection`, and it contains a `JSRef`
+ /// which cannot outlive this new `Root`.
+ fn new(roots: &'a RootCollection, unrooted: &JS<T>) -> Root<'a, 'b, T> {
+ let root = Root {
+ root_list: roots,
+ jsref: JSRef {
+ ptr: unrooted.ptr.clone(),
+ chain: ContravariantLifetime,
+ },
+ js_ptr: unrooted.reflector().get_jsobject(),
+ };
+ roots.root(&root);
+ root
+ }
+
+ /// Obtain a safe reference to the wrapped JS owned-value that cannot outlive
+ /// the lifetime of this root.
+ pub fn root_ref<'b>(&'b self) -> JSRef<'b,T> {
+ self.jsref.clone()
+ }
+}
+
+#[unsafe_destructor]
+impl<'a, 'b, T: Reflectable> Drop for Root<'a, 'b, T> {
+ fn drop(&mut self) {
+ self.root_list.unroot(self);
+ }
+}
+
+impl<'a, 'b, T: Reflectable> Deref<JSRef<'b, T>> for Root<'a, 'b, T> {
+ fn deref<'c>(&'c self) -> &'c JSRef<'b, T> {
+ &self.jsref
+ }
+}
+
+impl<'a, T: Reflectable> Deref<T> for JSRef<'a, T> {
+ fn deref<'b>(&'b self) -> &'b T {
+ unsafe {
+ &*self.ptr
+ }
+ }
+}
+
+/// Encapsulates a reference to something that is guaranteed to be alive. This is freely copyable.
+pub struct JSRef<'a, T> {
+ ptr: *const T,
+ chain: ContravariantLifetime<'a>,
+}
+
+impl<'a, T> Clone for JSRef<'a, T> {
+ fn clone(&self) -> JSRef<'a, T> {
+ JSRef {
+ ptr: self.ptr.clone(),
+ chain: self.chain,
+ }
+ }
+}
+
+impl<'a, T> PartialEq for JSRef<'a, T> {
+ fn eq(&self, other: &JSRef<T>) -> bool {
+ self.ptr == other.ptr
+ }
+}
+
+impl<'a,T> JSRef<'a,T> {
+ //XXXjdm It would be lovely if this could be private.
+ pub unsafe fn transmute<'b, To>(&'b self) -> &'b JSRef<'a, To> {
+ mem::transmute(self)
+ }
+
+ //XXXjdm It would be lovely if this could be private.
+ pub unsafe fn transmute_mut<'b, To>(&'b mut self) -> &'b mut JSRef<'a, To> {
+ mem::transmute(self)
+ }
+
+ pub fn unrooted(&self) -> JS<T> {
+ JS {
+ ptr: self.ptr
+ }
+ }
+}
+
+impl<'a, T: Reflectable> Reflectable for JSRef<'a, T> {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.deref().reflector()
+ }
+}
diff --git a/components/script/dom/bindings/proxyhandler.rs b/components/script/dom/bindings/proxyhandler.rs
new file mode 100644
index 00000000000..f2c1486280d
--- /dev/null
+++ b/components/script/dom/bindings/proxyhandler.rs
@@ -0,0 +1,155 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+///! Utilities for the implementation of JSAPI proxy handlers.
+
+use dom::bindings::utils::delete_property_by_id;
+use dom::bindings::utils::is_dom_proxy;
+use js::jsapi::{JSContext, jsid, JSPropertyDescriptor, JSObject, JSString, jschar};
+use js::jsapi::{JS_GetPropertyDescriptorById, JS_NewUCString, JS_malloc, JS_free};
+use js::jsapi::{JS_DefinePropertyById, JS_NewObjectWithGivenProto};
+use js::jsapi::{JS_ReportErrorFlagsAndNumber, JS_StrictPropertyStub};
+use js::jsapi::{JSREPORT_WARNING, JSREPORT_STRICT, JSREPORT_STRICT_MODE_ERROR};
+use js::jsval::ObjectValue;
+use js::glue::GetProxyExtra;
+use js::glue::{GetObjectProto, GetObjectParent, SetProxyExtra, GetProxyHandler};
+use js::glue::InvokeGetOwnPropertyDescriptor;
+use js::glue::RUST_js_GetErrorMessage;
+use js::{JSPROP_GETTER, JSPROP_ENUMERATE, JSPROP_READONLY, JSRESOLVE_QUALIFIED};
+
+use libc;
+use std::mem;
+use std::ptr;
+use std::string;
+use std::mem::size_of;
+
+static JSPROXYSLOT_EXPANDO: u32 = 0;
+
+pub extern fn getPropertyDescriptor(cx: *mut JSContext, proxy: *mut JSObject,
+ id: jsid, set: bool,
+ desc: *mut JSPropertyDescriptor)
+ -> bool {
+ unsafe {
+ let handler = GetProxyHandler(proxy);
+ if !InvokeGetOwnPropertyDescriptor(handler, cx, proxy, id, set, desc) {
+ return false;
+ }
+ if (*desc).obj.is_not_null() {
+ return true;
+ }
+
+ //let proto = JS_GetPrototype(proxy);
+ let proto = GetObjectProto(proxy);
+ if proto.is_null() {
+ (*desc).obj = ptr::mut_null();
+ return true;
+ }
+
+ JS_GetPropertyDescriptorById(cx, proto, id, JSRESOLVE_QUALIFIED, desc) != 0
+ }
+}
+
+pub fn defineProperty_(cx: *mut JSContext, proxy: *mut JSObject, id: jsid,
+ desc: *mut JSPropertyDescriptor) -> bool {
+ static JSMSG_GETTER_ONLY: libc::c_uint = 160;
+
+ unsafe {
+ //FIXME: Workaround for https://github.com/mozilla/rust/issues/13385
+ let setter: *const libc::c_void = mem::transmute((*desc).setter);
+ let setter_stub: *const libc::c_void = mem::transmute(JS_StrictPropertyStub);
+ if ((*desc).attrs & JSPROP_GETTER) != 0 && setter == setter_stub {
+ return JS_ReportErrorFlagsAndNumber(cx,
+ JSREPORT_WARNING | JSREPORT_STRICT |
+ JSREPORT_STRICT_MODE_ERROR,
+ Some(RUST_js_GetErrorMessage), ptr::mut_null(),
+ JSMSG_GETTER_ONLY) != 0;
+ }
+
+ let expando = EnsureExpandoObject(cx, proxy);
+ if expando.is_null() {
+ return false;
+ }
+
+ return JS_DefinePropertyById(cx, expando, id, (*desc).value, (*desc).getter,
+ (*desc).setter, (*desc).attrs) != 0;
+ }
+}
+
+pub extern fn defineProperty(cx: *mut JSContext, proxy: *mut JSObject, id: jsid,
+ desc: *mut JSPropertyDescriptor) -> bool {
+ defineProperty_(cx, proxy, id, desc)
+}
+
+pub extern fn delete_(cx: *mut JSContext, proxy: *mut JSObject, id: jsid,
+ bp: *mut bool) -> bool {
+ unsafe {
+ let expando = EnsureExpandoObject(cx, proxy);
+ if expando.is_null() {
+ return false;
+ }
+
+ return delete_property_by_id(cx, expando, id, &mut *bp);
+ }
+}
+
+pub fn _obj_toString(cx: *mut JSContext, className: *const libc::c_char) -> *mut JSString {
+ unsafe {
+ let name = string::raw::from_buf(className as *const i8 as *const u8);
+ let nchars = "[object ]".len() + name.len();
+ let chars: *mut jschar = JS_malloc(cx, (nchars + 1) as libc::size_t * (size_of::<jschar>() as libc::size_t)) as *mut jschar;
+ if chars.is_null() {
+ return ptr::mut_null();
+ }
+
+ let result = format!("[object {}]", name);
+ let result = result.as_slice();
+ for (i, c) in result.chars().enumerate() {
+ *chars.offset(i as int) = c as jschar;
+ }
+ *chars.offset(nchars as int) = 0;
+ let jsstr = JS_NewUCString(cx, chars, nchars as libc::size_t);
+ if jsstr.is_null() {
+ JS_free(cx, chars as *mut libc::c_void);
+ }
+ jsstr
+ }
+}
+
+pub fn GetExpandoObject(obj: *mut JSObject) -> *mut JSObject {
+ unsafe {
+ assert!(is_dom_proxy(obj));
+ let val = GetProxyExtra(obj, JSPROXYSLOT_EXPANDO);
+ if val.is_undefined() {
+ ptr::mut_null()
+ } else {
+ val.to_object()
+ }
+ }
+}
+
+pub fn EnsureExpandoObject(cx: *mut JSContext, obj: *mut JSObject) -> *mut JSObject {
+ unsafe {
+ assert!(is_dom_proxy(obj));
+ let mut expando = GetExpandoObject(obj);
+ if expando.is_null() {
+ expando = JS_NewObjectWithGivenProto(cx, ptr::mut_null(),
+ ptr::mut_null(),
+ GetObjectParent(obj));
+ if expando.is_null() {
+ return ptr::mut_null();
+ }
+
+ SetProxyExtra(obj, JSPROXYSLOT_EXPANDO, ObjectValue(&*expando));
+ }
+ return expando;
+ }
+}
+
+pub fn FillPropertyDescriptor(desc: &mut JSPropertyDescriptor, obj: *mut JSObject, readonly: bool) {
+ desc.obj = obj;
+ desc.attrs = if readonly { JSPROP_READONLY } else { 0 } | JSPROP_ENUMERATE;
+ desc.getter = None;
+ desc.setter = None;
+ desc.shortid = 0;
+}
diff --git a/components/script/dom/bindings/str.rs b/components/script/dom/bindings/str.rs
new file mode 100644
index 00000000000..825716bdf3e
--- /dev/null
+++ b/components/script/dom/bindings/str.rs
@@ -0,0 +1,157 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! The `ByteString` struct.
+
+use std::from_str::FromStr;
+use std::hash::{Hash, sip};
+use std::path::BytesContainer;
+use std::str;
+
+/// Encapsulates the IDL `ByteString` type.
+#[deriving(Encodable,Clone,Eq,PartialEq)]
+pub struct ByteString(Vec<u8>);
+
+impl ByteString {
+ /// Creates a new `ByteString`.
+ pub fn new(value: Vec<u8>) -> ByteString {
+ ByteString(value)
+ }
+
+ /// Returns `self` as a string, if it encodes valid UTF-8, and `None`
+ /// otherwise.
+ pub fn as_str<'a>(&'a self) -> Option<&'a str> {
+ let ByteString(ref vec) = *self;
+ str::from_utf8(vec.as_slice())
+ }
+
+ /// Returns the underlying vector as a slice.
+ pub fn as_slice<'a>(&'a self) -> &'a [u8] {
+ let ByteString(ref vector) = *self;
+ vector.as_slice()
+ }
+
+ /// Returns the length.
+ pub fn len(&self) -> uint {
+ let ByteString(ref vector) = *self;
+ vector.len()
+ }
+
+ /// Compare `self` to `other`, matching A–Z and a–z as equal.
+ pub fn eq_ignore_case(&self, other: &ByteString) -> bool {
+ // XXXManishearth make this more efficient
+ self.to_lower() == other.to_lower()
+ }
+
+ /// Returns `self` with A–Z replaced by a–z.
+ pub fn to_lower(&self) -> ByteString {
+ let ByteString(ref vec) = *self;
+ ByteString::new(vec.iter().map(|&x| {
+ if x > 'A' as u8 && x < 'Z' as u8 {
+ x + ('a' as u8) - ('A' as u8)
+ } else {
+ x
+ }
+ }).collect())
+ }
+
+ /// Returns whether `self` is a `token`, as defined by
+ /// [RFC 2616](http://tools.ietf.org/html/rfc2616#page-17).
+ pub fn is_token(&self) -> bool {
+ let ByteString(ref vec) = *self;
+ if vec.len() == 0 {
+ return false; // A token must be at least a single character
+ }
+ vec.iter().all(|&x| {
+ // http://tools.ietf.org/html/rfc2616#section-2.2
+ match x {
+ 0..31 | 127 => false, // CTLs
+ 40 | 41 | 60 | 62 | 64 |
+ 44 | 59 | 58 | 92 | 34 |
+ 47 | 91 | 93 | 63 | 61 |
+ 123 | 125 | 32 => false, // separators
+ x if x > 127 => false, // non-CHARs
+ _ => true
+ }
+ })
+ }
+
+ /// Returns whether `self` is a `field-value`, as defined by
+ /// [RFC 2616](http://tools.ietf.org/html/rfc2616#page-32).
+ pub fn is_field_value(&self) -> bool {
+ // Classifications of characters necessary for the [CRLF] (SP|HT) rule
+ #[deriving(PartialEq)]
+ enum PreviousCharacter {
+ Other,
+ CR,
+ LF,
+ SPHT // SP or HT
+ }
+ let ByteString(ref vec) = *self;
+ let mut prev = Other; // The previous character
+ vec.iter().all(|&x| {
+ // http://tools.ietf.org/html/rfc2616#section-2.2
+ match x {
+ 13 => { // CR
+ if prev == Other || prev == SPHT {
+ prev = CR;
+ true
+ } else {
+ false
+ }
+ },
+ 10 => { // LF
+ if prev == CR {
+ prev = LF;
+ true
+ } else {
+ false
+ }
+ },
+ 32 => { // SP
+ if prev == LF || prev == SPHT {
+ prev = SPHT;
+ true
+ } else if prev == Other {
+ // Counts as an Other here, since it's not preceded by a CRLF
+ // SP is not a CTL, so it can be used anywhere
+ // though if used immediately after a CR the CR is invalid
+ // We don't change prev since it's already Other
+ true
+ } else {
+ false
+ }
+ },
+ 9 => { // HT
+ if prev == LF || prev == SPHT {
+ prev = SPHT;
+ true
+ } else {
+ false
+ }
+ },
+ 0..31 | 127 => false, // CTLs
+ x if x > 127 => false, // non ASCII
+ _ if prev == Other || prev == SPHT => {
+ prev = Other;
+ true
+ },
+ _ => false // Previous character was a CR/LF but not part of the [CRLF] (SP|HT) rule
+ }
+ })
+ }
+}
+
+impl Hash for ByteString {
+ fn hash(&self, state: &mut sip::SipState) {
+ let ByteString(ref vec) = *self;
+ vec.hash(state);
+ }
+}
+
+impl FromStr for ByteString {
+ fn from_str(s: &str) -> Option<ByteString> {
+ Some(ByteString::new(s.container_into_owned_bytes()))
+ }
+}
diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs
new file mode 100644
index 00000000000..42d944e9781
--- /dev/null
+++ b/components/script/dom/bindings/trace.rs
@@ -0,0 +1,185 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! Utilities for tracing JS-managed values.
+//!
+//! The lifetime of DOM objects is managed by the SpiderMonkey Garbage
+//! Collector. A rooted DOM object implementing the interface `Foo` is traced
+//! as follows:
+//!
+//! 1. The GC calls `_trace` defined in `FooBinding` during the marking
+//! phase. (This happens through `JSClass.trace` for non-proxy bindings, and
+//! through `ProxyTraps.trace` otherwise.)
+//! 2. `_trace` calls `Foo::trace()` (an implementation of `JSTraceable`,
+//! defined in `InheritTypes.rs`).
+//! 3. `Foo::trace()` calls `Foo::encode()` (an implementation of `Encodable`).
+//! This implementation is typically derived by a `#[deriving(Encodable)]`
+//! annotation on the Rust struct.
+//! 4. For all fields (except those wrapped in `Untraceable`), `Foo::encode()`
+//! calls `encode()` on the field.
+//!
+//! For example, for fields of type `JS<T>`, `JS<T>::encode()` calls
+//! `trace_reflector()`.
+//! 6. `trace_reflector()` calls `trace_object()` with the `JSObject` for the
+//! reflector.
+//! 7. `trace_object()` calls `JS_CallTracer()` to notify the GC, which will
+//! add the object to the graph, and will trace that object as well.
+
+use dom::bindings::js::JS;
+use dom::bindings::utils::{Reflectable, Reflector};
+
+use js::jsapi::{JSObject, JSTracer, JS_CallTracer, JSTRACE_OBJECT};
+use js::jsval::JSVal;
+
+use libc;
+use std::mem;
+use std::cell::{Cell, RefCell};
+use serialize::{Encodable, Encoder};
+
+// IMPORTANT: We rely on the fact that we never attempt to encode DOM objects using
+// any encoder but JSTracer. Since we derive trace hooks automatically,
+// we are unfortunately required to use generic types everywhere and
+// unsafely cast to the concrete JSTracer we actually require.
+
+fn get_jstracer<'a, S: Encoder<E>, E>(s: &'a mut S) -> &'a mut JSTracer {
+ unsafe {
+ mem::transmute(s)
+ }
+}
+
+impl<T: Reflectable+Encodable<S, E>, S: Encoder<E>, E> Encodable<S, E> for JS<T> {
+ fn encode(&self, s: &mut S) -> Result<(), E> {
+ trace_reflector(get_jstracer(s), "", self.reflector());
+ Ok(())
+ }
+}
+
+impl<S: Encoder<E>, E> Encodable<S, E> for Reflector {
+ fn encode(&self, _s: &mut S) -> Result<(), E> {
+ Ok(())
+ }
+}
+
+/// A trait to allow tracing (only) DOM objects.
+pub trait JSTraceable {
+ fn trace(&self, trc: *mut JSTracer);
+}
+
+/// Trace a `JSVal`.
+pub fn trace_jsval(tracer: *mut JSTracer, description: &str, val: JSVal) {
+ if !val.is_markable() {
+ return;
+ }
+
+ unsafe {
+ let name = description.to_c_str();
+ (*tracer).debugPrinter = None;
+ (*tracer).debugPrintIndex = -1;
+ (*tracer).debugPrintArg = name.as_ptr() as *const libc::c_void;
+ debug!("tracing value {:s}", description);
+ JS_CallTracer(tracer, val.to_gcthing(), val.trace_kind());
+ }
+}
+
+/// Trace the `JSObject` held by `reflector`.
+pub fn trace_reflector(tracer: *mut JSTracer, description: &str, reflector: &Reflector) {
+ trace_object(tracer, description, reflector.get_jsobject())
+}
+
+/// Trace a `JSObject`.
+pub fn trace_object(tracer: *mut JSTracer, description: &str, obj: *mut JSObject) {
+ unsafe {
+ let name = description.to_c_str();
+ (*tracer).debugPrinter = None;
+ (*tracer).debugPrintIndex = -1;
+ (*tracer).debugPrintArg = name.as_ptr() as *const libc::c_void;
+ debug!("tracing {:s}", description);
+ JS_CallTracer(tracer, obj as *mut libc::c_void, JSTRACE_OBJECT);
+ }
+}
+
+/// Encapsulates a type that cannot easily have `Encodable` derived automagically,
+/// but also does not need to be made known to the SpiderMonkey garbage collector.
+///
+/// Use only with types that are not associated with a JS reflector and do not contain
+/// fields of types associated with JS reflectors.
+///
+/// This should really only be used for types that are from other crates,
+/// so we can't implement `Encodable`. See more details: mozilla#2662.
+pub struct Untraceable<T> {
+ inner: T,
+}
+
+impl<T> Untraceable<T> {
+ pub fn new(val: T) -> Untraceable<T> {
+ Untraceable {
+ inner: val
+ }
+ }
+}
+
+impl<S: Encoder<E>, E, T> Encodable<S, E> for Untraceable<T> {
+ fn encode(&self, _s: &mut S) -> Result<(), E> {
+ Ok(())
+ }
+}
+
+impl<T> Deref<T> for Untraceable<T> {
+ fn deref<'a>(&'a self) -> &'a T {
+ &self.inner
+ }
+}
+
+/// Encapsulates a type that can be traced but is boxed in a type we don't
+/// control (such as `RefCell`).
+///
+/// Wrap a field in Traceable and implement the `Encodable` trait
+/// for that new concrete type to achieve magic compiler-derived trace hooks.
+///
+/// We always prefer this, in case the contained type ever changes to something that should be traced.
+/// See more details: mozilla#2662.
+#[deriving(PartialEq, Clone)]
+pub struct Traceable<T> {
+ inner: T
+}
+
+impl<T> Traceable<T> {
+ pub fn new(val: T) -> Traceable<T> {
+ Traceable {
+ inner: val
+ }
+ }
+}
+
+impl<T> Deref<T> for Traceable<T> {
+ fn deref<'a>(&'a self) -> &'a T {
+ &self.inner
+ }
+}
+
+impl<S: Encoder<E>, E, T: Encodable<S, E>> Encodable<S, E> for Traceable<RefCell<T>> {
+ fn encode(&self, s: &mut S) -> Result<(), E> {
+ self.borrow().encode(s)
+ }
+}
+
+impl<S: Encoder<E>, E, T: Encodable<S, E>+Copy> Encodable<S, E> for Traceable<Cell<T>> {
+ fn encode(&self, s: &mut S) -> Result<(), E> {
+ self.deref().get().encode(s)
+ }
+}
+
+impl<S: Encoder<E>, E> Encodable<S, E> for Traceable<*mut JSObject> {
+ fn encode(&self, s: &mut S) -> Result<(), E> {
+ trace_object(get_jstracer(s), "object", **self);
+ Ok(())
+ }
+}
+
+impl<S: Encoder<E>, E> Encodable<S, E> for Traceable<JSVal> {
+ fn encode(&self, s: &mut S) -> Result<(), E> {
+ trace_jsval(get_jstracer(s), "val", **self);
+ Ok(())
+ }
+}
diff --git a/components/script/dom/bindings/utils.rs b/components/script/dom/bindings/utils.rs
new file mode 100644
index 00000000000..08b65dd084d
--- /dev/null
+++ b/components/script/dom/bindings/utils.rs
@@ -0,0 +1,791 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! Various utilities to glue JavaScript and the DOM implementation together.
+
+use dom::bindings::codegen::PrototypeList;
+use dom::bindings::codegen::PrototypeList::MAX_PROTO_CHAIN_LENGTH;
+use dom::bindings::conversions::{FromJSValConvertible, IDLInterface};
+use dom::bindings::error::throw_type_error;
+use dom::bindings::global::{GlobalRef, GlobalField, WindowField, WorkerField};
+use dom::bindings::js::{JS, Temporary, Root};
+use dom::bindings::trace::Untraceable;
+use dom::browsercontext;
+use dom::window;
+use servo_util::str::DOMString;
+
+use libc;
+use libc::c_uint;
+use std::cell::Cell;
+use std::mem;
+use std::cmp::PartialEq;
+use std::ptr;
+use std::slice;
+use js::glue::{js_IsObjectProxyClass, js_IsFunctionProxyClass, IsProxyHandlerFamily};
+use js::glue::{GetGlobalForObjectCrossCompartment, UnwrapObject, GetProxyHandlerExtra};
+use js::glue::{IsWrapper, RUST_JSID_TO_STRING, RUST_JSID_IS_INT};
+use js::glue::{RUST_JSID_IS_STRING, RUST_JSID_TO_INT};
+use js::jsapi::{JS_AlreadyHasOwnProperty, JS_NewFunction};
+use js::jsapi::{JS_DefineProperties, JS_ForwardGetPropertyTo};
+use js::jsapi::{JS_GetClass, JS_LinkConstructorAndPrototype, JS_GetStringCharsAndLength};
+use js::jsapi::{JS_ObjectIsRegExp, JS_ObjectIsDate, JSHandleObject};
+use js::jsapi::JS_GetFunctionObject;
+use js::jsapi::{JS_HasPropertyById, JS_GetPrototype};
+use js::jsapi::{JS_GetProperty, JS_HasProperty};
+use js::jsapi::{JS_DefineFunctions, JS_DefineProperty};
+use js::jsapi::{JS_ValueToString, JS_GetReservedSlot, JS_SetReservedSlot};
+use js::jsapi::{JSContext, JSObject, JSBool, jsid, JSClass};
+use js::jsapi::{JSFunctionSpec, JSPropertySpec};
+use js::jsapi::{JS_NewGlobalObject, JS_InitStandardClasses};
+use js::jsapi::{JSString};
+use js::jsapi::JS_DeletePropertyById2;
+use js::jsfriendapi::JS_ObjectToOuterObject;
+use js::jsfriendapi::bindgen::JS_NewObjectWithUniqueType;
+use js::jsval::JSVal;
+use js::jsval::{PrivateValue, ObjectValue, NullValue, ObjectOrNullValue};
+use js::jsval::{Int32Value, UInt32Value, DoubleValue, BooleanValue, UndefinedValue};
+use js::rust::with_compartment;
+use js::{JSPROP_ENUMERATE, JSCLASS_IS_GLOBAL, JSCLASS_IS_DOMJSCLASS};
+use js::JSPROP_PERMANENT;
+use js::{JSFUN_CONSTRUCTOR, JSPROP_READONLY};
+use js;
+
+#[allow(raw_pointer_deriving)]
+#[deriving(Encodable)]
+pub struct GlobalStaticData {
+ pub windowproxy_handler: Untraceable<*const libc::c_void>,
+}
+
+pub fn GlobalStaticData() -> GlobalStaticData {
+ GlobalStaticData {
+ windowproxy_handler: Untraceable::new(browsercontext::new_window_proxy_handler()),
+ }
+}
+
+/// Returns whether the given `clasp` is one for a DOM object.
+fn is_dom_class(clasp: *const JSClass) -> bool {
+ unsafe {
+ ((*clasp).flags & js::JSCLASS_IS_DOMJSCLASS) != 0
+ }
+}
+
+/// Returns whether `obj` is a DOM object implemented as a proxy.
+pub fn is_dom_proxy(obj: *mut JSObject) -> bool {
+ unsafe {
+ (js_IsObjectProxyClass(obj) || js_IsFunctionProxyClass(obj)) &&
+ IsProxyHandlerFamily(obj)
+ }
+}
+
+/// Returns the index of the slot wherein a pointer to the reflected DOM object
+/// is stored.
+///
+/// Fails if `obj` is not a DOM object.
+pub unsafe fn dom_object_slot(obj: *mut JSObject) -> u32 {
+ let clasp = JS_GetClass(obj);
+ if is_dom_class(&*clasp) {
+ DOM_OBJECT_SLOT as u32
+ } else {
+ assert!(is_dom_proxy(obj));
+ DOM_PROXY_OBJECT_SLOT as u32
+ }
+}
+
+/// Get the DOM object from the given reflector.
+pub unsafe fn unwrap<T>(obj: *mut JSObject) -> *const T {
+ let slot = dom_object_slot(obj);
+ let val = JS_GetReservedSlot(obj, slot);
+ val.to_private() as *const T
+}
+
+/// Get the `DOMClass` from `obj`, or `Err(())` if `obj` is not a DOM object.
+pub unsafe fn get_dom_class(obj: *mut JSObject) -> Result<DOMClass, ()> {
+ let clasp = JS_GetClass(obj);
+ if is_dom_class(&*clasp) {
+ debug!("plain old dom object");
+ let domjsclass: *const DOMJSClass = clasp as *const DOMJSClass;
+ return Ok((*domjsclass).dom_class);
+ }
+ if is_dom_proxy(obj) {
+ debug!("proxy dom object");
+ let dom_class: *const DOMClass = GetProxyHandlerExtra(obj) as *const DOMClass;
+ return Ok(*dom_class);
+ }
+ debug!("not a dom object");
+ return Err(());
+}
+
+/// Get a `JS<T>` for the given DOM object, unwrapping any wrapper around it
+/// first, and checking if the object is of the correct type.
+///
+/// Returns Err(()) if `obj` is an opaque security wrapper or if the object is
+/// not a reflector for a DOM object of the given type (as defined by the
+/// proto_id and proto_depth).
+pub fn unwrap_jsmanaged<T: Reflectable>(mut obj: *mut JSObject,
+ proto_id: PrototypeList::id::ID,
+ proto_depth: uint) -> Result<JS<T>, ()> {
+ unsafe {
+ let dom_class = get_dom_class(obj).or_else(|_| {
+ if IsWrapper(obj) == 1 {
+ debug!("found wrapper");
+ obj = UnwrapObject(obj, /* stopAtOuter = */ 0, ptr::mut_null());
+ if obj.is_null() {
+ debug!("unwrapping security wrapper failed");
+ Err(())
+ } else {
+ assert!(IsWrapper(obj) == 0);
+ debug!("unwrapped successfully");
+ get_dom_class(obj)
+ }
+ } else {
+ debug!("not a dom wrapper");
+ Err(())
+ }
+ });
+
+ dom_class.and_then(|dom_class| {
+ if dom_class.interface_chain[proto_depth] == proto_id {
+ debug!("good prototype");
+ Ok(JS::from_raw(unwrap(obj)))
+ } else {
+ debug!("bad prototype");
+ Err(())
+ }
+ })
+ }
+}
+
+/// Leak the given pointer.
+pub unsafe fn squirrel_away_unique<T>(x: Box<T>) -> *const T {
+ mem::transmute(x)
+}
+
+/// Convert the given `JSString` to a `DOMString`. Fails if the string does not
+/// contain valid UTF-16.
+pub fn jsstring_to_str(cx: *mut JSContext, s: *mut JSString) -> DOMString {
+ unsafe {
+ let mut length = 0;
+ let chars = JS_GetStringCharsAndLength(cx, s, &mut length);
+ slice::raw::buf_as_slice(chars, length as uint, |char_vec| {
+ String::from_utf16(char_vec).unwrap()
+ })
+ }
+}
+
+/// Convert the given `jsid` to a `DOMString`. Fails if the `jsid` is not a
+/// string, or if the string does not contain valid UTF-16.
+pub fn jsid_to_str(cx: *mut JSContext, id: jsid) -> DOMString {
+ unsafe {
+ assert!(RUST_JSID_IS_STRING(id) != 0);
+ jsstring_to_str(cx, RUST_JSID_TO_STRING(id))
+ }
+}
+
+/// The index of the slot wherein a pointer to the reflected DOM object is
+/// stored for non-proxy bindings.
+// We use slot 0 for holding the raw object. This is safe for both
+// globals and non-globals.
+pub static DOM_OBJECT_SLOT: uint = 0;
+static DOM_PROXY_OBJECT_SLOT: uint = js::JSSLOT_PROXY_PRIVATE as uint;
+
+// NOTE: This is baked into the Ion JIT as 0 in codegen for LGetDOMProperty and
+// LSetDOMProperty. Those constants need to be changed accordingly if this value
+// changes.
+static DOM_PROTO_INSTANCE_CLASS_SLOT: u32 = 0;
+
+/// The index of the slot that contains a reference to the ProtoOrIfaceArray.
+// All DOM globals must have a slot at DOM_PROTOTYPE_SLOT.
+pub static DOM_PROTOTYPE_SLOT: u32 = js::JSCLASS_GLOBAL_SLOT_COUNT;
+
+/// The flag set on the `JSClass`es for DOM global objects.
+// NOTE: This is baked into the Ion JIT as 0 in codegen for LGetDOMProperty and
+// LSetDOMProperty. Those constants need to be changed accordingly if this value
+// changes.
+pub static JSCLASS_DOM_GLOBAL: u32 = js::JSCLASS_USERBIT1;
+
+/// Representation of an IDL constant value.
+#[deriving(Clone)]
+pub enum ConstantVal {
+ IntVal(i32),
+ UintVal(u32),
+ DoubleVal(f64),
+ BoolVal(bool),
+ NullVal,
+ VoidVal
+}
+
+/// Representation of an IDL constant.
+#[deriving(Clone)]
+pub struct ConstantSpec {
+ pub name: &'static [u8],
+ pub value: ConstantVal
+}
+
+impl ConstantSpec {
+ /// Returns a `JSVal` that represents the value of this `ConstantSpec`.
+ pub fn get_value(&self) -> JSVal {
+ match self.value {
+ NullVal => NullValue(),
+ IntVal(i) => Int32Value(i),
+ UintVal(u) => UInt32Value(u),
+ DoubleVal(d) => DoubleValue(d),
+ BoolVal(b) => BooleanValue(b),
+ VoidVal => UndefinedValue(),
+ }
+ }
+}
+
+/// Helper structure for cross-origin wrappers for DOM binding objects.
+pub struct NativePropertyHooks {
+ /// The property arrays for this interface.
+ pub native_properties: &'static NativeProperties,
+
+ /// The NativePropertyHooks instance for the parent interface, if any.
+ pub proto_hooks: Option<&'static NativePropertyHooks>,
+}
+
+/// The struct that holds inheritance information for DOM object reflectors.
+pub struct DOMClass {
+ /// A list of interfaces that this object implements, in order of decreasing
+ /// derivedness.
+ pub interface_chain: [PrototypeList::id::ID, ..MAX_PROTO_CHAIN_LENGTH],
+
+ /// The NativePropertyHooks for the interface associated with this class.
+ pub native_hooks: &'static NativePropertyHooks,
+}
+
+/// The JSClass used for DOM object reflectors.
+pub struct DOMJSClass {
+ pub base: js::Class,
+ pub dom_class: DOMClass
+}
+
+/// Returns the ProtoOrIfaceArray for the given global object.
+/// Fails if `global` is not a DOM global object.
+pub fn GetProtoOrIfaceArray(global: *mut JSObject) -> *mut *mut JSObject {
+ unsafe {
+ assert!(((*JS_GetClass(global)).flags & JSCLASS_DOM_GLOBAL) != 0);
+ JS_GetReservedSlot(global, DOM_PROTOTYPE_SLOT).to_private() as *mut *mut JSObject
+ }
+}
+
+/// Contains references to lists of methods, attributes, and constants for a
+/// given interface.
+pub struct NativeProperties {
+ pub methods: Option<&'static [JSFunctionSpec]>,
+ pub attrs: Option<&'static [JSPropertySpec]>,
+ pub consts: Option<&'static [ConstantSpec]>,
+ pub staticMethods: Option<&'static [JSFunctionSpec]>,
+ pub staticAttrs: Option<&'static [JSPropertySpec]>,
+}
+
+/// A JSNative that cannot be null.
+pub type NonNullJSNative =
+ unsafe extern "C" fn (arg1: *mut JSContext, arg2: c_uint, arg3: *mut JSVal) -> JSBool;
+
+/// Creates the *interface prototype object* and the *interface object* (if
+/// needed).
+/// Fails on JSAPI failure.
+pub fn CreateInterfaceObjects2(cx: *mut JSContext, global: *mut JSObject, receiver: *mut JSObject,
+ protoProto: *mut JSObject,
+ protoClass: &'static JSClass,
+ constructor: Option<(NonNullJSNative, &'static str, u32)>,
+ domClass: *const DOMClass,
+ members: &'static NativeProperties) -> *mut JSObject {
+ let proto = CreateInterfacePrototypeObject(cx, global, protoProto,
+ protoClass, members);
+
+ unsafe {
+ JS_SetReservedSlot(proto, DOM_PROTO_INSTANCE_CLASS_SLOT,
+ PrivateValue(domClass as *const libc::c_void));
+ }
+
+ match constructor {
+ Some((native, name, nargs)) => {
+ let s = name.to_c_str();
+ CreateInterfaceObject(cx, global, receiver,
+ native, nargs, proto,
+ members, s.as_ptr())
+ },
+ None => (),
+ }
+
+ proto
+}
+
+/// Creates the *interface object*.
+/// Fails on JSAPI failure.
+fn CreateInterfaceObject(cx: *mut JSContext, global: *mut JSObject, receiver: *mut JSObject,
+ constructorNative: NonNullJSNative,
+ ctorNargs: u32, proto: *mut JSObject,
+ members: &'static NativeProperties,
+ name: *const libc::c_char) {
+ unsafe {
+ let fun = JS_NewFunction(cx, Some(constructorNative), ctorNargs,
+ JSFUN_CONSTRUCTOR, global, name);
+ assert!(fun.is_not_null());
+
+ let constructor = JS_GetFunctionObject(fun);
+ assert!(constructor.is_not_null());
+
+ match members.staticMethods {
+ Some(staticMethods) => DefineMethods(cx, constructor, staticMethods),
+ _ => (),
+ }
+
+ match members.staticAttrs {
+ Some(staticProperties) => DefineProperties(cx, constructor, staticProperties),
+ _ => (),
+ }
+
+ match members.consts {
+ Some(constants) => DefineConstants(cx, constructor, constants),
+ _ => (),
+ }
+
+ if proto.is_not_null() {
+ assert!(JS_LinkConstructorAndPrototype(cx, constructor, proto) != 0);
+ }
+
+ let mut alreadyDefined = 0;
+ assert!(JS_AlreadyHasOwnProperty(cx, receiver, name, &mut alreadyDefined) != 0);
+
+ if alreadyDefined == 0 {
+ assert!(JS_DefineProperty(cx, receiver, name,
+ ObjectValue(&*constructor),
+ None, None, 0) != 0);
+ }
+ }
+}
+
+/// Defines constants on `obj`.
+/// Fails on JSAPI failure.
+fn DefineConstants(cx: *mut JSContext, obj: *mut JSObject, constants: &'static [ConstantSpec]) {
+ for spec in constants.iter() {
+ unsafe {
+ assert!(JS_DefineProperty(cx, obj, spec.name.as_ptr() as *const libc::c_char,
+ spec.get_value(), None, None,
+ JSPROP_ENUMERATE | JSPROP_READONLY |
+ JSPROP_PERMANENT) != 0);
+ }
+ }
+}
+
+/// Defines methods on `obj`. The last entry of `methods` must contain zeroed
+/// memory.
+/// Fails on JSAPI failure.
+fn DefineMethods(cx: *mut JSContext, obj: *mut JSObject, methods: &'static [JSFunctionSpec]) {
+ unsafe {
+ assert!(JS_DefineFunctions(cx, obj, methods.as_ptr()) != 0);
+ }
+}
+
+/// Defines attributes on `obj`. The last entry of `properties` must contain
+/// zeroed memory.
+/// Fails on JSAPI failure.
+fn DefineProperties(cx: *mut JSContext, obj: *mut JSObject, properties: &'static [JSPropertySpec]) {
+ unsafe {
+ assert!(JS_DefineProperties(cx, obj, properties.as_ptr()) != 0);
+ }
+}
+
+/// Creates the *interface prototype object*.
+/// Fails on JSAPI failure.
+fn CreateInterfacePrototypeObject(cx: *mut JSContext, global: *mut JSObject,
+ parentProto: *mut JSObject,
+ protoClass: &'static JSClass,
+ members: &'static NativeProperties) -> *mut JSObject {
+ unsafe {
+ let ourProto = JS_NewObjectWithUniqueType(cx, protoClass, &*parentProto, &*global);
+ assert!(ourProto.is_not_null());
+
+ match members.methods {
+ Some(methods) => DefineMethods(cx, ourProto, methods),
+ _ => (),
+ }
+
+ match members.attrs {
+ Some(properties) => DefineProperties(cx, ourProto, properties),
+ _ => (),
+ }
+
+ match members.consts {
+ Some(constants) => DefineConstants(cx, ourProto, constants),
+ _ => (),
+ }
+
+ return ourProto;
+ }
+}
+
+/// A throwing constructor, for those interfaces that have neither
+/// `NoInterfaceObject` nor `Constructor`.
+pub extern fn ThrowingConstructor(cx: *mut JSContext, _argc: c_uint, _vp: *mut JSVal) -> JSBool {
+ throw_type_error(cx, "Illegal constructor.");
+ return 0;
+}
+
+/// Construct and cache the ProtoOrIfaceArray for the given global.
+/// Fails if the argument is not a DOM global.
+pub fn initialize_global(global: *mut JSObject) {
+ let protoArray = box () ([0 as *mut JSObject, ..PrototypeList::id::IDCount as uint]);
+ unsafe {
+ assert!(((*JS_GetClass(global)).flags & JSCLASS_DOM_GLOBAL) != 0);
+ let box_ = squirrel_away_unique(protoArray);
+ JS_SetReservedSlot(global,
+ DOM_PROTOTYPE_SLOT,
+ PrivateValue(box_ as *const libc::c_void));
+ }
+}
+
+/// A trait to provide access to the `Reflector` for a DOM object.
+pub trait Reflectable {
+ fn reflector<'a>(&'a self) -> &'a Reflector;
+}
+
+/// Create the reflector for a new DOM object and yield ownership to the
+/// reflector.
+pub fn reflect_dom_object<T: Reflectable>
+ (obj: Box<T>,
+ global: &GlobalRef,
+ wrap_fn: extern "Rust" fn(*mut JSContext, &GlobalRef, Box<T>) -> Temporary<T>)
+ -> Temporary<T> {
+ wrap_fn(global.get_cx(), global, obj)
+}
+
+/// A struct to store a reference to the reflector of a DOM object.
+#[allow(raw_pointer_deriving)]
+#[deriving(PartialEq)]
+pub struct Reflector {
+ object: Cell<*mut JSObject>,
+}
+
+impl Reflector {
+ /// Get the reflector.
+ #[inline]
+ pub fn get_jsobject(&self) -> *mut JSObject {
+ self.object.get()
+ }
+
+ /// Initialize the reflector. (May be called only once.)
+ pub fn set_jsobject(&self, object: *mut JSObject) {
+ assert!(self.object.get().is_null());
+ assert!(object.is_not_null());
+ self.object.set(object);
+ }
+
+ /// Return a pointer to the memory location at which the JS reflector object is stored.
+ /// Used by Temporary values to root the reflector, as required by the JSAPI rooting
+ /// APIs.
+ pub fn rootable(&self) -> *mut *mut JSObject {
+ &self.object as *const Cell<*mut JSObject>
+ as *mut Cell<*mut JSObject>
+ as *mut *mut JSObject
+ }
+
+ /// Create an uninitialized `Reflector`.
+ pub fn new() -> Reflector {
+ Reflector {
+ object: Cell::new(ptr::mut_null()),
+ }
+ }
+}
+
+pub fn GetPropertyOnPrototype(cx: *mut JSContext, proxy: *mut JSObject, id: jsid, found: *mut bool,
+ vp: *mut JSVal) -> bool {
+ unsafe {
+ //let proto = GetObjectProto(proxy);
+ let proto = JS_GetPrototype(proxy);
+ if proto.is_null() {
+ *found = false;
+ return true;
+ }
+ let mut hasProp = 0;
+ if JS_HasPropertyById(cx, proto, id, &mut hasProp) == 0 {
+ return false;
+ }
+ *found = hasProp != 0;
+ let no_output = vp.is_null();
+ if hasProp == 0 || no_output {
+ return true;
+ }
+
+ JS_ForwardGetPropertyTo(cx, proto, id, proxy, vp) != 0
+ }
+}
+
+/// Get an array index from the given `jsid`. Returns `None` if the given
+/// `jsid` is not an integer.
+pub fn GetArrayIndexFromId(_cx: *mut JSContext, id: jsid) -> Option<u32> {
+ unsafe {
+ if RUST_JSID_IS_INT(id) != 0 {
+ return Some(RUST_JSID_TO_INT(id) as u32);
+ }
+ return None;
+ }
+ // if id is length atom, -1, otherwise
+ /*return if JSID_IS_ATOM(id) {
+ let atom = JSID_TO_ATOM(id);
+ //let s = *GetAtomChars(id);
+ if s > 'a' && s < 'z' {
+ return -1;
+ }
+
+ let i = 0;
+ let str = AtomToLinearString(JSID_TO_ATOM(id));
+ return if StringIsArray(str, &mut i) != 0 { i } else { -1 }
+ } else {
+ IdToInt32(cx, id);
+ }*/
+}
+
+/// Find the index of a string given by `v` in `values`.
+/// Returns `Err(())` on JSAPI failure (there is a pending exception), and
+/// `Ok(None)` if there was no matching string.
+pub fn FindEnumStringIndex(cx: *mut JSContext,
+ v: JSVal,
+ values: &[&'static str]) -> Result<Option<uint>, ()> {
+ unsafe {
+ let jsstr = JS_ValueToString(cx, v);
+ if jsstr.is_null() {
+ return Err(());
+ }
+
+ let mut length = 0;
+ let chars = JS_GetStringCharsAndLength(cx, jsstr, &mut length);
+ if chars.is_null() {
+ return Err(());
+ }
+
+ Ok(values.iter().position(|value| {
+ value.len() == length as uint &&
+ range(0, length as uint).all(|j| {
+ value.as_bytes()[j] as u16 == *chars.offset(j as int)
+ })
+ }))
+ }
+}
+
+/// Get the property with name `property` from `object`.
+/// Returns `Err(())` on JSAPI failure (there is a pending exception), and
+/// `Ok(None)` if there was no property with the given name.
+pub fn get_dictionary_property(cx: *mut JSContext,
+ object: *mut JSObject,
+ property: &str) -> Result<Option<JSVal>, ()> {
+ use std::c_str::CString;
+ fn has_property(cx: *mut JSContext, object: *mut JSObject, property: &CString,
+ found: &mut JSBool) -> bool {
+ unsafe {
+ JS_HasProperty(cx, object, property.as_ptr(), found) != 0
+ }
+ }
+ fn get_property(cx: *mut JSContext, object: *mut JSObject, property: &CString,
+ value: &mut JSVal) -> bool {
+ unsafe {
+ JS_GetProperty(cx, object, property.as_ptr(), value) != 0
+ }
+ }
+
+ let property = property.to_c_str();
+ if object.is_null() {
+ return Ok(None);
+ }
+
+ let mut found: JSBool = 0;
+ if !has_property(cx, object, &property, &mut found) {
+ return Err(());
+ }
+
+ if found == 0 {
+ return Ok(None);
+ }
+
+ let mut value = NullValue();
+ if !get_property(cx, object, &property, &mut value) {
+ return Err(());
+ }
+
+ Ok(Some(value))
+}
+
+pub fn HasPropertyOnPrototype(cx: *mut JSContext, proxy: *mut JSObject, id: jsid) -> bool {
+ // MOZ_ASSERT(js::IsProxy(proxy) && js::GetProxyHandler(proxy) == handler);
+ let mut found = false;
+ return !GetPropertyOnPrototype(cx, proxy, id, &mut found, ptr::mut_null()) || found;
+}
+
+/// Returns whether `obj` can be converted to a callback interface per IDL.
+pub fn IsConvertibleToCallbackInterface(cx: *mut JSContext, obj: *mut JSObject) -> bool {
+ unsafe {
+ JS_ObjectIsDate(cx, obj) == 0 && JS_ObjectIsRegExp(cx, obj) == 0
+ }
+}
+
+/// Create a DOM global object with the given class.
+pub fn CreateDOMGlobal(cx: *mut JSContext, class: *const JSClass) -> *mut JSObject {
+ unsafe {
+ let obj = JS_NewGlobalObject(cx, class, ptr::mut_null());
+ if obj.is_null() {
+ return ptr::mut_null();
+ }
+ with_compartment(cx, obj, || {
+ JS_InitStandardClasses(cx, obj);
+ });
+ initialize_global(obj);
+ obj
+ }
+}
+
+/// Callback to outerize windows when wrapping.
+pub extern fn wrap_for_same_compartment(cx: *mut JSContext, obj: *mut JSObject) -> *mut JSObject {
+ unsafe {
+ JS_ObjectToOuterObject(cx, obj)
+ }
+}
+
+/// Callback to outerize windows before wrapping.
+pub extern fn pre_wrap(cx: *mut JSContext, _scope: *mut JSObject,
+ obj: *mut JSObject, _flags: c_uint) -> *mut JSObject {
+ unsafe {
+ JS_ObjectToOuterObject(cx, obj)
+ }
+}
+
+/// Callback to outerize windows.
+pub extern fn outerize_global(_cx: *mut JSContext, obj: JSHandleObject) -> *mut JSObject {
+ unsafe {
+ debug!("outerizing");
+ let obj = *obj.unnamed_field1;
+ let win: Root<window::Window> =
+ unwrap_jsmanaged(obj,
+ IDLInterface::get_prototype_id(None::<window::Window>),
+ IDLInterface::get_prototype_depth(None::<window::Window>))
+ .unwrap()
+ .root();
+ win.deref().browser_context.deref().borrow().get_ref().window_proxy()
+ }
+}
+
+/// Returns the global object of the realm that the given JS object was created in.
+pub fn global_object_for_js_object(obj: *mut JSObject) -> GlobalField {
+ unsafe {
+ let global = GetGlobalForObjectCrossCompartment(obj);
+ let clasp = JS_GetClass(global);
+ assert!(((*clasp).flags & (JSCLASS_IS_DOMJSCLASS | JSCLASS_IS_GLOBAL)) != 0);
+ match FromJSValConvertible::from_jsval(ptr::mut_null(), ObjectOrNullValue(global), ()) {
+ Ok(window) => return WindowField(window),
+ Err(_) => (),
+ }
+
+ match FromJSValConvertible::from_jsval(ptr::mut_null(), ObjectOrNullValue(global), ()) {
+ Ok(worker) => return WorkerField(worker),
+ Err(_) => (),
+ }
+
+ fail!("found DOM global that doesn't unwrap to Window or WorkerGlobalScope")
+ }
+}
+
+/// Get the `JSContext` for the `JSRuntime` associated with the thread
+/// this object is on.
+fn cx_for_dom_reflector(obj: *mut JSObject) -> *mut JSContext {
+ let global = global_object_for_js_object(obj);
+ let global = global.root();
+ global.root_ref().get_cx()
+}
+
+/// Get the `JSContext` for the `JSRuntime` associated with the thread
+/// this DOM object is on.
+pub fn cx_for_dom_object<T: Reflectable>(obj: &T) -> *mut JSContext {
+ cx_for_dom_reflector(obj.reflector().get_jsobject())
+}
+
+pub unsafe fn delete_property_by_id(cx: *mut JSContext, object: *mut JSObject,
+ id: jsid, bp: &mut bool) -> bool {
+ let mut value = UndefinedValue();
+ if JS_DeletePropertyById2(cx, object, id, &mut value) == 0 {
+ return false;
+ }
+
+ *bp = value.to_boolean();
+ return true;
+}
+
+/// Results of `xml_name_type`.
+#[deriving(PartialEq)]
+pub enum XMLName {
+ QName,
+ Name,
+ InvalidXMLName
+}
+
+/// Check if an element name is valid. See http://www.w3.org/TR/xml/#NT-Name
+/// for details.
+pub fn xml_name_type(name: &str) -> XMLName {
+ fn is_valid_start(c: char) -> bool {
+ match c {
+ ':' |
+ 'A' .. 'Z' |
+ '_' |
+ 'a' .. 'z' |
+ '\xC0' .. '\xD6' |
+ '\xD8' .. '\xF6' |
+ '\xF8' .. '\u02FF' |
+ '\u0370' .. '\u037D' |
+ '\u037F' .. '\u1FFF' |
+ '\u200C' .. '\u200D' |
+ '\u2070' .. '\u218F' |
+ '\u2C00' .. '\u2FEF' |
+ '\u3001' .. '\uD7FF' |
+ '\uF900' .. '\uFDCF' |
+ '\uFDF0' .. '\uFFFD' |
+ '\U00010000' .. '\U000EFFFF' => true,
+ _ => false,
+ }
+ }
+
+ fn is_valid_continuation(c: char) -> bool {
+ is_valid_start(c) || match c {
+ '-' |
+ '.' |
+ '0' .. '9' |
+ '\xB7' |
+ '\u0300' .. '\u036F' |
+ '\u203F' .. '\u2040' => true,
+ _ => false,
+ }
+ }
+
+ let mut iter = name.chars();
+ let mut non_qname_colons = false;
+ let mut seen_colon = false;
+ match iter.next() {
+ None => return InvalidXMLName,
+ Some(c) => {
+ if !is_valid_start(c) {
+ return InvalidXMLName;
+ }
+ if c == ':' {
+ non_qname_colons = true;
+ }
+ }
+ }
+
+ for c in name.chars() {
+ if !is_valid_continuation(c) {
+ return InvalidXMLName;
+ }
+ if c == ':' {
+ match seen_colon {
+ true => non_qname_colons = true,
+ false => seen_colon = true
+ }
+ }
+ }
+
+ match non_qname_colons {
+ false => QName,
+ true => Name
+ }
+}
diff --git a/components/script/dom/blob.rs b/components/script/dom/blob.rs
new file mode 100644
index 00000000000..1a7d2a21636
--- /dev/null
+++ b/components/script/dom/blob.rs
@@ -0,0 +1,59 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::InheritTypes::FileDerived;
+use dom::bindings::global::GlobalRef;
+use dom::bindings::js::Temporary;
+use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
+use dom::bindings::error::Fallible;
+use dom::bindings::codegen::Bindings::BlobBinding;
+
+#[deriving(Encodable)]
+pub enum BlobType {
+ BlobTypeId,
+ FileTypeId
+}
+
+#[deriving(Encodable)]
+pub struct Blob {
+ reflector_: Reflector,
+ type_: BlobType
+}
+
+impl Blob {
+ pub fn new_inherited() -> Blob {
+ Blob {
+ reflector_: Reflector::new(),
+ type_: BlobTypeId
+ }
+ }
+
+ pub fn new(global: &GlobalRef) -> Temporary<Blob> {
+ reflect_dom_object(box Blob::new_inherited(),
+ global,
+ BlobBinding::Wrap)
+ }
+
+ pub fn Constructor(global: &GlobalRef) -> Fallible<Temporary<Blob>> {
+ Ok(Blob::new(global))
+ }
+}
+
+pub trait BlobMethods {
+}
+
+impl Reflectable for Blob {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ &self.reflector_
+ }
+}
+
+impl FileDerived for Blob {
+ fn is_file(&self) -> bool {
+ match self.type_ {
+ FileTypeId => true,
+ _ => false
+ }
+ }
+}
diff --git a/components/script/dom/browsercontext.rs b/components/script/dom/browsercontext.rs
new file mode 100644
index 00000000000..a54477a2ff8
--- /dev/null
+++ b/components/script/dom/browsercontext.rs
@@ -0,0 +1,120 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::js::{JS, JSRef, Temporary};
+use dom::bindings::trace::Traceable;
+use dom::bindings::utils::Reflectable;
+use dom::document::Document;
+use dom::window::Window;
+
+use js::jsapi::JSObject;
+use js::glue::{WrapperNew, CreateWrapperProxyHandler, ProxyTraps};
+use js::rust::with_compartment;
+
+use libc::c_void;
+use std::ptr;
+
+#[allow(raw_pointer_deriving)]
+#[deriving(Encodable)]
+pub struct BrowserContext {
+ history: Vec<SessionHistoryEntry>,
+ active_index: uint,
+ window_proxy: Traceable<*mut JSObject>,
+}
+
+impl BrowserContext {
+ pub fn new(document: &JSRef<Document>) -> BrowserContext {
+ let mut context = BrowserContext {
+ history: vec!(SessionHistoryEntry::new(document)),
+ active_index: 0,
+ window_proxy: Traceable::new(ptr::mut_null()),
+ };
+ context.create_window_proxy();
+ context
+ }
+
+ pub fn active_document(&self) -> Temporary<Document> {
+ Temporary::new(self.history[self.active_index].document.clone())
+ }
+
+ pub fn active_window(&self) -> Temporary<Window> {
+ let doc = self.active_document().root();
+ Temporary::new(doc.deref().window.clone())
+ }
+
+ pub fn window_proxy(&self) -> *mut JSObject {
+ assert!(self.window_proxy.deref().is_not_null());
+ *self.window_proxy
+ }
+
+ fn create_window_proxy(&mut self) {
+ let win = self.active_window().root();
+ let page = win.deref().page();
+ let js_info = page.js_info();
+
+ let handler = js_info.get_ref().dom_static.windowproxy_handler;
+ assert!(handler.deref().is_not_null());
+
+ let parent = win.deref().reflector().get_jsobject();
+ let cx = js_info.get_ref().js_context.deref().deref().ptr;
+ let wrapper = with_compartment(cx, parent, || unsafe {
+ WrapperNew(cx, parent, *handler.deref())
+ });
+ assert!(wrapper.is_not_null());
+ self.window_proxy = Traceable::new(wrapper);
+ }
+}
+
+#[deriving(Encodable)]
+pub struct SessionHistoryEntry {
+ document: JS<Document>,
+ children: Vec<BrowserContext>
+}
+
+impl SessionHistoryEntry {
+ fn new(document: &JSRef<Document>) -> SessionHistoryEntry {
+ SessionHistoryEntry {
+ document: JS::from_rooted(document),
+ children: vec!()
+ }
+ }
+}
+
+static proxy_handler: ProxyTraps = ProxyTraps {
+ getPropertyDescriptor: None,
+ getOwnPropertyDescriptor: None,
+ defineProperty: None,
+ getOwnPropertyNames: 0 as *const u8,
+ delete_: None,
+ enumerate: 0 as *const u8,
+
+ has: None,
+ hasOwn: None,
+ get: None,
+ set: None,
+ keys: 0 as *const u8,
+ iterate: None,
+
+ call: None,
+ construct: None,
+ nativeCall: 0 as *const u8,
+ hasInstance: None,
+ typeOf: None,
+ objectClassIs: None,
+ obj_toString: None,
+ fun_toString: None,
+ //regexp_toShared: 0 as *u8,
+ defaultValue: None,
+ iteratorNext: None,
+ finalize: None,
+ getElementIfPresent: None,
+ getPrototypeOf: None,
+ trace: None
+};
+
+pub fn new_window_proxy_handler() -> *const c_void {
+ unsafe {
+ CreateWrapperProxyHandler(&proxy_handler)
+ }
+}
diff --git a/components/script/dom/canvasrenderingcontext2d.rs b/components/script/dom/canvasrenderingcontext2d.rs
new file mode 100644
index 00000000000..6b3f898123f
--- /dev/null
+++ b/components/script/dom/canvasrenderingcontext2d.rs
@@ -0,0 +1,79 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding;
+use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasRenderingContext2DMethods;
+use dom::bindings::global::{GlobalRef, GlobalField};
+use dom::bindings::js::{JS, JSRef, Temporary};
+use dom::bindings::trace::Untraceable;
+use dom::bindings::utils::{Reflector, Reflectable, reflect_dom_object};
+use dom::htmlcanvaselement::HTMLCanvasElement;
+
+use geom::point::Point2D;
+use geom::rect::Rect;
+use geom::size::Size2D;
+
+use canvas::canvas_render_task::{CanvasMsg, CanvasRenderTask, ClearRect, Close, FillRect, Recreate, StrokeRect};
+
+#[deriving(Encodable)]
+pub struct CanvasRenderingContext2D {
+ reflector_: Reflector,
+ global: GlobalField,
+ renderer: Untraceable<Sender<CanvasMsg>>,
+ canvas: JS<HTMLCanvasElement>,
+}
+
+impl CanvasRenderingContext2D {
+ pub fn new_inherited(global: &GlobalRef, canvas: &JSRef<HTMLCanvasElement>, size: Size2D<i32>) -> CanvasRenderingContext2D {
+ CanvasRenderingContext2D {
+ reflector_: Reflector::new(),
+ global: GlobalField::from_rooted(global),
+ renderer: Untraceable::new(CanvasRenderTask::start(size)),
+ canvas: JS::from_rooted(canvas),
+ }
+ }
+
+ pub fn new(global: &GlobalRef, canvas: &JSRef<HTMLCanvasElement>, size: Size2D<i32>) -> Temporary<CanvasRenderingContext2D> {
+ reflect_dom_object(box CanvasRenderingContext2D::new_inherited(global, canvas, size),
+ global, CanvasRenderingContext2DBinding::Wrap)
+ }
+
+ pub fn recreate(&self, size: Size2D<i32>) {
+ self.renderer.send(Recreate(size));
+ }
+}
+
+impl<'a> CanvasRenderingContext2DMethods for JSRef<'a, CanvasRenderingContext2D> {
+ fn Canvas(&self) -> Temporary<HTMLCanvasElement> {
+ Temporary::new(self.canvas)
+ }
+
+ fn FillRect(&self, x: f64, y: f64, width: f64, height: f64) {
+ let rect = Rect(Point2D(x as f32, y as f32), Size2D(width as f32, height as f32));
+ self.renderer.send(FillRect(rect));
+ }
+
+ fn ClearRect(&self, x: f64, y: f64, width: f64, height: f64) {
+ let rect = Rect(Point2D(x as f32, y as f32), Size2D(width as f32, height as f32));
+ self.renderer.send(ClearRect(rect));
+ }
+
+ fn StrokeRect(&self, x: f64, y: f64, width: f64, height: f64) {
+ let rect = Rect(Point2D(x as f32, y as f32), Size2D(width as f32, height as f32));
+ self.renderer.send(StrokeRect(rect));
+ }
+}
+
+impl Reflectable for CanvasRenderingContext2D {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ &self.reflector_
+ }
+}
+
+#[unsafe_destructor]
+impl Drop for CanvasRenderingContext2D {
+ fn drop(&mut self) {
+ self.renderer.send(Close);
+ }
+}
diff --git a/components/script/dom/characterdata.rs b/components/script/dom/characterdata.rs
new file mode 100644
index 00000000000..ebb17cf6993
--- /dev/null
+++ b/components/script/dom/characterdata.rs
@@ -0,0 +1,106 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! DOM bindings for `CharacterData`.
+
+use dom::bindings::codegen::Bindings::CharacterDataBinding::CharacterDataMethods;
+use dom::bindings::codegen::InheritTypes::{CharacterDataDerived, NodeCast};
+use dom::bindings::error::{Fallible, ErrorResult, IndexSize};
+use dom::bindings::js::JSRef;
+use dom::bindings::trace::Traceable;
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::node::{CommentNodeTypeId, Node, NodeTypeId, TextNodeTypeId, ProcessingInstructionNodeTypeId, NodeHelpers};
+use servo_util::str::DOMString;
+
+use std::cell::RefCell;
+
+#[deriving(Encodable)]
+pub struct CharacterData {
+ pub node: Node,
+ pub data: Traceable<RefCell<DOMString>>,
+}
+
+impl CharacterDataDerived for EventTarget {
+ fn is_characterdata(&self) -> bool {
+ match self.type_id {
+ NodeTargetTypeId(TextNodeTypeId) |
+ NodeTargetTypeId(CommentNodeTypeId) |
+ NodeTargetTypeId(ProcessingInstructionNodeTypeId) => true,
+ _ => false
+ }
+ }
+}
+
+impl CharacterData {
+ pub fn new_inherited(id: NodeTypeId, data: DOMString, document: &JSRef<Document>) -> CharacterData {
+ CharacterData {
+ node: Node::new_inherited(id, document),
+ data: Traceable::new(RefCell::new(data)),
+ }
+ }
+}
+
+impl<'a> CharacterDataMethods for JSRef<'a, CharacterData> {
+ fn Data(&self) -> DOMString {
+ self.data.deref().borrow().clone()
+ }
+
+ fn SetData(&self, arg: DOMString) -> ErrorResult {
+ *self.data.deref().borrow_mut() = arg;
+ Ok(())
+ }
+
+ fn Length(&self) -> u32 {
+ self.data.deref().borrow().len() as u32
+ }
+
+ fn SubstringData(&self, offset: u32, count: u32) -> Fallible<DOMString> {
+ Ok(self.data.deref().borrow().as_slice().slice(offset as uint, count as uint).to_string())
+ }
+
+ fn AppendData(&self, arg: DOMString) -> ErrorResult {
+ self.data.deref().borrow_mut().push_str(arg.as_slice());
+ Ok(())
+ }
+
+ fn InsertData(&self, offset: u32, arg: DOMString) -> ErrorResult {
+ self.ReplaceData(offset, 0, arg)
+ }
+
+ fn DeleteData(&self, offset: u32, count: u32) -> ErrorResult {
+ self.ReplaceData(offset, count, "".to_string())
+ }
+
+ fn ReplaceData(&self, offset: u32, count: u32, arg: DOMString) -> ErrorResult {
+ let length = self.data.deref().borrow().len() as u32;
+ if offset > length {
+ return Err(IndexSize);
+ }
+ let count = if offset + count > length {
+ length - offset
+ } else {
+ count
+ };
+ let mut data = self.data.deref().borrow().as_slice().slice(0, offset as uint).to_string();
+ data.push_str(arg.as_slice());
+ data.push_str(self.data.deref().borrow().as_slice().slice((offset + count) as uint, length as uint));
+ *self.data.deref().borrow_mut() = data;
+ // FIXME: Once we have `Range`, we should implement step7 to step11
+ Ok(())
+ }
+
+ // http://dom.spec.whatwg.org/#dom-childnode-remove
+ fn Remove(&self) {
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ node.remove_self();
+ }
+}
+
+impl Reflectable for CharacterData {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.node.reflector()
+ }
+}
diff --git a/components/script/dom/comment.rs b/components/script/dom/comment.rs
new file mode 100644
index 00000000000..e50b24b2a58
--- /dev/null
+++ b/components/script/dom/comment.rs
@@ -0,0 +1,55 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::CommentBinding;
+use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
+use dom::bindings::codegen::InheritTypes::CommentDerived;
+use dom::bindings::error::Fallible;
+use dom::bindings::global::GlobalRef;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::characterdata::CharacterData;
+use dom::document::Document;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::node::{CommentNodeTypeId, Node};
+use servo_util::str::DOMString;
+
+/// An HTML comment.
+#[deriving(Encodable)]
+pub struct Comment {
+ pub characterdata: CharacterData,
+}
+
+impl CommentDerived for EventTarget {
+ fn is_comment(&self) -> bool {
+ self.type_id == NodeTargetTypeId(CommentNodeTypeId)
+ }
+}
+
+impl Comment {
+ pub fn new_inherited(text: DOMString, document: &JSRef<Document>) -> Comment {
+ Comment {
+ characterdata: CharacterData::new_inherited(CommentNodeTypeId, text, document)
+ }
+ }
+
+ pub fn new(text: DOMString, document: &JSRef<Document>) -> Temporary<Comment> {
+ let node = Comment::new_inherited(text, document);
+ Node::reflect_node(box node, document, CommentBinding::Wrap)
+ }
+
+ pub fn Constructor(global: &GlobalRef, data: DOMString) -> Fallible<Temporary<Comment>> {
+ let document = global.as_window().Document().root();
+ Ok(Comment::new(data, &*document))
+ }
+}
+
+pub trait CommentMethods {
+}
+
+impl Reflectable for Comment {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.characterdata.reflector()
+ }
+}
diff --git a/components/script/dom/console.rs b/components/script/dom/console.rs
new file mode 100644
index 00000000000..3e74617ebcf
--- /dev/null
+++ b/components/script/dom/console.rs
@@ -0,0 +1,65 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::ConsoleBinding;
+use dom::bindings::codegen::Bindings::ConsoleBinding::ConsoleMethods;
+use dom::bindings::global::GlobalRef;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct Console {
+ pub reflector_: Reflector
+}
+
+impl Console {
+ pub fn new_inherited() -> Console {
+ Console {
+ reflector_: Reflector::new()
+ }
+ }
+
+ pub fn new(global: &GlobalRef) -> Temporary<Console> {
+ reflect_dom_object(box Console::new_inherited(), global, ConsoleBinding::Wrap)
+ }
+}
+
+impl<'a> ConsoleMethods for JSRef<'a, Console> {
+ fn Log(&self, message: DOMString) {
+ println!("{:s}", message);
+ }
+
+ fn Debug(&self, message: DOMString) {
+ println!("{:s}", message);
+ }
+
+ fn Info(&self, message: DOMString) {
+ println!("{:s}", message);
+ }
+
+ fn Warn(&self, message: DOMString) {
+ println!("{:s}", message);
+ }
+
+ fn Error(&self, message: DOMString) {
+ println!("{:s}", message);
+ }
+
+ fn Assert(&self, condition: bool, message: Option<DOMString>) {
+ if !condition {
+ let message = match message {
+ Some(ref message) => message.as_slice(),
+ None => "no message",
+ };
+ println!("Assertion failed: {:s}", message);
+ }
+ }
+}
+
+impl Reflectable for Console {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ &self.reflector_
+ }
+}
diff --git a/components/script/dom/customevent.rs b/components/script/dom/customevent.rs
new file mode 100644
index 00000000000..159601783ac
--- /dev/null
+++ b/components/script/dom/customevent.rs
@@ -0,0 +1,79 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::CustomEventBinding;
+use dom::bindings::codegen::Bindings::CustomEventBinding::CustomEventMethods;
+use dom::bindings::codegen::Bindings::EventBinding::EventMethods;
+use dom::bindings::codegen::InheritTypes::{EventCast, CustomEventDerived};
+use dom::bindings::error::Fallible;
+use dom::bindings::global::GlobalRef;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::trace::Traceable;
+use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
+use dom::event::{Event, EventTypeId, CustomEventTypeId};
+use js::jsapi::JSContext;
+use js::jsval::{JSVal, NullValue};
+use servo_util::str::DOMString;
+
+use std::cell::Cell;
+
+#[deriving(Encodable)]
+pub struct CustomEvent {
+ event: Event,
+ detail: Traceable<Cell<Traceable<JSVal>>>,
+}
+
+impl CustomEventDerived for Event {
+ fn is_customevent(&self) -> bool {
+ self.type_id == CustomEventTypeId
+ }
+}
+
+impl CustomEvent {
+ pub fn new_inherited(type_id: EventTypeId) -> CustomEvent {
+ CustomEvent {
+ event: Event::new_inherited(type_id),
+ detail: Traceable::new(Cell::new(Traceable::new(NullValue()))),
+ }
+ }
+
+ pub fn new_uninitialized(global: &GlobalRef) -> Temporary<CustomEvent> {
+ reflect_dom_object(box CustomEvent::new_inherited(CustomEventTypeId),
+ global,
+ CustomEventBinding::Wrap)
+ }
+ pub fn new(global: &GlobalRef, type_: DOMString, bubbles: bool, cancelable: bool, detail: JSVal) -> Temporary<CustomEvent> {
+ let ev = CustomEvent::new_uninitialized(global).root();
+ ev.deref().InitCustomEvent(global.get_cx(), type_, bubbles, cancelable, detail);
+ Temporary::from_rooted(&*ev)
+ }
+ pub fn Constructor(global: &GlobalRef,
+ type_: DOMString,
+ init: &CustomEventBinding::CustomEventInit) -> Fallible<Temporary<CustomEvent>>{
+ Ok(CustomEvent::new(global, type_, init.parent.bubbles, init.parent.cancelable, init.detail))
+ }
+}
+
+impl<'a> CustomEventMethods for JSRef<'a, CustomEvent> {
+ fn Detail(&self, _cx: *mut JSContext) -> JSVal {
+ *self.detail.deref().get()
+ }
+
+ fn InitCustomEvent(&self,
+ _cx: *mut JSContext,
+ type_: DOMString,
+ can_bubble: bool,
+ cancelable: bool,
+ detail: JSVal) {
+ self.detail.deref().set(Traceable::new(detail));
+ let event: &JSRef<Event> = EventCast::from_ref(self);
+ event.InitEvent(type_, can_bubble, cancelable);
+ }
+}
+
+impl Reflectable for CustomEvent {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.event.reflector()
+ }
+}
diff --git a/components/script/dom/dedicatedworkerglobalscope.rs b/components/script/dom/dedicatedworkerglobalscope.rs
new file mode 100644
index 00000000000..15bf075df44
--- /dev/null
+++ b/components/script/dom/dedicatedworkerglobalscope.rs
@@ -0,0 +1,200 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding;
+use dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding::DedicatedWorkerGlobalScopeMethods;
+use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
+use dom::bindings::codegen::InheritTypes::DedicatedWorkerGlobalScopeDerived;
+use dom::bindings::codegen::InheritTypes::{EventTargetCast, WorkerGlobalScopeCast};
+use dom::bindings::global::Worker;
+use dom::bindings::js::{JSRef, Temporary, RootCollection};
+use dom::bindings::trace::Untraceable;
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::eventtarget::{EventTarget, EventTargetHelpers};
+use dom::eventtarget::WorkerGlobalScopeTypeId;
+use dom::messageevent::MessageEvent;
+use dom::worker::{Worker, TrustedWorkerAddress};
+use dom::workerglobalscope::DedicatedGlobalScope;
+use dom::workerglobalscope::WorkerGlobalScope;
+use dom::xmlhttprequest::XMLHttpRequest;
+use script_task::{ScriptTask, ScriptChan};
+use script_task::{ScriptMsg, DOMMessage, XHRProgressMsg, WorkerRelease};
+use script_task::WorkerPostMessage;
+use script_task::StackRootTLS;
+
+use servo_net::resource_task::{ResourceTask, load_whole_resource};
+
+use js::glue::JS_STRUCTURED_CLONE_VERSION;
+use js::jsapi::{JSContext, JS_ReadStructuredClone, JS_WriteStructuredClone};
+use js::jsval::{JSVal, UndefinedValue};
+use js::rust::Cx;
+
+use std::rc::Rc;
+use std::ptr;
+use std::task::TaskBuilder;
+use native::task::NativeTaskBuilder;
+use url::Url;
+
+#[deriving(Encodable)]
+pub struct DedicatedWorkerGlobalScope {
+ workerglobalscope: WorkerGlobalScope,
+ receiver: Untraceable<Receiver<ScriptMsg>>,
+ /// Sender to the parent thread.
+ parent_sender: ScriptChan,
+ worker: Untraceable<TrustedWorkerAddress>,
+}
+
+impl DedicatedWorkerGlobalScope {
+ pub fn new_inherited(worker_url: Url,
+ worker: TrustedWorkerAddress,
+ cx: Rc<Cx>,
+ resource_task: ResourceTask,
+ parent_sender: ScriptChan,
+ own_sender: ScriptChan,
+ receiver: Receiver<ScriptMsg>)
+ -> DedicatedWorkerGlobalScope {
+ DedicatedWorkerGlobalScope {
+ workerglobalscope: WorkerGlobalScope::new_inherited(
+ DedicatedGlobalScope, worker_url, cx, resource_task,
+ own_sender),
+ receiver: Untraceable::new(receiver),
+ parent_sender: parent_sender,
+ worker: Untraceable::new(worker),
+ }
+ }
+
+ pub fn new(worker_url: Url,
+ worker: TrustedWorkerAddress,
+ cx: Rc<Cx>,
+ resource_task: ResourceTask,
+ parent_sender: ScriptChan,
+ own_sender: ScriptChan,
+ receiver: Receiver<ScriptMsg>)
+ -> Temporary<DedicatedWorkerGlobalScope> {
+ let scope = box DedicatedWorkerGlobalScope::new_inherited(
+ worker_url, worker, cx.clone(), resource_task, parent_sender,
+ own_sender, receiver);
+ DedicatedWorkerGlobalScopeBinding::Wrap(cx.ptr, scope)
+ }
+}
+
+impl DedicatedWorkerGlobalScope {
+ pub fn run_worker_scope(worker_url: Url,
+ worker: TrustedWorkerAddress,
+ resource_task: ResourceTask,
+ parent_sender: ScriptChan,
+ own_sender: ScriptChan,
+ receiver: Receiver<ScriptMsg>) {
+ TaskBuilder::new()
+ .native()
+ .named(format!("Web Worker at {}", worker_url.serialize()))
+ .spawn(proc() {
+ let roots = RootCollection::new();
+ let _stack_roots_tls = StackRootTLS::new(&roots);
+
+ let (url, source) = match load_whole_resource(&resource_task, worker_url.clone()) {
+ Err(_) => {
+ println!("error loading script {}", worker_url.serialize());
+ return;
+ }
+ Ok((metadata, bytes)) => {
+ (metadata.final_url, String::from_utf8(bytes).unwrap())
+ }
+ };
+
+ let (_js_runtime, js_context) = ScriptTask::new_rt_and_cx();
+ let global = DedicatedWorkerGlobalScope::new(
+ worker_url, worker, js_context.clone(), resource_task,
+ parent_sender, own_sender, receiver).root();
+ match js_context.evaluate_script(
+ global.reflector().get_jsobject(), source, url.serialize(), 1) {
+ Ok(_) => (),
+ Err(_) => println!("evaluate_script failed")
+ }
+ global.delayed_release_worker();
+
+ let scope: &JSRef<WorkerGlobalScope> =
+ WorkerGlobalScopeCast::from_ref(&*global);
+ let target: &JSRef<EventTarget> =
+ EventTargetCast::from_ref(&*global);
+ loop {
+ match global.receiver.recv_opt() {
+ Ok(DOMMessage(data, nbytes)) => {
+ let mut message = UndefinedValue();
+ unsafe {
+ assert!(JS_ReadStructuredClone(
+ js_context.ptr, data as *const u64, nbytes,
+ JS_STRUCTURED_CLONE_VERSION, &mut message,
+ ptr::null(), ptr::mut_null()) != 0);
+ }
+
+ MessageEvent::dispatch_jsval(target, &Worker(*scope), message);
+ global.delayed_release_worker();
+ },
+ Ok(XHRProgressMsg(addr, progress)) => {
+ XMLHttpRequest::handle_xhr_progress(addr, progress)
+ },
+ Ok(WorkerPostMessage(addr, data, nbytes)) => {
+ Worker::handle_message(addr, data, nbytes);
+ },
+ Ok(WorkerRelease(addr)) => {
+ Worker::handle_release(addr)
+ },
+ Ok(_) => fail!("Unexpected message"),
+ Err(_) => break,
+ }
+ }
+ });
+ }
+}
+
+impl<'a> DedicatedWorkerGlobalScopeMethods for JSRef<'a, DedicatedWorkerGlobalScope> {
+ fn PostMessage(&self, cx: *mut JSContext, message: JSVal) {
+ let mut data = ptr::mut_null();
+ let mut nbytes = 0;
+ unsafe {
+ assert!(JS_WriteStructuredClone(cx, message, &mut data, &mut nbytes,
+ ptr::null(), ptr::mut_null()) != 0);
+ }
+
+ let ScriptChan(ref sender) = self.parent_sender;
+ sender.send(WorkerPostMessage(*self.worker, data, nbytes));
+ }
+
+ fn GetOnmessage(&self) -> Option<EventHandlerNonNull> {
+ let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self);
+ eventtarget.get_event_handler_common("message")
+ }
+
+ fn SetOnmessage(&self, listener: Option<EventHandlerNonNull>) {
+ let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self);
+ eventtarget.set_event_handler_common("message", listener)
+ }
+}
+
+trait PrivateDedicatedWorkerGlobalScopeHelpers {
+ fn delayed_release_worker(&self);
+}
+
+impl<'a> PrivateDedicatedWorkerGlobalScopeHelpers for JSRef<'a, DedicatedWorkerGlobalScope> {
+ fn delayed_release_worker(&self) {
+ let ScriptChan(ref sender) = self.parent_sender;
+ sender.send(WorkerRelease(*self.worker));
+ }
+}
+
+impl Reflectable for DedicatedWorkerGlobalScope {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.workerglobalscope.reflector()
+ }
+}
+
+impl DedicatedWorkerGlobalScopeDerived for EventTarget {
+ fn is_dedicatedworkerglobalscope(&self) -> bool {
+ match self.type_id {
+ WorkerGlobalScopeTypeId(DedicatedGlobalScope) => true,
+ _ => false
+ }
+ }
+}
diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs
new file mode 100644
index 00000000000..503f618384d
--- /dev/null
+++ b/components/script/dom/document.rs
@@ -0,0 +1,855 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::DocumentBinding;
+use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
+use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
+use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
+use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
+use dom::bindings::codegen::InheritTypes::{DocumentDerived, EventCast, HTMLElementCast};
+use dom::bindings::codegen::InheritTypes::{HTMLHeadElementCast, TextCast, ElementCast};
+use dom::bindings::codegen::InheritTypes::{DocumentTypeCast, HTMLHtmlElementCast, NodeCast};
+use dom::bindings::codegen::InheritTypes::EventTargetCast;
+use dom::bindings::codegen::InheritTypes::{HTMLAnchorElementDerived, HTMLAppletElementDerived};
+use dom::bindings::codegen::InheritTypes::{HTMLAreaElementDerived, HTMLEmbedElementDerived};
+use dom::bindings::codegen::InheritTypes::{HTMLFormElementDerived, HTMLImageElementDerived};
+use dom::bindings::codegen::InheritTypes::{HTMLScriptElementDerived};
+use dom::bindings::error::{ErrorResult, Fallible, NotSupported, InvalidCharacter};
+use dom::bindings::error::{HierarchyRequest, NamespaceError};
+use dom::bindings::global::{GlobalRef, Window};
+use dom::bindings::js::{JS, JSRef, Temporary, OptionalSettable, TemporaryPushable};
+use dom::bindings::js::OptionalRootable;
+use dom::bindings::trace::{Traceable, Untraceable};
+use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
+use dom::bindings::utils::{xml_name_type, InvalidXMLName, Name, QName};
+use dom::comment::Comment;
+use dom::customevent::CustomEvent;
+use dom::documentfragment::DocumentFragment;
+use dom::documenttype::DocumentType;
+use dom::domimplementation::DOMImplementation;
+use dom::element::{Element, AttributeHandlers, get_attribute_parts};
+use dom::element::{HTMLHtmlElementTypeId, HTMLHeadElementTypeId, HTMLTitleElementTypeId};
+use dom::element::{HTMLBodyElementTypeId, HTMLFrameSetElementTypeId};
+use dom::event::Event;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId, EventTargetHelpers};
+use dom::htmlcollection::{HTMLCollection, CollectionFilter};
+use dom::htmlelement::HTMLElement;
+use dom::htmlheadelement::HTMLHeadElement;
+use dom::htmlhtmlelement::HTMLHtmlElement;
+use dom::htmltitleelement::HTMLTitleElement;
+use dom::location::Location;
+use dom::mouseevent::MouseEvent;
+use dom::node::{Node, ElementNodeTypeId, DocumentNodeTypeId, NodeHelpers};
+use dom::node::{CloneChildren, DoNotCloneChildren};
+use dom::nodelist::NodeList;
+use dom::text::Text;
+use dom::processinginstruction::ProcessingInstruction;
+use dom::range::Range;
+use dom::uievent::UIEvent;
+use dom::window::{Window, WindowHelpers};
+use html::hubbub_html_parser::build_element_from_tag;
+use hubbub::hubbub::{QuirksMode, NoQuirks, LimitedQuirks, FullQuirks};
+use layout_interface::{DocumentDamageLevel, ContentChangedDocumentDamage};
+use servo_util::namespace;
+use servo_util::namespace::{Namespace, Null};
+use servo_util::str::{DOMString, null_str_as_empty_ref, split_html_space_chars};
+
+use std::collections::hashmap::HashMap;
+use std::ascii::StrAsciiExt;
+use std::cell::{Cell, RefCell};
+use url::Url;
+use time;
+
+#[deriving(PartialEq,Encodable)]
+pub enum IsHTMLDocument {
+ HTMLDocument,
+ NonHTMLDocument,
+}
+
+#[deriving(Encodable)]
+pub struct Document {
+ pub node: Node,
+ reflector_: Reflector,
+ pub window: JS<Window>,
+ idmap: Traceable<RefCell<HashMap<DOMString, Vec<JS<Element>>>>>,
+ implementation: Cell<Option<JS<DOMImplementation>>>,
+ content_type: DOMString,
+ last_modified: Traceable<RefCell<Option<DOMString>>>,
+ pub encoding_name: Traceable<RefCell<DOMString>>,
+ pub is_html_document: bool,
+ url: Untraceable<Url>,
+ quirks_mode: Untraceable<Cell<QuirksMode>>,
+ images: Cell<Option<JS<HTMLCollection>>>,
+ embeds: Cell<Option<JS<HTMLCollection>>>,
+ links: Cell<Option<JS<HTMLCollection>>>,
+ forms: Cell<Option<JS<HTMLCollection>>>,
+ scripts: Cell<Option<JS<HTMLCollection>>>,
+ anchors: Cell<Option<JS<HTMLCollection>>>,
+ applets: Cell<Option<JS<HTMLCollection>>>,
+}
+
+impl DocumentDerived for EventTarget {
+ fn is_document(&self) -> bool {
+ self.type_id == NodeTargetTypeId(DocumentNodeTypeId)
+ }
+}
+
+struct ImagesFilter;
+impl CollectionFilter for ImagesFilter {
+ fn filter(&self, elem: &JSRef<Element>, _root: &JSRef<Node>) -> bool {
+ elem.is_htmlimageelement()
+ }
+}
+
+struct EmbedsFilter;
+impl CollectionFilter for EmbedsFilter {
+ fn filter(&self, elem: &JSRef<Element>, _root: &JSRef<Node>) -> bool {
+ elem.is_htmlembedelement()
+ }
+}
+
+struct LinksFilter;
+impl CollectionFilter for LinksFilter {
+ fn filter(&self, elem: &JSRef<Element>, _root: &JSRef<Node>) -> bool {
+ (elem.is_htmlanchorelement() || elem.is_htmlareaelement()) && elem.has_attribute("href")
+ }
+}
+
+struct FormsFilter;
+impl CollectionFilter for FormsFilter {
+ fn filter(&self, elem: &JSRef<Element>, _root: &JSRef<Node>) -> bool {
+ elem.is_htmlformelement()
+ }
+}
+
+struct ScriptsFilter;
+impl CollectionFilter for ScriptsFilter {
+ fn filter(&self, elem: &JSRef<Element>, _root: &JSRef<Node>) -> bool {
+ elem.is_htmlscriptelement()
+ }
+}
+
+struct AnchorsFilter;
+impl CollectionFilter for AnchorsFilter {
+ fn filter(&self, elem: &JSRef<Element>, _root: &JSRef<Node>) -> bool {
+ elem.is_htmlanchorelement() && elem.has_attribute("href")
+ }
+}
+
+struct AppletsFilter;
+impl CollectionFilter for AppletsFilter {
+ fn filter(&self, elem: &JSRef<Element>, _root: &JSRef<Node>) -> bool {
+ elem.is_htmlappletelement()
+ }
+}
+
+pub trait DocumentHelpers {
+ fn url<'a>(&'a self) -> &'a Url;
+ fn quirks_mode(&self) -> QuirksMode;
+ fn set_quirks_mode(&self, mode: QuirksMode);
+ fn set_last_modified(&self, value: DOMString);
+ fn set_encoding_name(&self, name: DOMString);
+ fn content_changed(&self);
+ fn damage_and_reflow(&self, damage: DocumentDamageLevel);
+ fn wait_until_safe_to_modify_dom(&self);
+ fn unregister_named_element(&self, to_unregister: &JSRef<Element>, id: DOMString);
+ fn register_named_element(&self, element: &JSRef<Element>, id: DOMString);
+ fn load_anchor_href(&self, href: DOMString);
+}
+
+impl<'a> DocumentHelpers for JSRef<'a, Document> {
+ fn url<'a>(&'a self) -> &'a Url {
+ &*self.url
+ }
+
+ fn quirks_mode(&self) -> QuirksMode {
+ self.quirks_mode.deref().get()
+ }
+
+ fn set_quirks_mode(&self, mode: QuirksMode) {
+ self.quirks_mode.deref().set(mode);
+ }
+
+ fn set_last_modified(&self, value: DOMString) {
+ *self.last_modified.deref().borrow_mut() = Some(value);
+ }
+
+ fn set_encoding_name(&self, name: DOMString) {
+ *self.encoding_name.deref().borrow_mut() = name;
+ }
+
+ fn content_changed(&self) {
+ self.damage_and_reflow(ContentChangedDocumentDamage);
+ }
+
+ fn damage_and_reflow(&self, damage: DocumentDamageLevel) {
+ self.window.root().damage_and_reflow(damage);
+ }
+
+ fn wait_until_safe_to_modify_dom(&self) {
+ self.window.root().wait_until_safe_to_modify_dom();
+ }
+
+
+ /// Remove any existing association between the provided id and any elements in this document.
+ fn unregister_named_element(&self,
+ to_unregister: &JSRef<Element>,
+ id: DOMString) {
+ let mut idmap = self.idmap.deref().borrow_mut();
+ let is_empty = match idmap.find_mut(&id) {
+ None => false,
+ Some(elements) => {
+ let position = elements.iter()
+ .map(|elem| elem.root())
+ .position(|element| &*element == to_unregister)
+ .expect("This element should be in registered.");
+ elements.remove(position);
+ elements.is_empty()
+ }
+ };
+ if is_empty {
+ idmap.remove(&id);
+ }
+ }
+
+ /// Associate an element present in this document with the provided id.
+ fn register_named_element(&self,
+ element: &JSRef<Element>,
+ id: DOMString) {
+ assert!({
+ let node: &JSRef<Node> = NodeCast::from_ref(element);
+ node.is_in_doc()
+ });
+ assert!(!id.is_empty());
+
+ let mut idmap = self.idmap.deref().borrow_mut();
+
+ // FIXME https://github.com/mozilla/rust/issues/13195
+ // Use mangle() when it exists again.
+ let root = self.GetDocumentElement().expect("The element is in the document, so there must be a document element.").root();
+ match idmap.find_mut(&id) {
+ Some(elements) => {
+ let new_node: &JSRef<Node> = NodeCast::from_ref(element);
+ let mut head : uint = 0u;
+ let root: &JSRef<Node> = NodeCast::from_ref(&*root);
+ for node in root.traverse_preorder() {
+ let elem: Option<&JSRef<Element>> = ElementCast::to_ref(&node);
+ match elem {
+ Some(elem) => {
+ if &*(*elements)[head].root() == elem {
+ head = head + 1;
+ }
+ if new_node == &node || head == elements.len() {
+ break;
+ }
+ }
+ None => {}
+ }
+ }
+ elements.insert_unrooted(head, element);
+ return;
+ },
+ None => (),
+ }
+ let mut elements = vec!();
+ elements.push_unrooted(element);
+ idmap.insert(id, elements);
+ }
+
+ fn load_anchor_href(&self, href: DOMString) {
+ let window = self.window.root();
+ window.load_url(href);
+ }
+}
+
+impl Document {
+ pub fn new_inherited(window: &JSRef<Window>,
+ url: Option<Url>,
+ is_html_document: IsHTMLDocument,
+ content_type: Option<DOMString>) -> Document {
+ let url = url.unwrap_or_else(|| Url::parse("about:blank").unwrap());
+
+ Document {
+ node: Node::new_without_doc(DocumentNodeTypeId),
+ reflector_: Reflector::new(),
+ window: JS::from_rooted(window),
+ idmap: Traceable::new(RefCell::new(HashMap::new())),
+ implementation: Cell::new(None),
+ content_type: match content_type {
+ Some(string) => string.clone(),
+ None => match is_html_document {
+ // http://dom.spec.whatwg.org/#dom-domimplementation-createhtmldocument
+ HTMLDocument => "text/html".to_string(),
+ // http://dom.spec.whatwg.org/#concept-document-content-type
+ NonHTMLDocument => "application/xml".to_string()
+ }
+ },
+ last_modified: Traceable::new(RefCell::new(None)),
+ url: Untraceable::new(url),
+ // http://dom.spec.whatwg.org/#concept-document-quirks
+ quirks_mode: Untraceable::new(Cell::new(NoQuirks)),
+ // http://dom.spec.whatwg.org/#concept-document-encoding
+ encoding_name: Traceable::new(RefCell::new("utf-8".to_string())),
+ is_html_document: is_html_document == HTMLDocument,
+ images: Cell::new(None),
+ embeds: Cell::new(None),
+ links: Cell::new(None),
+ forms: Cell::new(None),
+ scripts: Cell::new(None),
+ anchors: Cell::new(None),
+ applets: Cell::new(None),
+ }
+ }
+
+ // http://dom.spec.whatwg.org/#dom-document
+ pub fn Constructor(global: &GlobalRef) -> Fallible<Temporary<Document>> {
+ Ok(Document::new(global.as_window(), None, NonHTMLDocument, None))
+ }
+
+ pub fn new(window: &JSRef<Window>, url: Option<Url>, doctype: IsHTMLDocument, content_type: Option<DOMString>) -> Temporary<Document> {
+ let document = Document::new_inherited(window, url, doctype, content_type);
+ let document = reflect_dom_object(box document, &Window(*window),
+ DocumentBinding::Wrap).root();
+
+ let node: &JSRef<Node> = NodeCast::from_ref(&*document);
+ node.set_owner_doc(&*document);
+ Temporary::from_rooted(&*document)
+ }
+}
+
+impl Reflectable for Document {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.node.reflector()
+ }
+}
+
+trait PrivateDocumentHelpers {
+ fn createNodeList(&self, callback: |node: &JSRef<Node>| -> bool) -> Temporary<NodeList>;
+ fn get_html_element(&self) -> Option<Temporary<HTMLHtmlElement>>;
+}
+
+impl<'a> PrivateDocumentHelpers for JSRef<'a, Document> {
+ fn createNodeList(&self, callback: |node: &JSRef<Node>| -> bool) -> Temporary<NodeList> {
+ let window = self.window.root();
+
+ match self.GetDocumentElement().root() {
+ None => {
+ NodeList::new_simple_list(&*window, vec!())
+ },
+ Some(root) => {
+ let mut nodes = vec!();
+ let root: &JSRef<Node> = NodeCast::from_ref(&*root);
+ for child in root.traverse_preorder() {
+ if callback(&child) {
+ nodes.push(child);
+ }
+ }
+ NodeList::new_simple_list(&*window, nodes)
+ }
+ }
+
+ }
+
+ fn get_html_element(&self) -> Option<Temporary<HTMLHtmlElement>> {
+ self.GetDocumentElement().root().filtered(|root| {
+ let root: &JSRef<Node> = NodeCast::from_ref(&**root);
+ root.type_id() == ElementNodeTypeId(HTMLHtmlElementTypeId)
+ }).map(|elem| {
+ Temporary::from_rooted(HTMLHtmlElementCast::to_ref(&*elem).unwrap())
+ })
+ }
+}
+
+impl<'a> DocumentMethods for JSRef<'a, Document> {
+ // http://dom.spec.whatwg.org/#dom-document-implementation
+ fn Implementation(&self) -> Temporary<DOMImplementation> {
+ if self.implementation.get().is_none() {
+ self.implementation.assign(Some(DOMImplementation::new(self)));
+ }
+ Temporary::new(self.implementation.get().get_ref().clone())
+ }
+
+ // http://dom.spec.whatwg.org/#dom-document-url
+ fn URL(&self) -> DOMString {
+ self.url().serialize()
+ }
+
+ // http://dom.spec.whatwg.org/#dom-document-documenturi
+ fn DocumentURI(&self) -> DOMString {
+ self.URL()
+ }
+
+ // http://dom.spec.whatwg.org/#dom-document-compatmode
+ fn CompatMode(&self) -> DOMString {
+ match self.quirks_mode.deref().get() {
+ LimitedQuirks | NoQuirks => "CSS1Compat".to_string(),
+ FullQuirks => "BackCompat".to_string()
+ }
+ }
+
+ // http://dom.spec.whatwg.org/#dom-document-characterset
+ fn CharacterSet(&self) -> DOMString {
+ self.encoding_name.deref().borrow().as_slice().to_ascii_lower()
+ }
+
+ // http://dom.spec.whatwg.org/#dom-document-content_type
+ fn ContentType(&self) -> DOMString {
+ self.content_type.clone()
+ }
+
+ // http://dom.spec.whatwg.org/#dom-document-doctype
+ fn GetDoctype(&self) -> Option<Temporary<DocumentType>> {
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ node.children().find(|child| {
+ child.is_doctype()
+ }).map(|node| {
+ let doctype: &JSRef<DocumentType> = DocumentTypeCast::to_ref(&node).unwrap();
+ Temporary::from_rooted(doctype)
+ })
+ }
+
+ // http://dom.spec.whatwg.org/#dom-document-documentelement
+ fn GetDocumentElement(&self) -> Option<Temporary<Element>> {
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ node.child_elements().next().map(|elem| Temporary::from_rooted(&elem))
+ }
+
+ // http://dom.spec.whatwg.org/#dom-document-getelementsbytagname
+ fn GetElementsByTagName(&self, tag_name: DOMString) -> Temporary<HTMLCollection> {
+ let window = self.window.root();
+ HTMLCollection::by_tag_name(&*window, NodeCast::from_ref(self), tag_name)
+ }
+
+ // http://dom.spec.whatwg.org/#dom-document-getelementsbytagnamens
+ fn GetElementsByTagNameNS(&self, maybe_ns: Option<DOMString>, tag_name: DOMString) -> Temporary<HTMLCollection> {
+ let window = self.window.root();
+ HTMLCollection::by_tag_name_ns(&*window, NodeCast::from_ref(self), tag_name, maybe_ns)
+ }
+
+ // http://dom.spec.whatwg.org/#dom-document-getelementsbyclassname
+ fn GetElementsByClassName(&self, classes: DOMString) -> Temporary<HTMLCollection> {
+ let window = self.window.root();
+
+ HTMLCollection::by_class_name(&*window, NodeCast::from_ref(self), classes)
+ }
+
+ // http://dom.spec.whatwg.org/#dom-nonelementparentnode-getelementbyid
+ fn GetElementById(&self, id: DOMString) -> Option<Temporary<Element>> {
+ match self.idmap.deref().borrow().find_equiv(&id) {
+ None => None,
+ Some(ref elements) => Some(Temporary::new((*elements)[0].clone())),
+ }
+ }
+
+ // http://dom.spec.whatwg.org/#dom-document-createelement
+ fn CreateElement(&self, local_name: DOMString) -> Fallible<Temporary<Element>> {
+ if xml_name_type(local_name.as_slice()) == InvalidXMLName {
+ debug!("Not a valid element name");
+ return Err(InvalidCharacter);
+ }
+ let local_name = local_name.as_slice().to_ascii_lower();
+ Ok(build_element_from_tag(local_name, namespace::HTML, self))
+ }
+
+ // http://dom.spec.whatwg.org/#dom-document-createelementns
+ fn CreateElementNS(&self,
+ namespace: Option<DOMString>,
+ qualified_name: DOMString) -> Fallible<Temporary<Element>> {
+ let ns = Namespace::from_str(null_str_as_empty_ref(&namespace));
+ match xml_name_type(qualified_name.as_slice()) {
+ InvalidXMLName => {
+ debug!("Not a valid element name");
+ return Err(InvalidCharacter);
+ },
+ Name => {
+ debug!("Not a valid qualified element name");
+ return Err(NamespaceError);
+ },
+ QName => {}
+ }
+
+ let (prefix_from_qname,
+ local_name_from_qname) = get_attribute_parts(qualified_name.as_slice());
+ match (&ns, prefix_from_qname.clone(), local_name_from_qname.as_slice()) {
+ // throw if prefix is not null and namespace is null
+ (&namespace::Null, Some(_), _) => {
+ debug!("Namespace can't be null with a non-null prefix");
+ return Err(NamespaceError);
+ },
+ // throw if prefix is "xml" and namespace is not the XML namespace
+ (_, Some(ref prefix), _) if "xml" == prefix.as_slice() && ns != namespace::XML => {
+ debug!("Namespace must be the xml namespace if the prefix is 'xml'");
+ return Err(NamespaceError);
+ },
+ // throw if namespace is the XMLNS namespace and neither qualifiedName nor prefix is "xmlns"
+ (&namespace::XMLNS, Some(ref prefix), _) if "xmlns" == prefix.as_slice() => {},
+ (&namespace::XMLNS, _, "xmlns") => {},
+ (&namespace::XMLNS, _, _) => {
+ debug!("The prefix or the qualified name must be 'xmlns' if namespace is the XMLNS namespace ");
+ return Err(NamespaceError);
+ },
+ _ => {}
+ }
+
+ if ns == namespace::HTML {
+ Ok(build_element_from_tag(local_name_from_qname.to_string(), ns, self))
+ } else {
+ Ok(Element::new(local_name_from_qname.to_string(), ns,
+ prefix_from_qname.map(|s| s.to_string()), self))
+ }
+ }
+
+ // http://dom.spec.whatwg.org/#dom-document-createdocumentfragment
+ fn CreateDocumentFragment(&self) -> Temporary<DocumentFragment> {
+ DocumentFragment::new(self)
+ }
+
+ // http://dom.spec.whatwg.org/#dom-document-createtextnode
+ fn CreateTextNode(&self, data: DOMString)
+ -> Temporary<Text> {
+ Text::new(data, self)
+ }
+
+ // http://dom.spec.whatwg.org/#dom-document-createcomment
+ fn CreateComment(&self, data: DOMString) -> Temporary<Comment> {
+ Comment::new(data, self)
+ }
+
+ // http://dom.spec.whatwg.org/#dom-document-createprocessinginstruction
+ fn CreateProcessingInstruction(&self, target: DOMString,
+ data: DOMString) -> Fallible<Temporary<ProcessingInstruction>> {
+ // Step 1.
+ if xml_name_type(target.as_slice()) == InvalidXMLName {
+ return Err(InvalidCharacter);
+ }
+
+ // Step 2.
+ if data.as_slice().contains("?>") {
+ return Err(InvalidCharacter);
+ }
+
+ // Step 3.
+ Ok(ProcessingInstruction::new(target, data, self))
+ }
+
+ // http://dom.spec.whatwg.org/#dom-document-importnode
+ fn ImportNode(&self, node: &JSRef<Node>, deep: bool) -> Fallible<Temporary<Node>> {
+ // Step 1.
+ if node.is_document() {
+ return Err(NotSupported);
+ }
+
+ // Step 2.
+ let clone_children = match deep {
+ true => CloneChildren,
+ false => DoNotCloneChildren
+ };
+
+ Ok(Node::clone(node, Some(self), clone_children))
+ }
+
+ // http://dom.spec.whatwg.org/#dom-document-adoptnode
+ fn AdoptNode(&self, node: &JSRef<Node>) -> Fallible<Temporary<Node>> {
+ // Step 1.
+ if node.is_document() {
+ return Err(NotSupported);
+ }
+
+ // Step 2.
+ Node::adopt(node, self);
+
+ // Step 3.
+ Ok(Temporary::from_rooted(node))
+ }
+
+ // http://dom.spec.whatwg.org/#dom-document-createevent
+ fn CreateEvent(&self, interface: DOMString) -> Fallible<Temporary<Event>> {
+ let window = self.window.root();
+
+ match interface.as_slice().to_ascii_lower().as_slice() {
+ // FIXME: Implement CustomEvent (http://dom.spec.whatwg.org/#customevent)
+ "uievents" | "uievent" => Ok(EventCast::from_temporary(UIEvent::new_uninitialized(&*window))),
+ "mouseevents" | "mouseevent" => Ok(EventCast::from_temporary(MouseEvent::new_uninitialized(&*window))),
+ "customevent" => Ok(EventCast::from_temporary(CustomEvent::new_uninitialized(&Window(*window)))),
+ "htmlevents" | "events" | "event" => Ok(Event::new_uninitialized(&Window(*window))),
+ _ => Err(NotSupported)
+ }
+ }
+
+ // http://www.whatwg.org/html/#dom-document-lastmodified
+ fn LastModified(&self) -> DOMString {
+ match *self.last_modified.borrow() {
+ Some(ref t) => t.clone(),
+ None => time::now().strftime("%m/%d/%Y %H:%M:%S"),
+ }
+ }
+
+ // http://dom.spec.whatwg.org/#dom-document-createrange
+ fn CreateRange(&self) -> Temporary<Range> {
+ Range::new(self)
+ }
+
+ // http://www.whatwg.org/specs/web-apps/current-work/#document.title
+ fn Title(&self) -> DOMString {
+ let mut title = String::new();
+ self.GetDocumentElement().root().map(|root| {
+ let root: &JSRef<Node> = NodeCast::from_ref(&*root);
+ root.traverse_preorder()
+ .find(|node| node.type_id() == ElementNodeTypeId(HTMLTitleElementTypeId))
+ .map(|title_elem| {
+ for child in title_elem.children() {
+ if child.is_text() {
+ let text: &JSRef<Text> = TextCast::to_ref(&child).unwrap();
+ title.push_str(text.deref().characterdata.data.deref().borrow().as_slice());
+ }
+ }
+ });
+ });
+ let v: Vec<&str> = split_html_space_chars(title.as_slice()).collect();
+ v.connect(" ")
+ }
+
+ // http://www.whatwg.org/specs/web-apps/current-work/#document.title
+ fn SetTitle(&self, title: DOMString) -> ErrorResult {
+ self.GetDocumentElement().root().map(|root| {
+ let root: &JSRef<Node> = NodeCast::from_ref(&*root);
+ let head_node = root.traverse_preorder().find(|child| {
+ child.type_id() == ElementNodeTypeId(HTMLHeadElementTypeId)
+ });
+ head_node.map(|head| {
+ let title_node = head.children().find(|child| {
+ child.type_id() == ElementNodeTypeId(HTMLTitleElementTypeId)
+ });
+
+ match title_node {
+ Some(ref title_node) => {
+ for title_child in title_node.children() {
+ assert!(title_node.RemoveChild(&title_child).is_ok());
+ }
+ if !title.is_empty() {
+ let new_text = self.CreateTextNode(title.clone()).root();
+ assert!(title_node.AppendChild(NodeCast::from_ref(&*new_text)).is_ok());
+ }
+ },
+ None => {
+ let new_title = HTMLTitleElement::new("title".to_string(), self).root();
+ let new_title: &JSRef<Node> = NodeCast::from_ref(&*new_title);
+
+ if !title.is_empty() {
+ let new_text = self.CreateTextNode(title.clone()).root();
+ assert!(new_title.AppendChild(NodeCast::from_ref(&*new_text)).is_ok());
+ }
+ assert!(head.AppendChild(new_title).is_ok());
+ },
+ }
+ });
+ });
+ Ok(())
+ }
+
+ // http://www.whatwg.org/specs/web-apps/current-work/#dom-document-head
+ fn GetHead(&self) -> Option<Temporary<HTMLHeadElement>> {
+ self.get_html_element().and_then(|root| {
+ let root = root.root();
+ let node: &JSRef<Node> = NodeCast::from_ref(&*root);
+ node.children().find(|child| {
+ child.type_id() == ElementNodeTypeId(HTMLHeadElementTypeId)
+ }).map(|node| {
+ Temporary::from_rooted(HTMLHeadElementCast::to_ref(&node).unwrap())
+ })
+ })
+ }
+
+ // http://www.whatwg.org/specs/web-apps/current-work/#dom-document-body
+ fn GetBody(&self) -> Option<Temporary<HTMLElement>> {
+ self.get_html_element().and_then(|root| {
+ let root = root.root();
+ let node: &JSRef<Node> = NodeCast::from_ref(&*root);
+ node.children().find(|child| {
+ match child.type_id() {
+ ElementNodeTypeId(HTMLBodyElementTypeId) |
+ ElementNodeTypeId(HTMLFrameSetElementTypeId) => true,
+ _ => false
+ }
+ }).map(|node| {
+ Temporary::from_rooted(HTMLElementCast::to_ref(&node).unwrap())
+ })
+ })
+ }
+
+ // http://www.whatwg.org/specs/web-apps/current-work/#dom-document-body
+ fn SetBody(&self, new_body: Option<JSRef<HTMLElement>>) -> ErrorResult {
+ // Step 1.
+ match new_body {
+ Some(ref htmlelem) => {
+ let node: &JSRef<Node> = NodeCast::from_ref(htmlelem);
+ match node.type_id() {
+ ElementNodeTypeId(HTMLBodyElementTypeId) | ElementNodeTypeId(HTMLFrameSetElementTypeId) => {}
+ _ => return Err(HierarchyRequest)
+ }
+ }
+ None => return Err(HierarchyRequest)
+ }
+
+ // Step 2.
+ let old_body = self.GetBody().root();
+ //FIXME: covariant lifetime workaround. do not judge.
+ if old_body.as_ref().map(|body| body.deref()) == new_body.as_ref().map(|a| &*a) {
+ return Ok(());
+ }
+
+ // Step 3.
+ match self.get_html_element().root() {
+ // Step 4.
+ None => return Err(HierarchyRequest),
+ Some(ref root) => {
+ let new_body_unwrapped = new_body.unwrap();
+ let new_body: &JSRef<Node> = NodeCast::from_ref(&new_body_unwrapped);
+
+ let root: &JSRef<Node> = NodeCast::from_ref(&**root);
+ match old_body {
+ Some(ref child) => {
+ let child: &JSRef<Node> = NodeCast::from_ref(&**child);
+
+ assert!(root.ReplaceChild(new_body, child).is_ok())
+ }
+ None => assert!(root.AppendChild(new_body).is_ok())
+ };
+ }
+ }
+ Ok(())
+ }
+
+ // http://www.whatwg.org/specs/web-apps/current-work/#dom-document-getelementsbyname
+ fn GetElementsByName(&self, name: DOMString) -> Temporary<NodeList> {
+ self.createNodeList(|node| {
+ if !node.is_element() {
+ return false;
+ }
+
+ let element: &JSRef<Element> = ElementCast::to_ref(node).unwrap();
+ element.get_attribute(Null, "name").root().map_or(false, |attr| {
+ attr.value().as_slice() == name.as_slice()
+ })
+ })
+ }
+
+ fn Images(&self) -> Temporary<HTMLCollection> {
+ if self.images.get().is_none() {
+ let window = self.window.root();
+ let root = NodeCast::from_ref(self);
+ let filter = box ImagesFilter;
+ self.images.assign(Some(HTMLCollection::create(&*window, root, filter)));
+ }
+ Temporary::new(self.images.get().get_ref().clone())
+ }
+
+ fn Embeds(&self) -> Temporary<HTMLCollection> {
+ if self.embeds.get().is_none() {
+ let window = self.window.root();
+ let root = NodeCast::from_ref(self);
+ let filter = box EmbedsFilter;
+ self.embeds.assign(Some(HTMLCollection::create(&*window, root, filter)));
+ }
+ Temporary::new(self.embeds.get().get_ref().clone())
+ }
+
+ fn Plugins(&self) -> Temporary<HTMLCollection> {
+ self.Embeds()
+ }
+
+ fn Links(&self) -> Temporary<HTMLCollection> {
+ if self.links.get().is_none() {
+ let window = self.window.root();
+ let root = NodeCast::from_ref(self);
+ let filter = box LinksFilter;
+ self.links.assign(Some(HTMLCollection::create(&*window, root, filter)));
+ }
+ Temporary::new(self.links.get().get_ref().clone())
+ }
+
+ fn Forms(&self) -> Temporary<HTMLCollection> {
+ if self.forms.get().is_none() {
+ let window = self.window.root();
+ let root = NodeCast::from_ref(self);
+ let filter = box FormsFilter;
+ self.forms.assign(Some(HTMLCollection::create(&*window, root, filter)));
+ }
+ Temporary::new(self.forms.get().get_ref().clone())
+ }
+
+ fn Scripts(&self) -> Temporary<HTMLCollection> {
+ if self.scripts.get().is_none() {
+ let window = self.window.root();
+ let root = NodeCast::from_ref(self);
+ let filter = box ScriptsFilter;
+ self.scripts.assign(Some(HTMLCollection::create(&*window, root, filter)));
+ }
+ Temporary::new(self.scripts.get().get_ref().clone())
+ }
+
+ fn Anchors(&self) -> Temporary<HTMLCollection> {
+ if self.anchors.get().is_none() {
+ let window = self.window.root();
+ let root = NodeCast::from_ref(self);
+ let filter = box AnchorsFilter;
+ self.anchors.assign(Some(HTMLCollection::create(&*window, root, filter)));
+ }
+ Temporary::new(self.anchors.get().get_ref().clone())
+ }
+
+ fn Applets(&self) -> Temporary<HTMLCollection> {
+ // FIXME: This should be return OBJECT elements containing applets.
+ if self.applets.get().is_none() {
+ let window = self.window.root();
+ let root = NodeCast::from_ref(self);
+ let filter = box AppletsFilter;
+ self.applets.assign(Some(HTMLCollection::create(&*window, root, filter)));
+ }
+ Temporary::new(self.applets.get().get_ref().clone())
+ }
+
+ fn Location(&self) -> Temporary<Location> {
+ let window = self.window.root();
+ window.Location()
+ }
+
+ // http://dom.spec.whatwg.org/#dom-parentnode-children
+ fn Children(&self) -> Temporary<HTMLCollection> {
+ let window = self.window.root();
+ HTMLCollection::children(&*window, NodeCast::from_ref(self))
+ }
+
+ // http://dom.spec.whatwg.org/#dom-parentnode-queryselector
+ fn QuerySelector(&self, selectors: DOMString) -> Fallible<Option<Temporary<Element>>> {
+ let root: &JSRef<Node> = NodeCast::from_ref(self);
+ root.query_selector(selectors)
+ }
+
+ // http://dom.spec.whatwg.org/#dom-parentnode-queryselectorall
+ fn QuerySelectorAll(&self, selectors: DOMString) -> Fallible<Temporary<NodeList>> {
+ let root: &JSRef<Node> = NodeCast::from_ref(self);
+ root.query_selector_all(selectors)
+ }
+
+ fn GetOnclick(&self) -> Option<EventHandlerNonNull> {
+ let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self);
+ eventtarget.get_event_handler_common("click")
+ }
+
+ fn SetOnclick(&self, listener: Option<EventHandlerNonNull>) {
+ let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self);
+ eventtarget.set_event_handler_common("click", listener)
+ }
+
+ fn GetOnload(&self) -> Option<EventHandlerNonNull> {
+ let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self);
+ eventtarget.get_event_handler_common("load")
+ }
+
+ fn SetOnload(&self, listener: Option<EventHandlerNonNull>) {
+ let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self);
+ eventtarget.set_event_handler_common("load", listener)
+ }
+}
diff --git a/components/script/dom/documentfragment.rs b/components/script/dom/documentfragment.rs
new file mode 100644
index 00000000000..1f3fcb29424
--- /dev/null
+++ b/components/script/dom/documentfragment.rs
@@ -0,0 +1,78 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::DocumentFragmentBinding;
+use dom::bindings::codegen::Bindings::DocumentFragmentBinding::DocumentFragmentMethods;
+use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
+use dom::bindings::codegen::InheritTypes::{DocumentFragmentDerived, NodeCast};
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::error::Fallible;
+use dom::bindings::global::GlobalRef;
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::Element;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlcollection::HTMLCollection;
+use dom::node::{DocumentFragmentNodeTypeId, Node, NodeHelpers, window_from_node};
+use dom::nodelist::NodeList;
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct DocumentFragment {
+ pub node: Node,
+}
+
+impl DocumentFragmentDerived for EventTarget {
+ fn is_documentfragment(&self) -> bool {
+ self.type_id == NodeTargetTypeId(DocumentFragmentNodeTypeId)
+ }
+}
+
+impl DocumentFragment {
+ /// Creates a new DocumentFragment.
+ pub fn new_inherited(document: &JSRef<Document>) -> DocumentFragment {
+ DocumentFragment {
+ node: Node::new_inherited(DocumentFragmentNodeTypeId, document),
+ }
+ }
+
+ pub fn new(document: &JSRef<Document>) -> Temporary<DocumentFragment> {
+ let node = DocumentFragment::new_inherited(document);
+ Node::reflect_node(box node, document, DocumentFragmentBinding::Wrap)
+ }
+
+ pub fn Constructor(global: &GlobalRef) -> Fallible<Temporary<DocumentFragment>> {
+ let document = global.as_window().Document();
+ let document = document.root();
+
+ Ok(DocumentFragment::new(&document.root_ref()))
+ }
+}
+
+impl<'a> DocumentFragmentMethods for JSRef<'a, DocumentFragment> {
+ // http://dom.spec.whatwg.org/#dom-parentnode-children
+ fn Children(&self) -> Temporary<HTMLCollection> {
+ let window = window_from_node(self).root();
+ HTMLCollection::children(&window.root_ref(), NodeCast::from_ref(self))
+ }
+
+ // http://dom.spec.whatwg.org/#dom-parentnode-queryselector
+ fn QuerySelector(&self, selectors: DOMString) -> Fallible<Option<Temporary<Element>>> {
+ let root: &JSRef<Node> = NodeCast::from_ref(self);
+ root.query_selector(selectors)
+ }
+
+ // http://dom.spec.whatwg.org/#dom-parentnode-queryselectorall
+ fn QuerySelectorAll(&self, selectors: DOMString) -> Fallible<Temporary<NodeList>> {
+ let root: &JSRef<Node> = NodeCast::from_ref(self);
+ root.query_selector_all(selectors)
+ }
+
+}
+
+impl Reflectable for DocumentFragment {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.node.reflector()
+ }
+}
diff --git a/components/script/dom/documenttype.rs b/components/script/dom/documenttype.rs
new file mode 100644
index 00000000000..5f101942139
--- /dev/null
+++ b/components/script/dom/documenttype.rs
@@ -0,0 +1,81 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::DocumentTypeBinding;
+use dom::bindings::codegen::Bindings::DocumentTypeBinding::DocumentTypeMethods;
+use dom::bindings::codegen::InheritTypes::{DocumentTypeDerived, NodeCast};
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::node::{Node, DoctypeNodeTypeId, NodeHelpers};
+use servo_util::str::DOMString;
+
+/// The `DOCTYPE` tag.
+#[deriving(Encodable)]
+pub struct DocumentType {
+ pub node: Node,
+ pub name: DOMString,
+ pub public_id: DOMString,
+ pub system_id: DOMString,
+}
+
+impl DocumentTypeDerived for EventTarget {
+ fn is_documenttype(&self) -> bool {
+ self.type_id == NodeTargetTypeId(DoctypeNodeTypeId)
+ }
+}
+
+impl DocumentType {
+ pub fn new_inherited(name: DOMString,
+ public_id: Option<DOMString>,
+ system_id: Option<DOMString>,
+ document: &JSRef<Document>)
+ -> DocumentType {
+ DocumentType {
+ node: Node::new_inherited(DoctypeNodeTypeId, document),
+ name: name,
+ public_id: public_id.unwrap_or("".to_string()),
+ system_id: system_id.unwrap_or("".to_string())
+ }
+ }
+
+ pub fn new(name: DOMString,
+ public_id: Option<DOMString>,
+ system_id: Option<DOMString>,
+ document: &JSRef<Document>)
+ -> Temporary<DocumentType> {
+ let documenttype = DocumentType::new_inherited(name,
+ public_id,
+ system_id,
+ document);
+ Node::reflect_node(box documenttype, document, DocumentTypeBinding::Wrap)
+ }
+}
+
+impl<'a> DocumentTypeMethods for JSRef<'a, DocumentType> {
+ fn Name(&self) -> DOMString {
+ self.name.clone()
+ }
+
+ fn PublicId(&self) -> DOMString {
+ self.public_id.clone()
+ }
+
+ fn SystemId(&self) -> DOMString {
+ self.system_id.clone()
+ }
+
+ // http://dom.spec.whatwg.org/#dom-childnode-remove
+ fn Remove(&self) {
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ node.remove_self();
+ }
+}
+
+impl Reflectable for DocumentType {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.node.reflector()
+ }
+}
diff --git a/components/script/dom/domexception.rs b/components/script/dom/domexception.rs
new file mode 100644
index 00000000000..7d1ba33ffb8
--- /dev/null
+++ b/components/script/dom/domexception.rs
@@ -0,0 +1,132 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::DOMExceptionBinding;
+use dom::bindings::codegen::Bindings::DOMExceptionBinding::DOMExceptionConstants;
+use dom::bindings::codegen::Bindings::DOMExceptionBinding::DOMExceptionMethods;
+use dom::bindings::error;
+use dom::bindings::error::Error;
+use dom::bindings::global::GlobalRef;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
+use servo_util::str::DOMString;
+
+#[repr(uint)]
+#[deriving(Show, Encodable)]
+pub enum DOMErrorName {
+ IndexSizeError = DOMExceptionConstants::INDEX_SIZE_ERR as uint,
+ HierarchyRequestError = DOMExceptionConstants::HIERARCHY_REQUEST_ERR as uint,
+ WrongDocumentError = DOMExceptionConstants::WRONG_DOCUMENT_ERR as uint,
+ InvalidCharacterError = DOMExceptionConstants::INVALID_CHARACTER_ERR as uint,
+ NoModificationAllowedError = DOMExceptionConstants::NO_MODIFICATION_ALLOWED_ERR as uint,
+ NotFoundError = DOMExceptionConstants::NOT_FOUND_ERR as uint,
+ NotSupportedError = DOMExceptionConstants::NOT_SUPPORTED_ERR as uint,
+ InvalidStateError = DOMExceptionConstants::INVALID_STATE_ERR as uint,
+ SyntaxError = DOMExceptionConstants::SYNTAX_ERR as uint,
+ InvalidModificationError = DOMExceptionConstants::INVALID_MODIFICATION_ERR as uint,
+ NamespaceError = DOMExceptionConstants::NAMESPACE_ERR as uint,
+ InvalidAccessError = DOMExceptionConstants::INVALID_ACCESS_ERR as uint,
+ SecurityError = DOMExceptionConstants::SECURITY_ERR as uint,
+ NetworkError = DOMExceptionConstants::NETWORK_ERR as uint,
+ AbortError = DOMExceptionConstants::ABORT_ERR as uint,
+ URLMismatchError = DOMExceptionConstants::URL_MISMATCH_ERR as uint,
+ QuotaExceededError = DOMExceptionConstants::QUOTA_EXCEEDED_ERR as uint,
+ TimeoutError = DOMExceptionConstants::TIMEOUT_ERR as uint,
+ InvalidNodeTypeError = DOMExceptionConstants::INVALID_NODE_TYPE_ERR as uint,
+ DataCloneError = DOMExceptionConstants::DATA_CLONE_ERR as uint,
+ EncodingError
+}
+
+impl DOMErrorName {
+ fn from_error(error: Error) -> DOMErrorName {
+ match error {
+ error::IndexSize => IndexSizeError,
+ error::NotFound => NotFoundError,
+ error::HierarchyRequest => HierarchyRequestError,
+ error::InvalidCharacter => InvalidCharacterError,
+ error::NotSupported => NotSupportedError,
+ error::InvalidState => InvalidStateError,
+ error::Syntax => SyntaxError,
+ error::NamespaceError => NamespaceError,
+ error::InvalidAccess => InvalidAccessError,
+ error::Security => SecurityError,
+ error::Network => NetworkError,
+ error::Abort => AbortError,
+ error::Timeout => TimeoutError,
+ error::FailureUnknown => fail!(),
+ }
+ }
+}
+
+#[deriving(Encodable)]
+pub struct DOMException {
+ pub code: DOMErrorName,
+ pub reflector_: Reflector
+}
+
+impl DOMException {
+ pub fn new_inherited(code: DOMErrorName) -> DOMException {
+ DOMException {
+ code: code,
+ reflector_: Reflector::new()
+ }
+ }
+
+ pub fn new(global: &GlobalRef, code: DOMErrorName) -> Temporary<DOMException> {
+ reflect_dom_object(box DOMException::new_inherited(code), global, DOMExceptionBinding::Wrap)
+ }
+
+ pub fn new_from_error(global: &GlobalRef, code: Error) -> Temporary<DOMException> {
+ DOMException::new(global, DOMErrorName::from_error(code))
+ }
+}
+
+impl Reflectable for DOMException {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ &self.reflector_
+ }
+}
+
+impl<'a> DOMExceptionMethods for JSRef<'a, DOMException> {
+ // http://dom.spec.whatwg.org/#dom-domexception-code
+ fn Code(&self) -> u16 {
+ match self.code {
+ // http://dom.spec.whatwg.org/#concept-throw
+ EncodingError => 0,
+ code => code as u16
+ }
+ }
+
+ // http://dom.spec.whatwg.org/#error-names-0
+ fn Name(&self) -> DOMString {
+ self.code.to_string()
+ }
+
+ // http://dom.spec.whatwg.org/#error-names-0
+ fn Message(&self) -> DOMString {
+ match self.code {
+ IndexSizeError => "The index is not in the allowed range.".to_string(),
+ HierarchyRequestError => "The operation would yield an incorrect node tree.".to_string(),
+ WrongDocumentError => "The object is in the wrong document.".to_string(),
+ InvalidCharacterError => "The string contains invalid characters.".to_string(),
+ NoModificationAllowedError => "The object can not be modified.".to_string(),
+ NotFoundError => "The object can not be found here.".to_string(),
+ NotSupportedError => "The operation is not supported.".to_string(),
+ InvalidStateError => "The object is in an invalid state.".to_string(),
+ SyntaxError => "The string did not match the expected pattern.".to_string(),
+ InvalidModificationError => "The object can not be modified in this way.".to_string(),
+ NamespaceError => "The operation is not allowed by Namespaces in XML.".to_string(),
+ InvalidAccessError => "The object does not support the operation or argument.".to_string(),
+ SecurityError => "The operation is insecure.".to_string(),
+ NetworkError => "A network error occurred.".to_string(),
+ AbortError => "The operation was aborted.".to_string(),
+ URLMismatchError => "The given URL does not match another URL.".to_string(),
+ QuotaExceededError => "The quota has been exceeded.".to_string(),
+ TimeoutError => "The operation timed out.".to_string(),
+ InvalidNodeTypeError => "The supplied node is incorrect or has an incorrect ancestor for this operation.".to_string(),
+ DataCloneError => "The object can not be cloned.".to_string(),
+ EncodingError => "The encoding operation (either encoded or decoding) failed.".to_string()
+ }
+ }
+}
diff --git a/components/script/dom/domimplementation.rs b/components/script/dom/domimplementation.rs
new file mode 100644
index 00000000000..0dbb842ebe4
--- /dev/null
+++ b/components/script/dom/domimplementation.rs
@@ -0,0 +1,172 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
+use dom::bindings::codegen::Bindings::DOMImplementationBinding;
+use dom::bindings::codegen::Bindings::DOMImplementationBinding::DOMImplementationMethods;
+use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
+use dom::bindings::codegen::InheritTypes::NodeCast;
+use dom::bindings::error::{Fallible, InvalidCharacter, NamespaceError};
+use dom::bindings::global::Window;
+use dom::bindings::js::{JS, JSRef, Root, Temporary, OptionalRootable};
+use dom::bindings::utils::{Reflector, Reflectable, reflect_dom_object};
+use dom::bindings::utils::{QName, Name, InvalidXMLName, xml_name_type};
+use dom::document::{Document, HTMLDocument, NonHTMLDocument};
+use dom::documenttype::DocumentType;
+use dom::htmlbodyelement::HTMLBodyElement;
+use dom::htmlheadelement::HTMLHeadElement;
+use dom::htmlhtmlelement::HTMLHtmlElement;
+use dom::htmltitleelement::HTMLTitleElement;
+use dom::node::Node;
+use dom::text::Text;
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct DOMImplementation {
+ document: JS<Document>,
+ reflector_: Reflector,
+}
+
+impl DOMImplementation {
+ pub fn new_inherited(document: &JSRef<Document>) -> DOMImplementation {
+ DOMImplementation {
+ document: JS::from_rooted(document),
+ reflector_: Reflector::new(),
+ }
+ }
+
+ pub fn new(document: &JSRef<Document>) -> Temporary<DOMImplementation> {
+ let window = document.window.root();
+ reflect_dom_object(box DOMImplementation::new_inherited(document),
+ &Window(*window),
+ DOMImplementationBinding::Wrap)
+ }
+}
+
+impl Reflectable for DOMImplementation {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ &self.reflector_
+ }
+}
+
+// http://dom.spec.whatwg.org/#domimplementation
+impl<'a> DOMImplementationMethods for JSRef<'a, DOMImplementation> {
+ // http://dom.spec.whatwg.org/#dom-domimplementation-createdocumenttype
+ fn CreateDocumentType(&self, qname: DOMString, pubid: DOMString, sysid: DOMString) -> Fallible<Temporary<DocumentType>> {
+ match xml_name_type(qname.as_slice()) {
+ // Step 1.
+ InvalidXMLName => Err(InvalidCharacter),
+ // Step 2.
+ Name => Err(NamespaceError),
+ // Step 3.
+ QName => {
+ let document = self.document.root();
+ Ok(DocumentType::new(qname, Some(pubid), Some(sysid), &*document))
+ }
+ }
+ }
+
+ // http://dom.spec.whatwg.org/#dom-domimplementation-createdocument
+ fn CreateDocument(&self, namespace: Option<DOMString>, qname: DOMString,
+ maybe_doctype: Option<JSRef<DocumentType>>) -> Fallible<Temporary<Document>> {
+ let doc = self.document.root();
+ let win = doc.window.root();
+
+ // Step 1.
+ let doc = Document::new(&win.root_ref(), None, NonHTMLDocument, None).root();
+ // Step 2-3.
+ let maybe_elem = if qname.is_empty() {
+ None
+ } else {
+ match doc.CreateElementNS(namespace, qname) {
+ Err(error) => return Err(error),
+ Ok(elem) => Some(elem)
+ }
+ };
+
+ {
+ let doc_node: &JSRef<Node> = NodeCast::from_ref(&*doc);
+
+ // Step 4.
+ match maybe_doctype {
+ None => (),
+ Some(ref doctype) => {
+ let doc_type: &JSRef<Node> = NodeCast::from_ref(doctype);
+ assert!(doc_node.AppendChild(doc_type).is_ok())
+ }
+ }
+
+ // Step 5.
+ match maybe_elem.root() {
+ None => (),
+ Some(elem) => {
+ assert!(doc_node.AppendChild(NodeCast::from_ref(&*elem)).is_ok())
+ }
+ }
+ }
+
+ // Step 6.
+ // FIXME: https://github.com/mozilla/servo/issues/1522
+
+ // Step 7.
+ Ok(Temporary::from_rooted(&*doc))
+ }
+
+ // http://dom.spec.whatwg.org/#dom-domimplementation-createhtmldocument
+ fn CreateHTMLDocument(&self, title: Option<DOMString>) -> Temporary<Document> {
+ let document = self.document.root();
+ let win = document.window.root();
+
+ // Step 1-2.
+ let doc = Document::new(&win.root_ref(), None, HTMLDocument, None).root();
+ let doc_node: &JSRef<Node> = NodeCast::from_ref(&*doc);
+
+ {
+ // Step 3.
+ let doc_type = DocumentType::new("html".to_string(), None, None, &*doc).root();
+ assert!(doc_node.AppendChild(NodeCast::from_ref(&*doc_type)).is_ok());
+ }
+
+ {
+ // Step 4.
+ let doc_html: Root<Node> = NodeCast::from_temporary(HTMLHtmlElement::new("html".to_string(), &*doc)).root();
+ let doc_html = doc_html.deref();
+ assert!(doc_node.AppendChild(doc_html).is_ok());
+
+ {
+ // Step 5.
+ let doc_head: Root<Node> = NodeCast::from_temporary(HTMLHeadElement::new("head".to_string(), &*doc)).root();
+ let doc_head = doc_head.deref();
+ assert!(doc_html.AppendChild(doc_head).is_ok());
+
+ // Step 6.
+ match title {
+ None => (),
+ Some(title_str) => {
+ // Step 6.1.
+ let doc_title: Root<Node> = NodeCast::from_temporary(HTMLTitleElement::new("title".to_string(), &*doc)).root();
+ let doc_title = doc_title.deref();
+ assert!(doc_head.AppendChild(doc_title).is_ok());
+
+ // Step 6.2.
+ let title_text: Root<Text> = Text::new(title_str, &*doc).root();
+ let title_text = title_text.deref();
+ assert!(doc_title.AppendChild(NodeCast::from_ref(title_text)).is_ok());
+ }
+ }
+ }
+
+ // Step 7.
+ let doc_body: Root<HTMLBodyElement> = HTMLBodyElement::new("body".to_string(), &*doc).root();
+ let doc_body = doc_body.deref();
+ assert!(doc_html.AppendChild(NodeCast::from_ref(doc_body)).is_ok());
+ }
+
+ // Step 8.
+ // FIXME: https://github.com/mozilla/servo/issues/1522
+
+ // Step 9.
+ Temporary::from_rooted(&*doc)
+ }
+}
diff --git a/components/script/dom/domparser.rs b/components/script/dom/domparser.rs
new file mode 100644
index 00000000000..65273cab756
--- /dev/null
+++ b/components/script/dom/domparser.rs
@@ -0,0 +1,64 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::DOMParserBinding;
+use dom::bindings::codegen::Bindings::DOMParserBinding::DOMParserMethods;
+use dom::bindings::codegen::Bindings::DOMParserBinding::SupportedTypeValues::{Text_html, Text_xml};
+use dom::bindings::error::{Fallible, FailureUnknown};
+use dom::bindings::global::{GlobalRef, Window};
+use dom::bindings::js::{JS, JSRef, Temporary};
+use dom::bindings::utils::{Reflector, Reflectable, reflect_dom_object};
+use dom::document::{Document, HTMLDocument, NonHTMLDocument};
+use dom::window::Window;
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct DOMParser {
+ window: JS<Window>, //XXXjdm Document instead?
+ reflector_: Reflector
+}
+
+impl DOMParser {
+ pub fn new_inherited(window: &JSRef<Window>) -> DOMParser {
+ DOMParser {
+ window: JS::from_rooted(window),
+ reflector_: Reflector::new()
+ }
+ }
+
+ pub fn new(window: &JSRef<Window>) -> Temporary<DOMParser> {
+ reflect_dom_object(box DOMParser::new_inherited(window), &Window(*window),
+ DOMParserBinding::Wrap)
+ }
+
+ pub fn Constructor(global: &GlobalRef) -> Fallible<Temporary<DOMParser>> {
+ Ok(DOMParser::new(global.as_window()))
+ }
+}
+
+impl<'a> DOMParserMethods for JSRef<'a, DOMParser> {
+ fn ParseFromString(&self,
+ _s: DOMString,
+ ty: DOMParserBinding::SupportedType)
+ -> Fallible<Temporary<Document>> {
+ let window = self.window.root();
+ match ty {
+ Text_html => {
+ Ok(Document::new(&window.root_ref(), None, HTMLDocument, Some("text/html".to_string())))
+ }
+ Text_xml => {
+ Ok(Document::new(&window.root_ref(), None, NonHTMLDocument, Some("text/xml".to_string())))
+ }
+ _ => {
+ Err(FailureUnknown)
+ }
+ }
+ }
+}
+
+impl Reflectable for DOMParser {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ &self.reflector_
+ }
+}
diff --git a/components/script/dom/domrect.rs b/components/script/dom/domrect.rs
new file mode 100644
index 00000000000..2cf75ee4eb9
--- /dev/null
+++ b/components/script/dom/domrect.rs
@@ -0,0 +1,72 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::DOMRectBinding;
+use dom::bindings::codegen::Bindings::DOMRectBinding::DOMRectMethods;
+use dom::bindings::global::Window;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
+use dom::window::Window;
+use servo_util::geometry::Au;
+
+#[deriving(Encodable)]
+pub struct DOMRect {
+ reflector_: Reflector,
+ top: f32,
+ bottom: f32,
+ left: f32,
+ right: f32,
+}
+
+impl DOMRect {
+ pub fn new_inherited(top: Au, bottom: Au,
+ left: Au, right: Au) -> DOMRect {
+ DOMRect {
+ top: top.to_nearest_px() as f32,
+ bottom: bottom.to_nearest_px() as f32,
+ left: left.to_nearest_px() as f32,
+ right: right.to_nearest_px() as f32,
+ reflector_: Reflector::new(),
+ }
+ }
+
+ pub fn new(window: &JSRef<Window>,
+ top: Au, bottom: Au,
+ left: Au, right: Au) -> Temporary<DOMRect> {
+ let rect = DOMRect::new_inherited(top, bottom, left, right);
+ reflect_dom_object(box rect, &Window(*window), DOMRectBinding::Wrap)
+ }
+}
+
+impl<'a> DOMRectMethods for JSRef<'a, DOMRect> {
+ fn Top(&self) -> f32 {
+ self.top
+ }
+
+ fn Bottom(&self) -> f32 {
+ self.bottom
+ }
+
+ fn Left(&self) -> f32 {
+ self.left
+ }
+
+ fn Right(&self) -> f32 {
+ self.right
+ }
+
+ fn Width(&self) -> f32 {
+ (self.right - self.left).abs()
+ }
+
+ fn Height(&self) -> f32 {
+ (self.bottom - self.top).abs()
+ }
+}
+
+impl Reflectable for DOMRect {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ &self.reflector_
+ }
+}
diff --git a/components/script/dom/domrectlist.rs b/components/script/dom/domrectlist.rs
new file mode 100644
index 00000000000..0c661c4a51d
--- /dev/null
+++ b/components/script/dom/domrectlist.rs
@@ -0,0 +1,62 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::DOMRectListBinding;
+use dom::bindings::codegen::Bindings::DOMRectListBinding::DOMRectListMethods;
+use dom::bindings::global::Window;
+use dom::bindings::js::{JS, JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
+use dom::domrect::DOMRect;
+use dom::window::Window;
+
+#[deriving(Encodable)]
+pub struct DOMRectList {
+ reflector_: Reflector,
+ rects: Vec<JS<DOMRect>>,
+ window: JS<Window>,
+}
+
+impl DOMRectList {
+ pub fn new_inherited(window: &JSRef<Window>,
+ rects: Vec<JSRef<DOMRect>>) -> DOMRectList {
+ let rects = rects.iter().map(|rect| JS::from_rooted(rect)).collect();
+ DOMRectList {
+ reflector_: Reflector::new(),
+ rects: rects,
+ window: JS::from_rooted(window),
+ }
+ }
+
+ pub fn new(window: &JSRef<Window>,
+ rects: Vec<JSRef<DOMRect>>) -> Temporary<DOMRectList> {
+ reflect_dom_object(box DOMRectList::new_inherited(window, rects),
+ &Window(*window), DOMRectListBinding::Wrap)
+ }
+}
+
+impl<'a> DOMRectListMethods for JSRef<'a, DOMRectList> {
+ fn Length(&self) -> u32 {
+ self.rects.len() as u32
+ }
+
+ fn Item(&self, index: u32) -> Option<Temporary<DOMRect>> {
+ let rects = &self.rects;
+ if index < rects.len() as u32 {
+ Some(Temporary::new(rects[index as uint].clone()))
+ } else {
+ None
+ }
+ }
+
+ fn IndexedGetter(&self, index: u32, found: &mut bool) -> Option<Temporary<DOMRect>> {
+ *found = index < self.rects.len() as u32;
+ self.Item(index)
+ }
+}
+
+impl Reflectable for DOMRectList {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ &self.reflector_
+ }
+}
diff --git a/components/script/dom/domtokenlist.rs b/components/script/dom/domtokenlist.rs
new file mode 100644
index 00000000000..11f7eaf59d0
--- /dev/null
+++ b/components/script/dom/domtokenlist.rs
@@ -0,0 +1,101 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::attr::Attr;
+use dom::bindings::codegen::Bindings::DOMTokenListBinding;
+use dom::bindings::codegen::Bindings::DOMTokenListBinding::DOMTokenListMethods;
+use dom::bindings::error::{Fallible, InvalidCharacter, Syntax};
+use dom::bindings::global::Window;
+use dom::bindings::js::{JS, JSRef, Temporary, OptionalRootable};
+use dom::bindings::utils::{Reflector, Reflectable, reflect_dom_object};
+use dom::element::{Element, AttributeHandlers};
+use dom::node::window_from_node;
+
+use servo_util::atom::Atom;
+use servo_util::namespace::Null;
+use servo_util::str::{DOMString, HTML_SPACE_CHARACTERS};
+
+#[deriving(Encodable)]
+pub struct DOMTokenList {
+ reflector_: Reflector,
+ element: JS<Element>,
+ local_name: &'static str,
+}
+
+impl DOMTokenList {
+ pub fn new_inherited(element: &JSRef<Element>,
+ local_name: &'static str) -> DOMTokenList {
+ DOMTokenList {
+ reflector_: Reflector::new(),
+ element: JS::from_rooted(element),
+ local_name: local_name,
+ }
+ }
+
+ pub fn new(element: &JSRef<Element>,
+ local_name: &'static str) -> Temporary<DOMTokenList> {
+ let window = window_from_node(element).root();
+ reflect_dom_object(box DOMTokenList::new_inherited(element, local_name),
+ &Window(*window), DOMTokenListBinding::Wrap)
+ }
+}
+
+impl Reflectable for DOMTokenList {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ &self.reflector_
+ }
+}
+
+trait PrivateDOMTokenListHelpers {
+ fn attribute(&self) -> Option<Temporary<Attr>>;
+ fn check_token_exceptions<'a>(&self, token: &'a str) -> Fallible<&'a str>;
+}
+
+impl<'a> PrivateDOMTokenListHelpers for JSRef<'a, DOMTokenList> {
+ fn attribute(&self) -> Option<Temporary<Attr>> {
+ let element = self.element.root();
+ element.deref().get_attribute(Null, self.local_name)
+ }
+
+ fn check_token_exceptions<'a>(&self, token: &'a str) -> Fallible<&'a str> {
+ match token {
+ "" => Err(Syntax),
+ token if token.find(HTML_SPACE_CHARACTERS).is_some() => Err(InvalidCharacter),
+ token => Ok(token)
+ }
+ }
+}
+
+// http://dom.spec.whatwg.org/#domtokenlist
+impl<'a> DOMTokenListMethods for JSRef<'a, DOMTokenList> {
+ // http://dom.spec.whatwg.org/#dom-domtokenlist-length
+ fn Length(&self) -> u32 {
+ self.attribute().root().map(|attr| {
+ attr.value().tokens().map(|tokens| tokens.len()).unwrap_or(0)
+ }).unwrap_or(0) as u32
+ }
+
+ // http://dom.spec.whatwg.org/#dom-domtokenlist-item
+ fn Item(&self, index: u32) -> Option<DOMString> {
+ self.attribute().root().and_then(|attr| attr.value().tokens().and_then(|mut tokens| {
+ tokens.idx(index as uint).map(|token| token.as_slice().to_string())
+ }))
+ }
+
+ fn IndexedGetter(&self, index: u32, found: &mut bool) -> Option<DOMString> {
+ let item = self.Item(index);
+ *found = item.is_some();
+ item
+ }
+
+ // http://dom.spec.whatwg.org/#dom-domtokenlist-contains
+ fn Contains(&self, token: DOMString) -> Fallible<bool> {
+ self.check_token_exceptions(token.as_slice()).map(|slice| {
+ self.attribute().root().and_then(|attr| attr.value().tokens().map(|mut tokens| {
+ let atom = Atom::from_slice(slice);
+ tokens.any(|token| *token == atom)
+ })).unwrap_or(false)
+ })
+ }
+}
diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs
new file mode 100644
index 00000000000..1fe2c9f80bd
--- /dev/null
+++ b/components/script/dom/element.rs
@@ -0,0 +1,958 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! Element nodes.
+
+use dom::attr::{Attr, ReplacedAttr, FirstSetAttr, AttrHelpersForLayout};
+use dom::attr::{AttrValue, StringAttrValue, UIntAttrValue, AtomAttrValue};
+use dom::namednodemap::NamedNodeMap;
+use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
+use dom::bindings::codegen::Bindings::ElementBinding;
+use dom::bindings::codegen::Bindings::ElementBinding::ElementMethods;
+use dom::bindings::codegen::InheritTypes::{ElementDerived, NodeCast};
+use dom::bindings::js::{JS, JSRef, Temporary, TemporaryPushable};
+use dom::bindings::js::{OptionalSettable, OptionalRootable, Root};
+use dom::bindings::trace::Traceable;
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::bindings::error::{ErrorResult, Fallible, NamespaceError, InvalidCharacter, Syntax};
+use dom::bindings::utils::{QName, Name, InvalidXMLName, xml_name_type};
+use dom::domrect::DOMRect;
+use dom::domrectlist::DOMRectList;
+use dom::document::{Document, DocumentHelpers};
+use dom::domtokenlist::DOMTokenList;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlcollection::HTMLCollection;
+use dom::htmlserializer::serialize;
+use dom::node::{ElementNodeTypeId, Node, NodeHelpers, NodeIterator, document_from_node};
+use dom::node::{window_from_node, LayoutNodeHelpers};
+use dom::nodelist::NodeList;
+use dom::virtualmethods::{VirtualMethods, vtable_for};
+use layout_interface::ContentChangedDocumentDamage;
+use layout_interface::MatchSelectorsDocumentDamage;
+use style::{matches, parse_selector_list_from_str};
+use style;
+use servo_util::atom::Atom;
+use servo_util::namespace;
+use servo_util::namespace::{Namespace, Null};
+use servo_util::str::{DOMString, null_str_as_empty_ref};
+
+use std::ascii::StrAsciiExt;
+use std::cell::{Cell, RefCell};
+use std::mem;
+
+#[deriving(Encodable)]
+pub struct Element {
+ pub node: Node,
+ pub local_name: Atom,
+ pub namespace: Namespace,
+ pub prefix: Option<DOMString>,
+ pub attrs: RefCell<Vec<JS<Attr>>>,
+ pub style_attribute: Traceable<RefCell<Option<style::PropertyDeclarationBlock>>>,
+ pub attr_list: Cell<Option<JS<NamedNodeMap>>>,
+ class_list: Cell<Option<JS<DOMTokenList>>>,
+}
+
+impl ElementDerived for EventTarget {
+ fn is_element(&self) -> bool {
+ match self.type_id {
+ NodeTargetTypeId(ElementNodeTypeId(_)) => true,
+ _ => false
+ }
+ }
+}
+
+impl Reflectable for Element {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.node.reflector()
+ }
+}
+
+#[deriving(PartialEq,Encodable)]
+pub enum ElementTypeId {
+ HTMLElementTypeId,
+ HTMLAnchorElementTypeId,
+ HTMLAppletElementTypeId,
+ HTMLAreaElementTypeId,
+ HTMLAudioElementTypeId,
+ HTMLBaseElementTypeId,
+ HTMLBRElementTypeId,
+ HTMLBodyElementTypeId,
+ HTMLButtonElementTypeId,
+ HTMLCanvasElementTypeId,
+ HTMLDataElementTypeId,
+ HTMLDataListElementTypeId,
+ HTMLDirectoryElementTypeId,
+ HTMLDListElementTypeId,
+ HTMLDivElementTypeId,
+ HTMLEmbedElementTypeId,
+ HTMLFieldSetElementTypeId,
+ HTMLFontElementTypeId,
+ HTMLFormElementTypeId,
+ HTMLFrameElementTypeId,
+ HTMLFrameSetElementTypeId,
+ HTMLHRElementTypeId,
+ HTMLHeadElementTypeId,
+ HTMLHeadingElementTypeId,
+ HTMLHtmlElementTypeId,
+ HTMLIFrameElementTypeId,
+ HTMLImageElementTypeId,
+ HTMLInputElementTypeId,
+ HTMLLabelElementTypeId,
+ HTMLLegendElementTypeId,
+ HTMLLinkElementTypeId,
+ HTMLLIElementTypeId,
+ HTMLMapElementTypeId,
+ HTMLMediaElementTypeId,
+ HTMLMetaElementTypeId,
+ HTMLMeterElementTypeId,
+ HTMLModElementTypeId,
+ HTMLObjectElementTypeId,
+ HTMLOListElementTypeId,
+ HTMLOptGroupElementTypeId,
+ HTMLOptionElementTypeId,
+ HTMLOutputElementTypeId,
+ HTMLParagraphElementTypeId,
+ HTMLParamElementTypeId,
+ HTMLPreElementTypeId,
+ HTMLProgressElementTypeId,
+ HTMLQuoteElementTypeId,
+ HTMLScriptElementTypeId,
+ HTMLSelectElementTypeId,
+ HTMLSourceElementTypeId,
+ HTMLSpanElementTypeId,
+ HTMLStyleElementTypeId,
+ HTMLTableElementTypeId,
+ HTMLTableCaptionElementTypeId,
+ HTMLTableDataCellElementTypeId,
+ HTMLTableHeaderCellElementTypeId,
+ HTMLTableColElementTypeId,
+ HTMLTableRowElementTypeId,
+ HTMLTableSectionElementTypeId,
+ HTMLTemplateElementTypeId,
+ HTMLTextAreaElementTypeId,
+ HTMLTimeElementTypeId,
+ HTMLTitleElementTypeId,
+ HTMLTrackElementTypeId,
+ HTMLUListElementTypeId,
+ HTMLVideoElementTypeId,
+ HTMLUnknownElementTypeId,
+
+ ElementTypeId,
+}
+
+//
+// Element methods
+//
+
+impl Element {
+ pub fn new_inherited(type_id: ElementTypeId, local_name: DOMString, namespace: Namespace, prefix: Option<DOMString>, document: &JSRef<Document>) -> Element {
+ Element {
+ node: Node::new_inherited(ElementNodeTypeId(type_id), document),
+ local_name: Atom::from_slice(local_name.as_slice()),
+ namespace: namespace,
+ prefix: prefix,
+ attrs: RefCell::new(vec!()),
+ attr_list: Cell::new(None),
+ class_list: Cell::new(None),
+ style_attribute: Traceable::new(RefCell::new(None)),
+ }
+ }
+
+ pub fn new(local_name: DOMString, namespace: Namespace, prefix: Option<DOMString>, document: &JSRef<Document>) -> Temporary<Element> {
+ let element = Element::new_inherited(ElementTypeId, local_name, namespace, prefix, document);
+ Node::reflect_node(box element, document, ElementBinding::Wrap)
+ }
+}
+
+pub trait RawLayoutElementHelpers {
+ unsafe fn get_attr_val_for_layout(&self, namespace: &Namespace, name: &str) -> Option<&'static str>;
+ unsafe fn get_attr_atom_for_layout(&self, namespace: &Namespace, name: &str) -> Option<Atom>;
+}
+
+impl RawLayoutElementHelpers for Element {
+ #[inline]
+ unsafe fn get_attr_val_for_layout(&self, namespace: &Namespace, name: &str)
+ -> Option<&'static str> {
+ // cast to point to T in RefCell<T> directly
+ let attrs: *const Vec<JS<Attr>> = mem::transmute(&self.attrs);
+ (*attrs).iter().find(|attr: & &JS<Attr>| {
+ let attr = attr.unsafe_get();
+ name == (*attr).local_name().as_slice() &&
+ (*attr).namespace == *namespace
+ }).map(|attr| {
+ let attr = attr.unsafe_get();
+ (*attr).value_ref_forever()
+ })
+ }
+
+ #[inline]
+ unsafe fn get_attr_atom_for_layout(&self, namespace: &Namespace, name: &str)
+ -> Option<Atom> {
+ // cast to point to T in RefCell<T> directly
+ let attrs: *const Vec<JS<Attr>> = mem::transmute(&self.attrs);
+ (*attrs).iter().find(|attr: & &JS<Attr>| {
+ let attr = attr.unsafe_get();
+ name == (*attr).local_name().as_slice() &&
+ (*attr).namespace == *namespace
+ }).and_then(|attr| {
+ let attr = attr.unsafe_get();
+ (*attr).value_atom_forever()
+ })
+ }
+}
+
+pub trait LayoutElementHelpers {
+ unsafe fn html_element_in_html_document_for_layout(&self) -> bool;
+}
+
+impl LayoutElementHelpers for JS<Element> {
+ unsafe fn html_element_in_html_document_for_layout(&self) -> bool {
+ if (*self.unsafe_get()).namespace != namespace::HTML {
+ return false
+ }
+ let node: JS<Node> = self.transmute_copy();
+ let owner_doc = node.owner_doc_for_layout().unsafe_get();
+ (*owner_doc).is_html_document
+ }
+}
+
+pub trait ElementHelpers {
+ fn html_element_in_html_document(&self) -> bool;
+ fn get_local_name<'a>(&'a self) -> &'a Atom;
+ fn get_namespace<'a>(&'a self) -> &'a Namespace;
+}
+
+impl<'a> ElementHelpers for JSRef<'a, Element> {
+ fn html_element_in_html_document(&self) -> bool {
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ self.namespace == namespace::HTML && node.is_in_html_doc()
+ }
+
+ fn get_local_name<'a>(&'a self) -> &'a Atom {
+ &self.deref().local_name
+ }
+
+ fn get_namespace<'a>(&'a self) -> &'a Namespace {
+ &self.deref().namespace
+ }
+}
+
+pub trait AttributeHandlers {
+ fn get_attribute(&self, namespace: Namespace, name: &str) -> Option<Temporary<Attr>>;
+ fn set_attribute_from_parser(&self, local_name: Atom,
+ value: DOMString, namespace: Namespace,
+ prefix: Option<DOMString>);
+ fn set_attribute(&self, name: &str, value: AttrValue);
+ fn do_set_attribute(&self, local_name: Atom, value: AttrValue,
+ name: Atom, namespace: Namespace,
+ prefix: Option<DOMString>, cb: |&JSRef<Attr>| -> bool);
+ fn parse_attribute(&self, namespace: &Namespace, local_name: &Atom,
+ value: DOMString) -> AttrValue;
+
+ fn remove_attribute(&self, namespace: Namespace, name: &str);
+ fn notify_attribute_changed(&self, local_name: &Atom);
+ fn has_class(&self, name: &str) -> bool;
+
+ fn set_atomic_attribute(&self, name: &str, value: DOMString);
+
+ // http://www.whatwg.org/html/#reflecting-content-attributes-in-idl-attributes
+ fn has_attribute(&self, name: &str) -> bool;
+ fn set_bool_attribute(&self, name: &str, value: bool);
+ fn get_url_attribute(&self, name: &str) -> DOMString;
+ fn set_url_attribute(&self, name: &str, value: DOMString);
+ fn get_string_attribute(&self, name: &str) -> DOMString;
+ fn set_string_attribute(&self, name: &str, value: DOMString);
+ fn set_tokenlist_attribute(&self, name: &str, value: DOMString);
+ fn get_uint_attribute(&self, name: &str) -> u32;
+ fn set_uint_attribute(&self, name: &str, value: u32);
+}
+
+impl<'a> AttributeHandlers for JSRef<'a, Element> {
+ fn get_attribute(&self, namespace: Namespace, name: &str) -> Option<Temporary<Attr>> {
+ let element: &Element = self.deref();
+ let local_name = match self.html_element_in_html_document() {
+ true => Atom::from_slice(name.to_ascii_lower().as_slice()),
+ false => Atom::from_slice(name)
+ };
+ element.attrs.borrow().iter().map(|attr| attr.root()).find(|attr| {
+ *attr.local_name() == local_name && attr.namespace == namespace
+ }).map(|x| Temporary::from_rooted(&*x))
+ }
+
+ fn set_attribute_from_parser(&self, local_name: Atom,
+ value: DOMString, namespace: Namespace,
+ prefix: Option<DOMString>) {
+ let name = match prefix {
+ None => local_name.clone(),
+ Some(ref prefix) => {
+ let name = format!("{:s}:{:s}", *prefix, local_name.as_slice());
+ Atom::from_slice(name.as_slice())
+ },
+ };
+ let value = self.parse_attribute(&namespace, &local_name, value);
+ self.do_set_attribute(local_name, value, name, namespace, prefix, |_| false)
+ }
+
+ fn set_attribute(&self, name: &str, value: AttrValue) {
+ assert!(name == name.to_ascii_lower().as_slice());
+ assert!(!name.contains(":"));
+
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ node.wait_until_safe_to_modify_dom();
+
+ let name = Atom::from_slice(name);
+ self.do_set_attribute(name.clone(), value, name.clone(),
+ namespace::Null, None, |attr| *attr.local_name() == name);
+ }
+
+ fn do_set_attribute(&self, local_name: Atom, value: AttrValue,
+ name: Atom, namespace: Namespace,
+ prefix: Option<DOMString>, cb: |&JSRef<Attr>| -> bool) {
+ let idx = self.deref().attrs.borrow().iter()
+ .map(|attr| attr.root())
+ .position(|attr| cb(&*attr));
+ let (idx, set_type) = match idx {
+ Some(idx) => (idx, ReplacedAttr),
+ None => {
+ let window = window_from_node(self).root();
+ let attr = Attr::new(&*window, local_name, value.clone(),
+ name, namespace.clone(), prefix, self);
+ self.deref().attrs.borrow_mut().push_unrooted(&attr);
+ (self.deref().attrs.borrow().len() - 1, FirstSetAttr)
+ }
+ };
+
+ (*self.deref().attrs.borrow())[idx].root().set_value(set_type, value);
+ }
+
+ fn parse_attribute(&self, namespace: &Namespace, local_name: &Atom,
+ value: DOMString) -> AttrValue {
+ if *namespace == namespace::Null {
+ vtable_for(NodeCast::from_ref(self))
+ .parse_plain_attribute(local_name.as_slice(), value)
+ } else {
+ StringAttrValue(value)
+ }
+ }
+
+ fn remove_attribute(&self, namespace: Namespace, name: &str) {
+ let (_, local_name) = get_attribute_parts(name);
+ let local_name = Atom::from_slice(local_name);
+
+ let idx = self.deref().attrs.borrow().iter().map(|attr| attr.root()).position(|attr| {
+ *attr.local_name() == local_name
+ });
+
+ match idx {
+ None => (),
+ Some(idx) => {
+ {
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ node.wait_until_safe_to_modify_dom();
+ }
+
+ if namespace == namespace::Null {
+ let removed_raw_value = (*self.deref().attrs.borrow())[idx].root().Value();
+ vtable_for(NodeCast::from_ref(self))
+ .before_remove_attr(&local_name,
+ removed_raw_value);
+ }
+
+ self.deref().attrs.borrow_mut().remove(idx);
+ }
+ };
+ }
+
+ fn notify_attribute_changed(&self, local_name: &Atom) {
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ if node.is_in_doc() {
+ let damage = match local_name.as_slice() {
+ "style" | "id" | "class" => MatchSelectorsDocumentDamage,
+ _ => ContentChangedDocumentDamage
+ };
+ let document = node.owner_doc().root();
+ document.deref().damage_and_reflow(damage);
+ }
+ }
+
+ fn has_class(&self, name: &str) -> bool {
+ self.get_attribute(Null, "class").root().map(|attr| {
+ attr.deref().value().tokens().map(|mut tokens| {
+ tokens.any(|atom| atom.as_slice() == name)
+ }).unwrap_or(false)
+ }).unwrap_or(false)
+ }
+
+ fn set_atomic_attribute(&self, name: &str, value: DOMString) {
+ assert!(name == name.to_ascii_lower().as_slice());
+ let value = AttrValue::from_atomic(value);
+ self.set_attribute(name, value);
+ }
+
+ fn has_attribute(&self, name: &str) -> bool {
+ let name = match self.html_element_in_html_document() {
+ true => Atom::from_slice(name.to_ascii_lower().as_slice()),
+ false => Atom::from_slice(name)
+ };
+ self.deref().attrs.borrow().iter().map(|attr| attr.root()).any(|attr| {
+ *attr.local_name() == name && attr.namespace == Null
+ })
+ }
+
+ fn set_bool_attribute(&self, name: &str, value: bool) {
+ if self.has_attribute(name) == value { return; }
+ if value {
+ self.set_string_attribute(name, String::new());
+ } else {
+ self.remove_attribute(Null, name);
+ }
+ }
+
+ fn get_url_attribute(&self, name: &str) -> DOMString {
+ // XXX Resolve URL.
+ self.get_string_attribute(name)
+ }
+ fn set_url_attribute(&self, name: &str, value: DOMString) {
+ self.set_string_attribute(name, value);
+ }
+
+ fn get_string_attribute(&self, name: &str) -> DOMString {
+ match self.get_attribute(Null, name) {
+ Some(x) => {
+ let x = x.root();
+ x.deref().Value()
+ }
+ None => "".to_string()
+ }
+ }
+ fn set_string_attribute(&self, name: &str, value: DOMString) {
+ assert!(name == name.to_ascii_lower().as_slice());
+ self.set_attribute(name, StringAttrValue(value));
+ }
+
+ fn set_tokenlist_attribute(&self, name: &str, value: DOMString) {
+ assert!(name == name.to_ascii_lower().as_slice());
+ self.set_attribute(name, AttrValue::from_tokenlist(value));
+ }
+
+ fn get_uint_attribute(&self, name: &str) -> u32 {
+ assert!(name == name.to_ascii_lower().as_slice());
+ let attribute = self.get_attribute(Null, name).root();
+ match attribute {
+ Some(attribute) => {
+ match *attribute.deref().value() {
+ UIntAttrValue(_, value) => value,
+ _ => fail!("Expected a UIntAttrValue"),
+ }
+ }
+ None => 0,
+ }
+ }
+ fn set_uint_attribute(&self, name: &str, value: u32) {
+ assert!(name == name.to_ascii_lower().as_slice());
+ self.set_attribute(name, UIntAttrValue(value.to_string(), value));
+ }
+}
+
+impl Element {
+ pub fn is_void(&self) -> bool {
+ if self.namespace != namespace::HTML {
+ return false
+ }
+ match self.local_name.as_slice() {
+ /* List of void elements from
+ http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#html-fragment-serialization-algorithm */
+ "area" | "base" | "basefont" | "bgsound" | "br" | "col" | "embed" |
+ "frame" | "hr" | "img" | "input" | "keygen" | "link" | "menuitem" |
+ "meta" | "param" | "source" | "track" | "wbr" => true,
+ _ => false
+ }
+ }
+}
+
+impl<'a> ElementMethods for JSRef<'a, Element> {
+ // http://dom.spec.whatwg.org/#dom-element-namespaceuri
+ fn GetNamespaceURI(&self) -> Option<DOMString> {
+ match self.namespace {
+ Null => None,
+ ref ns => Some(ns.to_str().to_string())
+ }
+ }
+
+ fn LocalName(&self) -> DOMString {
+ self.local_name.as_slice().to_string()
+ }
+
+ // http://dom.spec.whatwg.org/#dom-element-prefix
+ fn GetPrefix(&self) -> Option<DOMString> {
+ self.prefix.clone()
+ }
+
+ // http://dom.spec.whatwg.org/#dom-element-tagname
+ fn TagName(&self) -> DOMString {
+ let qualified_name = match self.prefix {
+ Some(ref prefix) => format!("{}:{}", prefix, self.local_name).into_maybe_owned(),
+ None => self.local_name.as_slice().into_maybe_owned()
+ };
+ if self.html_element_in_html_document() {
+ qualified_name.as_slice().to_ascii_upper()
+ } else {
+ qualified_name.into_string()
+ }
+ }
+
+ // http://dom.spec.whatwg.org/#dom-element-id
+ fn Id(&self) -> DOMString {
+ self.get_string_attribute("id")
+ }
+
+ // http://dom.spec.whatwg.org/#dom-element-id
+ fn SetId(&self, id: DOMString) {
+ self.set_atomic_attribute("id", id);
+ }
+
+ // http://dom.spec.whatwg.org/#dom-element-classname
+ fn ClassName(&self) -> DOMString {
+ self.get_string_attribute("class")
+ }
+
+ // http://dom.spec.whatwg.org/#dom-element-classname
+ fn SetClassName(&self, class: DOMString) {
+ self.set_tokenlist_attribute("class", class);
+ }
+
+ // http://dom.spec.whatwg.org/#dom-element-classlist
+ fn ClassList(&self) -> Temporary<DOMTokenList> {
+ match self.class_list.get() {
+ Some(class_list) => Temporary::new(class_list),
+ None => {
+ let class_list = DOMTokenList::new(self, "class").root();
+ self.class_list.assign(Some(class_list.deref().clone()));
+ Temporary::from_rooted(&*class_list)
+ }
+ }
+ }
+
+ // http://dom.spec.whatwg.org/#dom-element-attributes
+ fn Attributes(&self) -> Temporary<NamedNodeMap> {
+ match self.attr_list.get() {
+ None => (),
+ Some(ref list) => return Temporary::new(list.clone()),
+ }
+
+ let doc = {
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ node.owner_doc().root()
+ };
+ let window = doc.deref().window.root();
+ let list = NamedNodeMap::new(&*window, self);
+ self.attr_list.assign(Some(list));
+ Temporary::new(self.attr_list.get().get_ref().clone())
+ }
+
+ // http://dom.spec.whatwg.org/#dom-element-getattribute
+ fn GetAttribute(&self, name: DOMString) -> Option<DOMString> {
+ let name = if self.html_element_in_html_document() {
+ name.as_slice().to_ascii_lower()
+ } else {
+ name
+ };
+ self.get_attribute(Null, name.as_slice()).root()
+ .map(|s| s.deref().Value())
+ }
+
+ // http://dom.spec.whatwg.org/#dom-element-getattributens
+ fn GetAttributeNS(&self,
+ namespace: Option<DOMString>,
+ local_name: DOMString) -> Option<DOMString> {
+ let namespace = Namespace::from_str(null_str_as_empty_ref(&namespace));
+ self.get_attribute(namespace, local_name.as_slice()).root()
+ .map(|attr| attr.deref().Value())
+ }
+
+ // http://dom.spec.whatwg.org/#dom-element-setattribute
+ fn SetAttribute(&self,
+ name: DOMString,
+ value: DOMString) -> ErrorResult {
+ {
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ node.wait_until_safe_to_modify_dom();
+ }
+
+ // Step 1.
+ match xml_name_type(name.as_slice()) {
+ InvalidXMLName => return Err(InvalidCharacter),
+ _ => {}
+ }
+
+ // Step 2.
+ let name = if self.html_element_in_html_document() {
+ name.as_slice().to_ascii_lower()
+ } else {
+ name
+ };
+
+ // Step 3-5.
+ let name = Atom::from_slice(name.as_slice());
+ let value = self.parse_attribute(&namespace::Null, &name, value);
+ self.do_set_attribute(name.clone(), value, name.clone(), namespace::Null, None, |attr| {
+ attr.deref().name.as_slice() == name.as_slice()
+ });
+ Ok(())
+ }
+
+ // http://dom.spec.whatwg.org/#dom-element-setattributens
+ fn SetAttributeNS(&self,
+ namespace_url: Option<DOMString>,
+ name: DOMString,
+ value: DOMString) -> ErrorResult {
+ {
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ node.wait_until_safe_to_modify_dom();
+ }
+
+ // Step 1.
+ let namespace = Namespace::from_str(null_str_as_empty_ref(&namespace_url));
+
+ let name_type = xml_name_type(name.as_slice());
+ match name_type {
+ // Step 2.
+ InvalidXMLName => return Err(InvalidCharacter),
+ // Step 3.
+ Name => return Err(NamespaceError),
+ QName => {}
+ }
+
+ // Step 4.
+ let (prefix, local_name) = get_attribute_parts(name.as_slice());
+ match prefix {
+ Some(ref prefix_str) => {
+ // Step 5.
+ if namespace == namespace::Null {
+ return Err(NamespaceError);
+ }
+
+ // Step 6.
+ if "xml" == prefix_str.as_slice() && namespace != namespace::XML {
+ return Err(NamespaceError);
+ }
+
+ // Step 7b.
+ if "xmlns" == prefix_str.as_slice() && namespace != namespace::XMLNS {
+ return Err(NamespaceError);
+ }
+ },
+ None => {}
+ }
+
+ let name = Atom::from_slice(name.as_slice());
+ let local_name = Atom::from_slice(local_name);
+ let xmlns = Atom::from_slice("xmlns"); // TODO: Make this a static atom type
+
+ // Step 7a.
+ if xmlns == name && namespace != namespace::XMLNS {
+ return Err(NamespaceError);
+ }
+
+ // Step 8.
+ if namespace == namespace::XMLNS && xmlns != name && Some("xmlns") != prefix {
+ return Err(NamespaceError);
+ }
+
+ // Step 9.
+ let value = self.parse_attribute(&namespace, &local_name, value);
+ self.do_set_attribute(local_name.clone(), value, name,
+ namespace.clone(), prefix.map(|s| s.to_string()),
+ |attr| {
+ *attr.local_name() == local_name &&
+ attr.namespace == namespace
+ });
+ Ok(())
+ }
+
+ // http://dom.spec.whatwg.org/#dom-element-removeattribute
+ fn RemoveAttribute(&self, name: DOMString) {
+ let name = if self.html_element_in_html_document() {
+ name.as_slice().to_ascii_lower()
+ } else {
+ name
+ };
+ self.remove_attribute(namespace::Null, name.as_slice())
+ }
+
+ // http://dom.spec.whatwg.org/#dom-element-removeattributens
+ fn RemoveAttributeNS(&self,
+ namespace: Option<DOMString>,
+ localname: DOMString) {
+ let namespace = Namespace::from_str(null_str_as_empty_ref(&namespace));
+ self.remove_attribute(namespace, localname.as_slice())
+ }
+
+ // http://dom.spec.whatwg.org/#dom-element-hasattribute
+ fn HasAttribute(&self,
+ name: DOMString) -> bool {
+ self.has_attribute(name.as_slice())
+ }
+
+ // http://dom.spec.whatwg.org/#dom-element-hasattributens
+ fn HasAttributeNS(&self,
+ namespace: Option<DOMString>,
+ local_name: DOMString) -> bool {
+ self.GetAttributeNS(namespace, local_name).is_some()
+ }
+
+ fn GetElementsByTagName(&self, localname: DOMString) -> Temporary<HTMLCollection> {
+ let window = window_from_node(self).root();
+ HTMLCollection::by_tag_name(&*window, NodeCast::from_ref(self), localname)
+ }
+
+ fn GetElementsByTagNameNS(&self, maybe_ns: Option<DOMString>,
+ localname: DOMString) -> Temporary<HTMLCollection> {
+ let window = window_from_node(self).root();
+ HTMLCollection::by_tag_name_ns(&*window, NodeCast::from_ref(self), localname, maybe_ns)
+ }
+
+ fn GetElementsByClassName(&self, classes: DOMString) -> Temporary<HTMLCollection> {
+ let window = window_from_node(self).root();
+ HTMLCollection::by_class_name(&*window, NodeCast::from_ref(self), classes)
+ }
+
+ // http://dev.w3.org/csswg/cssom-view/#dom-element-getclientrects
+ fn GetClientRects(&self) -> Temporary<DOMRectList> {
+ let win = window_from_node(self).root();
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ let rects = node.get_content_boxes();
+ let rects: Vec<Root<DOMRect>> = rects.iter().map(|r| {
+ DOMRect::new(
+ &*win,
+ r.origin.y,
+ r.origin.y + r.size.height,
+ r.origin.x,
+ r.origin.x + r.size.width).root()
+ }).collect();
+
+ DOMRectList::new(&*win, rects.iter().map(|rect| rect.deref().clone()).collect())
+ }
+
+ // http://dev.w3.org/csswg/cssom-view/#dom-element-getboundingclientrect
+ fn GetBoundingClientRect(&self) -> Temporary<DOMRect> {
+ let win = window_from_node(self).root();
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ let rect = node.get_bounding_content_box();
+ DOMRect::new(
+ &*win,
+ rect.origin.y,
+ rect.origin.y + rect.size.height,
+ rect.origin.x,
+ rect.origin.x + rect.size.width)
+ }
+
+ fn GetInnerHTML(&self) -> Fallible<DOMString> {
+ //XXX TODO: XML case
+ Ok(serialize(&mut NodeIterator::new(NodeCast::from_ref(self), false, false)))
+ }
+
+ fn GetOuterHTML(&self) -> Fallible<DOMString> {
+ Ok(serialize(&mut NodeIterator::new(NodeCast::from_ref(self), true, false)))
+ }
+
+ // http://dom.spec.whatwg.org/#dom-parentnode-children
+ fn Children(&self) -> Temporary<HTMLCollection> {
+ let window = window_from_node(self).root();
+ HTMLCollection::children(&*window, NodeCast::from_ref(self))
+ }
+
+ // http://dom.spec.whatwg.org/#dom-parentnode-queryselector
+ fn QuerySelector(&self, selectors: DOMString) -> Fallible<Option<Temporary<Element>>> {
+ let root: &JSRef<Node> = NodeCast::from_ref(self);
+ root.query_selector(selectors)
+ }
+
+ // http://dom.spec.whatwg.org/#dom-parentnode-queryselectorall
+ fn QuerySelectorAll(&self, selectors: DOMString) -> Fallible<Temporary<NodeList>> {
+ let root: &JSRef<Node> = NodeCast::from_ref(self);
+ root.query_selector_all(selectors)
+ }
+
+ // http://dom.spec.whatwg.org/#dom-childnode-remove
+ fn Remove(&self) {
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ node.remove_self();
+ }
+
+ // http://dom.spec.whatwg.org/#dom-element-matches
+ fn Matches(&self, selectors: DOMString) -> Fallible<bool> {
+ match parse_selector_list_from_str(selectors.as_slice()) {
+ Err(()) => Err(Syntax),
+ Ok(ref selectors) => {
+ let root: &JSRef<Node> = NodeCast::from_ref(self);
+ Ok(matches(selectors, root))
+ }
+ }
+ }
+}
+
+pub fn get_attribute_parts<'a>(name: &'a str) -> (Option<&'a str>, &'a str) {
+ //FIXME: Throw for XML-invalid names
+ //FIXME: Throw for XMLNS-invalid names
+ let (prefix, local_name) = if name.contains(":") {
+ let mut parts = name.splitn(':', 1);
+ (Some(parts.next().unwrap()), parts.next().unwrap())
+ } else {
+ (None, name)
+ };
+
+ (prefix, local_name)
+}
+
+impl<'a> VirtualMethods for JSRef<'a, Element> {
+ fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods> {
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ Some(node as &VirtualMethods)
+ }
+
+ fn after_set_attr(&self, name: &Atom, value: DOMString) {
+ match self.super_type() {
+ Some(ref s) => s.after_set_attr(name, value.clone()),
+ _ => (),
+ }
+
+ match name.as_slice() {
+ "style" => {
+ let doc = document_from_node(self).root();
+ let base_url = doc.deref().url().clone();
+ let style = Some(style::parse_style_attribute(value.as_slice(), &base_url));
+ *self.deref().style_attribute.deref().borrow_mut() = style;
+ }
+ "id" => {
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ if node.is_in_doc() && !value.is_empty() {
+ let doc = document_from_node(self).root();
+ doc.register_named_element(self, value.clone());
+ }
+ }
+ _ => ()
+ }
+
+ self.notify_attribute_changed(name);
+ }
+
+ fn before_remove_attr(&self, name: &Atom, value: DOMString) {
+ match self.super_type() {
+ Some(ref s) => s.before_remove_attr(name, value.clone()),
+ _ => (),
+ }
+
+ match name.as_slice() {
+ "style" => {
+ *self.deref().style_attribute.deref().borrow_mut() = None;
+ }
+ "id" => {
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ if node.is_in_doc() && !value.is_empty() {
+ let doc = document_from_node(self).root();
+ doc.unregister_named_element(self, value);
+ }
+ }
+ _ => ()
+ }
+
+ self.notify_attribute_changed(name);
+ }
+
+ fn parse_plain_attribute(&self, name: &str, value: DOMString) -> AttrValue {
+ match name {
+ "id" => AttrValue::from_atomic(value),
+ "class" => AttrValue::from_tokenlist(value),
+ _ => self.super_type().unwrap().parse_plain_attribute(name, value),
+ }
+ }
+
+ fn bind_to_tree(&self, tree_in_doc: bool) {
+ match self.super_type() {
+ Some(ref s) => s.bind_to_tree(tree_in_doc),
+ _ => (),
+ }
+
+ if !tree_in_doc { return; }
+
+ match self.get_attribute(Null, "id").root() {
+ Some(attr) => {
+ let doc = document_from_node(self).root();
+ let value = attr.deref().Value();
+ if !value.is_empty() {
+ doc.deref().register_named_element(self, value);
+ }
+ }
+ _ => ()
+ }
+ }
+
+ fn unbind_from_tree(&self, tree_in_doc: bool) {
+ match self.super_type() {
+ Some(ref s) => s.unbind_from_tree(tree_in_doc),
+ _ => (),
+ }
+
+ if !tree_in_doc { return; }
+
+ match self.get_attribute(Null, "id").root() {
+ Some(attr) => {
+ let doc = document_from_node(self).root();
+ let value = attr.deref().Value();
+ if !value.is_empty() {
+ doc.deref().unregister_named_element(self, value);
+ }
+ }
+ _ => ()
+ }
+ }
+}
+
+impl<'a> style::TElement for JSRef<'a, Element> {
+ fn get_attr(&self, namespace: &Namespace, attr: &str) -> Option<&'static str> {
+ self.get_attribute(namespace.clone(), attr).root().map(|attr| {
+ unsafe { mem::transmute(attr.deref().value().as_slice()) }
+ })
+ }
+ fn get_link(&self) -> Option<&'static str> {
+ // FIXME: This is HTML only.
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ match node.type_id() {
+ // http://www.whatwg.org/specs/web-apps/current-work/multipage/selectors.html#
+ // selector-link
+ ElementNodeTypeId(HTMLAnchorElementTypeId) |
+ ElementNodeTypeId(HTMLAreaElementTypeId) |
+ ElementNodeTypeId(HTMLLinkElementTypeId) => self.get_attr(&namespace::Null, "href"),
+ _ => None,
+ }
+ }
+ fn get_local_name<'a>(&'a self) -> &'a Atom {
+ (self as &ElementHelpers).get_local_name()
+ }
+ fn get_namespace<'a>(&'a self) -> &'a Namespace {
+ (self as &ElementHelpers).get_namespace()
+ }
+ fn get_hover_state(&self) -> bool {
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ node.get_hover_state()
+ }
+ fn get_id<'a>(&self) -> Option<Atom> {
+ self.get_attribute(namespace::Null, "id").map(|attr| {
+ let attr = attr.root();
+ match *attr.value() {
+ AtomAttrValue(ref val) => val.clone(),
+ _ => fail!("`id` attribute should be AtomAttrValue"),
+ }
+ })
+ }
+ fn get_disabled_state(&self) -> bool {
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ node.get_disabled_state()
+ }
+ fn get_enabled_state(&self) -> bool {
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ node.get_enabled_state()
+ }
+}
diff --git a/components/script/dom/event.rs b/components/script/dom/event.rs
new file mode 100644
index 00000000000..15709ad5bc1
--- /dev/null
+++ b/components/script/dom/event.rs
@@ -0,0 +1,174 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::EventBinding;
+use dom::bindings::codegen::Bindings::EventBinding::{EventConstants, EventMethods};
+use dom::bindings::error::Fallible;
+use dom::bindings::global::GlobalRef;
+use dom::bindings::js::{JS, JSRef, Temporary};
+use dom::bindings::trace::Traceable;
+use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
+use dom::eventtarget::EventTarget;
+use servo_util::str::DOMString;
+use std::cell::{Cell, RefCell};
+
+use time;
+
+#[deriving(Encodable)]
+pub enum EventPhase {
+ PhaseNone = EventConstants::NONE as int,
+ PhaseCapturing = EventConstants::CAPTURING_PHASE as int,
+ PhaseAtTarget = EventConstants::AT_TARGET as int,
+ PhaseBubbling = EventConstants::BUBBLING_PHASE as int,
+}
+
+#[deriving(PartialEq, Encodable)]
+pub enum EventTypeId {
+ CustomEventTypeId,
+ HTMLEventTypeId,
+ KeyEventTypeId,
+ MessageEventTypeId,
+ MouseEventTypeId,
+ ProgressEventTypeId,
+ UIEventTypeId
+}
+
+#[deriving(Encodable)]
+pub struct Event {
+ pub type_id: EventTypeId,
+ reflector_: Reflector,
+ pub current_target: Cell<Option<JS<EventTarget>>>,
+ pub target: Cell<Option<JS<EventTarget>>>,
+ type_: Traceable<RefCell<DOMString>>,
+ pub phase: Traceable<Cell<EventPhase>>,
+ pub canceled: Traceable<Cell<bool>>,
+ pub stop_propagation: Traceable<Cell<bool>>,
+ pub stop_immediate: Traceable<Cell<bool>>,
+ pub cancelable: Traceable<Cell<bool>>,
+ pub bubbles: Traceable<Cell<bool>>,
+ pub trusted: Traceable<Cell<bool>>,
+ pub dispatching: Traceable<Cell<bool>>,
+ pub initialized: Traceable<Cell<bool>>,
+ timestamp: u64,
+}
+
+impl Event {
+ pub fn new_inherited(type_id: EventTypeId) -> Event {
+ Event {
+ type_id: type_id,
+ reflector_: Reflector::new(),
+ current_target: Cell::new(None),
+ target: Cell::new(None),
+ phase: Traceable::new(Cell::new(PhaseNone)),
+ type_: Traceable::new(RefCell::new("".to_string())),
+ canceled: Traceable::new(Cell::new(false)),
+ cancelable: Traceable::new(Cell::new(true)),
+ bubbles: Traceable::new(Cell::new(false)),
+ trusted: Traceable::new(Cell::new(false)),
+ dispatching: Traceable::new(Cell::new(false)),
+ stop_propagation: Traceable::new(Cell::new(false)),
+ stop_immediate: Traceable::new(Cell::new(false)),
+ initialized: Traceable::new(Cell::new(false)),
+ timestamp: time::get_time().sec as u64,
+ }
+ }
+
+ pub fn new_uninitialized(global: &GlobalRef) -> Temporary<Event> {
+ reflect_dom_object(box Event::new_inherited(HTMLEventTypeId),
+ global,
+ EventBinding::Wrap)
+ }
+
+ pub fn new(global: &GlobalRef,
+ type_: DOMString,
+ can_bubble: bool,
+ cancelable: bool) -> Temporary<Event> {
+ let event = Event::new_uninitialized(global).root();
+ event.deref().InitEvent(type_, can_bubble, cancelable);
+ Temporary::from_rooted(&*event)
+ }
+
+ pub fn Constructor(global: &GlobalRef,
+ type_: DOMString,
+ init: &EventBinding::EventInit) -> Fallible<Temporary<Event>> {
+ Ok(Event::new(global, type_, init.bubbles, init.cancelable))
+ }
+}
+
+impl<'a> EventMethods for JSRef<'a, Event> {
+ fn EventPhase(&self) -> u16 {
+ self.phase.deref().get() as u16
+ }
+
+ fn Type(&self) -> DOMString {
+ self.type_.deref().borrow().clone()
+ }
+
+ fn GetTarget(&self) -> Option<Temporary<EventTarget>> {
+ self.target.get().as_ref().map(|target| Temporary::new(target.clone()))
+ }
+
+ fn GetCurrentTarget(&self) -> Option<Temporary<EventTarget>> {
+ self.current_target.get().as_ref().map(|target| Temporary::new(target.clone()))
+ }
+
+ fn DefaultPrevented(&self) -> bool {
+ self.canceled.deref().get()
+ }
+
+ fn PreventDefault(&self) {
+ if self.cancelable.deref().get() {
+ self.canceled.deref().set(true)
+ }
+ }
+
+ fn StopPropagation(&self) {
+ self.stop_propagation.deref().set(true);
+ }
+
+ fn StopImmediatePropagation(&self) {
+ self.stop_immediate.deref().set(true);
+ self.stop_propagation.deref().set(true);
+ }
+
+ fn Bubbles(&self) -> bool {
+ self.bubbles.deref().get()
+ }
+
+ fn Cancelable(&self) -> bool {
+ self.cancelable.deref().get()
+ }
+
+ fn TimeStamp(&self) -> u64 {
+ self.timestamp
+ }
+
+ fn InitEvent(&self,
+ type_: DOMString,
+ bubbles: bool,
+ cancelable: bool) {
+ self.initialized.deref().set(true);
+ if self.dispatching.deref().get() {
+ return;
+ }
+ self.stop_propagation.deref().set(false);
+ self.stop_immediate.deref().set(false);
+ self.canceled.deref().set(false);
+ self.trusted.deref().set(false);
+ self.target.set(None);
+ *self.type_.deref().borrow_mut() = type_;
+ self.bubbles.deref().set(bubbles);
+ self.cancelable.deref().set(cancelable);
+ }
+
+ fn IsTrusted(&self) -> bool {
+ self.trusted.deref().get()
+ }
+}
+
+impl Reflectable for Event {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ &self.reflector_
+ }
+}
diff --git a/components/script/dom/eventdispatcher.rs b/components/script/dom/eventdispatcher.rs
new file mode 100644
index 00000000000..f0648c13ce0
--- /dev/null
+++ b/components/script/dom/eventdispatcher.rs
@@ -0,0 +1,139 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::callback::ReportExceptions;
+use dom::bindings::codegen::Bindings::EventBinding::EventMethods;
+use dom::bindings::codegen::InheritTypes::{EventTargetCast, NodeCast, NodeDerived};
+use dom::bindings::js::{JS, JSRef, OptionalSettable, OptionalRootable, Root};
+use dom::eventtarget::{Capturing, Bubbling, EventTarget};
+use dom::event::{Event, PhaseAtTarget, PhaseNone, PhaseBubbling, PhaseCapturing};
+use dom::node::{Node, NodeHelpers};
+use dom::virtualmethods::vtable_for;
+
+// See http://dom.spec.whatwg.org/#concept-event-dispatch for the full dispatch algorithm
+pub fn dispatch_event<'a, 'b>(target: &JSRef<'a, EventTarget>,
+ pseudo_target: Option<JSRef<'b, EventTarget>>,
+ event: &JSRef<Event>) -> bool {
+ assert!(!event.deref().dispatching.deref().get());
+
+ event.target.assign(Some(match pseudo_target {
+ Some(pseudo_target) => pseudo_target,
+ None => target.clone(),
+ }));
+ event.dispatching.deref().set(true);
+
+ let type_ = event.Type();
+
+ //TODO: no chain if not participating in a tree
+ let mut chain: Vec<Root<EventTarget>> = if target.deref().is_node() {
+ let target_node: &JSRef<Node> = NodeCast::to_ref(target).unwrap();
+ target_node.ancestors().map(|ancestor| {
+ let ancestor_target: &JSRef<EventTarget> = EventTargetCast::from_ref(&ancestor);
+ JS::from_rooted(ancestor_target).root()
+ }).collect()
+ } else {
+ vec!()
+ };
+
+ event.deref().phase.deref().set(PhaseCapturing);
+
+ //FIXME: The "callback this value" should be currentTarget
+
+ /* capturing */
+ for cur_target in chain.as_slice().iter().rev() {
+ let stopped = match cur_target.get_listeners_for(type_.as_slice(), Capturing) {
+ Some(listeners) => {
+ event.current_target.assign(Some(cur_target.deref().clone()));
+ for listener in listeners.iter() {
+ // Explicitly drop any exception on the floor.
+ let _ = listener.HandleEvent_(&**cur_target, event, ReportExceptions);
+
+ if event.deref().stop_immediate.deref().get() {
+ break;
+ }
+ }
+
+ event.deref().stop_propagation.deref().get()
+ }
+ None => false
+ };
+
+ if stopped {
+ break;
+ }
+ }
+
+ /* at target */
+ if !event.deref().stop_propagation.deref().get() {
+ event.phase.deref().set(PhaseAtTarget);
+ event.current_target.assign(Some(target.clone()));
+
+ let opt_listeners = target.deref().get_listeners(type_.as_slice());
+ for listeners in opt_listeners.iter() {
+ for listener in listeners.iter() {
+ // Explicitly drop any exception on the floor.
+ let _ = listener.HandleEvent_(target, event, ReportExceptions);
+
+ if event.deref().stop_immediate.deref().get() {
+ break;
+ }
+ }
+ }
+ }
+
+ /* bubbling */
+ if event.deref().bubbles.deref().get() && !event.deref().stop_propagation.deref().get() {
+ event.deref().phase.deref().set(PhaseBubbling);
+
+ for cur_target in chain.iter() {
+ let stopped = match cur_target.deref().get_listeners_for(type_.as_slice(), Bubbling) {
+ Some(listeners) => {
+ event.deref().current_target.assign(Some(cur_target.deref().clone()));
+ for listener in listeners.iter() {
+ // Explicitly drop any exception on the floor.
+ let _ = listener.HandleEvent_(&**cur_target, event, ReportExceptions);
+
+ if event.deref().stop_immediate.deref().get() {
+ break;
+ }
+ }
+
+ event.deref().stop_propagation.deref().get()
+ }
+ None => false
+ };
+ if stopped {
+ break;
+ }
+ }
+ }
+
+ /* default action */
+ let target = event.GetTarget().root();
+ match target {
+ Some(target) => {
+ let node: Option<&JSRef<Node>> = NodeCast::to_ref(&*target);
+ match node {
+ Some(node) => {
+ let vtable = vtable_for(node);
+ vtable.handle_event(event);
+ }
+ None => {}
+ }
+ }
+ None => {}
+ }
+
+ // Root ordering restrictions mean we need to unroot the chain entries
+ // in the same order they were rooted.
+ while chain.len() > 0 {
+ let _ = chain.pop();
+ }
+
+ event.dispatching.deref().set(false);
+ event.phase.deref().set(PhaseNone);
+ event.current_target.set(None);
+
+ !event.DefaultPrevented()
+}
diff --git a/components/script/dom/eventtarget.rs b/components/script/dom/eventtarget.rs
new file mode 100644
index 00000000000..4f2cba18def
--- /dev/null
+++ b/components/script/dom/eventtarget.rs
@@ -0,0 +1,287 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::callback::CallbackContainer;
+use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
+use dom::bindings::codegen::Bindings::EventListenerBinding::EventListener;
+use dom::bindings::codegen::Bindings::EventTargetBinding::EventTargetMethods;
+use dom::bindings::error::{Fallible, InvalidState, report_pending_exception};
+use dom::bindings::js::JSRef;
+use dom::bindings::trace::Traceable;
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::event::Event;
+use dom::eventdispatcher::dispatch_event;
+use dom::node::NodeTypeId;
+use dom::workerglobalscope::WorkerGlobalScopeId;
+use dom::xmlhttprequest::XMLHttpRequestId;
+use dom::virtualmethods::VirtualMethods;
+use js::jsapi::{JS_CompileUCFunction, JS_GetFunctionObject, JS_CloneFunctionObject};
+use js::jsapi::{JSContext, JSObject};
+use servo_util::str::DOMString;
+use libc::{c_char, size_t};
+use std::cell::RefCell;
+use std::ptr;
+use url::Url;
+
+use std::collections::hashmap::HashMap;
+
+#[deriving(PartialEq,Encodable)]
+pub enum ListenerPhase {
+ Capturing,
+ Bubbling,
+}
+
+#[deriving(PartialEq,Encodable)]
+pub enum EventTargetTypeId {
+ NodeTargetTypeId(NodeTypeId),
+ WindowTypeId,
+ WorkerTypeId,
+ WorkerGlobalScopeTypeId(WorkerGlobalScopeId),
+ XMLHttpRequestTargetTypeId(XMLHttpRequestId)
+}
+
+#[deriving(PartialEq, Encodable)]
+pub enum EventListenerType {
+ Additive(EventListener),
+ Inline(EventListener),
+}
+
+impl EventListenerType {
+ fn get_listener(&self) -> EventListener {
+ match *self {
+ Additive(listener) | Inline(listener) => listener
+ }
+ }
+}
+
+#[deriving(PartialEq,Encodable)]
+pub struct EventListenerEntry {
+ pub phase: ListenerPhase,
+ pub listener: EventListenerType
+}
+
+#[deriving(Encodable)]
+pub struct EventTarget {
+ pub type_id: EventTargetTypeId,
+ reflector_: Reflector,
+ handlers: Traceable<RefCell<HashMap<DOMString, Vec<EventListenerEntry>>>>,
+}
+
+impl EventTarget {
+ pub fn new_inherited(type_id: EventTargetTypeId) -> EventTarget {
+ EventTarget {
+ type_id: type_id,
+ reflector_: Reflector::new(),
+ handlers: Traceable::new(RefCell::new(HashMap::new())),
+ }
+ }
+
+ pub fn get_listeners(&self, type_: &str) -> Option<Vec<EventListener>> {
+ self.handlers.deref().borrow().find_equiv(&type_).map(|listeners| {
+ listeners.iter().map(|entry| entry.listener.get_listener()).collect()
+ })
+ }
+
+ pub fn get_listeners_for(&self, type_: &str, desired_phase: ListenerPhase)
+ -> Option<Vec<EventListener>> {
+ self.handlers.deref().borrow().find_equiv(&type_).map(|listeners| {
+ let filtered = listeners.iter().filter(|entry| entry.phase == desired_phase);
+ filtered.map(|entry| entry.listener.get_listener()).collect()
+ })
+ }
+}
+
+pub trait EventTargetHelpers {
+ fn dispatch_event_with_target<'a>(&self,
+ target: Option<JSRef<'a, EventTarget>>,
+ event: &JSRef<Event>) -> Fallible<bool>;
+ fn set_inline_event_listener(&self,
+ ty: DOMString,
+ listener: Option<EventListener>);
+ fn get_inline_event_listener(&self, ty: DOMString) -> Option<EventListener>;
+ fn set_event_handler_uncompiled(&self,
+ cx: *mut JSContext,
+ url: Url,
+ scope: *mut JSObject,
+ ty: &str,
+ source: DOMString);
+ fn set_event_handler_common<T: CallbackContainer>(&self, ty: &str,
+ listener: Option<T>);
+ fn get_event_handler_common<T: CallbackContainer>(&self, ty: &str) -> Option<T>;
+
+ fn has_handlers(&self) -> bool;
+}
+
+impl<'a> EventTargetHelpers for JSRef<'a, EventTarget> {
+ fn dispatch_event_with_target<'b>(&self,
+ target: Option<JSRef<'b, EventTarget>>,
+ event: &JSRef<Event>) -> Fallible<bool> {
+ if event.deref().dispatching.deref().get() || !event.deref().initialized.deref().get() {
+ return Err(InvalidState);
+ }
+ Ok(dispatch_event(self, target, event))
+ }
+
+ fn set_inline_event_listener(&self,
+ ty: DOMString,
+ listener: Option<EventListener>) {
+ let mut handlers = self.handlers.deref().borrow_mut();
+ let entries = handlers.find_or_insert_with(ty, |_| vec!());
+ let idx = entries.iter().position(|&entry| {
+ match entry.listener {
+ Inline(_) => true,
+ _ => false,
+ }
+ });
+
+ match idx {
+ Some(idx) => {
+ match listener {
+ Some(listener) => entries.get_mut(idx).listener = Inline(listener),
+ None => {
+ entries.remove(idx);
+ }
+ }
+ }
+ None => {
+ if listener.is_some() {
+ entries.push(EventListenerEntry {
+ phase: Bubbling,
+ listener: Inline(listener.unwrap()),
+ });
+ }
+ }
+ }
+ }
+
+ fn get_inline_event_listener(&self, ty: DOMString) -> Option<EventListener> {
+ let handlers = self.handlers.deref().borrow();
+ let entries = handlers.find(&ty);
+ entries.and_then(|entries| entries.iter().find(|entry| {
+ match entry.listener {
+ Inline(_) => true,
+ _ => false,
+ }
+ }).map(|entry| entry.listener.get_listener()))
+ }
+
+ fn set_event_handler_uncompiled(&self,
+ cx: *mut JSContext,
+ url: Url,
+ scope: *mut JSObject,
+ ty: &str,
+ source: DOMString) {
+ let url = url.serialize().to_c_str();
+ let name = ty.to_c_str();
+ let lineno = 0; //XXXjdm need to get a real number here
+
+ let nargs = 1; //XXXjdm not true for onerror
+ static arg_name: [c_char, ..6] =
+ ['e' as c_char, 'v' as c_char, 'e' as c_char, 'n' as c_char, 't' as c_char, 0];
+ static arg_names: [*const c_char, ..1] = [&arg_name as *const c_char];
+
+ let source: Vec<u16> = source.as_slice().utf16_units().collect();
+ let handler = unsafe {
+ JS_CompileUCFunction(cx,
+ ptr::mut_null(),
+ name.as_ptr(),
+ nargs,
+ &arg_names as *const *const i8 as *mut *const i8,
+ source.as_ptr(),
+ source.len() as size_t,
+ url.as_ptr(),
+ lineno)
+ };
+ if handler.is_null() {
+ report_pending_exception(cx, self.reflector().get_jsobject());
+ return;
+ }
+
+ let funobj = unsafe {
+ JS_CloneFunctionObject(cx, JS_GetFunctionObject(handler), scope)
+ };
+ assert!(funobj.is_not_null());
+ self.set_event_handler_common(ty, Some(EventHandlerNonNull::new(funobj)));
+ }
+
+ fn set_event_handler_common<T: CallbackContainer>(
+ &self, ty: &str, listener: Option<T>)
+ {
+ let event_listener = listener.map(|listener|
+ EventListener::new(listener.callback()));
+ self.set_inline_event_listener(ty.to_string(), event_listener);
+ }
+
+ fn get_event_handler_common<T: CallbackContainer>(&self, ty: &str) -> Option<T> {
+ let listener = self.get_inline_event_listener(ty.to_string());
+ listener.map(|listener| CallbackContainer::new(listener.parent.callback()))
+ }
+
+ fn has_handlers(&self) -> bool {
+ !self.handlers.deref().borrow().is_empty()
+ }
+}
+
+impl<'a> EventTargetMethods for JSRef<'a, EventTarget> {
+ fn AddEventListener(&self,
+ ty: DOMString,
+ listener: Option<EventListener>,
+ capture: bool) {
+ match listener {
+ Some(listener) => {
+ let mut handlers = self.handlers.deref().borrow_mut();
+ let entry = handlers.find_or_insert_with(ty, |_| vec!());
+ let phase = if capture { Capturing } else { Bubbling };
+ let new_entry = EventListenerEntry {
+ phase: phase,
+ listener: Additive(listener)
+ };
+ if entry.as_slice().position_elem(&new_entry).is_none() {
+ entry.push(new_entry);
+ }
+ },
+ _ => (),
+ }
+ }
+
+ fn RemoveEventListener(&self,
+ ty: DOMString,
+ listener: Option<EventListener>,
+ capture: bool) {
+ match listener {
+ Some(listener) => {
+ let mut handlers = self.handlers.deref().borrow_mut();
+ let mut entry = handlers.find_mut(&ty);
+ for entry in entry.mut_iter() {
+ let phase = if capture { Capturing } else { Bubbling };
+ let old_entry = EventListenerEntry {
+ phase: phase,
+ listener: Additive(listener)
+ };
+ let position = entry.as_slice().position_elem(&old_entry);
+ for &position in position.iter() {
+ entry.remove(position);
+ }
+ }
+ },
+ _ => (),
+ }
+ }
+
+ fn DispatchEvent(&self, event: &JSRef<Event>) -> Fallible<bool> {
+ self.dispatch_event_with_target(None, event)
+ }
+}
+
+impl Reflectable for EventTarget {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ &self.reflector_
+ }
+}
+
+impl<'a> VirtualMethods for JSRef<'a, EventTarget> {
+ fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods> {
+ None
+ }
+}
diff --git a/components/script/dom/file.rs b/components/script/dom/file.rs
new file mode 100644
index 00000000000..c4c07e03399
--- /dev/null
+++ b/components/script/dom/file.rs
@@ -0,0 +1,48 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::FileBinding;
+use dom::bindings::codegen::Bindings::FileBinding::FileMethods;
+use dom::bindings::global::GlobalRef;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
+use dom::blob::{Blob, BlobType, FileTypeId};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct File {
+ pub blob: Blob,
+ pub name: DOMString,
+ pub type_: BlobType
+}
+
+impl File {
+ pub fn new_inherited(_file_bits: &JSRef<Blob>, name: DOMString) -> File {
+ File {
+ blob: Blob::new_inherited(),
+ name: name,
+ type_: FileTypeId
+ }
+ // XXXManishearth Once Blob is able to store data
+ // the relevant subfields of file_bits should be copied over
+ }
+
+ pub fn new(global: &GlobalRef, file_bits: &JSRef<Blob>, name: DOMString) -> Temporary<File> {
+ reflect_dom_object(box File::new_inherited(file_bits, name),
+ global,
+ FileBinding::Wrap)
+ }
+}
+
+impl FileMethods for File {
+ fn Name(&self) -> DOMString {
+ self.name.clone()
+ }
+}
+
+impl Reflectable for File {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.blob.reflector()
+ }
+}
diff --git a/components/script/dom/formdata.rs b/components/script/dom/formdata.rs
new file mode 100644
index 00000000000..0de17b83107
--- /dev/null
+++ b/components/script/dom/formdata.rs
@@ -0,0 +1,115 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::FormDataBinding;
+use dom::bindings::codegen::Bindings::FormDataBinding::FormDataMethods;
+use dom::bindings::codegen::InheritTypes::FileCast;
+use dom::bindings::codegen::UnionTypes::FileOrString::{FileOrString, eFile, eString};
+use dom::bindings::error::{Fallible};
+use dom::bindings::global::{GlobalRef, GlobalField};
+use dom::bindings::js::{JS, JSRef, Temporary};
+use dom::bindings::trace::Traceable;
+use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
+use dom::blob::Blob;
+use dom::file::File;
+use dom::htmlformelement::HTMLFormElement;
+use servo_util::str::DOMString;
+use std::cell::RefCell;
+use std::collections::hashmap::HashMap;
+
+#[deriving(Encodable, Clone)]
+pub enum FormDatum {
+ StringData(DOMString),
+ FileData(JS<File>)
+}
+
+#[deriving(Encodable)]
+pub struct FormData {
+ data: Traceable<RefCell<HashMap<DOMString, Vec<FormDatum>>>>,
+ reflector_: Reflector,
+ global: GlobalField,
+ form: Option<JS<HTMLFormElement>>
+}
+
+impl FormData {
+ pub fn new_inherited(form: Option<JSRef<HTMLFormElement>>, global: &GlobalRef) -> FormData {
+ FormData {
+ data: Traceable::new(RefCell::new(HashMap::new())),
+ reflector_: Reflector::new(),
+ global: GlobalField::from_rooted(global),
+ form: form.map(|f| JS::from_rooted(&f)),
+ }
+ }
+
+ pub fn new(form: Option<JSRef<HTMLFormElement>>, global: &GlobalRef) -> Temporary<FormData> {
+ reflect_dom_object(box FormData::new_inherited(form, global),
+ global, FormDataBinding::Wrap)
+ }
+
+ pub fn Constructor(global: &GlobalRef, form: Option<JSRef<HTMLFormElement>>) -> Fallible<Temporary<FormData>> {
+ Ok(FormData::new(form, global))
+ }
+}
+
+impl<'a> FormDataMethods for JSRef<'a, FormData> {
+ fn Append(&self, name: DOMString, value: &JSRef<Blob>, filename: Option<DOMString>) {
+ let file = FileData(JS::from_rooted(&self.get_file_from_blob(value, filename)));
+ self.data.deref().borrow_mut().insert_or_update_with(name.clone(), vec!(file.clone()),
+ |_k, v| {v.push(file.clone());});
+ }
+
+ fn Append_(&self, name: DOMString, value: DOMString) {
+ self.data.deref().borrow_mut().insert_or_update_with(name, vec!(StringData(value.clone())),
+ |_k, v| {v.push(StringData(value.clone()));});
+ }
+
+ fn Delete(&self, name: DOMString) {
+ self.data.deref().borrow_mut().remove(&name);
+ }
+
+ fn Get(&self, name: DOMString) -> Option<FileOrString> {
+ if self.data.deref().borrow().contains_key_equiv(&name) {
+ match self.data.deref().borrow().get(&name)[0].clone() {
+ StringData(ref s) => Some(eString(s.clone())),
+ FileData(ref f) => {
+ Some(eFile(f.clone()))
+ }
+ }
+ } else {
+ None
+ }
+ }
+
+ fn Has(&self, name: DOMString) -> bool {
+ self.data.deref().borrow().contains_key_equiv(&name)
+ }
+
+ fn Set(&self, name: DOMString, value: &JSRef<Blob>, filename: Option<DOMString>) {
+ let file = FileData(JS::from_rooted(&self.get_file_from_blob(value, filename)));
+ self.data.deref().borrow_mut().insert(name, vec!(file));
+ }
+
+ fn Set_(&self, name: DOMString, value: DOMString) {
+ self.data.deref().borrow_mut().insert(name, vec!(StringData(value)));
+ }
+}
+
+impl Reflectable for FormData {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ &self.reflector_
+ }
+}
+
+trait PrivateFormDataHelpers{
+ fn get_file_from_blob(&self, value: &JSRef<Blob>, filename: Option<DOMString>) -> Temporary<File>;
+}
+
+impl PrivateFormDataHelpers for FormData {
+ fn get_file_from_blob(&self, value: &JSRef<Blob>, filename: Option<DOMString>) -> Temporary<File> {
+ let global = self.global.root();
+ let f: Option<&JSRef<File>> = FileCast::to_ref(value);
+ let name = filename.unwrap_or(f.map(|inner| inner.name.clone()).unwrap_or("blob".to_string()));
+ File::new(&global.root_ref(), value, name)
+ }
+}
diff --git a/components/script/dom/htmlanchorelement.rs b/components/script/dom/htmlanchorelement.rs
new file mode 100644
index 00000000000..8c23abc940f
--- /dev/null
+++ b/components/script/dom/htmlanchorelement.rs
@@ -0,0 +1,132 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
+use dom::bindings::codegen::Bindings::EventBinding::EventMethods;
+use dom::bindings::codegen::Bindings::HTMLAnchorElementBinding;
+use dom::bindings::codegen::Bindings::HTMLAnchorElementBinding::HTMLAnchorElementMethods;
+use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
+use dom::bindings::codegen::InheritTypes::HTMLAnchorElementDerived;
+use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast, NodeCast};
+use dom::bindings::js::{JSRef, Temporary, OptionalRootable};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::{Document, DocumentHelpers};
+use dom::element::{Element, AttributeHandlers, HTMLAnchorElementTypeId};
+use dom::event::Event;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, NodeHelpers, ElementNodeTypeId};
+use dom::virtualmethods::VirtualMethods;
+
+use servo_util::atom::Atom;
+use servo_util::namespace::Null;
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLAnchorElement {
+ pub htmlelement: HTMLElement
+}
+
+impl HTMLAnchorElementDerived for EventTarget {
+ fn is_htmlanchorelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLAnchorElementTypeId))
+ }
+}
+
+impl HTMLAnchorElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLAnchorElement {
+ HTMLAnchorElement {
+ htmlelement: HTMLElement::new_inherited(HTMLAnchorElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLAnchorElement> {
+ let element = HTMLAnchorElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLAnchorElementBinding::Wrap)
+ }
+}
+
+trait PrivateHTMLAnchorElementHelpers {
+ fn handle_event_impl(&self, event: &JSRef<Event>);
+}
+
+impl<'a> PrivateHTMLAnchorElementHelpers for JSRef<'a, HTMLAnchorElement> {
+ fn handle_event_impl(&self, event: &JSRef<Event>) {
+ if "click" == event.Type().as_slice() && !event.DefaultPrevented() {
+ let element: &JSRef<Element> = ElementCast::from_ref(self);
+ let attr = element.get_attribute(Null, "href").root();
+ match attr {
+ Some(ref href) => {
+ let value = href.Value();
+ debug!("clicked on link to {:s}", value);
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ let doc = node.owner_doc().root();
+ doc.load_anchor_href(value);
+ }
+ None => ()
+ }
+ }
+ }
+}
+
+impl<'a> VirtualMethods for JSRef<'a, HTMLAnchorElement> {
+ fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods> {
+ let htmlelement: &JSRef<HTMLElement> = HTMLElementCast::from_ref(self);
+ Some(htmlelement as &VirtualMethods)
+ }
+
+ fn after_set_attr(&self, name: &Atom, value: DOMString) {
+ match self.super_type() {
+ Some(ref s) => s.after_set_attr(name, value.clone()),
+ _ => (),
+ }
+
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ match name.as_slice() {
+ "href" => node.set_enabled_state(true),
+ _ => ()
+ }
+ }
+
+ fn before_remove_attr(&self, name: &Atom, value: DOMString) {
+ match self.super_type() {
+ Some(ref s) => s.before_remove_attr(name, value.clone()),
+ _ => (),
+ }
+
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ match name.as_slice() {
+ "href" => node.set_enabled_state(false),
+ _ => ()
+ }
+ }
+
+ fn handle_event(&self, event: &JSRef<Event>) {
+ match self.super_type() {
+ Some(s) => {
+ s.handle_event(event);
+ }
+ None => {}
+ }
+ self.handle_event_impl(event);
+ }
+}
+
+impl Reflectable for HTMLAnchorElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
+
+impl<'a> HTMLAnchorElementMethods for JSRef<'a, HTMLAnchorElement> {
+ fn Text(&self) -> DOMString {
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ node.GetTextContent().unwrap()
+ }
+
+ fn SetText(&self, value: DOMString) {
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ node.SetTextContent(Some(value))
+ }
+}
diff --git a/components/script/dom/htmlappletelement.rs b/components/script/dom/htmlappletelement.rs
new file mode 100644
index 00000000000..97a3757f02e
--- /dev/null
+++ b/components/script/dom/htmlappletelement.rs
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLAppletElementBinding;
+use dom::bindings::codegen::InheritTypes::HTMLAppletElementDerived;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLAppletElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, ElementNodeTypeId};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLAppletElement {
+ pub htmlelement: HTMLElement
+}
+
+impl HTMLAppletElementDerived for EventTarget {
+ fn is_htmlappletelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLAppletElementTypeId))
+ }
+}
+
+impl HTMLAppletElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLAppletElement {
+ HTMLAppletElement {
+ htmlelement: HTMLElement::new_inherited(HTMLAppletElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLAppletElement> {
+ let element = HTMLAppletElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLAppletElementBinding::Wrap)
+ }
+}
+
+impl Reflectable for HTMLAppletElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmlareaelement.rs b/components/script/dom/htmlareaelement.rs
new file mode 100644
index 00000000000..3de80a21802
--- /dev/null
+++ b/components/script/dom/htmlareaelement.rs
@@ -0,0 +1,81 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLAreaElementBinding;
+use dom::bindings::codegen::InheritTypes::HTMLAreaElementDerived;
+use dom::bindings::codegen::InheritTypes::{HTMLElementCast, NodeCast};
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLAreaElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, NodeHelpers, ElementNodeTypeId};
+use dom::virtualmethods::VirtualMethods;
+
+use servo_util::atom::Atom;
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLAreaElement {
+ pub htmlelement: HTMLElement
+}
+
+impl HTMLAreaElementDerived for EventTarget {
+ fn is_htmlareaelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLAreaElementTypeId))
+ }
+}
+
+impl HTMLAreaElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLAreaElement {
+ HTMLAreaElement {
+ htmlelement: HTMLElement::new_inherited(HTMLAreaElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLAreaElement> {
+ let element = HTMLAreaElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLAreaElementBinding::Wrap)
+ }
+}
+
+impl<'a> VirtualMethods for JSRef<'a, HTMLAreaElement> {
+ fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods> {
+ let htmlelement: &JSRef<HTMLElement> = HTMLElementCast::from_ref(self);
+ Some(htmlelement as &VirtualMethods)
+ }
+
+ fn after_set_attr(&self, name: &Atom, value: DOMString) {
+ match self.super_type() {
+ Some(ref s) => s.after_set_attr(name, value.clone()),
+ _ => (),
+ }
+
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ match name.as_slice() {
+ "href" => node.set_enabled_state(true),
+ _ => ()
+ }
+ }
+
+ fn before_remove_attr(&self, name: &Atom, value: DOMString) {
+ match self.super_type() {
+ Some(ref s) => s.before_remove_attr(name, value.clone()),
+ _ => (),
+ }
+
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ match name.as_slice() {
+ "href" => node.set_enabled_state(false),
+ _ => ()
+ }
+ }
+}
+
+impl Reflectable for HTMLAreaElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmlaudioelement.rs b/components/script/dom/htmlaudioelement.rs
new file mode 100644
index 00000000000..626e2e99619
--- /dev/null
+++ b/components/script/dom/htmlaudioelement.rs
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLAudioElementBinding;
+use dom::bindings::codegen::InheritTypes::HTMLAudioElementDerived;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLAudioElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlmediaelement::HTMLMediaElement;
+use dom::node::{Node, ElementNodeTypeId};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLAudioElement {
+ pub htmlmediaelement: HTMLMediaElement
+}
+
+impl HTMLAudioElementDerived for EventTarget {
+ fn is_htmlaudioelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLAudioElementTypeId))
+ }
+}
+
+impl HTMLAudioElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLAudioElement {
+ HTMLAudioElement {
+ htmlmediaelement: HTMLMediaElement::new_inherited(HTMLAudioElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLAudioElement> {
+ let element = HTMLAudioElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLAudioElementBinding::Wrap)
+ }
+}
+
+impl Reflectable for HTMLAudioElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlmediaelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmlbaseelement.rs b/components/script/dom/htmlbaseelement.rs
new file mode 100644
index 00000000000..fbd3d5cdfca
--- /dev/null
+++ b/components/script/dom/htmlbaseelement.rs
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLBaseElementBinding;
+use dom::bindings::codegen::InheritTypes::HTMLBaseElementDerived;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLBaseElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, ElementNodeTypeId};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLBaseElement {
+ pub htmlelement: HTMLElement
+}
+
+impl HTMLBaseElementDerived for EventTarget {
+ fn is_htmlbaseelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLBaseElementTypeId))
+ }
+}
+
+impl HTMLBaseElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLBaseElement {
+ HTMLBaseElement {
+ htmlelement: HTMLElement::new_inherited(HTMLBaseElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLBaseElement> {
+ let element = HTMLBaseElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLBaseElementBinding::Wrap)
+ }
+}
+
+impl Reflectable for HTMLBaseElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmlbodyelement.rs b/components/script/dom/htmlbodyelement.rs
new file mode 100644
index 00000000000..b7bb35e618a
--- /dev/null
+++ b/components/script/dom/htmlbodyelement.rs
@@ -0,0 +1,98 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
+use dom::bindings::codegen::Bindings::HTMLBodyElementBinding;
+use dom::bindings::codegen::Bindings::HTMLBodyElementBinding::HTMLBodyElementMethods;
+use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
+use dom::bindings::codegen::InheritTypes::EventTargetCast;
+use dom::bindings::codegen::InheritTypes::{HTMLBodyElementDerived, HTMLElementCast};
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLBodyElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId, EventTargetHelpers};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, ElementNodeTypeId, window_from_node};
+use dom::virtualmethods::VirtualMethods;
+
+use servo_util::atom::Atom;
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLBodyElement {
+ pub htmlelement: HTMLElement
+}
+
+impl HTMLBodyElementDerived for EventTarget {
+ fn is_htmlbodyelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLBodyElementTypeId))
+ }
+}
+
+impl HTMLBodyElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLBodyElement {
+ HTMLBodyElement {
+ htmlelement: HTMLElement::new_inherited(HTMLBodyElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLBodyElement> {
+ let element = HTMLBodyElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLBodyElementBinding::Wrap)
+ }
+}
+
+impl<'a> HTMLBodyElementMethods for JSRef<'a, HTMLBodyElement> {
+ fn GetOnunload(&self) -> Option<EventHandlerNonNull> {
+ let win = window_from_node(self).root();
+ win.deref().GetOnunload()
+ }
+
+ fn SetOnunload(&self, listener: Option<EventHandlerNonNull>) {
+ let win = window_from_node(self).root();
+ win.deref().SetOnunload(listener)
+ }
+}
+
+impl<'a> VirtualMethods for JSRef<'a, HTMLBodyElement> {
+ fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods> {
+ let element: &JSRef<HTMLElement> = HTMLElementCast::from_ref(self);
+ Some(element as &VirtualMethods)
+ }
+
+ fn after_set_attr(&self, name: &Atom, value: DOMString) {
+ match self.super_type() {
+ Some(ref s) => s.after_set_attr(name, value.clone()),
+ _ => (),
+ }
+
+ if name.as_slice().starts_with("on") {
+ static forwarded_events: &'static [&'static str] =
+ &["onfocus", "onload", "onscroll", "onafterprint", "onbeforeprint",
+ "onbeforeunload", "onhashchange", "onlanguagechange", "onmessage",
+ "onoffline", "ononline", "onpagehide", "onpageshow", "onpopstate",
+ "onstorage", "onresize", "onunload", "onerror"];
+ let window = window_from_node(self).root();
+ let (cx, url, reflector) = (window.get_cx(),
+ window.get_url(),
+ window.reflector().get_jsobject());
+ let evtarget: &JSRef<EventTarget> =
+ if forwarded_events.iter().any(|&event| name.as_slice() == event) {
+ EventTargetCast::from_ref(&*window)
+ } else {
+ EventTargetCast::from_ref(self)
+ };
+ evtarget.set_event_handler_uncompiled(cx, url, reflector,
+ name.as_slice().slice_from(2),
+ value);
+ }
+ }
+}
+
+impl Reflectable for HTMLBodyElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmlbrelement.rs b/components/script/dom/htmlbrelement.rs
new file mode 100644
index 00000000000..70c415ba24a
--- /dev/null
+++ b/components/script/dom/htmlbrelement.rs
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLBRElementBinding;
+use dom::bindings::codegen::InheritTypes::HTMLBRElementDerived;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLBRElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, ElementNodeTypeId};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLBRElement {
+ pub htmlelement: HTMLElement,
+}
+
+impl HTMLBRElementDerived for EventTarget {
+ fn is_htmlbrelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLBRElementTypeId))
+ }
+}
+
+impl HTMLBRElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLBRElement {
+ HTMLBRElement {
+ htmlelement: HTMLElement::new_inherited(HTMLBRElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLBRElement> {
+ let element = HTMLBRElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLBRElementBinding::Wrap)
+ }
+}
+
+impl Reflectable for HTMLBRElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmlbuttonelement.rs b/components/script/dom/htmlbuttonelement.rs
new file mode 100644
index 00000000000..0c8903603e1
--- /dev/null
+++ b/components/script/dom/htmlbuttonelement.rs
@@ -0,0 +1,130 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLButtonElementBinding;
+use dom::bindings::codegen::Bindings::HTMLButtonElementBinding::HTMLButtonElementMethods;
+use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast, NodeCast};
+use dom::bindings::codegen::InheritTypes::{HTMLButtonElementDerived, HTMLFieldSetElementDerived};
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::{AttributeHandlers, Element, HTMLButtonElementTypeId};
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{DisabledStateHelpers, Node, NodeHelpers, ElementNodeTypeId, window_from_node};
+use dom::validitystate::ValidityState;
+use dom::virtualmethods::VirtualMethods;
+
+use servo_util::atom::Atom;
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLButtonElement {
+ pub htmlelement: HTMLElement
+}
+
+impl HTMLButtonElementDerived for EventTarget {
+ fn is_htmlbuttonelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLButtonElementTypeId))
+ }
+}
+
+impl HTMLButtonElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLButtonElement {
+ HTMLButtonElement {
+ htmlelement: HTMLElement::new_inherited(HTMLButtonElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLButtonElement> {
+ let element = HTMLButtonElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLButtonElementBinding::Wrap)
+ }
+}
+
+impl<'a> HTMLButtonElementMethods for JSRef<'a, HTMLButtonElement> {
+ fn Validity(&self) -> Temporary<ValidityState> {
+ let window = window_from_node(self).root();
+ ValidityState::new(&*window)
+ }
+
+ // http://www.whatwg.org/html/#dom-fe-disabled
+ make_bool_getter!(Disabled)
+
+ // http://www.whatwg.org/html/#dom-fe-disabled
+ fn SetDisabled(&self, disabled: bool) {
+ let elem: &JSRef<Element> = ElementCast::from_ref(self);
+ elem.set_bool_attribute("disabled", disabled)
+ }
+}
+
+impl<'a> VirtualMethods for JSRef<'a, HTMLButtonElement> {
+ fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods> {
+ let htmlelement: &JSRef<HTMLElement> = HTMLElementCast::from_ref(self);
+ Some(htmlelement as &VirtualMethods)
+ }
+
+ fn after_set_attr(&self, name: &Atom, value: DOMString) {
+ match self.super_type() {
+ Some(ref s) => s.after_set_attr(name, value.clone()),
+ _ => (),
+ }
+
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ match name.as_slice() {
+ "disabled" => {
+ node.set_disabled_state(true);
+ node.set_enabled_state(false);
+ },
+ _ => ()
+ }
+ }
+
+ fn before_remove_attr(&self, name: &Atom, value: DOMString) {
+ match self.super_type() {
+ Some(ref s) => s.before_remove_attr(name, value),
+ _ => (),
+ }
+
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ match name.as_slice() {
+ "disabled" => {
+ node.set_disabled_state(false);
+ node.set_enabled_state(true);
+ node.check_ancestors_disabled_state_for_form_control();
+ },
+ _ => ()
+ }
+ }
+
+ fn bind_to_tree(&self, tree_in_doc: bool) {
+ match self.super_type() {
+ Some(ref s) => s.bind_to_tree(tree_in_doc),
+ _ => (),
+ }
+
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ node.check_ancestors_disabled_state_for_form_control();
+ }
+
+ fn unbind_from_tree(&self, tree_in_doc: bool) {
+ match self.super_type() {
+ Some(ref s) => s.unbind_from_tree(tree_in_doc),
+ _ => (),
+ }
+
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ if node.ancestors().any(|ancestor| ancestor.is_htmlfieldsetelement()) {
+ node.check_ancestors_disabled_state_for_form_control();
+ } else {
+ node.check_disabled_attribute();
+ }
+ }
+}
+
+impl Reflectable for HTMLButtonElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmlcanvaselement.rs b/components/script/dom/htmlcanvaselement.rs
new file mode 100644
index 00000000000..28902265610
--- /dev/null
+++ b/components/script/dom/htmlcanvaselement.rs
@@ -0,0 +1,160 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLCanvasElementBinding;
+use dom::bindings::codegen::Bindings::HTMLCanvasElementBinding::HTMLCanvasElementMethods;
+use dom::bindings::codegen::InheritTypes::HTMLCanvasElementDerived;
+use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast};
+use dom::bindings::global::Window;
+use dom::bindings::js::{JS, JSRef, Temporary, OptionalSettable};
+use dom::bindings::trace::Traceable;
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::canvasrenderingcontext2d::CanvasRenderingContext2D;
+use dom::document::Document;
+use dom::element::{Element, HTMLCanvasElementTypeId, AttributeHandlers};
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, ElementNodeTypeId, window_from_node};
+use dom::virtualmethods::VirtualMethods;
+
+use servo_util::atom::Atom;
+use servo_util::str::{DOMString, parse_unsigned_integer};
+
+use geom::size::Size2D;
+
+use std::cell::Cell;
+
+static DefaultWidth: u32 = 300;
+static DefaultHeight: u32 = 150;
+
+#[deriving(Encodable)]
+pub struct HTMLCanvasElement {
+ pub htmlelement: HTMLElement,
+ context: Traceable<Cell<Option<JS<CanvasRenderingContext2D>>>>,
+ width: Traceable<Cell<u32>>,
+ height: Traceable<Cell<u32>>,
+}
+
+impl HTMLCanvasElementDerived for EventTarget {
+ fn is_htmlcanvaselement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLCanvasElementTypeId))
+ }
+}
+
+impl HTMLCanvasElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLCanvasElement {
+ HTMLCanvasElement {
+ htmlelement: HTMLElement::new_inherited(HTMLCanvasElementTypeId, localName, document),
+ context: Traceable::new(Cell::new(None)),
+ width: Traceable::new(Cell::new(DefaultWidth)),
+ height: Traceable::new(Cell::new(DefaultHeight)),
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLCanvasElement> {
+ let element = HTMLCanvasElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLCanvasElementBinding::Wrap)
+ }
+}
+
+impl<'a> HTMLCanvasElementMethods for JSRef<'a, HTMLCanvasElement> {
+ fn Width(&self) -> u32 {
+ self.width.get()
+ }
+
+ fn SetWidth(&self, width: u32) {
+ let elem: &JSRef<Element> = ElementCast::from_ref(self);
+ elem.set_uint_attribute("width", width)
+ }
+
+ fn Height(&self) -> u32 {
+ self.height.get()
+ }
+
+ fn SetHeight(&self, height: u32) {
+ let elem: &JSRef<Element> = ElementCast::from_ref(self);
+ elem.set_uint_attribute("height", height)
+ }
+
+ fn GetContext(&self, id: DOMString) -> Option<Temporary<CanvasRenderingContext2D>> {
+ if id.as_slice() != "2d" {
+ return None;
+ }
+
+ if self.context.get().is_none() {
+ let window = window_from_node(self).root();
+ let (w, h) = (self.width.get() as i32, self.height.get() as i32);
+ let context = CanvasRenderingContext2D::new(&Window(*window), self, Size2D(w, h));
+ self.context.assign(Some(context));
+ }
+ self.context.get().map(|context| Temporary::new(context))
+ }
+}
+
+impl<'a> VirtualMethods for JSRef<'a, HTMLCanvasElement> {
+ fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods> {
+ let element: &JSRef<HTMLElement> = HTMLElementCast::from_ref(self);
+ Some(element as &VirtualMethods)
+ }
+
+ fn before_remove_attr(&self, name: &Atom, value: DOMString) {
+ match self.super_type() {
+ Some(ref s) => s.before_remove_attr(name, value.clone()),
+ _ => (),
+ }
+
+ let recreate = match name.as_slice() {
+ "width" => {
+ self.width.set(DefaultWidth);
+ true
+ }
+ "height" => {
+ self.height.set(DefaultHeight);
+ true
+ }
+ _ => false,
+ };
+
+ if recreate {
+ let (w, h) = (self.width.get() as i32, self.height.get() as i32);
+ match self.context.get() {
+ Some(ref context) => context.root().recreate(Size2D(w, h)),
+ None => ()
+ }
+ }
+ }
+
+ fn after_set_attr(&self, name: &Atom, value: DOMString) {
+ match self.super_type() {
+ Some(ref s) => s.after_set_attr(name, value.clone()),
+ _ => (),
+ }
+
+ let recreate = match name.as_slice() {
+ "width" => {
+ self.width.set(parse_unsigned_integer(value.as_slice().chars()).unwrap_or(DefaultWidth));
+ true
+ }
+ "height" => {
+ self.height.set(parse_unsigned_integer(value.as_slice().chars()).unwrap_or(DefaultHeight));
+ true
+ }
+ _ => false,
+ };
+
+ if recreate {
+ let (w, h) = (self.width.get() as i32, self.height.get() as i32);
+ match self.context.get() {
+ Some(ref context) => context.root().recreate(Size2D(w, h)),
+ None => ()
+ }
+ }
+ }
+}
+
+impl Reflectable for HTMLCanvasElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmlcollection.rs b/components/script/dom/htmlcollection.rs
new file mode 100644
index 00000000000..5bef6c56ff5
--- /dev/null
+++ b/components/script/dom/htmlcollection.rs
@@ -0,0 +1,257 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLCollectionBinding;
+use dom::bindings::codegen::Bindings::HTMLCollectionBinding::HTMLCollectionMethods;
+use dom::bindings::codegen::InheritTypes::{ElementCast, NodeCast};
+use dom::bindings::global::Window;
+use dom::bindings::js::{JS, JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
+use dom::element::{Element, AttributeHandlers, ElementHelpers};
+use dom::node::{Node, NodeHelpers};
+use dom::window::Window;
+use servo_util::atom::Atom;
+use servo_util::namespace;
+use servo_util::namespace::Namespace;
+use servo_util::str::{DOMString, split_html_space_chars};
+
+use serialize::{Encoder, Encodable};
+use std::ascii::StrAsciiExt;
+
+pub trait CollectionFilter {
+ fn filter(&self, elem: &JSRef<Element>, root: &JSRef<Node>) -> bool;
+}
+
+impl<S: Encoder<E>, E> Encodable<S, E> for Box<CollectionFilter> {
+ fn encode(&self, _s: &mut S) -> Result<(), E> {
+ Ok(())
+ }
+}
+
+#[deriving(Encodable)]
+pub enum CollectionTypeId {
+ Static(Vec<JS<Element>>),
+ Live(JS<Node>, Box<CollectionFilter>)
+}
+
+#[deriving(Encodable)]
+pub struct HTMLCollection {
+ collection: CollectionTypeId,
+ reflector_: Reflector,
+}
+
+impl HTMLCollection {
+ pub fn new_inherited(collection: CollectionTypeId) -> HTMLCollection {
+ HTMLCollection {
+ collection: collection,
+ reflector_: Reflector::new(),
+ }
+ }
+
+ pub fn new(window: &JSRef<Window>, collection: CollectionTypeId) -> Temporary<HTMLCollection> {
+ reflect_dom_object(box HTMLCollection::new_inherited(collection),
+ &Window(*window), HTMLCollectionBinding::Wrap)
+ }
+}
+
+impl HTMLCollection {
+ pub fn create(window: &JSRef<Window>, root: &JSRef<Node>,
+ filter: Box<CollectionFilter>) -> Temporary<HTMLCollection> {
+ HTMLCollection::new(window, Live(JS::from_rooted(root), filter))
+ }
+
+ fn all_elements(window: &JSRef<Window>, root: &JSRef<Node>,
+ namespace_filter: Option<Namespace>) -> Temporary<HTMLCollection> {
+ struct AllElementFilter {
+ namespace_filter: Option<Namespace>
+ }
+ impl CollectionFilter for AllElementFilter {
+ fn filter(&self, elem: &JSRef<Element>, _root: &JSRef<Node>) -> bool {
+ match self.namespace_filter {
+ None => true,
+ Some(ref namespace) => elem.namespace == *namespace
+ }
+ }
+ }
+ let filter = AllElementFilter {namespace_filter: namespace_filter};
+ HTMLCollection::create(window, root, box filter)
+ }
+
+ pub fn by_tag_name(window: &JSRef<Window>, root: &JSRef<Node>, tag: DOMString)
+ -> Temporary<HTMLCollection> {
+ if tag.as_slice() == "*" {
+ return HTMLCollection::all_elements(window, root, None);
+ }
+
+ struct TagNameFilter {
+ tag: Atom,
+ ascii_lower_tag: Atom,
+ }
+ impl CollectionFilter for TagNameFilter {
+ fn filter(&self, elem: &JSRef<Element>, _root: &JSRef<Node>) -> bool {
+ if elem.html_element_in_html_document() {
+ elem.local_name == self.ascii_lower_tag
+ } else {
+ elem.local_name == self.tag
+ }
+ }
+ }
+ let filter = TagNameFilter {
+ tag: Atom::from_slice(tag.as_slice()),
+ ascii_lower_tag: Atom::from_slice(tag.as_slice().to_ascii_lower().as_slice()),
+ };
+ HTMLCollection::create(window, root, box filter)
+ }
+
+ pub fn by_tag_name_ns(window: &JSRef<Window>, root: &JSRef<Node>, tag: DOMString,
+ maybe_ns: Option<DOMString>) -> Temporary<HTMLCollection> {
+ let namespace_filter = match maybe_ns {
+ Some(namespace) => {
+ match namespace.as_slice() {
+ "*" => None,
+ ns => Some(Namespace::from_str(ns)),
+ }
+ },
+ None => Some(namespace::Null),
+ };
+
+ if tag.as_slice() == "*" {
+ return HTMLCollection::all_elements(window, root, namespace_filter);
+ }
+ struct TagNameNSFilter {
+ tag: Atom,
+ namespace_filter: Option<Namespace>
+ }
+ impl CollectionFilter for TagNameNSFilter {
+ fn filter(&self, elem: &JSRef<Element>, _root: &JSRef<Node>) -> bool {
+ let ns_match = match self.namespace_filter {
+ Some(ref namespace) => {
+ elem.deref().namespace == *namespace
+ },
+ None => true
+ };
+ ns_match && elem.deref().local_name == self.tag
+ }
+ }
+ let filter = TagNameNSFilter {
+ tag: Atom::from_slice(tag.as_slice()),
+ namespace_filter: namespace_filter
+ };
+ HTMLCollection::create(window, root, box filter)
+ }
+
+ pub fn by_class_name(window: &JSRef<Window>, root: &JSRef<Node>, classes: DOMString)
+ -> Temporary<HTMLCollection> {
+ struct ClassNameFilter {
+ classes: Vec<DOMString>
+ }
+ impl CollectionFilter for ClassNameFilter {
+ fn filter(&self, elem: &JSRef<Element>, _root: &JSRef<Node>) -> bool {
+ self.classes.iter().all(|class| elem.has_class(class.as_slice()))
+ }
+ }
+ let filter = ClassNameFilter {
+ classes: split_html_space_chars(classes.as_slice()).map(|class| class.to_string()).collect()
+ };
+ HTMLCollection::create(window, root, box filter)
+ }
+
+ pub fn children(window: &JSRef<Window>, root: &JSRef<Node>) -> Temporary<HTMLCollection> {
+ struct ElementChildFilter;
+ impl CollectionFilter for ElementChildFilter {
+ fn filter(&self, elem: &JSRef<Element>, root: &JSRef<Node>) -> bool {
+ root.is_parent_of(NodeCast::from_ref(elem))
+ }
+ }
+ HTMLCollection::create(window, root, box ElementChildFilter)
+ }
+}
+
+impl<'a> HTMLCollectionMethods for JSRef<'a, HTMLCollection> {
+ // http://dom.spec.whatwg.org/#dom-htmlcollection-length
+ fn Length(&self) -> u32 {
+ match self.collection {
+ Static(ref elems) => elems.len() as u32,
+ Live(ref root, ref filter) => {
+ let root = root.root();
+ root.deref().traverse_preorder()
+ .filter(|&child| {
+ let elem: Option<&JSRef<Element>> = ElementCast::to_ref(&child);
+ elem.map_or(false, |elem| filter.filter(elem, &*root))
+ }).count() as u32
+ }
+ }
+ }
+
+ // http://dom.spec.whatwg.org/#dom-htmlcollection-item
+ fn Item(&self, index: u32) -> Option<Temporary<Element>> {
+ match self.collection {
+ Static(ref elems) => elems
+ .as_slice()
+ .get(index as uint)
+ .map(|elem| Temporary::new(elem.clone())),
+ Live(ref root, ref filter) => {
+ let root = root.root();
+ root.deref().traverse_preorder()
+ .filter_map(|node| {
+ let elem: Option<&JSRef<Element>> = ElementCast::to_ref(&node);
+ elem.filtered(|&elem| filter.filter(elem, &*root))
+ .map(|elem| elem.clone())
+ })
+ .nth(index as uint)
+ .clone()
+ .map(|elem| Temporary::from_rooted(&elem))
+ }
+ }
+ }
+
+ // http://dom.spec.whatwg.org/#dom-htmlcollection-nameditem
+ fn NamedItem(&self, key: DOMString) -> Option<Temporary<Element>> {
+ // Step 1.
+ if key.is_empty() {
+ return None;
+ }
+
+ // Step 2.
+ match self.collection {
+ Static(ref elems) => elems.iter()
+ .map(|elem| elem.root())
+ .find(|elem| {
+ elem.get_string_attribute("name") == key ||
+ elem.get_string_attribute("id") == key })
+ .map(|maybe_elem| Temporary::from_rooted(&*maybe_elem)),
+ Live(ref root, ref filter) => {
+ let root = root.root();
+ root.deref().traverse_preorder()
+ .filter_map(|node| {
+ let elem: Option<&JSRef<Element>> = ElementCast::to_ref(&node);
+ elem.filtered(|&elem| filter.filter(elem, &*root))
+ .map(|elem| elem.clone())
+ })
+ .find(|elem| {
+ elem.get_string_attribute("name") == key ||
+ elem.get_string_attribute("id") == key })
+ .map(|maybe_elem| Temporary::from_rooted(&maybe_elem))
+ }
+ }
+ }
+
+ fn IndexedGetter(&self, index: u32, found: &mut bool) -> Option<Temporary<Element>> {
+ let maybe_elem = self.Item(index);
+ *found = maybe_elem.is_some();
+ maybe_elem
+ }
+
+ fn NamedGetter(&self, name: DOMString, found: &mut bool) -> Option<Temporary<Element>> {
+ let maybe_elem = self.NamedItem(name);
+ *found = maybe_elem.is_some();
+ maybe_elem
+ }
+}
+
+impl Reflectable for HTMLCollection {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ &self.reflector_
+ }
+}
diff --git a/components/script/dom/htmldataelement.rs b/components/script/dom/htmldataelement.rs
new file mode 100644
index 00000000000..cf35507a824
--- /dev/null
+++ b/components/script/dom/htmldataelement.rs
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLDataElementBinding;
+use dom::bindings::codegen::InheritTypes::HTMLDataElementDerived;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLDataElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, ElementNodeTypeId};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLDataElement {
+ pub htmlelement: HTMLElement
+}
+
+impl HTMLDataElementDerived for EventTarget {
+ fn is_htmldataelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLDataElementTypeId))
+ }
+}
+
+impl HTMLDataElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLDataElement {
+ HTMLDataElement {
+ htmlelement: HTMLElement::new_inherited(HTMLDataElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLDataElement> {
+ let element = HTMLDataElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLDataElementBinding::Wrap)
+ }
+}
+
+impl Reflectable for HTMLDataElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmldatalistelement.rs b/components/script/dom/htmldatalistelement.rs
new file mode 100644
index 00000000000..f7ff70e9858
--- /dev/null
+++ b/components/script/dom/htmldatalistelement.rs
@@ -0,0 +1,62 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLDataListElementBinding;
+use dom::bindings::codegen::Bindings::HTMLDataListElementBinding::HTMLDataListElementMethods;
+use dom::bindings::codegen::InheritTypes::{HTMLDataListElementDerived, HTMLOptionElementDerived};
+use dom::bindings::codegen::InheritTypes::NodeCast;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::{Element, HTMLDataListElementTypeId};
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlcollection::{HTMLCollection, CollectionFilter};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, ElementNodeTypeId, window_from_node};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLDataListElement {
+ pub htmlelement: HTMLElement
+}
+
+impl HTMLDataListElementDerived for EventTarget {
+ fn is_htmldatalistelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLDataListElementTypeId))
+ }
+}
+
+impl HTMLDataListElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLDataListElement {
+ HTMLDataListElement {
+ htmlelement: HTMLElement::new_inherited(HTMLDataListElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLDataListElement> {
+ let element = HTMLDataListElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLDataListElementBinding::Wrap)
+ }
+}
+
+impl<'a> HTMLDataListElementMethods for JSRef<'a, HTMLDataListElement> {
+ fn Options(&self) -> Temporary<HTMLCollection> {
+ struct HTMLDataListOptionsFilter;
+ impl CollectionFilter for HTMLDataListOptionsFilter {
+ fn filter(&self, elem: &JSRef<Element>, _root: &JSRef<Node>) -> bool {
+ elem.is_htmloptionelement()
+ }
+ }
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ let filter = box HTMLDataListOptionsFilter;
+ let window = window_from_node(node).root();
+ HTMLCollection::create(&*window, node, filter)
+ }
+}
+
+impl Reflectable for HTMLDataListElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmldirectoryelement.rs b/components/script/dom/htmldirectoryelement.rs
new file mode 100644
index 00000000000..2539a389e19
--- /dev/null
+++ b/components/script/dom/htmldirectoryelement.rs
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLDirectoryElementBinding;
+use dom::bindings::codegen::InheritTypes::HTMLDirectoryElementDerived;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLDirectoryElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, ElementNodeTypeId};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLDirectoryElement {
+ pub htmlelement: HTMLElement
+}
+
+impl HTMLDirectoryElementDerived for EventTarget {
+ fn is_htmldirectoryelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLDirectoryElementTypeId))
+ }
+}
+
+impl HTMLDirectoryElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLDirectoryElement {
+ HTMLDirectoryElement {
+ htmlelement: HTMLElement::new_inherited(HTMLDirectoryElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLDirectoryElement> {
+ let element = HTMLDirectoryElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLDirectoryElementBinding::Wrap)
+ }
+}
+
+impl Reflectable for HTMLDirectoryElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmldivelement.rs b/components/script/dom/htmldivelement.rs
new file mode 100644
index 00000000000..01319b56f98
--- /dev/null
+++ b/components/script/dom/htmldivelement.rs
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLDivElementBinding;
+use dom::bindings::codegen::InheritTypes::HTMLDivElementDerived;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLDivElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, ElementNodeTypeId};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLDivElement {
+ pub htmlelement: HTMLElement
+}
+
+impl HTMLDivElementDerived for EventTarget {
+ fn is_htmldivelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLDivElementTypeId))
+ }
+}
+
+impl HTMLDivElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLDivElement {
+ HTMLDivElement {
+ htmlelement: HTMLElement::new_inherited(HTMLDivElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLDivElement> {
+ let element = HTMLDivElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLDivElementBinding::Wrap)
+ }
+}
+
+impl Reflectable for HTMLDivElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmldlistelement.rs b/components/script/dom/htmldlistelement.rs
new file mode 100644
index 00000000000..0af66bca2c5
--- /dev/null
+++ b/components/script/dom/htmldlistelement.rs
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLDListElementBinding;
+use dom::bindings::codegen::InheritTypes::HTMLDListElementDerived;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLDListElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, ElementNodeTypeId};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLDListElement {
+ pub htmlelement: HTMLElement
+}
+
+impl HTMLDListElementDerived for EventTarget {
+ fn is_htmldlistelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLDListElementTypeId))
+ }
+}
+
+impl HTMLDListElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLDListElement {
+ HTMLDListElement {
+ htmlelement: HTMLElement::new_inherited(HTMLDListElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLDListElement> {
+ let element = HTMLDListElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLDListElementBinding::Wrap)
+ }
+}
+
+impl Reflectable for HTMLDListElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmlelement.rs b/components/script/dom/htmlelement.rs
new file mode 100644
index 00000000000..076ba5fddce
--- /dev/null
+++ b/components/script/dom/htmlelement.rs
@@ -0,0 +1,120 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
+use dom::bindings::codegen::Bindings::HTMLElementBinding;
+use dom::bindings::codegen::Bindings::HTMLElementBinding::HTMLElementMethods;
+use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
+use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLFrameSetElementDerived};
+use dom::bindings::codegen::InheritTypes::EventTargetCast;
+use dom::bindings::codegen::InheritTypes::{HTMLElementDerived, HTMLBodyElementDerived};
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::{Element, ElementTypeId, HTMLElementTypeId};
+use dom::eventtarget::{EventTarget, EventTargetHelpers, NodeTargetTypeId};
+use dom::node::{Node, ElementNodeTypeId, window_from_node};
+use dom::virtualmethods::VirtualMethods;
+
+use servo_util::atom::Atom;
+use servo_util::namespace;
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLElement {
+ pub element: Element
+}
+
+impl HTMLElementDerived for EventTarget {
+ fn is_htmlelement(&self) -> bool {
+ match self.type_id {
+ NodeTargetTypeId(ElementNodeTypeId(ElementTypeId)) => false,
+ NodeTargetTypeId(ElementNodeTypeId(_)) => true,
+ _ => false
+ }
+ }
+}
+
+impl HTMLElement {
+ pub fn new_inherited(type_id: ElementTypeId, tag_name: DOMString, document: &JSRef<Document>) -> HTMLElement {
+ HTMLElement {
+ element: Element::new_inherited(type_id, tag_name, namespace::HTML, None, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLElement> {
+ let element = HTMLElement::new_inherited(HTMLElementTypeId, localName, document);
+ Node::reflect_node(box element, document, HTMLElementBinding::Wrap)
+ }
+}
+
+trait PrivateHTMLElementHelpers {
+ fn is_body_or_frameset(&self) -> bool;
+}
+
+impl<'a> PrivateHTMLElementHelpers for JSRef<'a, HTMLElement> {
+ fn is_body_or_frameset(&self) -> bool {
+ let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self);
+ eventtarget.is_htmlbodyelement() || eventtarget.is_htmlframesetelement()
+ }
+}
+
+impl<'a> HTMLElementMethods for JSRef<'a, HTMLElement> {
+ fn GetOnclick(&self) -> Option<EventHandlerNonNull> {
+ let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self);
+ eventtarget.get_event_handler_common("click")
+ }
+
+ fn SetOnclick(&self, listener: Option<EventHandlerNonNull>) {
+ let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self);
+ eventtarget.set_event_handler_common("click", listener)
+ }
+
+ fn GetOnload(&self) -> Option<EventHandlerNonNull> {
+ if self.is_body_or_frameset() {
+ let win = window_from_node(self).root();
+ win.deref().GetOnload()
+ } else {
+ None
+ }
+ }
+
+ fn SetOnload(&self, listener: Option<EventHandlerNonNull>) {
+ if self.is_body_or_frameset() {
+ let win = window_from_node(self).root();
+ win.deref().SetOnload(listener)
+ }
+ }
+}
+
+impl<'a> VirtualMethods for JSRef<'a, HTMLElement> {
+ fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods> {
+ let element: &JSRef<Element> = ElementCast::from_ref(self);
+ Some(element as &VirtualMethods)
+ }
+
+ fn after_set_attr(&self, name: &Atom, value: DOMString) {
+ match self.super_type() {
+ Some(ref s) => s.after_set_attr(name, value.clone()),
+ _ => (),
+ }
+
+ if name.as_slice().starts_with("on") {
+ let window = window_from_node(self).root();
+ let (cx, url, reflector) = (window.get_cx(),
+ window.get_url(),
+ window.reflector().get_jsobject());
+ let evtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self);
+ evtarget.set_event_handler_uncompiled(cx, url, reflector,
+ name.as_slice().slice_from(2),
+ value);
+ }
+ }
+}
+
+impl Reflectable for HTMLElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.element.reflector()
+ }
+}
diff --git a/components/script/dom/htmlembedelement.rs b/components/script/dom/htmlembedelement.rs
new file mode 100644
index 00000000000..142e29ab9ef
--- /dev/null
+++ b/components/script/dom/htmlembedelement.rs
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLEmbedElementBinding;
+use dom::bindings::codegen::InheritTypes::HTMLEmbedElementDerived;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLEmbedElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, ElementNodeTypeId};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLEmbedElement {
+ pub htmlelement: HTMLElement
+}
+
+impl HTMLEmbedElementDerived for EventTarget {
+ fn is_htmlembedelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLEmbedElementTypeId))
+ }
+}
+
+impl HTMLEmbedElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLEmbedElement {
+ HTMLEmbedElement {
+ htmlelement: HTMLElement::new_inherited(HTMLEmbedElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLEmbedElement> {
+ let element = HTMLEmbedElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLEmbedElementBinding::Wrap)
+ }
+}
+
+impl Reflectable for HTMLEmbedElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmlfieldsetelement.rs b/components/script/dom/htmlfieldsetelement.rs
new file mode 100644
index 00000000000..3bb30fbf7a7
--- /dev/null
+++ b/components/script/dom/htmlfieldsetelement.rs
@@ -0,0 +1,156 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLFieldSetElementBinding;
+use dom::bindings::codegen::Bindings::HTMLFieldSetElementBinding::HTMLFieldSetElementMethods;
+use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLFieldSetElementDerived, NodeCast};
+use dom::bindings::codegen::InheritTypes::{HTMLElementCast, HTMLLegendElementDerived};
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::{AttributeHandlers, Element, HTMLFieldSetElementTypeId, HTMLButtonElementTypeId};
+use dom::element::{HTMLInputElementTypeId, HTMLSelectElementTypeId, HTMLTextAreaElementTypeId};
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlcollection::{HTMLCollection, CollectionFilter};
+use dom::htmlelement::HTMLElement;
+use dom::node::{DisabledStateHelpers, Node, NodeHelpers, ElementNodeTypeId, window_from_node};
+use dom::validitystate::ValidityState;
+use dom::virtualmethods::VirtualMethods;
+
+use servo_util::atom::Atom;
+use servo_util::str::{DOMString, StaticStringVec};
+
+#[deriving(Encodable)]
+pub struct HTMLFieldSetElement {
+ pub htmlelement: HTMLElement
+}
+
+impl HTMLFieldSetElementDerived for EventTarget {
+ fn is_htmlfieldsetelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLFieldSetElementTypeId))
+ }
+}
+
+impl HTMLFieldSetElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLFieldSetElement {
+ HTMLFieldSetElement {
+ htmlelement: HTMLElement::new_inherited(HTMLFieldSetElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLFieldSetElement> {
+ let element = HTMLFieldSetElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLFieldSetElementBinding::Wrap)
+ }
+}
+
+impl<'a> HTMLFieldSetElementMethods for JSRef<'a, HTMLFieldSetElement> {
+ // http://www.whatwg.org/html/#dom-fieldset-elements
+ fn Elements(&self) -> Temporary<HTMLCollection> {
+ struct ElementsFilter;
+ impl CollectionFilter for ElementsFilter {
+ fn filter(&self, elem: &JSRef<Element>, root: &JSRef<Node>) -> bool {
+ static tag_names: StaticStringVec = &["button", "fieldset", "input",
+ "keygen", "object", "output", "select", "textarea"];
+ let root: &JSRef<Element> = ElementCast::to_ref(root).unwrap();
+ elem != root && tag_names.iter().any(|&tag_name| tag_name == elem.deref().local_name.as_slice())
+ }
+ }
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ let filter = box ElementsFilter;
+ let window = window_from_node(node).root();
+ HTMLCollection::create(&*window, node, filter)
+ }
+
+ fn Validity(&self) -> Temporary<ValidityState> {
+ let window = window_from_node(self).root();
+ ValidityState::new(&*window)
+ }
+
+ // http://www.whatwg.org/html/#dom-fieldset-disabled
+ make_bool_getter!(Disabled)
+
+ // http://www.whatwg.org/html/#dom-fieldset-disabled
+ fn SetDisabled(&self, disabled: bool) {
+ let elem: &JSRef<Element> = ElementCast::from_ref(self);
+ elem.set_bool_attribute("disabled", disabled)
+ }
+}
+
+impl<'a> VirtualMethods for JSRef<'a, HTMLFieldSetElement> {
+ fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods> {
+ let htmlelement: &JSRef<HTMLElement> = HTMLElementCast::from_ref(self);
+ Some(htmlelement as &VirtualMethods)
+ }
+
+ fn after_set_attr(&self, name: &Atom, value: DOMString) {
+ match self.super_type() {
+ Some(ref s) => s.after_set_attr(name, value.clone()),
+ _ => (),
+ }
+
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ match name.as_slice() {
+ "disabled" => {
+ node.set_disabled_state(true);
+ node.set_enabled_state(false);
+ let maybe_legend = node.children().find(|node| node.is_htmllegendelement());
+ let filtered: Vec<JSRef<Node>> = node.children().filter(|child| {
+ maybe_legend.map_or(true, |legend| legend != *child)
+ }).collect();
+ for descendant in filtered.iter().flat_map(|child| child.traverse_preorder()) {
+ match descendant.type_id() {
+ ElementNodeTypeId(HTMLButtonElementTypeId) |
+ ElementNodeTypeId(HTMLInputElementTypeId) |
+ ElementNodeTypeId(HTMLSelectElementTypeId) |
+ ElementNodeTypeId(HTMLTextAreaElementTypeId) => {
+ descendant.set_disabled_state(true);
+ descendant.set_enabled_state(false);
+ },
+ _ => ()
+ }
+ }
+ },
+ _ => ()
+ }
+ }
+
+ fn before_remove_attr(&self, name: &Atom, value: DOMString) {
+ match self.super_type() {
+ Some(ref s) => s.before_remove_attr(name, value),
+ _ => (),
+ }
+
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ match name.as_slice() {
+ "disabled" => {
+ node.set_disabled_state(false);
+ node.set_enabled_state(true);
+ let maybe_legend = node.children().find(|node| node.is_htmllegendelement());
+ let filtered: Vec<JSRef<Node>> = node.children().filter(|child| {
+ maybe_legend.map_or(true, |legend| legend != *child)
+ }).collect();
+ for descendant in filtered.iter().flat_map(|child| child.traverse_preorder()) {
+ match descendant.type_id() {
+ ElementNodeTypeId(HTMLButtonElementTypeId) |
+ ElementNodeTypeId(HTMLInputElementTypeId) |
+ ElementNodeTypeId(HTMLSelectElementTypeId) |
+ ElementNodeTypeId(HTMLTextAreaElementTypeId) => {
+ descendant.check_disabled_attribute();
+ descendant.check_ancestors_disabled_state_for_form_control();
+ },
+ _ => ()
+ }
+ }
+ },
+ _ => ()
+ }
+ }
+}
+
+impl Reflectable for HTMLFieldSetElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmlfontelement.rs b/components/script/dom/htmlfontelement.rs
new file mode 100644
index 00000000000..a26d83fb7d2
--- /dev/null
+++ b/components/script/dom/htmlfontelement.rs
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLFontElementBinding;
+use dom::bindings::codegen::InheritTypes::HTMLFontElementDerived;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLFontElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, ElementNodeTypeId};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLFontElement {
+ pub htmlelement: HTMLElement
+}
+
+impl HTMLFontElementDerived for EventTarget {
+ fn is_htmlfontelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLFontElementTypeId))
+ }
+}
+
+impl HTMLFontElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLFontElement {
+ HTMLFontElement {
+ htmlelement: HTMLElement::new_inherited(HTMLFontElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLFontElement> {
+ let element = HTMLFontElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLFontElementBinding::Wrap)
+ }
+}
+
+impl Reflectable for HTMLFontElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmlformelement.rs b/components/script/dom/htmlformelement.rs
new file mode 100644
index 00000000000..e31179ed424
--- /dev/null
+++ b/components/script/dom/htmlformelement.rs
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLFormElementBinding;
+use dom::bindings::codegen::InheritTypes::HTMLFormElementDerived;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLFormElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, ElementNodeTypeId};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLFormElement {
+ pub htmlelement: HTMLElement
+}
+
+impl HTMLFormElementDerived for EventTarget {
+ fn is_htmlformelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLFormElementTypeId))
+ }
+}
+
+impl HTMLFormElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLFormElement {
+ HTMLFormElement {
+ htmlelement: HTMLElement::new_inherited(HTMLFormElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLFormElement> {
+ let element = HTMLFormElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLFormElementBinding::Wrap)
+ }
+}
+
+impl Reflectable for HTMLFormElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmlframeelement.rs b/components/script/dom/htmlframeelement.rs
new file mode 100644
index 00000000000..dd362a3947a
--- /dev/null
+++ b/components/script/dom/htmlframeelement.rs
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLFrameElementBinding;
+use dom::bindings::codegen::InheritTypes::HTMLFrameElementDerived;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLFrameElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, ElementNodeTypeId};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLFrameElement {
+ pub htmlelement: HTMLElement
+}
+
+impl HTMLFrameElementDerived for EventTarget {
+ fn is_htmlframeelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLFrameElementTypeId))
+ }
+}
+
+impl HTMLFrameElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLFrameElement {
+ HTMLFrameElement {
+ htmlelement: HTMLElement::new_inherited(HTMLFrameElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLFrameElement> {
+ let element = HTMLFrameElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLFrameElementBinding::Wrap)
+ }
+}
+
+impl Reflectable for HTMLFrameElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmlframesetelement.rs b/components/script/dom/htmlframesetelement.rs
new file mode 100644
index 00000000000..ad6168a0613
--- /dev/null
+++ b/components/script/dom/htmlframesetelement.rs
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLFrameSetElementBinding;
+use dom::bindings::codegen::InheritTypes::HTMLFrameSetElementDerived;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLFrameSetElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, ElementNodeTypeId};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLFrameSetElement {
+ pub htmlelement: HTMLElement
+}
+
+impl HTMLFrameSetElementDerived for EventTarget {
+ fn is_htmlframesetelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLFrameSetElementTypeId))
+ }
+}
+
+impl HTMLFrameSetElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLFrameSetElement {
+ HTMLFrameSetElement {
+ htmlelement: HTMLElement::new_inherited(HTMLFrameSetElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLFrameSetElement> {
+ let element = HTMLFrameSetElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLFrameSetElementBinding::Wrap)
+ }
+}
+
+impl Reflectable for HTMLFrameSetElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmlheadelement.rs b/components/script/dom/htmlheadelement.rs
new file mode 100644
index 00000000000..f3738058179
--- /dev/null
+++ b/components/script/dom/htmlheadelement.rs
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLHeadElementBinding;
+use dom::bindings::codegen::InheritTypes::HTMLHeadElementDerived;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLHeadElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, ElementNodeTypeId};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLHeadElement {
+ pub htmlelement: HTMLElement
+}
+
+impl HTMLHeadElementDerived for EventTarget {
+ fn is_htmlheadelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLHeadElementTypeId))
+ }
+}
+
+impl HTMLHeadElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLHeadElement {
+ HTMLHeadElement {
+ htmlelement: HTMLElement::new_inherited(HTMLHeadElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLHeadElement> {
+ let element = HTMLHeadElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLHeadElementBinding::Wrap)
+ }
+}
+
+impl Reflectable for HTMLHeadElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmlheadingelement.rs b/components/script/dom/htmlheadingelement.rs
new file mode 100644
index 00000000000..b869e9764e1
--- /dev/null
+++ b/components/script/dom/htmlheadingelement.rs
@@ -0,0 +1,56 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLHeadingElementBinding;
+use dom::bindings::codegen::InheritTypes::HTMLHeadingElementDerived;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLHeadingElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, ElementNodeTypeId};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub enum HeadingLevel {
+ Heading1,
+ Heading2,
+ Heading3,
+ Heading4,
+ Heading5,
+ Heading6,
+}
+
+#[deriving(Encodable)]
+pub struct HTMLHeadingElement {
+ pub htmlelement: HTMLElement,
+ pub level: HeadingLevel,
+}
+
+impl HTMLHeadingElementDerived for EventTarget {
+ fn is_htmlheadingelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLHeadingElementTypeId))
+ }
+}
+
+impl HTMLHeadingElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>, level: HeadingLevel) -> HTMLHeadingElement {
+ HTMLHeadingElement {
+ htmlelement: HTMLElement::new_inherited(HTMLHeadingElementTypeId, localName, document),
+ level: level,
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>, level: HeadingLevel) -> Temporary<HTMLHeadingElement> {
+ let element = HTMLHeadingElement::new_inherited(localName, document, level);
+ Node::reflect_node(box element, document, HTMLHeadingElementBinding::Wrap)
+ }
+}
+
+impl Reflectable for HTMLHeadingElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmlhrelement.rs b/components/script/dom/htmlhrelement.rs
new file mode 100644
index 00000000000..18a92df2679
--- /dev/null
+++ b/components/script/dom/htmlhrelement.rs
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLHRElementBinding;
+use dom::bindings::codegen::InheritTypes::HTMLHRElementDerived;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLHRElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, ElementNodeTypeId};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLHRElement {
+ pub htmlelement: HTMLElement,
+}
+
+impl HTMLHRElementDerived for EventTarget {
+ fn is_htmlhrelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLHRElementTypeId))
+ }
+}
+
+impl HTMLHRElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLHRElement {
+ HTMLHRElement {
+ htmlelement: HTMLElement::new_inherited(HTMLHRElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLHRElement> {
+ let element = HTMLHRElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLHRElementBinding::Wrap)
+ }
+}
+
+impl Reflectable for HTMLHRElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmlhtmlelement.rs b/components/script/dom/htmlhtmlelement.rs
new file mode 100644
index 00000000000..117cdf78257
--- /dev/null
+++ b/components/script/dom/htmlhtmlelement.rs
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLHtmlElementBinding;
+use dom::bindings::codegen::InheritTypes::HTMLHtmlElementDerived;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLHtmlElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, ElementNodeTypeId};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLHtmlElement {
+ pub htmlelement: HTMLElement
+}
+
+impl HTMLHtmlElementDerived for EventTarget {
+ fn is_htmlhtmlelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLHtmlElementTypeId))
+ }
+}
+
+impl HTMLHtmlElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLHtmlElement {
+ HTMLHtmlElement {
+ htmlelement: HTMLElement::new_inherited(HTMLHtmlElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLHtmlElement> {
+ let element = HTMLHtmlElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLHtmlElementBinding::Wrap)
+ }
+}
+
+impl Reflectable for HTMLHtmlElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmliframeelement.rs b/components/script/dom/htmliframeelement.rs
new file mode 100644
index 00000000000..b2d2c05f728
--- /dev/null
+++ b/components/script/dom/htmliframeelement.rs
@@ -0,0 +1,225 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLIFrameElementBinding;
+use dom::bindings::codegen::Bindings::HTMLIFrameElementBinding::HTMLIFrameElementMethods;
+use dom::bindings::codegen::InheritTypes::{NodeCast, ElementCast};
+use dom::bindings::codegen::InheritTypes::{HTMLElementCast, HTMLIFrameElementDerived};
+use dom::bindings::js::{JSRef, Temporary, OptionalRootable};
+use dom::bindings::trace::Traceable;
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::{HTMLIFrameElementTypeId, Element};
+use dom::element::AttributeHandlers;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, NodeHelpers, ElementNodeTypeId, window_from_node};
+use dom::virtualmethods::VirtualMethods;
+use dom::window::Window;
+use page::IterablePage;
+
+use servo_msg::constellation_msg::{PipelineId, SubpageId};
+use servo_msg::constellation_msg::{IFrameSandboxed, IFrameUnsandboxed};
+use servo_msg::constellation_msg::{ConstellationChan, LoadIframeUrlMsg};
+use servo_util::atom::Atom;
+use servo_util::namespace::Null;
+use servo_util::str::DOMString;
+
+use std::ascii::StrAsciiExt;
+use std::cell::Cell;
+use url::{Url, UrlParser};
+
+enum SandboxAllowance {
+ AllowNothing = 0x00,
+ AllowSameOrigin = 0x01,
+ AllowTopNavigation = 0x02,
+ AllowForms = 0x04,
+ AllowScripts = 0x08,
+ AllowPointerLock = 0x10,
+ AllowPopups = 0x20
+}
+
+#[deriving(Encodable)]
+pub struct HTMLIFrameElement {
+ pub htmlelement: HTMLElement,
+ pub size: Traceable<Cell<Option<IFrameSize>>>,
+ pub sandbox: Traceable<Cell<Option<u8>>>,
+}
+
+impl HTMLIFrameElementDerived for EventTarget {
+ fn is_htmliframeelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLIFrameElementTypeId))
+ }
+}
+
+#[deriving(Encodable)]
+pub struct IFrameSize {
+ pub pipeline_id: PipelineId,
+ pub subpage_id: SubpageId,
+}
+
+pub trait HTMLIFrameElementHelpers {
+ fn is_sandboxed(&self) -> bool;
+ fn get_url(&self) -> Option<Url>;
+ /// http://www.whatwg.org/html/#process-the-iframe-attributes
+ fn process_the_iframe_attributes(&self);
+}
+
+impl<'a> HTMLIFrameElementHelpers for JSRef<'a, HTMLIFrameElement> {
+ fn is_sandboxed(&self) -> bool {
+ self.sandbox.deref().get().is_some()
+ }
+
+ fn get_url(&self) -> Option<Url> {
+ let element: &JSRef<Element> = ElementCast::from_ref(self);
+ element.get_attribute(Null, "src").root().and_then(|src| {
+ let window = window_from_node(self).root();
+ UrlParser::new().base_url(&window.deref().page().get_url())
+ .parse(src.deref().value().as_slice()).ok()
+ })
+ }
+
+ fn process_the_iframe_attributes(&self) {
+ match self.get_url() {
+ Some(url) => {
+ let sandboxed = if self.is_sandboxed() {
+ IFrameSandboxed
+ } else {
+ IFrameUnsandboxed
+ };
+
+ // Subpage Id
+ let window = window_from_node(self).root();
+ let page = window.deref().page();
+ let subpage_id = page.get_next_subpage_id();
+
+ self.deref().size.deref().set(Some(IFrameSize {
+ pipeline_id: page.id,
+ subpage_id: subpage_id,
+ }));
+
+ let ConstellationChan(ref chan) = *page.constellation_chan.deref();
+ chan.send(LoadIframeUrlMsg(url, page.id, subpage_id, sandboxed));
+ }
+ _ => ()
+ }
+ }
+}
+
+impl HTMLIFrameElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLIFrameElement {
+ HTMLIFrameElement {
+ htmlelement: HTMLElement::new_inherited(HTMLIFrameElementTypeId, localName, document),
+ size: Traceable::new(Cell::new(None)),
+ sandbox: Traceable::new(Cell::new(None)),
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLIFrameElement> {
+ let element = HTMLIFrameElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLIFrameElementBinding::Wrap)
+ }
+}
+
+impl<'a> HTMLIFrameElementMethods for JSRef<'a, HTMLIFrameElement> {
+ fn Src(&self) -> DOMString {
+ let element: &JSRef<Element> = ElementCast::from_ref(self);
+ element.get_string_attribute("src")
+ }
+
+ fn SetSrc(&self, src: DOMString) {
+ let element: &JSRef<Element> = ElementCast::from_ref(self);
+ element.set_url_attribute("src", src)
+ }
+
+ fn Sandbox(&self) -> DOMString {
+ let element: &JSRef<Element> = ElementCast::from_ref(self);
+ element.get_string_attribute("sandbox")
+ }
+
+ fn SetSandbox(&self, sandbox: DOMString) {
+ let element: &JSRef<Element> = ElementCast::from_ref(self);
+ element.set_string_attribute("sandbox", sandbox);
+ }
+
+ fn GetContentWindow(&self) -> Option<Temporary<Window>> {
+ self.size.deref().get().and_then(|size| {
+ let window = window_from_node(self).root();
+ let children = &*window.deref().page.children.deref().borrow();
+ let child = children.iter().find(|child| {
+ child.subpage_id.unwrap() == size.subpage_id
+ });
+ child.and_then(|page| {
+ page.frame.deref().borrow().as_ref().map(|frame| {
+ Temporary::new(frame.window.clone())
+ })
+ })
+ })
+ }
+}
+
+impl<'a> VirtualMethods for JSRef<'a, HTMLIFrameElement> {
+ fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods> {
+ let htmlelement: &JSRef<HTMLElement> = HTMLElementCast::from_ref(self);
+ Some(htmlelement as &VirtualMethods)
+ }
+
+ fn after_set_attr(&self, name: &Atom, value: DOMString) {
+ match self.super_type() {
+ Some(ref s) => s.after_set_attr(name, value.clone()),
+ _ => (),
+ }
+
+ if "sandbox" == name.as_slice() {
+ let mut modes = AllowNothing as u8;
+ for word in value.as_slice().split(' ') {
+ modes |= match word.to_ascii_lower().as_slice() {
+ "allow-same-origin" => AllowSameOrigin,
+ "allow-forms" => AllowForms,
+ "allow-pointer-lock" => AllowPointerLock,
+ "allow-popups" => AllowPopups,
+ "allow-scripts" => AllowScripts,
+ "allow-top-navigation" => AllowTopNavigation,
+ _ => AllowNothing
+ } as u8;
+ }
+ self.deref().sandbox.deref().set(Some(modes));
+ }
+
+ if "src" == name.as_slice() {
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ if node.is_in_doc() {
+ self.process_the_iframe_attributes()
+ }
+ }
+ }
+
+ fn before_remove_attr(&self, name: &Atom, value: DOMString) {
+ match self.super_type() {
+ Some(ref s) => s.before_remove_attr(name, value),
+ _ => (),
+ }
+
+ if "sandbox" == name.as_slice() {
+ self.deref().sandbox.deref().set(None);
+ }
+ }
+
+ fn bind_to_tree(&self, tree_in_doc: bool) {
+ match self.super_type() {
+ Some(ref s) => s.bind_to_tree(tree_in_doc),
+ _ => (),
+ }
+
+ if tree_in_doc {
+ self.process_the_iframe_attributes();
+ }
+ }
+}
+
+impl Reflectable for HTMLIFrameElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmlimageelement.rs b/components/script/dom/htmlimageelement.rs
new file mode 100644
index 00000000000..33d7b7dfd31
--- /dev/null
+++ b/components/script/dom/htmlimageelement.rs
@@ -0,0 +1,233 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::attr::AttrValue;
+use dom::bindings::codegen::Bindings::HTMLImageElementBinding;
+use dom::bindings::codegen::Bindings::HTMLImageElementBinding::HTMLImageElementMethods;
+use dom::bindings::codegen::InheritTypes::{NodeCast, ElementCast, HTMLElementCast, HTMLImageElementDerived};
+use dom::bindings::js::{JS, JSRef, Temporary};
+use dom::bindings::trace::Untraceable;
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::{Element, HTMLImageElementTypeId};
+use dom::element::AttributeHandlers;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, ElementNodeTypeId, NodeHelpers, window_from_node};
+use dom::virtualmethods::VirtualMethods;
+use servo_net::image_cache_task;
+use servo_util::atom::Atom;
+use servo_util::geometry::to_px;
+use servo_util::str::DOMString;
+
+use url::{Url, UrlParser};
+
+use std::cell::RefCell;
+
+#[deriving(Encodable)]
+pub struct HTMLImageElement {
+ pub htmlelement: HTMLElement,
+ image: Untraceable<RefCell<Option<Url>>>,
+}
+
+impl HTMLImageElementDerived for EventTarget {
+ fn is_htmlimageelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLImageElementTypeId))
+ }
+}
+
+trait PrivateHTMLImageElementHelpers {
+ fn update_image(&self, value: Option<(DOMString, &Url)>);
+}
+
+impl<'a> PrivateHTMLImageElementHelpers for JSRef<'a, HTMLImageElement> {
+ /// Makes the local `image` member match the status of the `src` attribute and starts
+ /// prefetching the image. This method must be called after `src` is changed.
+ fn update_image(&self, value: Option<(DOMString, &Url)>) {
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ let document = node.owner_doc().root();
+ let window = document.deref().window.root();
+ let image_cache = &window.image_cache_task;
+ match value {
+ None => {
+ *self.image.deref().borrow_mut() = None;
+ }
+ Some((src, base_url)) => {
+ let img_url = UrlParser::new().base_url(base_url).parse(src.as_slice());
+ // FIXME: handle URL parse errors more gracefully.
+ let img_url = img_url.unwrap();
+ *self.image.deref().borrow_mut() = Some(img_url.clone());
+
+ // inform the image cache to load this, but don't store a
+ // handle.
+ //
+ // TODO (Issue #84): don't prefetch if we are within a
+ // <noscript> tag.
+ image_cache.send(image_cache_task::Prefetch(img_url));
+ }
+ }
+ }
+}
+
+impl HTMLImageElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLImageElement {
+ HTMLImageElement {
+ htmlelement: HTMLElement::new_inherited(HTMLImageElementTypeId, localName, document),
+ image: Untraceable::new(RefCell::new(None)),
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLImageElement> {
+ let element = HTMLImageElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLImageElementBinding::Wrap)
+ }
+}
+
+pub trait LayoutHTMLImageElementHelpers {
+ unsafe fn image(&self) -> Option<Url>;
+}
+
+impl LayoutHTMLImageElementHelpers for JS<HTMLImageElement> {
+ unsafe fn image(&self) -> Option<Url> {
+ (*self.unsafe_get()).image.borrow().clone()
+ }
+}
+
+impl<'a> HTMLImageElementMethods for JSRef<'a, HTMLImageElement> {
+ make_getter!(Alt)
+
+ fn SetAlt(&self, alt: DOMString) {
+ let element: &JSRef<Element> = ElementCast::from_ref(self);
+ element.set_string_attribute("alt", alt)
+ }
+
+ make_getter!(Src)
+
+ fn SetSrc(&self, src: DOMString) {
+ let element: &JSRef<Element> = ElementCast::from_ref(self);
+ element.set_url_attribute("src", src)
+ }
+
+ make_getter!(UseMap)
+
+ fn SetUseMap(&self, use_map: DOMString) {
+ let element: &JSRef<Element> = ElementCast::from_ref(self);
+ element.set_string_attribute("usemap", use_map)
+ }
+
+ make_bool_getter!(IsMap)
+
+ fn SetIsMap(&self, is_map: bool) {
+ let element: &JSRef<Element> = ElementCast::from_ref(self);
+ element.set_string_attribute("ismap", is_map.to_string())
+ }
+
+ fn Width(&self) -> u32 {
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ let rect = node.get_bounding_content_box();
+ to_px(rect.size.width) as u32
+ }
+
+ fn SetWidth(&self, width: u32) {
+ let elem: &JSRef<Element> = ElementCast::from_ref(self);
+ elem.set_uint_attribute("width", width)
+ }
+
+ fn Height(&self) -> u32 {
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ let rect = node.get_bounding_content_box();
+ to_px(rect.size.height) as u32
+ }
+
+ fn SetHeight(&self, height: u32) {
+ let elem: &JSRef<Element> = ElementCast::from_ref(self);
+ elem.set_uint_attribute("height", height)
+ }
+
+ make_getter!(Name)
+
+ fn SetName(&self, name: DOMString) {
+ let element: &JSRef<Element> = ElementCast::from_ref(self);
+ element.set_string_attribute("name", name)
+ }
+
+ make_getter!(Align)
+
+ fn SetAlign(&self, align: DOMString) {
+ let element: &JSRef<Element> = ElementCast::from_ref(self);
+ element.set_string_attribute("align", align)
+ }
+
+ make_uint_getter!(Hspace)
+
+ fn SetHspace(&self, hspace: u32) {
+ let element: &JSRef<Element> = ElementCast::from_ref(self);
+ element.set_uint_attribute("hspace", hspace)
+ }
+
+ make_uint_getter!(Vspace)
+
+ fn SetVspace(&self, vspace: u32) {
+ let element: &JSRef<Element> = ElementCast::from_ref(self);
+ element.set_uint_attribute("vspace", vspace)
+ }
+
+ make_getter!(LongDesc)
+
+ fn SetLongDesc(&self, longdesc: DOMString) {
+ let element: &JSRef<Element> = ElementCast::from_ref(self);
+ element.set_string_attribute("longdesc", longdesc)
+ }
+
+ make_getter!(Border)
+
+ fn SetBorder(&self, border: DOMString) {
+ let element: &JSRef<Element> = ElementCast::from_ref(self);
+ element.set_string_attribute("border", border)
+ }
+}
+
+impl<'a> VirtualMethods for JSRef<'a, HTMLImageElement> {
+ fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods> {
+ let htmlelement: &JSRef<HTMLElement> = HTMLElementCast::from_ref(self);
+ Some(htmlelement as &VirtualMethods)
+ }
+
+ fn after_set_attr(&self, name: &Atom, value: DOMString) {
+ match self.super_type() {
+ Some(ref s) => s.after_set_attr(name, value.clone()),
+ _ => (),
+ }
+
+ if "src" == name.as_slice() {
+ let window = window_from_node(self).root();
+ let url = window.deref().get_url();
+ self.update_image(Some((value, &url)));
+ }
+ }
+
+ fn before_remove_attr(&self, name: &Atom, value: DOMString) {
+ match self.super_type() {
+ Some(ref s) => s.before_remove_attr(name, value.clone()),
+ _ => (),
+ }
+
+ if "src" == name.as_slice() {
+ self.update_image(None);
+ }
+ }
+
+ fn parse_plain_attribute(&self, name: &str, value: DOMString) -> AttrValue {
+ match name {
+ "width" | "height" | "hspace" | "vspace" => AttrValue::from_u32(value, 0),
+ _ => self.super_type().unwrap().parse_plain_attribute(name, value),
+ }
+ }
+}
+
+impl Reflectable for HTMLImageElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs
new file mode 100644
index 00000000000..38d63acc7da
--- /dev/null
+++ b/components/script/dom/htmlinputelement.rs
@@ -0,0 +1,124 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLInputElementBinding;
+use dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementMethods;
+use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast, NodeCast};
+use dom::bindings::codegen::InheritTypes::{HTMLInputElementDerived, HTMLFieldSetElementDerived};
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::{AttributeHandlers, Element, HTMLInputElementTypeId};
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{DisabledStateHelpers, Node, NodeHelpers, ElementNodeTypeId};
+use dom::virtualmethods::VirtualMethods;
+
+use servo_util::atom::Atom;
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLInputElement {
+ pub htmlelement: HTMLElement,
+}
+
+impl HTMLInputElementDerived for EventTarget {
+ fn is_htmlinputelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLInputElementTypeId))
+ }
+}
+
+impl HTMLInputElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLInputElement {
+ HTMLInputElement {
+ htmlelement: HTMLElement::new_inherited(HTMLInputElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLInputElement> {
+ let element = HTMLInputElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLInputElementBinding::Wrap)
+ }
+}
+
+impl<'a> HTMLInputElementMethods for JSRef<'a, HTMLInputElement> {
+ // http://www.whatwg.org/html/#dom-fe-disabled
+ make_bool_getter!(Disabled)
+
+ // http://www.whatwg.org/html/#dom-fe-disabled
+ fn SetDisabled(&self, disabled: bool) {
+ let elem: &JSRef<Element> = ElementCast::from_ref(self);
+ elem.set_bool_attribute("disabled", disabled)
+ }
+}
+
+impl<'a> VirtualMethods for JSRef<'a, HTMLInputElement> {
+ fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods> {
+ let htmlelement: &JSRef<HTMLElement> = HTMLElementCast::from_ref(self);
+ Some(htmlelement as &VirtualMethods)
+ }
+
+ fn after_set_attr(&self, name: &Atom, value: DOMString) {
+ match self.super_type() {
+ Some(ref s) => s.after_set_attr(name, value.clone()),
+ _ => (),
+ }
+
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ match name.as_slice() {
+ "disabled" => {
+ node.set_disabled_state(true);
+ node.set_enabled_state(false);
+ },
+ _ => ()
+ }
+ }
+
+ fn before_remove_attr(&self, name: &Atom, value: DOMString) {
+ match self.super_type() {
+ Some(ref s) => s.before_remove_attr(name, value),
+ _ => (),
+ }
+
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ match name.as_slice() {
+ "disabled" => {
+ node.set_disabled_state(false);
+ node.set_enabled_state(true);
+ node.check_ancestors_disabled_state_for_form_control();
+ },
+ _ => ()
+ }
+ }
+
+ fn bind_to_tree(&self, tree_in_doc: bool) {
+ match self.super_type() {
+ Some(ref s) => s.bind_to_tree(tree_in_doc),
+ _ => (),
+ }
+
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ node.check_ancestors_disabled_state_for_form_control();
+ }
+
+ fn unbind_from_tree(&self, tree_in_doc: bool) {
+ match self.super_type() {
+ Some(ref s) => s.unbind_from_tree(tree_in_doc),
+ _ => (),
+ }
+
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ if node.ancestors().any(|ancestor| ancestor.is_htmlfieldsetelement()) {
+ node.check_ancestors_disabled_state_for_form_control();
+ } else {
+ node.check_disabled_attribute();
+ }
+ }
+}
+
+impl Reflectable for HTMLInputElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmllabelelement.rs b/components/script/dom/htmllabelelement.rs
new file mode 100644
index 00000000000..54349aa5bf5
--- /dev/null
+++ b/components/script/dom/htmllabelelement.rs
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLLabelElementBinding;
+use dom::bindings::codegen::InheritTypes::HTMLLabelElementDerived;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLLabelElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, ElementNodeTypeId};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLLabelElement {
+ pub htmlelement: HTMLElement,
+}
+
+impl HTMLLabelElementDerived for EventTarget {
+ fn is_htmllabelelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLLabelElementTypeId))
+ }
+}
+
+impl HTMLLabelElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLLabelElement {
+ HTMLLabelElement {
+ htmlelement: HTMLElement::new_inherited(HTMLLabelElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLLabelElement> {
+ let element = HTMLLabelElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLLabelElementBinding::Wrap)
+ }
+}
+
+impl Reflectable for HTMLLabelElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmllegendelement.rs b/components/script/dom/htmllegendelement.rs
new file mode 100644
index 00000000000..168f94bc27e
--- /dev/null
+++ b/components/script/dom/htmllegendelement.rs
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLLegendElementBinding;
+use dom::bindings::codegen::InheritTypes::HTMLLegendElementDerived;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLLegendElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, ElementNodeTypeId};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLLegendElement {
+ pub htmlelement: HTMLElement,
+}
+
+impl HTMLLegendElementDerived for EventTarget {
+ fn is_htmllegendelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLLegendElementTypeId))
+ }
+}
+
+impl HTMLLegendElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLLegendElement {
+ HTMLLegendElement {
+ htmlelement: HTMLElement::new_inherited(HTMLLegendElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLLegendElement> {
+ let element = HTMLLegendElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLLegendElementBinding::Wrap)
+ }
+}
+
+impl Reflectable for HTMLLegendElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmllielement.rs b/components/script/dom/htmllielement.rs
new file mode 100644
index 00000000000..5d15d405d94
--- /dev/null
+++ b/components/script/dom/htmllielement.rs
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLLIElementBinding;
+use dom::bindings::codegen::InheritTypes::HTMLLIElementDerived;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLLIElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, ElementNodeTypeId};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLLIElement {
+ pub htmlelement: HTMLElement,
+}
+
+impl HTMLLIElementDerived for EventTarget {
+ fn is_htmllielement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLLIElementTypeId))
+ }
+}
+
+impl HTMLLIElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLLIElement {
+ HTMLLIElement {
+ htmlelement: HTMLElement::new_inherited(HTMLLIElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLLIElement> {
+ let element = HTMLLIElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLLIElementBinding::Wrap)
+ }
+}
+
+impl Reflectable for HTMLLIElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmllinkelement.rs b/components/script/dom/htmllinkelement.rs
new file mode 100644
index 00000000000..fae89c1c520
--- /dev/null
+++ b/components/script/dom/htmllinkelement.rs
@@ -0,0 +1,81 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLLinkElementBinding;
+use dom::bindings::codegen::InheritTypes::HTMLLinkElementDerived;
+use dom::bindings::codegen::InheritTypes::{HTMLElementCast, NodeCast};
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLLinkElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, NodeHelpers, ElementNodeTypeId};
+use dom::virtualmethods::VirtualMethods;
+
+use servo_util::atom::Atom;
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLLinkElement {
+ pub htmlelement: HTMLElement,
+}
+
+impl HTMLLinkElementDerived for EventTarget {
+ fn is_htmllinkelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLLinkElementTypeId))
+ }
+}
+
+impl HTMLLinkElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLLinkElement {
+ HTMLLinkElement {
+ htmlelement: HTMLElement::new_inherited(HTMLLinkElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLLinkElement> {
+ let element = HTMLLinkElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLLinkElementBinding::Wrap)
+ }
+}
+
+impl<'a> VirtualMethods for JSRef<'a, HTMLLinkElement> {
+ fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods> {
+ let htmlelement: &JSRef<HTMLElement> = HTMLElementCast::from_ref(self);
+ Some(htmlelement as &VirtualMethods)
+ }
+
+ fn after_set_attr(&self, name: &Atom, value: DOMString) {
+ match self.super_type() {
+ Some(ref s) => s.after_set_attr(name, value.clone()),
+ _ => (),
+ }
+
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ match name.as_slice() {
+ "href" => node.set_enabled_state(true),
+ _ => ()
+ }
+ }
+
+ fn before_remove_attr(&self, name: &Atom, value: DOMString) {
+ match self.super_type() {
+ Some(ref s) => s.before_remove_attr(name, value.clone()),
+ _ => (),
+ }
+
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ match name.as_slice() {
+ "href" => node.set_enabled_state(false),
+ _ => ()
+ }
+ }
+}
+
+impl Reflectable for HTMLLinkElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmlmapelement.rs b/components/script/dom/htmlmapelement.rs
new file mode 100644
index 00000000000..21e9a04364f
--- /dev/null
+++ b/components/script/dom/htmlmapelement.rs
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLMapElementBinding;
+use dom::bindings::codegen::InheritTypes::HTMLMapElementDerived;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLMapElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, ElementNodeTypeId};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLMapElement {
+ pub htmlelement: HTMLElement
+}
+
+impl HTMLMapElementDerived for EventTarget {
+ fn is_htmlmapelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLMapElementTypeId))
+ }
+}
+
+impl HTMLMapElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLMapElement {
+ HTMLMapElement {
+ htmlelement: HTMLElement::new_inherited(HTMLMapElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLMapElement> {
+ let element = HTMLMapElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLMapElementBinding::Wrap)
+ }
+}
+
+impl Reflectable for HTMLMapElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmlmediaelement.rs b/components/script/dom/htmlmediaelement.rs
new file mode 100644
index 00000000000..b9e1ad7782d
--- /dev/null
+++ b/components/script/dom/htmlmediaelement.rs
@@ -0,0 +1,42 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::js::{JSRef};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::bindings::codegen::InheritTypes::HTMLMediaElementDerived;
+use dom::document::Document;
+use dom::element::{ElementTypeId, HTMLAudioElementTypeId, HTMLVideoElementTypeId};
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::ElementNodeTypeId;
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLMediaElement {
+ pub htmlelement: HTMLElement,
+}
+
+impl HTMLMediaElementDerived for EventTarget {
+ fn is_htmlmediaelement(&self) -> bool {
+ match self.type_id {
+ NodeTargetTypeId(ElementNodeTypeId(HTMLVideoElementTypeId)) |
+ NodeTargetTypeId(ElementNodeTypeId(HTMLAudioElementTypeId)) => true,
+ _ => false
+ }
+ }
+}
+
+impl HTMLMediaElement {
+ pub fn new_inherited(type_id: ElementTypeId, tag_name: DOMString, document: &JSRef<Document>) -> HTMLMediaElement {
+ HTMLMediaElement {
+ htmlelement: HTMLElement::new_inherited(type_id, tag_name, document)
+ }
+ }
+}
+
+impl Reflectable for HTMLMediaElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmlmetaelement.rs b/components/script/dom/htmlmetaelement.rs
new file mode 100644
index 00000000000..224d400a216
--- /dev/null
+++ b/components/script/dom/htmlmetaelement.rs
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLMetaElementBinding;
+use dom::bindings::codegen::InheritTypes::HTMLMetaElementDerived;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLMetaElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, ElementNodeTypeId};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLMetaElement {
+ pub htmlelement: HTMLElement,
+}
+
+impl HTMLMetaElementDerived for EventTarget {
+ fn is_htmlmetaelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLMetaElementTypeId))
+ }
+}
+
+impl HTMLMetaElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLMetaElement {
+ HTMLMetaElement {
+ htmlelement: HTMLElement::new_inherited(HTMLMetaElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLMetaElement> {
+ let element = HTMLMetaElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLMetaElementBinding::Wrap)
+ }
+}
+
+impl Reflectable for HTMLMetaElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmlmeterelement.rs b/components/script/dom/htmlmeterelement.rs
new file mode 100644
index 00000000000..a0eeff8cb92
--- /dev/null
+++ b/components/script/dom/htmlmeterelement.rs
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLMeterElementBinding;
+use dom::bindings::codegen::InheritTypes::HTMLMeterElementDerived;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLMeterElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, ElementNodeTypeId};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLMeterElement {
+ pub htmlelement: HTMLElement
+}
+
+impl HTMLMeterElementDerived for EventTarget {
+ fn is_htmlmeterelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLMeterElementTypeId))
+ }
+}
+
+impl HTMLMeterElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLMeterElement {
+ HTMLMeterElement {
+ htmlelement: HTMLElement::new_inherited(HTMLMeterElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLMeterElement> {
+ let element = HTMLMeterElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLMeterElementBinding::Wrap)
+ }
+}
+
+impl Reflectable for HTMLMeterElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmlmodelement.rs b/components/script/dom/htmlmodelement.rs
new file mode 100644
index 00000000000..6ea001f5185
--- /dev/null
+++ b/components/script/dom/htmlmodelement.rs
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLModElementBinding;
+use dom::bindings::codegen::InheritTypes::HTMLModElementDerived;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLModElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, ElementNodeTypeId};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLModElement {
+ pub htmlelement: HTMLElement
+}
+
+impl HTMLModElementDerived for EventTarget {
+ fn is_htmlmodelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLModElementTypeId))
+ }
+}
+
+impl HTMLModElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLModElement {
+ HTMLModElement {
+ htmlelement: HTMLElement::new_inherited(HTMLModElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLModElement> {
+ let element = HTMLModElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLModElementBinding::Wrap)
+ }
+}
+
+impl Reflectable for HTMLModElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmlobjectelement.rs b/components/script/dom/htmlobjectelement.rs
new file mode 100644
index 00000000000..94c87719563
--- /dev/null
+++ b/components/script/dom/htmlobjectelement.rs
@@ -0,0 +1,113 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
+use dom::bindings::codegen::Bindings::HTMLObjectElementBinding;
+use dom::bindings::codegen::Bindings::HTMLObjectElementBinding::HTMLObjectElementMethods;
+use dom::bindings::codegen::InheritTypes::HTMLObjectElementDerived;
+use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast};
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::{Element, HTMLObjectElementTypeId};
+use dom::element::AttributeHandlers;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, ElementNodeTypeId, NodeHelpers, window_from_node};
+use dom::validitystate::ValidityState;
+use dom::virtualmethods::VirtualMethods;
+
+use servo_net::image_cache_task;
+use servo_net::image_cache_task::ImageCacheTask;
+use servo_util::atom::Atom;
+use servo_util::namespace::Null;
+use servo_util::str::DOMString;
+
+use url::Url;
+
+#[deriving(Encodable)]
+pub struct HTMLObjectElement {
+ pub htmlelement: HTMLElement,
+}
+
+impl HTMLObjectElementDerived for EventTarget {
+ fn is_htmlobjectelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLObjectElementTypeId))
+ }
+}
+
+impl HTMLObjectElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLObjectElement {
+ HTMLObjectElement {
+ htmlelement: HTMLElement::new_inherited(HTMLObjectElementTypeId, localName, document),
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLObjectElement> {
+ let element = HTMLObjectElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLObjectElementBinding::Wrap)
+ }
+}
+
+trait ProcessDataURL {
+ fn process_data_url(&self, image_cache: ImageCacheTask);
+}
+
+impl<'a> ProcessDataURL for JSRef<'a, HTMLObjectElement> {
+ // Makes the local `data` member match the status of the `data` attribute and starts
+ /// prefetching the image. This method must be called after `data` is changed.
+ fn process_data_url(&self, image_cache: ImageCacheTask) {
+ let elem: &JSRef<Element> = ElementCast::from_ref(self);
+
+ // TODO: support other values
+ match (elem.get_attribute(Null, "type").map(|x| x.root().Value()),
+ elem.get_attribute(Null, "data").map(|x| x.root().Value())) {
+ (None, Some(uri)) => {
+ if is_image_data(uri.as_slice()) {
+ let data_url = Url::parse(uri.as_slice()).unwrap();
+ // Issue #84
+ image_cache.send(image_cache_task::Prefetch(data_url));
+ }
+ }
+ _ => { }
+ }
+ }
+}
+
+pub fn is_image_data(uri: &str) -> bool {
+ static types: &'static [&'static str] = &["data:image/png", "data:image/gif", "data:image/jpeg"];
+ types.iter().any(|&type_| uri.starts_with(type_))
+}
+
+impl<'a> HTMLObjectElementMethods for JSRef<'a, HTMLObjectElement> {
+ fn Validity(&self) -> Temporary<ValidityState> {
+ let window = window_from_node(self).root();
+ ValidityState::new(&*window)
+ }
+}
+
+impl<'a> VirtualMethods for JSRef<'a, HTMLObjectElement> {
+ fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods> {
+ let htmlelement: &JSRef<HTMLElement> = HTMLElementCast::from_ref(self);
+ Some(htmlelement as &VirtualMethods)
+ }
+
+ fn after_set_attr(&self, name: &Atom, value: DOMString) {
+ match self.super_type() {
+ Some(ref s) => s.after_set_attr(name, value),
+ _ => (),
+ }
+
+ if "data" == name.as_slice() {
+ let window = window_from_node(self).root();
+ self.process_data_url(window.deref().image_cache_task.clone());
+ }
+ }
+}
+
+impl Reflectable for HTMLObjectElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmlolistelement.rs b/components/script/dom/htmlolistelement.rs
new file mode 100644
index 00000000000..11637fe3bd5
--- /dev/null
+++ b/components/script/dom/htmlolistelement.rs
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLOListElementBinding;
+use dom::bindings::codegen::InheritTypes::HTMLOListElementDerived;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLOListElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, ElementNodeTypeId};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLOListElement {
+ pub htmlelement: HTMLElement,
+}
+
+impl HTMLOListElementDerived for EventTarget {
+ fn is_htmlolistelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLOListElementTypeId))
+ }
+}
+
+impl HTMLOListElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLOListElement {
+ HTMLOListElement {
+ htmlelement: HTMLElement::new_inherited(HTMLOListElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLOListElement> {
+ let element = HTMLOListElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLOListElementBinding::Wrap)
+ }
+}
+
+impl Reflectable for HTMLOListElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmloptgroupelement.rs b/components/script/dom/htmloptgroupelement.rs
new file mode 100644
index 00000000000..6e90cb45fe5
--- /dev/null
+++ b/components/script/dom/htmloptgroupelement.rs
@@ -0,0 +1,106 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLOptGroupElementBinding;
+use dom::bindings::codegen::Bindings::HTMLOptGroupElementBinding::HTMLOptGroupElementMethods;
+use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast, NodeCast};
+use dom::bindings::codegen::InheritTypes::{HTMLOptGroupElementDerived, HTMLOptionElementDerived};
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::{AttributeHandlers, Element, HTMLOptGroupElementTypeId};
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{DisabledStateHelpers, Node, NodeHelpers, ElementNodeTypeId};
+use dom::virtualmethods::VirtualMethods;
+
+use servo_util::atom::Atom;
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLOptGroupElement {
+ pub htmlelement: HTMLElement
+}
+
+impl HTMLOptGroupElementDerived for EventTarget {
+ fn is_htmloptgroupelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLOptGroupElementTypeId))
+ }
+}
+
+impl HTMLOptGroupElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLOptGroupElement {
+ HTMLOptGroupElement {
+ htmlelement: HTMLElement::new_inherited(HTMLOptGroupElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLOptGroupElement> {
+ let element = HTMLOptGroupElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLOptGroupElementBinding::Wrap)
+ }
+}
+
+impl<'a> HTMLOptGroupElementMethods for JSRef<'a, HTMLOptGroupElement> {
+ // http://www.whatwg.org/html#dom-optgroup-disabled
+ make_bool_getter!(Disabled)
+
+ // http://www.whatwg.org/html#dom-optgroup-disabled
+ fn SetDisabled(&self, disabled: bool) {
+ let elem: &JSRef<Element> = ElementCast::from_ref(self);
+ elem.set_bool_attribute("disabled", disabled)
+ }
+}
+
+impl<'a> VirtualMethods for JSRef<'a, HTMLOptGroupElement> {
+ fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods> {
+ let htmlelement: &JSRef<HTMLElement> = HTMLElementCast::from_ref(self);
+ Some(htmlelement as &VirtualMethods)
+ }
+
+ fn after_set_attr(&self, name: &Atom, value: DOMString) {
+ match self.super_type() {
+ Some(ref s) => s.after_set_attr(name, value.clone()),
+ _ => (),
+ }
+
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ match name.as_slice() {
+ "disabled" => {
+ node.set_disabled_state(true);
+ node.set_enabled_state(false);
+ for child in node.children().filter(|child| child.is_htmloptionelement()) {
+ child.set_disabled_state(true);
+ child.set_enabled_state(false);
+ }
+ },
+ _ => ()
+ }
+ }
+
+ fn before_remove_attr(&self, name: &Atom, value: DOMString) {
+ match self.super_type() {
+ Some(ref s) => s.before_remove_attr(name, value),
+ _ => (),
+ }
+
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ match name.as_slice() {
+ "disabled" => {
+ node.set_disabled_state(false);
+ node.set_enabled_state(true);
+ for child in node.children().filter(|child| child.is_htmloptionelement()) {
+ child.check_disabled_attribute();
+ }
+ },
+ _ => ()
+ }
+ }
+}
+
+impl Reflectable for HTMLOptGroupElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmloptionelement.rs b/components/script/dom/htmloptionelement.rs
new file mode 100644
index 00000000000..d066784b285
--- /dev/null
+++ b/components/script/dom/htmloptionelement.rs
@@ -0,0 +1,124 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLOptionElementBinding;
+use dom::bindings::codegen::Bindings::HTMLOptionElementBinding::HTMLOptionElementMethods;
+use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast, NodeCast};
+use dom::bindings::codegen::InheritTypes::HTMLOptionElementDerived;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::{AttributeHandlers, Element, HTMLOptionElementTypeId};
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{DisabledStateHelpers, Node, NodeHelpers, ElementNodeTypeId};
+use dom::virtualmethods::VirtualMethods;
+
+use servo_util::atom::Atom;
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLOptionElement {
+ pub htmlelement: HTMLElement
+}
+
+impl HTMLOptionElementDerived for EventTarget {
+ fn is_htmloptionelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLOptionElementTypeId))
+ }
+}
+
+impl HTMLOptionElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLOptionElement {
+ HTMLOptionElement {
+ htmlelement: HTMLElement::new_inherited(HTMLOptionElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLOptionElement> {
+ let element = HTMLOptionElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLOptionElementBinding::Wrap)
+ }
+}
+
+impl<'a> HTMLOptionElementMethods for JSRef<'a, HTMLOptionElement> {
+ // http://www.whatwg.org/html/#dom-option-disabled
+ make_bool_getter!(Disabled)
+
+ // http://www.whatwg.org/html/#dom-option-disabled
+ fn SetDisabled(&self, disabled: bool) {
+ let elem: &JSRef<Element> = ElementCast::from_ref(self);
+ elem.set_bool_attribute("disabled", disabled)
+ }
+}
+
+impl<'a> VirtualMethods for JSRef<'a, HTMLOptionElement> {
+ fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods> {
+ let htmlelement: &JSRef<HTMLElement> = HTMLElementCast::from_ref(self);
+ Some(htmlelement as &VirtualMethods)
+ }
+
+ fn after_set_attr(&self, name: &Atom, value: DOMString) {
+ match self.super_type() {
+ Some(ref s) => s.after_set_attr(name, value.clone()),
+ _ => (),
+ }
+
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ match name.as_slice() {
+ "disabled" => {
+ node.set_disabled_state(true);
+ node.set_enabled_state(false);
+ },
+ _ => ()
+ }
+ }
+
+ fn before_remove_attr(&self, name: &Atom, value: DOMString) {
+ match self.super_type() {
+ Some(ref s) => s.before_remove_attr(name, value),
+ _ => (),
+ }
+
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ match name.as_slice() {
+ "disabled" => {
+ node.set_disabled_state(false);
+ node.set_enabled_state(true);
+ node.check_parent_disabled_state_for_option();
+ },
+ _ => ()
+ }
+ }
+
+ fn bind_to_tree(&self, tree_in_doc: bool) {
+ match self.super_type() {
+ Some(ref s) => s.bind_to_tree(tree_in_doc),
+ _ => (),
+ }
+
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ node.check_parent_disabled_state_for_option();
+ }
+
+ fn unbind_from_tree(&self, tree_in_doc: bool) {
+ match self.super_type() {
+ Some(ref s) => s.unbind_from_tree(tree_in_doc),
+ _ => (),
+ }
+
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ if node.parent_node().is_some() {
+ node.check_parent_disabled_state_for_option();
+ } else {
+ node.check_disabled_attribute();
+ }
+ }
+}
+
+impl Reflectable for HTMLOptionElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmloutputelement.rs b/components/script/dom/htmloutputelement.rs
new file mode 100644
index 00000000000..19926cfe4fc
--- /dev/null
+++ b/components/script/dom/htmloutputelement.rs
@@ -0,0 +1,53 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLOutputElementBinding;
+use dom::bindings::codegen::Bindings::HTMLOutputElementBinding::HTMLOutputElementMethods;
+use dom::bindings::codegen::InheritTypes::HTMLOutputElementDerived;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLOutputElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, ElementNodeTypeId, window_from_node};
+use dom::validitystate::ValidityState;
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLOutputElement {
+ pub htmlelement: HTMLElement
+}
+
+impl HTMLOutputElementDerived for EventTarget {
+ fn is_htmloutputelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLOutputElementTypeId))
+ }
+}
+
+impl HTMLOutputElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLOutputElement {
+ HTMLOutputElement {
+ htmlelement: HTMLElement::new_inherited(HTMLOutputElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLOutputElement> {
+ let element = HTMLOutputElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLOutputElementBinding::Wrap)
+ }
+}
+
+impl<'a> HTMLOutputElementMethods for JSRef<'a, HTMLOutputElement> {
+ fn Validity(&self) -> Temporary<ValidityState> {
+ let window = window_from_node(self).root();
+ ValidityState::new(&*window)
+ }
+}
+
+impl Reflectable for HTMLOutputElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmlparagraphelement.rs b/components/script/dom/htmlparagraphelement.rs
new file mode 100644
index 00000000000..fe4dd4317cf
--- /dev/null
+++ b/components/script/dom/htmlparagraphelement.rs
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLParagraphElementBinding;
+use dom::bindings::codegen::InheritTypes::HTMLParagraphElementDerived;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLParagraphElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, ElementNodeTypeId};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLParagraphElement {
+ pub htmlelement: HTMLElement
+}
+
+impl HTMLParagraphElementDerived for EventTarget {
+ fn is_htmlparagraphelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLParagraphElementTypeId))
+ }
+}
+
+impl HTMLParagraphElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLParagraphElement {
+ HTMLParagraphElement {
+ htmlelement: HTMLElement::new_inherited(HTMLParagraphElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLParagraphElement> {
+ let element = HTMLParagraphElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLParagraphElementBinding::Wrap)
+ }
+}
+
+impl Reflectable for HTMLParagraphElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmlparamelement.rs b/components/script/dom/htmlparamelement.rs
new file mode 100644
index 00000000000..0f181bd1b32
--- /dev/null
+++ b/components/script/dom/htmlparamelement.rs
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLParamElementBinding;
+use dom::bindings::codegen::InheritTypes::HTMLParamElementDerived;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLParamElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, ElementNodeTypeId};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLParamElement {
+ pub htmlelement: HTMLElement
+}
+
+impl HTMLParamElementDerived for EventTarget {
+ fn is_htmlparamelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLParamElementTypeId))
+ }
+}
+
+impl HTMLParamElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLParamElement {
+ HTMLParamElement {
+ htmlelement: HTMLElement::new_inherited(HTMLParamElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLParamElement> {
+ let element = HTMLParamElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLParamElementBinding::Wrap)
+ }
+}
+
+impl Reflectable for HTMLParamElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmlpreelement.rs b/components/script/dom/htmlpreelement.rs
new file mode 100644
index 00000000000..25f9c75bc15
--- /dev/null
+++ b/components/script/dom/htmlpreelement.rs
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLPreElementBinding;
+use dom::bindings::codegen::InheritTypes::HTMLPreElementDerived;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLPreElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, ElementNodeTypeId};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLPreElement {
+ pub htmlelement: HTMLElement,
+}
+
+impl HTMLPreElementDerived for EventTarget {
+ fn is_htmlpreelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLPreElementTypeId))
+ }
+}
+
+impl HTMLPreElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLPreElement {
+ HTMLPreElement {
+ htmlelement: HTMLElement::new_inherited(HTMLPreElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLPreElement> {
+ let element = HTMLPreElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLPreElementBinding::Wrap)
+ }
+}
+
+impl Reflectable for HTMLPreElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmlprogresselement.rs b/components/script/dom/htmlprogresselement.rs
new file mode 100644
index 00000000000..74dcab1730f
--- /dev/null
+++ b/components/script/dom/htmlprogresselement.rs
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLProgressElementBinding;
+use dom::bindings::codegen::InheritTypes::HTMLProgressElementDerived;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLProgressElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, ElementNodeTypeId};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLProgressElement {
+ pub htmlelement: HTMLElement,
+}
+
+impl HTMLProgressElementDerived for EventTarget {
+ fn is_htmlprogresselement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLProgressElementTypeId))
+ }
+}
+
+impl HTMLProgressElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLProgressElement {
+ HTMLProgressElement {
+ htmlelement: HTMLElement::new_inherited(HTMLProgressElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLProgressElement> {
+ let element = HTMLProgressElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLProgressElementBinding::Wrap)
+ }
+}
+
+impl Reflectable for HTMLProgressElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmlquoteelement.rs b/components/script/dom/htmlquoteelement.rs
new file mode 100644
index 00000000000..488a82c394d
--- /dev/null
+++ b/components/script/dom/htmlquoteelement.rs
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLQuoteElementBinding;
+use dom::bindings::codegen::InheritTypes::HTMLQuoteElementDerived;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLQuoteElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, ElementNodeTypeId};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLQuoteElement {
+ pub htmlelement: HTMLElement,
+}
+
+impl HTMLQuoteElementDerived for EventTarget {
+ fn is_htmlquoteelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLQuoteElementTypeId))
+ }
+}
+
+impl HTMLQuoteElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLQuoteElement {
+ HTMLQuoteElement {
+ htmlelement: HTMLElement::new_inherited(HTMLQuoteElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLQuoteElement> {
+ let element = HTMLQuoteElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLQuoteElementBinding::Wrap)
+ }
+}
+
+impl Reflectable for HTMLQuoteElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmlscriptelement.rs b/components/script/dom/htmlscriptelement.rs
new file mode 100644
index 00000000000..3c189791b94
--- /dev/null
+++ b/components/script/dom/htmlscriptelement.rs
@@ -0,0 +1,130 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
+use dom::bindings::codegen::Bindings::HTMLScriptElementBinding;
+use dom::bindings::codegen::Bindings::HTMLScriptElementBinding::HTMLScriptElementMethods;
+use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
+use dom::bindings::codegen::InheritTypes::HTMLScriptElementDerived;
+use dom::bindings::codegen::InheritTypes::{ElementCast, NodeCast};
+use dom::bindings::js::{JSRef, Temporary, OptionalRootable};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::{HTMLScriptElementTypeId, Element, AttributeHandlers};
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, NodeHelpers, ElementNodeTypeId};
+
+use servo_util::namespace::Null;
+use servo_util::str::{DOMString, HTML_SPACE_CHARACTERS, StaticStringVec};
+
+#[deriving(Encodable)]
+pub struct HTMLScriptElement {
+ pub htmlelement: HTMLElement,
+}
+
+impl HTMLScriptElementDerived for EventTarget {
+ fn is_htmlscriptelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLScriptElementTypeId))
+ }
+}
+
+impl HTMLScriptElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLScriptElement {
+ HTMLScriptElement {
+ htmlelement: HTMLElement::new_inherited(HTMLScriptElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLScriptElement> {
+ let element = HTMLScriptElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLScriptElementBinding::Wrap)
+ }
+}
+
+pub trait HTMLScriptElementHelpers {
+ /// Prepare a script (<http://www.whatwg.org/html/#prepare-a-script>),
+ /// steps 6 and 7.
+ fn is_javascript(&self) -> bool;
+}
+
+/// Supported script types as defined by
+/// <http://whatwg.org/html/#support-the-scripting-language>.
+static SCRIPT_JS_MIMES: StaticStringVec = &[
+ "application/ecmascript",
+ "application/javascript",
+ "application/x-ecmascript",
+ "application/x-javascript",
+ "text/ecmascript",
+ "text/javascript",
+ "text/javascript1.0",
+ "text/javascript1.1",
+ "text/javascript1.2",
+ "text/javascript1.3",
+ "text/javascript1.4",
+ "text/javascript1.5",
+ "text/jscript",
+ "text/livescript",
+ "text/x-ecmascript",
+ "text/x-javascript",
+];
+
+impl<'a> HTMLScriptElementHelpers for JSRef<'a, HTMLScriptElement> {
+ fn is_javascript(&self) -> bool {
+ let element: &JSRef<Element> = ElementCast::from_ref(self);
+ match element.get_attribute(Null, "type").root().map(|s| s.Value()) {
+ Some(ref s) if s.is_empty() => {
+ // type attr exists, but empty means js
+ debug!("script type empty, inferring js");
+ true
+ },
+ Some(ref s) => {
+ debug!("script type={:s}", *s);
+ SCRIPT_JS_MIMES.contains(&s.as_slice().trim_chars(HTML_SPACE_CHARACTERS))
+ },
+ None => {
+ debug!("no script type");
+ match element.get_attribute(Null, "language").root().map(|s| s.Value()) {
+ Some(ref s) if s.is_empty() => {
+ debug!("script language empty, inferring js");
+ true
+ },
+ Some(ref s) => {
+ debug!("script language={:s}", *s);
+ SCRIPT_JS_MIMES.contains(&"text/".to_string().append(s.as_slice()).as_slice())
+ },
+ None => {
+ debug!("no script type or language, inferring js");
+ true
+ }
+ }
+ }
+ }
+ }
+}
+
+impl<'a> HTMLScriptElementMethods for JSRef<'a, HTMLScriptElement> {
+ fn Src(&self) -> DOMString {
+ let element: &JSRef<Element> = ElementCast::from_ref(self);
+ element.get_url_attribute("src")
+ }
+
+ // http://www.whatwg.org/html/#dom-script-text
+ fn Text(&self) -> DOMString {
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ Node::collect_text_contents(node.children())
+ }
+
+ // http://www.whatwg.org/html/#dom-script-text
+ fn SetText(&self, value: DOMString) {
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ node.SetTextContent(Some(value))
+ }
+}
+
+impl Reflectable for HTMLScriptElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmlselectelement.rs b/components/script/dom/htmlselectelement.rs
new file mode 100644
index 00000000000..b8b5303cffb
--- /dev/null
+++ b/components/script/dom/htmlselectelement.rs
@@ -0,0 +1,136 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLSelectElementBinding;
+use dom::bindings::codegen::Bindings::HTMLSelectElementBinding::HTMLSelectElementMethods;
+use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast, NodeCast};
+use dom::bindings::codegen::InheritTypes::{HTMLSelectElementDerived, HTMLFieldSetElementDerived};
+use dom::bindings::codegen::UnionTypes::HTMLElementOrLong::HTMLElementOrLong;
+use dom::bindings::codegen::UnionTypes::HTMLOptionElementOrHTMLOptGroupElement::HTMLOptionElementOrHTMLOptGroupElement;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::{AttributeHandlers, Element, HTMLSelectElementTypeId};
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{DisabledStateHelpers, Node, NodeHelpers, ElementNodeTypeId, window_from_node};
+use dom::validitystate::ValidityState;
+use dom::virtualmethods::VirtualMethods;
+
+use servo_util::atom::Atom;
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLSelectElement {
+ pub htmlelement: HTMLElement
+}
+
+impl HTMLSelectElementDerived for EventTarget {
+ fn is_htmlselectelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLSelectElementTypeId))
+ }
+}
+
+impl HTMLSelectElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLSelectElement {
+ HTMLSelectElement {
+ htmlelement: HTMLElement::new_inherited(HTMLSelectElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLSelectElement> {
+ let element = HTMLSelectElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLSelectElementBinding::Wrap)
+ }
+}
+
+impl<'a> HTMLSelectElementMethods for JSRef<'a, HTMLSelectElement> {
+ fn Validity(&self) -> Temporary<ValidityState> {
+ let window = window_from_node(self).root();
+ ValidityState::new(&*window)
+ }
+
+ // Note: this function currently only exists for test_union.html.
+ fn Add(&self, _element: HTMLOptionElementOrHTMLOptGroupElement, _before: Option<HTMLElementOrLong>) {
+ }
+
+ // http://www.whatwg.org/html/#dom-fe-disabled
+ make_bool_getter!(Disabled)
+
+ // http://www.whatwg.org/html/#dom-fe-disabled
+ fn SetDisabled(&self, disabled: bool) {
+ let elem: &JSRef<Element> = ElementCast::from_ref(self);
+ elem.set_bool_attribute("disabled", disabled)
+ }
+}
+
+impl<'a> VirtualMethods for JSRef<'a, HTMLSelectElement> {
+ fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods> {
+ let htmlelement: &JSRef<HTMLElement> = HTMLElementCast::from_ref(self);
+ Some(htmlelement as &VirtualMethods)
+ }
+
+ fn after_set_attr(&self, name: &Atom, value: DOMString) {
+ match self.super_type() {
+ Some(ref s) => s.after_set_attr(name, value.clone()),
+ _ => (),
+ }
+
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ match name.as_slice() {
+ "disabled" => {
+ node.set_disabled_state(true);
+ node.set_enabled_state(false);
+ },
+ _ => ()
+ }
+ }
+
+ fn before_remove_attr(&self, name: &Atom, value: DOMString) {
+ match self.super_type() {
+ Some(ref s) => s.before_remove_attr(name, value),
+ _ => (),
+ }
+
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ match name.as_slice() {
+ "disabled" => {
+ node.set_disabled_state(false);
+ node.set_enabled_state(true);
+ node.check_ancestors_disabled_state_for_form_control();
+ },
+ _ => ()
+ }
+ }
+
+ fn bind_to_tree(&self, tree_in_doc: bool) {
+ match self.super_type() {
+ Some(ref s) => s.bind_to_tree(tree_in_doc),
+ _ => (),
+ }
+
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ node.check_ancestors_disabled_state_for_form_control();
+ }
+
+ fn unbind_from_tree(&self, tree_in_doc: bool) {
+ match self.super_type() {
+ Some(ref s) => s.unbind_from_tree(tree_in_doc),
+ _ => (),
+ }
+
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ if node.ancestors().any(|ancestor| ancestor.is_htmlfieldsetelement()) {
+ node.check_ancestors_disabled_state_for_form_control();
+ } else {
+ node.check_disabled_attribute();
+ }
+ }
+}
+
+impl Reflectable for HTMLSelectElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmlserializer.rs b/components/script/dom/htmlserializer.rs
new file mode 100644
index 00000000000..cb9e1769255
--- /dev/null
+++ b/components/script/dom/htmlserializer.rs
@@ -0,0 +1,171 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::attr::Attr;
+use dom::bindings::codegen::InheritTypes::{ElementCast, TextCast, CommentCast, NodeCast};
+use dom::bindings::codegen::InheritTypes::{DocumentTypeCast, CharacterDataCast};
+use dom::bindings::codegen::InheritTypes::ProcessingInstructionCast;
+use dom::bindings::js::JSRef;
+use dom::characterdata::CharacterData;
+use dom::comment::Comment;
+use dom::documenttype::DocumentType;
+use dom::element::Element;
+use dom::node::{Node, NodeIterator};
+use dom::node::{DoctypeNodeTypeId, DocumentFragmentNodeTypeId, CommentNodeTypeId};
+use dom::node::{DocumentNodeTypeId, ElementNodeTypeId, ProcessingInstructionNodeTypeId};
+use dom::node::{TextNodeTypeId, NodeHelpers};
+use dom::processinginstruction::ProcessingInstruction;
+use dom::text::Text;
+
+use servo_util::atom::Atom;
+use servo_util::namespace;
+
+pub fn serialize(iterator: &mut NodeIterator) -> String {
+ let mut html = String::new();
+ let mut open_elements: Vec<String> = vec!();
+ let depth = iterator.depth;
+ for node in *iterator {
+ while open_elements.len() > depth {
+ html.push_str("</");
+ html.push_str(open_elements.pop().unwrap().as_slice());
+ html.push_str(">");
+ }
+ match node.type_id() {
+ ElementNodeTypeId(..) => {
+ let elem: &JSRef<Element> = ElementCast::to_ref(&node).unwrap();
+ serialize_elem(elem, &mut open_elements, &mut html)
+ }
+ CommentNodeTypeId => {
+ let comment: &JSRef<Comment> = CommentCast::to_ref(&node).unwrap();
+ serialize_comment(comment, &mut html)
+ }
+ TextNodeTypeId => {
+ let text: &JSRef<Text> = TextCast::to_ref(&node).unwrap();
+ serialize_text(text, &mut html)
+ }
+ DoctypeNodeTypeId => {
+ let doctype: &JSRef<DocumentType> = DocumentTypeCast::to_ref(&node).unwrap();
+ serialize_doctype(doctype, &mut html)
+ }
+ ProcessingInstructionNodeTypeId => {
+ let processing_instruction: &JSRef<ProcessingInstruction> =
+ ProcessingInstructionCast::to_ref(&node).unwrap();
+ serialize_processing_instruction(processing_instruction, &mut html)
+ }
+ DocumentFragmentNodeTypeId => {}
+ DocumentNodeTypeId => {
+ fail!("It shouldn't be possible to serialize a document node")
+ }
+ }
+ }
+ while open_elements.len() > 0 {
+ html.push_str("</");
+ html.push_str(open_elements.pop().unwrap().as_slice());
+ html.push_str(">");
+ }
+ html
+}
+
+fn serialize_comment(comment: &JSRef<Comment>, html: &mut String) {
+ html.push_str("<!--");
+ html.push_str(comment.deref().characterdata.data.deref().borrow().as_slice());
+ html.push_str("-->");
+}
+
+fn serialize_text(text: &JSRef<Text>, html: &mut String) {
+ let text_node: &JSRef<Node> = NodeCast::from_ref(text);
+ match text_node.parent_node().map(|node| node.root()) {
+ Some(ref parent) if parent.is_element() => {
+ let elem: &JSRef<Element> = ElementCast::to_ref(&**parent).unwrap();
+ match elem.deref().local_name.as_slice() {
+ "style" | "script" | "xmp" | "iframe" |
+ "noembed" | "noframes" | "plaintext" |
+ "noscript" if elem.deref().namespace == namespace::HTML
+ => html.push_str(text.deref().characterdata.data.deref().borrow().as_slice()),
+ _ => escape(text.deref().characterdata.data.deref().borrow().as_slice(), false, html)
+ }
+ }
+ _ => escape(text.deref().characterdata.data.deref().borrow().as_slice(), false, html)
+ }
+}
+
+fn serialize_processing_instruction(processing_instruction: &JSRef<ProcessingInstruction>,
+ html: &mut String) {
+ html.push_str("<?");
+ html.push_str(processing_instruction.deref().target.as_slice());
+ html.push_char(' ');
+ html.push_str(processing_instruction.deref().characterdata.data.deref().borrow().as_slice());
+ html.push_str("?>");
+}
+
+fn serialize_doctype(doctype: &JSRef<DocumentType>, html: &mut String) {
+ html.push_str("<!DOCTYPE");
+ html.push_str(doctype.deref().name.as_slice());
+ html.push_char('>');
+}
+
+fn serialize_elem(elem: &JSRef<Element>, open_elements: &mut Vec<String>, html: &mut String) {
+ html.push_char('<');
+ html.push_str(elem.deref().local_name.as_slice());
+ for attr in elem.deref().attrs.borrow().iter() {
+ let attr = attr.root();
+ serialize_attr(&*attr, html);
+ };
+ html.push_char('>');
+
+ match elem.deref().local_name.as_slice() {
+ "pre" | "listing" | "textarea" if elem.deref().namespace == namespace::HTML => {
+ let node: &JSRef<Node> = NodeCast::from_ref(elem);
+ match node.first_child().map(|child| child.root()) {
+ Some(ref child) if child.is_text() => {
+ let text: &JSRef<CharacterData> = CharacterDataCast::to_ref(&**child).unwrap();
+ if text.deref().data.deref().borrow().len() > 0 && text.deref().data.deref().borrow().as_slice().char_at(0) == '\n' {
+ html.push_char('\x0A');
+ }
+ },
+ _ => {}
+ }
+ },
+ _ => {}
+ }
+
+ if !elem.deref().is_void() {
+ open_elements.push(elem.deref().local_name.as_slice().to_string());
+ }
+}
+
+fn serialize_attr(attr: &JSRef<Attr>, html: &mut String) {
+ html.push_char(' ');
+ if attr.deref().namespace == namespace::XML {
+ html.push_str("xml:");
+ html.push_str(attr.local_name().as_slice());
+ } else if attr.deref().namespace == namespace::XMLNS &&
+ *attr.local_name() == Atom::from_slice("xmlns") {
+ html.push_str("xmlns");
+ } else if attr.deref().namespace == namespace::XMLNS {
+ html.push_str("xmlns:");
+ html.push_str(attr.local_name().as_slice());
+ } else if attr.deref().namespace == namespace::XLink {
+ html.push_str("xlink:");
+ html.push_str(attr.local_name().as_slice());
+ } else {
+ html.push_str(attr.deref().name.as_slice());
+ };
+ html.push_str("=\"");
+ escape(attr.deref().value().as_slice(), true, html);
+ html.push_char('"');
+}
+
+fn escape(string: &str, attr_mode: bool, html: &mut String) {
+ for c in string.chars() {
+ match c {
+ '&' => html.push_str("&amp;"),
+ '\xA0' => html.push_str("&nbsp;"),
+ '"' if attr_mode => html.push_str("&quot;"),
+ '<' if !attr_mode => html.push_str("&lt;"),
+ '>' if !attr_mode => html.push_str("&gt;"),
+ c => html.push_char(c),
+ }
+ }
+}
diff --git a/components/script/dom/htmlsourceelement.rs b/components/script/dom/htmlsourceelement.rs
new file mode 100644
index 00000000000..cb27b8c75d2
--- /dev/null
+++ b/components/script/dom/htmlsourceelement.rs
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLSourceElementBinding;
+use dom::bindings::codegen::InheritTypes::HTMLSourceElementDerived;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLSourceElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, ElementNodeTypeId};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLSourceElement {
+ pub htmlelement: HTMLElement
+}
+
+impl HTMLSourceElementDerived for EventTarget {
+ fn is_htmlsourceelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLSourceElementTypeId))
+ }
+}
+
+impl HTMLSourceElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLSourceElement {
+ HTMLSourceElement {
+ htmlelement: HTMLElement::new_inherited(HTMLSourceElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLSourceElement> {
+ let element = HTMLSourceElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLSourceElementBinding::Wrap)
+ }
+}
+
+impl Reflectable for HTMLSourceElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmlspanelement.rs b/components/script/dom/htmlspanelement.rs
new file mode 100644
index 00000000000..9b98b8fa28d
--- /dev/null
+++ b/components/script/dom/htmlspanelement.rs
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLSpanElementBinding;
+use dom::bindings::codegen::InheritTypes::HTMLSpanElementDerived;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLSpanElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, ElementNodeTypeId};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLSpanElement {
+ pub htmlelement: HTMLElement
+}
+
+impl HTMLSpanElementDerived for EventTarget {
+ fn is_htmlspanelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLSpanElementTypeId))
+ }
+}
+
+impl HTMLSpanElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLSpanElement {
+ HTMLSpanElement {
+ htmlelement: HTMLElement::new_inherited(HTMLSpanElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLSpanElement> {
+ let element = HTMLSpanElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLSpanElementBinding::Wrap)
+ }
+}
+
+impl Reflectable for HTMLSpanElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmlstyleelement.rs b/components/script/dom/htmlstyleelement.rs
new file mode 100644
index 00000000000..d32219ea8f9
--- /dev/null
+++ b/components/script/dom/htmlstyleelement.rs
@@ -0,0 +1,97 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLStyleElementBinding;
+use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
+use dom::bindings::codegen::InheritTypes::{HTMLElementCast, HTMLStyleElementDerived, NodeCast};
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLStyleElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, NodeHelpers, ElementNodeTypeId, window_from_node};
+use dom::virtualmethods::VirtualMethods;
+use html::cssparse::parse_inline_css;
+use layout_interface::{AddStylesheetMsg, LayoutChan};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLStyleElement {
+ pub htmlelement: HTMLElement,
+}
+
+impl HTMLStyleElementDerived for EventTarget {
+ fn is_htmlstyleelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLStyleElementTypeId))
+ }
+}
+
+impl HTMLStyleElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLStyleElement {
+ HTMLStyleElement {
+ htmlelement: HTMLElement::new_inherited(HTMLStyleElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLStyleElement> {
+ let element = HTMLStyleElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLStyleElementBinding::Wrap)
+ }
+}
+
+pub trait StyleElementHelpers {
+ fn parse_own_css(&self);
+}
+
+impl<'a> StyleElementHelpers for JSRef<'a, HTMLStyleElement> {
+ fn parse_own_css(&self) {
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ assert!(node.is_in_doc());
+
+ let win = window_from_node(node).root();
+ let url = win.deref().page().get_url();
+
+ let data = node.GetTextContent().expect("Element.textContent must be a string");
+ let sheet = parse_inline_css(url, data);
+ let LayoutChan(ref layout_chan) = *win.deref().page().layout_chan;
+ layout_chan.send(AddStylesheetMsg(sheet));
+ }
+}
+
+impl<'a> VirtualMethods for JSRef<'a, HTMLStyleElement> {
+ fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods> {
+ let htmlelement: &JSRef<HTMLElement> = HTMLElementCast::from_ref(self);
+ Some(htmlelement as &VirtualMethods)
+ }
+
+ fn child_inserted(&self, child: &JSRef<Node>) {
+ match self.super_type() {
+ Some(ref s) => s.child_inserted(child),
+ _ => (),
+ }
+
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ if node.is_in_doc() {
+ self.parse_own_css();
+ }
+ }
+
+ fn bind_to_tree(&self, tree_in_doc: bool) {
+ match self.super_type() {
+ Some(ref s) => s.bind_to_tree(tree_in_doc),
+ _ => ()
+ }
+
+ if tree_in_doc {
+ self.parse_own_css();
+ }
+ }
+}
+
+impl Reflectable for HTMLStyleElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmltablecaptionelement.rs b/components/script/dom/htmltablecaptionelement.rs
new file mode 100644
index 00000000000..92c45b49400
--- /dev/null
+++ b/components/script/dom/htmltablecaptionelement.rs
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLTableCaptionElementBinding;
+use dom::bindings::codegen::InheritTypes::HTMLTableCaptionElementDerived;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLTableCaptionElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, ElementNodeTypeId};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLTableCaptionElement {
+ pub htmlelement: HTMLElement
+}
+
+impl HTMLTableCaptionElementDerived for EventTarget {
+ fn is_htmltablecaptionelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLTableCaptionElementTypeId))
+ }
+}
+
+impl HTMLTableCaptionElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLTableCaptionElement {
+ HTMLTableCaptionElement {
+ htmlelement: HTMLElement::new_inherited(HTMLTableCaptionElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLTableCaptionElement> {
+ let element = HTMLTableCaptionElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLTableCaptionElementBinding::Wrap)
+ }
+}
+
+impl Reflectable for HTMLTableCaptionElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmltablecellelement.rs b/components/script/dom/htmltablecellelement.rs
new file mode 100644
index 00000000000..116768e23af
--- /dev/null
+++ b/components/script/dom/htmltablecellelement.rs
@@ -0,0 +1,42 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::InheritTypes::HTMLTableCellElementDerived;
+use dom::bindings::js::JSRef;
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::{ElementTypeId, HTMLTableDataCellElementTypeId, HTMLTableHeaderCellElementTypeId};
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::ElementNodeTypeId;
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLTableCellElement {
+ pub htmlelement: HTMLElement,
+}
+
+impl HTMLTableCellElementDerived for EventTarget {
+ fn is_htmltablecellelement(&self) -> bool {
+ match self.type_id {
+ NodeTargetTypeId(ElementNodeTypeId(HTMLTableDataCellElementTypeId)) |
+ NodeTargetTypeId(ElementNodeTypeId(HTMLTableHeaderCellElementTypeId)) => true,
+ _ => false
+ }
+ }
+}
+
+impl HTMLTableCellElement {
+ pub fn new_inherited(type_id: ElementTypeId, tag_name: DOMString, document: &JSRef<Document>) -> HTMLTableCellElement {
+ HTMLTableCellElement {
+ htmlelement: HTMLElement::new_inherited(type_id, tag_name, document)
+ }
+ }
+}
+
+impl Reflectable for HTMLTableCellElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmltablecolelement.rs b/components/script/dom/htmltablecolelement.rs
new file mode 100644
index 00000000000..48d6164e500
--- /dev/null
+++ b/components/script/dom/htmltablecolelement.rs
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLTableColElementBinding;
+use dom::bindings::codegen::InheritTypes::HTMLTableColElementDerived;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLTableColElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, ElementNodeTypeId};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLTableColElement {
+ pub htmlelement: HTMLElement,
+}
+
+impl HTMLTableColElementDerived for EventTarget {
+ fn is_htmltablecolelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLTableColElementTypeId))
+ }
+}
+
+impl HTMLTableColElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLTableColElement {
+ HTMLTableColElement {
+ htmlelement: HTMLElement::new_inherited(HTMLTableColElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLTableColElement> {
+ let element = HTMLTableColElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLTableColElementBinding::Wrap)
+ }
+}
+
+impl Reflectable for HTMLTableColElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmltabledatacellelement.rs b/components/script/dom/htmltabledatacellelement.rs
new file mode 100644
index 00000000000..07027b5d294
--- /dev/null
+++ b/components/script/dom/htmltabledatacellelement.rs
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLTableDataCellElementBinding;
+use dom::bindings::codegen::InheritTypes::HTMLTableDataCellElementDerived;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLTableDataCellElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmltablecellelement::HTMLTableCellElement;
+use dom::node::{Node, ElementNodeTypeId};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLTableDataCellElement {
+ pub htmltablecellelement: HTMLTableCellElement,
+}
+
+impl HTMLTableDataCellElementDerived for EventTarget {
+ fn is_htmltabledatacellelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLTableDataCellElementTypeId))
+ }
+}
+
+impl HTMLTableDataCellElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLTableDataCellElement {
+ HTMLTableDataCellElement {
+ htmltablecellelement: HTMLTableCellElement::new_inherited(HTMLTableDataCellElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLTableDataCellElement> {
+ let element = HTMLTableDataCellElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLTableDataCellElementBinding::Wrap)
+ }
+}
+
+impl Reflectable for HTMLTableDataCellElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmltablecellelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmltableelement.rs b/components/script/dom/htmltableelement.rs
new file mode 100644
index 00000000000..f33b03fb778
--- /dev/null
+++ b/components/script/dom/htmltableelement.rs
@@ -0,0 +1,81 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLTableElementBinding;
+use dom::bindings::codegen::Bindings::HTMLTableElementBinding::HTMLTableElementMethods;
+use dom::bindings::codegen::InheritTypes::{HTMLTableElementDerived, NodeCast, HTMLTableCaptionElementCast};
+use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLTableCaptionElementTypeId;
+use dom::element::HTMLTableElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::htmltablecaptionelement::HTMLTableCaptionElement;
+use dom::node::{Node, NodeHelpers, ElementNodeTypeId};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLTableElement {
+ pub htmlelement: HTMLElement,
+}
+
+impl HTMLTableElementDerived for EventTarget {
+ fn is_htmltableelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLTableElementTypeId))
+ }
+}
+
+impl HTMLTableElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLTableElement {
+ HTMLTableElement {
+ htmlelement: HTMLElement::new_inherited(HTMLTableElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLTableElement> {
+ let element = HTMLTableElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLTableElementBinding::Wrap)
+ }
+}
+
+impl Reflectable for HTMLTableElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
+
+impl<'a> HTMLTableElementMethods for JSRef<'a, HTMLTableElement> {
+
+ // http://www.whatwg.org/html/#dom-table-caption
+ fn GetCaption(&self) -> Option<Temporary<HTMLTableCaptionElement>> {
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ node.children().find(|child| {
+ child.type_id() == ElementNodeTypeId(HTMLTableCaptionElementTypeId)
+ }).map(|node| {
+ Temporary::from_rooted(HTMLTableCaptionElementCast::to_ref(&node).unwrap())
+ })
+ }
+
+ // http://www.whatwg.org/html/#dom-table-caption
+ fn SetCaption(&self, new_caption: Option<JSRef<HTMLTableCaptionElement>>) {
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ let old_caption = self.GetCaption();
+
+ match old_caption {
+ Some(htmlelem) => {
+ let htmlelem_jsref = &*htmlelem.root();
+ let old_caption_node: &JSRef<Node> = NodeCast::from_ref(htmlelem_jsref);
+ assert!(node.RemoveChild(old_caption_node).is_ok());
+ }
+ None => ()
+ }
+
+ new_caption.map(|caption| {
+ let new_caption_node: &JSRef<Node> = NodeCast::from_ref(&caption);
+ assert!(node.AppendChild(new_caption_node).is_ok());
+ });
+ }
+}
diff --git a/components/script/dom/htmltableheadercellelement.rs b/components/script/dom/htmltableheadercellelement.rs
new file mode 100644
index 00000000000..2ad288951da
--- /dev/null
+++ b/components/script/dom/htmltableheadercellelement.rs
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLTableHeaderCellElementBinding;
+use dom::bindings::codegen::InheritTypes::HTMLTableHeaderCellElementDerived;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLTableHeaderCellElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmltablecellelement::HTMLTableCellElement;
+use dom::node::{Node, ElementNodeTypeId};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLTableHeaderCellElement {
+ pub htmltablecellelement: HTMLTableCellElement,
+}
+
+impl HTMLTableHeaderCellElementDerived for EventTarget {
+ fn is_htmltableheadercellelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLTableHeaderCellElementTypeId))
+ }
+}
+
+impl HTMLTableHeaderCellElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLTableHeaderCellElement {
+ HTMLTableHeaderCellElement {
+ htmltablecellelement: HTMLTableCellElement::new_inherited(HTMLTableHeaderCellElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLTableHeaderCellElement> {
+ let element = HTMLTableHeaderCellElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLTableHeaderCellElementBinding::Wrap)
+ }
+}
+
+impl Reflectable for HTMLTableHeaderCellElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmltablecellelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmltablerowelement.rs b/components/script/dom/htmltablerowelement.rs
new file mode 100644
index 00000000000..de8978c75cd
--- /dev/null
+++ b/components/script/dom/htmltablerowelement.rs
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLTableRowElementBinding;
+use dom::bindings::codegen::InheritTypes::HTMLTableRowElementDerived;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLTableRowElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, ElementNodeTypeId};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLTableRowElement {
+ pub htmlelement: HTMLElement,
+}
+
+impl HTMLTableRowElementDerived for EventTarget {
+ fn is_htmltablerowelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLTableRowElementTypeId))
+ }
+}
+
+impl HTMLTableRowElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLTableRowElement {
+ HTMLTableRowElement {
+ htmlelement: HTMLElement::new_inherited(HTMLTableRowElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLTableRowElement> {
+ let element = HTMLTableRowElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLTableRowElementBinding::Wrap)
+ }
+}
+
+impl Reflectable for HTMLTableRowElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmltablesectionelement.rs b/components/script/dom/htmltablesectionelement.rs
new file mode 100644
index 00000000000..1dc372862fc
--- /dev/null
+++ b/components/script/dom/htmltablesectionelement.rs
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLTableSectionElementBinding;
+use dom::bindings::codegen::InheritTypes::HTMLTableSectionElementDerived;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLTableSectionElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, ElementNodeTypeId};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLTableSectionElement {
+ pub htmlelement: HTMLElement,
+}
+
+impl HTMLTableSectionElementDerived for EventTarget {
+ fn is_htmltablesectionelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLTableSectionElementTypeId))
+ }
+}
+
+impl HTMLTableSectionElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLTableSectionElement {
+ HTMLTableSectionElement {
+ htmlelement: HTMLElement::new_inherited(HTMLTableSectionElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLTableSectionElement> {
+ let element = HTMLTableSectionElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLTableSectionElementBinding::Wrap)
+ }
+}
+
+impl Reflectable for HTMLTableSectionElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmltemplateelement.rs b/components/script/dom/htmltemplateelement.rs
new file mode 100644
index 00000000000..12be7665169
--- /dev/null
+++ b/components/script/dom/htmltemplateelement.rs
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLTemplateElementBinding;
+use dom::bindings::codegen::InheritTypes::HTMLTemplateElementDerived;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLTemplateElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, ElementNodeTypeId};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLTemplateElement {
+ pub htmlelement: HTMLElement,
+}
+
+impl HTMLTemplateElementDerived for EventTarget {
+ fn is_htmltemplateelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLTemplateElementTypeId))
+ }
+}
+
+impl HTMLTemplateElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLTemplateElement {
+ HTMLTemplateElement {
+ htmlelement: HTMLElement::new_inherited(HTMLTemplateElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLTemplateElement> {
+ let element = HTMLTemplateElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLTemplateElementBinding::Wrap)
+ }
+}
+
+impl Reflectable for HTMLTemplateElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmltextareaelement.rs b/components/script/dom/htmltextareaelement.rs
new file mode 100644
index 00000000000..5385fefab7e
--- /dev/null
+++ b/components/script/dom/htmltextareaelement.rs
@@ -0,0 +1,124 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLTextAreaElementBinding;
+use dom::bindings::codegen::Bindings::HTMLTextAreaElementBinding::HTMLTextAreaElementMethods;
+use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast, NodeCast};
+use dom::bindings::codegen::InheritTypes::{HTMLTextAreaElementDerived, HTMLFieldSetElementDerived};
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::{AttributeHandlers, Element, HTMLTextAreaElementTypeId};
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{DisabledStateHelpers, Node, NodeHelpers, ElementNodeTypeId};
+use dom::virtualmethods::VirtualMethods;
+
+use servo_util::atom::Atom;
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLTextAreaElement {
+ pub htmlelement: HTMLElement,
+}
+
+impl HTMLTextAreaElementDerived for EventTarget {
+ fn is_htmltextareaelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLTextAreaElementTypeId))
+ }
+}
+
+impl HTMLTextAreaElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLTextAreaElement {
+ HTMLTextAreaElement {
+ htmlelement: HTMLElement::new_inherited(HTMLTextAreaElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLTextAreaElement> {
+ let element = HTMLTextAreaElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLTextAreaElementBinding::Wrap)
+ }
+}
+
+impl<'a> HTMLTextAreaElementMethods for JSRef<'a, HTMLTextAreaElement> {
+ // http://www.whatwg.org/html/#dom-fe-disabled
+ make_bool_getter!(Disabled)
+
+ // http://www.whatwg.org/html/#dom-fe-disabled
+ fn SetDisabled(&self, disabled: bool) {
+ let elem: &JSRef<Element> = ElementCast::from_ref(self);
+ elem.set_bool_attribute("disabled", disabled)
+ }
+}
+
+impl<'a> VirtualMethods for JSRef<'a, HTMLTextAreaElement> {
+ fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods> {
+ let htmlelement: &JSRef<HTMLElement> = HTMLElementCast::from_ref(self);
+ Some(htmlelement as &VirtualMethods)
+ }
+
+ fn after_set_attr(&self, name: &Atom, value: DOMString) {
+ match self.super_type() {
+ Some(ref s) => s.after_set_attr(name, value.clone()),
+ _ => (),
+ }
+
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ match name.as_slice() {
+ "disabled" => {
+ node.set_disabled_state(true);
+ node.set_enabled_state(false);
+ },
+ _ => ()
+ }
+ }
+
+ fn before_remove_attr(&self, name: &Atom, value: DOMString) {
+ match self.super_type() {
+ Some(ref s) => s.before_remove_attr(name, value),
+ _ => (),
+ }
+
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ match name.as_slice() {
+ "disabled" => {
+ node.set_disabled_state(false);
+ node.set_enabled_state(true);
+ node.check_ancestors_disabled_state_for_form_control();
+ },
+ _ => ()
+ }
+ }
+
+ fn bind_to_tree(&self, tree_in_doc: bool) {
+ match self.super_type() {
+ Some(ref s) => s.bind_to_tree(tree_in_doc),
+ _ => (),
+ }
+
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ node.check_ancestors_disabled_state_for_form_control();
+ }
+
+ fn unbind_from_tree(&self, tree_in_doc: bool) {
+ match self.super_type() {
+ Some(ref s) => s.unbind_from_tree(tree_in_doc),
+ _ => (),
+ }
+
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ if node.ancestors().any(|ancestor| ancestor.is_htmlfieldsetelement()) {
+ node.check_ancestors_disabled_state_for_form_control();
+ } else {
+ node.check_disabled_attribute();
+ }
+ }
+}
+
+impl Reflectable for HTMLTextAreaElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmltimeelement.rs b/components/script/dom/htmltimeelement.rs
new file mode 100644
index 00000000000..8eeb695d4c6
--- /dev/null
+++ b/components/script/dom/htmltimeelement.rs
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLTimeElementBinding;
+use dom::bindings::codegen::InheritTypes::HTMLTimeElementDerived;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLTimeElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, ElementNodeTypeId};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLTimeElement {
+ pub htmlelement: HTMLElement
+}
+
+impl HTMLTimeElementDerived for EventTarget {
+ fn is_htmltimeelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLTimeElementTypeId))
+ }
+}
+
+impl HTMLTimeElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLTimeElement {
+ HTMLTimeElement {
+ htmlelement: HTMLElement::new_inherited(HTMLTimeElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLTimeElement> {
+ let element = HTMLTimeElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLTimeElementBinding::Wrap)
+ }
+}
+
+impl Reflectable for HTMLTimeElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmltitleelement.rs b/components/script/dom/htmltitleelement.rs
new file mode 100644
index 00000000000..550a531aa76
--- /dev/null
+++ b/components/script/dom/htmltitleelement.rs
@@ -0,0 +1,69 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLTitleElementBinding;
+use dom::bindings::codegen::Bindings::HTMLTitleElementBinding::HTMLTitleElementMethods;
+use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
+use dom::bindings::codegen::InheritTypes::{HTMLTitleElementDerived, NodeCast, TextCast};
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLTitleElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, NodeHelpers, ElementNodeTypeId};
+use dom::text::Text;
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLTitleElement {
+ pub htmlelement: HTMLElement,
+}
+
+impl HTMLTitleElementDerived for EventTarget {
+ fn is_htmltitleelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLTitleElementTypeId))
+ }
+}
+
+impl HTMLTitleElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLTitleElement {
+ HTMLTitleElement {
+ htmlelement: HTMLElement::new_inherited(HTMLTitleElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLTitleElement> {
+ let element = HTMLTitleElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLTitleElementBinding::Wrap)
+ }
+}
+
+impl<'a> HTMLTitleElementMethods for JSRef<'a, HTMLTitleElement> {
+ // http://www.whatwg.org/html/#dom-title-text
+ fn Text(&self) -> DOMString {
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ let mut content = String::new();
+ for child in node.children() {
+ let text: Option<&JSRef<Text>> = TextCast::to_ref(&child);
+ match text {
+ Some(text) => content.push_str(text.characterdata.data.borrow().as_slice()),
+ None => (),
+ }
+ }
+ content
+ }
+
+ // http://www.whatwg.org/html/#dom-title-text
+ fn SetText(&self, value: DOMString) {
+ let node: &JSRef<Node> = NodeCast::from_ref(self);
+ node.SetTextContent(Some(value))
+ }
+}
+
+impl Reflectable for HTMLTitleElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmltrackelement.rs b/components/script/dom/htmltrackelement.rs
new file mode 100644
index 00000000000..5d22571db67
--- /dev/null
+++ b/components/script/dom/htmltrackelement.rs
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLTrackElementBinding;
+use dom::bindings::codegen::InheritTypes::HTMLTrackElementDerived;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLTrackElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, ElementNodeTypeId};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLTrackElement {
+ pub htmlelement: HTMLElement,
+}
+
+impl HTMLTrackElementDerived for EventTarget {
+ fn is_htmltrackelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLTrackElementTypeId))
+ }
+}
+
+impl HTMLTrackElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLTrackElement {
+ HTMLTrackElement {
+ htmlelement: HTMLElement::new_inherited(HTMLTrackElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLTrackElement> {
+ let element = HTMLTrackElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLTrackElementBinding::Wrap)
+ }
+}
+
+impl Reflectable for HTMLTrackElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmlulistelement.rs b/components/script/dom/htmlulistelement.rs
new file mode 100644
index 00000000000..228152cc663
--- /dev/null
+++ b/components/script/dom/htmlulistelement.rs
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLUListElementBinding;
+use dom::bindings::codegen::InheritTypes::HTMLUListElementDerived;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLUListElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, ElementNodeTypeId};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLUListElement {
+ pub htmlelement: HTMLElement
+}
+
+impl HTMLUListElementDerived for EventTarget {
+ fn is_htmlulistelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLUListElementTypeId))
+ }
+}
+
+impl HTMLUListElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLUListElement {
+ HTMLUListElement {
+ htmlelement: HTMLElement::new_inherited(HTMLUListElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLUListElement> {
+ let element = HTMLUListElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLUListElementBinding::Wrap)
+ }
+}
+
+impl Reflectable for HTMLUListElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmlunknownelement.rs b/components/script/dom/htmlunknownelement.rs
new file mode 100644
index 00000000000..2956b0c459a
--- /dev/null
+++ b/components/script/dom/htmlunknownelement.rs
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLUnknownElementBinding;
+use dom::bindings::codegen::InheritTypes::HTMLUnknownElementDerived;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLUnknownElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::node::{Node, ElementNodeTypeId};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLUnknownElement {
+ pub htmlelement: HTMLElement
+}
+
+impl HTMLUnknownElementDerived for EventTarget {
+ fn is_htmlunknownelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLUnknownElementTypeId))
+ }
+}
+
+impl HTMLUnknownElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLUnknownElement {
+ HTMLUnknownElement {
+ htmlelement: HTMLElement::new_inherited(HTMLUnknownElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLUnknownElement> {
+ let element = HTMLUnknownElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLUnknownElementBinding::Wrap)
+ }
+}
+
+impl Reflectable for HTMLUnknownElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlelement.reflector()
+ }
+}
diff --git a/components/script/dom/htmlvideoelement.rs b/components/script/dom/htmlvideoelement.rs
new file mode 100644
index 00000000000..365b9a38f20
--- /dev/null
+++ b/components/script/dom/htmlvideoelement.rs
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLVideoElementBinding;
+use dom::bindings::codegen::InheritTypes::HTMLVideoElementDerived;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::document::Document;
+use dom::element::HTMLVideoElementTypeId;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::htmlmediaelement::HTMLMediaElement;
+use dom::node::{Node, ElementNodeTypeId};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct HTMLVideoElement {
+ pub htmlmediaelement: HTMLMediaElement
+}
+
+impl HTMLVideoElementDerived for EventTarget {
+ fn is_htmlvideoelement(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLVideoElementTypeId))
+ }
+}
+
+impl HTMLVideoElement {
+ pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLVideoElement {
+ HTMLVideoElement {
+ htmlmediaelement: HTMLMediaElement::new_inherited(HTMLVideoElementTypeId, localName, document)
+ }
+ }
+
+ pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLVideoElement> {
+ let element = HTMLVideoElement::new_inherited(localName, document);
+ Node::reflect_node(box element, document, HTMLVideoElementBinding::Wrap)
+ }
+}
+
+impl Reflectable for HTMLVideoElement {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.htmlmediaelement.reflector()
+ }
+}
diff --git a/components/script/dom/location.rs b/components/script/dom/location.rs
new file mode 100644
index 00000000000..e310c52d49e
--- /dev/null
+++ b/components/script/dom/location.rs
@@ -0,0 +1,64 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::LocationBinding;
+use dom::bindings::codegen::Bindings::LocationBinding::LocationMethods;
+use dom::bindings::global::Window;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
+use dom::window::Window;
+use page::Page;
+
+use servo_util::str::DOMString;
+
+use std::rc::Rc;
+
+#[deriving(Encodable)]
+pub struct Location {
+ reflector_: Reflector, //XXXjdm cycle: window->Location->window
+ page: Rc<Page>,
+}
+
+impl Location {
+ pub fn new_inherited(page: Rc<Page>) -> Location {
+ Location {
+ reflector_: Reflector::new(),
+ page: page
+ }
+ }
+
+ pub fn new(window: &JSRef<Window>, page: Rc<Page>) -> Temporary<Location> {
+ reflect_dom_object(box Location::new_inherited(page),
+ &Window(*window),
+ LocationBinding::Wrap)
+ }
+}
+
+impl<'a> LocationMethods for JSRef<'a, Location> {
+ fn Href(&self) -> DOMString {
+ self.page.get_url().serialize()
+ }
+
+ fn Search(&self) -> DOMString {
+ match self.page.get_url().query {
+ None => "".to_string(),
+ Some(ref query) if query.as_slice() == "" => "".to_string(),
+ Some(ref query) => "?".to_string().append(query.as_slice())
+ }
+ }
+
+ fn Hash(&self) -> DOMString {
+ match self.page.get_url().fragment {
+ None => "".to_string(),
+ Some(ref hash) if hash.as_slice() == "" => "".to_string(),
+ Some(ref hash) => "#".to_string().append(hash.as_slice())
+ }
+ }
+}
+
+impl Reflectable for Location {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ &self.reflector_
+ }
+}
diff --git a/components/script/dom/macros.rs b/components/script/dom/macros.rs
new file mode 100644
index 00000000000..6cfca77593d
--- /dev/null
+++ b/components/script/dom/macros.rs
@@ -0,0 +1,44 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#![macro_escape]
+
+#[macro_export]
+macro_rules! make_getter(
+ ( $attr:ident ) => (
+ fn $attr(&self) -> DOMString {
+ use dom::element::{Element, AttributeHandlers};
+ use dom::bindings::codegen::InheritTypes::ElementCast;
+ use std::ascii::StrAsciiExt;
+ let element: &JSRef<Element> = ElementCast::from_ref(self);
+ element.get_string_attribute(stringify!($attr).to_ascii_lower().as_slice())
+ }
+ );
+)
+
+#[macro_export]
+macro_rules! make_bool_getter(
+ ( $attr:ident ) => (
+ fn $attr(&self) -> bool {
+ use dom::element::{Element, AttributeHandlers};
+ use dom::bindings::codegen::InheritTypes::ElementCast;
+ use std::ascii::StrAsciiExt;
+ let element: &JSRef<Element> = ElementCast::from_ref(self);
+ element.has_attribute(stringify!($attr).to_ascii_lower().as_slice())
+ }
+ );
+)
+
+#[macro_export]
+macro_rules! make_uint_getter(
+ ( $attr:ident ) => (
+ fn $attr(&self) -> u32 {
+ use dom::element::{Element, AttributeHandlers};
+ use dom::bindings::codegen::InheritTypes::ElementCast;
+ use std::ascii::StrAsciiExt;
+ let element: &JSRef<Element> = ElementCast::from_ref(self);
+ element.get_uint_attribute(stringify!($attr).to_ascii_lower().as_slice())
+ }
+ );
+)
diff --git a/components/script/dom/messageevent.rs b/components/script/dom/messageevent.rs
new file mode 100644
index 00000000000..13c8cf52dfd
--- /dev/null
+++ b/components/script/dom/messageevent.rs
@@ -0,0 +1,99 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::EventBinding::EventMethods;
+use dom::bindings::codegen::Bindings::MessageEventBinding;
+use dom::bindings::codegen::Bindings::MessageEventBinding::MessageEventMethods;
+use dom::bindings::codegen::InheritTypes::{EventCast, MessageEventDerived};
+use dom::bindings::error::Fallible;
+use dom::bindings::global::GlobalRef;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::trace::Traceable;
+use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
+use dom::event::{Event, MessageEventTypeId};
+use dom::eventtarget::{EventTarget, EventTargetHelpers};
+
+use servo_util::str::DOMString;
+
+use js::jsapi::JSContext;
+use js::jsval::JSVal;
+
+#[deriving(Encodable)]
+pub struct MessageEvent {
+ event: Event,
+ data: Traceable<JSVal>,
+ origin: DOMString,
+ lastEventId: DOMString,
+}
+
+impl MessageEventDerived for Event {
+ fn is_messageevent(&self) -> bool {
+ self.type_id == MessageEventTypeId
+ }
+}
+
+impl MessageEvent {
+ pub fn new_inherited(data: JSVal, origin: DOMString, lastEventId: DOMString)
+ -> MessageEvent {
+ MessageEvent {
+ event: Event::new_inherited(MessageEventTypeId),
+ data: Traceable::new(data),
+ origin: origin,
+ lastEventId: lastEventId,
+ }
+ }
+
+ pub fn new(global: &GlobalRef, type_: DOMString,
+ bubbles: bool, cancelable: bool,
+ data: JSVal, origin: DOMString, lastEventId: DOMString)
+ -> Temporary<MessageEvent> {
+ let ev = reflect_dom_object(box MessageEvent::new_inherited(data, origin, lastEventId),
+ global,
+ MessageEventBinding::Wrap).root();
+ let event: &JSRef<Event> = EventCast::from_ref(&*ev);
+ event.InitEvent(type_, bubbles, cancelable);
+ Temporary::from_rooted(&*ev)
+ }
+
+ pub fn Constructor(global: &GlobalRef,
+ type_: DOMString,
+ init: &MessageEventBinding::MessageEventInit)
+ -> Fallible<Temporary<MessageEvent>> {
+ let ev = MessageEvent::new(global, type_, init.parent.bubbles, init.parent.cancelable,
+ init.data, init.origin.clone(), init.lastEventId.clone());
+ Ok(ev)
+ }
+}
+
+impl MessageEvent {
+ pub fn dispatch_jsval(target: &JSRef<EventTarget>,
+ scope: &GlobalRef,
+ message: JSVal) {
+ let messageevent = MessageEvent::new(
+ scope, "message".to_string(), false, false, message,
+ "".to_string(), "".to_string()).root();
+ let event: &JSRef<Event> = EventCast::from_ref(&*messageevent);
+ target.dispatch_event_with_target(None, &*event).unwrap();
+ }
+}
+
+impl<'a> MessageEventMethods for JSRef<'a, MessageEvent> {
+ fn Data(&self, _cx: *mut JSContext) -> JSVal {
+ *self.data
+ }
+
+ fn Origin(&self) -> DOMString {
+ self.origin.clone()
+ }
+
+ fn LastEventId(&self) -> DOMString {
+ self.lastEventId.clone()
+ }
+}
+
+impl Reflectable for MessageEvent {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.event.reflector()
+ }
+}
diff --git a/components/script/dom/mouseevent.rs b/components/script/dom/mouseevent.rs
new file mode 100644
index 00000000000..aa750b501ba
--- /dev/null
+++ b/components/script/dom/mouseevent.rs
@@ -0,0 +1,182 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::MouseEventBinding;
+use dom::bindings::codegen::Bindings::MouseEventBinding::MouseEventMethods;
+use dom::bindings::codegen::Bindings::UIEventBinding::UIEventMethods;
+use dom::bindings::codegen::InheritTypes::{UIEventCast, MouseEventDerived};
+use dom::bindings::error::Fallible;
+use dom::bindings::global::{GlobalRef, Window};
+use dom::bindings::js::{JS, JSRef, RootedReference, Temporary, OptionalSettable};
+use dom::bindings::trace::Traceable;
+use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
+use dom::event::{Event, MouseEventTypeId};
+use dom::eventtarget::EventTarget;
+use dom::uievent::UIEvent;
+use dom::window::Window;
+use servo_util::str::DOMString;
+use std::cell::Cell;
+
+#[deriving(Encodable)]
+pub struct MouseEvent {
+ pub mouseevent: UIEvent,
+ pub screen_x: Traceable<Cell<i32>>,
+ pub screen_y: Traceable<Cell<i32>>,
+ pub client_x: Traceable<Cell<i32>>,
+ pub client_y: Traceable<Cell<i32>>,
+ pub ctrl_key: Traceable<Cell<bool>>,
+ pub shift_key: Traceable<Cell<bool>>,
+ pub alt_key: Traceable<Cell<bool>>,
+ pub meta_key: Traceable<Cell<bool>>,
+ pub button: Traceable<Cell<i16>>,
+ pub related_target: Cell<Option<JS<EventTarget>>>
+}
+
+impl MouseEventDerived for Event {
+ fn is_mouseevent(&self) -> bool {
+ self.type_id == MouseEventTypeId
+ }
+}
+
+impl MouseEvent {
+ pub fn new_inherited() -> MouseEvent {
+ MouseEvent {
+ mouseevent: UIEvent::new_inherited(MouseEventTypeId),
+ screen_x: Traceable::new(Cell::new(0)),
+ screen_y: Traceable::new(Cell::new(0)),
+ client_x: Traceable::new(Cell::new(0)),
+ client_y: Traceable::new(Cell::new(0)),
+ ctrl_key: Traceable::new(Cell::new(false)),
+ shift_key: Traceable::new(Cell::new(false)),
+ alt_key: Traceable::new(Cell::new(false)),
+ meta_key: Traceable::new(Cell::new(false)),
+ button: Traceable::new(Cell::new(0)),
+ related_target: Cell::new(None)
+ }
+ }
+
+ pub fn new_uninitialized(window: &JSRef<Window>) -> Temporary<MouseEvent> {
+ reflect_dom_object(box MouseEvent::new_inherited(),
+ &Window(*window),
+ MouseEventBinding::Wrap)
+ }
+
+ pub fn new(window: &JSRef<Window>,
+ type_: DOMString,
+ canBubble: bool,
+ cancelable: bool,
+ view: Option<JSRef<Window>>,
+ detail: i32,
+ screenX: i32,
+ screenY: i32,
+ clientX: i32,
+ clientY: i32,
+ ctrlKey: bool,
+ altKey: bool,
+ shiftKey: bool,
+ metaKey: bool,
+ button: i16,
+ relatedTarget: Option<JSRef<EventTarget>>) -> Temporary<MouseEvent> {
+ let ev = MouseEvent::new_uninitialized(window).root();
+ ev.deref().InitMouseEvent(type_, canBubble, cancelable, view, detail,
+ screenX, screenY, clientX, clientY,
+ ctrlKey, altKey, shiftKey, metaKey,
+ button, relatedTarget);
+ Temporary::from_rooted(&*ev)
+ }
+
+ pub fn Constructor(global: &GlobalRef,
+ type_: DOMString,
+ init: &MouseEventBinding::MouseEventInit) -> Fallible<Temporary<MouseEvent>> {
+ let event = MouseEvent::new(global.as_window(), type_,
+ init.parent.parent.bubbles,
+ init.parent.parent.cancelable,
+ init.parent.view.root_ref(),
+ init.parent.detail,
+ init.screenX, init.screenY,
+ init.clientX, init.clientY, init.ctrlKey,
+ init.altKey, init.shiftKey, init.metaKey,
+ init.button, init.relatedTarget.root_ref());
+ Ok(event)
+ }
+}
+
+impl<'a> MouseEventMethods for JSRef<'a, MouseEvent> {
+ fn ScreenX(&self) -> i32 {
+ self.screen_x.deref().get()
+ }
+
+ fn ScreenY(&self) -> i32 {
+ self.screen_y.deref().get()
+ }
+
+ fn ClientX(&self) -> i32 {
+ self.client_x.deref().get()
+ }
+
+ fn ClientY(&self) -> i32 {
+ self.client_y.deref().get()
+ }
+
+ fn CtrlKey(&self) -> bool {
+ self.ctrl_key.deref().get()
+ }
+
+ fn ShiftKey(&self) -> bool {
+ self.shift_key.deref().get()
+ }
+
+ fn AltKey(&self) -> bool {
+ self.alt_key.deref().get()
+ }
+
+ fn MetaKey(&self) -> bool {
+ self.meta_key.deref().get()
+ }
+
+ fn Button(&self) -> i16 {
+ self.button.deref().get()
+ }
+
+ fn GetRelatedTarget(&self) -> Option<Temporary<EventTarget>> {
+ self.related_target.get().clone().map(|target| Temporary::new(target))
+ }
+
+ fn InitMouseEvent(&self,
+ typeArg: DOMString,
+ canBubbleArg: bool,
+ cancelableArg: bool,
+ viewArg: Option<JSRef<Window>>,
+ detailArg: i32,
+ screenXArg: i32,
+ screenYArg: i32,
+ clientXArg: i32,
+ clientYArg: i32,
+ ctrlKeyArg: bool,
+ altKeyArg: bool,
+ shiftKeyArg: bool,
+ metaKeyArg: bool,
+ buttonArg: i16,
+ relatedTargetArg: Option<JSRef<EventTarget>>) {
+ let uievent: &JSRef<UIEvent> = UIEventCast::from_ref(self);
+ uievent.InitUIEvent(typeArg, canBubbleArg, cancelableArg, viewArg, detailArg);
+ self.screen_x.deref().set(screenXArg);
+ self.screen_y.deref().set(screenYArg);
+ self.client_x.deref().set(clientXArg);
+ self.client_y.deref().set(clientYArg);
+ self.ctrl_key.deref().set(ctrlKeyArg);
+ self.alt_key.deref().set(altKeyArg);
+ self.shift_key.deref().set(shiftKeyArg);
+ self.meta_key.deref().set(metaKeyArg);
+ self.button.deref().set(buttonArg);
+ self.related_target.assign(relatedTargetArg);
+ }
+}
+
+
+impl Reflectable for MouseEvent {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.mouseevent.reflector()
+ }
+}
diff --git a/components/script/dom/namednodemap.rs b/components/script/dom/namednodemap.rs
new file mode 100644
index 00000000000..d60160133c9
--- /dev/null
+++ b/components/script/dom/namednodemap.rs
@@ -0,0 +1,54 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::attr::Attr;
+use dom::bindings::codegen::Bindings::NamedNodeMapBinding;
+use dom::bindings::codegen::Bindings::NamedNodeMapBinding::NamedNodeMapMethods;
+use dom::bindings::global::Window;
+use dom::bindings::js::{JS, JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
+use dom::element::Element;
+use dom::window::Window;
+
+#[deriving(Encodable)]
+pub struct NamedNodeMap {
+ reflector_: Reflector,
+ owner: JS<Element>,
+}
+
+impl NamedNodeMap {
+ pub fn new_inherited(elem: &JSRef<Element>) -> NamedNodeMap {
+ NamedNodeMap {
+ reflector_: Reflector::new(),
+ owner: JS::from_rooted(elem),
+ }
+ }
+
+ pub fn new(window: &JSRef<Window>, elem: &JSRef<Element>) -> Temporary<NamedNodeMap> {
+ reflect_dom_object(box NamedNodeMap::new_inherited(elem),
+ &Window(*window), NamedNodeMapBinding::Wrap)
+ }
+}
+
+impl<'a> NamedNodeMapMethods for JSRef<'a, NamedNodeMap> {
+ fn Length(&self) -> u32 {
+ self.owner.root().attrs.borrow().len() as u32
+ }
+
+ fn Item(&self, index: u32) -> Option<Temporary<Attr>> {
+ self.owner.root().attrs.borrow().as_slice().get(index as uint).map(|x| Temporary::new(x.clone()))
+ }
+
+ fn IndexedGetter(&self, index: u32, found: &mut bool) -> Option<Temporary<Attr>> {
+ let item = self.Item(index);
+ *found = item.is_some();
+ item
+ }
+}
+
+impl Reflectable for NamedNodeMap {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ &self.reflector_
+ }
+}
diff --git a/components/script/dom/navigator.rs b/components/script/dom/navigator.rs
new file mode 100644
index 00000000000..d1d7596aa6e
--- /dev/null
+++ b/components/script/dom/navigator.rs
@@ -0,0 +1,58 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::NavigatorBinding;
+use dom::bindings::codegen::Bindings::NavigatorBinding::NavigatorMethods;
+use dom::bindings::global::Window;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
+use dom::window::Window;
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct Navigator {
+ pub reflector_: Reflector //XXXjdm cycle: window->navigator->window
+}
+
+impl Navigator {
+ pub fn new_inherited() -> Navigator {
+ Navigator {
+ reflector_: Reflector::new()
+ }
+ }
+
+ pub fn new(window: &JSRef<Window>) -> Temporary<Navigator> {
+ reflect_dom_object(box Navigator::new_inherited(),
+ &Window(*window),
+ NavigatorBinding::Wrap)
+ }
+}
+
+impl<'a> NavigatorMethods for JSRef<'a, Navigator> {
+ fn Product(&self) -> DOMString {
+ "Gecko".to_string()
+ }
+
+ fn TaintEnabled(&self) -> bool {
+ false
+ }
+
+ fn AppName(&self) -> DOMString {
+ "Netscape".to_string() // Like Gecko/Webkit
+ }
+
+ fn AppCodeName(&self) -> DOMString {
+ "Mozilla".to_string()
+ }
+
+ fn Platform(&self) -> DOMString {
+ "".to_string()
+ }
+}
+
+impl Reflectable for Navigator {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ &self.reflector_
+ }
+}
diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs
new file mode 100644
index 00000000000..96ee5f62ba2
--- /dev/null
+++ b/components/script/dom/node.rs
@@ -0,0 +1,2085 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! The core DOM types. Defines the basic DOM hierarchy as well as all the HTML elements.
+
+use dom::attr::Attr;
+use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
+use dom::bindings::codegen::Bindings::CharacterDataBinding::CharacterDataMethods;
+use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
+use dom::bindings::codegen::Bindings::ElementBinding::ElementMethods;
+use dom::bindings::codegen::Bindings::NodeBinding::{NodeConstants, NodeMethods};
+use dom::bindings::codegen::Bindings::ProcessingInstructionBinding::ProcessingInstructionMethods;
+use dom::bindings::codegen::InheritTypes::{CommentCast, DocumentCast, DocumentTypeCast};
+use dom::bindings::codegen::InheritTypes::{ElementCast, TextCast, NodeCast, ElementDerived};
+use dom::bindings::codegen::InheritTypes::{CharacterDataCast, NodeBase, NodeDerived};
+use dom::bindings::codegen::InheritTypes::{ProcessingInstructionCast, EventTargetCast};
+use dom::bindings::codegen::InheritTypes::{HTMLLegendElementDerived, HTMLFieldSetElementDerived};
+use dom::bindings::codegen::InheritTypes::HTMLOptGroupElementDerived;
+use dom::bindings::error::{Fallible, NotFound, HierarchyRequest, Syntax};
+use dom::bindings::global::{GlobalRef, Window};
+use dom::bindings::js::{JS, JSRef, RootedReference, Temporary, Root, OptionalUnrootable};
+use dom::bindings::js::{OptionalSettable, TemporaryPushable, OptionalRootedRootable};
+use dom::bindings::js::{ResultRootable, OptionalRootable};
+use dom::bindings::trace::Traceable;
+use dom::bindings::utils;
+use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
+use dom::characterdata::CharacterData;
+use dom::comment::Comment;
+use dom::document::{Document, DocumentHelpers, HTMLDocument, NonHTMLDocument};
+use dom::documentfragment::DocumentFragment;
+use dom::documenttype::DocumentType;
+use dom::element::{AttributeHandlers, Element, ElementTypeId};
+use dom::element::{HTMLAnchorElementTypeId, HTMLButtonElementTypeId, ElementHelpers};
+use dom::element::{HTMLInputElementTypeId, HTMLSelectElementTypeId};
+use dom::element::{HTMLTextAreaElementTypeId, HTMLOptGroupElementTypeId};
+use dom::element::{HTMLOptionElementTypeId, HTMLFieldSetElementTypeId};
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::nodelist::{NodeList};
+use dom::processinginstruction::ProcessingInstruction;
+use dom::text::Text;
+use dom::virtualmethods::{VirtualMethods, vtable_for};
+use dom::window::Window;
+use geom::rect::Rect;
+use html::hubbub_html_parser::build_element_from_tag;
+use layout_interface::{ContentBoxResponse, ContentBoxesResponse, LayoutRPC,
+ LayoutChan, ReapLayoutDataMsg, TrustedNodeAddress, UntrustedNodeAddress};
+use servo_util::geometry::Au;
+use servo_util::str::{DOMString, null_str_as_empty};
+use style::{parse_selector_list_from_str, matches};
+
+use js::jsapi::{JSContext, JSObject, JSRuntime};
+use js::jsfriendapi;
+use libc;
+use libc::uintptr_t;
+use std::cell::{Cell, RefCell, Ref, RefMut};
+use std::iter::{Map, Filter};
+use std::mem;
+use style;
+use style::ComputedValues;
+use sync::Arc;
+
+use serialize::{Encoder, Encodable};
+
+//
+// The basic Node structure
+//
+
+/// An HTML node.
+#[deriving(Encodable)]
+pub struct Node {
+ /// The JavaScript reflector for this node.
+ pub eventtarget: EventTarget,
+
+ /// The type of node that this is.
+ type_id: NodeTypeId,
+
+ /// The parent of this node.
+ parent_node: Cell<Option<JS<Node>>>,
+
+ /// The first child of this node.
+ first_child: Cell<Option<JS<Node>>>,
+
+ /// The last child of this node.
+ last_child: Cell<Option<JS<Node>>>,
+
+ /// The next sibling of this node.
+ next_sibling: Cell<Option<JS<Node>>>,
+
+ /// The previous sibling of this node.
+ prev_sibling: Cell<Option<JS<Node>>>,
+
+ /// The document that this node belongs to.
+ owner_doc: Cell<Option<JS<Document>>>,
+
+ /// The live list of children return by .childNodes.
+ child_list: Cell<Option<JS<NodeList>>>,
+
+ /// A bitfield of flags for node items.
+ flags: Traceable<RefCell<NodeFlags>>,
+
+ /// Layout information. Only the layout task may touch this data.
+ ///
+ /// Must be sent back to the layout task to be destroyed when this
+ /// node is finalized.
+ pub layout_data: LayoutDataRef,
+}
+
+impl<S: Encoder<E>, E> Encodable<S, E> for LayoutDataRef {
+ fn encode(&self, _s: &mut S) -> Result<(), E> {
+ Ok(())
+ }
+}
+
+impl NodeDerived for EventTarget {
+ fn is_node(&self) -> bool {
+ match self.type_id {
+ NodeTargetTypeId(_) => true,
+ _ => false
+ }
+ }
+}
+
+bitflags! {
+ #[doc = "Flags for node items."]
+ #[deriving(Encodable)]
+ flags NodeFlags: u8 {
+ #[doc = "Specifies whether this node is in a document."]
+ static IsInDoc = 0x01,
+ #[doc = "Specifies whether this node is in hover state."]
+ static InHoverState = 0x02,
+ #[doc = "Specifies whether this node is in disabled state."]
+ static InDisabledState = 0x04,
+ #[doc = "Specifies whether this node is in enabled state."]
+ static InEnabledState = 0x08
+ }
+}
+
+impl NodeFlags {
+ pub fn new(type_id: NodeTypeId) -> NodeFlags {
+ match type_id {
+ DocumentNodeTypeId => IsInDoc,
+ // The following elements are enabled by default.
+ ElementNodeTypeId(HTMLButtonElementTypeId) |
+ ElementNodeTypeId(HTMLInputElementTypeId) |
+ ElementNodeTypeId(HTMLSelectElementTypeId) |
+ ElementNodeTypeId(HTMLTextAreaElementTypeId) |
+ ElementNodeTypeId(HTMLOptGroupElementTypeId) |
+ ElementNodeTypeId(HTMLOptionElementTypeId) |
+ //ElementNodeTypeId(HTMLMenuItemElementTypeId) |
+ ElementNodeTypeId(HTMLFieldSetElementTypeId) => InEnabledState,
+ _ => NodeFlags::empty(),
+ }
+ }
+}
+
+#[unsafe_destructor]
+impl Drop for Node {
+ fn drop(&mut self) {
+ unsafe {
+ self.reap_layout_data();
+ }
+ }
+}
+
+/// suppress observers flag
+/// http://dom.spec.whatwg.org/#concept-node-insert
+/// http://dom.spec.whatwg.org/#concept-node-remove
+enum SuppressObserver {
+ Suppressed,
+ Unsuppressed
+}
+
+/// Layout data that is shared between the script and layout tasks.
+pub struct SharedLayoutData {
+ /// The results of CSS styling for this node.
+ pub style: Option<Arc<ComputedValues>>,
+}
+
+/// Encapsulates the abstract layout data.
+pub struct LayoutData {
+ chan: Option<LayoutChan>,
+ _shared_data: SharedLayoutData,
+ _data: *const (),
+}
+
+pub struct LayoutDataRef {
+ pub data_cell: RefCell<Option<LayoutData>>,
+}
+
+impl LayoutDataRef {
+ pub fn new() -> LayoutDataRef {
+ LayoutDataRef {
+ data_cell: RefCell::new(None),
+ }
+ }
+
+ /// Returns true if there is layout data present.
+ #[inline]
+ pub fn is_present(&self) -> bool {
+ self.data_cell.borrow().is_some()
+ }
+
+ /// Take the chan out of the layout data if it is present.
+ pub fn take_chan(&self) -> Option<LayoutChan> {
+ let mut layout_data = self.data_cell.borrow_mut();
+ match *layout_data {
+ None => None,
+ Some(..) => Some(layout_data.get_mut_ref().chan.take_unwrap()),
+ }
+ }
+
+ /// Borrows the layout data immutably, *asserting that there are no mutators*. Bad things will
+ /// happen if you try to mutate the layout data while this is held. This is the only thread-
+ /// safe layout data accessor.
+ #[inline]
+ pub unsafe fn borrow_unchecked(&self) -> *const Option<LayoutData> {
+ mem::transmute(&self.data_cell)
+ }
+
+ /// Borrows the layout data immutably. This function is *not* thread-safe.
+ #[inline]
+ pub fn borrow<'a>(&'a self) -> Ref<'a,Option<LayoutData>> {
+ self.data_cell.borrow()
+ }
+
+ /// Borrows the layout data mutably. This function is *not* thread-safe.
+ ///
+ /// FIXME(pcwalton): We should really put this behind a `MutLayoutView` phantom type, to
+ /// prevent CSS selector matching from mutably accessing nodes it's not supposed to and racing
+ /// on it. This has already resulted in one bug!
+ #[inline]
+ pub fn borrow_mut<'a>(&'a self) -> RefMut<'a,Option<LayoutData>> {
+ self.data_cell.borrow_mut()
+ }
+}
+
+/// The different types of nodes.
+#[deriving(PartialEq,Encodable)]
+pub enum NodeTypeId {
+ DoctypeNodeTypeId,
+ DocumentFragmentNodeTypeId,
+ CommentNodeTypeId,
+ DocumentNodeTypeId,
+ ElementNodeTypeId(ElementTypeId),
+ TextNodeTypeId,
+ ProcessingInstructionNodeTypeId,
+}
+
+trait PrivateNodeHelpers {
+ fn node_inserted(&self);
+ fn node_removed(&self, parent_in_doc: bool);
+ fn add_child(&self, new_child: &JSRef<Node>, before: Option<JSRef<Node>>);
+ fn remove_child(&self, child: &JSRef<Node>);
+}
+
+impl<'a> PrivateNodeHelpers for JSRef<'a, Node> {
+ // http://dom.spec.whatwg.org/#node-is-inserted
+ fn node_inserted(&self) {
+ assert!(self.parent_node().is_some());
+ let document = document_from_node(self).root();
+ let is_in_doc = self.is_in_doc();
+
+ for node in self.traverse_preorder() {
+ vtable_for(&node).bind_to_tree(is_in_doc);
+ }
+
+ let parent = self.parent_node().root();
+ parent.map(|parent| vtable_for(&*parent).child_inserted(self));
+
+ document.deref().content_changed();
+ }
+
+ // http://dom.spec.whatwg.org/#node-is-removed
+ fn node_removed(&self, parent_in_doc: bool) {
+ assert!(self.parent_node().is_none());
+ let document = document_from_node(self).root();
+
+ for node in self.traverse_preorder() {
+ vtable_for(&node).unbind_from_tree(parent_in_doc);
+ }
+
+ document.deref().content_changed();
+ }
+
+ //
+ // Pointer stitching
+ //
+
+ /// Adds a new child to the end of this node's list of children.
+ ///
+ /// Fails unless `new_child` is disconnected from the tree.
+ fn add_child(&self, new_child: &JSRef<Node>, before: Option<JSRef<Node>>) {
+ let doc = self.owner_doc().root();
+ doc.deref().wait_until_safe_to_modify_dom();
+
+ assert!(new_child.parent_node().is_none());
+ assert!(new_child.prev_sibling().is_none());
+ assert!(new_child.next_sibling().is_none());
+ match before {
+ Some(ref before) => {
+ assert!(before.parent_node().root().root_ref() == Some(*self));
+ match before.prev_sibling().root() {
+ None => {
+ assert!(Some(*before) == self.first_child().root().root_ref());
+ self.first_child.assign(Some(*new_child));
+ },
+ Some(ref prev_sibling) => {
+ prev_sibling.next_sibling.assign(Some(*new_child));
+ new_child.prev_sibling.assign(Some(**prev_sibling));
+ },
+ }
+ before.prev_sibling.assign(Some(*new_child));
+ new_child.next_sibling.assign(Some(*before));
+ },
+ None => {
+ match self.last_child().root() {
+ None => self.first_child.assign(Some(*new_child)),
+ Some(ref last_child) => {
+ assert!(last_child.next_sibling().is_none());
+ last_child.next_sibling.assign(Some(*new_child));
+ new_child.prev_sibling.assign(Some(**last_child));
+ }
+ }
+
+ self.last_child.assign(Some(*new_child));
+ },
+ }
+
+ new_child.parent_node.assign(Some(*self));
+ }
+
+ /// Removes the given child from this node's list of children.
+ ///
+ /// Fails unless `child` is a child of this node.
+ fn remove_child(&self, child: &JSRef<Node>) {
+ let doc = self.owner_doc().root();
+ doc.deref().wait_until_safe_to_modify_dom();
+
+ assert!(child.parent_node().root().root_ref() == Some(*self));
+
+ match child.prev_sibling.get().root() {
+ None => {
+ self.first_child.assign(child.next_sibling.get());
+ }
+ Some(ref prev_sibling) => {
+ prev_sibling.next_sibling.assign(child.next_sibling.get());
+ }
+ }
+
+ match child.next_sibling.get().root() {
+ None => {
+ self.last_child.assign(child.prev_sibling.get());
+ }
+ Some(ref next_sibling) => {
+ next_sibling.prev_sibling.assign(child.prev_sibling.get());
+ }
+ }
+
+ child.prev_sibling.set(None);
+ child.next_sibling.set(None);
+ child.parent_node.set(None);
+ }
+}
+
+pub trait NodeHelpers<'m, 'n> {
+ fn ancestors(&self) -> AncestorIterator<'n>;
+ fn children(&self) -> AbstractNodeChildrenIterator<'n>;
+ fn child_elements(&self) -> ChildElementIterator<'m, 'n>;
+ fn following_siblings(&self) -> AbstractNodeChildrenIterator<'n>;
+ fn is_in_doc(&self) -> bool;
+ fn is_inclusive_ancestor_of(&self, parent: &JSRef<Node>) -> bool;
+ fn is_parent_of(&self, child: &JSRef<Node>) -> bool;
+
+ fn type_id(&self) -> NodeTypeId;
+
+ fn parent_node(&self) -> Option<Temporary<Node>>;
+ fn first_child(&self) -> Option<Temporary<Node>>;
+ fn last_child(&self) -> Option<Temporary<Node>>;
+ fn prev_sibling(&self) -> Option<Temporary<Node>>;
+ fn next_sibling(&self) -> Option<Temporary<Node>>;
+
+ fn owner_doc(&self) -> Temporary<Document>;
+ fn set_owner_doc(&self, document: &JSRef<Document>);
+ fn is_in_html_doc(&self) -> bool;
+
+ fn wait_until_safe_to_modify_dom(&self);
+
+ fn is_element(&self) -> bool;
+ fn is_document(&self) -> bool;
+ fn is_doctype(&self) -> bool;
+ fn is_text(&self) -> bool;
+ fn is_anchor_element(&self) -> bool;
+
+ fn get_hover_state(&self) -> bool;
+ fn set_hover_state(&self, state: bool);
+
+ fn get_disabled_state(&self) -> bool;
+ fn set_disabled_state(&self, state: bool);
+
+ fn get_enabled_state(&self) -> bool;
+ fn set_enabled_state(&self, state: bool);
+
+ fn dump(&self);
+ fn dump_indent(&self, indent: uint);
+ fn debug_str(&self) -> String;
+
+ fn traverse_preorder(&self) -> TreeIterator<'n>;
+ fn sequential_traverse_postorder(&self) -> TreeIterator<'n>;
+ fn inclusively_following_siblings(&self) -> AbstractNodeChildrenIterator<'n>;
+
+ fn to_trusted_node_address(&self) -> TrustedNodeAddress;
+
+ fn get_bounding_content_box(&self) -> Rect<Au>;
+ fn get_content_boxes(&self) -> Vec<Rect<Au>>;
+
+ fn query_selector(&self, selectors: DOMString) -> Fallible<Option<Temporary<Element>>>;
+ fn query_selector_all(&self, selectors: DOMString) -> Fallible<Temporary<NodeList>>;
+
+ fn remove_self(&self);
+}
+
+impl<'m, 'n> NodeHelpers<'m, 'n> for JSRef<'n, Node> {
+ /// Dumps the subtree rooted at this node, for debugging.
+ fn dump(&self) {
+ self.dump_indent(0);
+ }
+
+ /// Dumps the node tree, for debugging, with indentation.
+ fn dump_indent(&self, indent: uint) {
+ let mut s = String::new();
+ for _ in range(0, indent) {
+ s.push_str(" ");
+ }
+
+ s.push_str(self.debug_str().as_slice());
+ debug!("{:s}", s);
+
+ // FIXME: this should have a pure version?
+ for kid in self.children() {
+ kid.dump_indent(indent + 1u)
+ }
+ }
+
+ /// Returns a string that describes this node.
+ fn debug_str(&self) -> String {
+ format!("{:?}", self.type_id)
+ }
+
+ fn is_in_doc(&self) -> bool {
+ self.deref().flags.deref().borrow().contains(IsInDoc)
+ }
+
+ /// Returns the type ID of this node. Fails if this node is borrowed mutably.
+ fn type_id(&self) -> NodeTypeId {
+ self.deref().type_id
+ }
+
+ fn parent_node(&self) -> Option<Temporary<Node>> {
+ self.deref().parent_node.get().map(|node| Temporary::new(node))
+ }
+
+ fn first_child(&self) -> Option<Temporary<Node>> {
+ self.deref().first_child.get().map(|node| Temporary::new(node))
+ }
+
+ fn last_child(&self) -> Option<Temporary<Node>> {
+ self.deref().last_child.get().map(|node| Temporary::new(node))
+ }
+
+ /// Returns the previous sibling of this node. Fails if this node is borrowed mutably.
+ fn prev_sibling(&self) -> Option<Temporary<Node>> {
+ self.deref().prev_sibling.get().map(|node| Temporary::new(node))
+ }
+
+ /// Returns the next sibling of this node. Fails if this node is borrowed mutably.
+ fn next_sibling(&self) -> Option<Temporary<Node>> {
+ self.deref().next_sibling.get().map(|node| Temporary::new(node))
+ }
+
+ #[inline]
+ fn is_element(&self) -> bool {
+ match self.type_id {
+ ElementNodeTypeId(..) => true,
+ _ => false
+ }
+ }
+
+ #[inline]
+ fn is_document(&self) -> bool {
+ self.type_id == DocumentNodeTypeId
+ }
+
+ #[inline]
+ fn is_anchor_element(&self) -> bool {
+ self.type_id == ElementNodeTypeId(HTMLAnchorElementTypeId)
+ }
+
+ #[inline]
+ fn is_doctype(&self) -> bool {
+ self.type_id == DoctypeNodeTypeId
+ }
+
+ #[inline]
+ fn is_text(&self) -> bool {
+ self.type_id == TextNodeTypeId
+ }
+
+ fn get_hover_state(&self) -> bool {
+ self.flags.deref().borrow().contains(InHoverState)
+ }
+
+ fn set_hover_state(&self, state: bool) {
+ if state {
+ self.flags.deref().borrow_mut().insert(InHoverState);
+ } else {
+ self.flags.deref().borrow_mut().remove(InHoverState);
+ }
+ }
+
+ fn get_disabled_state(&self) -> bool {
+ self.flags.deref().borrow().contains(InDisabledState)
+ }
+
+ fn set_disabled_state(&self, state: bool) {
+ if state {
+ self.flags.deref().borrow_mut().insert(InDisabledState);
+ } else {
+ self.flags.deref().borrow_mut().remove(InDisabledState);
+ }
+ }
+
+ fn get_enabled_state(&self) -> bool {
+ self.flags.deref().borrow().contains(InEnabledState)
+ }
+
+ fn set_enabled_state(&self, state: bool) {
+ if state {
+ self.flags.deref().borrow_mut().insert(InEnabledState);
+ } else {
+ self.flags.deref().borrow_mut().remove(InEnabledState);
+ }
+ }
+
+ /// Iterates over this node and all its descendants, in preorder.
+ fn traverse_preorder(&self) -> TreeIterator<'n> {
+ let mut nodes = vec!();
+ gather_abstract_nodes(self, &mut nodes, false);
+ TreeIterator::new(nodes)
+ }
+
+ /// Iterates over this node and all its descendants, in postorder.
+ fn sequential_traverse_postorder(&self) -> TreeIterator<'n> {
+ let mut nodes = vec!();
+ gather_abstract_nodes(self, &mut nodes, true);
+ TreeIterator::new(nodes)
+ }
+
+ fn inclusively_following_siblings(&self) -> AbstractNodeChildrenIterator<'n> {
+ AbstractNodeChildrenIterator {
+ current_node: Some(self.clone()),
+ }
+ }
+
+ fn is_inclusive_ancestor_of(&self, parent: &JSRef<Node>) -> bool {
+ self == parent || parent.ancestors().any(|ancestor| &ancestor == self)
+ }
+
+ fn following_siblings(&self) -> AbstractNodeChildrenIterator<'n> {
+ AbstractNodeChildrenIterator {
+ current_node: self.next_sibling().root().map(|next| next.deref().clone()),
+ }
+ }
+
+ fn is_parent_of(&self, child: &JSRef<Node>) -> bool {
+ match child.parent_node() {
+ Some(ref parent) if *parent == Temporary::from_rooted(self) => true,
+ _ => false
+ }
+ }
+
+ fn to_trusted_node_address(&self) -> TrustedNodeAddress {
+ TrustedNodeAddress(self.deref() as *const Node as *const libc::c_void)
+ }
+
+ fn get_bounding_content_box(&self) -> Rect<Au> {
+ let window = window_from_node(self).root();
+ let page = window.deref().page();
+ let addr = self.to_trusted_node_address();
+
+ let ContentBoxResponse(rect) = page.layout_rpc.content_box(addr);
+ rect
+ }
+
+ fn get_content_boxes(&self) -> Vec<Rect<Au>> {
+ let window = window_from_node(self).root();
+ let page = window.deref().page();
+ let addr = self.to_trusted_node_address();
+ let ContentBoxesResponse(rects) = page.layout_rpc.content_boxes(addr);
+ rects
+ }
+
+ // http://dom.spec.whatwg.org/#dom-parentnode-queryselector
+ fn query_selector(&self, selectors: DOMString) -> Fallible<Option<Temporary<Element>>> {
+ // Step 1.
+ match parse_selector_list_from_str(selectors.as_slice()) {
+ // Step 2.
+ Err(()) => return Err(Syntax),
+ // Step 3.
+ Ok(ref selectors) => {
+ let root = self.ancestors().last().unwrap_or(self.clone());
+ for node in root.traverse_preorder() {
+ if node.is_element() && matches(selectors, &node) {
+ let elem: &JSRef<Element> = ElementCast::to_ref(&node).unwrap();
+ return Ok(Some(Temporary::from_rooted(elem)));
+ }
+ }
+ }
+ }
+ Ok(None)
+ }
+
+ // http://dom.spec.whatwg.org/#dom-parentnode-queryselectorall
+ fn query_selector_all(&self, selectors: DOMString) -> Fallible<Temporary<NodeList>> {
+ // Step 1.
+ let nodes;
+ let root = self.ancestors().last().unwrap_or(self.clone());
+ match parse_selector_list_from_str(selectors.as_slice()) {
+ // Step 2.
+ Err(()) => return Err(Syntax),
+ // Step 3.
+ Ok(ref selectors) => {
+ nodes = root.traverse_preorder().filter(
+ |node| node.is_element() && matches(selectors, node)).collect()
+ }
+ }
+ let window = window_from_node(self).root();
+ Ok(NodeList::new_simple_list(&window.root_ref(), nodes))
+ }
+
+ fn ancestors(&self) -> AncestorIterator<'n> {
+ AncestorIterator {
+ current: self.parent_node.get().map(|node| (*node.root()).clone()),
+ }
+ }
+
+ fn owner_doc(&self) -> Temporary<Document> {
+ Temporary::new(self.owner_doc.get().get_ref().clone())
+ }
+
+ fn set_owner_doc(&self, document: &JSRef<Document>) {
+ self.owner_doc.assign(Some(document.clone()));
+ }
+
+ fn is_in_html_doc(&self) -> bool {
+ self.owner_doc().root().is_html_document
+ }
+
+ fn children(&self) -> AbstractNodeChildrenIterator<'n> {
+ AbstractNodeChildrenIterator {
+ current_node: self.first_child.get().map(|node| (*node.root()).clone()),
+ }
+ }
+
+ fn child_elements(&self) -> ChildElementIterator<'m, 'n> {
+ self.children()
+ .filter(|node| {
+ node.is_element()
+ })
+ .map(|node| {
+ let elem: &JSRef<Element> = ElementCast::to_ref(&node).unwrap();
+ elem.clone()
+ })
+ }
+
+ fn wait_until_safe_to_modify_dom(&self) {
+ let document = self.owner_doc().root();
+ document.deref().wait_until_safe_to_modify_dom();
+ }
+
+ fn remove_self(&self) {
+ match self.parent_node().root() {
+ Some(ref parent) => parent.remove_child(self),
+ None => ()
+ }
+ }
+}
+
+/// If the given untrusted node address represents a valid DOM node in the given runtime,
+/// returns it.
+pub fn from_untrusted_node_address(runtime: *mut JSRuntime, candidate: UntrustedNodeAddress)
+ -> Temporary<Node> {
+ unsafe {
+ let candidate: uintptr_t = mem::transmute(candidate);
+ let object: *mut JSObject = jsfriendapi::bindgen::JS_GetAddressableObject(runtime,
+ candidate);
+ if object.is_null() {
+ fail!("Attempted to create a `JS<Node>` from an invalid pointer!")
+ }
+ let boxed_node: *const Node = utils::unwrap(object);
+ Temporary::new(JS::from_raw(boxed_node))
+ }
+}
+
+pub trait LayoutNodeHelpers {
+ unsafe fn type_id_for_layout(&self) -> NodeTypeId;
+
+ unsafe fn parent_node_ref(&self) -> Option<JS<Node>>;
+ unsafe fn first_child_ref(&self) -> Option<JS<Node>>;
+ unsafe fn last_child_ref(&self) -> Option<JS<Node>>;
+ unsafe fn prev_sibling_ref(&self) -> Option<JS<Node>>;
+ unsafe fn next_sibling_ref(&self) -> Option<JS<Node>>;
+
+ unsafe fn owner_doc_for_layout(&self) -> JS<Document>;
+
+ unsafe fn is_element_for_layout(&self) -> bool;
+}
+
+impl LayoutNodeHelpers for JS<Node> {
+ #[inline]
+ unsafe fn type_id_for_layout(&self) -> NodeTypeId {
+ (*self.unsafe_get()).type_id
+ }
+
+ #[inline]
+ unsafe fn is_element_for_layout(&self) -> bool {
+ (*self.unsafe_get()).is_element()
+ }
+
+ #[inline]
+ unsafe fn parent_node_ref(&self) -> Option<JS<Node>> {
+ (*self.unsafe_get()).parent_node.get()
+ }
+
+ #[inline]
+ unsafe fn first_child_ref(&self) -> Option<JS<Node>> {
+ (*self.unsafe_get()).first_child.get()
+ }
+
+ #[inline]
+ unsafe fn last_child_ref(&self) -> Option<JS<Node>> {
+ (*self.unsafe_get()).last_child.get()
+ }
+
+ #[inline]
+ unsafe fn prev_sibling_ref(&self) -> Option<JS<Node>> {
+ (*self.unsafe_get()).prev_sibling.get()
+ }
+
+ #[inline]
+ unsafe fn next_sibling_ref(&self) -> Option<JS<Node>> {
+ (*self.unsafe_get()).next_sibling.get()
+ }
+
+ #[inline]
+ unsafe fn owner_doc_for_layout(&self) -> JS<Document> {
+ (*self.unsafe_get()).owner_doc.get().unwrap()
+ }
+}
+
+pub trait RawLayoutNodeHelpers {
+ unsafe fn get_hover_state_for_layout(&self) -> bool;
+ unsafe fn get_disabled_state_for_layout(&self) -> bool;
+ unsafe fn get_enabled_state_for_layout(&self) -> bool;
+ fn type_id_for_layout(&self) -> NodeTypeId;
+}
+
+impl RawLayoutNodeHelpers for Node {
+ unsafe fn get_hover_state_for_layout(&self) -> bool {
+ (*self.unsafe_get_flags()).contains(InHoverState)
+ }
+ unsafe fn get_disabled_state_for_layout(&self) -> bool {
+ (*self.unsafe_get_flags()).contains(InDisabledState)
+ }
+ unsafe fn get_enabled_state_for_layout(&self) -> bool {
+ (*self.unsafe_get_flags()).contains(InEnabledState)
+ }
+
+ fn type_id_for_layout(&self) -> NodeTypeId {
+ self.type_id
+ }
+}
+
+
+//
+// Iteration and traversal
+//
+
+pub type ChildElementIterator<'a, 'b> = Map<'a, JSRef<'b, Node>,
+ JSRef<'b, Element>,
+ Filter<'a, JSRef<'b, Node>, AbstractNodeChildrenIterator<'b>>>;
+
+pub struct AbstractNodeChildrenIterator<'a> {
+ current_node: Option<JSRef<'a, Node>>,
+}
+
+impl<'a> Iterator<JSRef<'a, Node>> for AbstractNodeChildrenIterator<'a> {
+ fn next(&mut self) -> Option<JSRef<'a, Node>> {
+ let node = self.current_node.clone();
+ self.current_node = node.clone().and_then(|node| {
+ node.next_sibling().map(|node| (*node.root()).clone())
+ });
+ node
+ }
+}
+
+pub struct AncestorIterator<'a> {
+ current: Option<JSRef<'a, Node>>,
+}
+
+impl<'a> Iterator<JSRef<'a, Node>> for AncestorIterator<'a> {
+ fn next(&mut self) -> Option<JSRef<'a, Node>> {
+ if self.current.is_none() {
+ return None;
+ }
+
+ // FIXME: Do we need two clones here?
+ let x = self.current.get_ref().clone();
+ self.current = x.parent_node().map(|node| (*node.root()).clone());
+ Some(x)
+ }
+}
+
+// FIXME: Do this without precomputing a vector of refs.
+// Easy for preorder; harder for postorder.
+pub struct TreeIterator<'a> {
+ nodes: Vec<JSRef<'a, Node>>,
+ index: uint,
+}
+
+impl<'a> TreeIterator<'a> {
+ fn new(nodes: Vec<JSRef<'a, Node>>) -> TreeIterator<'a> {
+ TreeIterator {
+ nodes: nodes,
+ index: 0,
+ }
+ }
+}
+
+impl<'a> Iterator<JSRef<'a, Node>> for TreeIterator<'a> {
+ fn next(&mut self) -> Option<JSRef<'a, Node>> {
+ if self.index >= self.nodes.len() {
+ None
+ } else {
+ let v = self.nodes[self.index];
+ let v = v.clone();
+ self.index += 1;
+ Some(v)
+ }
+ }
+}
+
+pub struct NodeIterator {
+ pub start_node: JS<Node>,
+ pub current_node: Option<JS<Node>>,
+ pub depth: uint,
+ include_start: bool,
+ include_descendants_of_void: bool
+}
+
+impl NodeIterator {
+ pub fn new<'a>(start_node: &JSRef<'a, Node>,
+ include_start: bool,
+ include_descendants_of_void: bool) -> NodeIterator {
+ NodeIterator {
+ start_node: JS::from_rooted(start_node),
+ current_node: None,
+ depth: 0,
+ include_start: include_start,
+ include_descendants_of_void: include_descendants_of_void
+ }
+ }
+
+ fn next_child<'b>(&self, node: &JSRef<'b, Node>) -> Option<JSRef<'b, Node>> {
+ if !self.include_descendants_of_void && node.is_element() {
+ let elem: &JSRef<Element> = ElementCast::to_ref(node).unwrap();
+ if elem.deref().is_void() {
+ None
+ } else {
+ node.first_child().map(|child| (*child.root()).clone())
+ }
+ } else {
+ node.first_child().map(|child| (*child.root()).clone())
+ }
+ }
+}
+
+impl<'a> Iterator<JSRef<'a, Node>> for NodeIterator {
+ fn next(&mut self) -> Option<JSRef<'a, Node>> {
+ self.current_node = match self.current_node.as_ref().map(|node| node.root()) {
+ None => {
+ if self.include_start {
+ Some(self.start_node)
+ } else {
+ self.next_child(&*self.start_node.root())
+ .map(|child| JS::from_rooted(&child))
+ }
+ },
+ Some(node) => {
+ match self.next_child(&*node) {
+ Some(child) => {
+ self.depth += 1;
+ Some(JS::from_rooted(&child))
+ },
+ None if JS::from_rooted(&*node) == self.start_node => None,
+ None => {
+ match node.deref().next_sibling().root() {
+ Some(sibling) => Some(JS::from_rooted(&*sibling)),
+ None => {
+ let mut candidate = node.deref().clone();
+ while candidate.next_sibling().is_none() {
+ candidate = (*candidate.parent_node()
+ .expect("Got to root without reaching start node")
+ .root()).clone();
+ self.depth -= 1;
+ if JS::from_rooted(&candidate) == self.start_node {
+ break;
+ }
+ }
+ if JS::from_rooted(&candidate) != self.start_node {
+ candidate.next_sibling().map(|node| JS::from_rooted(node.root().deref()))
+ } else {
+ None
+ }
+ }
+ }
+ }
+ }
+ }
+ };
+ self.current_node.map(|node| (*node.root()).clone())
+ }
+}
+
+fn gather_abstract_nodes<'a>(cur: &JSRef<'a, Node>, refs: &mut Vec<JSRef<'a, Node>>, postorder: bool) {
+ if !postorder {
+ refs.push(cur.clone());
+ }
+ for kid in cur.children() {
+ gather_abstract_nodes(&kid, refs, postorder)
+ }
+ if postorder {
+ refs.push(cur.clone());
+ }
+}
+
+/// Specifies whether children must be recursively cloned or not.
+#[deriving(PartialEq)]
+pub enum CloneChildrenFlag {
+ CloneChildren,
+ DoNotCloneChildren
+}
+
+fn as_uintptr<T>(t: &T) -> uintptr_t { t as *const T as uintptr_t }
+
+impl Node {
+ pub fn reflect_node<N: Reflectable+NodeBase>
+ (node: Box<N>,
+ document: &JSRef<Document>,
+ wrap_fn: extern "Rust" fn(*mut JSContext, &GlobalRef, Box<N>) -> Temporary<N>)
+ -> Temporary<N> {
+ let window = document.window.root();
+ reflect_dom_object(node, &Window(*window), wrap_fn)
+ }
+
+ pub fn new_inherited(type_id: NodeTypeId, doc: &JSRef<Document>) -> Node {
+ Node::new_(type_id, Some(doc.clone()))
+ }
+
+ pub fn new_without_doc(type_id: NodeTypeId) -> Node {
+ Node::new_(type_id, None)
+ }
+
+ fn new_(type_id: NodeTypeId, doc: Option<JSRef<Document>>) -> Node {
+ Node {
+ eventtarget: EventTarget::new_inherited(NodeTargetTypeId(type_id)),
+ type_id: type_id,
+
+ parent_node: Cell::new(None),
+ first_child: Cell::new(None),
+ last_child: Cell::new(None),
+ next_sibling: Cell::new(None),
+ prev_sibling: Cell::new(None),
+ owner_doc: Cell::new(doc.unrooted()),
+ child_list: Cell::new(None),
+
+ flags: Traceable::new(RefCell::new(NodeFlags::new(type_id))),
+
+ layout_data: LayoutDataRef::new(),
+ }
+ }
+
+ // http://dom.spec.whatwg.org/#concept-node-adopt
+ pub fn adopt(node: &JSRef<Node>, document: &JSRef<Document>) {
+ // Step 1.
+ match node.parent_node().root() {
+ Some(parent) => {
+ Node::remove(node, &*parent, Unsuppressed);
+ }
+ None => (),
+ }
+
+ // Step 2.
+ let node_doc = document_from_node(node).root();
+ if &*node_doc != document {
+ for descendant in node.traverse_preorder() {
+ descendant.set_owner_doc(document);
+ }
+ }
+
+ // Step 3.
+ // If node is an element, it is _affected by a base URL change_.
+ }
+
+ // http://dom.spec.whatwg.org/#concept-node-pre-insert
+ fn pre_insert(node: &JSRef<Node>, parent: &JSRef<Node>, child: Option<JSRef<Node>>)
+ -> Fallible<Temporary<Node>> {
+ // Step 1.
+ match parent.type_id() {
+ DocumentNodeTypeId |
+ DocumentFragmentNodeTypeId |
+ ElementNodeTypeId(..) => (),
+ _ => return Err(HierarchyRequest)
+ }
+
+ // Step 2.
+ if node.is_inclusive_ancestor_of(parent) {
+ return Err(HierarchyRequest);
+ }
+
+ // Step 3.
+ match child {
+ Some(ref child) if !parent.is_parent_of(child) => return Err(NotFound),
+ _ => ()
+ }
+
+ // Step 4-5.
+ match node.type_id() {
+ TextNodeTypeId => {
+ match node.parent_node().root() {
+ Some(ref parent) if parent.is_document() => return Err(HierarchyRequest),
+ _ => ()
+ }
+ }
+ DoctypeNodeTypeId => {
+ match node.parent_node().root() {
+ Some(ref parent) if !parent.is_document() => return Err(HierarchyRequest),
+ _ => ()
+ }
+ }
+ DocumentFragmentNodeTypeId |
+ ElementNodeTypeId(_) |
+ ProcessingInstructionNodeTypeId |
+ CommentNodeTypeId => (),
+ DocumentNodeTypeId => return Err(HierarchyRequest)
+ }
+
+ // Step 6.
+ match parent.type_id() {
+ DocumentNodeTypeId => {
+ match node.type_id() {
+ // Step 6.1
+ DocumentFragmentNodeTypeId => {
+ // Step 6.1.1(b)
+ if node.children().any(|c| c.is_text()) {
+ return Err(HierarchyRequest);
+ }
+ match node.child_elements().count() {
+ 0 => (),
+ // Step 6.1.2
+ 1 => {
+ // FIXME: change to empty() when https://github.com/mozilla/rust/issues/11218
+ // will be fixed
+ if parent.child_elements().count() > 0 {
+ return Err(HierarchyRequest);
+ }
+ match child {
+ Some(ref child) => {
+ if child.inclusively_following_siblings()
+ .any(|child| child.is_doctype()) {
+ return Err(HierarchyRequest)
+ }
+ }
+ _ => (),
+ }
+ },
+ // Step 6.1.1(a)
+ _ => return Err(HierarchyRequest),
+ }
+ },
+ // Step 6.2
+ ElementNodeTypeId(_) => {
+ // FIXME: change to empty() when https://github.com/mozilla/rust/issues/11218
+ // will be fixed
+ if parent.child_elements().count() > 0 {
+ return Err(HierarchyRequest);
+ }
+ match child {
+ Some(ref child) => {
+ if child.inclusively_following_siblings()
+ .any(|child| child.is_doctype()) {
+ return Err(HierarchyRequest)
+ }
+ }
+ _ => (),
+ }
+ },
+ // Step 6.3
+ DoctypeNodeTypeId => {
+ if parent.children().any(|c| c.is_doctype()) {
+ return Err(HierarchyRequest);
+ }
+ match child {
+ Some(ref child) => {
+ if parent.children()
+ .take_while(|c| c != child)
+ .any(|c| c.is_element()) {
+ return Err(HierarchyRequest);
+ }
+ },
+ None => {
+ // FIXME: change to empty() when https://github.com/mozilla/rust/issues/11218
+ // will be fixed
+ if parent.child_elements().count() > 0 {
+ return Err(HierarchyRequest);
+ }
+ },
+ }
+ },
+ TextNodeTypeId |
+ ProcessingInstructionNodeTypeId |
+ CommentNodeTypeId => (),
+ DocumentNodeTypeId => unreachable!(),
+ }
+ },
+ _ => (),
+ }
+
+ // Step 7-8.
+ let referenceChild = match child {
+ Some(ref child) if child == node => node.next_sibling().map(|node| (*node.root()).clone()),
+ _ => child
+ };
+
+ // Step 9.
+ let document = document_from_node(parent).root();
+ Node::adopt(node, &*document);
+
+ // Step 10.
+ Node::insert(node, parent, referenceChild, Unsuppressed);
+
+ // Step 11.
+ return Ok(Temporary::from_rooted(node))
+ }
+
+ // http://dom.spec.whatwg.org/#concept-node-insert
+ fn insert(node: &JSRef<Node>,
+ parent: &JSRef<Node>,
+ child: Option<JSRef<Node>>,
+ suppress_observers: SuppressObserver) {
+ // XXX assert owner_doc
+ // Step 1-3: ranges.
+ // Step 4.
+ let mut nodes = match node.type_id() {
+ DocumentFragmentNodeTypeId => node.children().collect(),
+ _ => vec!(node.clone()),
+ };
+
+ // Step 5: DocumentFragment, mutation records.
+ // Step 6: DocumentFragment.
+ match node.type_id() {
+ DocumentFragmentNodeTypeId => {
+ for c in node.children() {
+ Node::remove(&c, node, Suppressed);
+ }
+ },
+ _ => (),
+ }
+
+ // Step 7: mutation records.
+ // Step 8.
+ for node in nodes.mut_iter() {
+ parent.add_child(node, child);
+ let is_in_doc = parent.is_in_doc();
+ for kid in node.traverse_preorder() {
+ if is_in_doc {
+ kid.flags.deref().borrow_mut().insert(IsInDoc);
+ } else {
+ kid.flags.deref().borrow_mut().remove(IsInDoc);
+ }
+ }
+ }
+
+ // Step 9.
+ match suppress_observers {
+ Unsuppressed => {
+ for node in nodes.iter() {
+ node.node_inserted();
+ }
+ }
+ Suppressed => ()
+ }
+ }
+
+ // http://dom.spec.whatwg.org/#concept-node-replace-all
+ fn replace_all(node: Option<JSRef<Node>>, parent: &JSRef<Node>) {
+
+ // Step 1.
+ match node {
+ Some(ref node) => {
+ let document = document_from_node(parent).root();
+ Node::adopt(node, &*document);
+ }
+ None => (),
+ }
+
+ // Step 2.
+ let removedNodes: Vec<JSRef<Node>> = parent.children().collect();
+
+ // Step 3.
+ let addedNodes = match node {
+ None => vec!(),
+ Some(ref node) => match node.type_id() {
+ DocumentFragmentNodeTypeId => node.children().collect(),
+ _ => vec!(node.clone()),
+ },
+ };
+
+ // Step 4.
+ for child in parent.children() {
+ Node::remove(&child, parent, Suppressed);
+ }
+
+ // Step 5.
+ match node {
+ Some(ref node) => Node::insert(node, parent, None, Suppressed),
+ None => (),
+ }
+
+ // Step 6: mutation records.
+
+ // Step 7.
+ let parent_in_doc = parent.is_in_doc();
+ for removedNode in removedNodes.iter() {
+ removedNode.node_removed(parent_in_doc);
+ }
+ for addedNode in addedNodes.iter() {
+ addedNode.node_inserted();
+ }
+ }
+
+ // http://dom.spec.whatwg.org/#concept-node-pre-remove
+ fn pre_remove(child: &JSRef<Node>, parent: &JSRef<Node>) -> Fallible<Temporary<Node>> {
+ // Step 1.
+ match child.parent_node() {
+ Some(ref node) if *node != Temporary::from_rooted(parent) => return Err(NotFound),
+ _ => ()
+ }
+
+ // Step 2.
+ Node::remove(child, parent, Unsuppressed);
+
+ // Step 3.
+ Ok(Temporary::from_rooted(child))
+ }
+
+ // http://dom.spec.whatwg.org/#concept-node-remove
+ fn remove(node: &JSRef<Node>, parent: &JSRef<Node>, suppress_observers: SuppressObserver) {
+ assert!(node.parent_node().map_or(false, |node_parent| node_parent == Temporary::from_rooted(parent)));
+
+ // Step 1-5: ranges.
+ // Step 6-7: mutation observers.
+ // Step 8.
+ parent.remove_child(node);
+
+ node.deref().flags.deref().borrow_mut().remove(IsInDoc);
+
+ // Step 9.
+ match suppress_observers {
+ Suppressed => (),
+ Unsuppressed => node.node_removed(parent.is_in_doc()),
+ }
+ }
+
+ // http://dom.spec.whatwg.org/#concept-node-clone
+ pub fn clone(node: &JSRef<Node>, maybe_doc: Option<&JSRef<Document>>,
+ clone_children: CloneChildrenFlag) -> Temporary<Node> {
+
+ // Step 1.
+ let document = match maybe_doc {
+ Some(doc) => JS::from_rooted(doc).root(),
+ None => node.owner_doc().root()
+ };
+
+ // Step 2.
+ // XXXabinader: clone() for each node as trait?
+ let copy: Root<Node> = match node.type_id() {
+ DoctypeNodeTypeId => {
+ let doctype: &JSRef<DocumentType> = DocumentTypeCast::to_ref(node).unwrap();
+ let doctype = doctype.deref();
+ let doctype = DocumentType::new(doctype.name.clone(),
+ Some(doctype.public_id.clone()),
+ Some(doctype.system_id.clone()), &*document);
+ NodeCast::from_temporary(doctype)
+ },
+ DocumentFragmentNodeTypeId => {
+ let doc_fragment = DocumentFragment::new(&*document);
+ NodeCast::from_temporary(doc_fragment)
+ },
+ CommentNodeTypeId => {
+ let comment: &JSRef<Comment> = CommentCast::to_ref(node).unwrap();
+ let comment = comment.deref();
+ let comment = Comment::new(comment.characterdata.data.deref().borrow().clone(), &*document);
+ NodeCast::from_temporary(comment)
+ },
+ DocumentNodeTypeId => {
+ let document: &JSRef<Document> = DocumentCast::to_ref(node).unwrap();
+ let is_html_doc = match document.is_html_document {
+ true => HTMLDocument,
+ false => NonHTMLDocument
+ };
+ let window = document.window.root();
+ let document = Document::new(&*window, Some(document.url().clone()),
+ is_html_doc, None);
+ NodeCast::from_temporary(document)
+ },
+ ElementNodeTypeId(..) => {
+ let element: &JSRef<Element> = ElementCast::to_ref(node).unwrap();
+ let element = element.deref();
+ let element = build_element_from_tag(element.local_name.as_slice().to_string(),
+ element.namespace.clone(), &*document);
+ NodeCast::from_temporary(element)
+ },
+ TextNodeTypeId => {
+ let text: &JSRef<Text> = TextCast::to_ref(node).unwrap();
+ let text = text.deref();
+ let text = Text::new(text.characterdata.data.deref().borrow().clone(), &*document);
+ NodeCast::from_temporary(text)
+ },
+ ProcessingInstructionNodeTypeId => {
+ let pi: &JSRef<ProcessingInstruction> = ProcessingInstructionCast::to_ref(node).unwrap();
+ let pi = pi.deref();
+ let pi = ProcessingInstruction::new(pi.target.clone(),
+ pi.characterdata.data.deref().borrow().clone(), &*document);
+ NodeCast::from_temporary(pi)
+ },
+ }.root();
+
+ // Step 3.
+ let document = if copy.is_document() {
+ let doc: &JSRef<Document> = DocumentCast::to_ref(&*copy).unwrap();
+ JS::from_rooted(doc).root()
+ } else {
+ JS::from_rooted(&*document).root()
+ };
+ assert!(&*copy.owner_doc().root() == &*document);
+
+ // Step 4 (some data already copied in step 2).
+ match node.type_id() {
+ DocumentNodeTypeId => {
+ let node_doc: &JSRef<Document> = DocumentCast::to_ref(node).unwrap();
+ let copy_doc: &JSRef<Document> = DocumentCast::to_ref(&*copy).unwrap();
+ copy_doc.set_encoding_name(node_doc.encoding_name.deref().borrow().clone());
+ copy_doc.set_quirks_mode(node_doc.quirks_mode());
+ },
+ ElementNodeTypeId(..) => {
+ let node_elem: &JSRef<Element> = ElementCast::to_ref(node).unwrap();
+ let copy_elem: &JSRef<Element> = ElementCast::to_ref(&*copy).unwrap();
+
+ // FIXME: https://github.com/mozilla/servo/issues/1737
+ let window = document.deref().window.root();
+ for attr in node_elem.deref().attrs.borrow().iter().map(|attr| attr.root()) {
+ copy_elem.deref().attrs.borrow_mut().push_unrooted(
+ &Attr::new(&*window,
+ attr.local_name().clone(), attr.deref().value().clone(),
+ attr.deref().name.clone(), attr.deref().namespace.clone(),
+ attr.deref().prefix.clone(), copy_elem));
+ }
+ },
+ _ => ()
+ }
+
+ // Step 5: cloning steps.
+
+ // Step 6.
+ if clone_children == CloneChildren {
+ for ref child in node.children() {
+ let child_copy = Node::clone(&*child, Some(&*document), clone_children).root();
+ let _inserted_node = Node::pre_insert(&*child_copy, &*copy, None);
+ }
+ }
+
+ // Step 7.
+ Temporary::from_rooted(&*copy)
+ }
+
+ /// Sends layout data, if any, back to the layout task to be destroyed.
+ unsafe fn reap_layout_data(&mut self) {
+ if self.layout_data.is_present() {
+ let layout_data = mem::replace(&mut self.layout_data, LayoutDataRef::new());
+ let layout_chan = layout_data.take_chan();
+ match layout_chan {
+ None => {}
+ Some(chan) => {
+ let LayoutChan(chan) = chan;
+ chan.send(ReapLayoutDataMsg(layout_data))
+ },
+ }
+ }
+ }
+
+ pub unsafe fn unsafe_get_flags(&self) -> *const NodeFlags {
+ mem::transmute(&self.flags)
+ }
+
+ pub fn collect_text_contents<'a, T: Iterator<JSRef<'a, Node>>>(mut iterator: T) -> String {
+ let mut content = String::new();
+ for node in iterator {
+ let text: Option<&JSRef<Text>> = TextCast::to_ref(&node);
+ match text {
+ Some(text) => content.push_str(text.characterdata.data.borrow().as_slice()),
+ None => (),
+ }
+ }
+ content
+ }
+}
+
+impl<'a> NodeMethods for JSRef<'a, Node> {
+ // http://dom.spec.whatwg.org/#dom-node-nodetype
+ fn NodeType(&self) -> u16 {
+ match self.type_id {
+ ElementNodeTypeId(_) => NodeConstants::ELEMENT_NODE,
+ TextNodeTypeId => NodeConstants::TEXT_NODE,
+ ProcessingInstructionNodeTypeId => NodeConstants::PROCESSING_INSTRUCTION_NODE,
+ CommentNodeTypeId => NodeConstants::COMMENT_NODE,
+ DocumentNodeTypeId => NodeConstants::DOCUMENT_NODE,
+ DoctypeNodeTypeId => NodeConstants::DOCUMENT_TYPE_NODE,
+ DocumentFragmentNodeTypeId => NodeConstants::DOCUMENT_FRAGMENT_NODE,
+ }
+ }
+
+ // http://dom.spec.whatwg.org/#dom-node-nodename
+ fn NodeName(&self) -> DOMString {
+ match self.type_id {
+ ElementNodeTypeId(..) => {
+ let elem: &JSRef<Element> = ElementCast::to_ref(self).unwrap();
+ elem.TagName()
+ }
+ TextNodeTypeId => "#text".to_string(),
+ ProcessingInstructionNodeTypeId => {
+ let processing_instruction: &JSRef<ProcessingInstruction> =
+ ProcessingInstructionCast::to_ref(self).unwrap();
+ processing_instruction.Target()
+ }
+ CommentNodeTypeId => "#comment".to_string(),
+ DoctypeNodeTypeId => {
+ let doctype: &JSRef<DocumentType> = DocumentTypeCast::to_ref(self).unwrap();
+ doctype.deref().name.clone()
+ },
+ DocumentFragmentNodeTypeId => "#document-fragment".to_string(),
+ DocumentNodeTypeId => "#document".to_string()
+ }
+ }
+
+ // http://dom.spec.whatwg.org/#dom-node-baseuri
+ fn GetBaseURI(&self) -> Option<DOMString> {
+ // FIXME (#1824) implement.
+ None
+ }
+
+ // http://dom.spec.whatwg.org/#dom-node-ownerdocument
+ fn GetOwnerDocument(&self) -> Option<Temporary<Document>> {
+ match self.type_id {
+ ElementNodeTypeId(..) |
+ CommentNodeTypeId |
+ TextNodeTypeId |
+ ProcessingInstructionNodeTypeId |
+ DoctypeNodeTypeId |
+ DocumentFragmentNodeTypeId => Some(self.owner_doc()),
+ DocumentNodeTypeId => None
+ }
+ }
+
+ // http://dom.spec.whatwg.org/#dom-node-parentnode
+ fn GetParentNode(&self) -> Option<Temporary<Node>> {
+ self.parent_node.get().map(|node| Temporary::new(node))
+ }
+
+ // http://dom.spec.whatwg.org/#dom-node-parentelement
+ fn GetParentElement(&self) -> Option<Temporary<Element>> {
+ self.parent_node.get()
+ .and_then(|parent| {
+ let parent = parent.root();
+ ElementCast::to_ref(&*parent).map(|elem| {
+ Temporary::from_rooted(elem)
+ })
+ })
+ }
+
+ // http://dom.spec.whatwg.org/#dom-node-haschildnodes
+ fn HasChildNodes(&self) -> bool {
+ self.first_child.get().is_some()
+ }
+
+ // http://dom.spec.whatwg.org/#dom-node-childnodes
+ fn ChildNodes(&self) -> Temporary<NodeList> {
+ match self.child_list.get() {
+ None => (),
+ Some(ref list) => return Temporary::new(list.clone()),
+ }
+
+ let doc = self.owner_doc().root();
+ let window = doc.deref().window.root();
+ let child_list = NodeList::new_child_list(&*window, self);
+ self.child_list.assign(Some(child_list));
+ Temporary::new(self.child_list.get().get_ref().clone())
+ }
+
+ // http://dom.spec.whatwg.org/#dom-node-firstchild
+ fn GetFirstChild(&self) -> Option<Temporary<Node>> {
+ self.first_child.get().map(|node| Temporary::new(node))
+ }
+
+ // http://dom.spec.whatwg.org/#dom-node-lastchild
+ fn GetLastChild(&self) -> Option<Temporary<Node>> {
+ self.last_child.get().map(|node| Temporary::new(node))
+ }
+
+ // http://dom.spec.whatwg.org/#dom-node-previoussibling
+ fn GetPreviousSibling(&self) -> Option<Temporary<Node>> {
+ self.prev_sibling.get().map(|node| Temporary::new(node))
+ }
+
+ // http://dom.spec.whatwg.org/#dom-node-nextsibling
+ fn GetNextSibling(&self) -> Option<Temporary<Node>> {
+ self.next_sibling.get().map(|node| Temporary::new(node))
+ }
+
+ // http://dom.spec.whatwg.org/#dom-node-nodevalue
+ fn GetNodeValue(&self) -> Option<DOMString> {
+ match self.type_id {
+ CommentNodeTypeId |
+ TextNodeTypeId |
+ ProcessingInstructionNodeTypeId => {
+ let chardata: &JSRef<CharacterData> = CharacterDataCast::to_ref(self).unwrap();
+ Some(chardata.Data())
+ }
+ _ => {
+ None
+ }
+ }
+ }
+
+ // http://dom.spec.whatwg.org/#dom-node-nodevalue
+ fn SetNodeValue(&self, val: Option<DOMString>) {
+ match self.type_id {
+ CommentNodeTypeId |
+ TextNodeTypeId |
+ ProcessingInstructionNodeTypeId => {
+ self.SetTextContent(val)
+ }
+ _ => {}
+ }
+ }
+
+ // http://dom.spec.whatwg.org/#dom-node-textcontent
+ fn GetTextContent(&self) -> Option<DOMString> {
+ match self.type_id {
+ DocumentFragmentNodeTypeId |
+ ElementNodeTypeId(..) => {
+ let content = Node::collect_text_contents(self.traverse_preorder());
+ Some(content)
+ }
+ CommentNodeTypeId |
+ TextNodeTypeId |
+ ProcessingInstructionNodeTypeId => {
+ let characterdata: &JSRef<CharacterData> = CharacterDataCast::to_ref(self).unwrap();
+ Some(characterdata.Data())
+ }
+ DoctypeNodeTypeId |
+ DocumentNodeTypeId => {
+ None
+ }
+ }
+ }
+
+ // http://dom.spec.whatwg.org/#dom-node-textcontent
+ fn SetTextContent(&self, value: Option<DOMString>) {
+ let value = null_str_as_empty(&value);
+ match self.type_id {
+ DocumentFragmentNodeTypeId |
+ ElementNodeTypeId(..) => {
+ // Step 1-2.
+ let node = if value.len() == 0 {
+ None
+ } else {
+ let document = self.owner_doc().root();
+ Some(NodeCast::from_temporary(document.deref().CreateTextNode(value)))
+ }.root();
+
+ // Step 3.
+ Node::replace_all(node.root_ref(), self);
+ }
+ CommentNodeTypeId |
+ TextNodeTypeId |
+ ProcessingInstructionNodeTypeId => {
+ self.wait_until_safe_to_modify_dom();
+
+ let characterdata: &JSRef<CharacterData> = CharacterDataCast::to_ref(self).unwrap();
+ *characterdata.data.deref().borrow_mut() = value;
+
+ // Notify the document that the content of this node is different
+ let document = self.owner_doc().root();
+ document.deref().content_changed();
+ }
+ DoctypeNodeTypeId |
+ DocumentNodeTypeId => {}
+ }
+ }
+
+ // http://dom.spec.whatwg.org/#dom-node-insertbefore
+ fn InsertBefore(&self, node: &JSRef<Node>, child: Option<JSRef<Node>>) -> Fallible<Temporary<Node>> {
+ Node::pre_insert(node, self, child)
+ }
+
+ // http://dom.spec.whatwg.org/#dom-node-appendchild
+ fn AppendChild(&self, node: &JSRef<Node>) -> Fallible<Temporary<Node>> {
+ Node::pre_insert(node, self, None)
+ }
+
+ // http://dom.spec.whatwg.org/#concept-node-replace
+ fn ReplaceChild(&self, node: &JSRef<Node>, child: &JSRef<Node>) -> Fallible<Temporary<Node>> {
+
+ // Step 1.
+ match self.type_id {
+ DocumentNodeTypeId |
+ DocumentFragmentNodeTypeId |
+ ElementNodeTypeId(..) => (),
+ _ => return Err(HierarchyRequest)
+ }
+
+ // Step 2.
+ if node.is_inclusive_ancestor_of(self) {
+ return Err(HierarchyRequest);
+ }
+
+ // Step 3.
+ if !self.is_parent_of(child) {
+ return Err(NotFound);
+ }
+
+ // Step 4-5.
+ match node.type_id() {
+ TextNodeTypeId if self.is_document() => return Err(HierarchyRequest),
+ DoctypeNodeTypeId if !self.is_document() => return Err(HierarchyRequest),
+ DocumentFragmentNodeTypeId |
+ DoctypeNodeTypeId |
+ ElementNodeTypeId(..) |
+ TextNodeTypeId |
+ ProcessingInstructionNodeTypeId |
+ CommentNodeTypeId => (),
+ DocumentNodeTypeId => return Err(HierarchyRequest)
+ }
+
+ // Step 6.
+ match self.type_id {
+ DocumentNodeTypeId => {
+ match node.type_id() {
+ // Step 6.1
+ DocumentFragmentNodeTypeId => {
+ // Step 6.1.1(b)
+ if node.children().any(|c| c.is_text()) {
+ return Err(HierarchyRequest);
+ }
+ match node.child_elements().count() {
+ 0 => (),
+ // Step 6.1.2
+ 1 => {
+ if self.child_elements().any(|c| NodeCast::from_ref(&c) != child) {
+ return Err(HierarchyRequest);
+ }
+ if child.following_siblings()
+ .any(|child| child.is_doctype()) {
+ return Err(HierarchyRequest);
+ }
+ },
+ // Step 6.1.1(a)
+ _ => return Err(HierarchyRequest)
+ }
+ },
+ // Step 6.2
+ ElementNodeTypeId(..) => {
+ if self.child_elements().any(|c| NodeCast::from_ref(&c) != child) {
+ return Err(HierarchyRequest);
+ }
+ if child.following_siblings()
+ .any(|child| child.is_doctype()) {
+ return Err(HierarchyRequest);
+ }
+ },
+ // Step 6.3
+ DoctypeNodeTypeId => {
+ if self.children().any(|c| c.is_doctype() && &c != child) {
+ return Err(HierarchyRequest);
+ }
+ if self.children()
+ .take_while(|c| c != child)
+ .any(|c| c.is_element()) {
+ return Err(HierarchyRequest);
+ }
+ },
+ TextNodeTypeId |
+ ProcessingInstructionNodeTypeId |
+ CommentNodeTypeId => (),
+ DocumentNodeTypeId => unreachable!()
+ }
+ },
+ _ => ()
+ }
+
+ // Ok if not caught by previous error checks.
+ if *node == *child {
+ return Ok(Temporary::from_rooted(child));
+ }
+
+ // Step 7-8.
+ let next_sibling = child.next_sibling().map(|node| (*node.root()).clone());
+ let reference_child = match next_sibling {
+ Some(ref sibling) if sibling == node => node.next_sibling().map(|node| (*node.root()).clone()),
+ _ => next_sibling
+ };
+
+ // Step 9.
+ let document = document_from_node(self).root();
+ Node::adopt(node, &*document);
+
+ {
+ // Step 10.
+ Node::remove(child, self, Suppressed);
+
+ // Step 11.
+ Node::insert(node, self, reference_child, Suppressed);
+ }
+
+ // Step 12-14.
+ // Step 13: mutation records.
+ child.node_removed(self.is_in_doc());
+ if node.type_id() == DocumentFragmentNodeTypeId {
+ for child_node in node.children() {
+ child_node.node_inserted();
+ }
+ } else {
+ node.node_inserted();
+ }
+
+ // Step 15.
+ Ok(Temporary::from_rooted(child))
+ }
+
+ // http://dom.spec.whatwg.org/#dom-node-removechild
+ fn RemoveChild(&self, node: &JSRef<Node>)
+ -> Fallible<Temporary<Node>> {
+ Node::pre_remove(node, self)
+ }
+
+ // http://dom.spec.whatwg.org/#dom-node-normalize
+ fn Normalize(&self) {
+ let mut prev_text = None;
+ for child in self.children() {
+ if child.is_text() {
+ let characterdata: &JSRef<CharacterData> = CharacterDataCast::to_ref(&child).unwrap();
+ if characterdata.Length() == 0 {
+ self.remove_child(&child);
+ } else {
+ match prev_text {
+ Some(ref mut text_node) => {
+ let prev_characterdata: &mut JSRef<CharacterData> = CharacterDataCast::to_mut_ref(text_node).unwrap();
+ let _ = prev_characterdata.AppendData(characterdata.Data());
+ self.remove_child(&child);
+ },
+ None => prev_text = Some(child)
+ }
+ }
+ } else {
+ child.Normalize();
+ prev_text = None;
+ }
+
+ }
+ }
+
+ // http://dom.spec.whatwg.org/#dom-node-clonenode
+ fn CloneNode(&self, deep: bool) -> Temporary<Node> {
+ match deep {
+ true => Node::clone(self, None, CloneChildren),
+ false => Node::clone(self, None, DoNotCloneChildren)
+ }
+ }
+
+ // http://dom.spec.whatwg.org/#dom-node-isequalnode
+ fn IsEqualNode(&self, maybe_node: Option<JSRef<Node>>) -> bool {
+ fn is_equal_doctype(node: &JSRef<Node>, other: &JSRef<Node>) -> bool {
+ let doctype: &JSRef<DocumentType> = DocumentTypeCast::to_ref(node).unwrap();
+ let other_doctype: &JSRef<DocumentType> = DocumentTypeCast::to_ref(other).unwrap();
+ (doctype.deref().name == other_doctype.deref().name) &&
+ (doctype.deref().public_id == other_doctype.deref().public_id) &&
+ (doctype.deref().system_id == other_doctype.deref().system_id)
+ }
+ fn is_equal_element(node: &JSRef<Node>, other: &JSRef<Node>) -> bool {
+ let element: &JSRef<Element> = ElementCast::to_ref(node).unwrap();
+ let other_element: &JSRef<Element> = ElementCast::to_ref(other).unwrap();
+ // FIXME: namespace prefix
+ let element = element.deref();
+ let other_element = other_element.deref();
+ (element.namespace == other_element.namespace) &&
+ (element.local_name == other_element.local_name) &&
+ (element.attrs.borrow().len() == other_element.attrs.borrow().len())
+ }
+ fn is_equal_processinginstruction(node: &JSRef<Node>, other: &JSRef<Node>) -> bool {
+ let pi: &JSRef<ProcessingInstruction> = ProcessingInstructionCast::to_ref(node).unwrap();
+ let other_pi: &JSRef<ProcessingInstruction> = ProcessingInstructionCast::to_ref(other).unwrap();
+ (pi.deref().target == other_pi.deref().target) &&
+ (*pi.deref().characterdata.data.deref().borrow() == *other_pi.deref().characterdata.data.deref().borrow())
+ }
+ fn is_equal_characterdata(node: &JSRef<Node>, other: &JSRef<Node>) -> bool {
+ let characterdata: &JSRef<CharacterData> = CharacterDataCast::to_ref(node).unwrap();
+ let other_characterdata: &JSRef<CharacterData> = CharacterDataCast::to_ref(other).unwrap();
+ *characterdata.deref().data.deref().borrow() == *other_characterdata.deref().data.deref().borrow()
+ }
+ fn is_equal_element_attrs(node: &JSRef<Node>, other: &JSRef<Node>) -> bool {
+ let element: &JSRef<Element> = ElementCast::to_ref(node).unwrap();
+ let other_element: &JSRef<Element> = ElementCast::to_ref(other).unwrap();
+ let element = element.deref();
+ let other_element = other_element.deref();
+ assert!(element.attrs.borrow().len() == other_element.attrs.borrow().len());
+ element.attrs.borrow().iter().map(|attr| attr.root()).all(|attr| {
+ other_element.attrs.borrow().iter().map(|attr| attr.root()).any(|other_attr| {
+ (attr.namespace == other_attr.namespace) &&
+ (attr.local_name() == other_attr.local_name()) &&
+ (attr.deref().value().as_slice() == other_attr.deref().value().as_slice())
+ })
+ })
+ }
+ fn is_equal_node(this: &JSRef<Node>, node: &JSRef<Node>) -> bool {
+ // Step 2.
+ if this.type_id() != node.type_id() {
+ return false;
+ }
+
+ match node.type_id() {
+ // Step 3.
+ DoctypeNodeTypeId if !is_equal_doctype(this, node) => return false,
+ ElementNodeTypeId(..) if !is_equal_element(this, node) => return false,
+ ProcessingInstructionNodeTypeId if !is_equal_processinginstruction(this, node) => return false,
+ TextNodeTypeId |
+ CommentNodeTypeId if !is_equal_characterdata(this, node) => return false,
+ // Step 4.
+ ElementNodeTypeId(..) if !is_equal_element_attrs(this, node) => return false,
+ _ => ()
+ }
+
+ // Step 5.
+ if this.children().count() != node.children().count() {
+ return false;
+ }
+
+ // Step 6.
+ this.children().zip(node.children()).all(|(ref child, ref other_child)| {
+ is_equal_node(child, other_child)
+ })
+ }
+ match maybe_node {
+ // Step 1.
+ None => false,
+ // Step 2-6.
+ Some(ref node) => is_equal_node(self, node)
+ }
+ }
+
+ // http://dom.spec.whatwg.org/#dom-node-comparedocumentposition
+ fn CompareDocumentPosition(&self, other: &JSRef<Node>) -> u16 {
+ if self == other {
+ // step 2.
+ 0
+ } else {
+ let mut lastself = self.clone();
+ let mut lastother = other.clone();
+ for ancestor in self.ancestors() {
+ if &ancestor == other {
+ // step 4.
+ return NodeConstants::DOCUMENT_POSITION_CONTAINS +
+ NodeConstants::DOCUMENT_POSITION_PRECEDING;
+ }
+ lastself = ancestor.clone();
+ }
+ for ancestor in other.ancestors() {
+ if &ancestor == self {
+ // step 5.
+ return NodeConstants::DOCUMENT_POSITION_CONTAINED_BY +
+ NodeConstants::DOCUMENT_POSITION_FOLLOWING;
+ }
+ lastother = ancestor.clone();
+ }
+
+ if lastself != lastother {
+ let abstract_uint: uintptr_t = as_uintptr(&*self);
+ let other_uint: uintptr_t = as_uintptr(&*other);
+
+ let random = if abstract_uint < other_uint {
+ NodeConstants::DOCUMENT_POSITION_FOLLOWING
+ } else {
+ NodeConstants::DOCUMENT_POSITION_PRECEDING
+ };
+ // step 3.
+ return random +
+ NodeConstants::DOCUMENT_POSITION_DISCONNECTED +
+ NodeConstants::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC;
+ }
+
+ for child in lastself.traverse_preorder() {
+ if &child == other {
+ // step 6.
+ return NodeConstants::DOCUMENT_POSITION_PRECEDING;
+ }
+ if &child == self {
+ // step 7.
+ return NodeConstants::DOCUMENT_POSITION_FOLLOWING;
+ }
+ }
+ unreachable!()
+ }
+ }
+
+ // http://dom.spec.whatwg.org/#dom-node-contains
+ fn Contains(&self, maybe_other: Option<JSRef<Node>>) -> bool {
+ match maybe_other {
+ None => false,
+ Some(ref other) => self.is_inclusive_ancestor_of(other)
+ }
+ }
+
+ // http://dom.spec.whatwg.org/#dom-node-lookupprefix
+ fn LookupPrefix(&self, _prefix: Option<DOMString>) -> Option<DOMString> {
+ // FIXME (#1826) implement.
+ None
+ }
+
+ // http://dom.spec.whatwg.org/#dom-node-lookupnamespaceuri
+ fn LookupNamespaceURI(&self, _namespace: Option<DOMString>) -> Option<DOMString> {
+ // FIXME (#1826) implement.
+ None
+ }
+
+ // http://dom.spec.whatwg.org/#dom-node-isdefaultnamespace
+ fn IsDefaultNamespace(&self, _namespace: Option<DOMString>) -> bool {
+ // FIXME (#1826) implement.
+ false
+ }
+}
+
+
+impl Reflectable for Node {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.eventtarget.reflector()
+ }
+}
+
+pub fn document_from_node<T: NodeBase>(derived: &JSRef<T>) -> Temporary<Document> {
+ let node: &JSRef<Node> = NodeCast::from_ref(derived);
+ node.owner_doc()
+}
+
+pub fn window_from_node<T: NodeBase>(derived: &JSRef<T>) -> Temporary<Window> {
+ let document = document_from_node(derived).root();
+ Temporary::new(document.deref().window.clone())
+}
+
+impl<'a> VirtualMethods for JSRef<'a, Node> {
+ fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods> {
+ let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self);
+ Some(eventtarget as &VirtualMethods)
+ }
+}
+
+impl<'a> style::TNode<JSRef<'a, Element>> for JSRef<'a, Node> {
+ fn parent_node(&self) -> Option<JSRef<'a, Node>> {
+ (self as &NodeHelpers).parent_node().map(|node| *node.root())
+ }
+
+ fn prev_sibling(&self) -> Option<JSRef<'a, Node>> {
+ (self as &NodeHelpers).prev_sibling().map(|node| *node.root())
+ }
+
+ fn next_sibling(&self) -> Option<JSRef<'a, Node>> {
+ (self as &NodeHelpers).next_sibling().map(|node| *node.root())
+ }
+
+ fn is_document(&self) -> bool {
+ (self as &NodeHelpers).is_document()
+ }
+
+ fn is_element(&self) -> bool {
+ (self as &NodeHelpers).is_element()
+ }
+
+ fn as_element(&self) -> JSRef<'a, Element> {
+ let elem: Option<&JSRef<'a, Element>> = ElementCast::to_ref(self);
+ assert!(elem.is_some());
+ *elem.unwrap()
+ }
+
+ fn match_attr(&self, attr: &style::AttrSelector, test: |&str| -> bool) -> bool {
+ let name = {
+ if self.is_html_element_in_html_document() {
+ attr.lower_name.as_slice()
+ } else {
+ attr.name.as_slice()
+ }
+ };
+ match attr.namespace {
+ style::SpecificNamespace(ref ns) => {
+ self.as_element().get_attribute(ns.clone(), name).root()
+ .map_or(false, |attr| test(attr.deref().Value().as_slice()))
+ },
+ // FIXME: https://github.com/mozilla/servo/issues/1558
+ style::AnyNamespace => false,
+ }
+ }
+
+ fn is_html_element_in_html_document(&self) -> bool {
+ let elem: Option<&JSRef<'a, Element>> = ElementCast::to_ref(self);
+ assert!(elem.is_some());
+ let elem: &ElementHelpers = elem.unwrap() as &ElementHelpers;
+ elem.html_element_in_html_document()
+ }
+}
+
+pub trait DisabledStateHelpers {
+ fn check_ancestors_disabled_state_for_form_control(&self);
+ fn check_parent_disabled_state_for_option(&self);
+ fn check_disabled_attribute(&self);
+}
+
+impl<'a> DisabledStateHelpers for JSRef<'a, Node> {
+ fn check_ancestors_disabled_state_for_form_control(&self) {
+ if self.get_disabled_state() { return; }
+ for ancestor in self.ancestors().filter(|ancestor| ancestor.is_htmlfieldsetelement()) {
+ if !ancestor.get_disabled_state() { continue; }
+ if ancestor.is_parent_of(self) {
+ self.set_disabled_state(true);
+ self.set_enabled_state(false);
+ return;
+ }
+ match ancestor.children().find(|child| child.is_htmllegendelement()) {
+ Some(ref legend) => {
+ // XXXabinader: should we save previous ancestor to avoid this iteration?
+ if self.ancestors().any(|ancestor| ancestor == *legend) { continue; }
+ },
+ None => ()
+ }
+ self.set_disabled_state(true);
+ self.set_enabled_state(false);
+ return;
+ }
+ }
+
+ fn check_parent_disabled_state_for_option(&self) {
+ if self.get_disabled_state() { return; }
+ match self.parent_node().root() {
+ Some(ref parent) if parent.is_htmloptgroupelement() && parent.get_disabled_state() => {
+ self.set_disabled_state(true);
+ self.set_enabled_state(false);
+ },
+ _ => ()
+ }
+ }
+
+ fn check_disabled_attribute(&self) {
+ let elem: &JSRef<'a, Element> = ElementCast::to_ref(self).unwrap();
+ let has_disabled_attrib = elem.has_attribute("disabled");
+ self.set_disabled_state(has_disabled_attrib);
+ self.set_enabled_state(!has_disabled_attrib);
+ }
+}
diff --git a/components/script/dom/nodeiterator.rs b/components/script/dom/nodeiterator.rs
new file mode 100644
index 00000000000..f890f71cf4f
--- /dev/null
+++ b/components/script/dom/nodeiterator.rs
@@ -0,0 +1,35 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::NodeIteratorBinding;
+use dom::bindings::codegen::Bindings::NodeIteratorBinding::NodeIteratorMethods;
+use dom::bindings::global::GlobalRef;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
+
+#[deriving(Encodable)]
+pub struct NodeIterator {
+ pub reflector_: Reflector
+}
+
+impl NodeIterator {
+ pub fn new_inherited() -> NodeIterator {
+ NodeIterator {
+ reflector_: Reflector::new()
+ }
+ }
+
+ pub fn new(global: &GlobalRef) -> Temporary<NodeIterator> {
+ reflect_dom_object(box NodeIterator::new_inherited(), global, NodeIteratorBinding::Wrap)
+ }
+}
+
+impl<'a> NodeIteratorMethods for JSRef<'a, NodeIterator> {
+}
+
+impl Reflectable for NodeIterator {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ &self.reflector_
+ }
+}
diff --git a/components/script/dom/nodelist.rs b/components/script/dom/nodelist.rs
new file mode 100644
index 00000000000..424eb09416c
--- /dev/null
+++ b/components/script/dom/nodelist.rs
@@ -0,0 +1,82 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::NodeListBinding;
+use dom::bindings::codegen::Bindings::NodeListBinding::NodeListMethods;
+use dom::bindings::global::Window;
+use dom::bindings::js::{JS, JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
+use dom::node::{Node, NodeHelpers};
+use dom::window::Window;
+
+#[deriving(Encodable)]
+pub enum NodeListType {
+ Simple(Vec<JS<Node>>),
+ Children(JS<Node>)
+}
+
+#[deriving(Encodable)]
+pub struct NodeList {
+ list_type: NodeListType,
+ reflector_: Reflector,
+}
+
+impl NodeList {
+ pub fn new_inherited(list_type: NodeListType) -> NodeList {
+ NodeList {
+ list_type: list_type,
+ reflector_: Reflector::new(),
+ }
+ }
+
+ pub fn new(window: &JSRef<Window>,
+ list_type: NodeListType) -> Temporary<NodeList> {
+ reflect_dom_object(box NodeList::new_inherited(list_type),
+ &Window(*window), NodeListBinding::Wrap)
+ }
+
+ pub fn new_simple_list(window: &JSRef<Window>, elements: Vec<JSRef<Node>>) -> Temporary<NodeList> {
+ NodeList::new(window, Simple(elements.iter().map(|element| JS::from_rooted(element)).collect()))
+ }
+
+ pub fn new_child_list(window: &JSRef<Window>, node: &JSRef<Node>) -> Temporary<NodeList> {
+ NodeList::new(window, Children(JS::from_rooted(node)))
+ }
+}
+
+impl<'a> NodeListMethods for JSRef<'a, NodeList> {
+ fn Length(&self) -> u32 {
+ match self.list_type {
+ Simple(ref elems) => elems.len() as u32,
+ Children(ref node) => {
+ let node = node.root();
+ node.deref().children().count() as u32
+ }
+ }
+ }
+
+ fn Item(&self, index: u32) -> Option<Temporary<Node>> {
+ match self.list_type {
+ _ if index >= self.Length() => None,
+ Simple(ref elems) => Some(Temporary::new(elems[index as uint].clone())),
+ Children(ref node) => {
+ let node = node.root();
+ node.deref().children().nth(index as uint)
+ .map(|child| Temporary::from_rooted(&child))
+ }
+ }
+ }
+
+ fn IndexedGetter(&self, index: u32, found: &mut bool) -> Option<Temporary<Node>> {
+ let item = self.Item(index);
+ *found = item.is_some();
+ item
+ }
+}
+
+impl Reflectable for NodeList {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ &self.reflector_
+ }
+}
diff --git a/components/script/dom/performance.rs b/components/script/dom/performance.rs
new file mode 100644
index 00000000000..82b931b0c2e
--- /dev/null
+++ b/components/script/dom/performance.rs
@@ -0,0 +1,52 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::PerformanceBinding;
+use dom::bindings::codegen::Bindings::PerformanceBinding::PerformanceMethods;
+use dom::bindings::global::Window;
+use dom::bindings::js::{JS, JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
+use dom::performancetiming::{PerformanceTiming, PerformanceTimingHelpers};
+use dom::window::Window;
+use time;
+
+pub type DOMHighResTimeStamp = f64;
+
+#[deriving(Encodable)]
+pub struct Performance {
+ reflector_: Reflector,
+ timing: JS<PerformanceTiming>,
+}
+
+impl Performance {
+ fn new_inherited(window: &JSRef<Window>) -> Performance {
+ Performance {
+ reflector_: Reflector::new(),
+ timing: JS::from_rooted(&PerformanceTiming::new(window)),
+ }
+ }
+
+ pub fn new(window: &JSRef<Window>) -> Temporary<Performance> {
+ let performance = Performance::new_inherited(window);
+ reflect_dom_object(box performance, &Window(*window),
+ PerformanceBinding::Wrap)
+ }
+}
+
+impl<'a> PerformanceMethods for JSRef<'a, Performance> {
+ fn Timing(&self) -> Temporary<PerformanceTiming> {
+ Temporary::new(self.timing.clone())
+ }
+
+ fn Now(&self) -> DOMHighResTimeStamp {
+ let navStart = self.timing.root().NavigationStartPrecise() as f64;
+ (time::precise_time_s() - navStart) as DOMHighResTimeStamp
+ }
+}
+
+impl Reflectable for Performance {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ &self.reflector_
+ }
+}
diff --git a/components/script/dom/performancetiming.rs b/components/script/dom/performancetiming.rs
new file mode 100644
index 00000000000..f4331e06c0b
--- /dev/null
+++ b/components/script/dom/performancetiming.rs
@@ -0,0 +1,57 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::PerformanceTimingBinding;
+use dom::bindings::codegen::Bindings::PerformanceTimingBinding::PerformanceTimingMethods;
+use dom::bindings::global::Window;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
+use dom::window::Window;
+
+#[deriving(Encodable)]
+pub struct PerformanceTiming {
+ reflector_: Reflector,
+ navigationStart: u64,
+ navigationStartPrecise: f64,
+}
+
+impl PerformanceTiming {
+ pub fn new_inherited(navStart: u64, navStartPrecise: f64)
+ -> PerformanceTiming {
+ PerformanceTiming {
+ reflector_: Reflector::new(),
+ navigationStart: navStart,
+ navigationStartPrecise: navStartPrecise,
+ }
+ }
+
+ pub fn new(window: &JSRef<Window>) -> Temporary<PerformanceTiming> {
+ let timing = PerformanceTiming::new_inherited(window.navigationStart,
+ window.navigationStartPrecise);
+ reflect_dom_object(box timing, &Window(*window),
+ PerformanceTimingBinding::Wrap)
+ }
+}
+
+impl<'a> PerformanceTimingMethods for JSRef<'a, PerformanceTiming> {
+ fn NavigationStart(&self) -> u64 {
+ self.navigationStart
+ }
+}
+
+pub trait PerformanceTimingHelpers {
+ fn NavigationStartPrecise(&self) -> f64;
+}
+
+impl<'a> PerformanceTimingHelpers for JSRef<'a, PerformanceTiming> {
+ fn NavigationStartPrecise(&self) -> f64 {
+ self.navigationStartPrecise
+ }
+}
+
+impl Reflectable for PerformanceTiming {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ &self.reflector_
+ }
+}
diff --git a/components/script/dom/processinginstruction.rs b/components/script/dom/processinginstruction.rs
new file mode 100644
index 00000000000..40e6ca63e04
--- /dev/null
+++ b/components/script/dom/processinginstruction.rs
@@ -0,0 +1,53 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::ProcessingInstructionBinding;
+use dom::bindings::codegen::Bindings::ProcessingInstructionBinding::ProcessingInstructionMethods;
+use dom::bindings::codegen::InheritTypes::ProcessingInstructionDerived;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::characterdata::CharacterData;
+use dom::document::Document;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::node::{Node, ProcessingInstructionNodeTypeId};
+use servo_util::str::DOMString;
+
+/// An HTML processing instruction node.
+#[deriving(Encodable)]
+pub struct ProcessingInstruction {
+ pub characterdata: CharacterData,
+ pub target: DOMString,
+}
+
+impl ProcessingInstructionDerived for EventTarget {
+ fn is_processinginstruction(&self) -> bool {
+ self.type_id == NodeTargetTypeId(ProcessingInstructionNodeTypeId)
+ }
+}
+
+impl ProcessingInstruction {
+ pub fn new_inherited(target: DOMString, data: DOMString, document: &JSRef<Document>) -> ProcessingInstruction {
+ ProcessingInstruction {
+ characterdata: CharacterData::new_inherited(ProcessingInstructionNodeTypeId, data, document),
+ target: target
+ }
+ }
+
+ pub fn new(target: DOMString, data: DOMString, document: &JSRef<Document>) -> Temporary<ProcessingInstruction> {
+ let node = ProcessingInstruction::new_inherited(target, data, document);
+ Node::reflect_node(box node, document, ProcessingInstructionBinding::Wrap)
+ }
+}
+
+impl<'a> ProcessingInstructionMethods for JSRef<'a, ProcessingInstruction> {
+ fn Target(&self) -> DOMString {
+ self.target.clone()
+ }
+}
+
+impl Reflectable for ProcessingInstruction {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.characterdata.reflector()
+ }
+}
diff --git a/components/script/dom/progressevent.rs b/components/script/dom/progressevent.rs
new file mode 100644
index 00000000000..d001785984f
--- /dev/null
+++ b/components/script/dom/progressevent.rs
@@ -0,0 +1,75 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::EventBinding::EventMethods;
+use dom::bindings::codegen::Bindings::ProgressEventBinding;
+use dom::bindings::codegen::Bindings::ProgressEventBinding::ProgressEventMethods;
+use dom::bindings::codegen::InheritTypes::{EventCast, ProgressEventDerived};
+use dom::bindings::error::Fallible;
+use dom::bindings::global::GlobalRef;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
+use dom::event::{Event, ProgressEventTypeId};
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct ProgressEvent {
+ event: Event,
+ length_computable: bool,
+ loaded: u64,
+ total: u64
+}
+
+impl ProgressEventDerived for Event {
+ fn is_progressevent(&self) -> bool {
+ self.type_id == ProgressEventTypeId
+ }
+}
+
+impl ProgressEvent {
+ pub fn new_inherited(length_computable: bool, loaded: u64, total: u64) -> ProgressEvent {
+ ProgressEvent {
+ event: Event::new_inherited(ProgressEventTypeId),
+ length_computable: length_computable,
+ loaded: loaded,
+ total: total
+ }
+ }
+ pub fn new(global: &GlobalRef, type_: DOMString,
+ can_bubble: bool, cancelable: bool,
+ length_computable: bool, loaded: u64, total: u64) -> Temporary<ProgressEvent> {
+ let ev = reflect_dom_object(box ProgressEvent::new_inherited(length_computable, loaded, total),
+ global,
+ ProgressEventBinding::Wrap).root();
+ let event: &JSRef<Event> = EventCast::from_ref(&*ev);
+ event.InitEvent(type_, can_bubble, cancelable);
+ Temporary::from_rooted(&*ev)
+ }
+ pub fn Constructor(global: &GlobalRef,
+ type_: DOMString,
+ init: &ProgressEventBinding::ProgressEventInit)
+ -> Fallible<Temporary<ProgressEvent>> {
+ let ev = ProgressEvent::new(global, type_, init.parent.bubbles, init.parent.cancelable,
+ init.lengthComputable, init.loaded, init.total);
+ Ok(ev)
+ }
+}
+
+impl<'a> ProgressEventMethods for JSRef<'a, ProgressEvent> {
+ fn LengthComputable(&self) -> bool {
+ self.length_computable
+ }
+ fn Loaded(&self) -> u64{
+ self.loaded
+ }
+ fn Total(&self) -> u64 {
+ self.total
+ }
+}
+
+impl Reflectable for ProgressEvent {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.event.reflector()
+ }
+}
diff --git a/components/script/dom/range.rs b/components/script/dom/range.rs
new file mode 100644
index 00000000000..3ed02410a1e
--- /dev/null
+++ b/components/script/dom/range.rs
@@ -0,0 +1,50 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::RangeBinding;
+use dom::bindings::codegen::Bindings::RangeBinding::RangeMethods;
+use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
+use dom::bindings::error::Fallible;
+use dom::bindings::global::{GlobalRef, Window};
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
+use dom::document::Document;
+
+#[deriving(Encodable)]
+pub struct Range {
+ reflector_: Reflector
+}
+
+impl Range {
+ pub fn new_inherited() -> Range {
+ Range {
+ reflector_: Reflector::new()
+ }
+ }
+
+ pub fn new(document: &JSRef<Document>) -> Temporary<Range> {
+ let window = document.window.root();
+ reflect_dom_object(box Range::new_inherited(),
+ &Window(*window),
+ RangeBinding::Wrap)
+ }
+
+ pub fn Constructor(global: &GlobalRef) -> Fallible<Temporary<Range>> {
+ let document = global.as_window().Document().root();
+ Ok(Range::new(&*document))
+ }
+}
+
+impl<'a> RangeMethods for JSRef<'a, Range> {
+ /// http://dom.spec.whatwg.org/#dom-range-detach
+ fn Detach(&self) {
+ // This method intentionally left blank.
+ }
+}
+
+impl Reflectable for Range {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ &self.reflector_
+ }
+}
diff --git a/components/script/dom/screen.rs b/components/script/dom/screen.rs
new file mode 100644
index 00000000000..0e184d94ec3
--- /dev/null
+++ b/components/script/dom/screen.rs
@@ -0,0 +1,45 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::ScreenBinding;
+use dom::bindings::codegen::Bindings::ScreenBinding::ScreenMethods;
+use dom::bindings::global::Window;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
+use dom::window::Window;
+
+#[deriving(Encodable)]
+pub struct Screen {
+ reflector_: Reflector,
+}
+
+impl Screen {
+ pub fn new_inherited() -> Screen {
+ Screen {
+ reflector_: Reflector::new(),
+ }
+ }
+
+ pub fn new(window: &JSRef<Window>) -> Temporary<Screen> {
+ reflect_dom_object(box Screen::new_inherited(),
+ &Window(*window),
+ ScreenBinding::Wrap)
+ }
+}
+
+impl<'a> ScreenMethods for JSRef<'a, Screen> {
+ fn ColorDepth(&self) -> u32 {
+ 24
+ }
+
+ fn PixelDepth(&self) -> u32 {
+ 24
+ }
+}
+
+impl Reflectable for Screen {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ &self.reflector_
+ }
+}
diff --git a/components/script/dom/testbinding.rs b/components/script/dom/testbinding.rs
new file mode 100644
index 00000000000..b5d83fac620
--- /dev/null
+++ b/components/script/dom/testbinding.rs
@@ -0,0 +1,299 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::TestBindingBinding::TestBindingMethods;
+use dom::bindings::codegen::Bindings::TestBindingBinding::TestEnum;
+use dom::bindings::codegen::Bindings::TestBindingBinding::TestEnumValues::_empty;
+use dom::bindings::codegen::UnionTypes::BlobOrString::BlobOrString;
+use dom::bindings::codegen::UnionTypes::EventOrString::{EventOrString, eString};
+use dom::bindings::codegen::UnionTypes::HTMLElementOrLong::{HTMLElementOrLong, eLong};
+use dom::bindings::global::GlobalField;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::str::ByteString;
+use dom::bindings::utils::{Reflector, Reflectable};
+use dom::blob::Blob;
+use servo_util::str::DOMString;
+
+use js::jsapi::JSContext;
+use js::jsval::{JSVal, NullValue};
+
+#[deriving(Encodable)]
+pub struct TestBinding {
+ reflector: Reflector,
+ global: GlobalField,
+}
+
+impl<'a> TestBindingMethods for JSRef<'a, TestBinding> {
+ fn BooleanAttribute(&self) -> bool { false }
+ fn SetBooleanAttribute(&self, _: bool) {}
+ fn ByteAttribute(&self) -> i8 { 0 }
+ fn SetByteAttribute(&self, _: i8) {}
+ fn OctetAttribute(&self) -> u8 { 0 }
+ fn SetOctetAttribute(&self, _: u8) {}
+ fn ShortAttribute(&self) -> i16 { 0 }
+ fn SetShortAttribute(&self, _: i16) {}
+ fn UnsignedShortAttribute(&self) -> u16 { 0 }
+ fn SetUnsignedShortAttribute(&self, _: u16) {}
+ fn LongAttribute(&self) -> i32 { 0 }
+ fn SetLongAttribute(&self, _: i32) {}
+ fn UnsignedLongAttribute(&self) -> u32 { 0 }
+ fn SetUnsignedLongAttribute(&self, _: u32) {}
+ fn LongLongAttribute(&self) -> i64 { 0 }
+ fn SetLongLongAttribute(&self, _: i64) {}
+ fn UnsignedLongLongAttribute(&self) -> u64 { 0 }
+ fn SetUnsignedLongLongAttribute(&self, _: u64) {}
+ fn FloatAttribute(&self) -> f32 { 0. }
+ fn SetFloatAttribute(&self, _: f32) {}
+ fn DoubleAttribute(&self) -> f64 { 0. }
+ fn SetDoubleAttribute(&self, _: f64) {}
+ fn StringAttribute(&self) -> DOMString { "".to_string() }
+ fn SetStringAttribute(&self, _: DOMString) {}
+ fn ByteStringAttribute(&self) -> ByteString { ByteString::new(vec!()) }
+ fn SetByteStringAttribute(&self, _: ByteString) {}
+ fn EnumAttribute(&self) -> TestEnum { _empty }
+ fn SetEnumAttribute(&self, _: TestEnum) {}
+ fn InterfaceAttribute(&self) -> Temporary<Blob> {
+ let global = self.global.root();
+ Blob::new(&global.root_ref())
+ }
+ fn SetInterfaceAttribute(&self, _: &JSRef<Blob>) {}
+ fn UnionAttribute(&self) -> HTMLElementOrLong { eLong(0) }
+ fn SetUnionAttribute(&self, _: HTMLElementOrLong) {}
+ fn Union2Attribute(&self) -> EventOrString { eString("".to_string()) }
+ fn SetUnion2Attribute(&self, _: EventOrString) {}
+ fn AnyAttribute(&self, _: *mut JSContext) -> JSVal { NullValue() }
+ fn SetAnyAttribute(&self, _: *mut JSContext, _: JSVal) {}
+
+ fn GetBooleanAttributeNullable(&self) -> Option<bool> { Some(false) }
+ fn SetBooleanAttributeNullable(&self, _: Option<bool>) {}
+ fn GetByteAttributeNullable(&self) -> Option<i8> { Some(0) }
+ fn SetByteAttributeNullable(&self, _: Option<i8>) {}
+ fn GetOctetAttributeNullable(&self) -> Option<u8> { Some(0) }
+ fn SetOctetAttributeNullable(&self, _: Option<u8>) {}
+ fn GetShortAttributeNullable(&self) -> Option<i16> { Some(0) }
+ fn SetShortAttributeNullable(&self, _: Option<i16>) {}
+ fn GetUnsignedShortAttributeNullable(&self) -> Option<u16> { Some(0) }
+ fn SetUnsignedShortAttributeNullable(&self, _: Option<u16>) {}
+ fn GetLongAttributeNullable(&self) -> Option<i32> { Some(0) }
+ fn SetLongAttributeNullable(&self, _: Option<i32>) {}
+ fn GetUnsignedLongAttributeNullable(&self) -> Option<u32> { Some(0) }
+ fn SetUnsignedLongAttributeNullable(&self, _: Option<u32>) {}
+ fn GetLongLongAttributeNullable(&self) -> Option<i64> { Some(0) }
+ fn SetLongLongAttributeNullable(&self, _: Option<i64>) {}
+ fn GetUnsignedLongLongAttributeNullable(&self) -> Option<u64> { Some(0) }
+ fn SetUnsignedLongLongAttributeNullable(&self, _: Option<u64>) {}
+ fn GetFloatAttributeNullable(&self) -> Option<f32> { Some(0.) }
+ fn SetFloatAttributeNullable(&self, _: Option<f32>) {}
+ fn GetDoubleAttributeNullable(&self) -> Option<f64> { Some(0.) }
+ fn SetDoubleAttributeNullable(&self, _: Option<f64>) {}
+ fn GetByteStringAttributeNullable(&self) -> Option<ByteString> { Some(ByteString::new(vec!())) }
+ fn SetByteStringAttributeNullable(&self, _: Option<ByteString>) {}
+ fn GetStringAttributeNullable(&self) -> Option<DOMString> { Some("".to_string()) }
+ fn SetStringAttributeNullable(&self, _: Option<DOMString>) {}
+ fn GetEnumAttributeNullable(&self) -> Option<TestEnum> { Some(_empty) }
+ fn GetInterfaceAttributeNullable(&self) -> Option<Temporary<Blob>> {
+ let global = self.global.root();
+ Some(Blob::new(&global.root_ref()))
+ }
+ fn SetInterfaceAttributeNullable(&self, _: Option<JSRef<Blob>>) {}
+ fn GetUnionAttributeNullable(&self) -> Option<HTMLElementOrLong> { Some(eLong(0)) }
+ fn SetUnionAttributeNullable(&self, _: Option<HTMLElementOrLong>) {}
+ fn GetUnion2AttributeNullable(&self) -> Option<EventOrString> { Some(eString("".to_string())) }
+ fn SetUnion2AttributeNullable(&self, _: Option<EventOrString>) {}
+ fn ReceiveVoid(&self) -> () {}
+ fn ReceiveBoolean(&self) -> bool { false }
+ fn ReceiveByte(&self) -> i8 { 0 }
+ fn ReceiveOctet(&self) -> u8 { 0 }
+ fn ReceiveShort(&self) -> i16 { 0 }
+ fn ReceiveUnsignedShort(&self) -> u16 { 0 }
+ fn ReceiveLong(&self) -> i32 { 0 }
+ fn ReceiveUnsignedLong(&self) -> u32 { 0 }
+ fn ReceiveLongLong(&self) -> i64 { 0 }
+ fn ReceiveUnsignedLongLong(&self) -> u64 { 0 }
+ fn ReceiveFloat(&self) -> f32 { 0. }
+ fn ReceiveDouble(&self) -> f64 { 0. }
+ fn ReceiveString(&self) -> DOMString { "".to_string() }
+ fn ReceiveByteString(&self) -> ByteString { ByteString::new(vec!()) }
+ fn ReceiveEnum(&self) -> TestEnum { _empty }
+ fn ReceiveInterface(&self) -> Temporary<Blob> {
+ let global = self.global.root();
+ Blob::new(&global.root_ref())
+ }
+ fn ReceiveAny(&self, _: *mut JSContext) -> JSVal { NullValue() }
+ fn ReceiveUnion(&self) -> HTMLElementOrLong { eLong(0) }
+ fn ReceiveUnion2(&self) -> EventOrString { eString("".to_string()) }
+
+ fn ReceiveNullableBoolean(&self) -> Option<bool> { Some(false) }
+ fn ReceiveNullableByte(&self) -> Option<i8> { Some(0) }
+ fn ReceiveNullableOctet(&self) -> Option<u8> { Some(0) }
+ fn ReceiveNullableShort(&self) -> Option<i16> { Some(0) }
+ fn ReceiveNullableUnsignedShort(&self) -> Option<u16> { Some(0) }
+ fn ReceiveNullableLong(&self) -> Option<i32> { Some(0) }
+ fn ReceiveNullableUnsignedLong(&self) -> Option<u32> { Some(0) }
+ fn ReceiveNullableLongLong(&self) -> Option<i64> { Some(0) }
+ fn ReceiveNullableUnsignedLongLong(&self) -> Option<u64> { Some(0) }
+ fn ReceiveNullableFloat(&self) -> Option<f32> { Some(0.) }
+ fn ReceiveNullableDouble(&self) -> Option<f64> { Some(0.) }
+ fn ReceiveNullableString(&self) -> Option<DOMString> { Some("".to_string()) }
+ fn ReceiveNullableByteString(&self) -> Option<ByteString> { Some(ByteString::new(vec!())) }
+ fn ReceiveNullableEnum(&self) -> Option<TestEnum> { Some(_empty) }
+ fn ReceiveNullableInterface(&self) -> Option<Temporary<Blob>> {
+ let global = self.global.root();
+ Some(Blob::new(&global.root_ref()))
+ }
+ fn ReceiveNullableUnion(&self) -> Option<HTMLElementOrLong> { Some(eLong(0)) }
+ fn ReceiveNullableUnion2(&self) -> Option<EventOrString> { Some(eString("".to_string())) }
+
+ fn PassBoolean(&self, _: bool) {}
+ fn PassByte(&self, _: i8) {}
+ fn PassOctet(&self, _: u8) {}
+ fn PassShort(&self, _: i16) {}
+ fn PassUnsignedShort(&self, _: u16) {}
+ fn PassLong(&self, _: i32) {}
+ fn PassUnsignedLong(&self, _: u32) {}
+ fn PassLongLong(&self, _: i64) {}
+ fn PassUnsignedLongLong(&self, _: u64) {}
+ fn PassFloat(&self, _: f32) {}
+ fn PassDouble(&self, _: f64) {}
+ fn PassString(&self, _: DOMString) {}
+ fn PassByteString(&self, _: ByteString) {}
+ fn PassEnum(&self, _: TestEnum) {}
+ fn PassInterface(&self, _: &JSRef<Blob>) {}
+ fn PassUnion(&self, _: HTMLElementOrLong) {}
+ fn PassUnion2(&self, _: EventOrString) {}
+ fn PassUnion3(&self, _: BlobOrString) {}
+ fn PassAny(&self, _: *mut JSContext, _: JSVal) {}
+
+ fn PassNullableBoolean(&self, _: Option<bool>) {}
+ fn PassNullableByte(&self, _: Option<i8>) {}
+ fn PassNullableOctet(&self, _: Option<u8>) {}
+ fn PassNullableShort(&self, _: Option<i16>) {}
+ fn PassNullableUnsignedShort(&self, _: Option<u16>) {}
+ fn PassNullableLong(&self, _: Option<i32>) {}
+ fn PassNullableUnsignedLong(&self, _: Option<u32>) {}
+ fn PassNullableLongLong(&self, _: Option<i64>) {}
+ fn PassNullableUnsignedLongLong(&self, _: Option<u64>) {}
+ fn PassNullableFloat(&self, _: Option<f32>) {}
+ fn PassNullableDouble(&self, _: Option<f64>) {}
+ fn PassNullableString(&self, _: Option<DOMString>) {}
+ fn PassNullableByteString(&self, _: Option<ByteString>) {}
+ // fn PassNullableEnum(&self, _: Option<TestEnum>) {}
+ fn PassNullableInterface(&self, _: Option<JSRef<Blob>>) {}
+ fn PassNullableUnion(&self, _: Option<HTMLElementOrLong>) {}
+ fn PassNullableUnion2(&self, _: Option<EventOrString>) {}
+
+ fn PassOptionalBoolean(&self, _: Option<bool>) {}
+ fn PassOptionalByte(&self, _: Option<i8>) {}
+ fn PassOptionalOctet(&self, _: Option<u8>) {}
+ fn PassOptionalShort(&self, _: Option<i16>) {}
+ fn PassOptionalUnsignedShort(&self, _: Option<u16>) {}
+ fn PassOptionalLong(&self, _: Option<i32>) {}
+ fn PassOptionalUnsignedLong(&self, _: Option<u32>) {}
+ fn PassOptionalLongLong(&self, _: Option<i64>) {}
+ fn PassOptionalUnsignedLongLong(&self, _: Option<u64>) {}
+ fn PassOptionalFloat(&self, _: Option<f32>) {}
+ fn PassOptionalDouble(&self, _: Option<f64>) {}
+ fn PassOptionalString(&self, _: Option<DOMString>) {}
+ fn PassOptionalByteString(&self, _: Option<ByteString>) {}
+ fn PassOptionalEnum(&self, _: Option<TestEnum>) {}
+ fn PassOptionalInterface(&self, _: Option<JSRef<Blob>>) {}
+ fn PassOptionalUnion(&self, _: Option<HTMLElementOrLong>) {}
+ fn PassOptionalUnion2(&self, _: Option<EventOrString>) {}
+ fn PassOptionalAny(&self, _: *mut JSContext, _: JSVal) {}
+
+ fn PassOptionalNullableBoolean(&self, _: Option<Option<bool>>) {}
+ fn PassOptionalNullableByte(&self, _: Option<Option<i8>>) {}
+ fn PassOptionalNullableOctet(&self, _: Option<Option<u8>>) {}
+ fn PassOptionalNullableShort(&self, _: Option<Option<i16>>) {}
+ fn PassOptionalNullableUnsignedShort(&self, _: Option<Option<u16>>) {}
+ fn PassOptionalNullableLong(&self, _: Option<Option<i32>>) {}
+ fn PassOptionalNullableUnsignedLong(&self, _: Option<Option<u32>>) {}
+ fn PassOptionalNullableLongLong(&self, _: Option<Option<i64>>) {}
+ fn PassOptionalNullableUnsignedLongLong(&self, _: Option<Option<u64>>) {}
+ fn PassOptionalNullableFloat(&self, _: Option<Option<f32>>) {}
+ fn PassOptionalNullableDouble(&self, _: Option<Option<f64>>) {}
+ fn PassOptionalNullableString(&self, _: Option<Option<DOMString>>) {}
+ fn PassOptionalNullableByteString(&self, _: Option<Option<ByteString>>) {}
+ // fn PassOptionalNullableEnum(&self, _: Option<Option<TestEnum>>) {}
+ fn PassOptionalNullableInterface(&self, _: Option<Option<JSRef<Blob>>>) {}
+ fn PassOptionalNullableUnion(&self, _: Option<Option<HTMLElementOrLong>>) {}
+ fn PassOptionalNullableUnion2(&self, _: Option<Option<EventOrString>>) {}
+
+ fn PassOptionalBooleanWithDefault(&self, _: bool) {}
+ fn PassOptionalByteWithDefault(&self, _: i8) {}
+ fn PassOptionalOctetWithDefault(&self, _: u8) {}
+ fn PassOptionalShortWithDefault(&self, _: i16) {}
+ fn PassOptionalUnsignedShortWithDefault(&self, _: u16) {}
+ fn PassOptionalLongWithDefault(&self, _: i32) {}
+ fn PassOptionalUnsignedLongWithDefault(&self, _: u32) {}
+ fn PassOptionalLongLongWithDefault(&self, _: i64) {}
+ fn PassOptionalUnsignedLongLongWithDefault(&self, _: u64) {}
+ fn PassOptionalStringWithDefault(&self, _: DOMString) {}
+ fn PassOptionalEnumWithDefault(&self, _: TestEnum) {}
+
+ fn PassOptionalNullableBooleanWithDefault(&self, _: Option<bool>) {}
+ fn PassOptionalNullableByteWithDefault(&self, _: Option<i8>) {}
+ fn PassOptionalNullableOctetWithDefault(&self, _: Option<u8>) {}
+ fn PassOptionalNullableShortWithDefault(&self, _: Option<i16>) {}
+ fn PassOptionalNullableUnsignedShortWithDefault(&self, _: Option<u16>) {}
+ fn PassOptionalNullableLongWithDefault(&self, _: Option<i32>) {}
+ fn PassOptionalNullableUnsignedLongWithDefault(&self, _: Option<u32>) {}
+ fn PassOptionalNullableLongLongWithDefault(&self, _: Option<i64>) {}
+ fn PassOptionalNullableUnsignedLongLongWithDefault(&self, _: Option<u64>) {}
+ // fn PassOptionalNullableFloatWithDefault(&self, _: Option<f32>) {}
+ // fn PassOptionalNullableDoubleWithDefault(&self, _: Option<f64>) {}
+ fn PassOptionalNullableStringWithDefault(&self, _: Option<DOMString>) {}
+ fn PassOptionalNullableByteStringWithDefault(&self, _: Option<ByteString>) {}
+ // fn PassOptionalNullableEnumWithDefault(&self, _: Option<TestEnum>) {}
+ fn PassOptionalNullableInterfaceWithDefault(&self, _: Option<JSRef<Blob>>) {}
+ fn PassOptionalNullableUnionWithDefault(&self, _: Option<HTMLElementOrLong>) {}
+ fn PassOptionalNullableUnion2WithDefault(&self, _: Option<EventOrString>) {}
+ fn PassOptionalAnyWithDefault(&self, _: *mut JSContext, _: JSVal) {}
+
+ fn PassOptionalNullableBooleanWithNonNullDefault(&self, _: Option<bool>) {}
+ fn PassOptionalNullableByteWithNonNullDefault(&self, _: Option<i8>) {}
+ fn PassOptionalNullableOctetWithNonNullDefault(&self, _: Option<u8>) {}
+ fn PassOptionalNullableShortWithNonNullDefault(&self, _: Option<i16>) {}
+ fn PassOptionalNullableUnsignedShortWithNonNullDefault(&self, _: Option<u16>) {}
+ fn PassOptionalNullableLongWithNonNullDefault(&self, _: Option<i32>) {}
+ fn PassOptionalNullableUnsignedLongWithNonNullDefault(&self, _: Option<u32>) {}
+ fn PassOptionalNullableLongLongWithNonNullDefault(&self, _: Option<i64>) {}
+ fn PassOptionalNullableUnsignedLongLongWithNonNullDefault(&self, _: Option<u64>) {}
+ // fn PassOptionalNullableFloatWithNonNullDefault(&self, _: Option<f32>) {}
+ // fn PassOptionalNullableDoubleWithNonNullDefault(&self, _: Option<f64>) {}
+ fn PassOptionalNullableStringWithNonNullDefault(&self, _: Option<DOMString>) {}
+ // fn PassOptionalNullableEnumWithNonNullDefault(&self, _: Option<TestEnum>) {}
+
+ fn PassVariadicBoolean(&self, _: Vec<bool>) {}
+ fn PassVariadicByte(&self, _: Vec<i8>) {}
+ fn PassVariadicOctet(&self, _: Vec<u8>) {}
+ fn PassVariadicShort(&self, _: Vec<i16>) {}
+ fn PassVariadicUnsignedShort(&self, _: Vec<u16>) {}
+ fn PassVariadicLong(&self, _: Vec<i32>) {}
+ fn PassVariadicUnsignedLong(&self, _: Vec<u32>) {}
+ fn PassVariadicLongLong(&self, _: Vec<i64>) {}
+ fn PassVariadicUnsignedLongLong(&self, _: Vec<u64>) {}
+ fn PassVariadicFloat(&self, _: Vec<f32>) {}
+ fn PassVariadicDouble(&self, _: Vec<f64>) {}
+ fn PassVariadicString(&self, _: Vec<DOMString>) {}
+ fn PassVariadicByteString(&self, _: Vec<ByteString>) {}
+ fn PassVariadicEnum(&self, _: Vec<TestEnum>) {}
+ // fn PassVariadicInterface(&self, _: Vec<JSRef<Blob>>) {}
+ fn PassVariadicUnion(&self, _: Vec<HTMLElementOrLong>) {}
+ fn PassVariadicUnion2(&self, _: Vec<EventOrString>) {}
+ fn PassVariadicUnion3(&self, _: Vec<BlobOrString>) {}
+ fn PassVariadicAny(&self, _: *mut JSContext, _: Vec<JSVal>) {}
+}
+
+impl TestBinding {
+ pub fn BooleanAttributeStatic() -> bool { false }
+ pub fn SetBooleanAttributeStatic(_: bool) {}
+ pub fn ReceiveVoidStatic() {}
+}
+
+impl Reflectable for TestBinding {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ &self.reflector
+ }
+}
diff --git a/components/script/dom/text.rs b/components/script/dom/text.rs
new file mode 100644
index 00000000000..1bbd25cb8ff
--- /dev/null
+++ b/components/script/dom/text.rs
@@ -0,0 +1,52 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::TextBinding;
+use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
+use dom::bindings::codegen::InheritTypes::TextDerived;
+use dom::bindings::error::Fallible;
+use dom::bindings::global::GlobalRef;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::characterdata::CharacterData;
+use dom::document::Document;
+use dom::eventtarget::{EventTarget, NodeTargetTypeId};
+use dom::node::{Node, TextNodeTypeId};
+use servo_util::str::DOMString;
+
+/// An HTML text node.
+#[deriving(Encodable)]
+pub struct Text {
+ pub characterdata: CharacterData,
+}
+
+impl TextDerived for EventTarget {
+ fn is_text(&self) -> bool {
+ self.type_id == NodeTargetTypeId(TextNodeTypeId)
+ }
+}
+
+impl Text {
+ pub fn new_inherited(text: DOMString, document: &JSRef<Document>) -> Text {
+ Text {
+ characterdata: CharacterData::new_inherited(TextNodeTypeId, text, document)
+ }
+ }
+
+ pub fn new(text: DOMString, document: &JSRef<Document>) -> Temporary<Text> {
+ let node = Text::new_inherited(text, document);
+ Node::reflect_node(box node, document, TextBinding::Wrap)
+ }
+
+ pub fn Constructor(global: &GlobalRef, text: DOMString) -> Fallible<Temporary<Text>> {
+ let document = global.as_window().Document().root();
+ Ok(Text::new(text, &*document))
+ }
+}
+
+impl Reflectable for Text {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.characterdata.reflector()
+ }
+}
diff --git a/components/script/dom/treewalker.rs b/components/script/dom/treewalker.rs
new file mode 100644
index 00000000000..dc192c359ce
--- /dev/null
+++ b/components/script/dom/treewalker.rs
@@ -0,0 +1,35 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::TreeWalkerBinding;
+use dom::bindings::codegen::Bindings::TreeWalkerBinding::TreeWalkerMethods;
+use dom::bindings::global::GlobalRef;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
+
+#[deriving(Encodable)]
+pub struct TreeWalker {
+ pub reflector_: Reflector
+}
+
+impl TreeWalker {
+ pub fn new_inherited() -> TreeWalker {
+ TreeWalker {
+ reflector_: Reflector::new()
+ }
+ }
+
+ pub fn new(global: &GlobalRef) -> Temporary<TreeWalker> {
+ reflect_dom_object(box TreeWalker::new_inherited(), global, TreeWalkerBinding::Wrap)
+ }
+}
+
+impl<'a> TreeWalkerMethods for JSRef<'a, TreeWalker> {
+}
+
+impl Reflectable for TreeWalker {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ &self.reflector_
+ }
+}
diff --git a/components/script/dom/uievent.rs b/components/script/dom/uievent.rs
new file mode 100644
index 00000000000..c91f0fdb787
--- /dev/null
+++ b/components/script/dom/uievent.rs
@@ -0,0 +1,95 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::EventBinding::EventMethods;
+use dom::bindings::codegen::Bindings::UIEventBinding;
+use dom::bindings::codegen::Bindings::UIEventBinding::UIEventMethods;
+use dom::bindings::codegen::InheritTypes::{EventCast, UIEventDerived};
+use dom::bindings::error::Fallible;
+use dom::bindings::global::{GlobalRef, Window};
+use dom::bindings::js::{JS, JSRef, RootedReference, Temporary, OptionalSettable};
+use dom::bindings::trace::Traceable;
+use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
+use dom::event::{Event, EventTypeId, UIEventTypeId};
+use dom::window::Window;
+use servo_util::str::DOMString;
+
+use std::cell::Cell;
+
+#[deriving(Encodable)]
+pub struct UIEvent {
+ pub event: Event,
+ view: Cell<Option<JS<Window>>>,
+ detail: Traceable<Cell<i32>>
+}
+
+impl UIEventDerived for Event {
+ fn is_uievent(&self) -> bool {
+ self.type_id == UIEventTypeId
+ }
+}
+
+impl UIEvent {
+ pub fn new_inherited(type_id: EventTypeId) -> UIEvent {
+ UIEvent {
+ event: Event::new_inherited(type_id),
+ view: Cell::new(None),
+ detail: Traceable::new(Cell::new(0)),
+ }
+ }
+
+ pub fn new_uninitialized(window: &JSRef<Window>) -> Temporary<UIEvent> {
+ reflect_dom_object(box UIEvent::new_inherited(UIEventTypeId),
+ &Window(*window),
+ UIEventBinding::Wrap)
+ }
+
+ pub fn new(window: &JSRef<Window>,
+ type_: DOMString,
+ can_bubble: bool,
+ cancelable: bool,
+ view: Option<JSRef<Window>>,
+ detail: i32) -> Temporary<UIEvent> {
+ let ev = UIEvent::new_uninitialized(window).root();
+ ev.deref().InitUIEvent(type_, can_bubble, cancelable, view, detail);
+ Temporary::from_rooted(&*ev)
+ }
+
+ pub fn Constructor(global: &GlobalRef,
+ type_: DOMString,
+ init: &UIEventBinding::UIEventInit) -> Fallible<Temporary<UIEvent>> {
+ let event = UIEvent::new(global.as_window(), type_,
+ init.parent.bubbles, init.parent.cancelable,
+ init.view.root_ref(), init.detail);
+ Ok(event)
+ }
+}
+
+impl<'a> UIEventMethods for JSRef<'a, UIEvent> {
+ fn GetView(&self) -> Option<Temporary<Window>> {
+ self.view.get().map(|view| Temporary::new(view))
+ }
+
+ fn Detail(&self) -> i32 {
+ self.detail.deref().get()
+ }
+
+ fn InitUIEvent(&self,
+ type_: DOMString,
+ can_bubble: bool,
+ cancelable: bool,
+ view: Option<JSRef<Window>>,
+ detail: i32) {
+ let event: &JSRef<Event> = EventCast::from_ref(self);
+ event.InitEvent(type_, can_bubble, cancelable);
+ self.view.assign(view);
+ self.detail.deref().set(detail);
+ }
+}
+
+impl Reflectable for UIEvent {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.event.reflector()
+ }
+}
diff --git a/components/script/dom/urlsearchparams.rs b/components/script/dom/urlsearchparams.rs
new file mode 100644
index 00000000000..63fffe6bbf5
--- /dev/null
+++ b/components/script/dom/urlsearchparams.rs
@@ -0,0 +1,152 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::URLSearchParamsBinding;
+use dom::bindings::codegen::Bindings::URLSearchParamsBinding::URLSearchParamsMethods;
+use dom::bindings::codegen::UnionTypes::StringOrURLSearchParams::{StringOrURLSearchParams, eURLSearchParams, eString};
+use dom::bindings::error::{Fallible};
+use dom::bindings::global::GlobalRef;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::trace::Traceable;
+use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
+
+use servo_util::str::DOMString;
+
+use encoding::all::UTF_8;
+use encoding::types::{Encoding, EncodeReplace};
+
+use std::cell::RefCell;
+use std::collections::hashmap::HashMap;
+use std::fmt::radix;
+use std::ascii::OwnedStrAsciiExt;
+
+#[deriving(Encodable)]
+pub struct URLSearchParams {
+ data: Traceable<RefCell<HashMap<DOMString, Vec<DOMString>>>>,
+ reflector_: Reflector,
+}
+
+impl URLSearchParams {
+ pub fn new_inherited() -> URLSearchParams {
+ URLSearchParams {
+ data: Traceable::new(RefCell::new(HashMap::new())),
+ reflector_: Reflector::new(),
+ }
+ }
+
+ pub fn new(global: &GlobalRef) -> Temporary<URLSearchParams> {
+ reflect_dom_object(box URLSearchParams::new_inherited(), global, URLSearchParamsBinding::Wrap)
+ }
+
+ pub fn Constructor(global: &GlobalRef, init: Option<StringOrURLSearchParams>) -> Fallible<Temporary<URLSearchParams>> {
+ let usp = URLSearchParams::new(global).root();
+ match init {
+ Some(eString(_s)) => {
+ // XXXManishearth we need to parse the input here
+ // http://url.spec.whatwg.org/#concept-urlencoded-parser
+ // We can use rust-url's implementation here:
+ // https://github.com/SimonSapin/rust-url/blob/master/form_urlencoded.rs#L29
+ },
+ Some(eURLSearchParams(u)) => {
+ let u = u.root();
+ let mut map = usp.deref().data.deref().borrow_mut();
+ *map = u.data.deref().borrow().clone();
+ },
+ None => {}
+ }
+ Ok(Temporary::from_rooted(&*usp))
+ }
+}
+
+impl<'a> URLSearchParamsMethods for JSRef<'a, URLSearchParams> {
+ fn Append(&self, name: DOMString, value: DOMString) {
+ self.data.deref().borrow_mut().insert_or_update_with(name, vec!(value.clone()),
+ |_k, v| v.push(value.clone()));
+ self.update_steps();
+ }
+
+ fn Delete(&self, name: DOMString) {
+ self.data.deref().borrow_mut().remove(&name);
+ self.update_steps();
+ }
+
+ fn Get(&self, name: DOMString) -> Option<DOMString> {
+ self.data.deref().borrow().find_equiv(&name).map(|v| v[0].clone())
+ }
+
+ fn Has(&self, name: DOMString) -> bool {
+ self.data.deref().borrow().contains_key_equiv(&name)
+ }
+
+ fn Set(&self, name: DOMString, value: DOMString) {
+ self.data.deref().borrow_mut().insert(name, vec!(value));
+ self.update_steps();
+ }
+}
+
+impl Reflectable for URLSearchParams {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ &self.reflector_
+ }
+}
+
+pub trait URLSearchParamsHelpers {
+ fn serialize(&self, encoding: Option<&'static Encoding>) -> Vec<u8>;
+ fn update_steps(&self);
+}
+
+impl URLSearchParamsHelpers for URLSearchParams {
+ fn serialize(&self, encoding: Option<&'static Encoding>) -> Vec<u8> {
+ // http://url.spec.whatwg.org/#concept-urlencoded-serializer
+ fn serialize_string(value: &DOMString, encoding: &'static Encoding) -> Vec<u8> {
+ // http://url.spec.whatwg.org/#concept-urlencoded-byte-serializer
+
+ let value = value.as_slice();
+ // XXXManishearth should this be a strict encoding? Can unwrap()ing the result fail?
+ let value = encoding.encode(value, EncodeReplace).unwrap();
+ let mut buf = vec!();
+ for i in value.iter() {
+ let append = match *i {
+ 0x20 => vec!(0x2B),
+ 0x2A | 0x2D | 0x2E |
+ 0x30 .. 0x39 | 0x41 .. 0x5A |
+ 0x5F | 0x61..0x7A => vec!(*i),
+ a => {
+ // http://url.spec.whatwg.org/#percent-encode
+ let mut encoded = vec!(0x25); // %
+ let s = format!("{}", radix(a, 16)).into_ascii_upper();
+ let bytes = s.as_bytes();
+ encoded.push_all(bytes);
+ encoded
+ }
+ };
+ buf.push_all(append.as_slice());
+ }
+ buf
+ }
+ let encoding = encoding.unwrap_or(UTF_8 as &'static Encoding);
+ let mut buf = vec!();
+ let mut first_pair = true;
+ for (k, v) in self.data.deref().borrow().iter() {
+ let name = serialize_string(k, encoding);
+ for val in v.iter() {
+ let value = serialize_string(val, encoding);
+ if first_pair {
+ first_pair = false;
+ } else {
+ buf.push(0x26); // &
+ }
+ buf.push_all(name.as_slice());
+ buf.push(0x3D); // =
+ buf.push_all(value.as_slice())
+ }
+ }
+ buf
+ }
+
+ fn update_steps(&self) {
+ // XXXManishearth Implement this when the URL interface is implemented
+ // http://url.spec.whatwg.org/#concept-uq-update
+ }
+}
diff --git a/components/script/dom/validitystate.rs b/components/script/dom/validitystate.rs
new file mode 100644
index 00000000000..c2901009c41
--- /dev/null
+++ b/components/script/dom/validitystate.rs
@@ -0,0 +1,36 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::ValidityStateBinding;
+use dom::bindings::global::Window;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
+use dom::window::Window;
+
+#[deriving(Encodable)]
+pub struct ValidityState {
+ reflector_: Reflector,
+ state: u8,
+}
+
+impl ValidityState {
+ pub fn new_inherited() -> ValidityState {
+ ValidityState {
+ reflector_: Reflector::new(),
+ state: 0,
+ }
+ }
+
+ pub fn new(window: &JSRef<Window>) -> Temporary<ValidityState> {
+ reflect_dom_object(box ValidityState::new_inherited(),
+ &Window(*window),
+ ValidityStateBinding::Wrap)
+ }
+}
+
+impl Reflectable for ValidityState {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ &self.reflector_
+ }
+}
diff --git a/components/script/dom/virtualmethods.rs b/components/script/dom/virtualmethods.rs
new file mode 100644
index 00000000000..9ce38c4908b
--- /dev/null
+++ b/components/script/dom/virtualmethods.rs
@@ -0,0 +1,219 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::attr::{AttrValue, StringAttrValue};
+use dom::bindings::codegen::InheritTypes::ElementCast;
+use dom::bindings::codegen::InheritTypes::HTMLAnchorElementCast;
+use dom::bindings::codegen::InheritTypes::HTMLAreaElementCast;
+use dom::bindings::codegen::InheritTypes::HTMLBodyElementCast;
+use dom::bindings::codegen::InheritTypes::HTMLButtonElementCast;
+use dom::bindings::codegen::InheritTypes::HTMLCanvasElementCast;
+use dom::bindings::codegen::InheritTypes::HTMLElementCast;
+use dom::bindings::codegen::InheritTypes::HTMLFieldSetElementCast;
+use dom::bindings::codegen::InheritTypes::HTMLIFrameElementCast;
+use dom::bindings::codegen::InheritTypes::HTMLImageElementCast;
+use dom::bindings::codegen::InheritTypes::HTMLInputElementCast;
+use dom::bindings::codegen::InheritTypes::HTMLLinkElementCast;
+use dom::bindings::codegen::InheritTypes::HTMLObjectElementCast;
+use dom::bindings::codegen::InheritTypes::HTMLOptGroupElementCast;
+use dom::bindings::codegen::InheritTypes::HTMLOptionElementCast;
+use dom::bindings::codegen::InheritTypes::HTMLSelectElementCast;
+use dom::bindings::codegen::InheritTypes::HTMLStyleElementCast;
+use dom::bindings::codegen::InheritTypes::HTMLTextAreaElementCast;
+use dom::bindings::js::JSRef;
+use dom::element::Element;
+use dom::element::ElementTypeId;
+use dom::element::HTMLAnchorElementTypeId;
+use dom::element::HTMLAreaElementTypeId;
+use dom::element::HTMLBodyElementTypeId;
+use dom::element::HTMLButtonElementTypeId;
+use dom::element::HTMLCanvasElementTypeId;
+use dom::element::HTMLFieldSetElementTypeId;
+use dom::element::HTMLIFrameElementTypeId;
+use dom::element::HTMLImageElementTypeId;
+use dom::element::HTMLInputElementTypeId;
+use dom::element::HTMLLinkElementTypeId;
+use dom::element::HTMLObjectElementTypeId;
+use dom::element::HTMLOptGroupElementTypeId;
+use dom::element::HTMLOptionElementTypeId;
+use dom::element::HTMLSelectElementTypeId;
+use dom::element::HTMLStyleElementTypeId;
+use dom::element::HTMLTextAreaElementTypeId;
+use dom::event::Event;
+use dom::htmlanchorelement::HTMLAnchorElement;
+use dom::htmlareaelement::HTMLAreaElement;
+use dom::htmlbodyelement::HTMLBodyElement;
+use dom::htmlbuttonelement::HTMLButtonElement;
+use dom::htmlcanvaselement::HTMLCanvasElement;
+use dom::htmlelement::HTMLElement;
+use dom::htmlfieldsetelement::HTMLFieldSetElement;
+use dom::htmliframeelement::HTMLIFrameElement;
+use dom::htmlimageelement::HTMLImageElement;
+use dom::htmlinputelement::HTMLInputElement;
+use dom::htmllinkelement::HTMLLinkElement;
+use dom::htmlobjectelement::HTMLObjectElement;
+use dom::htmloptgroupelement::HTMLOptGroupElement;
+use dom::htmloptionelement::HTMLOptionElement;
+use dom::htmlselectelement::HTMLSelectElement;
+use dom::htmlstyleelement::HTMLStyleElement;
+use dom::htmltextareaelement::HTMLTextAreaElement;
+use dom::node::{Node, NodeHelpers, ElementNodeTypeId};
+
+use servo_util::atom::Atom;
+use servo_util::str::DOMString;
+
+/// Trait to allow DOM nodes to opt-in to overriding (or adding to) common
+/// behaviours. Replicates the effect of C++ virtual methods.
+pub trait VirtualMethods {
+ /// Returns self as the superclass of the implementation for this trait,
+ /// if any.
+ fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods>;
+
+ /// Called when changing or adding attributes, after the attribute's value
+ /// has been updated.
+ fn after_set_attr(&self, name: &Atom, value: DOMString) {
+ match self.super_type() {
+ Some(ref s) => s.after_set_attr(name, value),
+ _ => (),
+ }
+ }
+
+ /// Called when changing or removing attributes, before any modification
+ /// has taken place.
+ fn before_remove_attr(&self, name: &Atom, value: DOMString) {
+ match self.super_type() {
+ Some(ref s) => s.before_remove_attr(name, value),
+ _ => (),
+ }
+ }
+
+ /// Returns the right AttrValue variant for the attribute with name `name`
+ /// on this element.
+ fn parse_plain_attribute(&self, name: &str, value: DOMString) -> AttrValue {
+ match self.super_type() {
+ Some(ref s) => s.parse_plain_attribute(name, value),
+ _ => StringAttrValue(value),
+ }
+ }
+
+ /// Called when a Node is appended to a tree, where 'tree_in_doc' indicates
+ /// whether the tree is part of a Document.
+ fn bind_to_tree(&self, tree_in_doc: bool) {
+ match self.super_type() {
+ Some(ref s) => s.bind_to_tree(tree_in_doc),
+ _ => (),
+ }
+ }
+
+ /// Called when a Node is removed from a tree, where 'tree_in_doc'
+ /// indicates whether the tree is part of a Document.
+ fn unbind_from_tree(&self, tree_in_doc: bool) {
+ match self.super_type() {
+ Some(ref s) => s.unbind_from_tree(tree_in_doc),
+ _ => (),
+ }
+ }
+
+ /// Called on the parent when a node is added to its child list.
+ fn child_inserted(&self, child: &JSRef<Node>) {
+ match self.super_type() {
+ Some(ref s) => s.child_inserted(child),
+ _ => (),
+ }
+ }
+
+ /// Called during event dispatch after the bubbling phase completes.
+ fn handle_event(&self, event: &JSRef<Event>) {
+ match self.super_type() {
+ Some(s) => {
+ s.handle_event(event);
+ }
+ _ => (),
+ }
+ }
+}
+
+/// Obtain a VirtualMethods instance for a given Node-derived object. Any
+/// method call on the trait object will invoke the corresponding method on the
+/// concrete type, propagating up the parent hierarchy unless otherwise
+/// interrupted.
+pub fn vtable_for<'a>(node: &'a JSRef<Node>) -> &'a VirtualMethods {
+ match node.type_id() {
+ ElementNodeTypeId(HTMLAnchorElementTypeId) => {
+ let element: &JSRef<HTMLAnchorElement> = HTMLAnchorElementCast::to_ref(node).unwrap();
+ element as &VirtualMethods
+ }
+ ElementNodeTypeId(HTMLAreaElementTypeId) => {
+ let element: &JSRef<HTMLAreaElement> = HTMLAreaElementCast::to_ref(node).unwrap();
+ element as &VirtualMethods
+ }
+ ElementNodeTypeId(HTMLBodyElementTypeId) => {
+ let element: &JSRef<HTMLBodyElement> = HTMLBodyElementCast::to_ref(node).unwrap();
+ element as &VirtualMethods
+ }
+ ElementNodeTypeId(HTMLButtonElementTypeId) => {
+ let element: &JSRef<HTMLButtonElement> = HTMLButtonElementCast::to_ref(node).unwrap();
+ element as &VirtualMethods
+ }
+ ElementNodeTypeId(HTMLCanvasElementTypeId) => {
+ let element: &JSRef<HTMLCanvasElement> = HTMLCanvasElementCast::to_ref(node).unwrap();
+ element as &VirtualMethods
+ }
+ ElementNodeTypeId(HTMLFieldSetElementTypeId) => {
+ let element: &JSRef<HTMLFieldSetElement> = HTMLFieldSetElementCast::to_ref(node).unwrap();
+ element as &VirtualMethods
+ }
+ ElementNodeTypeId(HTMLImageElementTypeId) => {
+ let element: &JSRef<HTMLImageElement> = HTMLImageElementCast::to_ref(node).unwrap();
+ element as &VirtualMethods
+ }
+ ElementNodeTypeId(HTMLIFrameElementTypeId) => {
+ let element: &JSRef<HTMLIFrameElement> = HTMLIFrameElementCast::to_ref(node).unwrap();
+ element as &VirtualMethods
+ }
+ ElementNodeTypeId(HTMLInputElementTypeId) => {
+ let element: &JSRef<HTMLInputElement> = HTMLInputElementCast::to_ref(node).unwrap();
+ element as &VirtualMethods
+ }
+ ElementNodeTypeId(HTMLLinkElementTypeId) => {
+ let element: &JSRef<HTMLLinkElement> = HTMLLinkElementCast::to_ref(node).unwrap();
+ element as &VirtualMethods
+ }
+ ElementNodeTypeId(HTMLObjectElementTypeId) => {
+ let element: &JSRef<HTMLObjectElement> = HTMLObjectElementCast::to_ref(node).unwrap();
+ element as &VirtualMethods
+ }
+ ElementNodeTypeId(HTMLOptGroupElementTypeId) => {
+ let element: &JSRef<HTMLOptGroupElement> = HTMLOptGroupElementCast::to_ref(node).unwrap();
+ element as &VirtualMethods
+ }
+ ElementNodeTypeId(HTMLOptionElementTypeId) => {
+ let element: &JSRef<HTMLOptionElement> = HTMLOptionElementCast::to_ref(node).unwrap();
+ element as &VirtualMethods
+ }
+ ElementNodeTypeId(HTMLSelectElementTypeId) => {
+ let element: &JSRef<HTMLSelectElement> = HTMLSelectElementCast::to_ref(node).unwrap();
+ element as &VirtualMethods
+ }
+ ElementNodeTypeId(HTMLStyleElementTypeId) => {
+ let element: &JSRef<HTMLStyleElement> = HTMLStyleElementCast::to_ref(node).unwrap();
+ element as &VirtualMethods
+ }
+ ElementNodeTypeId(HTMLTextAreaElementTypeId) => {
+ let element: &JSRef<HTMLTextAreaElement> = HTMLTextAreaElementCast::to_ref(node).unwrap();
+ element as &VirtualMethods
+ }
+ ElementNodeTypeId(ElementTypeId) => {
+ let element: &JSRef<Element> = ElementCast::to_ref(node).unwrap();
+ element as &VirtualMethods
+ }
+ ElementNodeTypeId(_) => {
+ let element: &JSRef<HTMLElement> = HTMLElementCast::to_ref(node).unwrap();
+ element as &VirtualMethods
+ }
+ _ => {
+ node as &VirtualMethods
+ }
+ }
+}
diff --git a/components/script/dom/webidls/Attr.webidl b/components/script/dom/webidls/Attr.webidl
new file mode 100644
index 00000000000..2b3d18150d8
--- /dev/null
+++ b/components/script/dom/webidls/Attr.webidl
@@ -0,0 +1,18 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * http://dom.spec.whatwg.org/#interface-attr
+ *
+ */
+
+interface Attr {
+ readonly attribute DOMString localName;
+ attribute DOMString value;
+
+ readonly attribute DOMString name;
+ readonly attribute DOMString? namespaceURI;
+ readonly attribute DOMString? prefix;
+};
diff --git a/components/script/dom/webidls/Blob.webidl b/components/script/dom/webidls/Blob.webidl
new file mode 100644
index 00000000000..3a544024338
--- /dev/null
+++ b/components/script/dom/webidls/Blob.webidl
@@ -0,0 +1,29 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://dev.w3.org/2006/webapi/FileAPI/#dfn-Blob
+//[Exposed=Window,Worker][Constructor,
+// Constructor(sequence<(ArrayBuffer or ArrayBufferView or Blob or DOMString)> blobParts, optional BlobPropertyBag options)]
+[Constructor]
+interface Blob {
+
+ //readonly attribute unsigned long long size;
+ //readonly attribute DOMString type;
+ //readonly attribute boolean isClosed;
+
+ //slice Blob into byte-ranged chunks
+
+ //Blob slice([Clamp] optional long long start,
+ // [Clamp] optional long long end,
+ // optional DOMString contentType);
+ //void close();
+
+};
+
+dictionary BlobPropertyBag {
+
+ DOMString type = "";
+
+};
diff --git a/components/script/dom/webidls/CanvasRenderingContext2D.webidl b/components/script/dom/webidls/CanvasRenderingContext2D.webidl
new file mode 100644
index 00000000000..2043347bfd2
--- /dev/null
+++ b/components/script/dom/webidls/CanvasRenderingContext2D.webidl
@@ -0,0 +1,104 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#2dcontext
+//[Constructor(optional unsigned long width, unsigned long height), Exposed=Window,Worker]
+interface CanvasRenderingContext2D {
+
+ // back-reference to the canvas
+ readonly attribute HTMLCanvasElement canvas;
+
+ // canvas dimensions
+ // attribute unsigned long width;
+ // attribute unsigned long height;
+
+ // for contexts that aren't directly fixed to a specific canvas
+ //void commit(); // push the image to the output bitmap
+
+ // state
+ //void save(); // push state on state stack
+ //void restore(); // pop state stack and restore state
+
+ // transformations (default transform is the identity matrix)
+ // attribute SVGMatrix currentTransform;
+ //void scale(unrestricted double x, unrestricted double y);
+ //void rotate(unrestricted double angle);
+ //void translate(unrestricted double x, unrestricted double y);
+ //void transform(unrestricted double a, unrestricted double b, unrestricted double c, unrestricted double d, unrestricted double e, unrestricted double f);
+ //void setTransform(unrestricted double a, unrestricted double b, unrestricted double c, unrestricted double d, unrestricted double e, unrestricted double f);
+ //void resetTransform();
+
+ // compositing
+ // attribute unrestricted double globalAlpha; // (default 1.0)
+ // attribute DOMString globalCompositeOperation; // (default source-over)
+
+ // image smoothing
+ // attribute boolean imageSmoothingEnabled; // (default true)
+
+ // colours and styles (see also the CanvasDrawingStyles interface)
+ // attribute (DOMString or CanvasGradient or CanvasPattern) strokeStyle; // (default black)
+ // attribute (DOMString or CanvasGradient or CanvasPattern) fillStyle; // (default black)
+ //CanvasGradient createLinearGradient(double x0, double y0, double x1, double y1);
+ //CanvasGradient createRadialGradient(double x0, double y0, double r0, double x1, double y1, double r1);
+ //CanvasPattern createPattern(CanvasImageSource image, [TreatNullAs=EmptyString] DOMString repetition);
+
+ // shadows
+ // attribute unrestricted double shadowOffsetX; // (default 0)
+ // attribute unrestricted double shadowOffsetY; // (default 0)
+ // attribute unrestricted double shadowBlur; // (default 0)
+ // attribute DOMString shadowColor; // (default transparent black)
+
+ // rects
+ //void clearRect(unrestricted double x, unrestricted double y, unrestricted double w, unrestricted double h);
+ //[LenientFloat]
+ void clearRect(double x, double y, double w, double h);
+ //void fillRect(unrestricted double x, unrestricted double y, unrestricted double w, unrestricted double h);
+ //[LenientFloat]
+ void fillRect(double x, double y, double w, double h);
+ //void strokeRect(unrestricted double x, unrestricted double y, unrestricted double w, unrestricted double h);
+ //[LenientFloat]
+ void strokeRect(double x, double y, double w, double h);
+
+ // path API (see also CanvasPathMethods)
+ //void beginPath();
+ //void fill(optional CanvasFillRule fillRule = "nonzero");
+ //void fill(Path2D path, optional CanvasFillRule fillRule = "nonzero");
+ //void stroke();
+ //void stroke(Path2D path);
+ //void drawSystemFocusRing(Element element);
+ //void drawSystemFocusRing(Path2D path, Element element);
+ //boolean drawCustomFocusRing(Element element);
+ //boolean drawCustomFocusRing(Path2D path, Element element);
+ //void scrollPathIntoView();
+ //void scrollPathIntoView(Path2D path);
+ //void clip(optional CanvasFillRule fillRule = "nonzero");
+ //void clip(Path2D path, optional CanvasFillRule fillRule = "nonzero");
+ //void resetClip();
+ //boolean isPointInPath(unrestricted double x, unrestricted double y, optional CanvasFillRule fillRule = "nonzero");
+ //boolean isPointInPath(Path2D path, unrestricted double x, unrestricted double y, optional CanvasFillRule fillRule = "nonzero");
+ //boolean isPointInStroke(unrestricted double x, unrestricted double y);
+ //boolean isPointInStroke(Path2D path, unrestricted double x, unrestricted double y);
+
+ // text (see also the CanvasDrawingStyles interface)
+ //void fillText(DOMString text, unrestricted double x, unrestricted double y, optional unrestricted double maxWidth);
+ //void strokeText(DOMString text, unrestricted double x, unrestricted double y, optional unrestricted double maxWidth);
+ //TextMetrics measureText(DOMString text);
+
+ // drawing images
+ //void drawImage(CanvasImageSource image, unrestricted double dx, unrestricted double dy);
+ //void drawImage(CanvasImageSource image, unrestricted double dx, unrestricted double dy, unrestricted double dw, unrestricted double dh);
+ //void drawImage(CanvasImageSource image, unrestricted double sx, unrestricted double sy, unrestricted double sw, unrestricted double sh, unrestricted double dx, unrestricted double dy, unrestricted double dw, unrestricted double dh);
+
+ // hit regions
+ //void addHitRegion(optional HitRegionOptions options);
+ //void removeHitRegion(DOMString id);
+
+ // pixel manipulation
+ //ImageData createImageData(double sw, double sh);
+ //ImageData createImageData(ImageData imagedata);
+ //ImageData getImageData(double sx, double sy, double sw, double sh);
+ //void putImageData(ImageData imagedata, double dx, double dy);
+ //void putImageData(ImageData imagedata, double dx, double dy, double dirtyX, double dirtyY, double dirtyWidth, double dirtyHeight);
+};
diff --git a/components/script/dom/webidls/CharacterData.webidl b/components/script/dom/webidls/CharacterData.webidl
new file mode 100644
index 00000000000..d1b222bc168
--- /dev/null
+++ b/components/script/dom/webidls/CharacterData.webidl
@@ -0,0 +1,28 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * http://dom.spec.whatwg.org/#characterdata
+ *
+ * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
+ * liability, trademark and document use rules apply.
+ */
+
+interface CharacterData : Node {
+ [TreatNullAs=EmptyString,SetterThrows] attribute DOMString data;
+ readonly attribute unsigned long length;
+ [Throws]
+ DOMString substringData(unsigned long offset, unsigned long count);
+ [Throws]
+ void appendData(DOMString data);
+ [Throws]
+ void insertData(unsigned long offset, DOMString data);
+ [Throws]
+ void deleteData(unsigned long offset, unsigned long count);
+ [Throws]
+ void replaceData(unsigned long offset, unsigned long count, DOMString data);
+};
+
+CharacterData implements ChildNode;
diff --git a/components/script/dom/webidls/ChildNode.webidl b/components/script/dom/webidls/ChildNode.webidl
new file mode 100644
index 00000000000..16562fbafbf
--- /dev/null
+++ b/components/script/dom/webidls/ChildNode.webidl
@@ -0,0 +1,25 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is:
+ * http://dom.spec.whatwg.org/#interface-childnode
+ */
+
+[NoInterfaceObject]
+interface ChildNode {
+// Not implemented yet:
+// void before((Node or DOMString)... nodes);
+// void after((Node or DOMString)... nodes);
+// void replace((Node or DOMString)... nodes);
+ void remove();
+};
+
+// [NoInterfaceObject]
+// interface NonDocumentTypeChildNode {
+// [Pure]
+// readonly attribute Element? previousElementSibling;
+// [Pure]
+// readonly attribute Element? nextElementSibling;
+// };
diff --git a/components/script/dom/webidls/Comment.webidl b/components/script/dom/webidls/Comment.webidl
new file mode 100644
index 00000000000..023335f166a
--- /dev/null
+++ b/components/script/dom/webidls/Comment.webidl
@@ -0,0 +1,15 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * http://dom.spec.whatwg.org/#comment
+ *
+ * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
+ * liability, trademark and document use rules apply.
+ */
+
+[Constructor(optional DOMString data = "")]
+interface Comment : CharacterData {
+};
diff --git a/components/script/dom/webidls/Console.webidl b/components/script/dom/webidls/Console.webidl
new file mode 100644
index 00000000000..23b294596a8
--- /dev/null
+++ b/components/script/dom/webidls/Console.webidl
@@ -0,0 +1,21 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * References:
+ * MDN Docs - https://developer.mozilla.org/en-US/docs/Web/API/console
+ * Draft Spec - http://sideshowbarker.github.io/console-spec/
+ *
+ * © Copyright 2014 Mozilla Foundation.
+ */
+
+interface Console {
+ // These should be DOMString message, DOMString message2, ...
+ void log(DOMString message);
+ void debug(DOMString message);
+ void info(DOMString message);
+ void warn(DOMString message);
+ void error(DOMString message);
+ void assert(boolean condition, optional DOMString message);
+};
diff --git a/components/script/dom/webidls/CustomEvent.webidl b/components/script/dom/webidls/CustomEvent.webidl
new file mode 100644
index 00000000000..bfc99c99954
--- /dev/null
+++ b/components/script/dom/webidls/CustomEvent.webidl
@@ -0,0 +1,27 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * For more information on this interface please see
+ * http://dom.spec.whatwg.org/#interface-customevent
+ *
+ * To the extent possible under law, the editors have waived
+ * all copyright and related or neighboring rights to this work.
+ * In addition, as of 1 May 2014, the editors have made this specification
+ * available under the Open Web Foundation Agreement Version 1.0,
+ * which is available at
+ * http://www.openwebfoundation.org/legal/the-owf-1-0-agreements/owfa-1-0.
+ */
+
+[Constructor(DOMString type, optional CustomEventInit eventInitDict)/*,
+ Exposed=Window,Worker*/]
+interface CustomEvent : Event {
+ readonly attribute any detail;
+
+ void initCustomEvent(DOMString type, boolean bubbles, boolean cancelable, any detail);
+};
+
+dictionary CustomEventInit : EventInit {
+ any detail = null;
+};
diff --git a/components/script/dom/webidls/DOMException.webidl b/components/script/dom/webidls/DOMException.webidl
new file mode 100644
index 00000000000..7347d2e76cc
--- /dev/null
+++ b/components/script/dom/webidls/DOMException.webidl
@@ -0,0 +1,47 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is:
+ * http://dom.spec.whatwg.org/#domexception
+ */
+
+// XXXkhuey this is an 'exception', not an interface, but we don't have any
+// parser or codegen mechanisms for dealing with exceptions.
+interface DOMException {
+ const unsigned short INDEX_SIZE_ERR = 1;
+ const unsigned short DOMSTRING_SIZE_ERR = 2; // historical
+ const unsigned short HIERARCHY_REQUEST_ERR = 3;
+ const unsigned short WRONG_DOCUMENT_ERR = 4;
+ const unsigned short INVALID_CHARACTER_ERR = 5;
+ const unsigned short NO_DATA_ALLOWED_ERR = 6; // historical
+ const unsigned short NO_MODIFICATION_ALLOWED_ERR = 7;
+ const unsigned short NOT_FOUND_ERR = 8;
+ const unsigned short NOT_SUPPORTED_ERR = 9;
+ const unsigned short INUSE_ATTRIBUTE_ERR = 10; // historical
+ const unsigned short INVALID_STATE_ERR = 11;
+ const unsigned short SYNTAX_ERR = 12;
+ const unsigned short INVALID_MODIFICATION_ERR = 13;
+ const unsigned short NAMESPACE_ERR = 14;
+ const unsigned short INVALID_ACCESS_ERR = 15;
+ const unsigned short VALIDATION_ERR = 16; // historical
+ const unsigned short TYPE_MISMATCH_ERR = 17; // historical; use JavaScript's TypeError instead
+ const unsigned short SECURITY_ERR = 18;
+ const unsigned short NETWORK_ERR = 19;
+ const unsigned short ABORT_ERR = 20;
+ const unsigned short URL_MISMATCH_ERR = 21;
+ const unsigned short QUOTA_EXCEEDED_ERR = 22;
+ const unsigned short TIMEOUT_ERR = 23;
+ const unsigned short INVALID_NODE_TYPE_ERR = 24;
+ const unsigned short DATA_CLONE_ERR = 25;
+
+ // Error code as u16
+ readonly attribute unsigned short code;
+
+ // The name of the error code (ie, a string repr of |code|)
+ readonly attribute DOMString name;
+
+ // A custom message set by the thrower.
+ readonly attribute DOMString message;
+};
diff --git a/components/script/dom/webidls/DOMImplementation.webidl b/components/script/dom/webidls/DOMImplementation.webidl
new file mode 100644
index 00000000000..50f7510b800
--- /dev/null
+++ b/components/script/dom/webidls/DOMImplementation.webidl
@@ -0,0 +1,25 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * http://dom.spec.whatwg.org/#interface-domimplementation
+ *
+ * Copyright:
+ * To the extent possible under law, the editors have waived all copyright and
+ * related or neighboring rights to this work.
+ */
+
+interface DOMImplementation {
+ /*boolean hasFeature(DOMString feature,
+ [TreatNullAs=EmptyString] DOMString version);*/
+ [Throws]
+ DocumentType createDocumentType(DOMString qualifiedName, DOMString publicId,
+ DOMString systemId);
+ [Throws]
+ Document createDocument(DOMString? namespace,
+ [TreatNullAs=EmptyString] DOMString qualifiedName,
+ optional DocumentType? doctype = null);
+ Document createHTMLDocument(optional DOMString title);
+};
diff --git a/components/script/dom/webidls/DOMParser.webidl b/components/script/dom/webidls/DOMParser.webidl
new file mode 100644
index 00000000000..236fae785f2
--- /dev/null
+++ b/components/script/dom/webidls/DOMParser.webidl
@@ -0,0 +1,21 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * http://domparsing.spec.whatwg.org/#the-domparser-interface
+ */
+
+enum SupportedType {
+ "text/html",
+ "text/xml",
+ "application/xml",
+ "application/xhtml+xml",
+ "image/svg+xml"
+};
+
+[Constructor]
+interface DOMParser {
+ [Throws]
+ Document parseFromString(DOMString str, SupportedType type);
+};
diff --git a/components/script/dom/webidls/DOMRect.webidl b/components/script/dom/webidls/DOMRect.webidl
new file mode 100644
index 00000000000..6e0fe24b57d
--- /dev/null
+++ b/components/script/dom/webidls/DOMRect.webidl
@@ -0,0 +1,14 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://dev.w3.org/fxtf/geometry/#DOMRect
+interface DOMRect {
+ readonly attribute float top;
+ readonly attribute float right;
+ readonly attribute float bottom;
+ readonly attribute float left;
+ readonly attribute float width;
+ readonly attribute float height;
+};
diff --git a/components/script/dom/webidls/DOMRectList.webidl b/components/script/dom/webidls/DOMRectList.webidl
new file mode 100644
index 00000000000..064014e9abe
--- /dev/null
+++ b/components/script/dom/webidls/DOMRectList.webidl
@@ -0,0 +1,12 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://dev.w3.org/fxtf/geometry/#DOMRectList
+[NoInterfaceObject/*,
+ ArrayClass*/]
+interface DOMRectList {
+ readonly attribute unsigned long length;
+ getter DOMRect? item(unsigned long index);
+};
diff --git a/components/script/dom/webidls/DOMTokenList.webidl b/components/script/dom/webidls/DOMTokenList.webidl
new file mode 100644
index 00000000000..bc32f4bf256
--- /dev/null
+++ b/components/script/dom/webidls/DOMTokenList.webidl
@@ -0,0 +1,18 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://dom.spec.whatwg.org/#domtokenlist
+interface DOMTokenList {
+ readonly attribute unsigned long length;
+ getter DOMString? item(unsigned long index);
+
+ [Throws]
+ boolean contains(DOMString token);
+
+ //void add(DOMString... tokens);
+ //void remove(DOMString... tokens);
+ //boolean toggle(DOMString token, optional boolean force);
+ //stringifier;
+};
diff --git a/components/script/dom/webidls/DedicatedWorkerGlobalScope.webidl b/components/script/dom/webidls/DedicatedWorkerGlobalScope.webidl
new file mode 100644
index 00000000000..dbf2891f72a
--- /dev/null
+++ b/components/script/dom/webidls/DedicatedWorkerGlobalScope.webidl
@@ -0,0 +1,10 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#dedicatedworkerglobalscope
+[Global/*=Worker,DedicatedWorker*/]
+/*sealed*/ interface DedicatedWorkerGlobalScope : WorkerGlobalScope {
+ void postMessage(any message/*, optional sequence<Transferable> transfer*/);
+ attribute EventHandler onmessage;
+};
diff --git a/components/script/dom/webidls/Document.webidl b/components/script/dom/webidls/Document.webidl
new file mode 100644
index 00000000000..0599ba71f95
--- /dev/null
+++ b/components/script/dom/webidls/Document.webidl
@@ -0,0 +1,71 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is:
+ * http://dom.spec.whatwg.org/#interface-document
+ * http://www.whatwg.org/specs/web-apps/current-work/#the-document-object
+ */
+
+/* http://dom.spec.whatwg.org/#interface-document */
+[Constructor]
+interface Document : Node {
+ readonly attribute DOMImplementation implementation;
+ readonly attribute DOMString URL;
+ readonly attribute DOMString documentURI;
+ readonly attribute DOMString compatMode;
+ readonly attribute DOMString characterSet;
+ readonly attribute DOMString contentType;
+ readonly attribute Location location;
+
+ readonly attribute DocumentType? doctype;
+ readonly attribute Element? documentElement;
+ HTMLCollection getElementsByTagName(DOMString localName);
+ HTMLCollection getElementsByTagNameNS(DOMString? namespace, DOMString localName);
+ HTMLCollection getElementsByClassName(DOMString classNames);
+ Element? getElementById(DOMString elementId);
+
+ [Throws]
+ Element createElement(DOMString localName);
+ [Throws]
+ Element createElementNS(DOMString? namespace, DOMString qualifiedName);
+ DocumentFragment createDocumentFragment();
+ Text createTextNode(DOMString data);
+ Comment createComment(DOMString data);
+ [Throws]
+ ProcessingInstruction createProcessingInstruction(DOMString target, DOMString data);
+
+ [Throws]
+ Node importNode(Node node, optional boolean deep = false);
+ [Throws]
+ Node adoptNode(Node node);
+
+ [Throws]
+ Event createEvent(DOMString interface_);
+
+ Range createRange();
+};
+
+/* http://www.whatwg.org/specs/web-apps/current-work/#the-document-object */
+partial interface Document {
+ readonly attribute DOMString lastModified;
+ [SetterThrows]
+ attribute DOMString title;
+ [SetterThrows]
+ attribute HTMLElement? body;
+ readonly attribute HTMLHeadElement? head;
+ NodeList getElementsByName(DOMString elementName);
+
+ readonly attribute HTMLCollection images;
+ readonly attribute HTMLCollection embeds;
+ readonly attribute HTMLCollection plugins;
+ readonly attribute HTMLCollection links;
+ readonly attribute HTMLCollection forms;
+ readonly attribute HTMLCollection scripts;
+ readonly attribute HTMLCollection anchors;
+ readonly attribute HTMLCollection applets;
+};
+
+Document implements ParentNode;
+Document implements GlobalEventHandlers;
diff --git a/components/script/dom/webidls/DocumentFragment.webidl b/components/script/dom/webidls/DocumentFragment.webidl
new file mode 100644
index 00000000000..4248975f768
--- /dev/null
+++ b/components/script/dom/webidls/DocumentFragment.webidl
@@ -0,0 +1,11 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://dom.spec.whatwg.org/#interface-documentfragment
+[Constructor]
+interface DocumentFragment : Node {
+};
+
+DocumentFragment implements ParentNode;
diff --git a/components/script/dom/webidls/DocumentType.webidl b/components/script/dom/webidls/DocumentType.webidl
new file mode 100644
index 00000000000..89190266fde
--- /dev/null
+++ b/components/script/dom/webidls/DocumentType.webidl
@@ -0,0 +1,19 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * http://dom.spec.whatwg.org/#documenttype
+ *
+ * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
+ * liability, trademark and document use rules apply.
+ */
+
+interface DocumentType : Node {
+ readonly attribute DOMString name;
+ readonly attribute DOMString publicId;
+ readonly attribute DOMString systemId;
+};
+
+DocumentType implements ChildNode;
diff --git a/components/script/dom/webidls/Element.webidl b/components/script/dom/webidls/Element.webidl
new file mode 100644
index 00000000000..fc737bdb9ec
--- /dev/null
+++ b/components/script/dom/webidls/Element.webidl
@@ -0,0 +1,70 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * http://dom.spec.whatwg.org/#element and
+ * http://domparsing.spec.whatwg.org/ and
+ * http://dev.w3.org/csswg/cssom-view/ and
+ * http://www.w3.org/TR/selectors-api/
+ *
+ * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
+ * liability, trademark and document use rules apply.
+ */
+
+interface Element : Node {
+
+ readonly attribute DOMString? prefix;
+ readonly attribute DOMString localName;
+
+ [Constant]
+ readonly attribute DOMString? namespaceURI;
+ // Not [Constant] because it depends on which document we're in
+ [Pure]
+ readonly attribute DOMString tagName;
+
+ [Pure]
+ attribute DOMString id;
+ [Pure]
+ attribute DOMString className;
+ [Constant]
+ readonly attribute DOMTokenList classList;
+
+ [Constant]
+ readonly attribute NamedNodeMap attributes;
+ DOMString? getAttribute(DOMString name);
+ DOMString? getAttributeNS(DOMString? namespace, DOMString localName);
+ [Throws]
+ void setAttribute(DOMString name, DOMString value);
+ [Throws]
+ void setAttributeNS(DOMString? namespace, DOMString name, DOMString value);
+ void removeAttribute(DOMString name);
+ void removeAttributeNS(DOMString? namespace, DOMString localName);
+ boolean hasAttribute(DOMString name);
+ boolean hasAttributeNS(DOMString? namespace, DOMString localName);
+
+ [Throws]
+ boolean matches(DOMString selectors);
+
+ HTMLCollection getElementsByTagName(DOMString localName);
+ HTMLCollection getElementsByTagNameNS(DOMString? namespace, DOMString localName);
+ HTMLCollection getElementsByClassName(DOMString classNames);
+};
+
+// http://dev.w3.org/csswg/cssom-view/#extensions-to-the-element-interface
+partial interface Element {
+ DOMRectList getClientRects();
+ DOMRect getBoundingClientRect();
+};
+
+// http://domparsing.spec.whatwg.org/#extensions-to-the-element-interface
+partial interface Element {
+ [Throws,TreatNullAs=EmptyString]
+ readonly attribute DOMString innerHTML;
+ [Throws,TreatNullAs=EmptyString]
+ readonly attribute DOMString outerHTML;
+};
+
+Element implements ChildNode;
+Element implements ParentNode;
diff --git a/components/script/dom/webidls/Event.webidl b/components/script/dom/webidls/Event.webidl
new file mode 100644
index 00000000000..6e574427548
--- /dev/null
+++ b/components/script/dom/webidls/Event.webidl
@@ -0,0 +1,43 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * For more information on this interface please see
+ * http://dev.w3.org/2006/webapi/DOM-Level-3-Events/html/DOM3-Events.html
+ *
+ * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
+ * liability, trademark and document use rules apply.
+ */
+
+[Constructor(DOMString type, optional EventInit eventInitDict)]
+interface Event {
+ readonly attribute DOMString type;
+ readonly attribute EventTarget? target;
+ readonly attribute EventTarget? currentTarget;
+
+ const unsigned short NONE = 0;
+ const unsigned short CAPTURING_PHASE = 1;
+ const unsigned short AT_TARGET = 2;
+ const unsigned short BUBBLING_PHASE = 3;
+ readonly attribute unsigned short eventPhase;
+
+ void stopPropagation();
+ void stopImmediatePropagation();
+
+ readonly attribute boolean bubbles;
+ readonly attribute boolean cancelable;
+ void preventDefault();
+ readonly attribute boolean defaultPrevented;
+
+ readonly attribute boolean isTrusted;
+ readonly attribute DOMTimeStamp timeStamp;
+
+ void initEvent(DOMString type, boolean bubbles, boolean cancelable);
+};
+
+dictionary EventInit {
+ boolean bubbles = false;
+ boolean cancelable = false;
+};
+
diff --git a/components/script/dom/webidls/EventHandler.webidl b/components/script/dom/webidls/EventHandler.webidl
new file mode 100644
index 00000000000..1278d7467fd
--- /dev/null
+++ b/components/script/dom/webidls/EventHandler.webidl
@@ -0,0 +1,46 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * http://www.whatwg.org/specs/web-apps/current-work/#eventhandler
+ *
+ * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
+ * Opera Software ASA. You are granted a license to use, reproduce
+ * and create derivative works of this document.
+ */
+
+[TreatNonObjectAsNull]
+callback EventHandlerNonNull = any (Event event);
+typedef EventHandlerNonNull? EventHandler;
+
+[TreatNonObjectAsNull]
+callback OnErrorEventHandlerNonNull = boolean ((Event or DOMString) event, optional DOMString source, optional unsigned long lineno, optional unsigned long column, optional any error);
+typedef OnErrorEventHandlerNonNull? OnErrorEventHandler;
+
+[NoInterfaceObject]
+interface GlobalEventHandlers {
+ attribute EventHandler onclick;
+ attribute EventHandler onload;
+};
+
+[NoInterfaceObject]
+interface WindowEventHandlers {
+ attribute EventHandler onunload;
+};
+
+// The spec has |attribute OnErrorEventHandler onerror;| on
+// GlobalEventHandlers, and calls the handler differently depending on
+// whether an ErrorEvent was fired. We don't do that, and until we do we'll
+// need to distinguish between onerror on Window or on nodes.
+
+/*[NoInterfaceObject]
+interface OnErrorEventHandlerForNodes {
+ attribute EventHandler onerror;
+};*/
+
+[NoInterfaceObject]
+interface OnErrorEventHandlerForWindow {
+ attribute OnErrorEventHandler onerror;
+};
diff --git a/components/script/dom/webidls/EventListener.webidl b/components/script/dom/webidls/EventListener.webidl
new file mode 100644
index 00000000000..05e1684d31e
--- /dev/null
+++ b/components/script/dom/webidls/EventListener.webidl
@@ -0,0 +1,16 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * http://www.w3.org/TR/2012/WD-dom-20120105/
+ *
+ * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
+ * liability, trademark and document use rules apply.
+ */
+
+callback interface EventListener {
+ void handleEvent(Event event);
+};
+
diff --git a/components/script/dom/webidls/EventTarget.webidl b/components/script/dom/webidls/EventTarget.webidl
new file mode 100644
index 00000000000..897756fa273
--- /dev/null
+++ b/components/script/dom/webidls/EventTarget.webidl
@@ -0,0 +1,22 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * http://www.w3.org/TR/2012/WD-dom-20120105/
+ *
+ * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
+ * liability, trademark and document use rules apply.
+ */
+
+interface EventTarget {
+ void addEventListener(DOMString type,
+ EventListener? listener,
+ optional boolean capture = false);
+ void removeEventListener(DOMString type,
+ EventListener? listener,
+ optional boolean capture = false);
+ [Throws]
+ boolean dispatchEvent(Event event);
+};
diff --git a/components/script/dom/webidls/File.webidl b/components/script/dom/webidls/File.webidl
new file mode 100644
index 00000000000..0d5967b5e55
--- /dev/null
+++ b/components/script/dom/webidls/File.webidl
@@ -0,0 +1,15 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://dev.w3.org/2006/webapi/FileAPI/#dfn-file
+
+// [Constructor(sequence<(Blob or DOMString or ArrayBufferView or ArrayBuffer)> fileBits,
+// [EnsureUTF16] DOMString fileName, optional FilePropertyBag options)]
+interface File : Blob {
+
+ readonly attribute DOMString name;
+ // readonly attribute Date lastModifiedDate;
+
+}; \ No newline at end of file
diff --git a/components/script/dom/webidls/FormData.webidl b/components/script/dom/webidls/FormData.webidl
new file mode 100644
index 00000000000..cfa3e89b3cd
--- /dev/null
+++ b/components/script/dom/webidls/FormData.webidl
@@ -0,0 +1,22 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * http://xhr.spec.whatwg.org
+ */
+
+typedef (File or DOMString) FormDataEntryValue;
+
+[Constructor(optional HTMLFormElement form)]
+interface FormData {
+ void append(DOMString name, Blob value, optional DOMString filename);
+ void append(DOMString name, DOMString value);
+ void delete(DOMString name);
+ FormDataEntryValue? get(DOMString name);
+ // sequence<FormDataEntryValue> getAll(DOMString name);
+ boolean has(DOMString name);
+ void set(DOMString name, Blob value, optional DOMString filename);
+ void set(DOMString name, DOMString value);
+};
diff --git a/components/script/dom/webidls/HTMLAnchorElement.webidl b/components/script/dom/webidls/HTMLAnchorElement.webidl
new file mode 100644
index 00000000000..de80a803514
--- /dev/null
+++ b/components/script/dom/webidls/HTMLAnchorElement.webidl
@@ -0,0 +1,38 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * http://www.whatwg.org/specs/web-apps/current-work/#the-a-element
+ * http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
+ * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
+ * Opera Software ASA. You are granted a license to use, reproduce
+ * and create derivative works of this document.
+ */
+
+// http://www.whatwg.org/html/#htmlanchorelement
+interface HTMLAnchorElement : HTMLElement {
+ // attribute DOMString target;
+ // attribute DOMString download;
+ //[PutForwards=value] attribute DOMSettableTokenList ping;
+ // attribute DOMString rel;
+ //readonly attribute DOMTokenList relList;
+ // attribute DOMString hreflang;
+ // attribute DOMString type;
+
+ [Pure]
+ attribute DOMString text;
+
+ // also has obsolete members
+};
+//HTMLAnchorElement implements URLUtils;
+
+// http://www.whatwg.org/html/#HTMLAnchorElement-partial
+partial interface HTMLAnchorElement {
+ // attribute DOMString coords;
+ // attribute DOMString charset;
+ // attribute DOMString name;
+ // attribute DOMString rev;
+ // attribute DOMString shape;
+};
diff --git a/components/script/dom/webidls/HTMLAppletElement.webidl b/components/script/dom/webidls/HTMLAppletElement.webidl
new file mode 100644
index 00000000000..2612ffee2a1
--- /dev/null
+++ b/components/script/dom/webidls/HTMLAppletElement.webidl
@@ -0,0 +1,19 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmlappletelement
+interface HTMLAppletElement : HTMLElement {
+ // attribute DOMString align;
+ // attribute DOMString alt;
+ // attribute DOMString archive;
+ // attribute DOMString code;
+ // attribute DOMString codeBase;
+ // attribute DOMString height;
+ // attribute unsigned long hspace;
+ // attribute DOMString name;
+ // attribute DOMString _object; // the underscore is not part of the identifier
+ // attribute unsigned long vspace;
+ // attribute DOMString width;
+};
diff --git a/components/script/dom/webidls/HTMLAreaElement.webidl b/components/script/dom/webidls/HTMLAreaElement.webidl
new file mode 100644
index 00000000000..0cbbe2d7ce1
--- /dev/null
+++ b/components/script/dom/webidls/HTMLAreaElement.webidl
@@ -0,0 +1,26 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmlareaelement
+interface HTMLAreaElement : HTMLElement {
+ // attribute DOMString alt;
+ // attribute DOMString coords;
+ // attribute DOMString shape;
+ // attribute DOMString target;
+ // attribute DOMString download;
+ //[PutForwards=value] attribute DOMSettableTokenList ping;
+ // attribute DOMString rel;
+ //readonly attribute DOMTokenList relList;
+ // attribute DOMString hreflang;
+ // attribute DOMString type;
+
+ // also has obsolete members
+};
+//HTMLAreaElement implements URLUtils;
+
+// http://www.whatwg.org/html/#HTMLAreaElement-partial
+partial interface HTMLAreaElement {
+ // attribute boolean noHref;
+};
diff --git a/components/script/dom/webidls/HTMLAudioElement.webidl b/components/script/dom/webidls/HTMLAudioElement.webidl
new file mode 100644
index 00000000000..9832eeda044
--- /dev/null
+++ b/components/script/dom/webidls/HTMLAudioElement.webidl
@@ -0,0 +1,8 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmlaudioelement
+//[NamedConstructor=Audio(optional DOMString src)]
+interface HTMLAudioElement : HTMLMediaElement {};
diff --git a/components/script/dom/webidls/HTMLBRElement.webidl b/components/script/dom/webidls/HTMLBRElement.webidl
new file mode 100644
index 00000000000..972b9377a0e
--- /dev/null
+++ b/components/script/dom/webidls/HTMLBRElement.webidl
@@ -0,0 +1,14 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmlbrelement
+interface HTMLBRElement : HTMLElement {
+ // also has obsolete members
+};
+
+// http://www.whatwg.org/html/#HTMLBRElement-partial
+partial interface HTMLBRElement {
+ // attribute DOMString clear;
+};
diff --git a/components/script/dom/webidls/HTMLBaseElement.webidl b/components/script/dom/webidls/HTMLBaseElement.webidl
new file mode 100644
index 00000000000..c39951b6783
--- /dev/null
+++ b/components/script/dom/webidls/HTMLBaseElement.webidl
@@ -0,0 +1,10 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmlbaseelement
+interface HTMLBaseElement : HTMLElement {
+ // attribute DOMString href;
+ // attribute DOMString target;
+};
diff --git a/components/script/dom/webidls/HTMLBodyElement.webidl b/components/script/dom/webidls/HTMLBodyElement.webidl
new file mode 100644
index 00000000000..6d6967d2709
--- /dev/null
+++ b/components/script/dom/webidls/HTMLBodyElement.webidl
@@ -0,0 +1,21 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmlbodyelement
+interface HTMLBodyElement : HTMLElement {
+
+ // also has obsolete members
+};
+HTMLBodyElement implements WindowEventHandlers;
+
+// http://www.whatwg.org/html/#HTMLBodyElement-partial
+partial interface HTMLBodyElement {
+ //[TreatNullAs=EmptyString] attribute DOMString text;
+ //[TreatNullAs=EmptyString] attribute DOMString link;
+ //[TreatNullAs=EmptyString] attribute DOMString vLink;
+ //[TreatNullAs=EmptyString] attribute DOMString aLink;
+ //[TreatNullAs=EmptyString] attribute DOMString bgColor;
+ // attribute DOMString background;
+};
diff --git a/components/script/dom/webidls/HTMLButtonElement.webidl b/components/script/dom/webidls/HTMLButtonElement.webidl
new file mode 100644
index 00000000000..ad21e11370f
--- /dev/null
+++ b/components/script/dom/webidls/HTMLButtonElement.webidl
@@ -0,0 +1,29 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmlbuttonelement
+interface HTMLButtonElement : HTMLElement {
+ // attribute boolean autofocus;
+ attribute boolean disabled;
+ //readonly attribute HTMLFormElement? form;
+ // attribute DOMString formAction;
+ // attribute DOMString formEnctype;
+ // attribute DOMString formMethod;
+ // attribute boolean formNoValidate;
+ // attribute DOMString formTarget;
+ // attribute DOMString name;
+ // attribute DOMString type;
+ // attribute DOMString value;
+ // attribute HTMLMenuElement? menu;
+
+ //readonly attribute boolean willValidate;
+ readonly attribute ValidityState validity;
+ //readonly attribute DOMString validationMessage;
+ //boolean checkValidity();
+ //boolean reportValidity();
+ //void setCustomValidity(DOMString error);
+
+ //readonly attribute NodeList labels;
+};
diff --git a/components/script/dom/webidls/HTMLCanvasElement.webidl b/components/script/dom/webidls/HTMLCanvasElement.webidl
new file mode 100644
index 00000000000..baff0dc745c
--- /dev/null
+++ b/components/script/dom/webidls/HTMLCanvasElement.webidl
@@ -0,0 +1,24 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmlcanvaselement
+//typedef (CanvasRenderingContext2D or WebGLRenderingContext) RenderingContext;
+
+interface HTMLCanvasElement : HTMLElement {
+ [Pure]
+ attribute unsigned long width;
+ [Pure]
+ attribute unsigned long height;
+
+ //RenderingContext? getContext(DOMString contextId, any... arguments);
+ CanvasRenderingContext2D? getContext(DOMString contextId);
+ //boolean probablySupportsContext(DOMString contextId, any... arguments);
+
+ //void setContext(RenderingContext context);
+ //CanvasProxy transferControlToProxy();
+
+ //DOMString toDataURL(optional DOMString type, any... arguments);
+ //void toBlob(FileCallback? _callback, optional DOMString type, any... arguments);
+};
diff --git a/components/script/dom/webidls/HTMLCollection.webidl b/components/script/dom/webidls/HTMLCollection.webidl
new file mode 100644
index 00000000000..26227c54c4a
--- /dev/null
+++ b/components/script/dom/webidls/HTMLCollection.webidl
@@ -0,0 +1,10 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+interface HTMLCollection {
+ readonly attribute unsigned long length;
+ getter Element? item(unsigned long index);
+ getter Element? namedItem(DOMString name);
+};
diff --git a/components/script/dom/webidls/HTMLDListElement.webidl b/components/script/dom/webidls/HTMLDListElement.webidl
new file mode 100644
index 00000000000..3c0a8b9c63d
--- /dev/null
+++ b/components/script/dom/webidls/HTMLDListElement.webidl
@@ -0,0 +1,14 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmldlistelement
+interface HTMLDListElement : HTMLElement {
+ // also has obsolete members
+};
+
+// http://www.whatwg.org/html/#HTMLDListElement-partial
+partial interface HTMLDListElement {
+ // attribute boolean compact;
+};
diff --git a/components/script/dom/webidls/HTMLDataElement.webidl b/components/script/dom/webidls/HTMLDataElement.webidl
new file mode 100644
index 00000000000..c6025423fb0
--- /dev/null
+++ b/components/script/dom/webidls/HTMLDataElement.webidl
@@ -0,0 +1,9 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmldataelement
+interface HTMLDataElement : HTMLElement {
+ // attribute DOMString value;
+};
diff --git a/components/script/dom/webidls/HTMLDataListElement.webidl b/components/script/dom/webidls/HTMLDataListElement.webidl
new file mode 100644
index 00000000000..ae7055bb48c
--- /dev/null
+++ b/components/script/dom/webidls/HTMLDataListElement.webidl
@@ -0,0 +1,9 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmldatalistelement
+interface HTMLDataListElement : HTMLElement {
+ readonly attribute HTMLCollection options;
+};
diff --git a/components/script/dom/webidls/HTMLDirectoryElement.webidl b/components/script/dom/webidls/HTMLDirectoryElement.webidl
new file mode 100644
index 00000000000..6015b4e8859
--- /dev/null
+++ b/components/script/dom/webidls/HTMLDirectoryElement.webidl
@@ -0,0 +1,9 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmldirectoryelement
+interface HTMLDirectoryElement : HTMLElement {
+ // attribute boolean compact;
+};
diff --git a/components/script/dom/webidls/HTMLDivElement.webidl b/components/script/dom/webidls/HTMLDivElement.webidl
new file mode 100644
index 00000000000..be451ce3e23
--- /dev/null
+++ b/components/script/dom/webidls/HTMLDivElement.webidl
@@ -0,0 +1,14 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmldivelement
+interface HTMLDivElement : HTMLElement {
+ // also has obsolete members
+};
+
+// http://www.whatwg.org/html/#HTMLDivElement-partial
+partial interface HTMLDivElement {
+ // attribute DOMString align;
+};
diff --git a/components/script/dom/webidls/HTMLElement.webidl b/components/script/dom/webidls/HTMLElement.webidl
new file mode 100644
index 00000000000..ebaa83a19bf
--- /dev/null
+++ b/components/script/dom/webidls/HTMLElement.webidl
@@ -0,0 +1,48 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmlelement
+interface HTMLElement : Element {
+ // metadata attributes
+ // attribute DOMString title;
+ // attribute DOMString lang;
+ // attribute boolean translate;
+ // attribute DOMString dir;
+ //readonly attribute DOMStringMap dataset;
+
+ // microdata
+ // attribute boolean itemScope;
+ //[PutForwards=value] readonly attribute DOMSettableTokenList itemType;
+ // attribute DOMString itemId;
+ //[PutForwards=value] readonly attribute DOMSettableTokenList itemRef;
+ //[PutForwards=value] readonly attribute DOMSettableTokenList itemProp;
+ //readonly attribute HTMLPropertiesCollection properties;
+ // attribute any itemValue; // acts as DOMString on setting
+
+ // user interaction
+ // attribute boolean hidden;
+ //void click();
+ // attribute long tabIndex;
+ //void focus();
+ //void blur();
+ // attribute DOMString accessKey;
+ //readonly attribute DOMString accessKeyLabel;
+ // attribute boolean draggable;
+ //[PutForwards=value] readonly attribute DOMSettableTokenList dropzone;
+ // attribute DOMString contentEditable;
+ //readonly attribute boolean isContentEditable;
+ // attribute HTMLMenuElement? contextMenu;
+ // attribute boolean spellcheck;
+ //void forceSpellCheck();
+
+ // command API
+ //readonly attribute DOMString? commandType;
+ //readonly attribute DOMString? commandLabel;
+ //readonly attribute DOMString? commandIcon;
+ //readonly attribute boolean? commandHidden;
+ //readonly attribute boolean? commandDisabled;
+ //readonly attribute boolean? commandChecked;
+};
+HTMLElement implements GlobalEventHandlers;
diff --git a/components/script/dom/webidls/HTMLEmbedElement.webidl b/components/script/dom/webidls/HTMLEmbedElement.webidl
new file mode 100644
index 00000000000..0b708113b55
--- /dev/null
+++ b/components/script/dom/webidls/HTMLEmbedElement.webidl
@@ -0,0 +1,21 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmlembedelement
+interface HTMLEmbedElement : HTMLElement {
+ // attribute DOMString src;
+ // attribute DOMString type;
+ // attribute DOMString width;
+ // attribute DOMString height;
+ //legacycaller any (any... arguments);
+
+ // also has obsolete members
+};
+
+// http://www.whatwg.org/html/#HTMLEmbedElement-partial
+partial interface HTMLEmbedElement {
+ // attribute DOMString align;
+ // attribute DOMString name;
+};
diff --git a/components/script/dom/webidls/HTMLFieldSetElement.webidl b/components/script/dom/webidls/HTMLFieldSetElement.webidl
new file mode 100644
index 00000000000..6b64c60bd21
--- /dev/null
+++ b/components/script/dom/webidls/HTMLFieldSetElement.webidl
@@ -0,0 +1,23 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmlfieldsetelement
+interface HTMLFieldSetElement : HTMLElement {
+ attribute boolean disabled;
+ //readonly attribute HTMLFormElement? form;
+ // attribute DOMString name;
+
+ //readonly attribute DOMString type;
+
+ //readonly attribute HTMLFormControlsCollection elements;
+ readonly attribute HTMLCollection elements;
+
+ //readonly attribute boolean willValidate;
+ readonly attribute ValidityState validity;
+ //readonly attribute DOMString validationMessage;
+ //boolean checkValidity();
+ //boolean reportValidity();
+ //void setCustomValidity(DOMString error);
+};
diff --git a/components/script/dom/webidls/HTMLFontElement.webidl b/components/script/dom/webidls/HTMLFontElement.webidl
new file mode 100644
index 00000000000..4bdcb766745
--- /dev/null
+++ b/components/script/dom/webidls/HTMLFontElement.webidl
@@ -0,0 +1,11 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmlfontelement
+interface HTMLFontElement : HTMLElement {
+ //[TreatNullAs=EmptyString] attribute DOMString color;
+ // attribute DOMString face;
+ // attribute DOMString size;
+};
diff --git a/components/script/dom/webidls/HTMLFormElement.webidl b/components/script/dom/webidls/HTMLFormElement.webidl
new file mode 100644
index 00000000000..ffa36bc4d6b
--- /dev/null
+++ b/components/script/dom/webidls/HTMLFormElement.webidl
@@ -0,0 +1,30 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmlformelement
+//[OverrideBuiltins]
+interface HTMLFormElement : HTMLElement {
+ // attribute DOMString acceptCharset;
+ // attribute DOMString action;
+ // attribute DOMString autocomplete;
+ // attribute DOMString enctype;
+ // attribute DOMString encoding;
+ // attribute DOMString method;
+ // attribute DOMString name;
+ // attribute boolean noValidate;
+ // attribute DOMString target;
+
+ //readonly attribute HTMLFormControlsCollection elements;
+ //readonly attribute long length;
+ //getter Element (unsigned long index);
+ //getter (RadioNodeList or Element) (DOMString name);
+
+ //void submit();
+ //void reset();
+ //boolean checkValidity();
+ //boolean reportValidity();
+
+ //void requestAutocomplete();
+};
diff --git a/components/script/dom/webidls/HTMLFrameElement.webidl b/components/script/dom/webidls/HTMLFrameElement.webidl
new file mode 100644
index 00000000000..effa8d13e99
--- /dev/null
+++ b/components/script/dom/webidls/HTMLFrameElement.webidl
@@ -0,0 +1,19 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmlframeelement
+interface HTMLFrameElement : HTMLElement {
+ // attribute DOMString name;
+ // attribute DOMString scrolling;
+ // attribute DOMString src;
+ // attribute DOMString frameBorder;
+ // attribute DOMString longDesc;
+ // attribute boolean noResize;
+ //readonly attribute Document? contentDocument;
+ //readonly attribute WindowProxy? contentWindow;
+
+ //[TreatNullAs=EmptyString] attribute DOMString marginHeight;
+ //[TreatNullAs=EmptyString] attribute DOMString marginWidth;
+};
diff --git a/components/script/dom/webidls/HTMLFrameSetElement.webidl b/components/script/dom/webidls/HTMLFrameSetElement.webidl
new file mode 100644
index 00000000000..50245baa049
--- /dev/null
+++ b/components/script/dom/webidls/HTMLFrameSetElement.webidl
@@ -0,0 +1,11 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmlframesetelement
+interface HTMLFrameSetElement : HTMLElement {
+ // attribute DOMString cols;
+ // attribute DOMString rows;
+};
+//HTMLFrameSetElement implements WindowEventHandlers;
diff --git a/components/script/dom/webidls/HTMLHRElement.webidl b/components/script/dom/webidls/HTMLHRElement.webidl
new file mode 100644
index 00000000000..482e1bca516
--- /dev/null
+++ b/components/script/dom/webidls/HTMLHRElement.webidl
@@ -0,0 +1,18 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmlhrelement
+interface HTMLHRElement : HTMLElement {
+ // also has obsolete members
+};
+
+// http://www.whatwg.org/html/#HTMLHRElement-partial
+partial interface HTMLHRElement {
+ // attribute DOMString align;
+ // attribute DOMString color;
+ // attribute boolean noShade;
+ // attribute DOMString size;
+ // attribute DOMString width;
+};
diff --git a/components/script/dom/webidls/HTMLHeadElement.webidl b/components/script/dom/webidls/HTMLHeadElement.webidl
new file mode 100644
index 00000000000..b7a53d2052b
--- /dev/null
+++ b/components/script/dom/webidls/HTMLHeadElement.webidl
@@ -0,0 +1,7 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmlheadelement
+interface HTMLHeadElement : HTMLElement {};
diff --git a/components/script/dom/webidls/HTMLHeadingElement.webidl b/components/script/dom/webidls/HTMLHeadingElement.webidl
new file mode 100644
index 00000000000..21a6060c335
--- /dev/null
+++ b/components/script/dom/webidls/HTMLHeadingElement.webidl
@@ -0,0 +1,14 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmlheadingelement
+interface HTMLHeadingElement : HTMLElement {
+ // also has obsolete members
+};
+
+// http://www.whatwg.org/html/#HTMLHeadingElement-partial
+partial interface HTMLHeadingElement {
+ // attribute DOMString align;
+};
diff --git a/components/script/dom/webidls/HTMLHtmlElement.webidl b/components/script/dom/webidls/HTMLHtmlElement.webidl
new file mode 100644
index 00000000000..f48fc6dafac
--- /dev/null
+++ b/components/script/dom/webidls/HTMLHtmlElement.webidl
@@ -0,0 +1,14 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmlhtmlelement
+interface HTMLHtmlElement : HTMLElement {
+ // also has obsolete members
+};
+
+// http://www.whatwg.org/html/#HTMLHtmlElement-partial
+partial interface HTMLHtmlElement {
+ // attribute DOMString version;
+};
diff --git a/components/script/dom/webidls/HTMLIFrameElement.webidl b/components/script/dom/webidls/HTMLIFrameElement.webidl
new file mode 100644
index 00000000000..201f8700ce4
--- /dev/null
+++ b/components/script/dom/webidls/HTMLIFrameElement.webidl
@@ -0,0 +1,33 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmliframeelement
+interface HTMLIFrameElement : HTMLElement {
+ attribute DOMString src;
+ // attribute DOMString srcdoc;
+ // attribute DOMString name;
+ //[PutForwards=value] readonly attribute DOMSettableTokenList sandbox;
+ attribute DOMString sandbox;
+ // attribute boolean seamless;
+ // attribute boolean allowFullscreen;
+ // attribute DOMString width;
+ // attribute DOMString height;
+ //readonly attribute Document? contentDocument;
+ //readonly attribute WindowProxy? contentWindow;
+ readonly attribute Window? contentWindow;
+
+ // also has obsolete members
+};
+
+// http://www.whatwg.org/html/#HTMLIFrameElement-partial
+partial interface HTMLIFrameElement {
+ // attribute DOMString align;
+ // attribute DOMString scrolling;
+ // attribute DOMString frameBorder;
+ // attribute DOMString longDesc;
+
+ //[TreatNullAs=EmptyString] attribute DOMString marginHeight;
+ //[TreatNullAs=EmptyString] attribute DOMString marginWidth;
+};
diff --git a/components/script/dom/webidls/HTMLImageElement.webidl b/components/script/dom/webidls/HTMLImageElement.webidl
new file mode 100644
index 00000000000..08e71ff8ee7
--- /dev/null
+++ b/components/script/dom/webidls/HTMLImageElement.webidl
@@ -0,0 +1,34 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmlimageelement
+//[NamedConstructor=Image(optional unsigned long width, optional unsigned long height)]
+interface HTMLImageElement : HTMLElement {
+ attribute DOMString alt;
+ attribute DOMString src;
+ // attribute DOMString srcset;
+ // attribute DOMString crossOrigin;
+ attribute DOMString useMap;
+ attribute boolean isMap;
+ attribute unsigned long width;
+ attribute unsigned long height;
+ //readonly attribute unsigned long naturalWidth;
+ //readonly attribute unsigned long naturalHeight;
+ //readonly attribute boolean complete;
+
+ // also has obsolete members
+};
+
+// http://www.whatwg.org/html/#HTMLImageElement-partial
+partial interface HTMLImageElement {
+ attribute DOMString name;
+ // attribute DOMString lowsrc;
+ attribute DOMString align;
+ attribute unsigned long hspace;
+ attribute unsigned long vspace;
+ attribute DOMString longDesc;
+
+ [TreatNullAs=EmptyString] attribute DOMString border;
+};
diff --git a/components/script/dom/webidls/HTMLInputElement.webidl b/components/script/dom/webidls/HTMLInputElement.webidl
new file mode 100644
index 00000000000..1caa9137e0b
--- /dev/null
+++ b/components/script/dom/webidls/HTMLInputElement.webidl
@@ -0,0 +1,76 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmlinputelement
+interface HTMLInputElement : HTMLElement {
+ // attribute DOMString accept;
+ // attribute DOMString alt;
+ // attribute DOMString autocomplete;
+ // attribute boolean autofocus;
+ // attribute boolean defaultChecked;
+ // attribute boolean checked;
+ // attribute DOMString dirName;
+ attribute boolean disabled;
+ //readonly attribute HTMLFormElement? form;
+ //readonly attribute FileList? files;
+ // attribute DOMString formAction;
+ // attribute DOMString formEnctype;
+ // attribute DOMString formMethod;
+ // attribute boolean formNoValidate;
+ // attribute DOMString formTarget;
+ // attribute unsigned long height;
+ // attribute boolean indeterminate;
+ // attribute DOMString inputMode;
+ //readonly attribute HTMLElement? list;
+ // attribute DOMString max;
+ // attribute long maxLength;
+ // attribute DOMString min;
+ // attribute long minLength;
+ // attribute boolean multiple;
+ // attribute DOMString name;
+ // attribute DOMString pattern;
+ // attribute DOMString placeholder;
+ // attribute boolean readOnly;
+ // attribute boolean required;
+ // attribute unsigned long size;
+ // attribute DOMString src;
+ // attribute DOMString step;
+ // attribute DOMString type;
+ // attribute DOMString defaultValue;
+ //[TreatNullAs=EmptyString] attribute DOMString value;
+ // attribute Date? valueAsDate;
+ // attribute unrestricted double valueAsNumber;
+ // attribute double valueLow;
+ // attribute double valueHigh;
+ // attribute unsigned long width;
+
+ //void stepUp(optional long n = 1);
+ //void stepDown(optional long n = 1);
+
+ //readonly attribute boolean willValidate;
+ //readonly attribute ValidityState validity;
+ //readonly attribute DOMString validationMessage;
+ //boolean checkValidity();
+ //boolean reportValidity();
+ //void setCustomValidity(DOMString error);
+
+ //readonly attribute NodeList labels;
+
+ //void select();
+ // attribute unsigned long selectionStart;
+ // attribute unsigned long selectionEnd;
+ // attribute DOMString selectionDirection;
+ //void setRangeText(DOMString replacement);
+ //void setRangeText(DOMString replacement, unsigned long start, unsigned long end, optional SelectionMode selectionMode = "preserve");
+ //void setSelectionRange(unsigned long start, unsigned long end, optional DOMString direction);
+
+ // also has obsolete members
+};
+
+// http://www.whatwg.org/html/#HTMLInputElement-partial
+partial interface HTMLInputElement {
+ // attribute DOMString align;
+ // attribute DOMString useMap;
+};
diff --git a/components/script/dom/webidls/HTMLLIElement.webidl b/components/script/dom/webidls/HTMLLIElement.webidl
new file mode 100644
index 00000000000..87d8b78b175
--- /dev/null
+++ b/components/script/dom/webidls/HTMLLIElement.webidl
@@ -0,0 +1,16 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmllielement
+interface HTMLLIElement : HTMLElement {
+ // attribute long value;
+
+ // also has obsolete members
+};
+
+// http://www.whatwg.org/html/#HTMLLIElement-partial
+partial interface HTMLLIElement {
+ // attribute DOMString type;
+};
diff --git a/components/script/dom/webidls/HTMLLabelElement.webidl b/components/script/dom/webidls/HTMLLabelElement.webidl
new file mode 100644
index 00000000000..c3ff7fb50cd
--- /dev/null
+++ b/components/script/dom/webidls/HTMLLabelElement.webidl
@@ -0,0 +1,11 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmllabelelement
+interface HTMLLabelElement : HTMLElement {
+ //readonly attribute HTMLFormElement? form;
+ // attribute DOMString htmlFor;
+ //readonly attribute HTMLElement? control;
+};
diff --git a/components/script/dom/webidls/HTMLLegendElement.webidl b/components/script/dom/webidls/HTMLLegendElement.webidl
new file mode 100644
index 00000000000..3622cd27672
--- /dev/null
+++ b/components/script/dom/webidls/HTMLLegendElement.webidl
@@ -0,0 +1,16 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmllegendelement
+interface HTMLLegendElement : HTMLElement {
+ //readonly attribute HTMLFormElement? form;
+
+ // also has obsolete members
+};
+
+// http://www.whatwg.org/html/#HTMLLegendElement-partial
+partial interface HTMLLegendElement {
+ // attribute DOMString align;
+};
diff --git a/components/script/dom/webidls/HTMLLinkElement.webidl b/components/script/dom/webidls/HTMLLinkElement.webidl
new file mode 100644
index 00000000000..3757bada2b3
--- /dev/null
+++ b/components/script/dom/webidls/HTMLLinkElement.webidl
@@ -0,0 +1,26 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmllinkelement
+interface HTMLLinkElement : HTMLElement {
+ // attribute DOMString href;
+ // attribute DOMString crossOrigin;
+ // attribute DOMString rel;
+ //readonly attribute DOMTokenList relList;
+ // attribute DOMString media;
+ // attribute DOMString hreflang;
+ // attribute DOMString type;
+ //[PutForwards=value] readonly attribute DOMSettableTokenList sizes;
+
+ // also has obsolete members
+};
+//HTMLLinkElement implements LinkStyle;
+
+// http://www.whatwg.org/html/#HTMLLinkElement-partial
+partial interface HTMLLinkElement {
+ // attribute DOMString charset;
+ // attribute DOMString rev;
+ // attribute DOMString target;
+};
diff --git a/components/script/dom/webidls/HTMLMapElement.webidl b/components/script/dom/webidls/HTMLMapElement.webidl
new file mode 100644
index 00000000000..c5eb1cea3e4
--- /dev/null
+++ b/components/script/dom/webidls/HTMLMapElement.webidl
@@ -0,0 +1,11 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmlmapelement
+interface HTMLMapElement : HTMLElement {
+ // attribute DOMString name;
+ //readonly attribute HTMLCollection areas;
+ //readonly attribute HTMLCollection images;
+};
diff --git a/components/script/dom/webidls/HTMLMediaElement.webidl b/components/script/dom/webidls/HTMLMediaElement.webidl
new file mode 100644
index 00000000000..53f5770f54f
--- /dev/null
+++ b/components/script/dom/webidls/HTMLMediaElement.webidl
@@ -0,0 +1,67 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmlmediaelement
+//enum CanPlayTypeResult { "" /* empty string */, "maybe", "probably" };
+interface HTMLMediaElement : HTMLElement {
+
+ // error state
+ //readonly attribute MediaError? error;
+
+ // network state
+ // attribute DOMString src;
+ //readonly attribute DOMString currentSrc;
+ // attribute DOMString crossOrigin;
+ //const unsigned short NETWORK_EMPTY = 0;
+ //const unsigned short NETWORK_IDLE = 1;
+ //const unsigned short NETWORK_LOADING = 2;
+ //const unsigned short NETWORK_NO_SOURCE = 3;
+ //readonly attribute unsigned short networkState;
+ // attribute DOMString preload;
+ //readonly attribute TimeRanges buffered;
+ //void load();
+ //CanPlayTypeResult canPlayType(DOMString type);
+
+ // ready state
+ //const unsigned short HAVE_NOTHING = 0;
+ //const unsigned short HAVE_METADATA = 1;
+ //const unsigned short HAVE_CURRENT_DATA = 2;
+ //const unsigned short HAVE_FUTURE_DATA = 3;
+ //const unsigned short HAVE_ENOUGH_DATA = 4;
+ //readonly attribute unsigned short readyState;
+ //readonly attribute boolean seeking;
+
+ // playback state
+ // attribute double currentTime;
+ //void fastSeek(double time);
+ //readonly attribute unrestricted double duration;
+ //Date getStartDate();
+ //readonly attribute boolean paused;
+ // attribute double defaultPlaybackRate;
+ // attribute double playbackRate;
+ //readonly attribute TimeRanges played;
+ //readonly attribute TimeRanges seekable;
+ //readonly attribute boolean ended;
+ // attribute boolean autoplay;
+ // attribute boolean loop;
+ //void play();
+ //void pause();
+
+ // media controller
+ // attribute DOMString mediaGroup;
+ // attribute MediaController? controller;
+
+ // controls
+ // attribute boolean controls;
+ // attribute double volume;
+ // attribute boolean muted;
+ // attribute boolean defaultMuted;
+
+ // tracks
+ //readonly attribute AudioTrackList audioTracks;
+ //readonly attribute VideoTrackList videoTracks;
+ //readonly attribute TextTrackList textTracks;
+ //TextTrack addTextTrack(TextTrackKind kind, optional DOMString label = "", optional DOMString language = "");
+};
diff --git a/components/script/dom/webidls/HTMLMetaElement.webidl b/components/script/dom/webidls/HTMLMetaElement.webidl
new file mode 100644
index 00000000000..97f89b35576
--- /dev/null
+++ b/components/script/dom/webidls/HTMLMetaElement.webidl
@@ -0,0 +1,18 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmlmetaelement
+interface HTMLMetaElement : HTMLElement {
+ // attribute DOMString name;
+ // attribute DOMString httpEquiv;
+ // attribute DOMString content;
+
+ // also has obsolete members
+};
+
+// http://www.whatwg.org/html/#HTMLMetaElement-partial
+partial interface HTMLMetaElement {
+ // attribute DOMString scheme;
+};
diff --git a/components/script/dom/webidls/HTMLMeterElement.webidl b/components/script/dom/webidls/HTMLMeterElement.webidl
new file mode 100644
index 00000000000..96c40ba6114
--- /dev/null
+++ b/components/script/dom/webidls/HTMLMeterElement.webidl
@@ -0,0 +1,15 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmlmeterelement
+interface HTMLMeterElement : HTMLElement {
+ // attribute double value;
+ // attribute double min;
+ // attribute double max;
+ // attribute double low;
+ // attribute double high;
+ // attribute double optimum;
+ //readonly attribute NodeList labels;
+};
diff --git a/components/script/dom/webidls/HTMLModElement.webidl b/components/script/dom/webidls/HTMLModElement.webidl
new file mode 100644
index 00000000000..3f8f0e62638
--- /dev/null
+++ b/components/script/dom/webidls/HTMLModElement.webidl
@@ -0,0 +1,10 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmlmodelement
+interface HTMLModElement : HTMLElement {
+ // attribute DOMString cite;
+ // attribute DOMString dateTime;
+};
diff --git a/components/script/dom/webidls/HTMLOListElement.webidl b/components/script/dom/webidls/HTMLOListElement.webidl
new file mode 100644
index 00000000000..9f9f654acc5
--- /dev/null
+++ b/components/script/dom/webidls/HTMLOListElement.webidl
@@ -0,0 +1,18 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmlolistelement
+interface HTMLOListElement : HTMLElement {
+ // attribute boolean reversed;
+ // attribute long start;
+ // attribute DOMString type;
+
+ // also has obsolete members
+};
+
+// http://www.whatwg.org/html/#HTMLOListElement-partial
+partial interface HTMLOListElement {
+ // attribute boolean compact;
+};
diff --git a/components/script/dom/webidls/HTMLObjectElement.webidl b/components/script/dom/webidls/HTMLObjectElement.webidl
new file mode 100644
index 00000000000..56fc290e546
--- /dev/null
+++ b/components/script/dom/webidls/HTMLObjectElement.webidl
@@ -0,0 +1,44 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmlobjectelement
+interface HTMLObjectElement : HTMLElement {
+ // attribute DOMString data;
+ // attribute DOMString type;
+ // attribute boolean typeMustMatch;
+ // attribute DOMString name;
+ // attribute DOMString useMap;
+ //readonly attribute HTMLFormElement? form;
+ // attribute DOMString width;
+ // attribute DOMString height;
+ //readonly attribute Document? contentDocument;
+ //readonly attribute WindowProxy? contentWindow;
+
+ //readonly attribute boolean willValidate;
+ readonly attribute ValidityState validity;
+ //readonly attribute DOMString validationMessage;
+ //boolean checkValidity();
+ //boolean reportValidity();
+ //void setCustomValidity(DOMString error);
+
+ //legacycaller any (any... arguments);
+
+ // also has obsolete members
+};
+
+// http://www.whatwg.org/html/#HTMLObjectElement-partial
+partial interface HTMLObjectElement {
+ // attribute DOMString align;
+ // attribute DOMString archive;
+ // attribute DOMString code;
+ // attribute boolean declare;
+ // attribute unsigned long hspace;
+ // attribute DOMString standby;
+ // attribute unsigned long vspace;
+ // attribute DOMString codeBase;
+ // attribute DOMString codeType;
+
+ //[TreatNullAs=EmptyString] attribute DOMString border;
+};
diff --git a/components/script/dom/webidls/HTMLOptGroupElement.webidl b/components/script/dom/webidls/HTMLOptGroupElement.webidl
new file mode 100644
index 00000000000..13646f00ab1
--- /dev/null
+++ b/components/script/dom/webidls/HTMLOptGroupElement.webidl
@@ -0,0 +1,10 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmloptgroupelement
+interface HTMLOptGroupElement : HTMLElement {
+ attribute boolean disabled;
+ // attribute DOMString label;
+};
diff --git a/components/script/dom/webidls/HTMLOptionElement.webidl b/components/script/dom/webidls/HTMLOptionElement.webidl
new file mode 100644
index 00000000000..7855449c6f4
--- /dev/null
+++ b/components/script/dom/webidls/HTMLOptionElement.webidl
@@ -0,0 +1,18 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmloptionelement
+//[NamedConstructor=Option(optional DOMString text = "", optional DOMString value, optional boolean defaultSelected = false, optional boolean selected = false)]
+interface HTMLOptionElement : HTMLElement {
+ attribute boolean disabled;
+ //readonly attribute HTMLFormElement? form;
+ // attribute DOMString label;
+ // attribute boolean defaultSelected;
+ // attribute boolean selected;
+ // attribute DOMString value;
+
+ // attribute DOMString text;
+ //readonly attribute long index;
+};
diff --git a/components/script/dom/webidls/HTMLOutputElement.webidl b/components/script/dom/webidls/HTMLOutputElement.webidl
new file mode 100644
index 00000000000..d0d23d87d42
--- /dev/null
+++ b/components/script/dom/webidls/HTMLOutputElement.webidl
@@ -0,0 +1,24 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmloutputelement
+interface HTMLOutputElement : HTMLElement {
+ //[PutForwards=value] readonly attribute DOMSettableTokenList htmlFor;
+ //readonly attribute HTMLFormElement? form;
+ // attribute DOMString name;
+
+ //readonly attribute DOMString type;
+ // attribute DOMString defaultValue;
+ // attribute DOMString value;
+
+ //readonly attribute boolean willValidate;
+ readonly attribute ValidityState validity;
+ //readonly attribute DOMString validationMessage;
+ //boolean checkValidity();
+ //boolean reportValidity();
+ //void setCustomValidity(DOMString error);
+
+ //readonly attribute NodeList labels;
+};
diff --git a/components/script/dom/webidls/HTMLParagraphElement.webidl b/components/script/dom/webidls/HTMLParagraphElement.webidl
new file mode 100644
index 00000000000..86cae7cbe87
--- /dev/null
+++ b/components/script/dom/webidls/HTMLParagraphElement.webidl
@@ -0,0 +1,14 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmlparagraphelement
+interface HTMLParagraphElement : HTMLElement {
+ // also has obsolete members
+};
+
+// http://www.whatwg.org/html/#HTMLParagraphElement-partial
+partial interface HTMLParagraphElement {
+ // attribute DOMString align;
+};
diff --git a/components/script/dom/webidls/HTMLParamElement.webidl b/components/script/dom/webidls/HTMLParamElement.webidl
new file mode 100644
index 00000000000..afcb6ec1d9b
--- /dev/null
+++ b/components/script/dom/webidls/HTMLParamElement.webidl
@@ -0,0 +1,18 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmlparamelement
+interface HTMLParamElement : HTMLElement {
+ // attribute DOMString name;
+ // attribute DOMString value;
+
+ // also has obsolete members
+};
+
+// http://www.whatwg.org/html/#HTMLParamElement-partial
+partial interface HTMLParamElement {
+ // attribute DOMString type;
+ // attribute DOMString valueType;
+};
diff --git a/components/script/dom/webidls/HTMLPreElement.webidl b/components/script/dom/webidls/HTMLPreElement.webidl
new file mode 100644
index 00000000000..f0498ebd32c
--- /dev/null
+++ b/components/script/dom/webidls/HTMLPreElement.webidl
@@ -0,0 +1,14 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmlpreelement
+interface HTMLPreElement : HTMLElement {
+ // also has obsolete members
+};
+
+// http://www.whatwg.org/html/#HTMLPreElement-partial
+partial interface HTMLPreElement {
+ // attribute long width;
+};
diff --git a/components/script/dom/webidls/HTMLProgressElement.webidl b/components/script/dom/webidls/HTMLProgressElement.webidl
new file mode 100644
index 00000000000..53a95297afb
--- /dev/null
+++ b/components/script/dom/webidls/HTMLProgressElement.webidl
@@ -0,0 +1,12 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmlprogresselement
+interface HTMLProgressElement : HTMLElement {
+ // attribute double value;
+ // attribute double max;
+ //readonly attribute double position;
+ //readonly attribute NodeList labels;
+};
diff --git a/components/script/dom/webidls/HTMLQuoteElement.webidl b/components/script/dom/webidls/HTMLQuoteElement.webidl
new file mode 100644
index 00000000000..a7b1ae41276
--- /dev/null
+++ b/components/script/dom/webidls/HTMLQuoteElement.webidl
@@ -0,0 +1,9 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmlquoteelement
+interface HTMLQuoteElement : HTMLElement {
+ // attribute DOMString cite;
+};
diff --git a/components/script/dom/webidls/HTMLScriptElement.webidl b/components/script/dom/webidls/HTMLScriptElement.webidl
new file mode 100644
index 00000000000..260850fa78c
--- /dev/null
+++ b/components/script/dom/webidls/HTMLScriptElement.webidl
@@ -0,0 +1,25 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmlscriptelement
+interface HTMLScriptElement : HTMLElement {
+ // attribute DOMString src;
+ readonly attribute DOMString src;
+ // attribute DOMString type;
+ // attribute DOMString charset;
+ // attribute boolean async;
+ // attribute boolean defer;
+ // attribute DOMString crossOrigin;
+ [Pure]
+ attribute DOMString text;
+
+ // also has obsolete members
+};
+
+// http://www.whatwg.org/html/#HTMLScriptElement-partial
+partial interface HTMLScriptElement {
+ // attribute DOMString event;
+ // attribute DOMString htmlFor;
+};
diff --git a/components/script/dom/webidls/HTMLSelectElement.webidl b/components/script/dom/webidls/HTMLSelectElement.webidl
new file mode 100644
index 00000000000..91d4c3b0917
--- /dev/null
+++ b/components/script/dom/webidls/HTMLSelectElement.webidl
@@ -0,0 +1,40 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmlselectelement
+interface HTMLSelectElement : HTMLElement {
+ // attribute boolean autofocus;
+ attribute boolean disabled;
+ //readonly attribute HTMLFormElement? form;
+ // attribute boolean multiple;
+ // attribute DOMString name;
+ // attribute boolean required;
+ // attribute unsigned long size;
+
+ //readonly attribute DOMString type;
+
+ //readonly attribute HTMLOptionsCollection options;
+ // attribute unsigned long length;
+ //getter Element? item(unsigned long index);
+ //HTMLOptionElement? namedItem(DOMString name);
+ // Note: this function currently only exists for test_union.html.
+ void add((HTMLOptionElement or HTMLOptGroupElement) element, optional (HTMLElement or long)? before = null);
+ //void remove(); // ChildNode overload
+ //void remove(long index);
+ //setter creator void (unsigned long index, HTMLOptionElement? option);
+
+ //readonly attribute HTMLCollection selectedOptions;
+ // attribute long selectedIndex;
+ // attribute DOMString value;
+
+ //readonly attribute boolean willValidate;
+ readonly attribute ValidityState validity;
+ //readonly attribute DOMString validationMessage;
+ //boolean checkValidity();
+ //boolean reportValidity();
+ //void setCustomValidity(DOMString error);
+
+ //readonly attribute NodeList labels;
+};
diff --git a/components/script/dom/webidls/HTMLSourceElement.webidl b/components/script/dom/webidls/HTMLSourceElement.webidl
new file mode 100644
index 00000000000..6739f1cd0c1
--- /dev/null
+++ b/components/script/dom/webidls/HTMLSourceElement.webidl
@@ -0,0 +1,10 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmlsourceelement
+interface HTMLSourceElement : HTMLElement {
+ // attribute DOMString src;
+ // attribute DOMString type;
+};
diff --git a/components/script/dom/webidls/HTMLSpanElement.webidl b/components/script/dom/webidls/HTMLSpanElement.webidl
new file mode 100644
index 00000000000..ab7ac3edc85
--- /dev/null
+++ b/components/script/dom/webidls/HTMLSpanElement.webidl
@@ -0,0 +1,7 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmlspanelement
+interface HTMLSpanElement : HTMLElement {};
diff --git a/components/script/dom/webidls/HTMLStyleElement.webidl b/components/script/dom/webidls/HTMLStyleElement.webidl
new file mode 100644
index 00000000000..0dd71a58769
--- /dev/null
+++ b/components/script/dom/webidls/HTMLStyleElement.webidl
@@ -0,0 +1,12 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmlstyleelement
+interface HTMLStyleElement : HTMLElement {
+ // attribute DOMString media;
+ // attribute DOMString type;
+ // attribute boolean scoped;
+};
+//HTMLStyleElement implements LinkStyle;
diff --git a/components/script/dom/webidls/HTMLTableCaptionElement.webidl b/components/script/dom/webidls/HTMLTableCaptionElement.webidl
new file mode 100644
index 00000000000..7ab036c8eb1
--- /dev/null
+++ b/components/script/dom/webidls/HTMLTableCaptionElement.webidl
@@ -0,0 +1,14 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmltablecaptionelement
+interface HTMLTableCaptionElement : HTMLElement {
+ // also has obsolete members
+};
+
+// http://www.whatwg.org/html/#HTMLTableCaptionElement-partial
+partial interface HTMLTableCaptionElement {
+ // attribute DOMString align;
+};
diff --git a/components/script/dom/webidls/HTMLTableCellElement.webidl b/components/script/dom/webidls/HTMLTableCellElement.webidl
new file mode 100644
index 00000000000..131bf02b5bc
--- /dev/null
+++ b/components/script/dom/webidls/HTMLTableCellElement.webidl
@@ -0,0 +1,29 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmltablecellelement
+interface HTMLTableCellElement : HTMLElement {
+ // attribute unsigned long colSpan;
+ // attribute unsigned long rowSpan;
+ //[PutForwards=value] readonly attribute DOMSettableTokenList headers;
+ //readonly attribute long cellIndex;
+
+ // also has obsolete members
+};
+
+// http://www.whatwg.org/html/#HTMLTableCellElement-partial
+partial interface HTMLTableCellElement {
+ // attribute DOMString align;
+ // attribute DOMString axis;
+ // attribute DOMString height;
+ // attribute DOMString width;
+
+ // attribute DOMString ch;
+ // attribute DOMString chOff;
+ // attribute boolean noWrap;
+ // attribute DOMString vAlign;
+
+ //[TreatNullAs=EmptyString] attribute DOMString bgColor;
+};
diff --git a/components/script/dom/webidls/HTMLTableColElement.webidl b/components/script/dom/webidls/HTMLTableColElement.webidl
new file mode 100644
index 00000000000..5a7cfc4b5c4
--- /dev/null
+++ b/components/script/dom/webidls/HTMLTableColElement.webidl
@@ -0,0 +1,20 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmltablecolelement
+interface HTMLTableColElement : HTMLElement {
+ // attribute unsigned long span;
+
+ // also has obsolete members
+};
+
+// http://www.whatwg.org/html/#HTMLTableColElement-partial
+partial interface HTMLTableColElement {
+ // attribute DOMString align;
+ // attribute DOMString ch;
+ // attribute DOMString chOff;
+ // attribute DOMString vAlign;
+ // attribute DOMString width;
+};
diff --git a/components/script/dom/webidls/HTMLTableDataCellElement.webidl b/components/script/dom/webidls/HTMLTableDataCellElement.webidl
new file mode 100644
index 00000000000..62669ae026a
--- /dev/null
+++ b/components/script/dom/webidls/HTMLTableDataCellElement.webidl
@@ -0,0 +1,14 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmltabledatacellelement
+interface HTMLTableDataCellElement : HTMLTableCellElement {
+ // also has obsolete members
+};
+
+// http://www.whatwg.org/html/#HTMLTableDataCellElement-partial
+partial interface HTMLTableDataCellElement {
+ // attribute DOMString abbr;
+};
diff --git a/components/script/dom/webidls/HTMLTableElement.webidl b/components/script/dom/webidls/HTMLTableElement.webidl
new file mode 100644
index 00000000000..d71a38c12a5
--- /dev/null
+++ b/components/script/dom/webidls/HTMLTableElement.webidl
@@ -0,0 +1,40 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmltableelement
+interface HTMLTableElement : HTMLElement {
+ attribute HTMLTableCaptionElement? caption;
+ //HTMLElement createCaption();
+ //void deleteCaption();
+ // attribute HTMLTableSectionElement? tHead;
+ //HTMLElement createTHead();
+ //void deleteTHead();
+ // attribute HTMLTableSectionElement? tFoot;
+ //HTMLElement createTFoot();
+ //void deleteTFoot();
+ //readonly attribute HTMLCollection tBodies;
+ //HTMLElement createTBody();
+ //readonly attribute HTMLCollection rows;
+ //HTMLElement insertRow(optional long index = -1);
+ //void deleteRow(long index);
+ // attribute boolean sortable;
+ //void stopSorting();
+
+ // also has obsolete members
+};
+
+// http://www.whatwg.org/html/#HTMLTableElement-partial
+partial interface HTMLTableElement {
+ // attribute DOMString align;
+ // attribute DOMString border;
+ // attribute DOMString frame;
+ // attribute DOMString rules;
+ // attribute DOMString summary;
+ // attribute DOMString width;
+
+ //[TreatNullAs=EmptyString] attribute DOMString bgColor;
+ //[TreatNullAs=EmptyString] attribute DOMString cellPadding;
+ //[TreatNullAs=EmptyString] attribute DOMString cellSpacing;
+};
diff --git a/components/script/dom/webidls/HTMLTableHeaderCellElement.webidl b/components/script/dom/webidls/HTMLTableHeaderCellElement.webidl
new file mode 100644
index 00000000000..9bbf4d6f436
--- /dev/null
+++ b/components/script/dom/webidls/HTMLTableHeaderCellElement.webidl
@@ -0,0 +1,12 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmltableheadercellelement
+interface HTMLTableHeaderCellElement : HTMLTableCellElement {
+ // attribute DOMString scope;
+ // attribute DOMString abbr;
+ // attribute DOMString sorted;
+ //void sort();
+};
diff --git a/components/script/dom/webidls/HTMLTableRowElement.webidl b/components/script/dom/webidls/HTMLTableRowElement.webidl
new file mode 100644
index 00000000000..7cacb013936
--- /dev/null
+++ b/components/script/dom/webidls/HTMLTableRowElement.webidl
@@ -0,0 +1,25 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmltablerowelement
+interface HTMLTableRowElement : HTMLElement {
+ //readonly attribute long rowIndex;
+ //readonly attribute long sectionRowIndex;
+ //readonly attribute HTMLCollection cells;
+ //HTMLElement insertCell(optional long index = -1);
+ //void deleteCell(long index);
+
+ // also has obsolete members
+};
+
+// http://www.whatwg.org/html/#HTMLTableRowElement-partial
+partial interface HTMLTableRowElement {
+ // attribute DOMString align;
+ // attribute DOMString ch;
+ // attribute DOMString chOff;
+ // attribute DOMString vAlign;
+
+ //[TreatNullAs=EmptyString] attribute DOMString bgColor;
+};
diff --git a/components/script/dom/webidls/HTMLTableSectionElement.webidl b/components/script/dom/webidls/HTMLTableSectionElement.webidl
new file mode 100644
index 00000000000..c3909f3f3e0
--- /dev/null
+++ b/components/script/dom/webidls/HTMLTableSectionElement.webidl
@@ -0,0 +1,21 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmltablesectionelement
+interface HTMLTableSectionElement : HTMLElement {
+ //readonly attribute HTMLCollection rows;
+ //HTMLElement insertRow(optional long index = -1);
+ //void deleteRow(long index);
+
+ // also has obsolete members
+};
+
+// http://www.whatwg.org/html/#HTMLTableSectionElement-partial
+partial interface HTMLTableSectionElement {
+ // attribute DOMString align;
+ // attribute DOMString ch;
+ // attribute DOMString chOff;
+ // attribute DOMString vAlign;
+};
diff --git a/components/script/dom/webidls/HTMLTemplateElement.webidl b/components/script/dom/webidls/HTMLTemplateElement.webidl
new file mode 100644
index 00000000000..e148dfe2236
--- /dev/null
+++ b/components/script/dom/webidls/HTMLTemplateElement.webidl
@@ -0,0 +1,9 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmltemplateelement
+interface HTMLTemplateElement : HTMLElement {
+ //readonly attribute DocumentFragment content;
+};
diff --git a/components/script/dom/webidls/HTMLTextAreaElement.webidl b/components/script/dom/webidls/HTMLTextAreaElement.webidl
new file mode 100644
index 00000000000..534bb87a0e5
--- /dev/null
+++ b/components/script/dom/webidls/HTMLTextAreaElement.webidl
@@ -0,0 +1,45 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmltextareaelement
+interface HTMLTextAreaElement : HTMLElement {
+ // attribute DOMString autocomplete;
+ // attribute boolean autofocus;
+ // attribute unsigned long cols;
+ // attribute DOMString dirName;
+ attribute boolean disabled;
+ //readonly attribute HTMLFormElement? form;
+ // attribute DOMString inputMode;
+ // attribute long maxLength;
+ // attribute long minLength;
+ // attribute DOMString name;
+ // attribute DOMString placeholder;
+ // attribute boolean readOnly;
+ // attribute boolean required;
+ // attribute unsigned long rows;
+ // attribute DOMString wrap;
+
+ //readonly attribute DOMString type;
+ // attribute DOMString defaultValue;
+ //[TreatNullAs=EmptyString] attribute DOMString value;
+ //readonly attribute unsigned long textLength;
+
+ //readonly attribute boolean willValidate;
+ //readonly attribute ValidityState validity;
+ //readonly attribute DOMString validationMessage;
+ //boolean checkValidity();
+ //boolean reportValidity();
+ //void setCustomValidity(DOMString error);
+
+ //readonly attribute NodeList labels;
+
+ //void select();
+ // attribute unsigned long selectionStart;
+ // attribute unsigned long selectionEnd;
+ // attribute DOMString selectionDirection;
+ //void setRangeText(DOMString replacement);
+ //void setRangeText(DOMString replacement, unsigned long start, unsigned long end, optional SelectionMode selectionMode = "preserve");
+ //void setSelectionRange(unsigned long start, unsigned long end, optional DOMString direction);
+};
diff --git a/components/script/dom/webidls/HTMLTimeElement.webidl b/components/script/dom/webidls/HTMLTimeElement.webidl
new file mode 100644
index 00000000000..20ab9b04556
--- /dev/null
+++ b/components/script/dom/webidls/HTMLTimeElement.webidl
@@ -0,0 +1,9 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmltimeelement
+interface HTMLTimeElement : HTMLElement {
+ // attribute DOMString dateTime;
+};
diff --git a/components/script/dom/webidls/HTMLTitleElement.webidl b/components/script/dom/webidls/HTMLTitleElement.webidl
new file mode 100644
index 00000000000..789fba3cf17
--- /dev/null
+++ b/components/script/dom/webidls/HTMLTitleElement.webidl
@@ -0,0 +1,10 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmltitleelement
+interface HTMLTitleElement : HTMLElement {
+ [Pure]
+ attribute DOMString text;
+};
diff --git a/components/script/dom/webidls/HTMLTrackElement.webidl b/components/script/dom/webidls/HTMLTrackElement.webidl
new file mode 100644
index 00000000000..bab698709ab
--- /dev/null
+++ b/components/script/dom/webidls/HTMLTrackElement.webidl
@@ -0,0 +1,21 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmltrackelement
+interface HTMLTrackElement : HTMLElement {
+ // attribute DOMString kind;
+ // attribute DOMString src;
+ // attribute DOMString srclang;
+ // attribute DOMString label;
+ // attribute boolean default;
+
+ //const unsigned short NONE = 0;
+ //const unsigned short LOADING = 1;
+ //const unsigned short LOADED = 2;
+ //const unsigned short ERROR = 3;
+ //readonly attribute unsigned short readyState;
+
+ //readonly attribute TextTrack track;
+};
diff --git a/components/script/dom/webidls/HTMLUListElement.webidl b/components/script/dom/webidls/HTMLUListElement.webidl
new file mode 100644
index 00000000000..10c6451d4c7
--- /dev/null
+++ b/components/script/dom/webidls/HTMLUListElement.webidl
@@ -0,0 +1,15 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmlulistelement
+interface HTMLUListElement : HTMLElement {
+ // also has obsolete members
+};
+
+// http://www.whatwg.org/html/#HTMLUListElement-partial
+partial interface HTMLUListElement {
+ // attribute boolean compact;
+ // attribute DOMString type;
+};
diff --git a/components/script/dom/webidls/HTMLUnknownElement.webidl b/components/script/dom/webidls/HTMLUnknownElement.webidl
new file mode 100644
index 00000000000..db1307ae714
--- /dev/null
+++ b/components/script/dom/webidls/HTMLUnknownElement.webidl
@@ -0,0 +1,16 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * http://www.whatwg.org/specs/web-apps/current-work/ and
+ * http://dev.w3.org/csswg/cssom-view/
+ *
+ * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
+ * Opera Software ASA. You are granted a license to use, reproduce
+ * and create derivative works of this document.
+ */
+
+interface HTMLUnknownElement : HTMLElement {
+};
diff --git a/components/script/dom/webidls/HTMLVideoElement.webidl b/components/script/dom/webidls/HTMLVideoElement.webidl
new file mode 100644
index 00000000000..9d5d02cc530
--- /dev/null
+++ b/components/script/dom/webidls/HTMLVideoElement.webidl
@@ -0,0 +1,13 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#htmlvideoelement
+interface HTMLVideoElement : HTMLMediaElement {
+ // attribute unsigned long width;
+ // attribute unsigned long height;
+ //readonly attribute unsigned long videoWidth;
+ //readonly attribute unsigned long videoHeight;
+ // attribute DOMString poster;
+};
diff --git a/components/script/dom/webidls/Location.webidl b/components/script/dom/webidls/Location.webidl
new file mode 100644
index 00000000000..99076988122
--- /dev/null
+++ b/components/script/dom/webidls/Location.webidl
@@ -0,0 +1,12 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#location
+/*[Unforgeable]*/ interface Location {
+ //void assign(DOMString url);
+ //void replace(DOMString url);
+ //void reload();
+};
+Location implements URLUtils;
diff --git a/components/script/dom/webidls/MessageEvent.webidl b/components/script/dom/webidls/MessageEvent.webidl
new file mode 100644
index 00000000000..7198708499e
--- /dev/null
+++ b/components/script/dom/webidls/MessageEvent.webidl
@@ -0,0 +1,23 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#messageevent
+[Constructor(DOMString type, optional MessageEventInit eventInitDict)/*, Exposed=Window,Worker*/]
+interface MessageEvent : Event {
+ readonly attribute any data;
+ readonly attribute DOMString origin;
+ readonly attribute DOMString lastEventId;
+ //readonly attribute (WindowProxy or MessagePort)? source;
+ //readonly attribute MessagePort[]? ports;
+};
+
+dictionary MessageEventInit : EventInit {
+ any data = null;
+ DOMString origin = "";
+ DOMString lastEventId = "";
+ //DOMString channel;
+ //(WindowProxy or MessagePort)? source;
+ //sequence<MessagePort> ports;
+};
diff --git a/components/script/dom/webidls/MouseEvent.webidl b/components/script/dom/webidls/MouseEvent.webidl
new file mode 100644
index 00000000000..cdef58228c1
--- /dev/null
+++ b/components/script/dom/webidls/MouseEvent.webidl
@@ -0,0 +1,43 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#idl-def-MouseEvent
+[Constructor(DOMString typeArg, optional MouseEventInit mouseEventInitDict)]
+interface MouseEvent : UIEvent {
+ readonly attribute long screenX;
+ readonly attribute long screenY;
+ readonly attribute long clientX;
+ readonly attribute long clientY;
+ readonly attribute boolean ctrlKey;
+ readonly attribute boolean shiftKey;
+ readonly attribute boolean altKey;
+ readonly attribute boolean metaKey;
+ readonly attribute short button;
+ readonly attribute EventTarget? relatedTarget;
+ // Introduced in DOM Level 3
+ //readonly attribute unsigned short buttons;
+ //boolean getModifierState (DOMString keyArg);
+};
+
+// https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#idl-def-MouseEventInit
+dictionary MouseEventInit : UIEventInit {
+ long screenX = 0;
+ long screenY = 0;
+ long clientX = 0;
+ long clientY = 0;
+ boolean ctrlKey = false;
+ boolean shiftKey = false;
+ boolean altKey = false;
+ boolean metaKey = false;
+ short button = 0;
+ //unsigned short buttons = 0;
+ EventTarget? relatedTarget = null;
+};
+
+// https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#idl-def-MouseEvent-1
+partial interface MouseEvent {
+ // Deprecated in DOM Level 3
+ void initMouseEvent (DOMString typeArg, boolean bubblesArg, boolean cancelableArg, Window? viewArg, long detailArg, long screenXArg, long screenYArg, long clientXArg, long clientYArg, boolean ctrlKeyArg, boolean altKeyArg, boolean shiftKeyArg, boolean metaKeyArg, short buttonArg, EventTarget? relatedTargetArg);
+};
diff --git a/components/script/dom/webidls/NamedNodeMap.webidl b/components/script/dom/webidls/NamedNodeMap.webidl
new file mode 100644
index 00000000000..636c4a2782f
--- /dev/null
+++ b/components/script/dom/webidls/NamedNodeMap.webidl
@@ -0,0 +1,8 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+interface NamedNodeMap {
+ readonly attribute unsigned long length;
+ getter Attr? item(unsigned long index);
+};
diff --git a/components/script/dom/webidls/Navigator.webidl b/components/script/dom/webidls/Navigator.webidl
new file mode 100644
index 00000000000..16d96d53470
--- /dev/null
+++ b/components/script/dom/webidls/Navigator.webidl
@@ -0,0 +1,27 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#navigator
+interface Navigator {
+ // objects implementing this interface also implement the interfaces given below
+};
+Navigator implements NavigatorID;
+//Navigator implements NavigatorLanguage;
+//Navigator implements NavigatorOnLine;
+//Navigator implements NavigatorContentUtils;
+//Navigator implements NavigatorStorageUtils;
+//Navigator implements NavigatorPlugins;
+
+// http://www.whatwg.org/html/#navigatorid
+[NoInterfaceObject/*, Exposed=Window,Worker*/]
+interface NavigatorID {
+ readonly attribute DOMString appCodeName; // constant "Mozilla"
+ readonly attribute DOMString appName;
+ //readonly attribute DOMString appVersion;
+ readonly attribute DOMString platform;
+ readonly attribute DOMString product; // constant "Gecko"
+ boolean taintEnabled(); // constant false
+ //readonly attribute DOMString userAgent;
+};
diff --git a/components/script/dom/webidls/Node.webidl b/components/script/dom/webidls/Node.webidl
new file mode 100644
index 00000000000..3297b2c3ab1
--- /dev/null
+++ b/components/script/dom/webidls/Node.webidl
@@ -0,0 +1,79 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is:
+ * http://dom.spec.whatwg.org/#interface-node
+ */
+
+interface Node : EventTarget {
+ const unsigned short ELEMENT_NODE = 1;
+ const unsigned short ATTRIBUTE_NODE = 2; // historical
+ const unsigned short TEXT_NODE = 3;
+ const unsigned short CDATA_SECTION_NODE = 4; // historical
+ const unsigned short ENTITY_REFERENCE_NODE = 5; // historical
+ const unsigned short ENTITY_NODE = 6; // historical
+ const unsigned short PROCESSING_INSTRUCTION_NODE = 7;
+ const unsigned short COMMENT_NODE = 8;
+ const unsigned short DOCUMENT_NODE = 9;
+ const unsigned short DOCUMENT_TYPE_NODE = 10;
+ const unsigned short DOCUMENT_FRAGMENT_NODE = 11;
+ const unsigned short NOTATION_NODE = 12; // historical
+ [Constant]
+ readonly attribute unsigned short nodeType;
+ [Pure]
+ readonly attribute DOMString nodeName;
+
+ [Pure]
+ readonly attribute DOMString? baseURI;
+
+ [Pure]
+ readonly attribute Document? ownerDocument;
+ [Pure]
+ readonly attribute Node? parentNode;
+ [Pure]
+ readonly attribute Element? parentElement;
+ boolean hasChildNodes();
+ [Constant]
+ readonly attribute NodeList childNodes;
+ [Pure]
+ readonly attribute Node? firstChild;
+ [Pure]
+ readonly attribute Node? lastChild;
+ [Pure]
+ readonly attribute Node? previousSibling;
+ [Pure]
+ readonly attribute Node? nextSibling;
+
+ [Pure]
+ attribute DOMString? nodeValue;
+ [Pure]
+ attribute DOMString? textContent;
+ void normalize();
+
+ Node cloneNode(optional boolean deep = true);
+ boolean isEqualNode(Node? node);
+
+ const unsigned short DOCUMENT_POSITION_DISCONNECTED = 0x01;
+ const unsigned short DOCUMENT_POSITION_PRECEDING = 0x02;
+ const unsigned short DOCUMENT_POSITION_FOLLOWING = 0x04;
+ const unsigned short DOCUMENT_POSITION_CONTAINS = 0x08;
+ const unsigned short DOCUMENT_POSITION_CONTAINED_BY = 0x10;
+ const unsigned short DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = 0x20;
+ unsigned short compareDocumentPosition(Node other);
+ boolean contains(Node? other);
+
+ DOMString? lookupPrefix(DOMString? namespace);
+ DOMString? lookupNamespaceURI(DOMString? prefix);
+ boolean isDefaultNamespace(DOMString? namespace);
+
+ [Throws]
+ Node insertBefore(Node node, Node? child);
+ [Throws]
+ Node appendChild(Node node);
+ [Throws]
+ Node replaceChild(Node node, Node child);
+ [Throws]
+ Node removeChild(Node child);
+};
diff --git a/components/script/dom/webidls/NodeFilter.webidl b/components/script/dom/webidls/NodeFilter.webidl
new file mode 100644
index 00000000000..b84b369829e
--- /dev/null
+++ b/components/script/dom/webidls/NodeFilter.webidl
@@ -0,0 +1,33 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * http://dom.spec.whatwg.org/#interface-nodefilter
+ */
+// Import form http://hg.mozilla.org/mozilla-central/file/a5a720259d79/dom/webidl/NodeFilter.webidl
+
+callback interface NodeFilter {
+ // Constants for acceptNode()
+ // const unsigned short FILTER_ACCEPT = 1;
+ // const unsigned short FILTER_REJECT = 2;
+ // const unsigned short FILTER_SKIP = 3;
+
+ // Constants for whatToShow
+ // const unsigned long SHOW_ALL = 0xFFFFFFFF;
+ // const unsigned long SHOW_ELEMENT = 0x1;
+ // const unsigned long SHOW_ATTRIBUTE = 0x2; // historical
+ // const unsigned long SHOW_TEXT = 0x4;
+ // const unsigned long SHOW_CDATA_SECTION = 0x8; // historical
+ // const unsigned long SHOW_ENTITY_REFERENCE = 0x10; // historical
+ // const unsigned long SHOW_ENTITY = 0x20; // historical
+ // const unsigned long SHOW_PROCESSING_INSTRUCTION = 0x40;
+ // const unsigned long SHOW_COMMENT = 0x80;
+ // const unsigned long SHOW_DOCUMENT = 0x100;
+ // const unsigned long SHOW_DOCUMENT_TYPE = 0x200;
+ // const unsigned long SHOW_DOCUMENT_FRAGMENT = 0x400;
+ // const unsigned long SHOW_NOTATION = 0x800; // historical
+
+ unsigned short acceptNode(Node node);
+};
diff --git a/components/script/dom/webidls/NodeIterator.webidl b/components/script/dom/webidls/NodeIterator.webidl
new file mode 100644
index 00000000000..6eb684dd9f9
--- /dev/null
+++ b/components/script/dom/webidls/NodeIterator.webidl
@@ -0,0 +1,32 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * http://www.w3.org/TR/2012/WD-dom-20120105/
+ *
+ * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
+ * liability, trademark and document use rules apply.
+ */
+// Import from http://hg.mozilla.org/mozilla-central/raw-file/a5a720259d79/dom/webidl/NodeIterator.webidl
+
+interface NodeIterator {
+ // [Constant]
+ // readonly attribute Node root;
+ // [Pure]
+ // readonly attribute Node? referenceNode;
+ // [Pure]
+ // readonly attribute boolean pointerBeforeReferenceNode;
+ // [Constant]
+ // readonly attribute unsigned long whatToShow;
+ // [Constant]
+ // readonly attribute NodeFilter? filter;
+
+ // [Throws]
+ // Node? nextNode();
+ // [Throws]
+ // Node? previousNode();
+
+ // void detach();
+};
diff --git a/components/script/dom/webidls/NodeList.webidl b/components/script/dom/webidls/NodeList.webidl
new file mode 100644
index 00000000000..9773f8efcef
--- /dev/null
+++ b/components/script/dom/webidls/NodeList.webidl
@@ -0,0 +1,13 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is:
+ * http://dom.spec.whatwg.org/#interface-nodelist
+ */
+
+interface NodeList {
+ readonly attribute unsigned long length;
+ getter Node? item(unsigned long index);
+};
diff --git a/components/script/dom/webidls/ParentNode.webidl b/components/script/dom/webidls/ParentNode.webidl
new file mode 100644
index 00000000000..daa4339611f
--- /dev/null
+++ b/components/script/dom/webidls/ParentNode.webidl
@@ -0,0 +1,34 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * http://dom.spec.whatwg.org/#interface-parentnode
+ */
+
+[NoInterfaceObject]
+interface ParentNode {
+ [Constant]
+ readonly attribute HTMLCollection children;
+ /*
+ [Pure]
+ readonly attribute Element? firstElementChild;
+ [Pure]
+ readonly attribute Element? lastElementChild;
+ [Pure]
+ readonly attribute unsigned long childElementCount;
+ */
+ // Not implemented yet
+ // void prepend((Node or DOMString)... nodes);
+ // void append((Node or DOMString)... nodes);
+
+ //Element? query(DOMString relativeSelectors);
+ //[NewObject]
+ //Elements queryAll(DOMString relativeSelectors);
+ [Throws]
+ Element? querySelector(DOMString selectors);
+ //[NewObject]
+ [Throws]
+ NodeList querySelectorAll(DOMString selectors);
+};
diff --git a/components/script/dom/webidls/Performance.webidl b/components/script/dom/webidls/Performance.webidl
new file mode 100644
index 00000000000..ff7e0ee3754
--- /dev/null
+++ b/components/script/dom/webidls/Performance.webidl
@@ -0,0 +1,19 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/NavigationTiming/Overview.html#sec-window.performance-attribute
+ */
+
+typedef double DOMHighResTimeStamp;
+
+interface Performance {
+ readonly attribute PerformanceTiming timing;
+ /* readonly attribute PerformanceNavigation navigation; */
+};
+
+partial interface Performance {
+ DOMHighResTimeStamp now();
+};
diff --git a/components/script/dom/webidls/PerformanceTiming.webidl b/components/script/dom/webidls/PerformanceTiming.webidl
new file mode 100644
index 00000000000..c5dfd4502c7
--- /dev/null
+++ b/components/script/dom/webidls/PerformanceTiming.webidl
@@ -0,0 +1,32 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/NavigationTiming/Overview.html#sec-navigation-timing-interface
+ */
+
+interface PerformanceTiming {
+ readonly attribute unsigned long long navigationStart;
+ /* readonly attribute unsigned long long unloadEventStart;
+ readonly attribute unsigned long long unloadEventEnd;
+ readonly attribute unsigned long long redirectStart;
+ readonly attribute unsigned long long redirectEnd;
+ readonly attribute unsigned long long fetchStart;
+ readonly attribute unsigned long long domainLookupStart;
+ readonly attribute unsigned long long domainLookupEnd;
+ readonly attribute unsigned long long connectStart;
+ readonly attribute unsigned long long connectEnd;
+ readonly attribute unsigned long long secureConnectionStart;
+ readonly attribute unsigned long long requestStart;
+ readonly attribute unsigned long long responseStart;
+ readonly attribute unsigned long long responseEnd;
+ readonly attribute unsigned long long domLoading;
+ readonly attribute unsigned long long domInteractive;
+ readonly attribute unsigned long long domContentLoadedEventStart;
+ readonly attribute unsigned long long domContentLoadedEventEnd;
+ readonly attribute unsigned long long domComplete;
+ readonly attribute unsigned long long loadEventStart;
+ readonly attribute unsigned long long loadEventEnd; */
+};
diff --git a/components/script/dom/webidls/ProcessingInstruction.webidl b/components/script/dom/webidls/ProcessingInstruction.webidl
new file mode 100644
index 00000000000..96426538900
--- /dev/null
+++ b/components/script/dom/webidls/ProcessingInstruction.webidl
@@ -0,0 +1,12 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * http://dom.spec.whatwg.org/#interface-processinginstruction
+ */
+
+interface ProcessingInstruction : CharacterData {
+ readonly attribute DOMString target;
+};
diff --git a/components/script/dom/webidls/ProgressEvent.webidl b/components/script/dom/webidls/ProgressEvent.webidl
new file mode 100644
index 00000000000..420d745fe14
--- /dev/null
+++ b/components/script/dom/webidls/ProgressEvent.webidl
@@ -0,0 +1,28 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * http://xhr.spec.whatwg.org/#interface-progressevent
+ *
+ * To the extent possible under law, the editor has waived all copyright
+ * and related or neighboring rights to this work. In addition, as of 1 May 2014,
+ * the editor has made this specification available under the Open Web Foundation
+ * Agreement Version 1.0, which is available at
+ * http://www.openwebfoundation.org/legal/the-owf-1-0-agreements/owfa-1-0.
+ */
+
+[Constructor(DOMString type, optional ProgressEventInit eventInitDict)/*,
+ Exposed=Window,Worker*/]
+interface ProgressEvent : Event {
+ readonly attribute boolean lengthComputable;
+ readonly attribute unsigned long long loaded;
+ readonly attribute unsigned long long total;
+};
+
+dictionary ProgressEventInit : EventInit {
+ boolean lengthComputable = false;
+ unsigned long long loaded = 0;
+ unsigned long long total = 0;
+};
diff --git a/components/script/dom/webidls/Range.webidl b/components/script/dom/webidls/Range.webidl
new file mode 100644
index 00000000000..d74411b1dd9
--- /dev/null
+++ b/components/script/dom/webidls/Range.webidl
@@ -0,0 +1,85 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * http://dom.spec.whatwg.org/#range
+ * http://domparsing.spec.whatwg.org/#dom-range-createcontextualfragment
+ * http://dvcs.w3.org/hg/csswg/raw-file/tip/cssom-view/Overview.html#extensions-to-the-range-interface
+ */
+
+[Constructor]
+interface Range {
+ // [Throws]
+ // readonly attribute Node startContainer;
+ // [Throws]
+ // readonly attribute unsigned long startOffset;
+ // [Throws]
+ // readonly attribute Node endContainer;
+ // [Throws]
+ // readonly attribute unsigned long endOffset;
+ // readonly attribute boolean collapsed;
+ // [Throws]
+ // readonly attribute Node commonAncestorContainer;
+
+ // [Throws]
+ // void setStart(Node refNode, unsigned long offset);
+ // [Throws]
+ // void setEnd(Node refNode, unsigned long offset);
+ // [Throws]
+ // void setStartBefore(Node refNode);
+ // [Throws]
+ // void setStartAfter(Node refNode);
+ // [Throws]
+ // void setEndBefore(Node refNode);
+ // [Throws]
+ // void setEndAfter(Node refNode);
+ // void collapse(optional boolean toStart = false);
+ // [Throws]
+ // void selectNode(Node refNode);
+ // [Throws]
+ // void selectNodeContents(Node refNode);
+
+ // const unsigned short START_TO_START = 0;
+ // const unsigned short START_TO_END = 1;
+ // const unsigned short END_TO_END = 2;
+ // const unsigned short END_TO_START = 3;
+ // [Throws]
+ // short compareBoundaryPoints(unsigned short how, Range sourceRange);
+ // [Throws]
+ // void deleteContents();
+ // [Throws]
+ // DocumentFragment extractContents();
+ // [Throws]
+ // DocumentFragment cloneContents();
+ // [Throws]
+ // void insertNode(Node node);
+ // [Throws]
+ // void surroundContents(Node newParent);
+
+ // Range cloneRange();
+ void detach();
+
+ // [Throws]
+ // boolean isPointInRange(Node node, unsigned long offset);
+ // [Throws]
+ // short comparePoint(Node node, unsigned long offset);
+
+ // [Throws]
+ // boolean intersectsNode(Node node);
+
+ // stringifier;
+};
+
+// http://domparsing.spec.whatwg.org/#dom-range-createcontextualfragment
+partial interface Range {
+ // [Throws]
+ // DocumentFragment createContextualFragment(DOMString fragment);
+};//
+
+//// http://dvcs.w3.org/hg/csswg/raw-file/tip/cssom-view/Overview.html#extensions-to-the-range-interface
+partial interface Range {
+ // DOMRectList? getClientRects();
+ // DOMRect getBoundingClientRect();
+};
diff --git a/components/script/dom/webidls/Screen.webidl b/components/script/dom/webidls/Screen.webidl
new file mode 100644
index 00000000000..3065c113b96
--- /dev/null
+++ b/components/script/dom/webidls/Screen.webidl
@@ -0,0 +1,14 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://dev.w3.org/csswg/cssom-view/#the-screen-interface
+interface Screen {
+ //readonly attribute double availWidth;
+ //readonly attribute double availHeight;
+ //readonly attribute double width;
+ //readonly attribute double height;
+ readonly attribute unsigned long colorDepth;
+ readonly attribute unsigned long pixelDepth;
+};
diff --git a/components/script/dom/webidls/TestBinding.webidl b/components/script/dom/webidls/TestBinding.webidl
new file mode 100644
index 00000000000..e8ef05d8242
--- /dev/null
+++ b/components/script/dom/webidls/TestBinding.webidl
@@ -0,0 +1,276 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+enum TestEnum { "", "foo", "bar" };
+
+dictionary TestDictionary {
+ boolean booleanValue;
+ byte byteValue;
+ octet octetValue;
+ short shortValue;
+ unsigned short unsignedShortValue;
+ long longValue;
+ unsigned long unsignedLongValue;
+ long long longLongValue;
+ unsigned long long unsignedLongLongValue;
+ float floatValue;
+ double doubleValue;
+ DOMString stringValue;
+ TestEnum enumValue;
+ Blob interfaceValue;
+ any anyValue;
+};
+
+dictionary TestDictionaryDefaults {
+ boolean booleanValue = false;
+ byte byteValue = 7;
+ octet octetValue = 7;
+ short shortValue = 7;
+ unsigned short unsignedShortValue = 7;
+ long longValue = 7;
+ unsigned long unsignedLongValue = 7;
+ long long longLongValue = 7;
+ unsigned long long unsignedLongLongValue = 7;
+ // float floatValue = 7.0;
+ // double doubleValue = 7.0;
+ DOMString stringValue = "";
+ TestEnum enumValue = "bar";
+ any anyValue = null;
+
+ boolean? nullableBooleanValue = false;
+ byte? nullableByteValue = 7;
+ octet? nullableOctetValue = 7;
+ short? nullableShortValue = 7;
+ unsigned short? nullableUnsignedShortValue = 7;
+ long? nullableLongValue = 7;
+ unsigned long? nullableUnsignedLongValue = 7;
+ long long? nullableLongLongValue = 7;
+ unsigned long long? nullableUnsignedLongLongValue = 7;
+ // float? nullableFloatValue = 7.0;
+ // double? nullableDoubleValue = 7.0;
+ DOMString? nullableStringValue = "";
+ // TestEnum? nullableEnumValue = "bar";
+};
+
+interface TestBinding {
+ attribute boolean booleanAttribute;
+ attribute byte byteAttribute;
+ attribute octet octetAttribute;
+ attribute short shortAttribute;
+ attribute unsigned short unsignedShortAttribute;
+ attribute long longAttribute;
+ attribute unsigned long unsignedLongAttribute;
+ attribute long long longLongAttribute;
+ attribute unsigned long long unsignedLongLongAttribute;
+ attribute float floatAttribute;
+ attribute double doubleAttribute;
+ attribute DOMString stringAttribute;
+ attribute ByteString byteStringAttribute;
+ attribute TestEnum enumAttribute;
+ attribute Blob interfaceAttribute;
+ attribute (HTMLElement or long) unionAttribute;
+ attribute (Event or DOMString) union2Attribute;
+ attribute any anyAttribute;
+
+ attribute boolean? booleanAttributeNullable;
+ attribute byte? byteAttributeNullable;
+ attribute octet? octetAttributeNullable;
+ attribute short? shortAttributeNullable;
+ attribute unsigned short? unsignedShortAttributeNullable;
+ attribute long? longAttributeNullable;
+ attribute unsigned long? unsignedLongAttributeNullable;
+ attribute long long? longLongAttributeNullable;
+ attribute unsigned long long? unsignedLongLongAttributeNullable;
+ attribute float? floatAttributeNullable;
+ attribute double? doubleAttributeNullable;
+ attribute DOMString? stringAttributeNullable;
+ attribute ByteString? byteStringAttributeNullable;
+ readonly attribute TestEnum? enumAttributeNullable;
+ attribute Blob? interfaceAttributeNullable;
+ attribute (HTMLElement or long)? unionAttributeNullable;
+ attribute (Event or DOMString)? union2AttributeNullable;
+
+ void receiveVoid();
+ boolean receiveBoolean();
+ byte receiveByte();
+ octet receiveOctet();
+ short receiveShort();
+ unsigned short receiveUnsignedShort();
+ long receiveLong();
+ unsigned long receiveUnsignedLong();
+ long long receiveLongLong();
+ unsigned long long receiveUnsignedLongLong();
+ float receiveFloat();
+ double receiveDouble();
+ DOMString receiveString();
+ ByteString receiveByteString();
+ TestEnum receiveEnum();
+ Blob receiveInterface();
+ any receiveAny();
+ (HTMLElement or long) receiveUnion();
+ (Event or DOMString) receiveUnion2();
+
+ byte? receiveNullableByte();
+ boolean? receiveNullableBoolean();
+ octet? receiveNullableOctet();
+ short? receiveNullableShort();
+ unsigned short? receiveNullableUnsignedShort();
+ long? receiveNullableLong();
+ unsigned long? receiveNullableUnsignedLong();
+ long long? receiveNullableLongLong();
+ unsigned long long? receiveNullableUnsignedLongLong();
+ float? receiveNullableFloat();
+ double? receiveNullableDouble();
+ DOMString? receiveNullableString();
+ ByteString? receiveNullableByteString();
+ TestEnum? receiveNullableEnum();
+ Blob? receiveNullableInterface();
+ (HTMLElement or long)? receiveNullableUnion();
+ (Event or DOMString)? receiveNullableUnion2();
+
+ void passBoolean(boolean arg);
+ void passByte(byte arg);
+ void passOctet(octet arg);
+ void passShort(short arg);
+ void passUnsignedShort(unsigned short arg);
+ void passLong(long arg);
+ void passUnsignedLong(unsigned long arg);
+ void passLongLong(long long arg);
+ void passUnsignedLongLong(unsigned long long arg);
+ void passFloat(float arg);
+ void passDouble(double arg);
+ void passString(DOMString arg);
+ void passByteString(ByteString arg);
+ void passEnum(TestEnum arg);
+ void passInterface(Blob arg);
+ void passUnion((HTMLElement or long) arg);
+ void passUnion2((Event or DOMString) data);
+ void passUnion3((Blob or DOMString) data);
+ void passAny(any arg);
+
+ void passNullableBoolean(boolean? arg);
+ void passNullableByte(byte? arg);
+ void passNullableOctet(octet? arg);
+ void passNullableShort(short? arg);
+ void passNullableUnsignedShort(unsigned short? arg);
+ void passNullableLong(long? arg);
+ void passNullableUnsignedLong(unsigned long? arg);
+ void passNullableLongLong(long long? arg);
+ void passNullableUnsignedLongLong(unsigned long long? arg);
+ void passNullableFloat(float? arg);
+ void passNullableDouble(double? arg);
+ void passNullableString(DOMString? arg);
+ void passNullableByteString(ByteString? arg);
+ // void passNullableEnum(TestEnum? arg);
+ void passNullableInterface(Blob? arg);
+ void passNullableUnion((HTMLElement or long)? arg);
+ void passNullableUnion2((Event or DOMString)? data);
+
+ void passOptionalBoolean(optional boolean arg);
+ void passOptionalByte(optional byte arg);
+ void passOptionalOctet(optional octet arg);
+ void passOptionalShort(optional short arg);
+ void passOptionalUnsignedShort(optional unsigned short arg);
+ void passOptionalLong(optional long arg);
+ void passOptionalUnsignedLong(optional unsigned long arg);
+ void passOptionalLongLong(optional long long arg);
+ void passOptionalUnsignedLongLong(optional unsigned long long arg);
+ void passOptionalFloat(optional float arg);
+ void passOptionalDouble(optional double arg);
+ void passOptionalString(optional DOMString arg);
+ void passOptionalByteString(optional ByteString arg);
+ void passOptionalEnum(optional TestEnum arg);
+ void passOptionalInterface(optional Blob arg);
+ void passOptionalUnion(optional (HTMLElement or long) arg);
+ void passOptionalUnion2(optional (Event or DOMString) data);
+ void passOptionalAny(optional any arg);
+
+ void passOptionalNullableBoolean(optional boolean? arg);
+ void passOptionalNullableByte(optional byte? arg);
+ void passOptionalNullableOctet(optional octet? arg);
+ void passOptionalNullableShort(optional short? arg);
+ void passOptionalNullableUnsignedShort(optional unsigned short? arg);
+ void passOptionalNullableLong(optional long? arg);
+ void passOptionalNullableUnsignedLong(optional unsigned long? arg);
+ void passOptionalNullableLongLong(optional long long? arg);
+ void passOptionalNullableUnsignedLongLong(optional unsigned long long? arg);
+ void passOptionalNullableFloat(optional float? arg);
+ void passOptionalNullableDouble(optional double? arg);
+ void passOptionalNullableString(optional DOMString? arg);
+ void passOptionalNullableByteString(optional ByteString? arg);
+ // void passOptionalNullableEnum(optional TestEnum? arg);
+ void passOptionalNullableInterface(optional Blob? arg);
+ void passOptionalNullableUnion(optional (HTMLElement or long)? arg);
+ void passOptionalNullableUnion2(optional (Event or DOMString)? data);
+
+ void passOptionalBooleanWithDefault(optional boolean arg = false);
+ void passOptionalByteWithDefault(optional byte arg = 0);
+ void passOptionalOctetWithDefault(optional octet arg = 19);
+ void passOptionalShortWithDefault(optional short arg = 5);
+ void passOptionalUnsignedShortWithDefault(optional unsigned short arg = 2);
+ void passOptionalLongWithDefault(optional long arg = 7);
+ void passOptionalUnsignedLongWithDefault(optional unsigned long arg = 6);
+ void passOptionalLongLongWithDefault(optional long long arg = -12);
+ void passOptionalUnsignedLongLongWithDefault(optional unsigned long long arg = 17);
+ void passOptionalStringWithDefault(optional DOMString arg = "");
+ void passOptionalEnumWithDefault(optional TestEnum arg = "foo");
+ // void passOptionalUnionWithDefault(optional (HTMLElement or long) arg = 9);
+ // void passOptionalUnion2WithDefault(optional(Event or DOMString)? data = "foo");
+
+ void passOptionalNullableBooleanWithDefault(optional boolean? arg = null);
+ void passOptionalNullableByteWithDefault(optional byte? arg = null);
+ void passOptionalNullableOctetWithDefault(optional octet? arg = null);
+ void passOptionalNullableShortWithDefault(optional short? arg = null);
+ void passOptionalNullableUnsignedShortWithDefault(optional unsigned short? arg = null);
+ void passOptionalNullableLongWithDefault(optional long? arg = null);
+ void passOptionalNullableUnsignedLongWithDefault(optional unsigned long? arg = null);
+ void passOptionalNullableLongLongWithDefault(optional long long? arg = null);
+ void passOptionalNullableUnsignedLongLongWithDefault(optional unsigned long long? arg = null);
+ void passOptionalNullableStringWithDefault(optional DOMString? arg = null);
+ void passOptionalNullableByteStringWithDefault(optional ByteString? arg = null);
+ // void passOptionalNullableEnumWithDefault(optional TestEnum? arg = null);
+ void passOptionalNullableInterfaceWithDefault(optional Blob? arg = null);
+ void passOptionalNullableUnionWithDefault(optional (HTMLElement or long)? arg = null);
+ void passOptionalNullableUnion2WithDefault(optional (Event or DOMString)? data = null);
+ void passOptionalAnyWithDefault(optional any arg = null);
+
+ void passOptionalNullableBooleanWithNonNullDefault(optional boolean? arg = false);
+ void passOptionalNullableByteWithNonNullDefault(optional byte? arg = 7);
+ void passOptionalNullableOctetWithNonNullDefault(optional octet? arg = 7);
+ void passOptionalNullableShortWithNonNullDefault(optional short? arg = 7);
+ void passOptionalNullableUnsignedShortWithNonNullDefault(optional unsigned short? arg = 7);
+ void passOptionalNullableLongWithNonNullDefault(optional long? arg = 7);
+ void passOptionalNullableUnsignedLongWithNonNullDefault(optional unsigned long? arg = 7);
+ void passOptionalNullableLongLongWithNonNullDefault(optional long long? arg = 7);
+ void passOptionalNullableUnsignedLongLongWithNonNullDefault(optional unsigned long long? arg = 7);
+ // void passOptionalNullableFloatWithNonNullDefault(optional float? arg = 0.0);
+ // void passOptionalNullableDoubleWithNonNullDefault(optional double? arg = 0.0);
+ void passOptionalNullableStringWithNonNullDefault(optional DOMString? arg = "");
+ // void passOptionalNullableEnumWithNonNullDefault(optional TestEnum? arg = "foo");
+ // void passOptionalNullableUnionWithNonNullDefault(optional (HTMLElement or long)? arg = 7);
+ // void passOptionalNullableUnion2WithNonNullDefault(optional (Event or DOMString)? data = "foo");
+
+ void passVariadicBoolean(boolean... args);
+ void passVariadicByte(byte... args);
+ void passVariadicOctet(octet... args);
+ void passVariadicShort(short... args);
+ void passVariadicUnsignedShort(unsigned short... args);
+ void passVariadicLong(long... args);
+ void passVariadicUnsignedLong(unsigned long... args);
+ void passVariadicLongLong(long long... args);
+ void passVariadicUnsignedLongLong(unsigned long long... args);
+ void passVariadicFloat(float... args);
+ void passVariadicDouble(double... args);
+ void passVariadicString(DOMString... args);
+ void passVariadicByteString(ByteString... args);
+ void passVariadicEnum(TestEnum... args);
+ // void passVariadicInterface(Blob... args);
+ void passVariadicUnion((HTMLElement or long)... args);
+ void passVariadicUnion2((Event or DOMString)... args);
+ void passVariadicUnion3((Blob or DOMString)... args);
+ void passVariadicAny(any... args);
+
+ static attribute boolean booleanAttributeStatic;
+ static void receiveVoidStatic();
+};
diff --git a/components/script/dom/webidls/Text.webidl b/components/script/dom/webidls/Text.webidl
new file mode 100644
index 00000000000..972797c73c8
--- /dev/null
+++ b/components/script/dom/webidls/Text.webidl
@@ -0,0 +1,18 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * http://dom.spec.whatwg.org/
+ *
+ * To the extent possible under law, the editors have waived all copyright
+ * and related or neighboring rights to this work.
+ */
+
+// http://dom.spec.whatwg.org/#text
+[Constructor(optional DOMString data = "")]
+interface Text : CharacterData {
+ //[NewObject] Text splitText(unsigned long offset);
+ //readonly attribute DOMString wholeText;
+};
diff --git a/components/script/dom/webidls/TreeWalker.webidl b/components/script/dom/webidls/TreeWalker.webidl
new file mode 100644
index 00000000000..70987abb528
--- /dev/null
+++ b/components/script/dom/webidls/TreeWalker.webidl
@@ -0,0 +1,23 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * http://dom.spec.whatwg.org/#interface-treewalker
+ */
+
+interface TreeWalker {
+ // [SameObject] readonly attribute Node root;
+ // readonly attribute unsigned long whatToShow;
+ // readonly attribute NodeFilter? filter;
+ // attribute Node currentNode;
+
+ // Node? parentNode();
+ // Node? firstChild();
+ // Node? lastChild();
+ // Node? previousSibling();
+ // Node? nextSibling();
+ // Node? previousNode();
+ // Node? nextNode();
+};
diff --git a/components/script/dom/webidls/UIEvent.webidl b/components/script/dom/webidls/UIEvent.webidl
new file mode 100644
index 00000000000..4f5caeaad14
--- /dev/null
+++ b/components/script/dom/webidls/UIEvent.webidl
@@ -0,0 +1,25 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#idl-def-UIEvent
+[Constructor(DOMString type, optional UIEventInit eventInitDict)]
+interface UIEvent : Event {
+ // readonly attribute WindowProxy? view;
+ readonly attribute Window? view;
+ readonly attribute long detail;
+};
+
+// https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#idl-def-UIEventInit
+dictionary UIEventInit : EventInit {
+ // WindowProxy? view = null;
+ Window? view = null;
+ long detail = 0;
+};
+
+// https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#idl-def-UIEvent-1
+partial interface UIEvent {
+ // Deprecated in DOM Level 3
+ void initUIEvent (DOMString typeArg, boolean bubblesArg, boolean cancelableArg, Window? viewArg, long detailArg);
+};
diff --git a/components/script/dom/webidls/URLSearchParams.webidl b/components/script/dom/webidls/URLSearchParams.webidl
new file mode 100644
index 00000000000..c2e401c45f0
--- /dev/null
+++ b/components/script/dom/webidls/URLSearchParams.webidl
@@ -0,0 +1,19 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * http://url.spec.whatwg.org/#interface-urlsearchparams
+ */
+
+[Constructor(optional (DOMString or URLSearchParams) init)]
+interface URLSearchParams {
+ void append(DOMString name, DOMString value);
+ void delete(DOMString name);
+ DOMString? get(DOMString name);
+ // sequence<DOMString> getAll(DOMString name);
+ boolean has(DOMString name);
+ void set(DOMString name, DOMString value);
+ //stringifier;
+};
diff --git a/components/script/dom/webidls/URLUtils.webidl b/components/script/dom/webidls/URLUtils.webidl
new file mode 100644
index 00000000000..58fe13c5508
--- /dev/null
+++ b/components/script/dom/webidls/URLUtils.webidl
@@ -0,0 +1,25 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://url.spec.whatwg.org/#urlutils
+[NoInterfaceObject]
+interface URLUtils {
+ //stringifier attribute ScalarValueString href;
+ readonly attribute DOMString href;
+ //readonly attribute ScalarValueString origin;
+
+ // attribute ScalarValueString protocol;
+ // attribute ScalarValueString username;
+ // attribute ScalarValueString password;
+ // attribute ScalarValueString host;
+ // attribute ScalarValueString hostname;
+ // attribute ScalarValueString port;
+ // attribute ScalarValueString pathname;
+ // attribute ScalarValueString search;
+ readonly attribute DOMString search;
+ // attribute URLSearchParams searchParams;
+ // attribute ScalarValueString hash;
+ readonly attribute DOMString hash;
+};
diff --git a/components/script/dom/webidls/URLUtilsReadOnly.webidl b/components/script/dom/webidls/URLUtilsReadOnly.webidl
new file mode 100644
index 00000000000..8518019a6c1
--- /dev/null
+++ b/components/script/dom/webidls/URLUtilsReadOnly.webidl
@@ -0,0 +1,23 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://url.spec.whatwg.org/#urlutilsreadonly
+[NoInterfaceObject/*,
+ Exposed=(Window,Worker)*/]
+interface URLUtilsReadOnly {
+ //stringifier readonly attribute ScalarValueString href;
+ readonly attribute DOMString href;
+ //readonly attribute ScalarValueString origin;
+
+ //readonly attribute ScalarValueString protocol;
+ //readonly attribute ScalarValueString host;
+ //readonly attribute ScalarValueString hostname;
+ //readonly attribute ScalarValueString port;
+ //readonly attribute ScalarValueString pathname;
+ //readonly attribute ScalarValueString search;
+ readonly attribute DOMString search;
+ //readonly attribute ScalarValueString hash;
+ readonly attribute DOMString hash;
+};
diff --git a/components/script/dom/webidls/ValidityState.webidl b/components/script/dom/webidls/ValidityState.webidl
new file mode 100644
index 00000000000..d99677574cd
--- /dev/null
+++ b/components/script/dom/webidls/ValidityState.webidl
@@ -0,0 +1,19 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#validitystate
+interface ValidityState {
+ //readonly attribute boolean valueMissing;
+ //readonly attribute boolean typeMismatch;
+ //readonly attribute boolean patternMismatch;
+ //readonly attribute boolean tooLong;
+ //readonly attribute boolean tooShort;
+ //readonly attribute boolean rangeUnderflow;
+ //readonly attribute boolean rangeOverflow;
+ //readonly attribute boolean stepMismatch;
+ //readonly attribute boolean badInput;
+ //readonly attribute boolean customError;
+ //readonly attribute boolean valid;
+};
diff --git a/components/script/dom/webidls/Window.webidl b/components/script/dom/webidls/Window.webidl
new file mode 100644
index 00000000000..9cd6ed1c045
--- /dev/null
+++ b/components/script/dom/webidls/Window.webidl
@@ -0,0 +1,131 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#window
+[PrimaryGlobal]
+/*sealed*/ interface Window : EventTarget {
+ // the current browsing context
+ //[Unforgeable] readonly attribute WindowProxy window;
+ //[Replaceable] readonly attribute WindowProxy self;
+ readonly attribute Window window;
+ readonly attribute Window self;
+ /*[Unforgeable]*/ readonly attribute Document document;
+ // attribute DOMString name;
+ /*[PutForwards=href, Unforgeable]*/ readonly attribute Location location;
+ //readonly attribute History history;
+ //[Replaceable] readonly attribute BarProp locationbar;
+ //[Replaceable] readonly attribute BarProp menubar;
+ //[Replaceable] readonly attribute BarProp personalbar;
+ //[Replaceable] readonly attribute BarProp scrollbars;
+ //[Replaceable] readonly attribute BarProp statusbar;
+ //[Replaceable] readonly attribute BarProp toolbar;
+ // attribute DOMString status;
+ void close();
+ //readonly attribute boolean closed;
+ //void stop();
+ //void focus();
+ //void blur();
+
+ // other browsing contexts
+ //[Replaceable] readonly attribute WindowProxy frames;
+ readonly attribute Window frames;
+ //[Replaceable] readonly attribute unsigned long length;
+ //[Unforgeable] readonly attribute WindowProxy top;
+ // attribute any opener;
+ //readonly attribute WindowProxy parent;
+ readonly attribute Window parent;
+ //readonly attribute Element? frameElement;
+ //WindowProxy open(optional DOMString url = "about:blank", optional DOMString target = "_blank", optional DOMString features = "", optional boolean replace = false);
+ //getter WindowProxy (unsigned long index);
+ //getter object (DOMString name);
+
+ // the user agent
+ readonly attribute Navigator navigator;
+ //[Replaceable] readonly attribute External external;
+ //readonly attribute ApplicationCache applicationCache;
+
+ // user prompts
+ //void alert();
+ void alert(DOMString message);
+ //boolean confirm(optional DOMString message = "");
+ //DOMString? prompt(optional DOMString message = "", optional DOMString default = "");
+ //void print();
+ //any showModalDialog(DOMString url, optional any argument);
+
+ //void postMessage(any message, DOMString targetOrigin, optional sequence<Transferable> transfer);
+
+ // also has obsolete members
+};
+Window implements GlobalEventHandlers;
+Window implements WindowEventHandlers;
+
+// http://www.whatwg.org/html/#windowtimers
+[NoInterfaceObject/*, Exposed=Window,Worker*/]
+interface WindowTimers {
+ //long setTimeout(Function handler, optional long timeout = 0, any... arguments);
+ //long setTimeout(DOMString handler, optional long timeout = 0, any... arguments);
+ long setTimeout(any handler, optional long timeout = 0);
+ void clearTimeout(optional long handle = 0);
+ //long setInterval(Function handler, optional long timeout = 0, any... arguments);
+ //long setInterval(DOMString handler, optional long timeout = 0, any... arguments);
+ long setInterval(any handler, optional long timeout = 0);
+ void clearInterval(optional long handle = 0);
+};
+Window implements WindowTimers;
+
+// http://www.whatwg.org/html/#atob
+[NoInterfaceObject/*, Exposed=Window,Worker*/]
+interface WindowBase64 {
+ [Throws]
+ DOMString btoa(DOMString btoa);
+ [Throws]
+ DOMString atob(DOMString atob);
+};
+Window implements WindowBase64;
+
+// https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/NavigationTiming/Overview.html#sec-window.performance-attribute
+partial interface Window {
+ /*[Replaceable]*/ readonly attribute Performance performance;
+};
+
+// http://dev.w3.org/csswg/cssom-view/#extensions-to-the-window-interface
+partial interface Window {
+ //MediaQueryList matchMedia(DOMString query);
+ [SameObject] readonly attribute Screen screen;
+
+ // browsing context
+ //void moveTo(double x, double y);
+ //void moveBy(double x, double y);
+ //void resizeTo(double x, double y);
+ //void resizeBy(double x, double y);
+
+ // viewport
+ //readonly attribute double innerWidth;
+ //readonly attribute double innerHeight;
+
+ // viewport scrolling
+ //readonly attribute double scrollX;
+ //readonly attribute double pageXOffset;
+ //readonly attribute double scrollY;
+ //readonly attribute double pageYOffset;
+ //void scroll(double x, double y, optional ScrollOptions options);
+ //void scrollTo(double x, double y, optional ScrollOptions options);
+ //void scrollBy(double x, double y, optional ScrollOptions options);
+
+ // client
+ //readonly attribute double screenX;
+ //readonly attribute double screenY;
+ //readonly attribute double outerWidth;
+ //readonly attribute double outerHeight;
+ //readonly attribute double devicePixelRatio;
+};
+
+// Proprietary extensions.
+partial interface Window {
+ readonly attribute Console console;
+ void debug(DOMString arg);
+ void gc();
+};
+Window implements OnErrorEventHandlerForWindow;
diff --git a/components/script/dom/webidls/Worker.webidl b/components/script/dom/webidls/Worker.webidl
new file mode 100644
index 00000000000..2228c203781
--- /dev/null
+++ b/components/script/dom/webidls/Worker.webidl
@@ -0,0 +1,20 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#abstractworker
+[NoInterfaceObject/*, Exposed=Window,Worker*/]
+interface AbstractWorker {
+ // attribute EventHandler onerror;
+};
+
+// http://www.whatwg.org/html/#worker
+[Constructor(DOMString scriptURL)/*, Exposed=Window,Worker*/]
+interface Worker : EventTarget {
+ //void terminate();
+
+ void postMessage(any message/*, optional sequence<Transferable> transfer*/);
+ attribute EventHandler onmessage;
+};
+Worker implements AbstractWorker;
diff --git a/components/script/dom/webidls/WorkerGlobalScope.webidl b/components/script/dom/webidls/WorkerGlobalScope.webidl
new file mode 100644
index 00000000000..9c50682b056
--- /dev/null
+++ b/components/script/dom/webidls/WorkerGlobalScope.webidl
@@ -0,0 +1,32 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#workerglobalscope
+//[Exposed=Worker]
+interface WorkerGlobalScope : EventTarget {
+ readonly attribute WorkerGlobalScope self;
+ readonly attribute WorkerLocation location;
+
+ //void close();
+ // attribute OnErrorEventHandler onerror;
+ // attribute EventHandler onlanguagechange;
+ // attribute EventHandler onoffline;
+ // attribute EventHandler ononline;
+};
+
+// http://www.whatwg.org/html/#WorkerGlobalScope-partial
+//[Exposed=Worker]
+partial interface WorkerGlobalScope { // not obsolete
+ [Throws]
+ void importScripts(DOMString... urls);
+ readonly attribute WorkerNavigator navigator;
+};
+//WorkerGlobalScope implements WindowTimers;
+//WorkerGlobalScope implements WindowBase64;
+
+// Proprietary
+partial interface WorkerGlobalScope {
+ [Replaceable]
+ readonly attribute Console console;
+};
diff --git a/components/script/dom/webidls/WorkerLocation.webidl b/components/script/dom/webidls/WorkerLocation.webidl
new file mode 100644
index 00000000000..04d4c8e5cc7
--- /dev/null
+++ b/components/script/dom/webidls/WorkerLocation.webidl
@@ -0,0 +1,9 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#worker-locations
+//[Exposed=Worker]
+interface WorkerLocation { };
+WorkerLocation implements URLUtilsReadOnly;
diff --git a/components/script/dom/webidls/WorkerNavigator.webidl b/components/script/dom/webidls/WorkerNavigator.webidl
new file mode 100644
index 00000000000..aa8e19342e4
--- /dev/null
+++ b/components/script/dom/webidls/WorkerNavigator.webidl
@@ -0,0 +1,11 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// http://www.whatwg.org/html/#workernavigator
+//[Exposed=Worker]
+interface WorkerNavigator {};
+WorkerNavigator implements NavigatorID;
+//WorkerNavigator implements NavigatorLanguage;
+//WorkerNavigator implements NavigatorOnLine;
diff --git a/components/script/dom/webidls/XMLHttpRequest.webidl b/components/script/dom/webidls/XMLHttpRequest.webidl
new file mode 100644
index 00000000000..ba100ca23ad
--- /dev/null
+++ b/components/script/dom/webidls/XMLHttpRequest.webidl
@@ -0,0 +1,72 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * http://xhr.spec.whatwg.org/#interface-xmlhttprequest
+ *
+ * To the extent possible under law, the editor has waived all copyright
+ * and related or neighboring rights to this work. In addition, as of 1 May 2014,
+ * the editor has made this specification available under the Open Web Foundation
+ * Agreement Version 1.0, which is available at
+ * http://www.openwebfoundation.org/legal/the-owf-1-0-agreements/owfa-1-0.
+ */
+
+// http://fetch.spec.whatwg.org/#fetchbodyinit
+typedef (/*ArrayBuffer or ArrayBufferView or Blob or FormData or */DOMString or URLSearchParams) FetchBodyInit;
+
+enum XMLHttpRequestResponseType {
+ "",
+ "arraybuffer",
+ "blob",
+ "document",
+ "json",
+ "text"
+};
+
+[Constructor/*,
+ Exposed=Window,Worker*/]
+interface XMLHttpRequest : XMLHttpRequestEventTarget {
+ // event handler
+ attribute EventHandler onreadystatechange;
+
+ // states
+ const unsigned short UNSENT = 0;
+ const unsigned short OPENED = 1;
+ const unsigned short HEADERS_RECEIVED = 2;
+ const unsigned short LOADING = 3;
+ const unsigned short DONE = 4;
+
+ readonly attribute unsigned short readyState;
+
+ // request
+ [Throws]
+ void open(ByteString method, /* [EnsureUTF16] */ DOMString url);
+ [Throws]
+ void open(ByteString method, /* [EnsureUTF16] */ DOMString url, boolean async, optional /* [EnsureUTF16] */ DOMString? username = null, optional /* [EnsureUTF16] */ DOMString? password = null);
+
+ [Throws]
+ void setRequestHeader(ByteString name, ByteString value);
+ [SetterThrows]
+ attribute unsigned long timeout;
+ attribute boolean withCredentials;
+ readonly attribute XMLHttpRequestUpload upload;
+ [Throws]
+ void send(optional /*Document or*/ FetchBodyInit? data = null);
+ void abort();
+
+ // response
+ readonly attribute DOMString responseURL;
+ readonly attribute unsigned short status;
+ readonly attribute ByteString statusText;
+ ByteString? getResponseHeader(ByteString name);
+ ByteString getAllResponseHeaders();
+ // void overrideMimeType(DOMString mime);
+ [SetterThrows]
+ attribute XMLHttpRequestResponseType responseType;
+ readonly attribute any response;
+ [Throws]
+ readonly attribute DOMString responseText;
+ /*[Exposed=Window]*/ readonly attribute Document? responseXML;
+};
diff --git a/components/script/dom/webidls/XMLHttpRequestEventTarget.webidl b/components/script/dom/webidls/XMLHttpRequestEventTarget.webidl
new file mode 100644
index 00000000000..0d772edca0b
--- /dev/null
+++ b/components/script/dom/webidls/XMLHttpRequestEventTarget.webidl
@@ -0,0 +1,26 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * http://xhr.spec.whatwg.org/#interface-xmlhttprequest
+ *
+ * To the extent possible under law, the editor has waived all copyright
+ * and related or neighboring rights to this work. In addition, as of 1 May 2014,
+ * the editor has made this specification available under the Open Web Foundation
+ * Agreement Version 1.0, which is available at
+ * http://www.openwebfoundation.org/legal/the-owf-1-0-agreements/owfa-1-0.
+ */
+
+[NoInterfaceObject]
+interface XMLHttpRequestEventTarget : EventTarget {
+ // event handlers
+ attribute EventHandler onloadstart;
+ attribute EventHandler onprogress;
+ attribute EventHandler onabort;
+ attribute EventHandler onerror;
+ attribute EventHandler onload;
+ attribute EventHandler ontimeout;
+ attribute EventHandler onloadend;
+};
diff --git a/components/script/dom/webidls/XMLHttpRequestUpload.webidl b/components/script/dom/webidls/XMLHttpRequestUpload.webidl
new file mode 100644
index 00000000000..9ff8b4cc8e6
--- /dev/null
+++ b/components/script/dom/webidls/XMLHttpRequestUpload.webidl
@@ -0,0 +1,18 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * http://xhr.spec.whatwg.org/#interface-xmlhttprequest
+ *
+ * To the extent possible under law, the editor has waived all copyright
+ * and related or neighboring rights to this work. In addition, as of 1 May 2014,
+ * the editor has made this specification available under the Open Web Foundation
+ * Agreement Version 1.0, which is available at
+ * http://www.openwebfoundation.org/legal/the-owf-1-0-agreements/owfa-1-0.
+ */
+
+//[Exposed=Window,Worker]
+interface XMLHttpRequestUpload : XMLHttpRequestEventTarget {
+};
diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs
new file mode 100644
index 00000000000..23b6c71e029
--- /dev/null
+++ b/components/script/dom/window.rs
@@ -0,0 +1,513 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::EventHandlerBinding::{OnErrorEventHandlerNonNull, EventHandlerNonNull};
+use dom::bindings::codegen::Bindings::WindowBinding;
+use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
+use dom::bindings::codegen::InheritTypes::EventTargetCast;
+use dom::bindings::error::{Fallible, InvalidCharacter};
+use dom::bindings::global;
+use dom::bindings::js::{JS, JSRef, Temporary, OptionalSettable};
+use dom::bindings::trace::{Traceable, Untraceable};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::browsercontext::BrowserContext;
+use dom::console::Console;
+use dom::document::Document;
+use dom::eventtarget::{EventTarget, WindowTypeId, EventTargetHelpers};
+use dom::location::Location;
+use dom::navigator::Navigator;
+use dom::performance::Performance;
+use dom::screen::Screen;
+use layout_interface::{ReflowForDisplay, DocumentDamageLevel};
+use page::Page;
+use script_task::{ExitWindowMsg, FireTimerMsg, ScriptChan, TriggerLoadMsg, TriggerFragmentMsg};
+use script_traits::ScriptControlChan;
+
+use servo_msg::compositor_msg::ScriptListener;
+use servo_net::image_cache_task::ImageCacheTask;
+use servo_util::str::{DOMString,HTML_SPACE_CHARACTERS};
+use servo_util::task::{spawn_named};
+
+use js::jsapi::JS_CallFunctionValue;
+use js::jsapi::JSContext;
+use js::jsapi::{JS_GC, JS_GetRuntime};
+use js::jsval::JSVal;
+use js::jsval::NullValue;
+use js::rust::with_compartment;
+use url::{Url, UrlParser};
+
+use serialize::base64::{FromBase64, ToBase64, STANDARD};
+use std::collections::hashmap::HashMap;
+use std::cell::{Cell, RefCell};
+use std::cmp;
+use std::comm::{channel, Sender};
+use std::comm::Select;
+use std::hash::{Hash, sip};
+use std::io::timer::Timer;
+use std::ptr;
+use std::rc::Rc;
+use time;
+
+#[deriving(PartialEq, Encodable, Eq)]
+pub struct TimerId(i32);
+
+#[deriving(Encodable)]
+pub struct TimerHandle {
+ handle: TimerId,
+ pub data: TimerData,
+ cancel_chan: Untraceable<Option<Sender<()>>>,
+}
+
+impl Hash for TimerId {
+ fn hash(&self, state: &mut sip::SipState) {
+ let TimerId(id) = *self;
+ id.hash(state);
+ }
+}
+
+impl TimerHandle {
+ fn cancel(&mut self) {
+ self.cancel_chan.as_ref().map(|chan| chan.send_opt(()).ok());
+ }
+}
+
+#[deriving(Encodable)]
+pub struct Window {
+ eventtarget: EventTarget,
+ pub script_chan: ScriptChan,
+ control_chan: ScriptControlChan,
+ console: Cell<Option<JS<Console>>>,
+ location: Cell<Option<JS<Location>>>,
+ navigator: Cell<Option<JS<Navigator>>>,
+ pub image_cache_task: ImageCacheTask,
+ pub active_timers: Traceable<RefCell<HashMap<TimerId, TimerHandle>>>,
+ next_timer_handle: Traceable<Cell<i32>>,
+ compositor: Untraceable<Box<ScriptListener>>,
+ pub browser_context: Traceable<RefCell<Option<BrowserContext>>>,
+ pub page: Rc<Page>,
+ performance: Cell<Option<JS<Performance>>>,
+ pub navigationStart: u64,
+ pub navigationStartPrecise: f64,
+ screen: Cell<Option<JS<Screen>>>,
+}
+
+impl Window {
+ pub fn get_cx(&self) -> *mut JSContext {
+ let js_info = self.page().js_info();
+ (**js_info.get_ref().js_context).ptr
+ }
+
+ pub fn page<'a>(&'a self) -> &'a Page {
+ &*self.page
+ }
+ pub fn get_url(&self) -> Url {
+ self.page().get_url()
+ }
+}
+
+#[unsafe_destructor]
+impl Drop for Window {
+ fn drop(&mut self) {
+ for (_, timer_handle) in self.active_timers.borrow_mut().mut_iter() {
+ timer_handle.cancel();
+ }
+ }
+}
+
+// Holder for the various JS values associated with setTimeout
+// (ie. function value to invoke and all arguments to pass
+// to the function when calling it)
+#[deriving(Encodable)]
+pub struct TimerData {
+ pub is_interval: bool,
+ pub funval: Traceable<JSVal>,
+}
+
+impl<'a> WindowMethods for JSRef<'a, Window> {
+ fn Alert(&self, s: DOMString) {
+ // Right now, just print to the console
+ println!("ALERT: {:s}", s);
+ }
+
+ fn Close(&self) {
+ let ScriptChan(ref chan) = self.script_chan;
+ chan.send(ExitWindowMsg(self.page.id.clone()));
+ }
+
+ fn Document(&self) -> Temporary<Document> {
+ let frame = self.page().frame();
+ Temporary::new(frame.get_ref().document.clone())
+ }
+
+ fn Location(&self) -> Temporary<Location> {
+ if self.location.get().is_none() {
+ let page = self.deref().page.clone();
+ let location = Location::new(self, page);
+ self.location.assign(Some(location));
+ }
+ Temporary::new(self.location.get().get_ref().clone())
+ }
+
+ fn Console(&self) -> Temporary<Console> {
+ if self.console.get().is_none() {
+ let console = Console::new(&global::Window(*self));
+ self.console.assign(Some(console));
+ }
+ Temporary::new(self.console.get().get_ref().clone())
+ }
+
+ fn Navigator(&self) -> Temporary<Navigator> {
+ if self.navigator.get().is_none() {
+ let navigator = Navigator::new(self);
+ self.navigator.assign(Some(navigator));
+ }
+ Temporary::new(self.navigator.get().get_ref().clone())
+ }
+
+ fn SetTimeout(&self, _cx: *mut JSContext, callback: JSVal, timeout: i32) -> i32 {
+ self.set_timeout_or_interval(callback, timeout, false)
+ }
+
+ fn ClearTimeout(&self, handle: i32) {
+ let mut timers = self.active_timers.deref().borrow_mut();
+ let mut timer_handle = timers.pop(&TimerId(handle));
+ match timer_handle {
+ Some(ref mut handle) => handle.cancel(),
+ None => { }
+ }
+ timers.remove(&TimerId(handle));
+ }
+
+ fn SetInterval(&self, _cx: *mut JSContext, callback: JSVal, timeout: i32) -> i32 {
+ self.set_timeout_or_interval(callback, timeout, true)
+ }
+
+ fn ClearInterval(&self, handle: i32) {
+ self.ClearTimeout(handle);
+ }
+
+ fn Window(&self) -> Temporary<Window> {
+ Temporary::from_rooted(self)
+ }
+
+ fn Self(&self) -> Temporary<Window> {
+ self.Window()
+ }
+
+ // http://www.whatwg.org/html/#dom-frames
+ fn Frames(&self) -> Temporary<Window> {
+ self.Window()
+ }
+
+ fn Parent(&self) -> Temporary<Window> {
+ //TODO - Once we support iframes correctly this needs to return the parent frame
+ self.Window()
+ }
+
+ fn Performance(&self) -> Temporary<Performance> {
+ if self.performance.get().is_none() {
+ let performance = Performance::new(self);
+ self.performance.assign(Some(performance));
+ }
+ Temporary::new(self.performance.get().get_ref().clone())
+ }
+
+ fn GetOnclick(&self) -> Option<EventHandlerNonNull> {
+ let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self);
+ eventtarget.get_event_handler_common("click")
+ }
+
+ fn SetOnclick(&self, listener: Option<EventHandlerNonNull>) {
+ let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self);
+ eventtarget.set_event_handler_common("click", listener)
+ }
+
+ fn GetOnload(&self) -> Option<EventHandlerNonNull> {
+ let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self);
+ eventtarget.get_event_handler_common("load")
+ }
+
+ fn SetOnload(&self, listener: Option<EventHandlerNonNull>) {
+ let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self);
+ eventtarget.set_event_handler_common("load", listener)
+ }
+
+ fn GetOnunload(&self) -> Option<EventHandlerNonNull> {
+ let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self);
+ eventtarget.get_event_handler_common("unload")
+ }
+
+ fn SetOnunload(&self, listener: Option<EventHandlerNonNull>) {
+ let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self);
+ eventtarget.set_event_handler_common("unload", listener)
+ }
+
+ fn GetOnerror(&self) -> Option<OnErrorEventHandlerNonNull> {
+ let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self);
+ eventtarget.get_event_handler_common("error")
+ }
+
+ fn SetOnerror(&self, listener: Option<OnErrorEventHandlerNonNull>) {
+ let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self);
+ eventtarget.set_event_handler_common("error", listener)
+ }
+
+ fn Screen(&self) -> Temporary<Screen> {
+ if self.screen.get().is_none() {
+ let screen = Screen::new(self);
+ self.screen.assign(Some(screen));
+ }
+ Temporary::new(self.screen.get().get_ref().clone())
+ }
+
+ fn Debug(&self, message: DOMString) {
+ debug!("{:s}", message);
+ }
+
+ fn Gc(&self) {
+ unsafe {
+ JS_GC(JS_GetRuntime(self.get_cx()));
+ }
+ }
+
+ // http://www.whatwg.org/html/#atob
+ fn Btoa(&self, btoa: DOMString) -> Fallible<DOMString> {
+ let input = btoa.as_slice();
+ // "The btoa() method must throw an InvalidCharacterError exception if
+ // the method's first argument contains any character whose code point
+ // is greater than U+00FF."
+ if input.chars().any(|c: char| c > '\u00FF') {
+ Err(InvalidCharacter)
+ } else {
+ // "Otherwise, the user agent must convert that argument to a
+ // sequence of octets whose nth octet is the eight-bit
+ // representation of the code point of the nth character of
+ // the argument,"
+ let octets = input.chars().map(|c: char| c as u8).collect::<Vec<u8>>();
+
+ // "and then must apply the base64 algorithm to that sequence of
+ // octets, and return the result. [RFC4648]"
+ Ok(octets.as_slice().to_base64(STANDARD))
+ }
+ }
+
+ // http://www.whatwg.org/html/#atob
+ fn Atob(&self, atob: DOMString) -> Fallible<DOMString> {
+ // "Let input be the string being parsed."
+ let mut input = atob.as_slice();
+
+ // "Remove all space characters from input."
+ // serialize::base64::from_base64 ignores \r and \n,
+ // but it treats the other space characters as
+ // invalid input.
+ fn is_html_space(c: char) -> bool {
+ HTML_SPACE_CHARACTERS.iter().any(|&m| m == c)
+ }
+ let without_spaces = input.chars()
+ .filter(|&c| ! is_html_space(c))
+ .collect::<String>();
+ input = without_spaces.as_slice();
+
+ // "If the length of input divides by 4 leaving no remainder, then:
+ // if input ends with one or two U+003D EQUALS SIGN (=) characters,
+ // remove them from input."
+ if input.len() % 4 == 0 {
+ if input.ends_with("==") {
+ input = input.slice_to(input.len() - 2)
+ } else if input.ends_with("=") {
+ input = input.slice_to(input.len() - 1)
+ }
+ }
+
+ // "If the length of input divides by 4 leaving a remainder of 1,
+ // throw an InvalidCharacterError exception and abort these steps."
+ if input.len() % 4 == 1 {
+ return Err(InvalidCharacter)
+ }
+
+ // "If input contains a character that is not in the following list of
+ // characters and character ranges, throw an InvalidCharacterError
+ // exception and abort these steps:
+ //
+ // U+002B PLUS SIGN (+)
+ // U+002F SOLIDUS (/)
+ // Alphanumeric ASCII characters"
+ if input.chars()
+ .find(|&c| !(c == '+' || c == '/' || c.is_alphanumeric()))
+ .is_some() {
+ return Err(InvalidCharacter)
+ }
+
+ match input.from_base64() {
+ Ok(data) => Ok(data.iter().map(|&b| b as char).collect::<String>()),
+ Err(..) => Err(InvalidCharacter)
+ }
+ }
+}
+
+impl Reflectable for Window {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.eventtarget.reflector()
+ }
+}
+
+pub trait WindowHelpers {
+ fn damage_and_reflow(&self, damage: DocumentDamageLevel);
+ fn wait_until_safe_to_modify_dom(&self);
+ fn init_browser_context(&self, doc: &JSRef<Document>);
+ fn load_url(&self, href: DOMString);
+ fn handle_fire_timer(&self, timer_id: TimerId, cx: *mut JSContext);
+}
+
+trait PrivateWindowHelpers {
+ fn set_timeout_or_interval(&self, callback: JSVal, timeout: i32, is_interval: bool) -> i32;
+}
+
+impl<'a> WindowHelpers for JSRef<'a, Window> {
+ fn damage_and_reflow(&self, damage: DocumentDamageLevel) {
+ // FIXME This should probably be ReflowForQuery, not Display. All queries currently
+ // currently rely on the display list, which means we can't destroy it by
+ // doing a query reflow.
+ self.page().damage(damage);
+ self.page().reflow(ReflowForDisplay, self.control_chan.clone(), *self.compositor);
+ }
+
+ fn wait_until_safe_to_modify_dom(&self) {
+ // FIXME: This disables concurrent layout while we are modifying the DOM, since
+ // our current architecture is entirely unsafe in the presence of races.
+ self.page().join_layout();
+ }
+
+ fn init_browser_context(&self, doc: &JSRef<Document>) {
+ *self.browser_context.deref().borrow_mut() = Some(BrowserContext::new(doc));
+ }
+
+ /// Commence a new URL load which will either replace this window or scroll to a fragment.
+ fn load_url(&self, href: DOMString) {
+ let base_url = self.page().get_url();
+ debug!("current page url is {:?}", base_url);
+ let url = UrlParser::new().base_url(&base_url).parse(href.as_slice());
+ // FIXME: handle URL parse errors more gracefully.
+ let url = url.unwrap();
+ let ScriptChan(ref script_chan) = self.script_chan;
+ if href.as_slice().starts_with("#") {
+ script_chan.send(TriggerFragmentMsg(self.page.id, url));
+ } else {
+ script_chan.send(TriggerLoadMsg(self.page.id, url));
+ }
+ }
+
+ fn handle_fire_timer(&self, timer_id: TimerId, cx: *mut JSContext) {
+ let this_value = self.reflector().get_jsobject();
+
+ let data = match self.active_timers.deref().borrow().find(&timer_id) {
+ None => return,
+ Some(timer_handle) => timer_handle.data,
+ };
+
+ // TODO: Support extra arguments. This requires passing a `*JSVal` array as `argv`.
+ with_compartment(cx, this_value, || {
+ let mut rval = NullValue();
+ unsafe {
+ JS_CallFunctionValue(cx, this_value, *data.funval,
+ 0, ptr::mut_null(), &mut rval);
+ }
+ });
+
+ if !data.is_interval {
+ self.active_timers.deref().borrow_mut().remove(&timer_id);
+ }
+ }
+}
+
+impl<'a> PrivateWindowHelpers for JSRef<'a, Window> {
+ fn set_timeout_or_interval(&self, callback: JSVal, timeout: i32, is_interval: bool) -> i32 {
+ let timeout = cmp::max(0, timeout) as u64;
+ let handle = self.next_timer_handle.deref().get();
+ self.next_timer_handle.deref().set(handle + 1);
+
+ // Post a delayed message to the per-window timer task; it will dispatch it
+ // to the relevant script handler that will deal with it.
+ let tm = Timer::new().unwrap();
+ let (cancel_chan, cancel_port) = channel();
+ let chan = self.script_chan.clone();
+ let page_id = self.page.id.clone();
+ let spawn_name = if is_interval {
+ "Window:SetInterval"
+ } else {
+ "Window:SetTimeout"
+ };
+ spawn_named(spawn_name, proc() {
+ let mut tm = tm;
+ let timeout_port = if is_interval {
+ tm.periodic(timeout)
+ } else {
+ tm.oneshot(timeout)
+ };
+ let cancel_port = cancel_port;
+
+ let select = Select::new();
+ let mut timeout_handle = select.handle(&timeout_port);
+ unsafe { timeout_handle.add() };
+ let mut cancel_handle = select.handle(&cancel_port);
+ unsafe { cancel_handle.add() };
+
+ loop {
+ let id = select.wait();
+ if id == timeout_handle.id() {
+ timeout_port.recv();
+ let ScriptChan(ref chan) = chan;
+ chan.send(FireTimerMsg(page_id, TimerId(handle)));
+ if !is_interval {
+ break;
+ }
+ } else if id == cancel_handle.id() {
+ break;
+ }
+ }
+ });
+ let timer_id = TimerId(handle);
+ let timer = TimerHandle {
+ handle: timer_id,
+ cancel_chan: Untraceable::new(Some(cancel_chan)),
+ data: TimerData {
+ is_interval: is_interval,
+ funval: Traceable::new(callback),
+ }
+ };
+ self.active_timers.deref().borrow_mut().insert(timer_id, timer);
+ handle
+ }
+}
+
+impl Window {
+ pub fn new(cx: *mut JSContext,
+ page: Rc<Page>,
+ script_chan: ScriptChan,
+ control_chan: ScriptControlChan,
+ compositor: Box<ScriptListener>,
+ image_cache_task: ImageCacheTask)
+ -> Temporary<Window> {
+ let win = box Window {
+ eventtarget: EventTarget::new_inherited(WindowTypeId),
+ script_chan: script_chan,
+ control_chan: control_chan,
+ console: Cell::new(None),
+ compositor: Untraceable::new(compositor),
+ page: page,
+ location: Cell::new(None),
+ navigator: Cell::new(None),
+ image_cache_task: image_cache_task,
+ active_timers: Traceable::new(RefCell::new(HashMap::new())),
+ next_timer_handle: Traceable::new(Cell::new(0)),
+ browser_context: Traceable::new(RefCell::new(None)),
+ performance: Cell::new(None),
+ navigationStart: time::get_time().sec as u64,
+ navigationStartPrecise: time::precise_time_s(),
+ screen: Cell::new(None),
+ };
+
+ WindowBinding::Wrap(cx, win)
+ }
+}
diff --git a/components/script/dom/worker.rs b/components/script/dom/worker.rs
new file mode 100644
index 00000000000..3119b4c96f2
--- /dev/null
+++ b/components/script/dom/worker.rs
@@ -0,0 +1,160 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::WorkerBinding;
+use dom::bindings::codegen::Bindings::WorkerBinding::WorkerMethods;
+use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
+use dom::bindings::codegen::InheritTypes::EventTargetCast;
+use dom::bindings::error::{Fallible, Syntax};
+use dom::bindings::global::{GlobalRef, GlobalField};
+use dom::bindings::js::{JS, JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
+use dom::dedicatedworkerglobalscope::DedicatedWorkerGlobalScope;
+use dom::eventtarget::{EventTarget, EventTargetHelpers, WorkerTypeId};
+use dom::messageevent::MessageEvent;
+use script_task::{ScriptChan, DOMMessage};
+
+use servo_util::str::DOMString;
+
+use js::glue::JS_STRUCTURED_CLONE_VERSION;
+use js::jsapi::{JSContext, JS_AddObjectRoot, JS_RemoveObjectRoot};
+use js::jsapi::{JS_ReadStructuredClone, JS_WriteStructuredClone};
+use js::jsval::{JSVal, UndefinedValue};
+use url::UrlParser;
+
+use libc::{c_void, size_t};
+use std::cell::Cell;
+use std::ptr;
+
+pub struct TrustedWorkerAddress(pub *const c_void);
+
+#[deriving(Encodable)]
+pub struct Worker {
+ eventtarget: EventTarget,
+ refcount: Cell<uint>,
+ global: GlobalField,
+ /// Sender to the Receiver associated with the DedicatedWorkerGlobalScope
+ /// this Worker created.
+ sender: ScriptChan,
+}
+
+impl Worker {
+ pub fn new_inherited(global: &GlobalRef, sender: ScriptChan) -> Worker {
+ Worker {
+ eventtarget: EventTarget::new_inherited(WorkerTypeId),
+ refcount: Cell::new(0),
+ global: GlobalField::from_rooted(global),
+ sender: sender,
+ }
+ }
+
+ pub fn new(global: &GlobalRef, sender: ScriptChan) -> Temporary<Worker> {
+ reflect_dom_object(box Worker::new_inherited(global, sender),
+ global,
+ WorkerBinding::Wrap)
+ }
+
+ // http://www.whatwg.org/html/#dom-worker
+ pub fn Constructor(global: &GlobalRef, scriptURL: DOMString) -> Fallible<Temporary<Worker>> {
+ // Step 2-4.
+ let worker_url = match UrlParser::new().base_url(&global.get_url())
+ .parse(scriptURL.as_slice()) {
+ Ok(url) => url,
+ Err(_) => return Err(Syntax),
+ };
+
+ let resource_task = global.resource_task();
+ let (receiver, sender) = ScriptChan::new();
+
+ let worker = Worker::new(global, sender.clone()).root();
+ let worker_ref = worker.addref();
+
+ DedicatedWorkerGlobalScope::run_worker_scope(
+ worker_url, worker_ref, resource_task, global.script_chan().clone(),
+ sender, receiver);
+
+ Ok(Temporary::from_rooted(&*worker))
+ }
+
+ pub fn handle_message(address: TrustedWorkerAddress,
+ data: *mut u64, nbytes: size_t) {
+ let worker = unsafe { JS::from_trusted_worker_address(address).root() };
+
+ let global = worker.global.root();
+
+ let mut message = UndefinedValue();
+ unsafe {
+ assert!(JS_ReadStructuredClone(
+ global.root_ref().get_cx(), data as *const u64, nbytes,
+ JS_STRUCTURED_CLONE_VERSION, &mut message,
+ ptr::null(), ptr::mut_null()) != 0);
+ }
+
+ let target: &JSRef<EventTarget> = EventTargetCast::from_ref(&*worker);
+ MessageEvent::dispatch_jsval(target, &global.root_ref(), message);
+ }
+}
+
+impl Worker {
+ // Creates a trusted address to the object, and roots it. Always pair this with a release()
+ pub fn addref(&self) -> TrustedWorkerAddress {
+ let refcount = self.refcount.get();
+ if refcount == 0 {
+ let cx = self.global.root().root_ref().get_cx();
+ unsafe {
+ JS_AddObjectRoot(cx, self.reflector().rootable());
+ }
+ }
+ self.refcount.set(refcount + 1);
+ TrustedWorkerAddress(self as *const Worker as *const c_void)
+ }
+
+ pub fn release(&self) {
+ let refcount = self.refcount.get();
+ assert!(refcount > 0)
+ self.refcount.set(refcount - 1);
+ if refcount == 1 {
+ let cx = self.global.root().root_ref().get_cx();
+ unsafe {
+ JS_RemoveObjectRoot(cx, self.reflector().rootable());
+ }
+ }
+ }
+
+ pub fn handle_release(address: TrustedWorkerAddress) {
+ let worker = unsafe { JS::from_trusted_worker_address(address).root() };
+ worker.release();
+ }
+}
+
+impl<'a> WorkerMethods for JSRef<'a, Worker> {
+ fn PostMessage(&self, cx: *mut JSContext, message: JSVal) {
+ let mut data = ptr::mut_null();
+ let mut nbytes = 0;
+ unsafe {
+ assert!(JS_WriteStructuredClone(cx, message, &mut data, &mut nbytes,
+ ptr::null(), ptr::mut_null()) != 0);
+ }
+
+ self.addref();
+ let ScriptChan(ref sender) = self.sender;
+ sender.send(DOMMessage(data, nbytes));
+ }
+
+ fn GetOnmessage(&self) -> Option<EventHandlerNonNull> {
+ let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self);
+ eventtarget.get_event_handler_common("message")
+ }
+
+ fn SetOnmessage(&self, listener: Option<EventHandlerNonNull>) {
+ let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self);
+ eventtarget.set_event_handler_common("message", listener)
+ }
+}
+
+impl Reflectable for Worker {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.eventtarget.reflector()
+ }
+}
diff --git a/components/script/dom/workerglobalscope.rs b/components/script/dom/workerglobalscope.rs
new file mode 100644
index 00000000000..dcf205cf3a9
--- /dev/null
+++ b/components/script/dom/workerglobalscope.rs
@@ -0,0 +1,145 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::WorkerGlobalScopeBinding::WorkerGlobalScopeMethods;
+use dom::bindings::error::{ErrorResult, Syntax, Network, FailureUnknown};
+use dom::bindings::trace::Untraceable;
+use dom::bindings::global;
+use dom::bindings::js::{JS, JSRef, Temporary, OptionalSettable};
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::console::Console;
+use dom::eventtarget::{EventTarget, WorkerGlobalScopeTypeId};
+use dom::workerlocation::WorkerLocation;
+use dom::workernavigator::WorkerNavigator;
+use script_task::ScriptChan;
+
+use servo_net::resource_task::{ResourceTask, load_whole_resource};
+use servo_util::str::DOMString;
+
+use js::jsapi::JSContext;
+use js::rust::Cx;
+
+use std::cell::Cell;
+use std::rc::Rc;
+use url::{Url, UrlParser};
+
+#[deriving(PartialEq,Encodable)]
+pub enum WorkerGlobalScopeId {
+ DedicatedGlobalScope,
+}
+
+#[deriving(Encodable)]
+pub struct WorkerGlobalScope {
+ pub eventtarget: EventTarget,
+ worker_url: Untraceable<Url>,
+ js_context: Untraceable<Rc<Cx>>,
+ resource_task: Untraceable<ResourceTask>,
+ script_chan: ScriptChan,
+ location: Cell<Option<JS<WorkerLocation>>>,
+ navigator: Cell<Option<JS<WorkerNavigator>>>,
+ console: Cell<Option<JS<Console>>>,
+}
+
+impl WorkerGlobalScope {
+ pub fn new_inherited(type_id: WorkerGlobalScopeId,
+ worker_url: Url,
+ cx: Rc<Cx>,
+ resource_task: ResourceTask,
+ script_chan: ScriptChan) -> WorkerGlobalScope {
+ WorkerGlobalScope {
+ eventtarget: EventTarget::new_inherited(WorkerGlobalScopeTypeId(type_id)),
+ worker_url: Untraceable::new(worker_url),
+ js_context: Untraceable::new(cx),
+ resource_task: Untraceable::new(resource_task),
+ script_chan: script_chan,
+ location: Cell::new(None),
+ navigator: Cell::new(None),
+ console: Cell::new(None),
+ }
+ }
+
+ pub fn get_cx(&self) -> *mut JSContext {
+ self.js_context.ptr
+ }
+
+ pub fn resource_task<'a>(&'a self) -> &'a ResourceTask {
+ &*self.resource_task
+ }
+
+ pub fn get_url<'a>(&'a self) -> &'a Url {
+ &*self.worker_url
+ }
+
+ pub fn script_chan<'a>(&'a self) -> &'a ScriptChan {
+ &self.script_chan
+ }
+}
+
+impl<'a> WorkerGlobalScopeMethods for JSRef<'a, WorkerGlobalScope> {
+ fn Self(&self) -> Temporary<WorkerGlobalScope> {
+ Temporary::from_rooted(self)
+ }
+
+ fn Location(&self) -> Temporary<WorkerLocation> {
+ if self.location.get().is_none() {
+ let location = WorkerLocation::new(self, self.worker_url.clone());
+ self.location.assign(Some(location));
+ }
+ Temporary::new(self.location.get().get_ref().clone())
+ }
+
+ fn ImportScripts(&self, url_strings: Vec<DOMString>) -> ErrorResult {
+ let mut urls = Vec::with_capacity(url_strings.len());
+ for url in url_strings.move_iter() {
+ let url = UrlParser::new().base_url(&*self.worker_url)
+ .parse(url.as_slice());
+ match url {
+ Ok(url) => urls.push(url),
+ Err(_) => return Err(Syntax),
+ };
+ }
+
+ for url in urls.move_iter() {
+ let (url, source) = match load_whole_resource(&*self.resource_task, url) {
+ Err(_) => return Err(Network),
+ Ok((metadata, bytes)) => {
+ (metadata.final_url, String::from_utf8(bytes).unwrap())
+ }
+ };
+
+ match self.js_context.evaluate_script(
+ self.reflector().get_jsobject(), source, url.serialize(), 1) {
+ Ok(_) => (),
+ Err(_) => {
+ println!("evaluate_script failed");
+ return Err(FailureUnknown);
+ }
+ }
+ }
+
+ Ok(())
+ }
+
+ fn Navigator(&self) -> Temporary<WorkerNavigator> {
+ if self.navigator.get().is_none() {
+ let navigator = WorkerNavigator::new(self);
+ self.navigator.assign(Some(navigator));
+ }
+ Temporary::new(self.navigator.get().get_ref().clone())
+ }
+
+ fn Console(&self) -> Temporary<Console> {
+ if self.console.get().is_none() {
+ let console = Console::new(&global::Worker(*self));
+ self.console.assign(Some(console));
+ }
+ Temporary::new(self.console.get().get_ref().clone())
+ }
+}
+
+impl Reflectable for WorkerGlobalScope {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.eventtarget.reflector()
+ }
+}
diff --git a/components/script/dom/workerlocation.rs b/components/script/dom/workerlocation.rs
new file mode 100644
index 00000000000..0d32c211554
--- /dev/null
+++ b/components/script/dom/workerlocation.rs
@@ -0,0 +1,64 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::WorkerLocationBinding;
+use dom::bindings::codegen::Bindings::WorkerLocationBinding::WorkerLocationMethods;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::global::Worker;
+use dom::bindings::trace::Untraceable;
+use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
+use dom::workerglobalscope::WorkerGlobalScope;
+
+use servo_util::str::DOMString;
+
+use url::Url;
+
+#[deriving(Encodable)]
+pub struct WorkerLocation {
+ reflector_: Reflector,
+ url: Untraceable<Url>,
+}
+
+impl WorkerLocation {
+ pub fn new_inherited(url: Url) -> WorkerLocation {
+ WorkerLocation {
+ reflector_: Reflector::new(),
+ url: Untraceable::new(url),
+ }
+ }
+
+ pub fn new(global: &JSRef<WorkerGlobalScope>, url: Url) -> Temporary<WorkerLocation> {
+ reflect_dom_object(box WorkerLocation::new_inherited(url),
+ &Worker(*global),
+ WorkerLocationBinding::Wrap)
+ }
+}
+
+impl<'a> WorkerLocationMethods for JSRef<'a, WorkerLocation> {
+ fn Href(&self) -> DOMString {
+ self.url.serialize()
+ }
+
+ fn Search(&self) -> DOMString {
+ match self.url.query {
+ None => "".to_string(),
+ Some(ref query) if query.as_slice() == "" => "".to_string(),
+ Some(ref query) => "?".to_string().append(query.as_slice())
+ }
+ }
+
+ fn Hash(&self) -> DOMString {
+ match self.url.fragment {
+ None => "".to_string(),
+ Some(ref hash) if hash.as_slice() == "" => "".to_string(),
+ Some(ref hash) => "#".to_string().append(hash.as_slice())
+ }
+ }
+}
+
+impl Reflectable for WorkerLocation {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ &self.reflector_
+ }
+}
diff --git a/components/script/dom/workernavigator.rs b/components/script/dom/workernavigator.rs
new file mode 100644
index 00000000000..e732696617d
--- /dev/null
+++ b/components/script/dom/workernavigator.rs
@@ -0,0 +1,58 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::WorkerNavigatorBinding;
+use dom::bindings::codegen::Bindings::WorkerNavigatorBinding::WorkerNavigatorMethods;
+use dom::bindings::global::Worker;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
+use dom::workerglobalscope::WorkerGlobalScope;
+use servo_util::str::DOMString;
+
+#[deriving(Encodable)]
+pub struct WorkerNavigator {
+ reflector_: Reflector,
+}
+
+impl WorkerNavigator {
+ pub fn new_inherited() -> WorkerNavigator {
+ WorkerNavigator {
+ reflector_: Reflector::new(),
+ }
+ }
+
+ pub fn new(global: &JSRef<WorkerGlobalScope>) -> Temporary<WorkerNavigator> {
+ reflect_dom_object(box WorkerNavigator::new_inherited(),
+ &Worker(*global),
+ WorkerNavigatorBinding::Wrap)
+ }
+}
+
+impl<'a> WorkerNavigatorMethods for JSRef<'a, WorkerNavigator> {
+ fn Product(&self) -> DOMString {
+ "Gecko".to_string()
+ }
+
+ fn TaintEnabled(&self) -> bool {
+ false
+ }
+
+ fn AppName(&self) -> DOMString {
+ "Netscape".to_string() // Like Gecko/Webkit
+ }
+
+ fn AppCodeName(&self) -> DOMString {
+ "Mozilla".to_string()
+ }
+
+ fn Platform(&self) -> DOMString {
+ "".to_string()
+ }
+}
+
+impl Reflectable for WorkerNavigator {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ &self.reflector_
+ }
+}
diff --git a/components/script/dom/xmlhttprequest.rs b/components/script/dom/xmlhttprequest.rs
new file mode 100644
index 00000000000..b2bfd64eb4e
--- /dev/null
+++ b/components/script/dom/xmlhttprequest.rs
@@ -0,0 +1,970 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
+use dom::bindings::codegen::Bindings::XMLHttpRequestBinding;
+use dom::bindings::codegen::Bindings::XMLHttpRequestBinding::XMLHttpRequestMethods;
+use dom::bindings::codegen::Bindings::XMLHttpRequestBinding::XMLHttpRequestResponseType;
+use dom::bindings::codegen::Bindings::XMLHttpRequestBinding::XMLHttpRequestResponseTypeValues::{_empty, Document, Json, Text};
+use dom::bindings::codegen::InheritTypes::{EventCast, EventTargetCast, XMLHttpRequestDerived};
+use dom::bindings::conversions::ToJSValConvertible;
+use dom::bindings::error::{Error, ErrorResult, Fallible, InvalidState, InvalidAccess};
+use dom::bindings::error::{Network, Syntax, Security, Abort, Timeout};
+use dom::bindings::global::{GlobalField, GlobalRef, WorkerField};
+use dom::bindings::js::{JS, JSRef, Temporary, OptionalRootedRootable};
+use dom::bindings::str::ByteString;
+use dom::bindings::trace::{Traceable, Untraceable};
+use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
+use dom::document::Document;
+use dom::event::Event;
+use dom::eventtarget::{EventTarget, EventTargetHelpers, XMLHttpRequestTargetTypeId};
+use dom::progressevent::ProgressEvent;
+use dom::urlsearchparams::URLSearchParamsHelpers;
+use dom::xmlhttprequesteventtarget::XMLHttpRequestEventTarget;
+use dom::xmlhttprequestupload::XMLHttpRequestUpload;
+
+use encoding::all::UTF_8;
+use encoding::label::encoding_from_whatwg_label;
+use encoding::types::{DecodeReplace, Encoding, EncodingRef, EncodeReplace};
+
+use ResponseHeaderCollection = http::headers::response::HeaderCollection;
+use RequestHeaderCollection = http::headers::request::HeaderCollection;
+use http::headers::content_type::MediaType;
+use http::headers::{HeaderEnum, HeaderValueByteIterator};
+use http::headers::request::Header;
+use http::method::{Method, Get, Head, Connect, Trace, ExtensionMethod};
+use http::status::Status;
+
+use js::jsapi::{JS_AddObjectRoot, JS_ParseJSON, JS_RemoveObjectRoot, JSContext};
+use js::jsapi::JS_ClearPendingException;
+use js::jsval::{JSVal, NullValue, UndefinedValue};
+
+use libc;
+use libc::c_void;
+
+use net::resource_task::{ResourceTask, ResourceCORSData, Load, LoadData, Payload, Done};
+use cors::{allow_cross_origin_request, CORSRequest, CORSMode, ForcedPreflightMode};
+use script_task::{ScriptChan, XHRProgressMsg};
+use servo_util::str::DOMString;
+use servo_util::task::spawn_named;
+
+use std::ascii::StrAsciiExt;
+use std::cell::{Cell, RefCell};
+use std::comm::{Sender, Receiver, channel};
+use std::io::{BufReader, MemWriter, Timer};
+use std::from_str::FromStr;
+use std::path::BytesContainer;
+use std::task::TaskBuilder;
+use time;
+use url::{Url, UrlParser};
+
+use dom::bindings::codegen::UnionTypes::StringOrURLSearchParams::{eString, eURLSearchParams, StringOrURLSearchParams};
+pub type SendParam = StringOrURLSearchParams;
+
+
+#[deriving(PartialEq,Encodable)]
+pub enum XMLHttpRequestId {
+ XMLHttpRequestTypeId,
+ XMLHttpRequestUploadTypeId
+}
+
+#[deriving(PartialEq, Encodable)]
+enum XMLHttpRequestState {
+ Unsent = 0,
+ Opened = 1,
+ HeadersReceived = 2,
+ Loading = 3,
+ XHRDone = 4, // So as not to conflict with the ProgressMsg `Done`
+}
+
+pub enum XHRProgress {
+ /// Notify that headers have been received
+ HeadersReceivedMsg(Option<ResponseHeaderCollection>, Status),
+ /// Partial progress (after receiving headers), containing portion of the response
+ LoadingMsg(ByteString),
+ /// Loading is done
+ DoneMsg,
+ /// There was an error (Abort or Timeout). For a network or other error, just pass None
+ ErroredMsg(Option<Error>),
+ /// Timeout was reached
+ TimeoutMsg
+}
+
+enum SyncOrAsync<'a, 'b> {
+ Sync(&'b JSRef<'a, XMLHttpRequest>),
+ Async(TrustedXHRAddress, ScriptChan)
+}
+
+
+#[deriving(Encodable)]
+pub struct XMLHttpRequest {
+ eventtarget: XMLHttpRequestEventTarget,
+ ready_state: Traceable<Cell<XMLHttpRequestState>>,
+ timeout: Traceable<Cell<u32>>,
+ with_credentials: Traceable<Cell<bool>>,
+ upload: JS<XMLHttpRequestUpload>,
+ response_url: DOMString,
+ status: Traceable<Cell<u16>>,
+ status_text: Traceable<RefCell<ByteString>>,
+ response: Traceable<RefCell<ByteString>>,
+ response_type: Traceable<Cell<XMLHttpRequestResponseType>>,
+ response_xml: Cell<Option<JS<Document>>>,
+ response_headers: Untraceable<RefCell<ResponseHeaderCollection>>,
+
+ // Associated concepts
+ request_method: Untraceable<RefCell<Method>>,
+ request_url: Untraceable<RefCell<Option<Url>>>,
+ request_headers: Untraceable<RefCell<RequestHeaderCollection>>,
+ request_body_len: Traceable<Cell<uint>>,
+ sync: Traceable<Cell<bool>>,
+ upload_complete: Traceable<Cell<bool>>,
+ upload_events: Traceable<Cell<bool>>,
+ send_flag: Traceable<Cell<bool>>,
+
+ global: GlobalField,
+ pinned_count: Traceable<Cell<uint>>,
+ timer: Untraceable<RefCell<Timer>>,
+ fetch_time: Traceable<Cell<i64>>,
+ timeout_pinned: Traceable<Cell<bool>>,
+ terminate_sender: Untraceable<RefCell<Option<Sender<Error>>>>,
+}
+
+impl XMLHttpRequest {
+ pub fn new_inherited(global: &GlobalRef) -> XMLHttpRequest {
+ let xhr = XMLHttpRequest {
+ eventtarget: XMLHttpRequestEventTarget::new_inherited(XMLHttpRequestTypeId),
+ ready_state: Traceable::new(Cell::new(Unsent)),
+ timeout: Traceable::new(Cell::new(0u32)),
+ with_credentials: Traceable::new(Cell::new(false)),
+ upload: JS::from_rooted(&XMLHttpRequestUpload::new(global)),
+ response_url: "".to_string(),
+ status: Traceable::new(Cell::new(0)),
+ status_text: Traceable::new(RefCell::new(ByteString::new(vec!()))),
+ response: Traceable::new(RefCell::new(ByteString::new(vec!()))),
+ response_type: Traceable::new(Cell::new(_empty)),
+ response_xml: Cell::new(None),
+ response_headers: Untraceable::new(RefCell::new(ResponseHeaderCollection::new())),
+
+ request_method: Untraceable::new(RefCell::new(Get)),
+ request_url: Untraceable::new(RefCell::new(None)),
+ request_headers: Untraceable::new(RefCell::new(RequestHeaderCollection::new())),
+ request_body_len: Traceable::new(Cell::new(0)),
+ sync: Traceable::new(Cell::new(false)),
+ send_flag: Traceable::new(Cell::new(false)),
+
+ upload_complete: Traceable::new(Cell::new(false)),
+ upload_events: Traceable::new(Cell::new(false)),
+
+ global: GlobalField::from_rooted(global),
+ pinned_count: Traceable::new(Cell::new(0)),
+ timer: Untraceable::new(RefCell::new(Timer::new().unwrap())),
+ fetch_time: Traceable::new(Cell::new(0)),
+ timeout_pinned: Traceable::new(Cell::new(false)),
+ terminate_sender: Untraceable::new(RefCell::new(None)),
+ };
+ xhr
+ }
+ pub fn new(global: &GlobalRef) -> Temporary<XMLHttpRequest> {
+ reflect_dom_object(box XMLHttpRequest::new_inherited(global),
+ global,
+ XMLHttpRequestBinding::Wrap)
+ }
+ pub fn Constructor(global: &GlobalRef) -> Fallible<Temporary<XMLHttpRequest>> {
+ Ok(XMLHttpRequest::new(global))
+ }
+
+ pub fn handle_xhr_progress(addr: TrustedXHRAddress, progress: XHRProgress) {
+ unsafe {
+ let xhr = JS::from_trusted_xhr_address(addr).root();
+ xhr.deref().process_partial_response(progress);
+ }
+ }
+
+ fn fetch(fetch_type: &SyncOrAsync, resource_task: ResourceTask,
+ mut load_data: LoadData, terminate_receiver: Receiver<Error>,
+ cors_request: Result<Option<CORSRequest>,()>) -> ErrorResult {
+ fn notify_partial_progress(fetch_type: &SyncOrAsync, msg: XHRProgress) {
+ match *fetch_type {
+ Sync(ref xhr) => {
+ xhr.process_partial_response(msg);
+ },
+ Async(addr, ref script_chan) => {
+ let ScriptChan(ref chan) = *script_chan;
+ chan.send(XHRProgressMsg(addr, msg));
+ }
+ }
+ }
+
+ match cors_request {
+ Err(_) => return Err(Network), // Happens in case of cross-origin non-http URIs
+ Ok(Some(ref req)) => {
+ let response = req.http_fetch();
+ if response.network_error {
+ return Err(Network)
+ } else {
+ load_data.cors = Some(ResourceCORSData {
+ preflight: req.preflight_flag,
+ origin: req.origin.clone()
+ })
+ }
+ },
+ _ => {}
+ }
+
+ // Step 10, 13
+ let (start_chan, start_port) = channel();
+ resource_task.send(Load(load_data, start_chan));
+ let response = start_port.recv();
+ match terminate_receiver.try_recv() {
+ Ok(e) => return Err(e),
+ _ => {}
+ }
+ match cors_request {
+ Ok(Some(ref req)) => {
+ match response.metadata.headers {
+ Some(ref h) if allow_cross_origin_request(req, h) => {},
+ _ => return Err(Network)
+ }
+ },
+ _ => {}
+ }
+ // XXXManishearth Clear cache entries in case of a network error
+
+ notify_partial_progress(fetch_type, HeadersReceivedMsg(
+ response.metadata.headers.clone(), response.metadata.status.clone()));
+ let mut buf = vec!();
+ loop {
+ let progress = response.progress_port.recv();
+ match terminate_receiver.try_recv() {
+ Ok(e) => return Err(e),
+ _ => {}
+ }
+ match progress {
+ Payload(data) => {
+ buf.push_all(data.as_slice());
+ notify_partial_progress(fetch_type, LoadingMsg(ByteString::new(buf.clone())));
+ },
+ Done(Ok(())) => {
+ notify_partial_progress(fetch_type, DoneMsg);
+ return Ok(());
+ },
+ Done(Err(_)) => {
+ notify_partial_progress(fetch_type, ErroredMsg(None));
+ return Err(Network)
+ }
+ }
+ }
+ }
+}
+
+impl<'a> XMLHttpRequestMethods for JSRef<'a, XMLHttpRequest> {
+ fn GetOnreadystatechange(&self) -> Option<EventHandlerNonNull> {
+ let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self);
+ eventtarget.get_event_handler_common("readystatechange")
+ }
+
+ fn SetOnreadystatechange(&self, listener: Option<EventHandlerNonNull>) {
+ let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self);
+ eventtarget.set_event_handler_common("readystatechange", listener)
+ }
+
+ fn ReadyState(&self) -> u16 {
+ self.ready_state.deref().get() as u16
+ }
+
+ fn Open(&self, method: ByteString, url: DOMString) -> ErrorResult {
+ // Clean up from previous requests, if any:
+ self.cancel_timeout();
+ let uppercase_method = method.as_str().map(|s| {
+ let upper = s.to_ascii_upper();
+ match upper.as_slice() {
+ "DELETE" | "GET" | "HEAD" | "OPTIONS" |
+ "POST" | "PUT" | "CONNECT" | "TRACE" |
+ "TRACK" => upper,
+ _ => s.to_string()
+ }
+ });
+ let maybe_method: Option<Method> = uppercase_method.and_then(|s| {
+ // Note: rust-http tests against the uppercase versions
+ // Since we want to pass methods not belonging to the short list above
+ // without changing capitalization, this will actually sidestep rust-http's type system
+ // since methods like "patch" or "PaTcH" will be considered extension methods
+ // despite the there being a rust-http method variant for them
+ Method::from_str_or_new(s.as_slice())
+ });
+ // Step 2
+ match maybe_method {
+ // Step 4
+ Some(Connect) | Some(Trace) => Err(Security),
+ Some(ExtensionMethod(ref t)) if t.as_slice() == "TRACK" => Err(Security),
+ Some(_) if method.is_token() => {
+
+ *self.request_method.deref().borrow_mut() = maybe_method.unwrap();
+
+ // Step 6
+ let base = self.global.root().root_ref().get_url();
+ let parsed_url = match UrlParser::new().base_url(&base).parse(url.as_slice()) {
+ Ok(parsed) => parsed,
+ Err(_) => return Err(Syntax) // Step 7
+ };
+ // XXXManishearth Do some handling of username/passwords
+ if self.sync.deref().get() {
+ // FIXME: This should only happen if the global environment is a document environment
+ if self.timeout.deref().get() != 0 || self.with_credentials.deref().get() || self.response_type.deref().get() != _empty {
+ return Err(InvalidAccess)
+ }
+ }
+ // XXXManishearth abort existing requests
+ // Step 12
+ *self.request_url.deref().borrow_mut() = Some(parsed_url);
+ *self.request_headers.deref().borrow_mut() = RequestHeaderCollection::new();
+ self.send_flag.deref().set(false);
+ *self.status_text.deref().borrow_mut() = ByteString::new(vec!());
+ self.status.deref().set(0);
+
+ // Step 13
+ if self.ready_state.deref().get() != Opened {
+ self.change_ready_state(Opened);
+ }
+ Ok(())
+ },
+ // This includes cases where as_str() returns None, and when is_token() returns false,
+ // both of which indicate invalid extension method names
+ _ => Err(Syntax), // Step 3
+ }
+ }
+ fn Open_(&self, method: ByteString, url: DOMString, async: bool,
+ _username: Option<DOMString>, _password: Option<DOMString>) -> ErrorResult {
+ self.sync.deref().set(!async);
+ self.Open(method, url)
+ }
+ fn SetRequestHeader(&self, name: ByteString, mut value: ByteString) -> ErrorResult {
+ if self.ready_state.deref().get() != Opened || self.send_flag.deref().get() {
+ return Err(InvalidState); // Step 1, 2
+ }
+ if !name.is_token() || !value.is_field_value() {
+ return Err(Syntax); // Step 3, 4
+ }
+ let name_str = match name.to_lower().as_str() {
+ Some(s) => {
+ match s {
+ // Disallowed headers
+ "accept-charset" | "accept-encoding" |
+ "access-control-request-headers" |
+ "access-control-request-method" |
+ "connection" | "content-length" |
+ "cookie" | "cookie2" | "date" |"dnt" |
+ "expect" | "host" | "keep-alive" | "origin" |
+ "referer" | "te" | "trailer" | "transfer-encoding" |
+ "upgrade" | "user-agent" | "via" => {
+ return Ok(()); // Step 5
+ },
+ _ => String::from_str(s)
+ }
+ },
+ None => return Err(Syntax)
+ };
+ let mut collection = self.request_headers.deref().borrow_mut();
+
+
+ // Steps 6,7
+ let old_header = collection.iter().find(|ref h| -> bool {
+ // XXXManishearth following line waiting on the rust upgrade:
+ ByteString::new(h.header_name().into_bytes()).eq_ignore_case(&value)
+ });
+ match old_header {
+ Some(h) => {
+ unsafe {
+ // By step 4, the value is a subset of valid utf8
+ // So this unsafe block should never fail
+
+ let mut buf = h.header_value();
+ buf.push_bytes(&[0x2C, 0x20]);
+ buf.push_bytes(value.as_slice());
+ value = ByteString::new(buf.container_into_owned_bytes());
+
+ }
+ },
+ None => {}
+ }
+
+ let mut reader = BufReader::new(value.as_slice());
+ let maybe_header: Option<Header> = HeaderEnum::value_from_stream(
+ name_str,
+ &mut HeaderValueByteIterator::new(&mut reader));
+ match maybe_header {
+ Some(h) => {
+ // Overwrites existing headers, which we want since we have
+ // prepended the new header value with the old one already
+ collection.insert(h);
+ Ok(())
+ },
+ None => Err(Syntax)
+ }
+ }
+ fn Timeout(&self) -> u32 {
+ self.timeout.deref().get()
+ }
+ fn SetTimeout(&self, timeout: u32) -> ErrorResult {
+ if self.sync.deref().get() {
+ // FIXME: Not valid for a worker environment
+ Err(InvalidState)
+ } else {
+ self.timeout.deref().set(timeout);
+ if self.send_flag.deref().get() {
+ if timeout == 0 {
+ self.cancel_timeout();
+ return Ok(());
+ }
+ let progress = time::now().to_timespec().sec - self.fetch_time.deref().get();
+ if timeout > (progress * 1000) as u32 {
+ self.set_timeout(timeout - (progress * 1000) as u32);
+ } else {
+ // Immediately execute the timeout steps
+ self.set_timeout(0);
+ }
+ }
+ Ok(())
+ }
+ }
+ fn WithCredentials(&self) -> bool {
+ self.with_credentials.deref().get()
+ }
+ fn SetWithCredentials(&self, with_credentials: bool) {
+ self.with_credentials.deref().set(with_credentials);
+ }
+ fn Upload(&self) -> Temporary<XMLHttpRequestUpload> {
+ Temporary::new(self.upload)
+ }
+ fn Send(&self, data: Option<SendParam>) -> ErrorResult {
+ if self.ready_state.deref().get() != Opened || self.send_flag.deref().get() {
+ return Err(InvalidState); // Step 1, 2
+ }
+
+ let data = match *self.request_method.deref().borrow() {
+ Get | Head => None, // Step 3
+ _ => data
+ };
+ let extracted = data.map(|d| d.extract());
+ self.request_body_len.set(extracted.as_ref().map(|e| e.len()).unwrap_or(0));
+
+ // Step 6
+ self.upload_events.deref().set(false);
+ // Step 7
+ self.upload_complete.deref().set(match extracted {
+ None => true,
+ Some (ref v) if v.len() == 0 => true,
+ _ => false
+ });
+ let mut addr = None;
+ if !self.sync.deref().get() {
+ // If one of the event handlers below aborts the fetch,
+ // the assertion in release_once() will fail since we haven't pinned it yet.
+ // Pin early to avoid dealing with this
+ unsafe {
+ addr = Some(self.to_trusted());
+ }
+
+ // Step 8
+ let upload_target = &*self.upload.root();
+ let event_target: &JSRef<EventTarget> = EventTargetCast::from_ref(upload_target);
+ if event_target.has_handlers() {
+ self.upload_events.deref().set(true);
+ }
+
+ // Step 9
+ self.send_flag.deref().set(true);
+ self.dispatch_response_progress_event("loadstart".to_string());
+ if !self.upload_complete.deref().get() {
+ self.dispatch_upload_progress_event("loadstart".to_string(), Some(0));
+ }
+ }
+
+ if self.ready_state.deref().get() == Unsent {
+ // The progress events above might have run abort(), in which case we terminate the fetch.
+ return Ok(());
+ }
+
+ let global = self.global.root();
+ let resource_task = global.root_ref().resource_task();
+ let mut load_data = LoadData::new(self.request_url.deref().borrow().clone().unwrap());
+ load_data.data = extracted;
+
+ // Default headers
+ let request_headers = self.request_headers.deref();
+ if request_headers.borrow().content_type.is_none() {
+ let parameters = vec!((String::from_str("charset"), String::from_str("UTF-8")));
+ request_headers.borrow_mut().content_type = match data {
+ Some(eString(_)) =>
+ Some(MediaType {
+ type_: String::from_str("text"),
+ subtype: String::from_str("plain"),
+ parameters: parameters
+ }),
+ Some(eURLSearchParams(_)) =>
+ Some(MediaType {
+ type_: String::from_str("application"),
+ subtype: String::from_str("x-www-form-urlencoded"),
+ parameters: parameters
+ }),
+ None => None
+ }
+ }
+
+ if request_headers.borrow().accept.is_none() {
+ request_headers.borrow_mut().accept = Some(String::from_str("*/*"))
+ }
+
+ load_data.headers = (*self.request_headers.deref().borrow()).clone();
+ load_data.method = (*self.request_method.deref().borrow()).clone();
+ let (terminate_sender, terminate_receiver) = channel();
+ *self.terminate_sender.deref().borrow_mut() = Some(terminate_sender);
+
+ // CORS stuff
+ let referer_url = self.global.root().root_ref().get_url();
+ let mode = if self.upload_events.deref().get() {
+ ForcedPreflightMode
+ } else {
+ CORSMode
+ };
+ let cors_request = CORSRequest::maybe_new(referer_url.clone(), load_data.url.clone(), mode,
+ load_data.method.clone(), load_data.headers.clone());
+ match cors_request {
+ Ok(None) => {
+ let mut buf = String::new();
+ buf.push_str(referer_url.scheme.as_slice());
+ buf.push_str("://".as_slice());
+ referer_url.serialize_host().map(|ref h| buf.push_str(h.as_slice()));
+ referer_url.port().as_ref().map(|&p| {
+ buf.push_str(":".as_slice());
+ buf.push_str(p);
+ });
+ referer_url.serialize_path().map(|ref h| buf.push_str(h.as_slice()));
+ self.request_headers.deref().borrow_mut().referer = Some(buf);
+ },
+ Ok(Some(ref req)) => self.insert_trusted_header("origin".to_string(),
+ format!("{}", req.origin)),
+ _ => {}
+ }
+
+ if self.sync.deref().get() {
+ return XMLHttpRequest::fetch(&mut Sync(self), resource_task, load_data,
+ terminate_receiver, cors_request);
+ } else {
+ let builder = TaskBuilder::new().named("XHRTask");
+ self.fetch_time.deref().set(time::now().to_timespec().sec);
+ let script_chan = global.root_ref().script_chan().clone();
+ builder.spawn(proc() {
+ let _ = XMLHttpRequest::fetch(&mut Async(addr.unwrap(), script_chan),
+ resource_task, load_data, terminate_receiver, cors_request);
+ });
+ let timeout = self.timeout.deref().get();
+ if timeout > 0 {
+ self.set_timeout(timeout);
+ }
+ }
+ Ok(())
+ }
+ fn Abort(&self) {
+ self.terminate_sender.deref().borrow().as_ref().map(|s| s.send_opt(Abort));
+ match self.ready_state.deref().get() {
+ Opened if self.send_flag.deref().get() => self.process_partial_response(ErroredMsg(Some(Abort))),
+ HeadersReceived | Loading => self.process_partial_response(ErroredMsg(Some(Abort))),
+ _ => {}
+ };
+ self.ready_state.deref().set(Unsent);
+ }
+ fn ResponseURL(&self) -> DOMString {
+ self.response_url.clone()
+ }
+ fn Status(&self) -> u16 {
+ self.status.deref().get()
+ }
+ fn StatusText(&self) -> ByteString {
+ self.status_text.deref().borrow().clone()
+ }
+ fn GetResponseHeader(&self, name: ByteString) -> Option<ByteString> {
+ self.filter_response_headers().iter().find(|h| {
+ name.eq_ignore_case(&FromStr::from_str(h.header_name().as_slice()).unwrap())
+ }).map(|h| {
+ // rust-http doesn't decode properly, we'll convert it back to bytes here
+ ByteString::new(h.header_value().as_slice().chars().map(|c| { assert!(c <= '\u00FF'); c as u8 }).collect())
+ })
+ }
+ fn GetAllResponseHeaders(&self) -> ByteString {
+ let mut writer = MemWriter::new();
+ self.filter_response_headers().write_all(&mut writer).ok().expect("Writing response headers failed");
+ let mut vec = writer.unwrap();
+
+ // rust-http appends an extra "\r\n" when using write_all
+ vec.pop();
+ vec.pop();
+
+ ByteString::new(vec)
+ }
+ fn ResponseType(&self) -> XMLHttpRequestResponseType {
+ self.response_type.deref().get()
+ }
+ fn SetResponseType(&self, response_type: XMLHttpRequestResponseType) -> ErrorResult {
+ match self.global {
+ WorkerField(_) if response_type == Document => return Ok(()),
+ _ => {}
+ }
+ match self.ready_state.deref().get() {
+ Loading | XHRDone => Err(InvalidState),
+ _ if self.sync.deref().get() => Err(InvalidAccess),
+ _ => {
+ self.response_type.deref().set(response_type);
+ Ok(())
+ }
+ }
+ }
+ fn Response(&self, cx: *mut JSContext) -> JSVal {
+ match self.response_type.deref().get() {
+ _empty | Text => {
+ let ready_state = self.ready_state.deref().get();
+ if ready_state == XHRDone || ready_state == Loading {
+ self.text_response().to_jsval(cx)
+ } else {
+ "".to_string().to_jsval(cx)
+ }
+ },
+ _ if self.ready_state.deref().get() != XHRDone => NullValue(),
+ Json => {
+ let decoded = UTF_8.decode(self.response.deref().borrow().as_slice(), DecodeReplace).unwrap().to_string();
+ let decoded: Vec<u16> = decoded.as_slice().utf16_units().collect();
+ let mut vp = UndefinedValue();
+ unsafe {
+ if JS_ParseJSON(cx, decoded.as_ptr(), decoded.len() as u32, &mut vp) == 0 {
+ JS_ClearPendingException(cx);
+ return NullValue();
+ }
+ }
+ vp
+ }
+ _ => {
+ // XXXManishearth handle other response types
+ self.response.deref().borrow().to_jsval(cx)
+ }
+ }
+ }
+ fn GetResponseText(&self) -> Fallible<DOMString> {
+ match self.response_type.deref().get() {
+ _empty | Text => {
+ match self.ready_state.deref().get() {
+ Loading | XHRDone => Ok(self.text_response()),
+ _ => Ok("".to_string())
+ }
+ },
+ _ => Err(InvalidState)
+ }
+ }
+ fn GetResponseXML(&self) -> Option<Temporary<Document>> {
+ self.response_xml.get().map(|response| Temporary::new(response))
+ }
+}
+
+impl Reflectable for XMLHttpRequest {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.eventtarget.reflector()
+ }
+}
+
+impl XMLHttpRequestDerived for EventTarget {
+ fn is_xmlhttprequest(&self) -> bool {
+ match self.type_id {
+ XMLHttpRequestTargetTypeId(XMLHttpRequestTypeId) => true,
+ _ => false
+ }
+ }
+}
+
+pub struct TrustedXHRAddress(pub *const c_void);
+
+impl TrustedXHRAddress {
+ pub fn release_once(self) {
+ unsafe {
+ JS::from_trusted_xhr_address(self).root().release_once();
+ }
+ }
+}
+
+
+trait PrivateXMLHttpRequestHelpers {
+ unsafe fn to_trusted(&self) -> TrustedXHRAddress;
+ fn release_once(&self);
+ fn change_ready_state(&self, XMLHttpRequestState);
+ fn process_partial_response(&self, progress: XHRProgress);
+ fn insert_trusted_header(&self, name: String, value: String);
+ fn dispatch_progress_event(&self, upload: bool, type_: DOMString, loaded: u64, total: Option<u64>);
+ fn dispatch_upload_progress_event(&self, type_: DOMString, partial_load: Option<u64>);
+ fn dispatch_response_progress_event(&self, type_: DOMString);
+ fn text_response(&self) -> DOMString;
+ fn set_timeout(&self, timeout:u32);
+ fn cancel_timeout(&self);
+ fn filter_response_headers(&self) -> ResponseHeaderCollection;
+}
+
+impl<'a> PrivateXMLHttpRequestHelpers for JSRef<'a, XMLHttpRequest> {
+ // Creates a trusted address to the object, and roots it. Always pair this with a release()
+ unsafe fn to_trusted(&self) -> TrustedXHRAddress {
+ if self.pinned_count.deref().get() == 0 {
+ JS_AddObjectRoot(self.global.root().root_ref().get_cx(), self.reflector().rootable());
+ }
+ let pinned_count = self.pinned_count.deref().get();
+ self.pinned_count.deref().set(pinned_count + 1);
+ TrustedXHRAddress(self.deref() as *const XMLHttpRequest as *const libc::c_void)
+ }
+
+ fn release_once(&self) {
+ if self.sync.deref().get() {
+ // Lets us call this at various termination cases without having to
+ // check self.sync every time, since the pinning mechanism only is
+ // meaningful during an async fetch
+ return;
+ }
+ assert!(self.pinned_count.deref().get() > 0)
+ let pinned_count = self.pinned_count.deref().get();
+ self.pinned_count.deref().set(pinned_count - 1);
+ if self.pinned_count.deref().get() == 0 {
+ unsafe {
+ JS_RemoveObjectRoot(self.global.root().root_ref().get_cx(), self.reflector().rootable());
+ }
+ }
+ }
+
+ fn change_ready_state(&self, rs: XMLHttpRequestState) {
+ assert!(self.ready_state.deref().get() != rs)
+ self.ready_state.deref().set(rs);
+ let global = self.global.root();
+ let event = Event::new(&global.root_ref(),
+ "readystatechange".to_string(),
+ false, true).root();
+ let target: &JSRef<EventTarget> = EventTargetCast::from_ref(self);
+ target.dispatch_event_with_target(None, &*event).ok();
+ }
+
+ fn process_partial_response(&self, progress: XHRProgress) {
+ match progress {
+ HeadersReceivedMsg(headers, status) => {
+ // For synchronous requests, this should not fire any events, and just store data
+ // XXXManishearth Find a way to track partial progress of the send (onprogresss for XHRUpload)
+
+ // Part of step 13, send() (processing request end of file)
+ // Substep 1
+ self.upload_complete.deref().set(true);
+ // Substeps 2-4
+ if !self.sync.deref().get() {
+ self.dispatch_upload_progress_event("progress".to_string(), None);
+ self.dispatch_upload_progress_event("load".to_string(), None);
+ self.dispatch_upload_progress_event("loadend".to_string(), None);
+ }
+ // Part of step 13, send() (processing response)
+ // XXXManishearth handle errors, if any (substep 1)
+ // Substep 2
+ *self.status_text.deref().borrow_mut() = ByteString::new(status.reason().container_into_owned_bytes());
+ self.status.deref().set(status.code());
+ match headers {
+ Some(ref h) => {
+ *self.response_headers.deref().borrow_mut() = h.clone();
+ }
+ None => {}
+ };
+ // Substep 3
+ if self.ready_state.deref().get() == Opened && !self.sync.deref().get() {
+ self.change_ready_state(HeadersReceived);
+ }
+ },
+ LoadingMsg(partial_response) => {
+ // For synchronous requests, this should not fire any events, and just store data
+ // Part of step 13, send() (processing response body)
+ // XXXManishearth handle errors, if any (substep 1)
+
+ // Substep 2
+ if self.ready_state.deref().get() == HeadersReceived && !self.sync.deref().get() {
+ self.change_ready_state(Loading);
+ }
+ // Substep 3
+ *self.response.deref().borrow_mut() = partial_response;
+ // Substep 4
+ if !self.sync.deref().get() {
+ self.dispatch_response_progress_event("progress".to_string());
+ }
+ },
+ DoneMsg => {
+ // Part of step 13, send() (processing response end of file)
+ // XXXManishearth handle errors, if any (substep 1)
+
+ // Substep 3
+ if self.ready_state.deref().get() == Loading || self.sync.deref().get() {
+ // Subsubsteps 2-4
+ self.send_flag.deref().set(false);
+ self.change_ready_state(XHRDone);
+
+ // Subsubsteps 5-7
+ self.dispatch_response_progress_event("progress".to_string());
+ self.dispatch_response_progress_event("load".to_string());
+ self.dispatch_response_progress_event("loadend".to_string());
+ }
+ self.cancel_timeout();
+ self.release_once();
+ },
+ ErroredMsg(e) => {
+ self.send_flag.deref().set(false);
+ // XXXManishearth set response to NetworkError
+ self.change_ready_state(XHRDone);
+ let errormsg = match e {
+ Some(Abort) => "abort",
+ Some(Timeout) => "timeout",
+ None => "error",
+ _ => unreachable!()
+ };
+
+ let upload_complete: &Cell<bool> = self.upload_complete.deref();
+ if !upload_complete.get() {
+ upload_complete.set(true);
+ self.dispatch_upload_progress_event("progress".to_string(), None);
+ self.dispatch_upload_progress_event(errormsg.to_string(), None);
+ self.dispatch_upload_progress_event("loadend".to_string(), None);
+ }
+ self.dispatch_response_progress_event("progress".to_string());
+ self.dispatch_response_progress_event(errormsg.to_string());
+ self.dispatch_response_progress_event("loadend".to_string());
+
+ self.cancel_timeout();
+ self.release_once();
+ },
+ TimeoutMsg => {
+ match self.ready_state.deref().get() {
+ Opened if self.send_flag.deref().get() => self.process_partial_response(ErroredMsg(Some(Timeout))),
+ Loading | HeadersReceived => self.process_partial_response(ErroredMsg(Some(Timeout))),
+ _ => self.release_once()
+ };
+ }
+ }
+ }
+
+ fn insert_trusted_header(&self, name: String, value: String) {
+ // Insert a header without checking spec-compliance
+ // Use for hardcoded headers
+ let mut collection = self.request_headers.deref().borrow_mut();
+ let value_bytes = value.into_bytes();
+ let mut reader = BufReader::new(value_bytes.as_slice());
+ let maybe_header: Option<Header> = HeaderEnum::value_from_stream(
+ String::from_str(name.as_slice()),
+ &mut HeaderValueByteIterator::new(&mut reader));
+ collection.insert(maybe_header.unwrap());
+ }
+
+ fn dispatch_progress_event(&self, upload: bool, type_: DOMString, loaded: u64, total: Option<u64>) {
+ let global = self.global.root();
+ let upload_target = &*self.upload.root();
+ let progressevent = ProgressEvent::new(&global.root_ref(),
+ type_, false, false,
+ total.is_some(), loaded,
+ total.unwrap_or(0)).root();
+ let target: &JSRef<EventTarget> = if upload {
+ EventTargetCast::from_ref(upload_target)
+ } else {
+ EventTargetCast::from_ref(self)
+ };
+ let event: &JSRef<Event> = EventCast::from_ref(&*progressevent);
+ target.dispatch_event_with_target(None, event).ok();
+ }
+
+ fn dispatch_upload_progress_event(&self, type_: DOMString, partial_load: Option<u64>) {
+ // If partial_load is None, loading has completed and we can just use the value from the request body
+
+ let total = self.request_body_len.get() as u64;
+ self.dispatch_progress_event(true, type_, partial_load.unwrap_or(total), Some(total));
+ }
+
+ fn dispatch_response_progress_event(&self, type_: DOMString) {
+ let len = self.response.deref().borrow().len() as u64;
+ let total = self.response_headers.deref().borrow().content_length.map(|x| {x as u64});
+ self.dispatch_progress_event(false, type_, len, total);
+ }
+ fn set_timeout(&self, timeout: u32) {
+ // Sets up the object to timeout in a given number of milliseconds
+ // This will cancel all previous timeouts
+ let oneshot = self.timer.deref().borrow_mut().oneshot(timeout as u64);
+ let addr = unsafe {
+ self.to_trusted() // This will increment the pin counter by one
+ };
+ if self.timeout_pinned.deref().get() {
+ // Already pinned due to a timeout, no need to pin it again since the old timeout was cancelled above
+ self.release_once();
+ }
+ self.timeout_pinned.deref().set(true);
+ let global = self.global.root();
+ let script_chan = global.root_ref().script_chan().clone();
+ let terminate_sender = (*self.terminate_sender.deref().borrow()).clone();
+ spawn_named("XHR:Timer", proc () {
+ match oneshot.recv_opt() {
+ Ok(_) => {
+ let ScriptChan(ref chan) = script_chan;
+ terminate_sender.map(|s| s.send_opt(Timeout));
+ chan.send(XHRProgressMsg(addr, TimeoutMsg));
+ },
+ Err(_) => {
+ // This occurs if xhr.timeout (the sender) goes out of scope (i.e, xhr went out of scope)
+ // or if the oneshot timer was overwritten. The former case should not happen due to pinning.
+ debug!("XHR timeout was overwritten or canceled")
+ }
+ }
+ }
+ );
+ }
+ fn cancel_timeout(&self) {
+ // Cancels timeouts on the object, if any
+ if self.timeout_pinned.deref().get() {
+ self.timeout_pinned.deref().set(false);
+ self.release_once();
+ }
+ // oneshot() closes the previous channel, canceling the timeout
+ self.timer.deref().borrow_mut().oneshot(0);
+ }
+ fn text_response(&self) -> DOMString {
+ let mut encoding = UTF_8 as EncodingRef;
+ match self.response_headers.deref().borrow().content_type {
+ Some(ref x) => {
+ for &(ref name, ref value) in x.parameters.iter() {
+ if name.as_slice().eq_ignore_ascii_case("charset") {
+ encoding = encoding_from_whatwg_label(value.as_slice()).unwrap_or(encoding);
+ }
+ }
+ },
+ None => {}
+ }
+ // According to Simon, decode() should never return an error, so unwrap()ing
+ // the result should be fine. XXXManishearth have a closer look at this later
+ encoding.decode(self.response.deref().borrow().as_slice(), DecodeReplace).unwrap().to_string()
+ }
+ fn filter_response_headers(&self) -> ResponseHeaderCollection {
+ // http://fetch.spec.whatwg.org/#concept-response-header-list
+ let mut headers = ResponseHeaderCollection::new();
+ for header in self.response_headers.deref().borrow().iter() {
+ match header.header_name().as_slice().to_ascii_lower().as_slice() {
+ "set-cookie" | "set-cookie2" => {},
+ // XXXManishearth additional CORS filtering goes here
+ _ => headers.insert(header)
+ };
+ }
+ headers
+ }
+}
+
+trait Extractable {
+ fn extract(&self) -> Vec<u8>;
+}
+impl Extractable for SendParam {
+ fn extract(&self) -> Vec<u8> {
+ // http://fetch.spec.whatwg.org/#concept-fetchbodyinit-extract
+ let encoding = UTF_8 as EncodingRef;
+ match *self {
+ eString(ref s) => encoding.encode(s.as_slice(), EncodeReplace).unwrap(),
+ eURLSearchParams(ref usp) => usp.root().serialize(None) // Default encoding is UTF8
+ }
+ }
+}
diff --git a/components/script/dom/xmlhttprequesteventtarget.rs b/components/script/dom/xmlhttprequesteventtarget.rs
new file mode 100644
index 00000000000..06c42fbdc4e
--- /dev/null
+++ b/components/script/dom/xmlhttprequesteventtarget.rs
@@ -0,0 +1,112 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
+use dom::bindings::codegen::Bindings::XMLHttpRequestEventTargetBinding::XMLHttpRequestEventTargetMethods;
+use dom::bindings::codegen::InheritTypes::EventTargetCast;
+use dom::bindings::codegen::InheritTypes::XMLHttpRequestEventTargetDerived;
+use dom::bindings::js::JSRef;
+use dom::bindings::utils::{Reflectable, Reflector};
+use dom::eventtarget::{EventTarget, EventTargetHelpers, XMLHttpRequestTargetTypeId};
+use dom::xmlhttprequest::XMLHttpRequestId;
+
+#[deriving(Encodable)]
+pub struct XMLHttpRequestEventTarget {
+ pub eventtarget: EventTarget,
+}
+
+impl XMLHttpRequestEventTarget {
+ pub fn new_inherited(type_id: XMLHttpRequestId) -> XMLHttpRequestEventTarget {
+ XMLHttpRequestEventTarget {
+ eventtarget: EventTarget::new_inherited(XMLHttpRequestTargetTypeId(type_id))
+ }
+ }
+}
+impl XMLHttpRequestEventTargetDerived for EventTarget {
+ fn is_xmlhttprequesteventtarget(&self) -> bool {
+ match self.type_id {
+ XMLHttpRequestTargetTypeId(_) => true,
+ _ => false
+ }
+ }
+
+}
+
+impl Reflectable for XMLHttpRequestEventTarget {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.eventtarget.reflector()
+ }
+}
+
+impl<'a> XMLHttpRequestEventTargetMethods for JSRef<'a, XMLHttpRequestEventTarget> {
+ fn GetOnloadstart(&self) -> Option<EventHandlerNonNull> {
+ let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self);
+ eventtarget.get_event_handler_common("loadstart")
+ }
+
+ fn SetOnloadstart(&self, listener: Option<EventHandlerNonNull>) {
+ let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self);
+ eventtarget.set_event_handler_common("loadstart", listener)
+ }
+
+ fn GetOnprogress(&self) -> Option<EventHandlerNonNull> {
+ let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self);
+ eventtarget.get_event_handler_common("progress")
+ }
+
+ fn SetOnprogress(&self, listener: Option<EventHandlerNonNull>) {
+ let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self);
+ eventtarget.set_event_handler_common("progress", listener)
+ }
+
+ fn GetOnabort(&self) -> Option<EventHandlerNonNull> {
+ let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self);
+ eventtarget.get_event_handler_common("abort")
+ }
+
+ fn SetOnabort(&self, listener: Option<EventHandlerNonNull>) {
+ let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self);
+ eventtarget.set_event_handler_common("abort", listener)
+ }
+
+ fn GetOnerror(&self) -> Option<EventHandlerNonNull> {
+ let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self);
+ eventtarget.get_event_handler_common("error")
+ }
+
+ fn SetOnerror(&self, listener: Option<EventHandlerNonNull>) {
+ let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self);
+ eventtarget.set_event_handler_common("error", listener)
+ }
+
+ fn GetOnload(&self) -> Option<EventHandlerNonNull> {
+ let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self);
+ eventtarget.get_event_handler_common("load")
+ }
+
+ fn SetOnload(&self, listener: Option<EventHandlerNonNull>) {
+ let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self);
+ eventtarget.set_event_handler_common("load", listener)
+ }
+
+ fn GetOntimeout(&self) -> Option<EventHandlerNonNull> {
+ let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self);
+ eventtarget.get_event_handler_common("timeout")
+ }
+
+ fn SetOntimeout(&self, listener: Option<EventHandlerNonNull>) {
+ let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self);
+ eventtarget.set_event_handler_common("timeout", listener)
+ }
+
+ fn GetOnloadend(&self) -> Option<EventHandlerNonNull> {
+ let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self);
+ eventtarget.get_event_handler_common("loadend")
+ }
+
+ fn SetOnloadend(&self, listener: Option<EventHandlerNonNull>) {
+ let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self);
+ eventtarget.set_event_handler_common("loadend", listener)
+ }
+}
diff --git a/components/script/dom/xmlhttprequestupload.rs b/components/script/dom/xmlhttprequestupload.rs
new file mode 100644
index 00000000000..477d382bffe
--- /dev/null
+++ b/components/script/dom/xmlhttprequestupload.rs
@@ -0,0 +1,41 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::InheritTypes::XMLHttpRequestUploadDerived;
+use dom::bindings::codegen::Bindings::XMLHttpRequestUploadBinding;
+use dom::bindings::global::GlobalRef;
+use dom::bindings::js::Temporary;
+use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
+use dom::eventtarget::{EventTarget, XMLHttpRequestTargetTypeId};
+use dom::xmlhttprequest::{XMLHttpRequestUploadTypeId};
+use dom::xmlhttprequesteventtarget::XMLHttpRequestEventTarget;
+
+#[deriving(Encodable)]
+pub struct XMLHttpRequestUpload {
+ eventtarget: XMLHttpRequestEventTarget
+}
+
+impl XMLHttpRequestUpload {
+ pub fn new_inherited() -> XMLHttpRequestUpload {
+ XMLHttpRequestUpload {
+ eventtarget:XMLHttpRequestEventTarget::new_inherited(XMLHttpRequestUploadTypeId)
+ }
+ }
+ pub fn new(global: &GlobalRef) -> Temporary<XMLHttpRequestUpload> {
+ reflect_dom_object(box XMLHttpRequestUpload::new_inherited(),
+ global,
+ XMLHttpRequestUploadBinding::Wrap)
+ }
+}
+impl Reflectable for XMLHttpRequestUpload {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.eventtarget.reflector()
+ }
+}
+
+impl XMLHttpRequestUploadDerived for EventTarget {
+ fn is_xmlhttprequestupload(&self) -> bool {
+ self.type_id == XMLHttpRequestTargetTypeId(XMLHttpRequestUploadTypeId)
+ }
+}
diff --git a/components/script/html/cssparse.rs b/components/script/html/cssparse.rs
new file mode 100644
index 00000000000..473b64c7d76
--- /dev/null
+++ b/components/script/html/cssparse.rs
@@ -0,0 +1,72 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/// Some little helpers for hooking up the HTML parser with the CSS parser.
+
+use std::comm::{channel, Receiver};
+use encoding::EncodingRef;
+use encoding::all::UTF_8;
+use style::Stylesheet;
+use servo_net::resource_task::{Load, LoadData, LoadResponse, ProgressMsg, Payload, Done, ResourceTask};
+use servo_util::task::spawn_named;
+use url::Url;
+
+/// Where a style sheet comes from.
+pub enum StylesheetProvenance {
+ UrlProvenance(Url, ResourceTask),
+ InlineProvenance(Url, String),
+}
+
+// Parses the style data and returns the stylesheet
+pub fn parse_inline_css(url: Url, data: String) -> Stylesheet {
+ parse_css(InlineProvenance(url, data))
+}
+
+fn parse_css(provenance: StylesheetProvenance) -> Stylesheet {
+ // TODO: Get the actual value. http://dev.w3.org/csswg/css-syntax/#environment-encoding
+ let environment_encoding = UTF_8 as EncodingRef;
+
+ match provenance {
+ UrlProvenance(url, resource_task) => {
+ debug!("cssparse: loading style sheet at {:s}", url.serialize());
+ let (input_chan, input_port) = channel();
+ resource_task.send(Load(LoadData::new(url), input_chan));
+ let LoadResponse { metadata: metadata, progress_port: progress_port , ..}
+ = input_port.recv();
+ let final_url = &metadata.final_url;
+ let protocol_encoding_label = metadata.charset.as_ref().map(|s| s.as_slice());
+ let iter = ProgressMsgPortIterator { progress_port: progress_port };
+ Stylesheet::from_bytes_iter(
+ iter, final_url.clone(),
+ protocol_encoding_label, Some(environment_encoding))
+ }
+ InlineProvenance(base_url, data) => {
+ debug!("cssparse: loading inline stylesheet {:s}", data);
+ Stylesheet::from_str(data.as_slice(), base_url)
+ }
+ }
+}
+
+pub fn spawn_css_parser(provenance: StylesheetProvenance) -> Receiver<Stylesheet> {
+ let (result_chan, result_port) = channel();
+
+ spawn_named("cssparser", proc() {
+ result_chan.send(parse_css(provenance));
+ });
+
+ return result_port;
+}
+
+struct ProgressMsgPortIterator {
+ progress_port: Receiver<ProgressMsg>
+}
+
+impl Iterator<Vec<u8>> for ProgressMsgPortIterator {
+ fn next(&mut self) -> Option<Vec<u8>> {
+ match self.progress_port.recv() {
+ Payload(data) => Some(data),
+ Done(..) => None
+ }
+ }
+}
diff --git a/components/script/html/hubbub_html_parser.rs b/components/script/html/hubbub_html_parser.rs
new file mode 100644
index 00000000000..8b49a2bae03
--- /dev/null
+++ b/components/script/html/hubbub_html_parser.rs
@@ -0,0 +1,615 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
+use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
+use dom::bindings::codegen::InheritTypes::{NodeBase, NodeCast, TextCast};
+use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLScriptElementCast};
+use dom::bindings::js::{JS, JSRef, Temporary, OptionalRootable, Root};
+use dom::bindings::utils::Reflectable;
+use dom::document::{Document, DocumentHelpers};
+use dom::element::{AttributeHandlers, HTMLLinkElementTypeId};
+use dom::htmlelement::HTMLElement;
+use dom::htmlheadingelement::{Heading1, Heading2, Heading3, Heading4, Heading5, Heading6};
+use dom::htmlformelement::HTMLFormElement;
+use dom::htmlscriptelement::HTMLScriptElementHelpers;
+use dom::node::{ElementNodeTypeId, NodeHelpers};
+use dom::types::*;
+use html::cssparse::{StylesheetProvenance, UrlProvenance, spawn_css_parser};
+use page::Page;
+
+use hubbub::hubbub;
+use hubbub::hubbub::{NullNs, HtmlNs, MathMlNs, SvgNs, XLinkNs, XmlNs, XmlNsNs};
+use servo_net::resource_task::{Load, LoadData, Payload, Done, ResourceTask, load_whole_resource};
+use servo_util::atom::Atom;
+use servo_util::namespace;
+use servo_util::namespace::{Namespace, Null};
+use servo_util::str::{DOMString, HTML_SPACE_CHARACTERS};
+use servo_util::task::spawn_named;
+use std::ascii::StrAsciiExt;
+use std::mem;
+use std::cell::RefCell;
+use std::comm::{channel, Sender, Receiver};
+use style::Stylesheet;
+use url::{Url, UrlParser};
+use http::headers::HeaderEnum;
+use time;
+
+macro_rules! handle_element(
+ ($document: expr,
+ $localName: expr,
+ $string: expr,
+ $ctor: ident
+ $(, $arg:expr )*) => (
+ if $string == $localName.as_slice() {
+ return ElementCast::from_temporary($ctor::new($localName, $document $(, $arg)*));
+ }
+ )
+)
+
+
+pub struct JSFile {
+ pub data: String,
+ pub url: Url
+}
+
+pub type JSResult = Vec<JSFile>;
+
+enum CSSMessage {
+ CSSTaskNewFile(StylesheetProvenance),
+ CSSTaskExit
+}
+
+enum JSMessage {
+ JSTaskNewFile(Url),
+ JSTaskNewInlineScript(String, Url),
+ JSTaskExit
+}
+
+/// Messages generated by the HTML parser upon discovery of additional resources
+pub enum HtmlDiscoveryMessage {
+ HtmlDiscoveredStyle(Stylesheet),
+ HtmlDiscoveredScript(JSResult)
+}
+
+pub struct HtmlParserResult {
+ pub discovery_port: Receiver<HtmlDiscoveryMessage>,
+}
+
+trait NodeWrapping<T> {
+ unsafe fn to_hubbub_node(&self) -> hubbub::NodeDataPtr;
+}
+
+impl<'a, T: NodeBase+Reflectable> NodeWrapping<T> for JSRef<'a, T> {
+ unsafe fn to_hubbub_node(&self) -> hubbub::NodeDataPtr {
+ mem::transmute(self.deref())
+ }
+}
+
+unsafe fn from_hubbub_node<T: Reflectable>(n: hubbub::NodeDataPtr) -> Temporary<T> {
+ Temporary::new(JS::from_raw(mem::transmute(n)))
+}
+
+/**
+Runs a task that coordinates parsing links to css stylesheets.
+
+This function should be spawned in a separate task and spins waiting
+for the html builder to find links to css stylesheets and sends off
+tasks to parse each link. When the html process finishes, it notifies
+the listener, who then collects the css rules from each task it
+spawned, collates them, and sends them to the given result channel.
+
+# Arguments
+
+* `to_parent` - A channel on which to send back the full set of rules.
+* `from_parent` - A port on which to receive new links.
+
+*/
+fn css_link_listener(to_parent: Sender<HtmlDiscoveryMessage>,
+ from_parent: Receiver<CSSMessage>) {
+ let mut result_vec = vec!();
+
+ loop {
+ match from_parent.recv_opt() {
+ Ok(CSSTaskNewFile(provenance)) => {
+ result_vec.push(spawn_css_parser(provenance));
+ }
+ Ok(CSSTaskExit) | Err(()) => {
+ break;
+ }
+ }
+ }
+
+ // Send the sheets back in order
+ // FIXME: Shouldn't wait until after we've recieved CSSTaskExit to start sending these
+ for port in result_vec.iter() {
+ assert!(to_parent.send_opt(HtmlDiscoveredStyle(port.recv())).is_ok());
+ }
+}
+
+fn js_script_listener(to_parent: Sender<HtmlDiscoveryMessage>,
+ from_parent: Receiver<JSMessage>,
+ resource_task: ResourceTask) {
+ let mut result_vec = vec!();
+
+ loop {
+ match from_parent.recv_opt() {
+ Ok(JSTaskNewFile(url)) => {
+ match load_whole_resource(&resource_task, url.clone()) {
+ Err(_) => {
+ error!("error loading script {:s}", url.serialize());
+ }
+ Ok((metadata, bytes)) => {
+ result_vec.push(JSFile {
+ data: String::from_utf8(bytes).unwrap().to_string(),
+ url: metadata.final_url,
+ });
+ }
+ }
+ }
+ Ok(JSTaskNewInlineScript(data, url)) => {
+ result_vec.push(JSFile { data: data, url: url });
+ }
+ Ok(JSTaskExit) | Err(()) => {
+ break;
+ }
+ }
+ }
+
+ assert!(to_parent.send_opt(HtmlDiscoveredScript(result_vec)).is_ok());
+}
+
+// Parses an RFC 2616 compliant date/time string, and returns a localized
+// date/time string in a format suitable for document.lastModified.
+fn parse_last_modified(timestamp: &str) -> String {
+ let format = "%m/%d/%Y %H:%M:%S";
+
+ // RFC 822, updated by RFC 1123
+ match time::strptime(timestamp, "%a, %d %b %Y %T %Z") {
+ Ok(t) => return t.to_local().strftime(format),
+ Err(_) => ()
+ }
+
+ // RFC 850, obsoleted by RFC 1036
+ match time::strptime(timestamp, "%A, %d-%b-%y %T %Z") {
+ Ok(t) => return t.to_local().strftime(format),
+ Err(_) => ()
+ }
+
+ // ANSI C's asctime() format
+ match time::strptime(timestamp, "%c") {
+ Ok(t) => t.to_local().strftime(format),
+ Err(_) => String::from_str("")
+ }
+}
+
+// Silly macros to handle constructing DOM nodes. This produces bad code and should be optimized
+// via atomization (issue #85).
+
+pub fn build_element_from_tag(tag: DOMString, ns: Namespace, document: &JSRef<Document>) -> Temporary<Element> {
+ if ns != namespace::HTML {
+ return Element::new(tag, ns, None, document);
+ }
+
+ // TODO (Issue #85): use atoms
+ handle_element!(document, tag, "a", HTMLAnchorElement);
+ handle_element!(document, tag, "abbr", HTMLElement);
+ handle_element!(document, tag, "acronym", HTMLElement);
+ handle_element!(document, tag, "address", HTMLElement);
+ handle_element!(document, tag, "applet", HTMLAppletElement);
+ handle_element!(document, tag, "area", HTMLAreaElement);
+ handle_element!(document, tag, "article", HTMLElement);
+ handle_element!(document, tag, "aside", HTMLElement);
+ handle_element!(document, tag, "audio", HTMLAudioElement);
+ handle_element!(document, tag, "b", HTMLElement);
+ handle_element!(document, tag, "base", HTMLBaseElement);
+ handle_element!(document, tag, "bdi", HTMLElement);
+ handle_element!(document, tag, "bdo", HTMLElement);
+ handle_element!(document, tag, "bgsound", HTMLElement);
+ handle_element!(document, tag, "big", HTMLElement);
+ handle_element!(document, tag, "blockquote",HTMLElement);
+ handle_element!(document, tag, "body", HTMLBodyElement);
+ handle_element!(document, tag, "br", HTMLBRElement);
+ handle_element!(document, tag, "button", HTMLButtonElement);
+ handle_element!(document, tag, "canvas", HTMLCanvasElement);
+ handle_element!(document, tag, "caption", HTMLTableCaptionElement);
+ handle_element!(document, tag, "center", HTMLElement);
+ handle_element!(document, tag, "cite", HTMLElement);
+ handle_element!(document, tag, "code", HTMLElement);
+ handle_element!(document, tag, "col", HTMLTableColElement);
+ handle_element!(document, tag, "colgroup", HTMLTableColElement);
+ handle_element!(document, tag, "data", HTMLDataElement);
+ handle_element!(document, tag, "datalist", HTMLDataListElement);
+ handle_element!(document, tag, "dd", HTMLElement);
+ handle_element!(document, tag, "del", HTMLModElement);
+ handle_element!(document, tag, "details", HTMLElement);
+ handle_element!(document, tag, "dfn", HTMLElement);
+ handle_element!(document, tag, "dir", HTMLDirectoryElement);
+ handle_element!(document, tag, "div", HTMLDivElement);
+ handle_element!(document, tag, "dl", HTMLDListElement);
+ handle_element!(document, tag, "dt", HTMLElement);
+ handle_element!(document, tag, "em", HTMLElement);
+ handle_element!(document, tag, "embed", HTMLEmbedElement);
+ handle_element!(document, tag, "fieldset", HTMLFieldSetElement);
+ handle_element!(document, tag, "figcaption",HTMLElement);
+ handle_element!(document, tag, "figure", HTMLElement);
+ handle_element!(document, tag, "font", HTMLFontElement);
+ handle_element!(document, tag, "footer", HTMLElement);
+ handle_element!(document, tag, "form", HTMLFormElement);
+ handle_element!(document, tag, "frame", HTMLFrameElement);
+ handle_element!(document, tag, "frameset", HTMLFrameSetElement);
+ handle_element!(document, tag, "h1", HTMLHeadingElement, Heading1);
+ handle_element!(document, tag, "h2", HTMLHeadingElement, Heading2);
+ handle_element!(document, tag, "h3", HTMLHeadingElement, Heading3);
+ handle_element!(document, tag, "h4", HTMLHeadingElement, Heading4);
+ handle_element!(document, tag, "h5", HTMLHeadingElement, Heading5);
+ handle_element!(document, tag, "h6", HTMLHeadingElement, Heading6);
+ handle_element!(document, tag, "head", HTMLHeadElement);
+ handle_element!(document, tag, "header", HTMLElement);
+ handle_element!(document, tag, "hgroup", HTMLElement);
+ handle_element!(document, tag, "hr", HTMLHRElement);
+ handle_element!(document, tag, "html", HTMLHtmlElement);
+ handle_element!(document, tag, "i", HTMLElement);
+ handle_element!(document, tag, "iframe", HTMLIFrameElement);
+ handle_element!(document, tag, "img", HTMLImageElement);
+ handle_element!(document, tag, "input", HTMLInputElement);
+ handle_element!(document, tag, "ins", HTMLModElement);
+ handle_element!(document, tag, "isindex", HTMLElement);
+ handle_element!(document, tag, "kbd", HTMLElement);
+ handle_element!(document, tag, "label", HTMLLabelElement);
+ handle_element!(document, tag, "legend", HTMLLegendElement);
+ handle_element!(document, tag, "li", HTMLLIElement);
+ handle_element!(document, tag, "link", HTMLLinkElement);
+ handle_element!(document, tag, "main", HTMLElement);
+ handle_element!(document, tag, "map", HTMLMapElement);
+ handle_element!(document, tag, "mark", HTMLElement);
+ handle_element!(document, tag, "marquee", HTMLElement);
+ handle_element!(document, tag, "meta", HTMLMetaElement);
+ handle_element!(document, tag, "meter", HTMLMeterElement);
+ handle_element!(document, tag, "nav", HTMLElement);
+ handle_element!(document, tag, "nobr", HTMLElement);
+ handle_element!(document, tag, "noframes", HTMLElement);
+ handle_element!(document, tag, "noscript", HTMLElement);
+ handle_element!(document, tag, "object", HTMLObjectElement);
+ handle_element!(document, tag, "ol", HTMLOListElement);
+ handle_element!(document, tag, "optgroup", HTMLOptGroupElement);
+ handle_element!(document, tag, "option", HTMLOptionElement);
+ handle_element!(document, tag, "output", HTMLOutputElement);
+ handle_element!(document, tag, "p", HTMLParagraphElement);
+ handle_element!(document, tag, "param", HTMLParamElement);
+ handle_element!(document, tag, "pre", HTMLPreElement);
+ handle_element!(document, tag, "progress", HTMLProgressElement);
+ handle_element!(document, tag, "q", HTMLQuoteElement);
+ handle_element!(document, tag, "rp", HTMLElement);
+ handle_element!(document, tag, "rt", HTMLElement);
+ handle_element!(document, tag, "ruby", HTMLElement);
+ handle_element!(document, tag, "s", HTMLElement);
+ handle_element!(document, tag, "samp", HTMLElement);
+ handle_element!(document, tag, "script", HTMLScriptElement);
+ handle_element!(document, tag, "section", HTMLElement);
+ handle_element!(document, tag, "select", HTMLSelectElement);
+ handle_element!(document, tag, "small", HTMLElement);
+ handle_element!(document, tag, "source", HTMLSourceElement);
+ handle_element!(document, tag, "spacer", HTMLElement);
+ handle_element!(document, tag, "span", HTMLSpanElement);
+ handle_element!(document, tag, "strike", HTMLElement);
+ handle_element!(document, tag, "strong", HTMLElement);
+ handle_element!(document, tag, "style", HTMLStyleElement);
+ handle_element!(document, tag, "sub", HTMLElement);
+ handle_element!(document, tag, "summary", HTMLElement);
+ handle_element!(document, tag, "sup", HTMLElement);
+ handle_element!(document, tag, "table", HTMLTableElement);
+ handle_element!(document, tag, "tbody", HTMLTableSectionElement);
+ handle_element!(document, tag, "td", HTMLTableDataCellElement);
+ handle_element!(document, tag, "template", HTMLTemplateElement);
+ handle_element!(document, tag, "textarea", HTMLTextAreaElement);
+ handle_element!(document, tag, "th", HTMLTableHeaderCellElement);
+ handle_element!(document, tag, "time", HTMLTimeElement);
+ handle_element!(document, tag, "title", HTMLTitleElement);
+ handle_element!(document, tag, "tr", HTMLTableRowElement);
+ handle_element!(document, tag, "tt", HTMLElement);
+ handle_element!(document, tag, "track", HTMLTrackElement);
+ handle_element!(document, tag, "u", HTMLElement);
+ handle_element!(document, tag, "ul", HTMLUListElement);
+ handle_element!(document, tag, "var", HTMLElement);
+ handle_element!(document, tag, "video", HTMLVideoElement);
+ handle_element!(document, tag, "wbr", HTMLElement);
+
+ return ElementCast::from_temporary(HTMLUnknownElement::new(tag, document));
+}
+
+pub fn parse_html(page: &Page,
+ document: &JSRef<Document>,
+ url: Url,
+ resource_task: ResourceTask)
+ -> HtmlParserResult {
+ debug!("Hubbub: parsing {:?}", url);
+ // Spawn a CSS parser to receive links to CSS style sheets.
+
+ let (discovery_chan, discovery_port) = channel();
+ let stylesheet_chan = discovery_chan.clone();
+ let (css_chan, css_msg_port) = channel();
+ spawn_named("parse_html:css", proc() {
+ css_link_listener(stylesheet_chan, css_msg_port);
+ });
+
+ // Spawn a JS parser to receive JavaScript.
+ let resource_task2 = resource_task.clone();
+ let js_result_chan = discovery_chan.clone();
+ let (js_chan, js_msg_port) = channel();
+ spawn_named("parse_html:js", proc() {
+ js_script_listener(js_result_chan, js_msg_port, resource_task2.clone());
+ });
+
+ // Wait for the LoadResponse so that the parser knows the final URL.
+ let (input_chan, input_port) = channel();
+ resource_task.send(Load(LoadData::new(url.clone()), input_chan));
+ let load_response = input_port.recv();
+
+ debug!("Fetched page; metadata is {:?}", load_response.metadata);
+
+ load_response.metadata.headers.map(|headers| {
+ let header = headers.iter().find(|h|
+ h.header_name().as_slice().to_ascii_lower() == "last-modified".to_string()
+ );
+
+ match header {
+ Some(h) => document.set_last_modified(
+ parse_last_modified(h.header_value().as_slice())),
+ None => {},
+ };
+ });
+
+ let base_url = &load_response.metadata.final_url;
+
+ {
+ // Store the final URL before we start parsing, so that DOM routines
+ // (e.g. HTMLImageElement::update_image) can resolve relative URLs
+ // correctly.
+ *page.mut_url() = Some((base_url.clone(), true));
+ }
+
+ let mut parser = build_parser(unsafe { document.to_hubbub_node() });
+ debug!("created parser");
+
+ let (css_chan2, js_chan2) = (css_chan.clone(), js_chan.clone());
+
+ let doc_cell = RefCell::new(document);
+
+ let mut tree_handler = hubbub::TreeHandler {
+ create_comment: |data: String| {
+ debug!("create comment");
+ // NOTE: tmp vars are workaround for lifetime issues. Both required.
+ let tmp_borrow = doc_cell.borrow();
+ let tmp = &*tmp_borrow;
+ let comment = Comment::new(data, *tmp).root();
+ let comment: &JSRef<Node> = NodeCast::from_ref(&*comment);
+ unsafe { comment.to_hubbub_node() }
+ },
+ create_doctype: |box hubbub::Doctype { name: name, public_id: public_id, system_id: system_id, ..}: Box<hubbub::Doctype>| {
+ debug!("create doctype");
+ // NOTE: tmp vars are workaround for lifetime issues. Both required.
+ let tmp_borrow = doc_cell.borrow();
+ let tmp = &*tmp_borrow;
+ let doctype_node = DocumentType::new(name, public_id, system_id, *tmp).root();
+ unsafe {
+ doctype_node.deref().to_hubbub_node()
+ }
+ },
+ create_element: |tag: Box<hubbub::Tag>| {
+ debug!("create element {}", tag.name);
+ // NOTE: tmp vars are workaround for lifetime issues. Both required.
+ let tmp_borrow = doc_cell.borrow();
+ let tmp = &*tmp_borrow;
+ let namespace = match tag.ns {
+ HtmlNs => namespace::HTML,
+ MathMlNs => namespace::MathML,
+ SvgNs => namespace::SVG,
+ ns => fail!("Not expecting namespace {:?}", ns),
+ };
+ let element: Root<Element> = build_element_from_tag(tag.name.clone(), namespace, *tmp).root();
+
+ debug!("-- attach attrs");
+ for attr in tag.attributes.iter() {
+ let (namespace, prefix) = match attr.ns {
+ NullNs => (namespace::Null, None),
+ XLinkNs => (namespace::XLink, Some("xlink")),
+ XmlNs => (namespace::XML, Some("xml")),
+ XmlNsNs => (namespace::XMLNS, Some("xmlns")),
+ ns => fail!("Not expecting namespace {:?}", ns),
+ };
+ element.set_attribute_from_parser(Atom::from_slice(attr.name.as_slice()),
+ attr.value.clone(),
+ namespace,
+ prefix.map(|p| p.to_string()));
+ }
+
+ //FIXME: workaround for https://github.com/mozilla/rust/issues/13246;
+ // we get unrooting order failures if these are inside the match.
+ let rel = {
+ let rel = element.deref().get_attribute(Null, "rel").root();
+ rel.map(|a| a.deref().Value())
+ };
+ let href = {
+ let href= element.deref().get_attribute(Null, "href").root();
+ href.map(|a| a.deref().Value())
+ };
+
+ // Spawn additional parsing, network loads, etc. from tag and attrs
+ let type_id = {
+ let node: &JSRef<Node> = NodeCast::from_ref(&*element);
+ node.type_id()
+ };
+ match type_id {
+ // Handle CSS style sheets from <link> elements
+ ElementNodeTypeId(HTMLLinkElementTypeId) => {
+ match (rel, href) {
+ (Some(ref rel), Some(ref href)) => {
+ if rel.as_slice()
+ .split(HTML_SPACE_CHARACTERS.as_slice())
+ .any(|s| {
+ s.as_slice().eq_ignore_ascii_case("stylesheet")
+ }) {
+ debug!("found CSS stylesheet: {:s}", *href);
+ match UrlParser::new().base_url(base_url).parse(href.as_slice()) {
+ Ok(url) => css_chan2.send(CSSTaskNewFile(
+ UrlProvenance(url, resource_task.clone()))),
+ Err(e) => debug!("Parsing url {:s} failed: {:?}", *href, e)
+ };
+ }
+ }
+ _ => {}
+ }
+ }
+ _ => {}
+ }
+
+ unsafe { element.deref().to_hubbub_node() }
+ },
+ create_text: |data: String| {
+ debug!("create text");
+ // NOTE: tmp vars are workaround for lifetime issues. Both required.
+ let tmp_borrow = doc_cell.borrow();
+ let tmp = &*tmp_borrow;
+ let text = Text::new(data, *tmp).root();
+ unsafe { text.deref().to_hubbub_node() }
+ },
+ ref_node: |_| {},
+ unref_node: |_| {},
+ append_child: |parent: hubbub::NodeDataPtr, child: hubbub::NodeDataPtr| {
+ unsafe {
+ debug!("append child {:x} {:x}", parent, child);
+ let child: Root<Node> = from_hubbub_node(child).root();
+ let parent: Root<Node> = from_hubbub_node(parent).root();
+ assert!(parent.deref().AppendChild(&*child).is_ok());
+ }
+ child
+ },
+ insert_before: |_parent, _child| {
+ debug!("insert before");
+ 0u
+ },
+ remove_child: |_parent, _child| {
+ debug!("remove child");
+ 0u
+ },
+ clone_node: |_node, deep| {
+ debug!("clone node");
+ if deep { error!("-- deep clone unimplemented"); }
+ fail!("clone node unimplemented")
+ },
+ reparent_children: |_node, _new_parent| {
+ debug!("reparent children");
+ 0u
+ },
+ get_parent: |_node, _element_only| {
+ debug!("get parent");
+ 0u
+ },
+ has_children: |_node| {
+ debug!("has children");
+ false
+ },
+ form_associate: |_form, _node| {
+ debug!("form associate");
+ },
+ add_attributes: |_node, _attributes| {
+ debug!("add attributes");
+ },
+ set_quirks_mode: |mode| {
+ debug!("set quirks mode");
+ // NOTE: tmp vars are workaround for lifetime issues. Both required.
+ let tmp_borrow = doc_cell.borrow_mut();
+ let tmp = &*tmp_borrow;
+ tmp.set_quirks_mode(mode);
+ },
+ encoding_change: |encname| {
+ debug!("encoding change");
+ // NOTE: tmp vars are workaround for lifetime issues. Both required.
+ let tmp_borrow = doc_cell.borrow_mut();
+ let tmp = &*tmp_borrow;
+ tmp.set_encoding_name(encname);
+ },
+ complete_script: |script| {
+ unsafe {
+ let script = from_hubbub_node::<Node>(script).root();
+ let script: Option<&JSRef<HTMLScriptElement>> =
+ HTMLScriptElementCast::to_ref(&*script);
+ let script = match script {
+ Some(script) if script.is_javascript() => script,
+ _ => return,
+ };
+
+ let script_element: &JSRef<Element> = ElementCast::from_ref(script);
+ match script_element.get_attribute(Null, "src").root() {
+ Some(src) => {
+ debug!("found script: {:s}", src.deref().Value());
+ match UrlParser::new().base_url(base_url)
+ .parse(src.deref().value().as_slice()) {
+ Ok(new_url) => js_chan2.send(JSTaskNewFile(new_url)),
+ Err(e) => debug!("Parsing url {:s} failed: {:?}", src.deref().Value(), e)
+ };
+ }
+ None => {
+ let mut data = String::new();
+ let scriptnode: &JSRef<Node> = NodeCast::from_ref(script);
+ debug!("iterating over children {:?}", scriptnode.first_child());
+ for child in scriptnode.children() {
+ debug!("child = {:?}", child);
+ let text: &JSRef<Text> = TextCast::to_ref(&child).unwrap();
+ data.push_str(text.deref().characterdata.data.deref().borrow().as_slice());
+ }
+
+ debug!("script data = {:?}", data);
+ js_chan2.send(JSTaskNewInlineScript(data, base_url.clone()));
+ }
+ }
+ }
+ debug!("complete script");
+ },
+ complete_style: |_| {
+ // style parsing is handled in element::notify_child_list_changed.
+ },
+ };
+ parser.set_tree_handler(&mut tree_handler);
+ debug!("set tree handler");
+ debug!("loaded page");
+ match load_response.metadata.content_type {
+ Some((ref t, _)) if t.as_slice().eq_ignore_ascii_case("image") => {
+ let page = format!("<html><body><img src='{:s}' /></body></html>", base_url.serialize());
+ parser.parse_chunk(page.into_bytes().as_slice());
+ },
+ _ => loop {
+ match load_response.progress_port.recv() {
+ Payload(data) => {
+ debug!("received data");
+ parser.parse_chunk(data.as_slice());
+ }
+ Done(Err(err)) => {
+ fail!("Failed to load page URL {:s}, error: {:s}", url.serialize(), err);
+ }
+ Done(..) => {
+ break;
+ }
+ }
+ }
+ }
+
+ debug!("finished parsing");
+ css_chan.send(CSSTaskExit);
+ js_chan.send(JSTaskExit);
+
+ HtmlParserResult {
+ discovery_port: discovery_port,
+ }
+}
+
+fn build_parser<'a>(node: hubbub::NodeDataPtr) -> hubbub::Parser<'a> {
+ let mut parser = hubbub::Parser::new("UTF-8", false);
+ parser.set_document_node(node);
+ parser.enable_scripting(true);
+ parser.enable_styling(true);
+ parser
+}
+
diff --git a/components/script/layout_interface.rs b/components/script/layout_interface.rs
new file mode 100644
index 00000000000..1e5e23f9c9a
--- /dev/null
+++ b/components/script/layout_interface.rs
@@ -0,0 +1,204 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! The high-level interface from script to layout. Using this abstract interface helps reduce
+/// coupling between these two components, and enables the DOM to be placed in a separate crate
+/// from layout.
+
+use dom::bindings::js::JS;
+use dom::node::{Node, LayoutDataRef};
+
+use geom::point::Point2D;
+use geom::rect::Rect;
+use libc::c_void;
+use script_traits::{ScriptControlChan, OpaqueScriptLayoutChannel};
+use servo_msg::constellation_msg::WindowSizeData;
+use servo_util::geometry::Au;
+use std::any::{Any, AnyRefExt};
+use std::cmp;
+use std::comm::{channel, Receiver, Sender};
+use std::owned::BoxAny;
+use style::Stylesheet;
+use url::Url;
+
+use serialize::{Encodable, Encoder};
+
+/// Asynchronous messages that script can send to layout.
+pub enum Msg {
+ /// Adds the given stylesheet to the document.
+ AddStylesheetMsg(Stylesheet),
+
+ /// Requests a reflow.
+ ReflowMsg(Box<Reflow>),
+
+ /// Get an RPC interface.
+ GetRPCMsg(Sender<Box<LayoutRPC + Send>>),
+
+ /// Destroys layout data associated with a DOM node.
+ ///
+ /// TODO(pcwalton): Maybe think about batching to avoid message traffic.
+ ReapLayoutDataMsg(LayoutDataRef),
+
+ /// Requests that the layout task enter a quiescent state in which no more messages are
+ /// accepted except `ExitMsg`. A response message will be sent on the supplied channel when
+ /// this happens.
+ PrepareToExitMsg(Sender<()>),
+
+ /// Requests that the layout task immediately shut down. There must be no more nodes left after
+ /// this, or layout will crash.
+ ExitNowMsg,
+}
+
+/// Synchronous messages that script can send to layout.
+///
+/// In general, you should use messages to talk to Layout. Use the RPC interface
+/// if and only if the work is
+///
+/// 1) read-only with respect to LayoutTaskData,
+/// 2) small,
+// 3) and really needs to be fast.
+pub trait LayoutRPC {
+ /// Requests the dimensions of the content box, as in the `getBoundingClientRect()` call.
+ fn content_box(&self, node: TrustedNodeAddress) -> ContentBoxResponse;
+ /// Requests the dimensions of all the content boxes, as in the `getClientRects()` call.
+ fn content_boxes(&self, node: TrustedNodeAddress) -> ContentBoxesResponse;
+ /// Requests the node containing the point of interest
+ fn hit_test(&self, node: TrustedNodeAddress, point: Point2D<f32>) -> Result<HitTestResponse, ()>;
+ fn mouse_over(&self, node: TrustedNodeAddress, point: Point2D<f32>) -> Result<MouseOverResponse, ()>;
+}
+
+/// The address of a node known to be valid. These must only be sent from content -> layout,
+/// because we do not trust layout.
+pub struct TrustedNodeAddress(pub *const c_void);
+
+impl<S: Encoder<E>, E> Encodable<S, E> for TrustedNodeAddress {
+ fn encode(&self, s: &mut S) -> Result<(), E> {
+ let TrustedNodeAddress(addr) = *self;
+ let node = addr as *const Node;
+ unsafe {
+ JS::from_raw(node).encode(s)
+ }
+ }
+}
+
+/// The address of a node. Layout sends these back. They must be validated via
+/// `from_untrusted_node_address` before they can be used, because we do not trust layout.
+pub type UntrustedNodeAddress = *const c_void;
+
+pub struct ContentBoxResponse(pub Rect<Au>);
+pub struct ContentBoxesResponse(pub Vec<Rect<Au>>);
+pub struct HitTestResponse(pub UntrustedNodeAddress);
+pub struct MouseOverResponse(pub Vec<UntrustedNodeAddress>);
+
+/// Determines which part of the
+#[deriving(PartialEq, PartialOrd, Eq, Ord, Encodable)]
+pub enum DocumentDamageLevel {
+ /// Reflow, but do not perform CSS selector matching.
+ ReflowDocumentDamage,
+ /// Perform CSS selector matching and reflow.
+ MatchSelectorsDocumentDamage,
+ /// Content changed; set full style damage and do the above.
+ ContentChangedDocumentDamage,
+}
+
+impl DocumentDamageLevel {
+ /// Sets this damage to the maximum of this damage and the given damage.
+ pub fn add(&mut self, new_damage: DocumentDamageLevel) {
+ *self = cmp::max(*self, new_damage);
+ }
+}
+
+/// What parts of the document have changed, as far as the script task can tell.
+///
+/// Note that this is fairly coarse-grained and is separate from layout's notion of the document
+#[deriving(Encodable)]
+pub struct DocumentDamage {
+ /// The topmost node in the tree that has changed.
+ pub root: TrustedNodeAddress,
+ /// The amount of damage that occurred.
+ pub level: DocumentDamageLevel,
+}
+
+/// Why we're doing reflow.
+#[deriving(PartialEq)]
+pub enum ReflowGoal {
+ /// We're reflowing in order to send a display list to the screen.
+ ReflowForDisplay,
+ /// We're reflowing in order to satisfy a script query. No display list will be created.
+ ReflowForScriptQuery,
+}
+
+/// Information needed for a reflow.
+pub struct Reflow {
+ /// The document node.
+ pub document_root: TrustedNodeAddress,
+ /// The style changes that need to be done.
+ pub damage: DocumentDamage,
+ /// The goal of reflow: either to render to the screen or to flush layout info for script.
+ pub goal: ReflowGoal,
+ /// The URL of the page.
+ pub url: Url,
+ /// The channel through which messages can be sent back to the script task.
+ pub script_chan: ScriptControlChan,
+ /// The current window size.
+ pub window_size: WindowSizeData,
+ /// The channel that we send a notification to.
+ pub script_join_chan: Sender<()>,
+ /// Unique identifier
+ pub id: uint
+}
+
+/// Encapsulates a channel to the layout task.
+#[deriving(Clone)]
+pub struct LayoutChan(pub Sender<Msg>);
+
+impl LayoutChan {
+ pub fn new() -> (Receiver<Msg>, LayoutChan) {
+ let (chan, port) = channel();
+ (port, LayoutChan(chan))
+ }
+}
+
+/// A trait to manage opaque references to script<->layout channels without needing
+/// to expose the message type to crates that don't need to know about them.
+pub trait ScriptLayoutChan {
+ fn new(sender: Sender<Msg>, receiver: Receiver<Msg>) -> Self;
+ fn sender(&self) -> Sender<Msg>;
+ fn receiver(self) -> Receiver<Msg>;
+}
+
+impl ScriptLayoutChan for OpaqueScriptLayoutChannel {
+ fn new(sender: Sender<Msg>, receiver: Receiver<Msg>) -> OpaqueScriptLayoutChannel {
+ let inner = (box sender as Box<Any+Send>, box receiver as Box<Any+Send>);
+ OpaqueScriptLayoutChannel(inner)
+ }
+
+ fn sender(&self) -> Sender<Msg> {
+ let &OpaqueScriptLayoutChannel((ref sender, _)) = self;
+ (*sender.downcast_ref::<Sender<Msg>>().unwrap()).clone()
+ }
+
+ fn receiver(self) -> Receiver<Msg> {
+ let OpaqueScriptLayoutChannel((_, receiver)) = self;
+ *receiver.downcast::<Receiver<Msg>>().unwrap()
+ }
+}
+
+#[test]
+fn test_add_damage() {
+ fn assert_add(mut a: DocumentDamageLevel, b: DocumentDamageLevel,
+ result: DocumentDamageLevel) {
+ a.add(b);
+ assert!(a == result);
+ }
+
+ assert_add(ReflowDocumentDamage, ReflowDocumentDamage, ReflowDocumentDamage);
+ assert_add(ContentChangedDocumentDamage, ContentChangedDocumentDamage, ContentChangedDocumentDamage);
+ assert_add(ReflowDocumentDamage, MatchSelectorsDocumentDamage, MatchSelectorsDocumentDamage);
+ assert_add(MatchSelectorsDocumentDamage, ReflowDocumentDamage, MatchSelectorsDocumentDamage);
+ assert_add(ReflowDocumentDamage, ContentChangedDocumentDamage, ContentChangedDocumentDamage);
+ assert_add(ContentChangedDocumentDamage, ReflowDocumentDamage, ContentChangedDocumentDamage);
+ assert_add(MatchSelectorsDocumentDamage, ContentChangedDocumentDamage, ContentChangedDocumentDamage);
+ assert_add(ContentChangedDocumentDamage, MatchSelectorsDocumentDamage, ContentChangedDocumentDamage);
+}
diff --git a/components/script/lib.rs b/components/script/lib.rs
new file mode 100644
index 00000000000..6e44b295968
--- /dev/null
+++ b/components/script/lib.rs
@@ -0,0 +1,209 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#![comment = "The Servo Parallel Browser Project"]
+#![license = "MPL"]
+
+#![feature(globs, macro_rules, struct_variant, phase, unsafe_destructor)]
+
+#![feature(phase)]
+
+#![doc="The script crate contains all matters DOM."]
+
+#![allow(non_snake_case_functions)]
+
+#[phase(plugin, link)]
+extern crate log;
+
+extern crate debug;
+extern crate cssparser;
+extern crate collections;
+extern crate geom;
+extern crate hubbub;
+extern crate encoding;
+extern crate http;
+extern crate js;
+extern crate libc;
+extern crate native;
+extern crate net;
+extern crate rustrt;
+extern crate serialize;
+extern crate time;
+extern crate canvas;
+extern crate script_traits;
+#[phase(plugin)]
+extern crate servo_macros = "macros";
+extern crate servo_net = "net";
+extern crate servo_util = "util";
+extern crate style;
+extern crate sync;
+extern crate servo_msg = "msg";
+extern crate url;
+
+pub mod cors;
+
+/// The implementation of the DOM.
+pub mod dom {
+ /// The code to expose the DOM to JavaScript through IDL bindings.
+ pub mod bindings {
+ pub mod global;
+ pub mod js;
+ pub mod utils;
+ pub mod callback;
+ pub mod error;
+ pub mod conversions;
+ mod proxyhandler;
+ pub mod str;
+ pub mod trace;
+
+ /// Generated JS-Rust bindings.
+ pub mod codegen {
+ pub mod Bindings;
+ pub mod InterfaceTypes;
+ pub mod InheritTypes;
+ pub mod PrototypeList;
+ pub mod RegisterBindings;
+ pub mod UnionTypes;
+ }
+ }
+
+ #[path="bindings/codegen/InterfaceTypes.rs"]
+ pub mod types;
+ pub mod macros;
+
+ pub mod attr;
+ pub mod blob;
+ pub mod browsercontext;
+ pub mod canvasrenderingcontext2d;
+ pub mod characterdata;
+ pub mod domrect;
+ pub mod domrectlist;
+ pub mod comment;
+ pub mod console;
+ pub mod customevent;
+ pub mod dedicatedworkerglobalscope;
+ pub mod document;
+ pub mod documentfragment;
+ pub mod documenttype;
+ pub mod domexception;
+ pub mod domimplementation;
+ pub mod domparser;
+ pub mod domtokenlist;
+ pub mod element;
+ pub mod event;
+ pub mod eventdispatcher;
+ pub mod eventtarget;
+ pub mod file;
+ pub mod formdata;
+ pub mod htmlanchorelement;
+ pub mod htmlappletelement;
+ pub mod htmlareaelement;
+ pub mod htmlaudioelement;
+ pub mod htmlbaseelement;
+ pub mod htmlbodyelement;
+ pub mod htmlbrelement;
+ pub mod htmlbuttonelement;
+ pub mod htmlcanvaselement;
+ pub mod htmlcollection;
+ pub mod htmldataelement;
+ pub mod htmldatalistelement;
+ pub mod htmldirectoryelement;
+ pub mod htmldivelement;
+ pub mod htmldlistelement;
+ pub mod htmlelement;
+ pub mod htmlembedelement;
+ pub mod htmlfieldsetelement;
+ pub mod htmlfontelement;
+ pub mod htmlformelement;
+ pub mod htmlframeelement;
+ pub mod htmlframesetelement;
+ pub mod htmlheadelement;
+ pub mod htmlheadingelement;
+ pub mod htmlhrelement;
+ pub mod htmlhtmlelement;
+ pub mod htmliframeelement;
+ pub mod htmlimageelement;
+ pub mod htmlinputelement;
+ pub mod htmllabelelement;
+ pub mod htmllegendelement;
+ pub mod htmllielement;
+ pub mod htmllinkelement;
+ pub mod htmlmapelement;
+ pub mod htmlmediaelement;
+ pub mod htmlmetaelement;
+ pub mod htmlmeterelement;
+ pub mod htmlmodelement;
+ pub mod htmlobjectelement;
+ pub mod htmlolistelement;
+ pub mod htmloptgroupelement;
+ pub mod htmloptionelement;
+ pub mod htmloutputelement;
+ pub mod htmlparagraphelement;
+ pub mod htmlparamelement;
+ pub mod htmlpreelement;
+ pub mod htmlprogresselement;
+ pub mod htmlquoteelement;
+ pub mod htmlscriptelement;
+ pub mod htmlselectelement;
+ pub mod htmlserializer;
+ pub mod htmlspanelement;
+ pub mod htmlsourceelement;
+ pub mod htmlstyleelement;
+ pub mod htmltableelement;
+ pub mod htmltablecaptionelement;
+ pub mod htmltablecellelement;
+ pub mod htmltabledatacellelement;
+ pub mod htmltableheadercellelement;
+ pub mod htmltablecolelement;
+ pub mod htmltablerowelement;
+ pub mod htmltablesectionelement;
+ pub mod htmltemplateelement;
+ pub mod htmltextareaelement;
+ pub mod htmltimeelement;
+ pub mod htmltitleelement;
+ pub mod htmltrackelement;
+ pub mod htmlulistelement;
+ pub mod htmlvideoelement;
+ pub mod htmlunknownelement;
+ pub mod location;
+ pub mod messageevent;
+ pub mod mouseevent;
+ pub mod namednodemap;
+ pub mod navigator;
+ pub mod node;
+ pub mod nodeiterator;
+ pub mod nodelist;
+ pub mod processinginstruction;
+ pub mod performance;
+ pub mod performancetiming;
+ pub mod progressevent;
+ pub mod range;
+ pub mod screen;
+ pub mod text;
+ pub mod treewalker;
+ pub mod uievent;
+ pub mod urlsearchparams;
+ pub mod validitystate;
+ pub mod virtualmethods;
+ pub mod window;
+ pub mod worker;
+ pub mod workerglobalscope;
+ pub mod workerlocation;
+ pub mod workernavigator;
+ pub mod xmlhttprequest;
+ pub mod xmlhttprequesteventtarget;
+ pub mod xmlhttprequestupload;
+
+ pub mod testbinding;
+}
+
+/// Parsers for HTML and CSS.
+pub mod html {
+ pub mod cssparse;
+ pub mod hubbub_html_parser;
+}
+
+pub mod layout_interface;
+pub mod page;
+pub mod script_task;
diff --git a/components/script/makefile.cargo b/components/script/makefile.cargo
new file mode 100644
index 00000000000..652bcab9000
--- /dev/null
+++ b/components/script/makefile.cargo
@@ -0,0 +1,45 @@
+# Recursive wildcard function
+# http://blog.jgc.org/2011/07/gnu-make-recursive-wildcard-function.html
+rwildcard=$(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) \
+ $(filter $(subst *,%,$2),$d))
+
+PYTHON = $(shell which python2.7 2>/dev/null || echo python)
+BINDINGS_SRC = $(shell pwd)/dom/bindings/codegen
+WEBIDLS_SRC = $(shell pwd)/dom/webidls
+WEBIDLS = $(call rwildcard,$(WEBIDLS_SRC),*.webidl)
+BINDINGS = $(patsubst %.webidl,%Binding.rs,$(WEBIDLS))
+AUTOGEN_SRC = $(foreach var,$(BINDINGS),$(subst $(WEBIDLS_SRC),$(BINDINGS_SRC)/Bindings,$(var)))
+
+CACHE_DIR = $(BINDINGS_SRC)/_cache
+
+bindinggen_dependencies := $(addprefix $(BINDINGS_SRC)/,BindingGen.py Bindings.conf Configuration.py CodegenRust.py parser/WebIDL.py ParserResults.pkl Bindings/.done)
+
+globalgen_dependencies := $(addprefix $(BINDINGS_SRC)/,GlobalGen.py Bindings.conf Configuration.py CodegenRust.py parser/WebIDL.py) $(CACHE_DIR)/.done $(BINDINGS_SRC)/Bindings/.done
+
+.PHONY: all
+all: $(AUTOGEN_SRC)
+
+$(BINDINGS_SRC)/Bindings/.done:
+ mkdir -p $(BINDINGS_SRC)/Bindings
+ touch $@
+
+$(CACHE_DIR)/.done:
+ mkdir -p $(CACHE_DIR)
+ touch $@
+
+$(BINDINGS_SRC)/ParserResults.pkl: $(globalgen_dependencies) $(WEBIDLS)
+ $(PYTHON) $(BINDINGS_SRC)/pythonpath.py \
+ -I$(BINDINGS_SRC)/parser -I$(BINDINGS_SRC)/ply \
+ -D$(BINDINGS_SRC) \
+ $(BINDINGS_SRC)/GlobalGen.py $(BINDINGS_SRC)/Bindings.conf . \
+ --cachedir=$(CACHE_DIR) \
+ $(WEBIDLS)
+
+$(AUTOGEN_SRC): $(BINDINGS_SRC)/Bindings/%Binding.rs: $(bindinggen_dependencies) \
+ $(addprefix $(WEBIDLS_SRC)/,%.webidl)
+ $(PYTHON) $(BINDINGS_SRC)/pythonpath.py \
+ -I$(BINDINGS_SRC)/parser -I$(BINDINGS_SRC)/ply \
+ -D$(BINDINGS_SRC) \
+ $(BINDINGS_SRC)/BindingGen.py \
+ $(BINDINGS_SRC)/Bindings.conf Bindings/$*Binding $(addprefix $(WEBIDLS_SRC)/,$*.webidl)
+ touch $@
diff --git a/components/script/page.rs b/components/script/page.rs
new file mode 100644
index 00000000000..633a7de204b
--- /dev/null
+++ b/components/script/page.rs
@@ -0,0 +1,437 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
+use dom::bindings::codegen::InheritTypes::{NodeCast, ElementCast};
+use dom::bindings::js::{JS, JSRef, Temporary};
+use dom::bindings::js::OptionalRootable;
+use dom::bindings::trace::{Traceable, Untraceable};
+use dom::bindings::utils::GlobalStaticData;
+use dom::document::{Document, DocumentHelpers};
+use dom::element::{Element, AttributeHandlers};
+use dom::node::{Node, NodeHelpers};
+use dom::window::Window;
+use layout_interface::{DocumentDamage};
+use layout_interface::{DocumentDamageLevel, HitTestResponse, MouseOverResponse};
+use layout_interface::{GetRPCMsg, LayoutChan, LayoutRPC};
+use layout_interface::{Reflow, ReflowGoal, ReflowMsg};
+use layout_interface::UntrustedNodeAddress;
+use script_traits::ScriptControlChan;
+
+use geom::point::Point2D;
+use js::rust::Cx;
+use servo_msg::compositor_msg::PerformingLayout;
+use servo_msg::compositor_msg::ScriptListener;
+use servo_msg::constellation_msg::{ConstellationChan, WindowSizeData};
+use servo_msg::constellation_msg::{PipelineId, SubpageId};
+use servo_net::resource_task::ResourceTask;
+use servo_util::namespace::Null;
+use servo_util::str::DOMString;
+use std::cell::{Cell, RefCell, Ref, RefMut};
+use std::comm::{channel, Receiver, Empty, Disconnected};
+use std::mem::replace;
+use std::rc::Rc;
+use url::Url;
+
+use serialize::{Encoder, Encodable};
+
+/// Encapsulates a handle to a frame and its associated layout information.
+#[deriving(Encodable)]
+pub struct Page {
+ /// Pipeline id associated with this page.
+ pub id: PipelineId,
+
+ /// Subpage id associated with this page, if any.
+ pub subpage_id: Option<SubpageId>,
+
+ /// Unique id for last reflow request; used for confirming completion reply.
+ pub last_reflow_id: Traceable<Cell<uint>>,
+
+ /// The outermost frame containing the document, window, and page URL.
+ pub frame: Traceable<RefCell<Option<Frame>>>,
+
+ /// A handle for communicating messages to the layout task.
+ pub layout_chan: Untraceable<LayoutChan>,
+
+ /// A handle to perform RPC calls into the layout, quickly.
+ pub layout_rpc: Untraceable<Box<LayoutRPC>>,
+
+ /// The port that we will use to join layout. If this is `None`, then layout is not running.
+ pub layout_join_port: Untraceable<RefCell<Option<Receiver<()>>>>,
+
+ /// What parts of the document are dirty, if any.
+ damage: Traceable<RefCell<Option<DocumentDamage>>>,
+
+ /// The current size of the window, in pixels.
+ pub window_size: Traceable<Cell<WindowSizeData>>,
+
+ js_info: Traceable<RefCell<Option<JSPageInfo>>>,
+
+ /// Cached copy of the most recent url loaded by the script
+ /// TODO(tkuehn): this currently does not follow any particular caching policy
+ /// and simply caches pages forever (!). The bool indicates if reflow is required
+ /// when reloading.
+ url: Untraceable<RefCell<Option<(Url, bool)>>>,
+
+ next_subpage_id: Traceable<Cell<SubpageId>>,
+
+ /// Pending resize event, if any.
+ pub resize_event: Untraceable<Cell<Option<WindowSizeData>>>,
+
+ /// Pending scroll to fragment event, if any
+ pub fragment_node: Cell<Option<JS<Element>>>,
+
+ /// Associated resource task for use by DOM objects like XMLHttpRequest
+ pub resource_task: Untraceable<ResourceTask>,
+
+ /// A handle for communicating messages to the constellation task.
+ pub constellation_chan: Untraceable<ConstellationChan>,
+
+ // Child Pages.
+ pub children: Traceable<RefCell<Vec<Rc<Page>>>>,
+}
+
+pub struct PageIterator {
+ stack: Vec<Rc<Page>>,
+}
+
+pub trait IterablePage {
+ fn iter(&self) -> PageIterator;
+ fn find(&self, id: PipelineId) -> Option<Rc<Page>>;
+}
+
+impl IterablePage for Rc<Page> {
+ fn iter(&self) -> PageIterator {
+ PageIterator {
+ stack: vec!(self.clone()),
+ }
+ }
+ fn find(&self, id: PipelineId) -> Option<Rc<Page>> {
+ if self.id == id { return Some(self.clone()); }
+ for page in self.children.deref().borrow().iter() {
+ let found = page.find(id);
+ if found.is_some() { return found; }
+ }
+ None
+ }
+
+}
+
+impl Page {
+ pub fn new(id: PipelineId, subpage_id: Option<SubpageId>,
+ layout_chan: LayoutChan,
+ window_size: WindowSizeData,
+ resource_task: ResourceTask,
+ constellation_chan: ConstellationChan,
+ js_context: Rc<Cx>) -> Page {
+ let js_info = JSPageInfo {
+ dom_static: GlobalStaticData(),
+ js_context: Untraceable::new(js_context),
+ };
+ let layout_rpc: Box<LayoutRPC> = {
+ let (rpc_send, rpc_recv) = channel();
+ let LayoutChan(ref lchan) = layout_chan;
+ lchan.send(GetRPCMsg(rpc_send));
+ rpc_recv.recv()
+ };
+ Page {
+ id: id,
+ subpage_id: subpage_id,
+ frame: Traceable::new(RefCell::new(None)),
+ layout_chan: Untraceable::new(layout_chan),
+ layout_rpc: Untraceable::new(layout_rpc),
+ layout_join_port: Untraceable::new(RefCell::new(None)),
+ damage: Traceable::new(RefCell::new(None)),
+ window_size: Traceable::new(Cell::new(window_size)),
+ js_info: Traceable::new(RefCell::new(Some(js_info))),
+ url: Untraceable::new(RefCell::new(None)),
+ next_subpage_id: Traceable::new(Cell::new(SubpageId(0))),
+ resize_event: Untraceable::new(Cell::new(None)),
+ fragment_node: Cell::new(None),
+ last_reflow_id: Traceable::new(Cell::new(0)),
+ resource_task: Untraceable::new(resource_task),
+ constellation_chan: Untraceable::new(constellation_chan),
+ children: Traceable::new(RefCell::new(vec!())),
+ }
+ }
+
+ // must handle root case separately
+ pub fn remove(&self, id: PipelineId) -> Option<Rc<Page>> {
+ let remove_idx = {
+ self.children
+ .deref()
+ .borrow_mut()
+ .mut_iter()
+ .enumerate()
+ .find(|&(_idx, ref page_tree)| {
+ // FIXME: page_tree has a lifetime such that it's unusable for anything.
+ let page_tree_id = page_tree.id;
+ page_tree_id == id
+ })
+ .map(|(idx, _)| idx)
+ };
+ match remove_idx {
+ Some(idx) => return Some(self.children.deref().borrow_mut().remove(idx).unwrap()),
+ None => {
+ for page_tree in self.children.deref().borrow_mut().mut_iter() {
+ match page_tree.remove(id) {
+ found @ Some(_) => return found,
+ None => (), // keep going...
+ }
+ }
+ }
+ }
+ None
+ }
+}
+
+impl Iterator<Rc<Page>> for PageIterator {
+ fn next(&mut self) -> Option<Rc<Page>> {
+ if !self.stack.is_empty() {
+ let next = self.stack.pop().unwrap();
+ for child in next.children.deref().borrow().iter() {
+ self.stack.push(child.clone());
+ }
+ Some(next.clone())
+ } else {
+ None
+ }
+ }
+}
+
+impl Page {
+ pub fn mut_js_info<'a>(&'a self) -> RefMut<'a, Option<JSPageInfo>> {
+ self.js_info.deref().borrow_mut()
+ }
+
+ pub fn js_info<'a>(&'a self) -> Ref<'a, Option<JSPageInfo>> {
+ self.js_info.deref().borrow()
+ }
+
+ pub fn url<'a>(&'a self) -> Ref<'a, Option<(Url, bool)>> {
+ self.url.deref().borrow()
+ }
+
+ pub fn mut_url<'a>(&'a self) -> RefMut<'a, Option<(Url, bool)>> {
+ self.url.deref().borrow_mut()
+ }
+
+ pub fn frame<'a>(&'a self) -> Ref<'a, Option<Frame>> {
+ self.frame.deref().borrow()
+ }
+
+ pub fn mut_frame<'a>(&'a self) -> RefMut<'a, Option<Frame>> {
+ self.frame.deref().borrow_mut()
+ }
+
+ pub fn get_next_subpage_id(&self) -> SubpageId {
+ let subpage_id = self.next_subpage_id.deref().get();
+ let SubpageId(id_num) = subpage_id;
+ self.next_subpage_id.deref().set(SubpageId(id_num + 1));
+ subpage_id
+ }
+
+ /// Adds the given damage.
+ pub fn damage(&self, level: DocumentDamageLevel) {
+ let root = match *self.frame() {
+ None => return,
+ Some(ref frame) => frame.document.root().GetDocumentElement()
+ };
+ match root.root() {
+ None => {},
+ Some(root) => {
+ let root: &JSRef<Node> = NodeCast::from_ref(&*root);
+ let mut damage = *self.damage.deref().borrow_mut();
+ match damage {
+ None => {}
+ Some(ref mut damage) => {
+ // FIXME(pcwalton): This is wrong. We should trace up to the nearest ancestor.
+ damage.root = root.to_trusted_node_address();
+ damage.level.add(level);
+ return
+ }
+ }
+
+ *self.damage.deref().borrow_mut() = Some(DocumentDamage {
+ root: root.to_trusted_node_address(),
+ level: level,
+ })
+ }
+ };
+ }
+
+ pub fn get_url(&self) -> Url {
+ self.url().get_ref().ref0().clone()
+ }
+
+ // FIXME(cgaebel): join_layout is racey. What if the compositor triggers a
+ // reflow between the "join complete" message and returning from this
+ // function?
+
+ /// Sends a ping to layout and waits for the response. The response will arrive when the
+ /// layout task has finished any pending request messages.
+ pub fn join_layout(&self) {
+ let mut layout_join_port = self.layout_join_port.deref().borrow_mut();
+ if layout_join_port.is_some() {
+ let join_port = replace(&mut *layout_join_port, None);
+ match join_port {
+ Some(ref join_port) => {
+ match join_port.try_recv() {
+ Err(Empty) => {
+ info!("script: waiting on layout");
+ join_port.recv();
+ }
+ Ok(_) => {}
+ Err(Disconnected) => {
+ fail!("Layout task failed while script was waiting for a result.");
+ }
+ }
+
+ debug!("script: layout joined")
+ }
+ None => fail!("reader forked but no join port?"),
+ }
+ }
+ }
+
+ /// Reflows the page if it's possible to do so. This method will wait until the layout task has
+ /// completed its current action, join the layout task, and then request a new layout run. It
+ /// won't wait for the new layout computation to finish.
+ ///
+ /// If there is no window size yet, the page is presumed invisible and no reflow is performed.
+ ///
+ /// This function fails if there is no root frame.
+ pub fn reflow(&self,
+ goal: ReflowGoal,
+ script_chan: ScriptControlChan,
+ compositor: &ScriptListener) {
+
+ let root = match *self.frame() {
+ None => return,
+ Some(ref frame) => {
+ frame.document.root().GetDocumentElement()
+ }
+ };
+
+ match root.root() {
+ None => {},
+ Some(root) => {
+ debug!("script: performing reflow for goal {:?}", goal);
+
+ // Now, join the layout so that they will see the latest changes we have made.
+ self.join_layout();
+
+ // Tell the user that we're performing layout.
+ compositor.set_ready_state(PerformingLayout);
+
+ // Layout will let us know when it's done.
+ let (join_chan, join_port) = channel();
+ let mut layout_join_port = self.layout_join_port.deref().borrow_mut();
+ *layout_join_port = Some(join_port);
+
+ let last_reflow_id = self.last_reflow_id.deref();
+ last_reflow_id.set(last_reflow_id.get() + 1);
+
+ let root: &JSRef<Node> = NodeCast::from_ref(&*root);
+ let mut damage = self.damage.deref().borrow_mut();
+ let window_size = self.window_size.deref().get();
+
+ // Send new document and relevant styles to layout.
+ let reflow = box Reflow {
+ document_root: root.to_trusted_node_address(),
+ url: self.get_url(),
+ goal: goal,
+ window_size: window_size,
+ script_chan: script_chan,
+ script_join_chan: join_chan,
+ damage: replace(&mut *damage, None).unwrap(),
+ id: last_reflow_id.get(),
+ };
+
+ let LayoutChan(ref chan) = *self.layout_chan;
+ chan.send(ReflowMsg(reflow));
+
+ debug!("script: layout forked")
+ }
+ }
+ }
+
+ /// Attempt to find a named element in this page's document.
+ pub fn find_fragment_node(&self, fragid: DOMString) -> Option<Temporary<Element>> {
+ let document = self.frame().get_ref().document.root();
+ match document.deref().GetElementById(fragid.to_string()) {
+ Some(node) => Some(node),
+ None => {
+ let doc_node: &JSRef<Node> = NodeCast::from_ref(&*document);
+ let mut anchors = doc_node.traverse_preorder()
+ .filter(|node| node.is_anchor_element());
+ anchors.find(|node| {
+ let elem: &JSRef<Element> = ElementCast::to_ref(node).unwrap();
+ elem.get_attribute(Null, "name").root().map_or(false, |attr| {
+ attr.deref().value().as_slice() == fragid.as_slice()
+ })
+ }).map(|node| Temporary::from_rooted(ElementCast::to_ref(&node).unwrap()))
+ }
+ }
+ }
+
+ pub fn hit_test(&self, point: &Point2D<f32>) -> Option<UntrustedNodeAddress> {
+ let frame = self.frame();
+ let document = frame.get_ref().document.root();
+ let root = document.deref().GetDocumentElement().root();
+ if root.is_none() {
+ return None;
+ }
+ let root = root.unwrap();
+ let root: &JSRef<Node> = NodeCast::from_ref(&*root);
+ let address = match self.layout_rpc.hit_test(root.to_trusted_node_address(), *point) {
+ Ok(HitTestResponse(node_address)) => {
+ Some(node_address)
+ }
+ Err(()) => {
+ debug!("layout query error");
+ None
+ }
+ };
+ address
+ }
+
+ pub fn get_nodes_under_mouse(&self, point: &Point2D<f32>) -> Option<Vec<UntrustedNodeAddress>> {
+ let frame = self.frame();
+ let document = frame.get_ref().document.root();
+ let root = document.deref().GetDocumentElement().root();
+ if root.is_none() {
+ return None;
+ }
+ let root = root.unwrap();
+ let root: &JSRef<Node> = NodeCast::from_ref(&*root);
+ let address = match self.layout_rpc.mouse_over(root.to_trusted_node_address(), *point) {
+ Ok(MouseOverResponse(node_address)) => {
+ Some(node_address)
+ }
+ Err(()) => {
+ None
+ }
+ };
+ address
+ }
+}
+
+/// Information for one frame in the browsing context.
+#[deriving(Encodable)]
+pub struct Frame {
+ /// The document for this frame.
+ pub document: JS<Document>,
+ /// The window object for this frame.
+ pub window: JS<Window>,
+}
+
+/// Encapsulation of the javascript information associated with each frame.
+#[deriving(Encodable)]
+pub struct JSPageInfo {
+ /// Global static data related to the DOM.
+ pub dom_static: GlobalStaticData,
+ /// The JavaScript context.
+ pub js_context: Untraceable<Rc<Cx>>,
+}
diff --git a/components/script/script_task.rs b/components/script/script_task.rs
new file mode 100644
index 00000000000..fdbcff82410
--- /dev/null
+++ b/components/script/script_task.rs
@@ -0,0 +1,933 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! The script task is the task that owns the DOM in memory, runs JavaScript, and spawns parsing
+//! and layout tasks.
+
+use dom::bindings::codegen::InheritTypes::{EventTargetCast, NodeCast, EventCast};
+use dom::bindings::global::Window;
+use dom::bindings::js::{JS, JSRef, RootCollection, Temporary, OptionalSettable};
+use dom::bindings::js::OptionalRootable;
+use dom::bindings::utils::Reflectable;
+use dom::bindings::utils::{wrap_for_same_compartment, pre_wrap};
+use dom::document::{Document, HTMLDocument, DocumentHelpers};
+use dom::element::{Element, HTMLButtonElementTypeId, HTMLInputElementTypeId};
+use dom::element::{HTMLSelectElementTypeId, HTMLTextAreaElementTypeId, HTMLOptionElementTypeId};
+use dom::event::Event;
+use dom::uievent::UIEvent;
+use dom::eventtarget::{EventTarget, EventTargetHelpers};
+use dom::node;
+use dom::node::{ElementNodeTypeId, Node, NodeHelpers};
+use dom::window::{TimerId, Window, WindowHelpers};
+use dom::worker::{Worker, TrustedWorkerAddress};
+use dom::xmlhttprequest::{TrustedXHRAddress, XMLHttpRequest, XHRProgress};
+use html::hubbub_html_parser::HtmlParserResult;
+use html::hubbub_html_parser::{HtmlDiscoveredStyle, HtmlDiscoveredScript};
+use html::hubbub_html_parser;
+use layout_interface::AddStylesheetMsg;
+use layout_interface::{ScriptLayoutChan, LayoutChan, MatchSelectorsDocumentDamage};
+use layout_interface::{ReflowDocumentDamage, ReflowForDisplay};
+use layout_interface::ContentChangedDocumentDamage;
+use layout_interface;
+use page::{Page, IterablePage, Frame};
+
+use script_traits::{CompositorEvent, ResizeEvent, ReflowEvent, ClickEvent, MouseDownEvent};
+use script_traits::{MouseMoveEvent, MouseUpEvent, ConstellationControlMsg, ScriptTaskFactory};
+use script_traits::{ResizeMsg, AttachLayoutMsg, LoadMsg, SendEventMsg, ResizeInactiveMsg};
+use script_traits::{ExitPipelineMsg, NewLayoutInfo, OpaqueScriptLayoutChannel, ScriptControlChan};
+use script_traits::ReflowCompleteMsg;
+use servo_msg::compositor_msg::{FinishedLoading, LayerId, Loading};
+use servo_msg::compositor_msg::{ScriptListener};
+use servo_msg::constellation_msg::{ConstellationChan, LoadCompleteMsg, LoadUrlMsg, NavigationDirection};
+use servo_msg::constellation_msg::{PipelineId, Failure, FailureMsg, WindowSizeData};
+use servo_msg::constellation_msg;
+use servo_net::image_cache_task::ImageCacheTask;
+use servo_net::resource_task::ResourceTask;
+use servo_util::geometry::to_frac_px;
+use servo_util::task::spawn_named_with_send_on_failure;
+
+use geom::point::Point2D;
+use js::jsapi::{JS_SetWrapObjectCallbacks, JS_SetGCZeal, JS_DEFAULT_ZEAL_FREQ, JS_GC};
+use js::jsapi::{JSContext, JSRuntime};
+use js::jsapi::{JS_SetGCParameter, JSGC_MAX_BYTES};
+use js::rust::{Cx, RtUtils};
+use js::rust::with_compartment;
+use js;
+use url::Url;
+
+use libc::size_t;
+use serialize::{Encoder, Encodable};
+use std::any::{Any, AnyRefExt};
+use std::cell::RefCell;
+use std::comm::{channel, Sender, Receiver, Select};
+use std::mem::replace;
+use std::rc::Rc;
+use std::u32;
+
+local_data_key!(pub StackRoots: *const RootCollection)
+
+/// Messages used to control script event loops, such as ScriptTask and
+/// DedicatedWorkerGlobalScope.
+pub enum ScriptMsg {
+ /// Acts on a fragment URL load on the specified pipeline (only dispatched
+ /// to ScriptTask).
+ TriggerFragmentMsg(PipelineId, Url),
+ /// Begins a content-initiated load on the specified pipeline (only
+ /// dispatched to ScriptTask).
+ TriggerLoadMsg(PipelineId, Url),
+ /// Instructs the script task to send a navigate message to
+ /// the constellation (only dispatched to ScriptTask).
+ NavigateMsg(NavigationDirection),
+ /// Fires a JavaScript timeout (only dispatched to ScriptTask).
+ FireTimerMsg(PipelineId, TimerId),
+ /// Notifies the script that a window associated with a particular pipeline
+ /// should be closed (only dispatched to ScriptTask).
+ ExitWindowMsg(PipelineId),
+ /// Notifies the script of progress on a fetch (dispatched to all tasks).
+ XHRProgressMsg(TrustedXHRAddress, XHRProgress),
+ /// Message sent through Worker.postMessage (only dispatched to
+ /// DedicatedWorkerGlobalScope).
+ DOMMessage(*mut u64, size_t),
+ /// Posts a message to the Worker object (dispatched to all tasks).
+ WorkerPostMessage(TrustedWorkerAddress, *mut u64, size_t),
+ /// Releases one reference to the Worker object (dispatched to all tasks).
+ WorkerRelease(TrustedWorkerAddress),
+}
+
+/// Encapsulates internal communication within the script task.
+#[deriving(Clone)]
+pub struct ScriptChan(pub Sender<ScriptMsg>);
+
+impl<S: Encoder<E>, E> Encodable<S, E> for ScriptChan {
+ fn encode(&self, _s: &mut S) -> Result<(), E> {
+ Ok(())
+ }
+}
+
+impl ScriptChan {
+ /// Creates a new script chan.
+ pub fn new() -> (Receiver<ScriptMsg>, ScriptChan) {
+ let (chan, port) = channel();
+ (port, ScriptChan(chan))
+ }
+}
+
+pub struct StackRootTLS;
+
+impl StackRootTLS {
+ pub fn new(roots: &RootCollection) -> StackRootTLS {
+ StackRoots.replace(Some(roots as *const RootCollection));
+ StackRootTLS
+ }
+}
+
+impl Drop for StackRootTLS {
+ fn drop(&mut self) {
+ let _ = StackRoots.replace(None);
+ }
+}
+
+/// Information for an entire page. Pages are top-level browsing contexts and can contain multiple
+/// frames.
+///
+/// FIXME: Rename to `Page`, following WebKit?
+pub struct ScriptTask {
+ /// A handle to the information pertaining to page layout
+ page: RefCell<Rc<Page>>,
+ /// A handle to the image cache task.
+ image_cache_task: ImageCacheTask,
+ /// A handle to the resource task.
+ resource_task: ResourceTask,
+
+ /// The port on which the script task receives messages (load URL, exit, etc.)
+ port: Receiver<ScriptMsg>,
+ /// A channel to hand out to script task-based entities that need to be able to enqueue
+ /// events in the event queue.
+ chan: ScriptChan,
+
+ /// A channel to hand out to tasks that need to respond to a message from the script task.
+ control_chan: ScriptControlChan,
+
+ /// The port on which the constellation and layout tasks can communicate with the
+ /// script task.
+ control_port: Receiver<ConstellationControlMsg>,
+
+ /// For communicating load url messages to the constellation
+ constellation_chan: ConstellationChan,
+ /// A handle to the compositor for communicating ready state messages.
+ compositor: Box<ScriptListener>,
+
+ /// The JavaScript runtime.
+ js_runtime: js::rust::rt,
+ /// The JSContext.
+ js_context: RefCell<Option<Rc<Cx>>>,
+
+ mouse_over_targets: RefCell<Option<Vec<JS<Node>>>>
+}
+
+/// In the event of task failure, all data on the stack runs its destructor. However, there
+/// are no reachable, owning pointers to the DOM memory, so it never gets freed by default
+/// when the script task fails. The ScriptMemoryFailsafe uses the destructor bomb pattern
+/// to forcibly tear down the JS compartments for pages associated with the failing ScriptTask.
+struct ScriptMemoryFailsafe<'a> {
+ owner: Option<&'a ScriptTask>,
+}
+
+impl<'a> ScriptMemoryFailsafe<'a> {
+ fn neuter(&mut self) {
+ self.owner = None;
+ }
+
+ fn new(owner: &'a ScriptTask) -> ScriptMemoryFailsafe<'a> {
+ ScriptMemoryFailsafe {
+ owner: Some(owner),
+ }
+ }
+}
+
+#[unsafe_destructor]
+impl<'a> Drop for ScriptMemoryFailsafe<'a> {
+ fn drop(&mut self) {
+ match self.owner {
+ Some(owner) => {
+ let mut page = owner.page.borrow_mut();
+ for page in page.iter() {
+ *page.mut_js_info() = None;
+ }
+ *owner.js_context.borrow_mut() = None;
+ }
+ None => (),
+ }
+ }
+}
+
+trait PrivateScriptTaskHelpers {
+ fn click_event_filter_by_disabled_state(&self) -> bool;
+}
+
+impl<'a> PrivateScriptTaskHelpers for JSRef<'a, Node> {
+ fn click_event_filter_by_disabled_state(&self) -> bool {
+ match self.type_id() {
+ ElementNodeTypeId(HTMLButtonElementTypeId) |
+ ElementNodeTypeId(HTMLInputElementTypeId) |
+ // ElementNodeTypeId(HTMLKeygenElementTypeId) |
+ ElementNodeTypeId(HTMLOptionElementTypeId) |
+ ElementNodeTypeId(HTMLSelectElementTypeId) |
+ ElementNodeTypeId(HTMLTextAreaElementTypeId) if self.get_disabled_state() => true,
+ _ => false
+ }
+ }
+}
+
+impl ScriptTaskFactory for ScriptTask {
+ fn create_layout_channel(_phantom: Option<&mut ScriptTask>) -> OpaqueScriptLayoutChannel {
+ let (chan, port) = channel();
+ ScriptLayoutChan::new(chan, port)
+ }
+
+ fn clone_layout_channel(_phantom: Option<&mut ScriptTask>, pair: &OpaqueScriptLayoutChannel) -> Box<Any+Send> {
+ box pair.sender() as Box<Any+Send>
+ }
+
+ fn create<C:ScriptListener + Send>(
+ _phantom: Option<&mut ScriptTask>,
+ id: PipelineId,
+ compositor: Box<C>,
+ layout_chan: &OpaqueScriptLayoutChannel,
+ control_chan: ScriptControlChan,
+ control_port: Receiver<ConstellationControlMsg>,
+ constellation_chan: ConstellationChan,
+ failure_msg: Failure,
+ resource_task: ResourceTask,
+ image_cache_task: ImageCacheTask,
+ window_size: WindowSizeData) {
+ let ConstellationChan(const_chan) = constellation_chan.clone();
+ let (script_chan, script_port) = channel();
+ let layout_chan = LayoutChan(layout_chan.sender());
+ spawn_named_with_send_on_failure("ScriptTask", proc() {
+ let script_task = ScriptTask::new(id,
+ compositor as Box<ScriptListener>,
+ layout_chan,
+ script_port,
+ ScriptChan(script_chan),
+ control_chan,
+ control_port,
+ constellation_chan,
+ resource_task,
+ image_cache_task,
+ window_size);
+ let mut failsafe = ScriptMemoryFailsafe::new(&*script_task);
+ script_task.start();
+
+ // This must always be the very last operation performed before the task completes
+ failsafe.neuter();
+ }, FailureMsg(failure_msg), const_chan, false);
+ }
+}
+
+impl ScriptTask {
+ /// Creates a new script task.
+ pub fn new(id: PipelineId,
+ compositor: Box<ScriptListener>,
+ layout_chan: LayoutChan,
+ port: Receiver<ScriptMsg>,
+ chan: ScriptChan,
+ control_chan: ScriptControlChan,
+ control_port: Receiver<ConstellationControlMsg>,
+ constellation_chan: ConstellationChan,
+ resource_task: ResourceTask,
+ img_cache_task: ImageCacheTask,
+ window_size: WindowSizeData)
+ -> Rc<ScriptTask> {
+ let (js_runtime, js_context) = ScriptTask::new_rt_and_cx();
+ unsafe {
+ // JS_SetWrapObjectCallbacks clobbers the existing wrap callback,
+ // and JSCompartment::wrap crashes if that happens. The only way
+ // to retrieve the default callback is as the result of
+ // JS_SetWrapObjectCallbacks, which is why we call it twice.
+ let callback = JS_SetWrapObjectCallbacks((*js_runtime).ptr,
+ None,
+ Some(wrap_for_same_compartment),
+ None);
+ JS_SetWrapObjectCallbacks((*js_runtime).ptr,
+ callback,
+ Some(wrap_for_same_compartment),
+ Some(pre_wrap));
+ }
+
+ let page = Page::new(id, None, layout_chan, window_size,
+ resource_task.clone(),
+ constellation_chan.clone(),
+ js_context.clone());
+ Rc::new(ScriptTask {
+ page: RefCell::new(Rc::new(page)),
+
+ image_cache_task: img_cache_task,
+ resource_task: resource_task,
+
+ port: port,
+ chan: chan,
+ control_chan: control_chan,
+ control_port: control_port,
+ constellation_chan: constellation_chan,
+ compositor: compositor,
+
+ js_runtime: js_runtime,
+ js_context: RefCell::new(Some(js_context)),
+ mouse_over_targets: RefCell::new(None)
+ })
+ }
+
+ pub fn new_rt_and_cx() -> (js::rust::rt, Rc<Cx>) {
+ let js_runtime = js::rust::rt();
+ assert!({
+ let ptr: *mut JSRuntime = (*js_runtime).ptr;
+ ptr.is_not_null()
+ });
+
+ // Unconstrain the runtime's threshold on nominal heap size, to avoid
+ // triggering GC too often if operating continuously near an arbitrary
+ // finite threshold. This leaves the maximum-JS_malloc-bytes threshold
+ // still in effect to cause periodical, and we hope hygienic,
+ // last-ditch GCs from within the GC's allocator.
+ unsafe {
+ JS_SetGCParameter(js_runtime.ptr, JSGC_MAX_BYTES, u32::MAX);
+ }
+
+ let js_context = js_runtime.cx();
+ assert!({
+ let ptr: *mut JSContext = (*js_context).ptr;
+ ptr.is_not_null()
+ });
+ js_context.set_default_options_and_version();
+ js_context.set_logging_error_reporter();
+ unsafe {
+ JS_SetGCZeal((*js_context).ptr, 0, JS_DEFAULT_ZEAL_FREQ);
+ }
+
+ (js_runtime, js_context)
+ }
+
+ pub fn get_cx(&self) -> *mut JSContext {
+ (**self.js_context.borrow().get_ref()).ptr
+ }
+
+ /// Starts the script task. After calling this method, the script task will loop receiving
+ /// messages on its port.
+ pub fn start(&self) {
+ while self.handle_msgs() {
+ // Go on...
+ }
+ }
+
+ /// Handle incoming control messages.
+ fn handle_msgs(&self) -> bool {
+ let roots = RootCollection::new();
+ let _stack_roots_tls = StackRootTLS::new(&roots);
+
+ // Handle pending resize events.
+ // Gather them first to avoid a double mut borrow on self.
+ let mut resizes = vec!();
+
+ {
+ let mut page = self.page.borrow_mut();
+ for page in page.iter() {
+ // Only process a resize if layout is idle.
+ let layout_join_port = page.layout_join_port.deref().borrow();
+ if layout_join_port.is_none() {
+ let mut resize_event = page.resize_event.deref().get();
+ match resize_event.take() {
+ Some(size) => resizes.push((page.id, size)),
+ None => ()
+ }
+ page.resize_event.deref().set(None);
+ }
+ }
+ }
+
+ for (id, size) in resizes.move_iter() {
+ self.handle_event(id, ResizeEvent(size));
+ }
+
+ enum MixedMessage {
+ FromConstellation(ConstellationControlMsg),
+ FromScript(ScriptMsg),
+ }
+
+ // Store new resizes, and gather all other events.
+ let mut sequential = vec!();
+
+ // Receive at least one message so we don't spinloop.
+ let mut event = {
+ let sel = Select::new();
+ let mut port1 = sel.handle(&self.port);
+ let mut port2 = sel.handle(&self.control_port);
+ unsafe {
+ port1.add();
+ port2.add();
+ }
+ let ret = sel.wait();
+ if ret == port1.id() {
+ FromScript(self.port.recv())
+ } else if ret == port2.id() {
+ FromConstellation(self.control_port.recv())
+ } else {
+ fail!("unexpected select result")
+ }
+ };
+
+ loop {
+ match event {
+ FromConstellation(ResizeMsg(id, size)) => {
+ let mut page = self.page.borrow_mut();
+ let page = page.find(id).expect("resize sent to nonexistent pipeline");
+ page.resize_event.deref().set(Some(size));
+ }
+ _ => {
+ sequential.push(event);
+ }
+ }
+
+ match self.control_port.try_recv() {
+ Err(_) => match self.port.try_recv() {
+ Err(_) => break,
+ Ok(ev) => event = FromScript(ev),
+ },
+ Ok(ev) => event = FromConstellation(ev),
+ }
+ }
+
+ // Process the gathered events.
+ for msg in sequential.move_iter() {
+ match msg {
+ // TODO(tkuehn) need to handle auxiliary layouts for iframes
+ FromConstellation(AttachLayoutMsg(new_layout_info)) =>
+ self.handle_new_layout(new_layout_info),
+ FromConstellation(LoadMsg(id, url)) => self.load(id, url),
+ FromScript(TriggerLoadMsg(id, url)) => self.trigger_load(id, url),
+ FromScript(TriggerFragmentMsg(id, url)) => self.trigger_fragment(id, url),
+ FromConstellation(SendEventMsg(id, event)) => self.handle_event(id, event),
+ FromScript(FireTimerMsg(id, timer_id)) => self.handle_fire_timer_msg(id, timer_id),
+ FromScript(NavigateMsg(direction)) => self.handle_navigate_msg(direction),
+ FromConstellation(ReflowCompleteMsg(id, reflow_id)) => self.handle_reflow_complete_msg(id, reflow_id),
+ FromConstellation(ResizeInactiveMsg(id, new_size)) => self.handle_resize_inactive_msg(id, new_size),
+ FromConstellation(ExitPipelineMsg(id)) => if self.handle_exit_pipeline_msg(id) { return false },
+ FromScript(ExitWindowMsg(id)) => self.handle_exit_window_msg(id),
+ FromConstellation(ResizeMsg(..)) => fail!("should have handled ResizeMsg already"),
+ FromScript(XHRProgressMsg(addr, progress)) => XMLHttpRequest::handle_xhr_progress(addr, progress),
+ FromScript(DOMMessage(..)) => fail!("unexpected message"),
+ FromScript(WorkerPostMessage(addr, data, nbytes)) => Worker::handle_message(addr, data, nbytes),
+ FromScript(WorkerRelease(addr)) => Worker::handle_release(addr),
+ }
+ }
+
+ true
+ }
+
+ fn handle_new_layout(&self, new_layout_info: NewLayoutInfo) {
+ debug!("Script: new layout: {:?}", new_layout_info);
+ let NewLayoutInfo {
+ old_pipeline_id,
+ new_pipeline_id,
+ subpage_id,
+ layout_chan
+ } = new_layout_info;
+
+ let mut page = self.page.borrow_mut();
+ let parent_page = page.find(old_pipeline_id).expect("ScriptTask: received a layout
+ whose parent has a PipelineId which does not correspond to a pipeline in the script
+ task's page tree. This is a bug.");
+ let new_page = {
+ let window_size = parent_page.window_size.deref().get();
+ Page::new(new_pipeline_id, Some(subpage_id),
+ LayoutChan(layout_chan.downcast_ref::<Sender<layout_interface::Msg>>().unwrap().clone()),
+ window_size,
+ parent_page.resource_task.deref().clone(),
+ self.constellation_chan.clone(),
+ self.js_context.borrow().get_ref().clone())
+ };
+ parent_page.children.deref().borrow_mut().push(Rc::new(new_page));
+ }
+
+ /// Handles a timer that fired.
+ fn handle_fire_timer_msg(&self, id: PipelineId, timer_id: TimerId) {
+ let mut page = self.page.borrow_mut();
+ let page = page.find(id).expect("ScriptTask: received fire timer msg for a
+ pipeline ID not associated with this script task. This is a bug.");
+ let frame = page.frame();
+ let window = frame.get_ref().window.root();
+ window.handle_fire_timer(timer_id, self.get_cx());
+ }
+
+ /// Handles a notification that reflow completed.
+ fn handle_reflow_complete_msg(&self, pipeline_id: PipelineId, reflow_id: uint) {
+ debug!("Script: Reflow {:?} complete for {:?}", reflow_id, pipeline_id);
+ let mut page = self.page.borrow_mut();
+ let page = page.find(pipeline_id).expect(
+ "ScriptTask: received a load message for a layout channel that is not associated \
+ with this script task. This is a bug.");
+ let last_reflow_id = page.last_reflow_id.deref().get();
+ if last_reflow_id == reflow_id {
+ let mut layout_join_port = page.layout_join_port.deref().borrow_mut();
+ *layout_join_port = None;
+ }
+ self.compositor.set_ready_state(FinishedLoading);
+ }
+
+ /// Handles a navigate forward or backward message.
+ /// TODO(tkuehn): is it ever possible to navigate only on a subframe?
+ fn handle_navigate_msg(&self, direction: NavigationDirection) {
+ let ConstellationChan(ref chan) = self.constellation_chan;
+ chan.send(constellation_msg::NavigateMsg(direction));
+ }
+
+ /// Window was resized, but this script was not active, so don't reflow yet
+ fn handle_resize_inactive_msg(&self, id: PipelineId, new_size: WindowSizeData) {
+ let mut page = self.page.borrow_mut();
+ let page = page.find(id).expect("Received resize message for PipelineId not associated
+ with a page in the page tree. This is a bug.");
+ page.window_size.deref().set(new_size);
+ let mut page_url = page.mut_url();
+ let last_loaded_url = replace(&mut *page_url, None);
+ for url in last_loaded_url.iter() {
+ *page_url = Some((url.ref0().clone(), true));
+ }
+ }
+
+ /// We have gotten a window.close from script, which we pass on to the compositor.
+ /// We do not shut down the script task now, because the compositor will ask the
+ /// constellation to shut down the pipeline, which will clean everything up
+ /// normally. If we do exit, we will tear down the DOM nodes, possibly at a point
+ /// where layout is still accessing them.
+ fn handle_exit_window_msg(&self, _: PipelineId) {
+ debug!("script task handling exit window msg");
+
+ // TODO(tkuehn): currently there is only one window,
+ // so this can afford to be naive and just shut down the
+ // compositor. In the future it'll need to be smarter.
+ self.compositor.close();
+ }
+
+ /// Handles a request to exit the script task and shut down layout.
+ /// Returns true if the script task should shut down and false otherwise.
+ fn handle_exit_pipeline_msg(&self, id: PipelineId) -> bool {
+ // If root is being exited, shut down all pages
+ let mut page = self.page.borrow_mut();
+ if page.id == id {
+ debug!("shutting down layout for root page {:?}", id);
+ *self.js_context.borrow_mut() = None;
+ shut_down_layout(&*page, (*self.js_runtime).ptr);
+ return true
+ }
+
+ // otherwise find just the matching page and exit all sub-pages
+ match page.remove(id) {
+ Some(ref mut page) => {
+ shut_down_layout(&*page, (*self.js_runtime).ptr);
+ false
+ }
+ // TODO(tkuehn): pipeline closing is currently duplicated across
+ // script and constellation, which can cause this to happen. Constellation
+ // needs to be smarter about exiting pipelines.
+ None => false,
+ }
+
+ }
+
+ /// The entry point to document loading. Defines bindings, sets up the window and document
+ /// objects, parses HTML and CSS, and kicks off initial layout.
+ fn load(&self, pipeline_id: PipelineId, url: Url) {
+ debug!("ScriptTask: loading {:?} on page {:?}", url, pipeline_id);
+
+ let mut page = self.page.borrow_mut();
+ let page = page.find(pipeline_id).expect("ScriptTask: received a load
+ message for a layout channel that is not associated with this script task. This
+ is a bug.");
+
+ let last_loaded_url = replace(&mut *page.mut_url(), None);
+ match last_loaded_url {
+ Some((ref loaded, needs_reflow)) if *loaded == url => {
+ *page.mut_url() = Some((loaded.clone(), false));
+ if needs_reflow {
+ page.damage(ContentChangedDocumentDamage);
+ page.reflow(ReflowForDisplay, self.control_chan.clone(), self.compositor);
+ }
+ return;
+ },
+ _ => (),
+ }
+
+ let cx = self.js_context.borrow();
+ let cx = cx.get_ref();
+ // Create the window and document objects.
+ let window = Window::new(cx.deref().ptr,
+ page.clone(),
+ self.chan.clone(),
+ self.control_chan.clone(),
+ self.compositor.dup(),
+ self.image_cache_task.clone()).root();
+ let document = Document::new(&*window, Some(url.clone()), HTMLDocument, None).root();
+ window.deref().init_browser_context(&*document);
+
+ self.compositor.set_ready_state(Loading);
+ // Parse HTML.
+ //
+ // Note: We can parse the next document in parallel with any previous documents.
+ let html_parsing_result = hubbub_html_parser::parse_html(&*page,
+ &*document,
+ url.clone(),
+ self.resource_task.clone());
+
+ let HtmlParserResult {
+ discovery_port
+ } = html_parsing_result;
+
+ {
+ // Create the root frame.
+ let mut frame = page.mut_frame();
+ *frame = Some(Frame {
+ document: JS::from_rooted(document.deref()),
+ window: JS::from_rooted(window.deref()),
+ });
+ }
+
+ // Send style sheets over to layout.
+ //
+ // FIXME: These should be streamed to layout as they're parsed. We don't need to stop here
+ // in the script task.
+
+ let mut js_scripts = None;
+ loop {
+ match discovery_port.recv_opt() {
+ Ok(HtmlDiscoveredScript(scripts)) => {
+ assert!(js_scripts.is_none());
+ js_scripts = Some(scripts);
+ }
+ Ok(HtmlDiscoveredStyle(sheet)) => {
+ let LayoutChan(ref chan) = *page.layout_chan;
+ chan.send(AddStylesheetMsg(sheet));
+ }
+ Err(()) => break
+ }
+ }
+
+ // Kick off the initial reflow of the page.
+ document.deref().content_changed();
+
+ let fragment = url.fragment.as_ref().map(|ref fragment| fragment.to_string());
+
+ {
+ // No more reflow required
+ let mut page_url = page.mut_url();
+ *page_url = Some((url.clone(), false));
+ }
+
+ // Receive the JavaScript scripts.
+ assert!(js_scripts.is_some());
+ let js_scripts = js_scripts.take_unwrap();
+ debug!("js_scripts: {:?}", js_scripts);
+
+ with_compartment((**cx).ptr, window.reflector().get_jsobject(), || {
+ // Evaluate every script in the document.
+ for file in js_scripts.iter() {
+ let global_obj = window.reflector().get_jsobject();
+ //FIXME: this should have some kind of error handling, or explicitly
+ // drop an exception on the floor.
+ match cx.evaluate_script(global_obj, file.data.clone(), file.url.serialize(), 1) {
+ Ok(_) => (),
+ Err(_) => println!("evaluate_script failed")
+ }
+ }
+ });
+
+ // We have no concept of a document loader right now, so just dispatch the
+ // "load" event as soon as we've finished executing all scripts parsed during
+ // the initial load.
+ let event = Event::new(&Window(*window), "load".to_string(), false, false).root();
+ let doctarget: &JSRef<EventTarget> = EventTargetCast::from_ref(&*document);
+ let wintarget: &JSRef<EventTarget> = EventTargetCast::from_ref(&*window);
+ let _ = wintarget.dispatch_event_with_target(Some((*doctarget).clone()),
+ &*event);
+
+ page.fragment_node.assign(fragment.map_or(None, |fragid| page.find_fragment_node(fragid)));
+
+ let ConstellationChan(ref chan) = self.constellation_chan;
+ chan.send(LoadCompleteMsg(page.id, url));
+ }
+
+ fn scroll_fragment_point(&self, pipeline_id: PipelineId, node: &JSRef<Element>) {
+ let node: &JSRef<Node> = NodeCast::from_ref(node);
+ let rect = node.get_bounding_content_box();
+ let point = Point2D(to_frac_px(rect.origin.x).to_f32().unwrap(),
+ to_frac_px(rect.origin.y).to_f32().unwrap());
+ // FIXME(#2003, pcwalton): This is pretty bogus when multiple layers are involved.
+ // Really what needs to happen is that this needs to go through layout to ask which
+ // layer the element belongs to, and have it send the scroll message to the
+ // compositor.
+ self.compositor.scroll_fragment_point(pipeline_id, LayerId::null(), point);
+ }
+
+ /// This is the main entry point for receiving and dispatching DOM events.
+ ///
+ /// TODO: Actually perform DOM event dispatch.
+ fn handle_event(&self, pipeline_id: PipelineId, event: CompositorEvent) {
+ match event {
+ ResizeEvent(new_size) => {
+ debug!("script got resize event: {:?}", new_size);
+
+ let window = {
+ let page = get_page(&*self.page.borrow(), pipeline_id);
+ page.window_size.deref().set(new_size);
+
+ let frame = page.frame();
+ if frame.is_some() {
+ page.damage(ReflowDocumentDamage);
+ page.reflow(ReflowForDisplay, self.control_chan.clone(), self.compositor)
+ }
+
+ let mut fragment_node = page.fragment_node.get();
+ match fragment_node.take().map(|node| node.root()) {
+ Some(node) => self.scroll_fragment_point(pipeline_id, &*node),
+ None => {}
+ }
+
+ frame.as_ref().map(|frame| Temporary::new(frame.window.clone()))
+ };
+
+ match window.root() {
+ Some(window) => {
+ // http://dev.w3.org/csswg/cssom-view/#resizing-viewports
+ // https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#event-type-resize
+ let uievent = UIEvent::new(&window.clone(),
+ "resize".to_string(), false,
+ false, Some(window.clone()),
+ 0i32).root();
+ let event: &JSRef<Event> = EventCast::from_ref(&*uievent);
+
+ let wintarget: &JSRef<EventTarget> = EventTargetCast::from_ref(&*window);
+ let _ = wintarget.dispatch_event_with_target(None, event);
+ }
+ None => ()
+ }
+ }
+
+ // FIXME(pcwalton): This reflows the entire document and is not incremental-y.
+ ReflowEvent => {
+ debug!("script got reflow event");
+ let page = get_page(&*self.page.borrow(), pipeline_id);
+ let frame = page.frame();
+ if frame.is_some() {
+ page.damage(MatchSelectorsDocumentDamage);
+ page.reflow(ReflowForDisplay, self.control_chan.clone(), self.compositor)
+ }
+ }
+
+ ClickEvent(_button, point) => {
+ debug!("ClickEvent: clicked at {:?}", point);
+ let page = get_page(&*self.page.borrow(), pipeline_id);
+ match page.hit_test(&point) {
+ Some(node_address) => {
+ debug!("node address is {:?}", node_address);
+
+ let temp_node =
+ node::from_untrusted_node_address(
+ self.js_runtime.deref().ptr, node_address);
+
+ let maybe_node = temp_node.root().ancestors().find(|node| node.is_element());
+ match maybe_node {
+ Some(node) => {
+ debug!("clicked on {:s}", node.debug_str());
+ // Prevent click event if form control element is disabled.
+ if node.click_event_filter_by_disabled_state() { return; }
+ match *page.frame() {
+ Some(ref frame) => {
+ let window = frame.window.root();
+ let event =
+ Event::new(&Window(*window),
+ "click".to_string(),
+ true, true).root();
+ let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(&node);
+ let _ = eventtarget.dispatch_event_with_target(None, &*event);
+ }
+ None => {}
+ }
+ }
+ None => {}
+ }
+ }
+
+ None => {}
+ }
+ }
+ MouseDownEvent(..) => {}
+ MouseUpEvent(..) => {}
+ MouseMoveEvent(point) => {
+ let page = get_page(&*self.page.borrow(), pipeline_id);
+ match page.get_nodes_under_mouse(&point) {
+ Some(node_address) => {
+
+ let mut target_list = vec!();
+ let mut target_compare = false;
+
+ let mouse_over_targets = &mut *self.mouse_over_targets.borrow_mut();
+ match *mouse_over_targets {
+ Some(ref mut mouse_over_targets) => {
+ for node in mouse_over_targets.mut_iter() {
+ let node = node.root();
+ node.deref().set_hover_state(false);
+ }
+ }
+ None => {}
+ }
+
+ for node_address in node_address.iter() {
+
+ let temp_node =
+ node::from_untrusted_node_address(
+ self.js_runtime.deref().ptr, *node_address);
+
+ let maybe_node = temp_node.root().ancestors().find(|node| node.is_element());
+ match maybe_node {
+ Some(node) => {
+ node.set_hover_state(true);
+
+ match *mouse_over_targets {
+ Some(ref mouse_over_targets) => {
+ if !target_compare {
+ target_compare = !mouse_over_targets.contains(&JS::from_rooted(&node));
+ }
+ }
+ None => {}
+ }
+ target_list.push(JS::from_rooted(&node));
+ }
+ None => {}
+ }
+ }
+ match *mouse_over_targets {
+ Some(ref mouse_over_targets) => {
+ if mouse_over_targets.len() != target_list.len() {
+ target_compare = true;
+ }
+ }
+ None => { target_compare = true; }
+ }
+
+ if target_compare {
+ if mouse_over_targets.is_some() {
+ page.damage(MatchSelectorsDocumentDamage);
+ page.reflow(ReflowForDisplay, self.control_chan.clone(), self.compositor);
+ }
+ *mouse_over_targets = Some(target_list);
+ }
+ }
+
+ None => {}
+ }
+ }
+ }
+ }
+
+ /// The entry point for content to notify that a new load has been requested
+ /// for the given pipeline.
+ fn trigger_load(&self, pipeline_id: PipelineId, url: Url) {
+ let ConstellationChan(ref const_chan) = self.constellation_chan;
+ const_chan.send(LoadUrlMsg(pipeline_id, url));
+ }
+
+ /// The entry point for content to notify that a fragment url has been requested
+ /// for the given pipeline.
+ fn trigger_fragment(&self, pipeline_id: PipelineId, url: Url) {
+ let page = get_page(&*self.page.borrow(), pipeline_id);
+ match page.find_fragment_node(url.fragment.unwrap()).root() {
+ Some(node) => {
+ self.scroll_fragment_point(pipeline_id, &*node);
+ }
+ None => {}
+ }
+ }
+}
+
+/// Shuts down layout for the given page tree.
+fn shut_down_layout(page_tree: &Rc<Page>, rt: *mut JSRuntime) {
+ for page in page_tree.iter() {
+ page.join_layout();
+
+ // Tell the layout task to begin shutting down, and wait until it
+ // processed this message.
+ let (response_chan, response_port) = channel();
+ let LayoutChan(ref chan) = *page.layout_chan;
+ chan.send(layout_interface::PrepareToExitMsg(response_chan));
+ response_port.recv();
+ }
+
+ // Remove our references to the DOM objects in this page tree.
+ for page in page_tree.iter() {
+ *page.mut_frame() = None;
+ }
+
+ // Drop our references to the JSContext, potentially triggering a GC.
+ for page in page_tree.iter() {
+ *page.mut_js_info() = None;
+ }
+
+ // Force a GC to make sure that our DOM reflectors are released before we tell
+ // layout to exit.
+ unsafe {
+ JS_GC(rt);
+ }
+
+ // Destroy the layout task. If there were node leaks, layout will now crash safely.
+ for page in page_tree.iter() {
+ let LayoutChan(ref chan) = *page.layout_chan;
+ chan.send(layout_interface::ExitNowMsg);
+ }
+}
+
+
+fn get_page(page: &Rc<Page>, pipeline_id: PipelineId) -> Rc<Page> {
+ page.find(pipeline_id).expect("ScriptTask: received an event \
+ message for a layout channel that is not associated with this script task.\
+ This is a bug.")
+}