summaryrefslogtreecommitdiffstats
path: root/dom/bindings
diff options
context:
space:
mode:
Diffstat (limited to 'dom/bindings')
-rw-r--r--dom/bindings/AtomList.h27
-rw-r--r--dom/bindings/BindingDeclarations.h535
-rw-r--r--dom/bindings/BindingUtils.cpp3549
-rw-r--r--dom/bindings/BindingUtils.h3441
-rw-r--r--dom/bindings/Bindings.conf1701
-rw-r--r--dom/bindings/CallbackFunction.h78
-rw-r--r--dom/bindings/CallbackInterface.cpp36
-rw-r--r--dom/bindings/CallbackInterface.h59
-rw-r--r--dom/bindings/CallbackObject.cpp331
-rw-r--r--dom/bindings/CallbackObject.h607
-rw-r--r--dom/bindings/Codegen.py17364
-rw-r--r--dom/bindings/Configuration.py791
-rw-r--r--dom/bindings/DOMJSClass.h467
-rw-r--r--dom/bindings/DOMJSProxyHandler.cpp362
-rw-r--r--dom/bindings/DOMJSProxyHandler.h268
-rw-r--r--dom/bindings/DOMString.h245
-rw-r--r--dom/bindings/Date.cpp49
-rw-r--r--dom/bindings/Date.h61
-rw-r--r--dom/bindings/ErrorIPCUtils.h84
-rw-r--r--dom/bindings/ErrorResult.h587
-rw-r--r--dom/bindings/Errors.msg107
-rw-r--r--dom/bindings/Exceptions.cpp709
-rw-r--r--dom/bindings/Exceptions.h69
-rw-r--r--dom/bindings/FakeString.h160
-rw-r--r--dom/bindings/GenerateCSS2PropertiesWebIDL.py84
-rw-r--r--dom/bindings/IterableIterator.cpp35
-rw-r--r--dom/bindings/IterableIterator.h204
-rw-r--r--dom/bindings/JSSlots.h34
-rw-r--r--dom/bindings/Makefile.in70
-rw-r--r--dom/bindings/MozMap.h121
-rw-r--r--dom/bindings/NonRefcountedDOMObject.h37
-rw-r--r--dom/bindings/Nullable.h140
-rw-r--r--dom/bindings/PrimitiveConversions.h359
-rw-r--r--dom/bindings/RootedDictionary.h58
-rw-r--r--dom/bindings/RootedOwningNonNull.h73
-rw-r--r--dom/bindings/RootedRefPtr.h59
-rw-r--r--dom/bindings/SimpleGlobalObject.cpp176
-rw-r--r--dom/bindings/SimpleGlobalObject.h99
-rw-r--r--dom/bindings/StructuredClone.cpp58
-rw-r--r--dom/bindings/StructuredClone.h25
-rw-r--r--dom/bindings/ToJSValue.cpp80
-rw-r--r--dom/bindings/ToJSValue.h377
-rw-r--r--dom/bindings/TypedArray.h441
-rw-r--r--dom/bindings/UnionMember.h59
-rw-r--r--dom/bindings/WebIDLGlobalNameHash.cpp324
-rw-r--r--dom/bindings/WebIDLGlobalNameHash.h70
-rw-r--r--dom/bindings/XrayExpandoClass.h41
-rw-r--r--dom/bindings/crashtests/1010658-1.html16
-rw-r--r--dom/bindings/crashtests/1010658-2.html16
-rw-r--r--dom/bindings/crashtests/769464.html11
-rw-r--r--dom/bindings/crashtests/822340-1.html11
-rw-r--r--dom/bindings/crashtests/822340-2.html8
-rw-r--r--dom/bindings/crashtests/832899.html5
-rw-r--r--dom/bindings/crashtests/860551.html4
-rw-r--r--dom/bindings/crashtests/860591.html20
-rw-r--r--dom/bindings/crashtests/862092.html19
-rw-r--r--dom/bindings/crashtests/862610.html20
-rw-r--r--dom/bindings/crashtests/869038.html22
-rw-r--r--dom/bindings/crashtests/949940.html16
-rw-r--r--dom/bindings/crashtests/crashtests.list12
-rw-r--r--dom/bindings/docs/index.rst123
-rw-r--r--dom/bindings/mach_commands.py55
-rw-r--r--dom/bindings/moz.build161
-rw-r--r--dom/bindings/mozwebidlcodegen/__init__.py583
-rw-r--r--dom/bindings/mozwebidlcodegen/test/Child.webidl3
-rw-r--r--dom/bindings/mozwebidlcodegen/test/ExampleBinding.webidl3
-rw-r--r--dom/bindings/mozwebidlcodegen/test/Parent.webidl3
-rw-r--r--dom/bindings/mozwebidlcodegen/test/TestEvent.webidl13
-rw-r--r--dom/bindings/mozwebidlcodegen/test/test_mozwebidlcodegen.py298
-rw-r--r--dom/bindings/parser/README1
-rw-r--r--dom/bindings/parser/UPSTREAM1
-rw-r--r--dom/bindings/parser/WebIDL.py6874
-rw-r--r--dom/bindings/parser/runtests.py108
-rw-r--r--dom/bindings/parser/tests/test_any_null.py14
-rw-r--r--dom/bindings/parser/tests/test_argument_identifier_conflicts.py14
-rw-r--r--dom/bindings/parser/tests/test_argument_novoid.py14
-rw-r--r--dom/bindings/parser/tests/test_arraybuffer.py81
-rw-r--r--dom/bindings/parser/tests/test_attr.py177
-rw-r--r--dom/bindings/parser/tests/test_attr_sequence_type.py67
-rw-r--r--dom/bindings/parser/tests/test_builtin_filename.py11
-rw-r--r--dom/bindings/parser/tests/test_builtins.py41
-rw-r--r--dom/bindings/parser/tests/test_bytestring.py99
-rw-r--r--dom/bindings/parser/tests/test_callback.py34
-rw-r--r--dom/bindings/parser/tests/test_callback_interface.py94
-rw-r--r--dom/bindings/parser/tests/test_conditional_dictionary_member.py110
-rw-r--r--dom/bindings/parser/tests/test_const.py80
-rw-r--r--dom/bindings/parser/tests/test_constructor.py109
-rw-r--r--dom/bindings/parser/tests/test_constructor_no_interface_object.py36
-rw-r--r--dom/bindings/parser/tests/test_date.py15
-rw-r--r--dom/bindings/parser/tests/test_deduplicate.py15
-rw-r--r--dom/bindings/parser/tests/test_dictionary.py555
-rw-r--r--dom/bindings/parser/tests/test_distinguishability.py293
-rw-r--r--dom/bindings/parser/tests/test_double_null.py14
-rw-r--r--dom/bindings/parser/tests/test_duplicate_qualifiers.py84
-rw-r--r--dom/bindings/parser/tests/test_empty_enum.py14
-rw-r--r--dom/bindings/parser/tests/test_empty_sequence_default_value.py45
-rw-r--r--dom/bindings/parser/tests/test_enum.py93
-rw-r--r--dom/bindings/parser/tests/test_enum_duplicate_values.py13
-rw-r--r--dom/bindings/parser/tests/test_error_colno.py20
-rw-r--r--dom/bindings/parser/tests/test_error_lineno.py28
-rw-r--r--dom/bindings/parser/tests/test_exposed_extended_attribute.py222
-rw-r--r--dom/bindings/parser/tests/test_extended_attributes.py107
-rw-r--r--dom/bindings/parser/tests/test_float_types.py125
-rw-r--r--dom/bindings/parser/tests/test_forward_decl.py15
-rw-r--r--dom/bindings/parser/tests/test_global_extended_attr.py122
-rw-r--r--dom/bindings/parser/tests/test_identifier_conflict.py39
-rw-r--r--dom/bindings/parser/tests/test_implements.py216
-rw-r--r--dom/bindings/parser/tests/test_incomplete_parent.py18
-rw-r--r--dom/bindings/parser/tests/test_incomplete_types.py44
-rw-r--r--dom/bindings/parser/tests/test_interface.py405
-rw-r--r--dom/bindings/parser/tests/test_interface_const_identifier_conflicts.py15
-rw-r--r--dom/bindings/parser/tests/test_interface_identifier_conflicts_across_members.py60
-rw-r--r--dom/bindings/parser/tests/test_interface_maplikesetlikeiterable.py691
-rw-r--r--dom/bindings/parser/tests/test_lenientSetter.py58
-rw-r--r--dom/bindings/parser/tests/test_method.py178
-rw-r--r--dom/bindings/parser/tests/test_mozmap.py39
-rw-r--r--dom/bindings/parser/tests/test_namespace.py223
-rw-r--r--dom/bindings/parser/tests/test_newobject.py70
-rw-r--r--dom/bindings/parser/tests/test_nullable_equivalency.py115
-rw-r--r--dom/bindings/parser/tests/test_nullable_void.py14
-rw-r--r--dom/bindings/parser/tests/test_optional_constraints.py30
-rw-r--r--dom/bindings/parser/tests/test_overload.py60
-rw-r--r--dom/bindings/parser/tests/test_promise.py63
-rw-r--r--dom/bindings/parser/tests/test_prototype_ident.py80
-rw-r--r--dom/bindings/parser/tests/test_putForwards.py107
-rw-r--r--dom/bindings/parser/tests/test_replaceable.py58
-rw-r--r--dom/bindings/parser/tests/test_sanity.py7
-rw-r--r--dom/bindings/parser/tests/test_securecontext_extended_attribute.py332
-rw-r--r--dom/bindings/parser/tests/test_special_method_signature_mismatch.py294
-rw-r--r--dom/bindings/parser/tests/test_special_methods.py85
-rw-r--r--dom/bindings/parser/tests/test_special_methods_uniqueness.py62
-rw-r--r--dom/bindings/parser/tests/test_stringifier.py46
-rw-r--r--dom/bindings/parser/tests/test_treatNonCallableAsNull.py71
-rw-r--r--dom/bindings/parser/tests/test_typedef.py76
-rw-r--r--dom/bindings/parser/tests/test_unenumerable_own_properties.py64
-rw-r--r--dom/bindings/parser/tests/test_unforgeable.py253
-rw-r--r--dom/bindings/parser/tests/test_union.py168
-rw-r--r--dom/bindings/parser/tests/test_union_any.py14
-rw-r--r--dom/bindings/parser/tests/test_union_nullable.py53
-rw-r--r--dom/bindings/parser/tests/test_usvstring.py36
-rw-r--r--dom/bindings/parser/tests/test_variadic_callback.py10
-rw-r--r--dom/bindings/parser/tests/test_variadic_constraints.py63
-rw-r--r--dom/bindings/test/Makefile.in21
-rw-r--r--dom/bindings/test/TestBindingHeader.h1431
-rw-r--r--dom/bindings/test/TestCImplementedInterface.h43
-rw-r--r--dom/bindings/test/TestCodeGen.webidl1264
-rw-r--r--dom/bindings/test/TestDictionary.webidl9
-rw-r--r--dom/bindings/test/TestExampleGen.webidl811
-rw-r--r--dom/bindings/test/TestFunctions.cpp94
-rw-r--r--dom/bindings/test/TestFunctions.h52
-rw-r--r--dom/bindings/test/TestInterfaceIterableDouble.cpp82
-rw-r--r--dom/bindings/test/TestInterfaceIterableDouble.h51
-rw-r--r--dom/bindings/test/TestInterfaceIterableDoubleUnion.cpp83
-rw-r--r--dom/bindings/test/TestInterfaceIterableDoubleUnion.h51
-rw-r--r--dom/bindings/test/TestInterfaceIterableSingle.cpp77
-rw-r--r--dom/bindings/test/TestInterfaceIterableSingle.h51
-rw-r--r--dom/bindings/test/TestInterfaceJS.js166
-rw-r--r--dom/bindings/test/TestInterfaceJS.manifest4
-rw-r--r--dom/bindings/test/TestInterfaceJSMaplike.js38
-rw-r--r--dom/bindings/test/TestInterfaceMaplike.cpp84
-rw-r--r--dom/bindings/test/TestInterfaceMaplike.h52
-rw-r--r--dom/bindings/test/TestInterfaceMaplikeObject.cpp88
-rw-r--r--dom/bindings/test/TestInterfaceMaplikeObject.h52
-rw-r--r--dom/bindings/test/TestInterfaceSetlike.cpp58
-rw-r--r--dom/bindings/test/TestInterfaceSetlike.h46
-rw-r--r--dom/bindings/test/TestInterfaceSetlikeNode.cpp58
-rw-r--r--dom/bindings/test/TestInterfaceSetlikeNode.h46
-rw-r--r--dom/bindings/test/TestJSImplGen.webidl836
-rw-r--r--dom/bindings/test/TestJSImplInheritanceGen.webidl29
-rw-r--r--dom/bindings/test/TestTypedef.webidl7
-rw-r--r--dom/bindings/test/chrome.ini22
-rw-r--r--dom/bindings/test/file_InstanceOf.html12
-rw-r--r--dom/bindings/test/file_bug775543.html5
-rw-r--r--dom/bindings/test/file_document_location_set_via_xray.html5
-rw-r--r--dom/bindings/test/file_dom_xrays.html24
-rw-r--r--dom/bindings/test/file_focuser.html24
-rw-r--r--dom/bindings/test/file_fullScreenPropertyAccessor.html24
-rw-r--r--dom/bindings/test/file_proxies_via_xray.html8
-rw-r--r--dom/bindings/test/forOf_iframe.html13
-rw-r--r--dom/bindings/test/mochitest.ini79
-rw-r--r--dom/bindings/test/moz.build58
-rw-r--r--dom/bindings/test/test_ByteString.html32
-rw-r--r--dom/bindings/test/test_InstanceOf.html54
-rw-r--r--dom/bindings/test/test_Object.prototype_props.html20
-rw-r--r--dom/bindings/test/test_async_stacks.html108
-rw-r--r--dom/bindings/test/test_barewordGetsWindow.html60
-rw-r--r--dom/bindings/test/test_blacklisted_prerendering_function.xul124
-rw-r--r--dom/bindings/test/test_bug1036214.html123
-rw-r--r--dom/bindings/test/test_bug1041646.html49
-rw-r--r--dom/bindings/test/test_bug1123516_maplikesetlike.html271
-rw-r--r--dom/bindings/test/test_bug1123516_maplikesetlikechrome.xul68
-rw-r--r--dom/bindings/test/test_bug1123875.html14
-rw-r--r--dom/bindings/test/test_bug1287912.html37
-rw-r--r--dom/bindings/test/test_bug560072.html33
-rw-r--r--dom/bindings/test/test_bug742191.html36
-rw-r--r--dom/bindings/test/test_bug759621.html29
-rw-r--r--dom/bindings/test/test_bug773326.html11
-rw-r--r--dom/bindings/test/test_bug775543.html37
-rw-r--r--dom/bindings/test/test_bug788369.html30
-rw-r--r--dom/bindings/test/test_bug852846.html34
-rw-r--r--dom/bindings/test/test_bug862092.html37
-rw-r--r--dom/bindings/test/test_bug963382.html43
-rw-r--r--dom/bindings/test/test_callback_across_document_open.html21
-rw-r--r--dom/bindings/test/test_callback_default_thisval.html36
-rw-r--r--dom/bindings/test/test_callback_exceptions.html17
-rw-r--r--dom/bindings/test/test_cloneAndImportNode.html48
-rw-r--r--dom/bindings/test/test_crossOriginWindowSymbolAccess.html23
-rw-r--r--dom/bindings/test/test_defineProperty.html157
-rw-r--r--dom/bindings/test/test_document_location_set_via_xray.html49
-rw-r--r--dom/bindings/test/test_document_location_via_xray_cached.html36
-rw-r--r--dom/bindings/test/test_domProxyArrayLengthGetter.html28
-rw-r--r--dom/bindings/test/test_dom_xrays.html231
-rw-r--r--dom/bindings/test/test_enums.html15
-rw-r--r--dom/bindings/test/test_exceptionSanitization.html45
-rw-r--r--dom/bindings/test/test_exceptionThrowing.html56
-rw-r--r--dom/bindings/test/test_exception_messages.html82
-rw-r--r--dom/bindings/test/test_exception_options_from_jsimplemented.html166
-rw-r--r--dom/bindings/test/test_exceptions_from_jsimplemented.html56
-rw-r--r--dom/bindings/test/test_forOf.html86
-rw-r--r--dom/bindings/test/test_integers.html50
-rw-r--r--dom/bindings/test/test_interfaceName.html28
-rw-r--r--dom/bindings/test/test_interfaceToString.html47
-rw-r--r--dom/bindings/test/test_iterable.html241
-rw-r--r--dom/bindings/test/test_jsimplemented_eventhandler.html47
-rw-r--r--dom/bindings/test/test_kill_longrunning_prerendered_content.xul85
-rw-r--r--dom/bindings/test/test_lenientThis.html27
-rw-r--r--dom/bindings/test/test_lookupGetter.html49
-rw-r--r--dom/bindings/test/test_namedNoIndexed.html36
-rw-r--r--dom/bindings/test/test_named_getter_enumerability.html40
-rw-r--r--dom/bindings/test/test_oom_reporting.html42
-rw-r--r--dom/bindings/test/test_primitive_this.html45
-rw-r--r--dom/bindings/test/test_promise_rejections_from_jsimplemented.html143
-rw-r--r--dom/bindings/test/test_proxies_via_xray.html99
-rw-r--r--dom/bindings/test/test_queryInterface.html41
-rw-r--r--dom/bindings/test/test_returnUnion.html59
-rw-r--r--dom/bindings/test/test_sequence_detection.html53
-rw-r--r--dom/bindings/test/test_sequence_wrapping.html59
-rw-r--r--dom/bindings/test/test_setWithNamedGetterNoNamedSetter.html40
-rw-r--r--dom/bindings/test/test_stringBindings.html59
-rw-r--r--dom/bindings/test/test_throwing_method_noDCE.html27
-rw-r--r--dom/bindings/test/test_traceProtos.html37
-rw-r--r--dom/bindings/test/test_treat_non_object_as_null.html39
-rw-r--r--dom/bindings/test/test_unforgeablesonexpando.html18
-rw-r--r--dom/bindings/test/test_usvstring.html41
-rw-r--r--dom/bindings/test/test_worker_UnwrapArg.html58
245 files changed, 59965 insertions, 0 deletions
diff --git a/dom/bindings/AtomList.h b/dom/bindings/AtomList.h
new file mode 100644
index 000000000..53765e101
--- /dev/null
+++ b/dom/bindings/AtomList.h
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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_AtomList_h
+#define mozilla_dom_AtomList_h
+
+#include "jsapi.h"
+#include "mozilla/dom/GeneratedAtomList.h"
+
+namespace mozilla {
+namespace dom {
+
+template<class T>
+T* GetAtomCache(JSContext* aCx)
+{
+ auto atomCache = static_cast<PerThreadAtomCache*>(JS_GetContextPrivate(aCx));
+
+ return static_cast<T*>(atomCache);
+}
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_AtomList_h
diff --git a/dom/bindings/BindingDeclarations.h b/dom/bindings/BindingDeclarations.h
new file mode 100644
index 000000000..c712511ab
--- /dev/null
+++ b/dom/bindings/BindingDeclarations.h
@@ -0,0 +1,535 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 header for declaring various things that binding implementation headers
+ * might need. The idea is to make binding implementation headers safe to
+ * include anywhere without running into include hell like we do with
+ * BindingUtils.h
+ */
+#ifndef mozilla_dom_BindingDeclarations_h__
+#define mozilla_dom_BindingDeclarations_h__
+
+#include "js/RootingAPI.h"
+#include "js/Value.h"
+
+#include "mozilla/Maybe.h"
+#include "mozilla/RootedOwningNonNull.h"
+#include "mozilla/RootedRefPtr.h"
+
+#include "mozilla/dom/DOMString.h"
+
+#include "nsCOMPtr.h"
+#include "nsStringGlue.h"
+#include "nsTArray.h"
+
+class nsIPrincipal;
+class nsWrapperCache;
+
+namespace mozilla {
+namespace dom {
+
+// Struct that serves as a base class for all dictionaries. Particularly useful
+// so we can use IsBaseOf to detect dictionary template arguments.
+struct DictionaryBase
+{
+protected:
+ bool ParseJSON(JSContext* aCx, const nsAString& aJSON,
+ JS::MutableHandle<JS::Value> aVal);
+
+ bool StringifyToJSON(JSContext* aCx,
+ JS::Handle<JSObject*> aObj,
+ nsAString& aJSON) const;
+
+ // Struct used as a way to force a dictionary constructor to not init the
+ // dictionary (via constructing from a pointer to this class). We're putting
+ // it here so that all the dictionaries will have access to it, but outside
+ // code will not.
+ struct FastDictionaryInitializer {
+ };
+
+ bool mIsAnyMemberPresent = false;
+
+private:
+ // aString is expected to actually be an nsAString*. Should only be
+ // called from StringifyToJSON.
+ static bool AppendJSONToString(const char16_t* aJSONData,
+ uint32_t aDataLength, void* aString);
+
+public:
+ bool IsAnyMemberPresent() const
+ {
+ return mIsAnyMemberPresent;
+ }
+};
+
+// Struct that serves as a base class for all typed arrays and array buffers and
+// array buffer views. Particularly useful so we can use IsBaseOf to detect
+// typed array/buffer/view template arguments.
+struct AllTypedArraysBase {
+};
+
+// Struct that serves as a base class for all owning unions.
+// Particularly useful so we can use IsBaseOf to detect owning union
+// template arguments.
+struct AllOwningUnionBase {
+};
+
+
+struct EnumEntry {
+ const char* value;
+ size_t length;
+};
+
+class MOZ_STACK_CLASS GlobalObject
+{
+public:
+ GlobalObject(JSContext* aCx, JSObject* aObject);
+
+ JSObject* Get() const
+ {
+ return mGlobalJSObject;
+ }
+
+ nsISupports* GetAsSupports() const;
+
+ // The context that this returns is not guaranteed to be in the compartment of
+ // the object returned from Get(), in fact it's generally in the caller's
+ // compartment.
+ JSContext* Context() const
+ {
+ return mCx;
+ }
+
+ bool Failed() const
+ {
+ return !Get();
+ }
+
+ // It returns the subjectPrincipal if called on the main-thread, otherwise
+ // a nullptr is returned.
+ nsIPrincipal* GetSubjectPrincipal() const;
+
+protected:
+ JS::Rooted<JSObject*> mGlobalJSObject;
+ JSContext* mCx;
+ mutable nsISupports* MOZ_UNSAFE_REF("Valid because GlobalObject is a stack "
+ "class, and mGlobalObject points to the "
+ "global, so it won't be destroyed as long "
+ "as GlobalObject lives on the stack") mGlobalObject;
+};
+
+// Class for representing optional arguments.
+template<typename T, typename InternalType>
+class Optional_base
+{
+public:
+ Optional_base()
+ {}
+
+ explicit Optional_base(const T& aValue)
+ {
+ mImpl.emplace(aValue);
+ }
+
+ bool operator==(const Optional_base<T, InternalType>& aOther) const
+ {
+ return mImpl == aOther.mImpl;
+ }
+
+ template<typename T1, typename T2>
+ explicit Optional_base(const T1& aValue1, const T2& aValue2)
+ {
+ mImpl.emplace(aValue1, aValue2);
+ }
+
+ bool WasPassed() const
+ {
+ return mImpl.isSome();
+ }
+
+ // Return InternalType here so we can work with it usefully.
+ template<typename... Args>
+ InternalType& Construct(Args&&... aArgs)
+ {
+ mImpl.emplace(Forward<Args>(aArgs)...);
+ return *mImpl;
+ }
+
+ void Reset()
+ {
+ mImpl.reset();
+ }
+
+ const T& Value() const
+ {
+ return *mImpl;
+ }
+
+ // Return InternalType here so we can work with it usefully.
+ InternalType& Value()
+ {
+ return *mImpl;
+ }
+
+ // And an explicit way to get the InternalType even if we're const.
+ const InternalType& InternalValue() const
+ {
+ return *mImpl;
+ }
+
+ // If we ever decide to add conversion operators for optional arrays
+ // like the ones Nullable has, we'll need to ensure that Maybe<> has
+ // the boolean before the actual data.
+
+private:
+ // Forbid copy-construction and assignment
+ Optional_base(const Optional_base& other) = delete;
+ const Optional_base &operator=(const Optional_base &other) = delete;
+
+protected:
+ Maybe<InternalType> mImpl;
+};
+
+template<typename T>
+class Optional : public Optional_base<T, T>
+{
+public:
+ Optional() :
+ Optional_base<T, T>()
+ {}
+
+ explicit Optional(const T& aValue) :
+ Optional_base<T, T>(aValue)
+ {}
+};
+
+template<typename T>
+class Optional<JS::Handle<T> > :
+ public Optional_base<JS::Handle<T>, JS::Rooted<T> >
+{
+public:
+ Optional() :
+ Optional_base<JS::Handle<T>, JS::Rooted<T> >()
+ {}
+
+ explicit Optional(JSContext* cx) :
+ Optional_base<JS::Handle<T>, JS::Rooted<T> >()
+ {
+ this->Construct(cx);
+ }
+
+ Optional(JSContext* cx, const T& aValue) :
+ Optional_base<JS::Handle<T>, JS::Rooted<T> >(cx, aValue)
+ {}
+
+ // Override the const Value() to return the right thing so we're not
+ // returning references to temporaries.
+ JS::Handle<T> Value() const
+ {
+ return *this->mImpl;
+ }
+
+ // And we have to override the non-const one too, since we're
+ // shadowing the one on the superclass.
+ JS::Rooted<T>& Value()
+ {
+ return *this->mImpl;
+ }
+};
+
+// A specialization of Optional for JSObject* to make sure that when someone
+// calls Construct() on it we will pre-initialized the JSObject* to nullptr so
+// it can be traced safely.
+template<>
+class Optional<JSObject*> : public Optional_base<JSObject*, JSObject*>
+{
+public:
+ Optional() :
+ Optional_base<JSObject*, JSObject*>()
+ {}
+
+ explicit Optional(JSObject* aValue) :
+ Optional_base<JSObject*, JSObject*>(aValue)
+ {}
+
+ // Don't allow us to have an uninitialized JSObject*
+ JSObject*& Construct()
+ {
+ // The Android compiler sucks and thinks we're trying to construct
+ // a JSObject* from an int if we don't cast here. :(
+ return Optional_base<JSObject*, JSObject*>::Construct(
+ static_cast<JSObject*>(nullptr));
+ }
+
+ template <class T1>
+ JSObject*& Construct(const T1& t1)
+ {
+ return Optional_base<JSObject*, JSObject*>::Construct(t1);
+ }
+};
+
+// A specialization of Optional for JS::Value to make sure no one ever uses it.
+template<>
+class Optional<JS::Value>
+{
+private:
+ Optional() = delete;
+
+ explicit Optional(const JS::Value& aValue) = delete;
+};
+
+// A specialization of Optional for NonNull that lets us get a T& from Value()
+template<typename U> class NonNull;
+template<typename T>
+class Optional<NonNull<T> > : public Optional_base<T, NonNull<T> >
+{
+public:
+ // We want our Value to actually return a non-const reference, even
+ // if we're const. At least for things that are normally pointer
+ // types...
+ T& Value() const
+ {
+ return *this->mImpl->get();
+ }
+
+ // And we have to override the non-const one too, since we're
+ // shadowing the one on the superclass.
+ NonNull<T>& Value()
+ {
+ return *this->mImpl;
+ }
+};
+
+// A specialization of Optional for OwningNonNull that lets us get a
+// T& from Value()
+template<typename T>
+class Optional<OwningNonNull<T> > : public Optional_base<T, OwningNonNull<T> >
+{
+public:
+ // We want our Value to actually return a non-const reference, even
+ // if we're const. At least for things that are normally pointer
+ // types...
+ T& Value() const
+ {
+ return *this->mImpl->get();
+ }
+
+ // And we have to override the non-const one too, since we're
+ // shadowing the one on the superclass.
+ OwningNonNull<T>& Value()
+ {
+ return *this->mImpl;
+ }
+};
+
+// Specialization for strings.
+// XXXbz we can't pull in FakeString here, because it depends on internal
+// strings. So we just have to forward-declare it and reimplement its
+// ToAStringPtr.
+
+namespace binding_detail {
+struct FakeString;
+} // namespace binding_detail
+
+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;
+ }
+
+ // If this code ever goes away, remove the comment pointing to it in the
+ // FakeString class in BindingUtils.h.
+ void operator=(const binding_detail::FakeString* str)
+ {
+ MOZ_ASSERT(str);
+ mStr = reinterpret_cast<const nsString*>(str);
+ mPassed = true;
+ }
+
+ const nsAString& Value() const
+ {
+ MOZ_ASSERT(WasPassed());
+ return *mStr;
+ }
+
+private:
+ // Forbid copy-construction and assignment
+ Optional(const Optional& other) = delete;
+ const Optional &operator=(const Optional &other) = delete;
+
+ bool mPassed;
+ const nsAString* mStr;
+};
+
+template<class T>
+class NonNull
+{
+public:
+ NonNull()
+#ifdef DEBUG
+ : inited(false)
+#endif
+ {}
+
+ // This is no worse than get() in terms of const handling.
+ operator T&() const {
+ MOZ_ASSERT(inited);
+ MOZ_ASSERT(ptr, "NonNull<T> was set to null");
+ return *ptr;
+ }
+
+ operator 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;
+ }
+
+ T* Ptr() {
+ MOZ_ASSERT(inited);
+ MOZ_ASSERT(ptr, "NonNull<T> was set to null");
+ return ptr;
+ }
+
+ // Make us work with smart-ptr helpers that expect a get()
+ T* get() const {
+ MOZ_ASSERT(inited);
+ MOZ_ASSERT(ptr);
+ return ptr;
+ }
+
+protected:
+ T* ptr;
+#ifdef DEBUG
+ bool inited;
+#endif
+};
+
+// Class for representing sequences in arguments. We use a non-auto array
+// because that allows us to use sequences of sequences and the like. 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 FallibleTArray<T>
+{
+public:
+ Sequence() : FallibleTArray<T>()
+ {}
+};
+
+inline nsWrapperCache*
+GetWrapperCache(nsWrapperCache* cache)
+{
+ return cache;
+}
+
+inline nsWrapperCache*
+GetWrapperCache(void* p)
+{
+ return nullptr;
+}
+
+// Helper template for smart pointers to resolve ambiguity between
+// GetWrappeCache(void*) and GetWrapperCache(const ParentObject&).
+template <template <typename> class SmartPtr, typename T>
+inline nsWrapperCache*
+GetWrapperCache(const SmartPtr<T>& aObject)
+{
+ return GetWrapperCache(aObject.get());
+}
+
+struct MOZ_STACK_CLASS ParentObject {
+ template<class T>
+ MOZ_IMPLICIT ParentObject(T* aObject) :
+ mObject(aObject),
+ mWrapperCache(GetWrapperCache(aObject)),
+ mUseXBLScope(false)
+ {}
+
+ template<class T, template<typename> class SmartPtr>
+ MOZ_IMPLICIT ParentObject(const SmartPtr<T>& aObject) :
+ mObject(aObject.get()),
+ mWrapperCache(GetWrapperCache(aObject.get())),
+ mUseXBLScope(false)
+ {}
+
+ ParentObject(nsISupports* aObject, nsWrapperCache* aCache) :
+ mObject(aObject),
+ mWrapperCache(aCache),
+ mUseXBLScope(false)
+ {}
+
+ // We don't want to make this an nsCOMPtr because of performance reasons, but
+ // it's safe because ParentObject is a stack class.
+ nsISupports* const MOZ_NON_OWNING_REF mObject;
+ nsWrapperCache* const mWrapperCache;
+ bool mUseXBLScope;
+};
+
+namespace binding_detail {
+
+// Class for simple sequence arguments, only used internally by codegen.
+template<typename T>
+class AutoSequence : public AutoTArray<T, 16>
+{
+public:
+ AutoSequence() : AutoTArray<T, 16>()
+ {}
+
+ // Allow converting to const sequences as needed
+ operator const Sequence<T>&() const {
+ return *reinterpret_cast<const Sequence<T>*>(this);
+ }
+};
+
+} // namespace binding_detail
+
+// Enum to represent a system or non-system caller type.
+enum class CallerType : uint32_t {
+ System,
+ NonSystem
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_BindingDeclarations_h__
diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp
new file mode 100644
index 000000000..33f5f7a44
--- /dev/null
+++ b/dom/bindings/BindingUtils.cpp
@@ -0,0 +1,3549 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "BindingUtils.h"
+
+#include <algorithm>
+#include <stdarg.h>
+
+#include "mozilla/Assertions.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/FloatingPoint.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/SizePrintfMacros.h"
+#include "mozilla/Unused.h"
+#include "mozilla/UseCounter.h"
+
+#include "AccessCheck.h"
+#include "jsfriendapi.h"
+#include "nsContentUtils.h"
+#include "nsGlobalWindow.h"
+#include "nsIDocShell.h"
+#include "nsIDOMGlobalPropertyInitializer.h"
+#include "nsIPermissionManager.h"
+#include "nsIPrincipal.h"
+#include "nsIXPConnect.h"
+#include "nsUTF8Utils.h"
+#include "WorkerPrivate.h"
+#include "WorkerRunnable.h"
+#include "WrapperFactory.h"
+#include "xpcprivate.h"
+#include "XrayWrapper.h"
+#include "nsPrintfCString.h"
+#include "mozilla/Sprintf.h"
+#include "nsGlobalWindow.h"
+
+#include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/dom/DOMError.h"
+#include "mozilla/dom/DOMErrorBinding.h"
+#include "mozilla/dom/DOMException.h"
+#include "mozilla/dom/ElementBinding.h"
+#include "mozilla/dom/HTMLObjectElement.h"
+#include "mozilla/dom/HTMLObjectElementBinding.h"
+#include "mozilla/dom/HTMLSharedObjectElement.h"
+#include "mozilla/dom/HTMLEmbedElementBinding.h"
+#include "mozilla/dom/HTMLAppletElementBinding.h"
+#include "mozilla/dom/Promise.h"
+#include "mozilla/dom/ResolveSystemBinding.h"
+#include "mozilla/dom/WebIDLGlobalNameHash.h"
+#include "mozilla/dom/WorkerPrivate.h"
+#include "mozilla/dom/WorkerScope.h"
+#include "mozilla/dom/XrayExpandoClass.h"
+#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
+#include "nsDOMClassInfo.h"
+#include "ipc/ErrorIPCUtils.h"
+#include "mozilla/UseCounter.h"
+
+namespace mozilla {
+namespace dom {
+
+using namespace workers;
+
+const JSErrorFormatString ErrorFormatString[] = {
+#define MSG_DEF(_name, _argc, _exn, _str) \
+ { #_name, _str, _argc, _exn },
+#include "mozilla/dom/Errors.msg"
+#undef MSG_DEF
+};
+
+#define MSG_DEF(_name, _argc, _exn, _str) \
+ static_assert(_argc < JS::MaxNumErrorArguments, \
+ #_name " must only have as many error arguments as the JS engine can support");
+#include "mozilla/dom/Errors.msg"
+#undef MSG_DEF
+
+const JSErrorFormatString*
+GetErrorMessage(void* aUserRef, const unsigned aErrorNumber)
+{
+ MOZ_ASSERT(aErrorNumber < ArrayLength(ErrorFormatString));
+ return &ErrorFormatString[aErrorNumber];
+}
+
+uint16_t
+GetErrorArgCount(const ErrNum aErrorNumber)
+{
+ return GetErrorMessage(nullptr, aErrorNumber)->argCount;
+}
+
+void
+binding_detail::ThrowErrorMessage(JSContext* aCx, const unsigned aErrorNumber, ...)
+{
+ va_list ap;
+ va_start(ap, aErrorNumber);
+ JS_ReportErrorNumberUTF8VA(aCx, GetErrorMessage, nullptr, aErrorNumber, ap);
+ va_end(ap);
+}
+
+bool
+ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs,
+ bool aSecurityError, const char* aInterfaceName)
+{
+ NS_ConvertASCIItoUTF16 ifaceName(aInterfaceName);
+ // This should only be called for DOM methods/getters/setters, which
+ // are JSNative-backed functions, so we can assume that
+ // JS_ValueToFunction and JS_GetFunctionDisplayId will both return
+ // non-null and that JS_GetStringCharsZ returns non-null.
+ JS::Rooted<JSFunction*> func(aCx, JS_ValueToFunction(aCx, aArgs.calleev()));
+ MOZ_ASSERT(func);
+ JS::Rooted<JSString*> funcName(aCx, JS_GetFunctionDisplayId(func));
+ MOZ_ASSERT(funcName);
+ nsAutoJSString funcNameStr;
+ if (!funcNameStr.init(aCx, funcName)) {
+ return false;
+ }
+ const ErrNum errorNumber = aSecurityError ?
+ MSG_METHOD_THIS_UNWRAPPING_DENIED :
+ MSG_METHOD_THIS_DOES_NOT_IMPLEMENT_INTERFACE;
+ MOZ_RELEASE_ASSERT(GetErrorArgCount(errorNumber) <= 2);
+ JS_ReportErrorNumberUC(aCx, GetErrorMessage, nullptr,
+ static_cast<const unsigned>(errorNumber),
+ funcNameStr.get(), ifaceName.get());
+ return false;
+}
+
+bool
+ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs,
+ bool aSecurityError,
+ prototypes::ID aProtoId)
+{
+ return ThrowInvalidThis(aCx, aArgs, aSecurityError,
+ NamesOfInterfacesWithProtos(aProtoId));
+}
+
+bool
+ThrowNoSetterArg(JSContext* aCx, prototypes::ID aProtoId)
+{
+ nsPrintfCString errorMessage("%s attribute setter",
+ NamesOfInterfacesWithProtos(aProtoId));
+ return ThrowErrorMessage(aCx, MSG_MISSING_ARGUMENTS, errorMessage.get());
+}
+
+} // namespace dom
+
+namespace binding_danger {
+
+template<typename CleanupPolicy>
+struct TErrorResult<CleanupPolicy>::Message {
+ Message() { MOZ_COUNT_CTOR(TErrorResult::Message); }
+ ~Message() { MOZ_COUNT_DTOR(TErrorResult::Message); }
+
+ nsTArray<nsString> mArgs;
+ dom::ErrNum mErrorNumber;
+
+ bool HasCorrectNumberOfArguments()
+ {
+ return GetErrorArgCount(mErrorNumber) == mArgs.Length();
+ }
+};
+
+template<typename CleanupPolicy>
+nsTArray<nsString>&
+TErrorResult<CleanupPolicy>::CreateErrorMessageHelper(const dom::ErrNum errorNumber,
+ nsresult errorType)
+{
+ AssertInOwningThread();
+ mResult = errorType;
+
+ mMessage = new Message();
+ mMessage->mErrorNumber = errorNumber;
+ return mMessage->mArgs;
+}
+
+template<typename CleanupPolicy>
+void
+TErrorResult<CleanupPolicy>::SerializeMessage(IPC::Message* aMsg) const
+{
+ using namespace IPC;
+ AssertInOwningThread();
+ MOZ_ASSERT(mUnionState == HasMessage);
+ MOZ_ASSERT(mMessage);
+ WriteParam(aMsg, mMessage->mArgs);
+ WriteParam(aMsg, mMessage->mErrorNumber);
+}
+
+template<typename CleanupPolicy>
+bool
+TErrorResult<CleanupPolicy>::DeserializeMessage(const IPC::Message* aMsg,
+ PickleIterator* aIter)
+{
+ using namespace IPC;
+ AssertInOwningThread();
+ nsAutoPtr<Message> readMessage(new Message());
+ if (!ReadParam(aMsg, aIter, &readMessage->mArgs) ||
+ !ReadParam(aMsg, aIter, &readMessage->mErrorNumber)) {
+ return false;
+ }
+ if (!readMessage->HasCorrectNumberOfArguments()) {
+ return false;
+ }
+
+ MOZ_ASSERT(mUnionState == HasNothing);
+ mMessage = readMessage.forget();
+#ifdef DEBUG
+ mUnionState = HasMessage;
+#endif // DEBUG
+ return true;
+}
+
+template<typename CleanupPolicy>
+void
+TErrorResult<CleanupPolicy>::SetPendingExceptionWithMessage(JSContext* aCx)
+{
+ AssertInOwningThread();
+ MOZ_ASSERT(mMessage, "SetPendingExceptionWithMessage() can be called only once");
+ MOZ_ASSERT(mUnionState == HasMessage);
+
+ Message* message = mMessage;
+ MOZ_RELEASE_ASSERT(message->HasCorrectNumberOfArguments());
+ const uint32_t argCount = message->mArgs.Length();
+ const char16_t* args[JS::MaxNumErrorArguments + 1];
+ for (uint32_t i = 0; i < argCount; ++i) {
+ args[i] = message->mArgs.ElementAt(i).get();
+ }
+ args[argCount] = nullptr;
+
+ JS_ReportErrorNumberUCArray(aCx, dom::GetErrorMessage, nullptr,
+ static_cast<const unsigned>(message->mErrorNumber),
+ argCount > 0 ? args : nullptr);
+
+ ClearMessage();
+ mResult = NS_OK;
+}
+
+template<typename CleanupPolicy>
+void
+TErrorResult<CleanupPolicy>::ClearMessage()
+{
+ AssertInOwningThread();
+ MOZ_ASSERT(IsErrorWithMessage());
+ delete mMessage;
+ mMessage = nullptr;
+#ifdef DEBUG
+ mUnionState = HasNothing;
+#endif // DEBUG
+}
+
+template<typename CleanupPolicy>
+void
+TErrorResult<CleanupPolicy>::ThrowJSException(JSContext* cx, JS::Handle<JS::Value> exn)
+{
+ AssertInOwningThread();
+ MOZ_ASSERT(mMightHaveUnreportedJSException,
+ "Why didn't you tell us you planned to throw a JS exception?");
+
+ ClearUnionData();
+
+ // Make sure mJSException is initialized _before_ we try to root it. But
+ // don't set it to exn yet, because we don't want to do that until after we
+ // root.
+ mJSException.setUndefined();
+ if (!js::AddRawValueRoot(cx, &mJSException, "TErrorResult::mJSException")) {
+ // Don't use NS_ERROR_DOM_JS_EXCEPTION, because that indicates we have
+ // in fact rooted mJSException.
+ mResult = NS_ERROR_OUT_OF_MEMORY;
+ } else {
+ mJSException = exn;
+ mResult = NS_ERROR_DOM_JS_EXCEPTION;
+#ifdef DEBUG
+ mUnionState = HasJSException;
+#endif // DEBUG
+ }
+}
+
+template<typename CleanupPolicy>
+void
+TErrorResult<CleanupPolicy>::SetPendingJSException(JSContext* cx)
+{
+ AssertInOwningThread();
+ MOZ_ASSERT(!mMightHaveUnreportedJSException,
+ "Why didn't you tell us you planned to handle JS exceptions?");
+ MOZ_ASSERT(mUnionState == HasJSException);
+
+ JS::Rooted<JS::Value> exception(cx, mJSException);
+ if (JS_WrapValue(cx, &exception)) {
+ JS_SetPendingException(cx, exception);
+ }
+ mJSException = exception;
+ // If JS_WrapValue failed, not much we can do about it... No matter
+ // what, go ahead and unroot mJSException.
+ js::RemoveRawValueRoot(cx, &mJSException);
+
+ mResult = NS_OK;
+#ifdef DEBUG
+ mUnionState = HasNothing;
+#endif // DEBUG
+}
+
+template<typename CleanupPolicy>
+struct TErrorResult<CleanupPolicy>::DOMExceptionInfo {
+ DOMExceptionInfo(nsresult rv, const nsACString& message)
+ : mMessage(message)
+ , mRv(rv)
+ {}
+
+ nsCString mMessage;
+ nsresult mRv;
+};
+
+template<typename CleanupPolicy>
+void
+TErrorResult<CleanupPolicy>::SerializeDOMExceptionInfo(IPC::Message* aMsg) const
+{
+ using namespace IPC;
+ AssertInOwningThread();
+ MOZ_ASSERT(mDOMExceptionInfo);
+ MOZ_ASSERT(mUnionState == HasDOMExceptionInfo);
+ WriteParam(aMsg, mDOMExceptionInfo->mMessage);
+ WriteParam(aMsg, mDOMExceptionInfo->mRv);
+}
+
+template<typename CleanupPolicy>
+bool
+TErrorResult<CleanupPolicy>::DeserializeDOMExceptionInfo(const IPC::Message* aMsg,
+ PickleIterator* aIter)
+{
+ using namespace IPC;
+ AssertInOwningThread();
+ nsCString message;
+ nsresult rv;
+ if (!ReadParam(aMsg, aIter, &message) ||
+ !ReadParam(aMsg, aIter, &rv)) {
+ return false;
+ }
+
+ MOZ_ASSERT(mUnionState == HasNothing);
+ MOZ_ASSERT(IsDOMException());
+ mDOMExceptionInfo = new DOMExceptionInfo(rv, message);
+#ifdef DEBUG
+ mUnionState = HasDOMExceptionInfo;
+#endif // DEBUG
+ return true;
+}
+
+template<typename CleanupPolicy>
+void
+TErrorResult<CleanupPolicy>::ThrowDOMException(nsresult rv,
+ const nsACString& message)
+{
+ AssertInOwningThread();
+ ClearUnionData();
+
+ mResult = NS_ERROR_DOM_DOMEXCEPTION;
+ mDOMExceptionInfo = new DOMExceptionInfo(rv, message);
+#ifdef DEBUG
+ mUnionState = HasDOMExceptionInfo;
+#endif
+}
+
+template<typename CleanupPolicy>
+void
+TErrorResult<CleanupPolicy>::SetPendingDOMException(JSContext* cx)
+{
+ AssertInOwningThread();
+ MOZ_ASSERT(mDOMExceptionInfo,
+ "SetPendingDOMException() can be called only once");
+ MOZ_ASSERT(mUnionState == HasDOMExceptionInfo);
+
+ dom::Throw(cx, mDOMExceptionInfo->mRv, mDOMExceptionInfo->mMessage);
+
+ ClearDOMExceptionInfo();
+ mResult = NS_OK;
+}
+
+template<typename CleanupPolicy>
+void
+TErrorResult<CleanupPolicy>::ClearDOMExceptionInfo()
+{
+ AssertInOwningThread();
+ MOZ_ASSERT(IsDOMException());
+ MOZ_ASSERT(mUnionState == HasDOMExceptionInfo || !mDOMExceptionInfo);
+ delete mDOMExceptionInfo;
+ mDOMExceptionInfo = nullptr;
+#ifdef DEBUG
+ mUnionState = HasNothing;
+#endif // DEBUG
+}
+
+template<typename CleanupPolicy>
+void
+TErrorResult<CleanupPolicy>::ClearUnionData()
+{
+ AssertInOwningThread();
+ if (IsJSException()) {
+ JSContext* cx = dom::danger::GetJSContext();
+ MOZ_ASSERT(cx);
+ mJSException.setUndefined();
+ js::RemoveRawValueRoot(cx, &mJSException);
+#ifdef DEBUG
+ mUnionState = HasNothing;
+#endif // DEBUG
+ } else if (IsErrorWithMessage()) {
+ ClearMessage();
+ } else if (IsDOMException()) {
+ ClearDOMExceptionInfo();
+ }
+}
+
+template<typename CleanupPolicy>
+void
+TErrorResult<CleanupPolicy>::SetPendingGenericErrorException(JSContext* cx)
+{
+ AssertInOwningThread();
+ MOZ_ASSERT(!IsErrorWithMessage());
+ MOZ_ASSERT(!IsJSException());
+ MOZ_ASSERT(!IsDOMException());
+ dom::Throw(cx, ErrorCode());
+ mResult = NS_OK;
+}
+
+template<typename CleanupPolicy>
+TErrorResult<CleanupPolicy>&
+TErrorResult<CleanupPolicy>::operator=(TErrorResult<CleanupPolicy>&& aRHS)
+{
+ AssertInOwningThread();
+ aRHS.AssertInOwningThread();
+ // Clear out any union members we may have right now, before we
+ // start writing to it.
+ ClearUnionData();
+
+#ifdef DEBUG
+ mMightHaveUnreportedJSException = aRHS.mMightHaveUnreportedJSException;
+ aRHS.mMightHaveUnreportedJSException = false;
+#endif
+ if (aRHS.IsErrorWithMessage()) {
+ mMessage = aRHS.mMessage;
+ aRHS.mMessage = nullptr;
+ } else if (aRHS.IsJSException()) {
+ JSContext* cx = dom::danger::GetJSContext();
+ MOZ_ASSERT(cx);
+ mJSException.setUndefined();
+ if (!js::AddRawValueRoot(cx, &mJSException, "TErrorResult::mJSException")) {
+ MOZ_CRASH("Could not root mJSException, we're about to OOM");
+ }
+ mJSException = aRHS.mJSException;
+ aRHS.mJSException.setUndefined();
+ js::RemoveRawValueRoot(cx, &aRHS.mJSException);
+ } else if (aRHS.IsDOMException()) {
+ mDOMExceptionInfo = aRHS.mDOMExceptionInfo;
+ aRHS.mDOMExceptionInfo = nullptr;
+ } else {
+ // Null out the union on both sides for hygiene purposes.
+ mMessage = aRHS.mMessage = nullptr;
+ }
+
+#ifdef DEBUG
+ mUnionState = aRHS.mUnionState;
+ aRHS.mUnionState = HasNothing;
+#endif // DEBUG
+
+ // Note: It's important to do this last, since this affects the condition
+ // checks above!
+ mResult = aRHS.mResult;
+ aRHS.mResult = NS_OK;
+ return *this;
+}
+
+template<typename CleanupPolicy>
+void
+TErrorResult<CleanupPolicy>::CloneTo(TErrorResult& aRv) const
+{
+ AssertInOwningThread();
+ aRv.AssertInOwningThread();
+
+ aRv.ClearUnionData();
+ aRv.mResult = mResult;
+#ifdef DEBUG
+ aRv.mMightHaveUnreportedJSException = mMightHaveUnreportedJSException;
+#endif
+
+ if (IsErrorWithMessage()) {
+#ifdef DEBUG
+ aRv.mUnionState = HasMessage;
+#endif
+ aRv.mMessage = new Message();
+ aRv.mMessage->mArgs = mMessage->mArgs;
+ aRv.mMessage->mErrorNumber = mMessage->mErrorNumber;
+ } else if (IsDOMException()) {
+#ifdef DEBUG
+ aRv.mUnionState = HasDOMExceptionInfo;
+#endif
+ aRv.mDOMExceptionInfo = new DOMExceptionInfo(mDOMExceptionInfo->mRv,
+ mDOMExceptionInfo->mMessage);
+ } else if (IsJSException()) {
+#ifdef DEBUG
+ aRv.mUnionState = HasJSException;
+#endif
+ JSContext* cx = dom::danger::GetJSContext();
+ JS::Rooted<JS::Value> exception(cx, mJSException);
+ aRv.ThrowJSException(cx, exception);
+ }
+}
+
+template<typename CleanupPolicy>
+void
+TErrorResult<CleanupPolicy>::SuppressException()
+{
+ AssertInOwningThread();
+ WouldReportJSException();
+ ClearUnionData();
+ // We don't use AssignErrorCode, because we want to override existing error
+ // states, which AssignErrorCode is not allowed to do.
+ mResult = NS_OK;
+}
+
+template<typename CleanupPolicy>
+void
+TErrorResult<CleanupPolicy>::SetPendingException(JSContext* cx)
+{
+ AssertInOwningThread();
+ if (IsUncatchableException()) {
+ // Nuke any existing exception on cx, to make sure we're uncatchable.
+ JS_ClearPendingException(cx);
+ // Don't do any reporting. Just return, to create an
+ // uncatchable exception.
+ mResult = NS_OK;
+ return;
+ }
+ if (IsJSContextException()) {
+ // Whatever we need to throw is on the JSContext already.
+ MOZ_ASSERT(JS_IsExceptionPending(cx));
+ mResult = NS_OK;
+ return;
+ }
+ if (IsErrorWithMessage()) {
+ SetPendingExceptionWithMessage(cx);
+ return;
+ }
+ if (IsJSException()) {
+ SetPendingJSException(cx);
+ return;
+ }
+ if (IsDOMException()) {
+ SetPendingDOMException(cx);
+ return;
+ }
+ SetPendingGenericErrorException(cx);
+}
+
+template<typename CleanupPolicy>
+void
+TErrorResult<CleanupPolicy>::StealExceptionFromJSContext(JSContext* cx)
+{
+ AssertInOwningThread();
+ MOZ_ASSERT(mMightHaveUnreportedJSException,
+ "Why didn't you tell us you planned to throw a JS exception?");
+
+ JS::Rooted<JS::Value> exn(cx);
+ if (!JS_GetPendingException(cx, &exn)) {
+ ThrowUncatchableException();
+ return;
+ }
+
+ ThrowJSException(cx, exn);
+ JS_ClearPendingException(cx);
+}
+
+template<typename CleanupPolicy>
+void
+TErrorResult<CleanupPolicy>::NoteJSContextException(JSContext* aCx)
+{
+ AssertInOwningThread();
+ if (JS_IsExceptionPending(aCx)) {
+ mResult = NS_ERROR_DOM_EXCEPTION_ON_JSCONTEXT;
+ } else {
+ mResult = NS_ERROR_UNCATCHABLE_EXCEPTION;
+ }
+}
+
+template class TErrorResult<JustAssertCleanupPolicy>;
+template class TErrorResult<AssertAndSuppressCleanupPolicy>;
+template class TErrorResult<JustSuppressCleanupPolicy>;
+
+} // namespace binding_danger
+
+namespace dom {
+
+bool
+DefineConstants(JSContext* cx, JS::Handle<JSObject*> obj,
+ const ConstantSpec* cs)
+{
+ JS::Rooted<JS::Value> value(cx);
+ for (; cs->name; ++cs) {
+ value = cs->value;
+ bool ok =
+ JS_DefineProperty(cx, obj, cs->name, value,
+ JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
+ if (!ok) {
+ return false;
+ }
+ }
+ return true;
+}
+
+static inline bool
+Define(JSContext* cx, JS::Handle<JSObject*> obj, const JSFunctionSpec* spec) {
+ return JS_DefineFunctions(cx, obj, spec);
+}
+static inline bool
+Define(JSContext* cx, JS::Handle<JSObject*> obj, const JSPropertySpec* spec) {
+ return JS_DefineProperties(cx, obj, spec);
+}
+static inline bool
+Define(JSContext* cx, JS::Handle<JSObject*> obj, const ConstantSpec* spec) {
+ return DefineConstants(cx, obj, spec);
+}
+
+template<typename T>
+bool
+DefinePrefable(JSContext* cx, JS::Handle<JSObject*> obj,
+ const Prefable<T>* props)
+{
+ MOZ_ASSERT(props);
+ MOZ_ASSERT(props->specs);
+ do {
+ // Define if enabled
+ if (props->isEnabled(cx, obj)) {
+ if (!Define(cx, obj, props->specs)) {
+ return false;
+ }
+ }
+ } while ((++props)->specs);
+ return true;
+}
+
+bool
+DefineUnforgeableMethods(JSContext* cx, JS::Handle<JSObject*> obj,
+ const Prefable<const JSFunctionSpec>* props)
+{
+ return DefinePrefable(cx, obj, props);
+}
+
+bool
+DefineUnforgeableAttributes(JSContext* cx, JS::Handle<JSObject*> obj,
+ const Prefable<const JSPropertySpec>* props)
+{
+ return DefinePrefable(cx, obj, props);
+}
+
+
+// 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. We also need Xrays and arbitrary numbers of
+// reserved slots (e.g. for named constructors). So we define a custom
+// funToString ObjectOps member for interface objects.
+JSString*
+InterfaceObjectToString(JSContext* aCx, JS::Handle<JSObject*> aObject,
+ unsigned /* indent */)
+{
+ const js::Class* clasp = js::GetObjectClass(aObject);
+ MOZ_ASSERT(IsDOMIfaceAndProtoClass(clasp));
+
+ const DOMIfaceAndProtoJSClass* ifaceAndProtoJSClass =
+ DOMIfaceAndProtoJSClass::FromJSClass(clasp);
+ return JS_NewStringCopyZ(aCx, ifaceAndProtoJSClass->mToString);
+}
+
+bool
+Constructor(JSContext* cx, unsigned argc, JS::Value* vp)
+{
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ const JS::Value& v =
+ js::GetFunctionNativeReserved(&args.callee(),
+ CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT);
+ const JSNativeHolder* nativeHolder =
+ static_cast<const JSNativeHolder*>(v.toPrivate());
+ return (nativeHolder->mNative)(cx, argc, vp);
+}
+
+static JSObject*
+CreateConstructor(JSContext* cx, JS::Handle<JSObject*> global, const char* name,
+ const JSNativeHolder* nativeHolder, unsigned ctorNargs)
+{
+ JSFunction* fun = js::NewFunctionWithReserved(cx, Constructor, ctorNargs,
+ JSFUN_CONSTRUCTOR, name);
+ if (!fun) {
+ return nullptr;
+ }
+
+ JSObject* constructor = JS_GetFunctionObject(fun);
+ js::SetFunctionNativeReserved(constructor,
+ CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT,
+ js::PrivateValue(const_cast<JSNativeHolder*>(nativeHolder)));
+ return constructor;
+}
+
+static bool
+DefineConstructor(JSContext* cx, JS::Handle<JSObject*> global, const char* name,
+ JS::Handle<JSObject*> constructor)
+{
+ bool alreadyDefined;
+ if (!JS_AlreadyHasOwnProperty(cx, global, name, &alreadyDefined)) {
+ return false;
+ }
+
+ // This is Enumerable: False per spec.
+ return alreadyDefined ||
+ JS_DefineProperty(cx, global, name, constructor, JSPROP_RESOLVING);
+}
+
+static JSObject*
+CreateInterfaceObject(JSContext* cx, JS::Handle<JSObject*> global,
+ JS::Handle<JSObject*> constructorProto,
+ const js::Class* constructorClass,
+ unsigned ctorNargs, const NamedConstructor* namedConstructors,
+ JS::Handle<JSObject*> proto,
+ const NativeProperties* properties,
+ const NativeProperties* chromeOnlyProperties,
+ const char* name, bool defineOnGlobal)
+{
+ JS::Rooted<JSObject*> constructor(cx);
+ MOZ_ASSERT(constructorProto);
+ MOZ_ASSERT(constructorClass);
+ constructor = JS_NewObjectWithGivenProto(cx, Jsvalify(constructorClass),
+ constructorProto);
+ if (!constructor) {
+ return nullptr;
+ }
+
+ if (!JS_DefineProperty(cx, constructor, "length", ctorNargs,
+ JSPROP_READONLY)) {
+ return nullptr;
+ }
+
+ // Might as well intern, since we're going to need an atomized
+ // version of name anyway when we stick our constructor on the
+ // global.
+ JS::Rooted<JSString*> nameStr(cx, JS_AtomizeAndPinString(cx, name));
+ if (!nameStr) {
+ return nullptr;
+ }
+
+ if (!JS_DefineProperty(cx, constructor, "name", nameStr, JSPROP_READONLY)) {
+ return nullptr;
+ }
+
+ if (DOMIfaceAndProtoJSClass::FromJSClass(constructorClass)->wantsInterfaceHasInstance) {
+ JS::Rooted<jsid> hasInstanceId(cx,
+ SYMBOL_TO_JSID(JS::GetWellKnownSymbol(cx, JS::SymbolCode::hasInstance)));
+ if (!JS_DefineFunctionById(cx, constructor, hasInstanceId,
+ InterfaceHasInstance, 1,
+ // Flags match those of Function[Symbol.hasInstance]
+ JSPROP_READONLY | JSPROP_PERMANENT)) {
+ return nullptr;
+ }
+ }
+
+ if (properties) {
+ if (properties->HasStaticMethods() &&
+ !DefinePrefable(cx, constructor, properties->StaticMethods())) {
+ return nullptr;
+ }
+
+ if (properties->HasStaticAttributes() &&
+ !DefinePrefable(cx, constructor, properties->StaticAttributes())) {
+ return nullptr;
+ }
+
+ if (properties->HasConstants() &&
+ !DefinePrefable(cx, constructor, properties->Constants())) {
+ return nullptr;
+ }
+ }
+
+ if (chromeOnlyProperties) {
+ if (chromeOnlyProperties->HasStaticMethods() &&
+ !DefinePrefable(cx, constructor,
+ chromeOnlyProperties->StaticMethods())) {
+ return nullptr;
+ }
+
+ if (chromeOnlyProperties->HasStaticAttributes() &&
+ !DefinePrefable(cx, constructor,
+ chromeOnlyProperties->StaticAttributes())) {
+ return nullptr;
+ }
+
+ if (chromeOnlyProperties->HasConstants() &&
+ !DefinePrefable(cx, constructor, chromeOnlyProperties->Constants())) {
+ return nullptr;
+ }
+ }
+
+ if (proto && !JS_LinkConstructorAndPrototype(cx, constructor, proto)) {
+ return nullptr;
+ }
+
+ if (defineOnGlobal && !DefineConstructor(cx, global, name, constructor)) {
+ return nullptr;
+ }
+
+ if (namedConstructors) {
+ int namedConstructorSlot = DOM_INTERFACE_SLOTS_BASE;
+ while (namedConstructors->mName) {
+ JS::Rooted<JSObject*> namedConstructor(cx,
+ CreateConstructor(cx, global, namedConstructors->mName,
+ &namedConstructors->mHolder,
+ namedConstructors->mNargs));
+ if (!namedConstructor ||
+ !JS_DefineProperty(cx, namedConstructor, "prototype",
+ proto,
+ JSPROP_PERMANENT | JSPROP_READONLY,
+ JS_STUBGETTER, JS_STUBSETTER) ||
+ (defineOnGlobal &&
+ !DefineConstructor(cx, global, namedConstructors->mName,
+ namedConstructor))) {
+ return nullptr;
+ }
+ js::SetReservedSlot(constructor, namedConstructorSlot++,
+ JS::ObjectValue(*namedConstructor));
+ ++namedConstructors;
+ }
+ }
+
+ return constructor;
+}
+
+static JSObject*
+CreateInterfacePrototypeObject(JSContext* cx, JS::Handle<JSObject*> global,
+ JS::Handle<JSObject*> parentProto,
+ const js::Class* protoClass,
+ const NativeProperties* properties,
+ const NativeProperties* chromeOnlyProperties,
+ const char* const* unscopableNames,
+ bool isGlobal)
+{
+ JS::Rooted<JSObject*> ourProto(cx,
+ JS_NewObjectWithUniqueType(cx, Jsvalify(protoClass), parentProto));
+ if (!ourProto ||
+ // We don't try to define properties on the global's prototype; those
+ // properties go on the global itself.
+ (!isGlobal &&
+ !DefineProperties(cx, ourProto, properties, chromeOnlyProperties))) {
+ return nullptr;
+ }
+
+ if (unscopableNames) {
+ JS::Rooted<JSObject*> unscopableObj(cx, JS_NewPlainObject(cx));
+ if (!unscopableObj) {
+ return nullptr;
+ }
+
+ for (; *unscopableNames; ++unscopableNames) {
+ if (!JS_DefineProperty(cx, unscopableObj, *unscopableNames,
+ JS::TrueHandleValue, JSPROP_ENUMERATE)) {
+ return nullptr;
+ }
+ }
+
+ JS::Rooted<jsid> unscopableId(cx,
+ SYMBOL_TO_JSID(JS::GetWellKnownSymbol(cx, JS::SymbolCode::unscopables)));
+ // Readonly and non-enumerable to match Array.prototype.
+ if (!JS_DefinePropertyById(cx, ourProto, unscopableId, unscopableObj,
+ JSPROP_READONLY)) {
+ return nullptr;
+ }
+ }
+
+ return ourProto;
+}
+
+bool
+DefineProperties(JSContext* cx, JS::Handle<JSObject*> obj,
+ const NativeProperties* properties,
+ const NativeProperties* chromeOnlyProperties)
+{
+ if (properties) {
+ if (properties->HasMethods() &&
+ !DefinePrefable(cx, obj, properties->Methods())) {
+ return false;
+ }
+
+ if (properties->HasAttributes() &&
+ !DefinePrefable(cx, obj, properties->Attributes())) {
+ return false;
+ }
+
+ if (properties->HasConstants() &&
+ !DefinePrefable(cx, obj, properties->Constants())) {
+ return false;
+ }
+ }
+
+ if (chromeOnlyProperties) {
+ if (chromeOnlyProperties->HasMethods() &&
+ !DefinePrefable(cx, obj, chromeOnlyProperties->Methods())) {
+ return false;
+ }
+
+ if (chromeOnlyProperties->HasAttributes() &&
+ !DefinePrefable(cx, obj, chromeOnlyProperties->Attributes())) {
+ return false;
+ }
+
+ if (chromeOnlyProperties->HasConstants() &&
+ !DefinePrefable(cx, obj, chromeOnlyProperties->Constants())) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void
+CreateInterfaceObjects(JSContext* cx, JS::Handle<JSObject*> global,
+ JS::Handle<JSObject*> protoProto,
+ const js::Class* protoClass, JS::Heap<JSObject*>* protoCache,
+ JS::Handle<JSObject*> constructorProto,
+ const js::Class* constructorClass,
+ unsigned ctorNargs, const NamedConstructor* namedConstructors,
+ JS::Heap<JSObject*>* constructorCache,
+ const NativeProperties* properties,
+ const NativeProperties* chromeOnlyProperties,
+ const char* name, bool defineOnGlobal,
+ const char* const* unscopableNames,
+ bool isGlobal)
+{
+ MOZ_ASSERT(protoClass || constructorClass,
+ "Need at least one class!");
+ MOZ_ASSERT(!((properties &&
+ (properties->HasMethods() || properties->HasAttributes())) ||
+ (chromeOnlyProperties &&
+ (chromeOnlyProperties->HasMethods() ||
+ chromeOnlyProperties->HasAttributes()))) || protoClass,
+ "Methods or properties but no protoClass!");
+ MOZ_ASSERT(!((properties &&
+ (properties->HasStaticMethods() ||
+ properties->HasStaticAttributes())) ||
+ (chromeOnlyProperties &&
+ (chromeOnlyProperties->HasStaticMethods() ||
+ chromeOnlyProperties->HasStaticAttributes()))) ||
+ constructorClass,
+ "Static methods but no constructorClass!");
+ MOZ_ASSERT(bool(name) == bool(constructorClass),
+ "Must have name precisely when we have an interface object");
+ MOZ_ASSERT(!protoClass == !protoCache,
+ "If, and only if, there is an interface prototype object we need "
+ "to cache it");
+ MOZ_ASSERT(bool(constructorClass) == bool(constructorCache),
+ "If, and only if, there is an interface object we need to cache "
+ "it");
+ MOZ_ASSERT(constructorProto || !constructorClass,
+ "Must have a constructor proto if we plan to create a constructor "
+ "object");
+
+ JS::Rooted<JSObject*> proto(cx);
+ if (protoClass) {
+ proto =
+ CreateInterfacePrototypeObject(cx, global, protoProto, protoClass,
+ properties, chromeOnlyProperties,
+ unscopableNames, isGlobal);
+ if (!proto) {
+ return;
+ }
+
+ *protoCache = proto;
+ }
+ else {
+ MOZ_ASSERT(!proto);
+ }
+
+ JSObject* interface;
+ if (constructorClass) {
+ interface = CreateInterfaceObject(cx, global, constructorProto,
+ constructorClass, ctorNargs,
+ namedConstructors, proto, properties,
+ chromeOnlyProperties, name,
+ defineOnGlobal);
+ if (!interface) {
+ if (protoCache) {
+ // If we fail we need to make sure to clear the value of protoCache we
+ // set above.
+ *protoCache = nullptr;
+ }
+ return;
+ }
+ *constructorCache = interface;
+ }
+}
+
+bool
+NativeInterface2JSObjectAndThrowIfFailed(JSContext* aCx,
+ JS::Handle<JSObject*> aScope,
+ JS::MutableHandle<JS::Value> aRetval,
+ xpcObjectHelper& aHelper,
+ const nsIID* aIID,
+ bool aAllowNativeWrapper)
+{
+ js::AssertSameCompartment(aCx, aScope);
+ nsresult rv;
+ // Inline some logic from XPCConvert::NativeInterfaceToJSObject that we need
+ // on all threads.
+ nsWrapperCache *cache = aHelper.GetWrapperCache();
+
+ if (cache && cache->IsDOMBinding()) {
+ JS::Rooted<JSObject*> obj(aCx, cache->GetWrapper());
+ if (!obj) {
+ obj = cache->WrapObject(aCx, nullptr);
+ }
+
+ if (obj && aAllowNativeWrapper && !JS_WrapObject(aCx, &obj)) {
+ return false;
+ }
+
+ if (obj) {
+ aRetval.setObject(*obj);
+ return true;
+ }
+ }
+
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!XPCConvert::NativeInterface2JSObject(aRetval, nullptr, aHelper, aIID,
+ 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(aCx, NS_FAILED(rv) ? rv : NS_ERROR_UNEXPECTED);
+ }
+ return false;
+ }
+ return true;
+}
+
+bool
+TryPreserveWrapper(JSObject* obj)
+{
+ MOZ_ASSERT(IsDOMObject(obj));
+
+ if (nsISupports* native = UnwrapDOMObjectToISupports(obj)) {
+ nsWrapperCache* cache = nullptr;
+ CallQueryInterface(native, &cache);
+ if (cache) {
+ cache->PreserveWrapper(native);
+ }
+ return true;
+ }
+
+ // If this DOMClass is not cycle collected, then it isn't wrappercached,
+ // so it does not need to be preserved. If it is cycle collected, then
+ // we can't tell if it is wrappercached or not, so we just return false.
+ const DOMJSClass* domClass = GetDOMClass(obj);
+ return domClass && !domClass->mParticipant;
+}
+
+// Can only be called with a DOM JSClass.
+bool
+InstanceClassHasProtoAtDepth(const js::Class* clasp,
+ uint32_t protoID, uint32_t depth)
+{
+ const DOMJSClass* domClass = DOMJSClass::FromJSClass(clasp);
+ return static_cast<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, JS::Handle<JSObject*> scope,
+ xpcObjectHelper& helper, const nsIID* iid,
+ bool allowNativeWrapper, JS::MutableHandle<JS::Value> rval)
+{
+ if (!NativeInterface2JSObjectAndThrowIfFailed(cx, scope, rval, helper, iid,
+ allowNativeWrapper)) {
+ return false;
+ }
+
+#ifdef DEBUG
+ JSObject* jsobj = rval.toObjectOrNull();
+ if (jsobj &&
+ js::GetGlobalForObjectCrossCompartment(jsobj) == jsobj) {
+ NS_ASSERTION(js::GetObjectClass(jsobj)->flags & JSCLASS_IS_GLOBAL,
+ "Why did we recreate this wrapper?");
+ }
+#endif
+
+ return true;
+}
+
+bool
+VariantToJsval(JSContext* aCx, nsIVariant* aVariant,
+ JS::MutableHandle<JS::Value> aRetval)
+{
+ nsresult rv;
+ if (!XPCVariant::VariantDataToJS(aVariant, &rv, aRetval)) {
+ // Does it throw? Who knows
+ if (!JS_IsExceptionPending(aCx)) {
+ Throw(aCx, NS_FAILED(rv) ? rv : NS_ERROR_UNEXPECTED);
+ }
+ return false;
+ }
+
+ return true;
+}
+
+bool
+QueryInterface(JSContext* cx, unsigned argc, JS::Value* vp)
+{
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ JS::Rooted<JS::Value> thisv(cx, JS_THIS(cx, vp));
+ if (thisv.isNull())
+ return false;
+
+ // Get the object. It might be a security wrapper, in which case we do a checked
+ // unwrap.
+ JS::Rooted<JSObject*> origObj(cx, &thisv.toObject());
+ JS::Rooted<JSObject*> obj(cx, js::CheckedUnwrap(origObj,
+ /* stopAtWindowProxy = */ false));
+ if (!obj) {
+ JS_ReportErrorASCII(cx, "Permission denied to access object");
+ return false;
+ }
+
+ // Switch this to UnwrapDOMObjectToISupports once our global objects are
+ // using new bindings.
+ nsCOMPtr<nsISupports> native;
+ UnwrapArg<nsISupports>(obj, getter_AddRefs(native));
+ if (!native) {
+ return Throw(cx, NS_ERROR_FAILURE);
+ }
+
+ if (argc < 1) {
+ return Throw(cx, NS_ERROR_XPC_NOT_ENOUGH_ARGS);
+ }
+
+ if (!args[0].isObject()) {
+ return Throw(cx, NS_ERROR_XPC_BAD_CONVERT_JS);
+ }
+
+ nsCOMPtr<nsIJSID> iid;
+ obj = &args[0].toObject();
+ if (NS_FAILED(UnwrapArg<nsIJSID>(obj, getter_AddRefs(iid)))) {
+ return Throw(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(cx, rv);
+ }
+
+ return WrapObject(cx, ci, &NS_GET_IID(nsIClassInfo), args.rval());
+ }
+
+ nsCOMPtr<nsISupports> unused;
+ nsresult rv = native->QueryInterface(*iid->GetID(), getter_AddRefs(unused));
+ if (NS_FAILED(rv)) {
+ return Throw(cx, rv);
+ }
+
+ *vp = thisv;
+ return true;
+}
+
+void
+GetInterfaceImpl(JSContext* aCx, nsIInterfaceRequestor* aRequestor,
+ nsWrapperCache* aCache, nsIJSID* aIID,
+ JS::MutableHandle<JS::Value> aRetval, ErrorResult& aError)
+{
+ const nsID* iid = aIID->GetID();
+
+ RefPtr<nsISupports> result;
+ aError = aRequestor->GetInterface(*iid, getter_AddRefs(result));
+ if (aError.Failed()) {
+ return;
+ }
+
+ if (!WrapObject(aCx, result, iid, aRetval)) {
+ aError.Throw(NS_ERROR_FAILURE);
+ }
+}
+
+bool
+UnforgeableValueOf(JSContext* cx, unsigned argc, JS::Value* vp)
+{
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ args.rval().set(args.thisv());
+ return true;
+}
+
+bool
+ThrowingConstructor(JSContext* cx, unsigned argc, JS::Value* vp)
+{
+ return ThrowErrorMessage(cx, MSG_ILLEGAL_CONSTRUCTOR);
+}
+
+bool
+ThrowConstructorWithoutNew(JSContext* cx, const char* name)
+{
+ return ThrowErrorMessage(cx, MSG_CONSTRUCTOR_WITHOUT_NEW, name);
+}
+
+inline const NativePropertyHooks*
+GetNativePropertyHooksFromConstructorFunction(JS::Handle<JSObject*> obj)
+{
+ MOZ_ASSERT(JS_IsNativeFunction(obj, Constructor));
+ const JS::Value& v =
+ js::GetFunctionNativeReserved(obj,
+ CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT);
+ const JSNativeHolder* nativeHolder =
+ static_cast<const JSNativeHolder*>(v.toPrivate());
+ return nativeHolder->mPropertyHooks;
+}
+
+inline const NativePropertyHooks*
+GetNativePropertyHooks(JSContext *cx, JS::Handle<JSObject*> obj,
+ DOMObjectType& type)
+{
+ const js::Class* clasp = js::GetObjectClass(obj);
+
+ const DOMJSClass* domClass = GetDOMClass(clasp);
+ if (domClass) {
+ bool isGlobal = (clasp->flags & JSCLASS_DOM_GLOBAL) != 0;
+ type = isGlobal ? eGlobalInstance : eInstance;
+ return domClass->mNativeHooks;
+ }
+
+ if (JS_ObjectIsFunction(cx, obj)) {
+ type = eInterface;
+ return GetNativePropertyHooksFromConstructorFunction(obj);
+ }
+
+ MOZ_ASSERT(IsDOMIfaceAndProtoClass(js::GetObjectClass(obj)));
+ const DOMIfaceAndProtoJSClass* ifaceAndProtoJSClass =
+ DOMIfaceAndProtoJSClass::FromJSClass(js::GetObjectClass(obj));
+ type = ifaceAndProtoJSClass->mType;
+ return ifaceAndProtoJSClass->mNativeHooks;
+}
+
+static JSObject*
+XrayCreateFunction(JSContext* cx, JS::Handle<JSObject*> wrapper,
+ JSNativeWrapper native, unsigned nargs, JS::Handle<jsid> id)
+{
+ JSFunction* fun;
+ if (JSID_IS_STRING(id)) {
+ fun = js::NewFunctionByIdWithReserved(cx, native.op, nargs, 0, id);
+ } else {
+ // Can't pass this id (probably a symbol) to NewFunctionByIdWithReserved;
+ // just use an empty name for lack of anything better.
+ fun = js::NewFunctionWithReserved(cx, native.op, nargs, 0, nullptr);
+ }
+
+ if (!fun) {
+ return nullptr;
+ }
+
+ SET_JITINFO(fun, native.info);
+ JSObject* obj = JS_GetFunctionObject(fun);
+ js::SetFunctionNativeReserved(obj, XRAY_DOM_FUNCTION_PARENT_WRAPPER_SLOT,
+ JS::ObjectValue(*wrapper));
+#ifdef DEBUG
+ js::SetFunctionNativeReserved(obj, XRAY_DOM_FUNCTION_NATIVE_SLOT_FOR_SELF,
+ JS::ObjectValue(*obj));
+#endif
+ return obj;
+}
+
+static bool
+XrayResolveAttribute(JSContext* cx, JS::Handle<JSObject*> wrapper,
+ JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
+ const Prefable<const JSPropertySpec>* attributes,
+ const jsid* attributeIds,
+ const JSPropertySpec* attributeSpecs,
+ JS::MutableHandle<JS::PropertyDescriptor> desc,
+ bool& cacheOnHolder)
+{
+ for (; attributes->specs; ++attributes) {
+ if (attributes->isEnabled(cx, obj)) {
+ // Set i to be the index into our full list of ids/specs that we're
+ // looking at now.
+ size_t i = attributes->specs - attributeSpecs;
+ for ( ; attributeIds[i] != JSID_VOID; ++i) {
+ if (id == attributeIds[i]) {
+ cacheOnHolder = true;
+
+ const JSPropertySpec& attrSpec = attributeSpecs[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.setAttributes(attrSpec.flags);
+ // They all have getters, so we can just make it.
+ JS::Rooted<JSObject*> funobj(cx,
+ XrayCreateFunction(cx, wrapper, attrSpec.accessors.getter.native, 0, id));
+ if (!funobj)
+ return false;
+ desc.setGetterObject(funobj);
+ desc.attributesRef() |= JSPROP_GETTER;
+ if (attrSpec.accessors.setter.native.op) {
+ // We have a setter! Make it.
+ funobj =
+ XrayCreateFunction(cx, wrapper, attrSpec.accessors.setter.native, 1, id);
+ if (!funobj)
+ return false;
+ desc.setSetterObject(funobj);
+ desc.attributesRef() |= JSPROP_SETTER;
+ } else {
+ desc.setSetter(nullptr);
+ }
+ desc.object().set(wrapper);
+ return true;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+static bool
+XrayResolveMethod(JSContext* cx, JS::Handle<JSObject*> wrapper,
+ JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
+ const Prefable<const JSFunctionSpec>* methods,
+ const jsid* methodIds,
+ const JSFunctionSpec* methodSpecs,
+ JS::MutableHandle<JS::PropertyDescriptor> desc,
+ bool& cacheOnHolder)
+{
+ const Prefable<const JSFunctionSpec>* method;
+ for (method = methods; method->specs; ++method) {
+ if (method->isEnabled(cx, obj)) {
+ // Set i to be the index into our full list of ids/specs that we're
+ // looking at now.
+ size_t i = method->specs - methodSpecs;
+ for ( ; methodIds[i] != JSID_VOID; ++i) {
+ if (id == methodIds[i]) {
+ cacheOnHolder = true;
+
+ const JSFunctionSpec& methodSpec = methodSpecs[i];
+ JSObject *funobj;
+ if (methodSpec.selfHostedName) {
+ JSFunction* fun =
+ JS::GetSelfHostedFunction(cx, methodSpec.selfHostedName, id,
+ methodSpec.nargs);
+ if (!fun) {
+ return false;
+ }
+ MOZ_ASSERT(!methodSpec.call.op, "Bad FunctionSpec declaration: non-null native");
+ MOZ_ASSERT(!methodSpec.call.info, "Bad FunctionSpec declaration: non-null jitinfo");
+ funobj = JS_GetFunctionObject(fun);
+ } else {
+ funobj = XrayCreateFunction(cx, wrapper, methodSpec.call,
+ methodSpec.nargs, id);
+ if (!funobj) {
+ return false;
+ }
+ }
+ desc.value().setObject(*funobj);
+ desc.setAttributes(methodSpec.flags);
+ desc.object().set(wrapper);
+ desc.setSetter(nullptr);
+ desc.setGetter(nullptr);
+ return true;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+// Try to resolve a property as an unforgeable property from the given
+// NativeProperties, if it's there. nativeProperties is allowed to be null (in
+// which case we of course won't resolve anything).
+static bool
+XrayResolveUnforgeableProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
+ JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
+ JS::MutableHandle<JS::PropertyDescriptor> desc,
+ bool& cacheOnHolder,
+ const NativeProperties* nativeProperties)
+{
+ if (!nativeProperties) {
+ return true;
+ }
+
+ if (nativeProperties->HasUnforgeableAttributes()) {
+ if (!XrayResolveAttribute(cx, wrapper, obj, id,
+ nativeProperties->UnforgeableAttributes(),
+ nativeProperties->UnforgeableAttributeIds(),
+ nativeProperties->UnforgeableAttributeSpecs(),
+ desc, cacheOnHolder)) {
+ return false;
+ }
+
+ if (desc.object()) {
+ return true;
+ }
+ }
+
+ if (nativeProperties->HasUnforgeableMethods()) {
+ if (!XrayResolveMethod(cx, wrapper, obj, id,
+ nativeProperties->UnforgeableMethods(),
+ nativeProperties->UnforgeableMethodIds(),
+ nativeProperties->UnforgeableMethodSpecs(),
+ desc, cacheOnHolder)) {
+ return false;
+ }
+
+ if (desc.object()) {
+ return true;
+ }
+ }
+
+ return true;
+}
+
+static bool
+XrayResolveProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
+ JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
+ JS::MutableHandle<JS::PropertyDescriptor> desc,
+ bool& cacheOnHolder, DOMObjectType type,
+ const NativeProperties* nativeProperties)
+{
+ bool hasMethods = false;
+ if (type == eInterface) {
+ hasMethods = nativeProperties->HasStaticMethods();
+ } else {
+ hasMethods = nativeProperties->HasMethods();
+ }
+ if (hasMethods) {
+ const Prefable<const JSFunctionSpec>* methods;
+ const jsid* methodIds;
+ const JSFunctionSpec* methodSpecs;
+ if (type == eInterface) {
+ methods = nativeProperties->StaticMethods();
+ methodIds = nativeProperties->StaticMethodIds();
+ methodSpecs = nativeProperties->StaticMethodSpecs();
+ } else {
+ methods = nativeProperties->Methods();
+ methodIds = nativeProperties->MethodIds();
+ methodSpecs = nativeProperties->MethodSpecs();
+ }
+ JS::Rooted<jsid> methodId(cx);
+ if (nativeProperties->iteratorAliasMethodIndex != -1 &&
+ id == SYMBOL_TO_JSID(
+ JS::GetWellKnownSymbol(cx, JS::SymbolCode::iterator))) {
+ methodId =
+ nativeProperties->MethodIds()[nativeProperties->iteratorAliasMethodIndex];
+ } else {
+ methodId = id;
+ }
+ if (!XrayResolveMethod(cx, wrapper, obj, methodId, methods, methodIds,
+ methodSpecs, desc, cacheOnHolder)) {
+ return false;
+ }
+ if (desc.object()) {
+ return true;
+ }
+ }
+
+ if (type == eInterface) {
+ if (nativeProperties->HasStaticAttributes()) {
+ if (!XrayResolveAttribute(cx, wrapper, obj, id,
+ nativeProperties->StaticAttributes(),
+ nativeProperties->StaticAttributeIds(),
+ nativeProperties->StaticAttributeSpecs(),
+ desc, cacheOnHolder)) {
+ return false;
+ }
+ if (desc.object()) {
+ return true;
+ }
+ }
+ } else {
+ if (nativeProperties->HasAttributes()) {
+ if (!XrayResolveAttribute(cx, wrapper, obj, id,
+ nativeProperties->Attributes(),
+ nativeProperties->AttributeIds(),
+ nativeProperties->AttributeSpecs(),
+ desc, cacheOnHolder)) {
+ return false;
+ }
+ if (desc.object()) {
+ return true;
+ }
+ }
+ }
+
+ if (nativeProperties->HasConstants()) {
+ const Prefable<const ConstantSpec>* constant;
+ for (constant = nativeProperties->Constants(); constant->specs; ++constant) {
+ if (constant->isEnabled(cx, obj)) {
+ // Set i to be the index into our full list of ids/specs that we're
+ // looking at now.
+ size_t i = constant->specs - nativeProperties->ConstantSpecs();
+ for ( ; nativeProperties->ConstantIds()[i] != JSID_VOID; ++i) {
+ if (id == nativeProperties->ConstantIds()[i]) {
+ cacheOnHolder = true;
+
+ desc.setAttributes(JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
+ desc.object().set(wrapper);
+ desc.value().set(nativeProperties->ConstantSpecs()[i].value);
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+static bool
+ResolvePrototypeOrConstructor(JSContext* cx, JS::Handle<JSObject*> wrapper,
+ JS::Handle<JSObject*> obj,
+ size_t protoAndIfaceCacheIndex, unsigned attrs,
+ JS::MutableHandle<JS::PropertyDescriptor> desc,
+ bool& cacheOnHolder)
+{
+ JS::Rooted<JSObject*> global(cx, js::GetGlobalForObjectCrossCompartment(obj));
+ {
+ JSAutoCompartment ac(cx, global);
+ ProtoAndIfaceCache& protoAndIfaceCache = *GetProtoAndIfaceCache(global);
+ JSObject* protoOrIface =
+ protoAndIfaceCache.EntrySlotIfExists(protoAndIfaceCacheIndex);
+ if (!protoOrIface) {
+ return false;
+ }
+
+ cacheOnHolder = true;
+
+ desc.object().set(wrapper);
+ desc.setAttributes(attrs);
+ desc.setGetter(nullptr);
+ desc.setSetter(nullptr);
+ desc.value().set(JS::ObjectValue(*protoOrIface));
+ }
+ return JS_WrapPropertyDescriptor(cx, desc);
+}
+
+#ifdef DEBUG
+
+static void
+DEBUG_CheckXBLCallable(JSContext *cx, JSObject *obj)
+{
+ // In general, we shouldn't have cross-compartment wrappers here, because
+ // we should be running in an XBL scope, and the content prototype should
+ // contain wrappers to functions defined in the XBL scope. But if the node
+ // has been adopted into another compartment, those prototypes will now point
+ // to a different XBL scope (which is ok).
+ MOZ_ASSERT_IF(js::IsCrossCompartmentWrapper(obj),
+ xpc::IsContentXBLScope(js::GetObjectCompartment(js::UncheckedUnwrap(obj))));
+ MOZ_ASSERT(JS::IsCallable(obj));
+}
+
+static void
+DEBUG_CheckXBLLookup(JSContext *cx, JS::PropertyDescriptor *desc)
+{
+ if (!desc->obj)
+ return;
+ if (!desc->value.isUndefined()) {
+ MOZ_ASSERT(desc->value.isObject());
+ DEBUG_CheckXBLCallable(cx, &desc->value.toObject());
+ }
+ if (desc->getter) {
+ MOZ_ASSERT(desc->attrs & JSPROP_GETTER);
+ DEBUG_CheckXBLCallable(cx, JS_FUNC_TO_DATA_PTR(JSObject *, desc->getter));
+ }
+ if (desc->setter) {
+ MOZ_ASSERT(desc->attrs & JSPROP_SETTER);
+ DEBUG_CheckXBLCallable(cx, JS_FUNC_TO_DATA_PTR(JSObject *, desc->setter));
+ }
+}
+#else
+#define DEBUG_CheckXBLLookup(a, b) {}
+#endif
+
+/* static */ bool
+XrayResolveOwnProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
+ JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
+ JS::MutableHandle<JS::PropertyDescriptor> desc,
+ bool& cacheOnHolder)
+{
+ cacheOnHolder = false;
+
+ DOMObjectType type;
+ const NativePropertyHooks *nativePropertyHooks =
+ GetNativePropertyHooks(cx, obj, type);
+ const NativePropertiesHolder& nativeProperties =
+ nativePropertyHooks->mNativeProperties;
+ ResolveOwnProperty resolveOwnProperty =
+ nativePropertyHooks->mResolveOwnProperty;
+
+ if (type == eNamedPropertiesObject) {
+ // None of these should be cached on the holder, since they're dynamic.
+ return resolveOwnProperty(cx, wrapper, obj, id, desc);
+ }
+
+ // Check for unforgeable properties first.
+ if (IsInstance(type)) {
+ const NativePropertiesHolder& nativeProperties =
+ nativePropertyHooks->mNativeProperties;
+ if (!XrayResolveUnforgeableProperty(cx, wrapper, obj, id, desc, cacheOnHolder,
+ nativeProperties.regular)) {
+ return false;
+ }
+
+ if (!desc.object() && xpc::AccessCheck::isChrome(wrapper) &&
+ !XrayResolveUnforgeableProperty(cx, wrapper, obj, id, desc, cacheOnHolder,
+ nativeProperties.chromeOnly)) {
+ return false;
+ }
+
+ if (desc.object()) {
+ return true;
+ }
+ }
+
+ if (IsInstance(type)) {
+ if (resolveOwnProperty) {
+ if (!resolveOwnProperty(cx, wrapper, obj, id, desc)) {
+ return false;
+ }
+
+ if (desc.object()) {
+ // None of these should be cached on the holder, since they're dynamic.
+ return true;
+ }
+ }
+
+ // If we're a special scope for in-content XBL, our script expects to see
+ // the bound XBL methods and attributes when accessing content. However,
+ // these members are implemented in content via custom-spliced prototypes,
+ // and thus aren't visible through Xray wrappers unless we handle them
+ // explicitly. So we check if we're running in such a scope, and if so,
+ // whether the wrappee is a bound element. If it is, we do a lookup via
+ // specialized XBL machinery.
+ //
+ // While we have to do some sketchy walking through content land, we should
+ // be protected by read-only/non-configurable properties, and any functions
+ // we end up with should _always_ be living in our own scope (the XBL scope).
+ // Make sure to assert that.
+ JS::Rooted<JSObject*> maybeElement(cx, obj);
+ Element* element;
+ if (xpc::ObjectScope(wrapper)->IsContentXBLScope() &&
+ NS_SUCCEEDED(UNWRAP_OBJECT(Element, &maybeElement, element))) {
+ if (!nsContentUtils::LookupBindingMember(cx, element, id, desc)) {
+ return false;
+ }
+
+ DEBUG_CheckXBLLookup(cx, desc.address());
+
+ if (desc.object()) {
+ // XBL properties shouldn't be cached on the holder, as they might be
+ // shadowed by own properties returned from mResolveOwnProperty.
+ desc.object().set(wrapper);
+
+ return true;
+ }
+ }
+
+ // For non-global instance Xrays there are no other properties, so return
+ // here for them.
+ if (type != eGlobalInstance) {
+ return true;
+ }
+ } else if (type == eInterface) {
+ if (IdEquals(id, "prototype")) {
+ return nativePropertyHooks->mPrototypeID == prototypes::id::_ID_Count ||
+ ResolvePrototypeOrConstructor(cx, wrapper, obj,
+ nativePropertyHooks->mPrototypeID,
+ JSPROP_PERMANENT | JSPROP_READONLY,
+ desc, cacheOnHolder);
+ }
+
+ if (id == SYMBOL_TO_JSID(JS::GetWellKnownSymbol(cx, JS::SymbolCode::hasInstance)) &&
+ DOMIfaceAndProtoJSClass::FromJSClass(js::GetObjectClass(obj))->
+ wantsInterfaceHasInstance) {
+ cacheOnHolder = true;
+ JSNativeWrapper interfaceHasInstanceWrapper = { InterfaceHasInstance,
+ nullptr };
+ JSObject* funObj = XrayCreateFunction(cx, wrapper,
+ interfaceHasInstanceWrapper, 1, id);
+ if (!funObj) {
+ return false;
+ }
+
+ desc.value().setObject(*funObj);
+ desc.setAttributes(JSPROP_READONLY | JSPROP_PERMANENT);
+ desc.object().set(wrapper);
+ desc.setSetter(nullptr);
+ desc.setGetter(nullptr);
+ return true;
+ }
+ } else {
+ MOZ_ASSERT(IsInterfacePrototype(type));
+
+ if (IdEquals(id, "constructor")) {
+ return nativePropertyHooks->mConstructorID == constructors::id::_ID_Count ||
+ ResolvePrototypeOrConstructor(cx, wrapper, obj,
+ nativePropertyHooks->mConstructorID,
+ 0, desc, cacheOnHolder);
+ }
+
+ // The properties for globals live on the instance, so return here as there
+ // are no properties on their interface prototype object.
+ if (type == eGlobalInterfacePrototype) {
+ return true;
+ }
+ }
+
+ if (nativeProperties.regular &&
+ !XrayResolveProperty(cx, wrapper, obj, id, desc, cacheOnHolder, type,
+ nativeProperties.regular)) {
+ return false;
+ }
+
+ if (!desc.object() &&
+ nativeProperties.chromeOnly &&
+ xpc::AccessCheck::isChrome(js::GetObjectCompartment(wrapper)) &&
+ !XrayResolveProperty(cx, wrapper, obj, id, desc, cacheOnHolder, type,
+ nativeProperties.chromeOnly)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool
+XrayDefineProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
+ JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
+ JS::Handle<JS::PropertyDescriptor> desc,
+ JS::ObjectOpResult &result, bool *defined)
+{
+ if (!js::IsProxy(obj))
+ return true;
+
+ const DOMProxyHandler* handler = GetDOMProxyHandler(obj);
+ return handler->defineProperty(cx, wrapper, id, desc, result, defined);
+}
+
+template<typename SpecType>
+bool
+XrayAttributeOrMethodKeys(JSContext* cx, JS::Handle<JSObject*> wrapper,
+ JS::Handle<JSObject*> obj,
+ const Prefable<const SpecType>* list,
+ const jsid* ids, const SpecType* specList,
+ unsigned flags, JS::AutoIdVector& props)
+{
+ for (; list->specs; ++list) {
+ if (list->isEnabled(cx, obj)) {
+ // Set i to be the index into our full list of ids/specs that we're
+ // looking at now.
+ size_t i = list->specs - specList;
+ for ( ; ids[i] != JSID_VOID; ++i) {
+ // Skip non-enumerable properties and symbol-keyed properties unless
+ // they are specially requested via flags.
+ if (((flags & JSITER_HIDDEN) ||
+ (specList[i].flags & JSPROP_ENUMERATE)) &&
+ ((flags & JSITER_SYMBOLS) || !JSID_IS_SYMBOL(ids[i])) &&
+ !props.append(ids[i])) {
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+#define ADD_KEYS_IF_DEFINED(FieldName) { \
+ if (nativeProperties->Has##FieldName##s() && \
+ !XrayAttributeOrMethodKeys(cx, wrapper, obj, \
+ nativeProperties->FieldName##s(), \
+ nativeProperties->FieldName##Ids(), \
+ nativeProperties->FieldName##Specs(), \
+ flags, props)) { \
+ return false; \
+ } \
+}
+
+
+bool
+XrayOwnPropertyKeys(JSContext* cx, JS::Handle<JSObject*> wrapper,
+ JS::Handle<JSObject*> obj,
+ unsigned flags, JS::AutoIdVector& props,
+ DOMObjectType type,
+ const NativeProperties* nativeProperties)
+{
+ MOZ_ASSERT(type != eNamedPropertiesObject);
+
+ if (IsInstance(type)) {
+ ADD_KEYS_IF_DEFINED(UnforgeableMethod);
+ ADD_KEYS_IF_DEFINED(UnforgeableAttribute);
+ if (type == eGlobalInstance) {
+ ADD_KEYS_IF_DEFINED(Method);
+ ADD_KEYS_IF_DEFINED(Attribute);
+ }
+ } else if (type == eInterface) {
+ ADD_KEYS_IF_DEFINED(StaticMethod);
+ ADD_KEYS_IF_DEFINED(StaticAttribute);
+ } else if (type != eGlobalInterfacePrototype) {
+ MOZ_ASSERT(IsInterfacePrototype(type));
+ ADD_KEYS_IF_DEFINED(Method);
+ ADD_KEYS_IF_DEFINED(Attribute);
+ }
+
+ if (nativeProperties->HasConstants()) {
+ const Prefable<const ConstantSpec>* constant;
+ for (constant = nativeProperties->Constants(); constant->specs; ++constant) {
+ if (constant->isEnabled(cx, obj)) {
+ // Set i to be the index into our full list of ids/specs that we're
+ // looking at now.
+ size_t i = constant->specs - nativeProperties->ConstantSpecs();
+ for ( ; nativeProperties->ConstantIds()[i] != JSID_VOID; ++i) {
+ if (!props.append(nativeProperties->ConstantIds()[i])) {
+ return false;
+ }
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+#undef ADD_KEYS_IF_DEFINED
+
+bool
+XrayOwnNativePropertyKeys(JSContext* cx, JS::Handle<JSObject*> wrapper,
+ const NativePropertyHooks* nativePropertyHooks,
+ DOMObjectType type, JS::Handle<JSObject*> obj,
+ unsigned flags, JS::AutoIdVector& props)
+{
+ MOZ_ASSERT(type != eNamedPropertiesObject);
+
+ if (type == eInterface &&
+ nativePropertyHooks->mPrototypeID != prototypes::id::_ID_Count &&
+ !AddStringToIDVector(cx, props, "prototype")) {
+ return false;
+ }
+
+ if (IsInterfacePrototype(type) &&
+ nativePropertyHooks->mConstructorID != constructors::id::_ID_Count &&
+ (flags & JSITER_HIDDEN) &&
+ !AddStringToIDVector(cx, props, "constructor")) {
+ return false;
+ }
+
+ const NativePropertiesHolder& nativeProperties =
+ nativePropertyHooks->mNativeProperties;
+
+ if (nativeProperties.regular &&
+ !XrayOwnPropertyKeys(cx, wrapper, obj, flags, props, type,
+ nativeProperties.regular)) {
+ return false;
+ }
+
+ if (nativeProperties.chromeOnly &&
+ xpc::AccessCheck::isChrome(js::GetObjectCompartment(wrapper)) &&
+ !XrayOwnPropertyKeys(cx, wrapper, obj, flags, props, type,
+ nativeProperties.chromeOnly)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool
+XrayOwnPropertyKeys(JSContext* cx, JS::Handle<JSObject*> wrapper,
+ JS::Handle<JSObject*> obj,
+ unsigned flags, JS::AutoIdVector& props)
+{
+ DOMObjectType type;
+ const NativePropertyHooks* nativePropertyHooks =
+ GetNativePropertyHooks(cx, obj, type);
+ EnumerateOwnProperties enumerateOwnProperties =
+ nativePropertyHooks->mEnumerateOwnProperties;
+
+ if (type == eNamedPropertiesObject) {
+ return enumerateOwnProperties(cx, wrapper, obj, props);
+ }
+
+ if (IsInstance(type)) {
+ // FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=1071189
+ // Should do something about XBL properties too.
+ if (enumerateOwnProperties &&
+ !enumerateOwnProperties(cx, wrapper, obj, props)) {
+ return false;
+ }
+ }
+
+ return type == eGlobalInterfacePrototype ||
+ XrayOwnNativePropertyKeys(cx, wrapper, nativePropertyHooks, type,
+ obj, flags, props);
+}
+
+const JSClass*
+XrayGetExpandoClass(JSContext* cx, JS::Handle<JSObject*> obj)
+{
+ DOMObjectType type;
+ const NativePropertyHooks* nativePropertyHooks =
+ GetNativePropertyHooks(cx, obj, type);
+ if (!IsInstance(type)) {
+ // Non-instances don't need any special expando classes.
+ return &DefaultXrayExpandoObjectClass;
+ }
+
+ return nativePropertyHooks->mXrayExpandoClass;
+}
+
+bool
+XrayDeleteNamedProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
+ JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
+ JS::ObjectOpResult& opresult)
+{
+ DOMObjectType type;
+ const NativePropertyHooks* nativePropertyHooks =
+ GetNativePropertyHooks(cx, obj, type);
+ if (!IsInstance(type) || !nativePropertyHooks->mDeleteNamedProperty) {
+ return opresult.succeed();
+ }
+ return nativePropertyHooks->mDeleteNamedProperty(cx, wrapper, obj, id,
+ opresult);
+}
+
+JSObject*
+GetCachedSlotStorageObjectSlow(JSContext* cx, JS::Handle<JSObject*> obj,
+ bool* isXray)
+{
+ if (!xpc::WrapperFactory::IsXrayWrapper(obj)) {
+ JSObject* retval = js::UncheckedUnwrap(obj, /* stopAtWindowProxy = */ false);
+ MOZ_ASSERT(IsDOMObject(retval));
+ *isXray = false;
+ return retval;
+ }
+
+ *isXray = true;
+ return xpc::EnsureXrayExpandoObject(cx, obj);;
+}
+
+DEFINE_XRAY_EXPANDO_CLASS(, DefaultXrayExpandoObjectClass, 0);
+
+NativePropertyHooks sEmptyNativePropertyHooks = {
+ nullptr,
+ nullptr,
+ nullptr,
+ {
+ nullptr,
+ nullptr
+ },
+ prototypes::id::_ID_Count,
+ constructors::id::_ID_Count,
+ nullptr
+};
+
+const js::ClassOps sBoringInterfaceObjectClassClassOps = {
+ nullptr, /* addProperty */
+ nullptr, /* delProperty */
+ nullptr, /* getProperty */
+ nullptr, /* setProperty */
+ nullptr, /* enumerate */
+ nullptr, /* resolve */
+ nullptr, /* mayResolve */
+ nullptr, /* finalize */
+ ThrowingConstructor, /* call */
+ nullptr, /* hasInstance */
+ ThrowingConstructor, /* construct */
+ nullptr, /* trace */
+};
+
+const js::ObjectOps sInterfaceObjectClassObjectOps = {
+ nullptr, /* lookupProperty */
+ nullptr, /* defineProperty */
+ nullptr, /* hasProperty */
+ nullptr, /* getProperty */
+ nullptr, /* setProperty */
+ nullptr, /* getOwnPropertyDescriptor */
+ nullptr, /* deleteProperty */
+ nullptr, /* watch */
+ nullptr, /* unwatch */
+ nullptr, /* getElements */
+ nullptr, /* enumerate */
+ InterfaceObjectToString, /* funToString */
+};
+
+bool
+GetPropertyOnPrototype(JSContext* cx, JS::Handle<JSObject*> proxy,
+ JS::Handle<JS::Value> receiver, JS::Handle<jsid> id,
+ bool* found, JS::MutableHandle<JS::Value> vp)
+{
+ JS::Rooted<JSObject*> proto(cx);
+ if (!js::GetObjectProto(cx, proxy, &proto)) {
+ return false;
+ }
+ if (!proto) {
+ *found = false;
+ return true;
+ }
+
+ if (!JS_HasPropertyById(cx, proto, id, found)) {
+ return false;
+ }
+
+ if (!*found) {
+ return true;
+ }
+
+ return JS_ForwardGetPropertyTo(cx, proto, id, receiver, vp);
+}
+
+bool
+HasPropertyOnPrototype(JSContext* cx, JS::Handle<JSObject*> proxy,
+ JS::Handle<jsid> id, bool* has)
+{
+ JS::Rooted<JSObject*> proto(cx);
+ if (!js::GetObjectProto(cx, proxy, &proto)) {
+ return false;
+ }
+ if (!proto) {
+ *has = false;
+ return true;
+ }
+
+ return JS_HasPropertyById(cx, proto, id, has);
+}
+
+bool
+AppendNamedPropertyIds(JSContext* cx, JS::Handle<JSObject*> proxy,
+ nsTArray<nsString>& names,
+ bool shadowPrototypeProperties,
+ JS::AutoIdVector& props)
+{
+ for (uint32_t i = 0; i < names.Length(); ++i) {
+ JS::Rooted<JS::Value> v(cx);
+ if (!xpc::NonVoidStringToJsval(cx, names[i], &v)) {
+ return false;
+ }
+
+ JS::Rooted<jsid> id(cx);
+ if (!JS_ValueToId(cx, v, &id)) {
+ return false;
+ }
+
+ bool shouldAppend = shadowPrototypeProperties;
+ if (!shouldAppend) {
+ bool has;
+ if (!HasPropertyOnPrototype(cx, proxy, id, &has)) {
+ return false;
+ }
+ shouldAppend = !has;
+ }
+
+ if (shouldAppend) {
+ if (!props.append(id)) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+bool
+DictionaryBase::ParseJSON(JSContext* aCx,
+ const nsAString& aJSON,
+ JS::MutableHandle<JS::Value> aVal)
+{
+ if (aJSON.IsEmpty()) {
+ return true;
+ }
+ return JS_ParseJSON(aCx, PromiseFlatString(aJSON).get(), aJSON.Length(), aVal);
+}
+
+bool
+DictionaryBase::StringifyToJSON(JSContext* aCx,
+ JS::Handle<JSObject*> aObj,
+ nsAString& aJSON) const
+{
+ return JS::ToJSONMaybeSafely(aCx, aObj, AppendJSONToString, &aJSON);
+}
+
+/* static */
+bool
+DictionaryBase::AppendJSONToString(const char16_t* aJSONData,
+ uint32_t aDataLength,
+ void* aString)
+{
+ nsAString* string = static_cast<nsAString*>(aString);
+ string->Append(aJSONData, aDataLength);
+ return true;
+}
+
+nsresult
+ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObjArg)
+{
+ js::AssertSameCompartment(aCx, aObjArg);
+
+ // Check if we're anywhere near the stack limit before we reach the
+ // transplanting code, since it has no good way to handle errors. This uses
+ // the untrusted script limit, which is not strictly necessary since no
+ // actual script should run.
+ JS_CHECK_RECURSION_CONSERVATIVE(aCx, return NS_ERROR_FAILURE);
+
+ JS::Rooted<JSObject*> aObj(aCx, aObjArg);
+ const DOMJSClass* domClass = GetDOMClass(aObj);
+
+ // DOM things are always parented to globals.
+ JS::Rooted<JSObject*> oldParent(aCx,
+ js::GetGlobalForObjectCrossCompartment(aObj));
+ MOZ_ASSERT(js::GetGlobalForObjectCrossCompartment(oldParent) == oldParent);
+
+ JS::Rooted<JSObject*> newParent(aCx,
+ domClass->mGetAssociatedGlobal(aCx, aObj));
+ MOZ_ASSERT(JS_IsGlobalObject(newParent));
+
+ JSAutoCompartment oldAc(aCx, oldParent);
+
+ JSCompartment* oldCompartment = js::GetObjectCompartment(oldParent);
+ JSCompartment* newCompartment = js::GetObjectCompartment(newParent);
+ if (oldCompartment == newCompartment) {
+ MOZ_ASSERT(oldParent == newParent);
+ return NS_OK;
+ }
+
+ nsISupports* native = UnwrapDOMObjectToISupports(aObj);
+ if (!native) {
+ return NS_OK;
+ }
+
+ bool isProxy = js::IsProxy(aObj);
+ JS::Rooted<JSObject*> expandoObject(aCx);
+ if (isProxy) {
+ expandoObject = DOMProxyHandler::GetAndClearExpandoObject(aObj);
+ }
+
+ JSAutoCompartment newAc(aCx, newParent);
+
+ // First we clone the reflector. We get a copy of its properties and clone its
+ // expando chain.
+
+ JS::Handle<JSObject*> proto = (domClass->mGetProto)(aCx);
+ if (!proto) {
+ return NS_ERROR_FAILURE;
+ }
+
+ JS::Rooted<JSObject*> newobj(aCx, JS_CloneObject(aCx, aObj, proto));
+ if (!newobj) {
+ return NS_ERROR_FAILURE;
+ }
+
+ JS::Rooted<JSObject*> propertyHolder(aCx);
+ JS::Rooted<JSObject*> copyFrom(aCx, isProxy ? expandoObject : aObj);
+ if (copyFrom) {
+ propertyHolder = JS_NewObjectWithGivenProto(aCx, nullptr, nullptr);
+ if (!propertyHolder) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (!JS_CopyPropertiesFrom(aCx, propertyHolder, copyFrom)) {
+ return NS_ERROR_FAILURE;
+ }
+ } else {
+ propertyHolder = nullptr;
+ }
+
+ // Expandos from other compartments are attached to the target JS object.
+ // Copy them over, and let the old ones die a natural death.
+
+ // Note that at this point the DOM_OBJECT_SLOT for |newobj| has not been set.
+ // CloneExpandoChain() will use this property of |newobj| when it calls
+ // preserveWrapper() via attachExpandoObject() if |aObj| has expandos set, and
+ // preserveWrapper() will not do anything in this case. This is safe because
+ // if expandos are present then the wrapper will already have been preserved
+ // for this native.
+ if (!xpc::XrayUtils::CloneExpandoChain(aCx, newobj, aObj)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // We've set up |newobj|, so we make it own the native by setting its reserved
+ // slot and nulling out the reserved slot of |obj|.
+ //
+ // NB: It's important to do this _after_ copying the properties to
+ // propertyHolder. Otherwise, an object with |foo.x === foo| will
+ // crash when JS_CopyPropertiesFrom tries to call wrap() on foo.x.
+ js::SetReservedOrProxyPrivateSlot(newobj, DOM_OBJECT_SLOT,
+ js::GetReservedOrProxyPrivateSlot(aObj, DOM_OBJECT_SLOT));
+ js::SetReservedOrProxyPrivateSlot(aObj, DOM_OBJECT_SLOT, JS::PrivateValue(nullptr));
+
+ aObj = xpc::TransplantObject(aCx, aObj, newobj);
+ if (!aObj) {
+ MOZ_CRASH();
+ }
+
+ nsWrapperCache* cache = nullptr;
+ CallQueryInterface(native, &cache);
+ bool preserving = cache->PreservingWrapper();
+ cache->SetPreservingWrapper(false);
+ cache->SetWrapper(aObj);
+ cache->SetPreservingWrapper(preserving);
+
+ if (propertyHolder) {
+ JS::Rooted<JSObject*> copyTo(aCx);
+ if (isProxy) {
+ copyTo = DOMProxyHandler::EnsureExpandoObject(aCx, aObj);
+ } else {
+ copyTo = aObj;
+ }
+
+ if (!copyTo || !JS_CopyPropertiesFrom(aCx, copyTo, propertyHolder)) {
+ MOZ_CRASH();
+ }
+ }
+
+ JS::Rooted<JSObject*> maybeObjLC(aCx, aObj);
+ nsObjectLoadingContent* htmlobject;
+ nsresult rv = UNWRAP_OBJECT(HTMLObjectElement, &maybeObjLC, htmlobject);
+ if (NS_FAILED(rv)) {
+ rv = UnwrapObject<prototypes::id::HTMLEmbedElement,
+ HTMLSharedObjectElement>(&maybeObjLC, htmlobject);
+ if (NS_FAILED(rv)) {
+ rv = UnwrapObject<prototypes::id::HTMLAppletElement,
+ HTMLSharedObjectElement>(&maybeObjLC, htmlobject);
+ if (NS_FAILED(rv)) {
+ htmlobject = nullptr;
+ }
+ }
+ }
+ if (htmlobject) {
+ htmlobject->SetupProtoChain(aCx, aObj);
+ }
+
+ // Now we can just return the wrapper
+ return NS_OK;
+}
+
+GlobalObject::GlobalObject(JSContext* aCx, JSObject* aObject)
+ : mGlobalJSObject(aCx),
+ mCx(aCx),
+ mGlobalObject(nullptr)
+{
+ MOZ_ASSERT(mCx);
+ JS::Rooted<JSObject*> obj(aCx, aObject);
+ if (js::IsWrapper(obj)) {
+ obj = js::CheckedUnwrap(obj, /* stopAtWindowProxy = */ false);
+ if (!obj) {
+ // We should never end up here on a worker thread, since there shouldn't
+ // be any security wrappers to worry about.
+ if (!MOZ_LIKELY(NS_IsMainThread())) {
+ MOZ_CRASH();
+ }
+
+ Throw(aCx, NS_ERROR_XPC_SECURITY_MANAGER_VETO);
+ return;
+ }
+ }
+
+ mGlobalJSObject = js::GetGlobalForObjectCrossCompartment(obj);
+}
+
+nsISupports*
+GlobalObject::GetAsSupports() const
+{
+ if (mGlobalObject) {
+ return mGlobalObject;
+ }
+
+ MOZ_ASSERT(!js::IsWrapper(mGlobalJSObject));
+
+ // Most of our globals are DOM objects. Try that first. Note that this
+ // assumes that either the first nsISupports in the object is the canonical
+ // one or that we don't care about the canonical nsISupports here.
+ mGlobalObject = UnwrapDOMObjectToISupports(mGlobalJSObject);
+ if (mGlobalObject) {
+ return mGlobalObject;
+ }
+
+ MOZ_ASSERT(NS_IsMainThread(), "All our worker globals are DOM objects");
+
+ // Remove everything below here once all our global objects are using new
+ // bindings. If that ever happens; it would need to include Sandbox and
+ // BackstagePass.
+
+ // See whether mGlobalJSObject is an XPCWrappedNative. This will redo the
+ // IsWrapper bit above and the UnwrapDOMObjectToISupports in the case when
+ // we're not actually an XPCWrappedNative, but this should be a rare-ish case
+ // anyway.
+ nsCOMPtr<nsISupports> supp = xpc::UnwrapReflectorToISupports(mGlobalJSObject);
+ if (supp) {
+ // See documentation for mGlobalJSObject for why this assignment is OK.
+ mGlobalObject = supp;
+ return mGlobalObject;
+ }
+
+ // And now a final hack. Sandbox is not a reflector, but it does have an
+ // nsIGlobalObject hanging out in its private slot. Handle that case here,
+ // (though again, this will do the useless UnwrapDOMObjectToISupports if we
+ // got here for something that is somehow not a DOM object, not an
+ // XPCWrappedNative _and_ not a Sandbox).
+ if (XPCConvert::GetISupportsFromJSObject(mGlobalJSObject, &mGlobalObject)) {
+ return mGlobalObject;
+ }
+
+ MOZ_ASSERT(!mGlobalObject);
+
+ Throw(mCx, NS_ERROR_XPC_BAD_CONVERT_JS);
+ return nullptr;
+}
+
+nsIPrincipal*
+GlobalObject::GetSubjectPrincipal() const
+{
+ if (!NS_IsMainThread()) {
+ return nullptr;
+ }
+
+ JSCompartment* compartment = js::GetContextCompartment(mCx);
+ MOZ_ASSERT(compartment);
+ JSPrincipals* principals = JS_GetCompartmentPrincipals(compartment);
+ return nsJSPrincipals::get(principals);
+}
+
+static bool
+CallOrdinaryHasInstance(JSContext* cx, JS::CallArgs& args)
+{
+ JS::Rooted<JSObject*> thisObj(cx, &args.thisv().toObject());
+ bool isInstance;
+ if (!JS::OrdinaryHasInstance(cx, thisObj, args.get(0), &isInstance)) {
+ return false;
+ }
+ args.rval().setBoolean(isInstance);
+ return true;
+}
+
+bool
+InterfaceHasInstance(JSContext* cx, unsigned argc, JS::Value* vp)
+{
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ // If the thing we were passed is not an object, return false like
+ // OrdinaryHasInstance does.
+ if (!args.get(0).isObject()) {
+ args.rval().setBoolean(false);
+ return true;
+ }
+
+ // If "this" is not an object, likewise return false (again, like
+ // OrdinaryHasInstance).
+ if (!args.thisv().isObject()) {
+ args.rval().setBoolean(false);
+ return true;
+ }
+
+ // If "this" doesn't have a DOMIfaceAndProtoJSClass, it's not a DOM
+ // constructor, so just fall back to OrdinaryHasInstance. But note that we
+ // should CheckedUnwrap here, because otherwise we won't get the right
+ // answers.
+ JS::Rooted<JSObject*> thisObj(cx, js::CheckedUnwrap(&args.thisv().toObject()));
+ if (!thisObj) {
+ // Just fall back on the normal thing, in case it still happens to work.
+ return CallOrdinaryHasInstance(cx, args);
+ }
+
+ const js::Class* thisClass = js::GetObjectClass(thisObj);
+
+ if (!IsDOMIfaceAndProtoClass(thisClass)) {
+ return CallOrdinaryHasInstance(cx, args);
+ }
+
+ const DOMIfaceAndProtoJSClass* clasp =
+ DOMIfaceAndProtoJSClass::FromJSClass(thisClass);
+
+ // If "this" isn't a DOM constructor or is a constructor for an interface
+ // without a prototype, just fall back to OrdinaryHasInstance.
+ if (clasp->mType != eInterface ||
+ clasp->mPrototypeID == prototypes::id::_ID_Count) {
+ return CallOrdinaryHasInstance(cx, args);
+ }
+
+ JS::Rooted<JSObject*> instance(cx, &args[0].toObject());
+ const DOMJSClass* domClass =
+ GetDOMClass(js::UncheckedUnwrap(instance, /* stopAtWindowProxy = */ false));
+
+ if (domClass &&
+ domClass->mInterfaceChain[clasp->mDepth] == clasp->mPrototypeID) {
+ args.rval().setBoolean(true);
+ return true;
+ }
+
+ if (jsipc::IsWrappedCPOW(instance)) {
+ bool boolp = false;
+ if (!jsipc::DOMInstanceOf(cx, js::UncheckedUnwrap(instance), clasp->mPrototypeID,
+ clasp->mDepth, &boolp)) {
+ return false;
+ }
+ args.rval().setBoolean(boolp);
+ return true;
+ }
+
+ return CallOrdinaryHasInstance(cx, args);
+}
+
+bool
+InterfaceHasInstance(JSContext* cx, int prototypeID, int depth,
+ JS::Handle<JSObject*> instance,
+ bool* bp)
+{
+ const DOMJSClass* domClass = GetDOMClass(js::UncheckedUnwrap(instance));
+
+ MOZ_ASSERT(!domClass || prototypeID != prototypes::id::_ID_Count,
+ "Why do we have a hasInstance hook if we don't have a prototype "
+ "ID?");
+
+ *bp = (domClass && domClass->mInterfaceChain[depth] == prototypeID);
+ return true;
+}
+
+bool
+ReportLenientThisUnwrappingFailure(JSContext* cx, JSObject* obj)
+{
+ JS::Rooted<JSObject*> rootedObj(cx, obj);
+ GlobalObject global(cx, rootedObj);
+ if (global.Failed()) {
+ return false;
+ }
+ nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(global.GetAsSupports());
+ if (window && window->GetDoc()) {
+ window->GetDoc()->WarnOnceAbout(nsIDocument::eLenientThis);
+ }
+ return true;
+}
+
+bool
+GetContentGlobalForJSImplementedObject(JSContext* cx, JS::Handle<JSObject*> obj,
+ nsIGlobalObject** globalObj)
+{
+ // Be very careful to not get tricked here.
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!xpc::AccessCheck::isChrome(js::GetObjectCompartment(obj))) {
+ NS_RUNTIMEABORT("Should have a chrome object here");
+ }
+
+ // Look up the content-side object.
+ JS::Rooted<JS::Value> domImplVal(cx);
+ if (!JS_GetProperty(cx, obj, "__DOM_IMPL__", &domImplVal)) {
+ return false;
+ }
+
+ if (!domImplVal.isObject()) {
+ ThrowErrorMessage(cx, MSG_NOT_OBJECT, "Value");
+ return false;
+ }
+
+ // Go ahead and get the global from it. GlobalObject will handle
+ // doing unwrapping as needed.
+ GlobalObject global(cx, &domImplVal.toObject());
+ if (global.Failed()) {
+ return false;
+ }
+
+ DebugOnly<nsresult> rv = CallQueryInterface(global.GetAsSupports(), globalObj);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+ MOZ_ASSERT(*globalObj);
+ return true;
+}
+
+already_AddRefed<nsIGlobalObject>
+ConstructJSImplementation(const char* aContractId,
+ const GlobalObject& aGlobal,
+ JS::MutableHandle<JSObject*> aObject,
+ ErrorResult& aRv)
+{
+ // Get the global object to use as a parent and for initialization.
+ nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
+ if (!global) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return nullptr;
+ }
+
+ ConstructJSImplementation(aContractId, global, aObject, aRv);
+
+ if (aRv.Failed()) {
+ return nullptr;
+ }
+ return global.forget();
+}
+
+void
+ConstructJSImplementation(const char* aContractId,
+ nsIGlobalObject* aGlobal,
+ JS::MutableHandle<JSObject*> aObject,
+ ErrorResult& aRv)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // Make sure to divorce ourselves from the calling JS while creating and
+ // initializing the object, so exceptions from that will get reported
+ // properly, since those are never exceptions that a spec wants to be thrown.
+ {
+ AutoNoJSAPI nojsapi;
+
+ // Get the XPCOM component containing the JS implementation.
+ nsresult rv;
+ nsCOMPtr<nsISupports> implISupports = do_CreateInstance(aContractId, &rv);
+ if (!implISupports) {
+ nsPrintfCString msg("Failed to get JS implementation for contract \"%s\"",
+ aContractId);
+ NS_WARNING(msg.get());
+ aRv.Throw(rv);
+ return;
+ }
+ // Initialize the object, if it implements nsIDOMGlobalPropertyInitializer
+ // and our global is a window.
+ nsCOMPtr<nsIDOMGlobalPropertyInitializer> gpi =
+ do_QueryInterface(implISupports);
+ nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal);
+ if (gpi) {
+ JS::Rooted<JS::Value> initReturn(RootingCx());
+ rv = gpi->Init(window, &initReturn);
+ if (NS_FAILED(rv)) {
+ aRv.Throw(rv);
+ return;
+ }
+ // With JS-implemented WebIDL, the return value of init() is not used to determine
+ // if init() failed, so init() should only return undefined. Any kind of permission
+ // or pref checking must happen by adding an attribute to the WebIDL interface.
+ if (!initReturn.isUndefined()) {
+ MOZ_ASSERT(false, "The init() method for JS-implemented WebIDL should not return anything");
+ MOZ_CRASH();
+ }
+ }
+ // Extract the JS implementation from the XPCOM object.
+ nsCOMPtr<nsIXPConnectWrappedJS> implWrapped =
+ do_QueryInterface(implISupports, &rv);
+ MOZ_ASSERT(implWrapped, "Failed to get wrapped JS from XPCOM component.");
+ if (!implWrapped) {
+ aRv.Throw(rv);
+ return;
+ }
+ aObject.set(implWrapped->GetJSObject());
+ if (!aObject) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ }
+ }
+}
+
+bool
+NonVoidByteStringToJsval(JSContext *cx, const nsACString &str,
+ JS::MutableHandle<JS::Value> rval)
+{
+ // ByteStrings are not UTF-8 encoded.
+ JSString* jsStr = JS_NewStringCopyN(cx, str.Data(), str.Length());
+
+ if (!jsStr)
+ return false;
+
+ rval.setString(jsStr);
+ return true;
+}
+
+
+template<typename T> static void
+NormalizeUSVStringInternal(JSContext* aCx, T& aString)
+{
+ char16_t* start = aString.BeginWriting();
+ // Must use const here because we can't pass char** to UTF16CharEnumerator as
+ // it expects const char**. Unclear why this is illegal...
+ const char16_t* nextChar = start;
+ const char16_t* end = aString.Data() + aString.Length();
+ while (nextChar < end) {
+ uint32_t enumerated = UTF16CharEnumerator::NextChar(&nextChar, end);
+ if (enumerated == UCS2_REPLACEMENT_CHAR) {
+ int32_t lastCharIndex = (nextChar - start) - 1;
+ start[lastCharIndex] = static_cast<char16_t>(enumerated);
+ }
+ }
+}
+
+void
+NormalizeUSVString(JSContext* aCx, nsAString& aString)
+{
+ NormalizeUSVStringInternal(aCx, aString);
+}
+
+void
+NormalizeUSVString(JSContext* aCx, binding_detail::FakeString& aString)
+{
+ NormalizeUSVStringInternal(aCx, aString);
+}
+
+bool
+ConvertJSValueToByteString(JSContext* cx, JS::Handle<JS::Value> v,
+ bool nullable, nsACString& result)
+{
+ JS::Rooted<JSString*> s(cx);
+ if (v.isString()) {
+ s = v.toString();
+ } else {
+
+ if (nullable && v.isNullOrUndefined()) {
+ result.SetIsVoid(true);
+ return true;
+ }
+
+ s = JS::ToString(cx, v);
+ if (!s) {
+ return false;
+ }
+ }
+
+ // Conversion from Javascript string to ByteString is only valid if all
+ // characters < 256. This is always the case for Latin1 strings.
+ size_t length;
+ if (!js::StringHasLatin1Chars(s)) {
+ // ThrowErrorMessage can GC, so we first scan the string for bad chars
+ // and report the error outside the AutoCheckCannotGC scope.
+ bool foundBadChar = false;
+ size_t badCharIndex;
+ char16_t badChar;
+ {
+ JS::AutoCheckCannotGC nogc;
+ const char16_t* chars = JS_GetTwoByteStringCharsAndLength(cx, nogc, s, &length);
+ if (!chars) {
+ return false;
+ }
+
+ for (size_t i = 0; i < length; i++) {
+ if (chars[i] > 255) {
+ badCharIndex = i;
+ badChar = chars[i];
+ foundBadChar = true;
+ break;
+ }
+ }
+ }
+
+ if (foundBadChar) {
+ MOZ_ASSERT(badCharIndex < length);
+ MOZ_ASSERT(badChar > 255);
+ // The largest unsigned 64 bit number (18,446,744,073,709,551,615) has
+ // 20 digits, plus one more for the null terminator.
+ char index[21];
+ static_assert(sizeof(size_t) <= 8, "index array too small");
+ SprintfLiteral(index, "%" PRIuSIZE, badCharIndex);
+ // A char16_t is 16 bits long. The biggest unsigned 16 bit
+ // number (65,535) has 5 digits, plus one more for the null
+ // terminator.
+ char badCharArray[6];
+ static_assert(sizeof(char16_t) <= 2, "badCharArray too small");
+ SprintfLiteral(badCharArray, "%d", badChar);
+ ThrowErrorMessage(cx, MSG_INVALID_BYTESTRING, index, badCharArray);
+ return false;
+ }
+ } else {
+ length = js::GetStringLength(s);
+ }
+
+ static_assert(js::MaxStringLength < UINT32_MAX,
+ "length+1 shouldn't overflow");
+
+ if (!result.SetLength(length, fallible)) {
+ return false;
+ }
+
+ JS_EncodeStringToBuffer(cx, s, result.BeginWriting(), length);
+
+ return true;
+}
+
+void
+FinalizeGlobal(JSFreeOp* aFreeOp, JSObject* aObj)
+{
+ MOZ_ASSERT(js::GetObjectClass(aObj)->flags & JSCLASS_DOM_GLOBAL);
+ mozilla::dom::DestroyProtoAndIfaceCache(aObj);
+}
+
+bool
+ResolveGlobal(JSContext* aCx, JS::Handle<JSObject*> aObj,
+ JS::Handle<jsid> aId, bool* aResolvedp)
+{
+ MOZ_ASSERT(JS_IsGlobalObject(aObj),
+ "Should have a global here, since we plan to resolve standard "
+ "classes!");
+
+ return JS_ResolveStandardClass(aCx, aObj, aId, aResolvedp);
+}
+
+bool
+MayResolveGlobal(const JSAtomState& aNames, jsid aId, JSObject* aMaybeObj)
+{
+ return JS_MayResolveStandardClass(aNames, aId, aMaybeObj);
+}
+
+bool
+EnumerateGlobal(JSContext* aCx, JS::Handle<JSObject*> aObj)
+{
+ MOZ_ASSERT(JS_IsGlobalObject(aObj),
+ "Should have a global here, since we plan to enumerate standard "
+ "classes!");
+
+ return JS_EnumerateStandardClasses(aCx, aObj);
+}
+
+bool
+IsNonExposedGlobal(JSContext* aCx, JSObject* aGlobal,
+ uint32_t aNonExposedGlobals)
+{
+ MOZ_ASSERT(aNonExposedGlobals, "Why did we get called?");
+ MOZ_ASSERT((aNonExposedGlobals &
+ ~(GlobalNames::Window |
+ GlobalNames::BackstagePass |
+ GlobalNames::DedicatedWorkerGlobalScope |
+ GlobalNames::SharedWorkerGlobalScope |
+ GlobalNames::ServiceWorkerGlobalScope |
+ GlobalNames::WorkerDebuggerGlobalScope |
+ GlobalNames::WorkletGlobalScope)) == 0,
+ "Unknown non-exposed global type");
+
+ const char* name = js::GetObjectClass(aGlobal)->name;
+
+ if ((aNonExposedGlobals & GlobalNames::Window) &&
+ !strcmp(name, "Window")) {
+ return true;
+ }
+
+ if ((aNonExposedGlobals & GlobalNames::BackstagePass) &&
+ !strcmp(name, "BackstagePass")) {
+ return true;
+ }
+
+ if ((aNonExposedGlobals & GlobalNames::DedicatedWorkerGlobalScope) &&
+ !strcmp(name, "DedicatedWorkerGlobalScope")) {
+ return true;
+ }
+
+ if ((aNonExposedGlobals & GlobalNames::SharedWorkerGlobalScope) &&
+ !strcmp(name, "SharedWorkerGlobalScope")) {
+ return true;
+ }
+
+ if ((aNonExposedGlobals & GlobalNames::ServiceWorkerGlobalScope) &&
+ !strcmp(name, "ServiceWorkerGlobalScope")) {
+ return true;
+ }
+
+ if ((aNonExposedGlobals & GlobalNames::WorkerDebuggerGlobalScope) &&
+ !strcmp(name, "WorkerDebuggerGlobalScopex")) {
+ return true;
+ }
+
+ if ((aNonExposedGlobals & GlobalNames::WorkletGlobalScope) &&
+ !strcmp(name, "WorkletGlobalScope")) {
+ return true;
+ }
+
+ return false;
+}
+
+void
+HandlePrerenderingViolation(nsPIDOMWindowInner* aWindow)
+{
+ // Freeze the window and its workers, and its children too.
+ aWindow->Freeze();
+
+ // Suspend event handling on the document
+ nsCOMPtr<nsIDocument> doc = aWindow->GetExtantDoc();
+ if (doc) {
+ doc->SuppressEventHandling(nsIDocument::eEvents);
+ }
+}
+
+bool
+EnforceNotInPrerendering(JSContext* aCx, JSObject* aObj)
+{
+ JS::Rooted<JSObject*> thisObj(aCx, js::CheckedUnwrap(aObj));
+ if (!thisObj) {
+ // Without a this object, we cannot check the safety.
+ return true;
+ }
+ nsGlobalWindow* window = xpc::WindowGlobalOrNull(thisObj);
+ if (!window) {
+ // Without a window, we cannot check the safety.
+ return true;
+ }
+
+ if (window->GetIsPrerendered()) {
+ HandlePrerenderingViolation(window->AsInner());
+ // When the bindings layer sees a false return value, it returns false form
+ // the JSNative in order to trigger an uncatchable exception.
+ return false;
+ }
+
+ return true;
+}
+
+bool
+GenericBindingGetter(JSContext* cx, unsigned argc, JS::Value* vp)
+{
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
+ prototypes::ID protoID = static_cast<prototypes::ID>(info->protoID);
+ if (!args.thisv().isObject()) {
+ return ThrowInvalidThis(cx, args, false, protoID);
+ }
+ JS::Rooted<JSObject*> obj(cx, &args.thisv().toObject());
+
+ // NOTE: we want to leave obj in its initial compartment, so don't want to
+ // pass it to UnwrapObject.
+ JS::Rooted<JSObject*> rootSelf(cx, obj);
+ void* self;
+ {
+ binding_detail::MutableObjectHandleWrapper wrapper(&rootSelf);
+ nsresult rv = binding_detail::UnwrapObjectInternal<void, true>(wrapper,
+ self,
+ protoID,
+ info->depth);
+ if (NS_FAILED(rv)) {
+ return ThrowInvalidThis(cx, args,
+ rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO,
+ protoID);
+ }
+ }
+
+ MOZ_ASSERT(info->type() == JSJitInfo::Getter);
+ JSJitGetterOp getter = info->getter;
+ bool ok = getter(cx, obj, self, JSJitGetterCallArgs(args));
+#ifdef DEBUG
+ if (ok) {
+ AssertReturnTypeMatchesJitinfo(info, args.rval());
+ }
+#endif
+ return ok;
+}
+
+bool
+GenericBindingSetter(JSContext* cx, unsigned argc, JS::Value* vp)
+{
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
+ prototypes::ID protoID = static_cast<prototypes::ID>(info->protoID);
+ if (!args.thisv().isObject()) {
+ return ThrowInvalidThis(cx, args, false, protoID);
+ }
+ JS::Rooted<JSObject*> obj(cx, &args.thisv().toObject());
+
+ // NOTE: we want to leave obj in its initial compartment, so don't want to
+ // pass it to UnwrapObject.
+ JS::Rooted<JSObject*> rootSelf(cx, obj);
+ void* self;
+ {
+ binding_detail::MutableObjectHandleWrapper wrapper(&rootSelf);
+ nsresult rv = binding_detail::UnwrapObjectInternal<void, true>(wrapper,
+ self,
+ protoID,
+ info->depth);
+ if (NS_FAILED(rv)) {
+ return ThrowInvalidThis(cx, args,
+ rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO,
+ protoID);
+ }
+ }
+ if (args.length() == 0) {
+ return ThrowNoSetterArg(cx, protoID);
+ }
+ MOZ_ASSERT(info->type() == JSJitInfo::Setter);
+ JSJitSetterOp setter = info->setter;
+ if (!setter(cx, obj, self, JSJitSetterCallArgs(args))) {
+ return false;
+ }
+ args.rval().setUndefined();
+#ifdef DEBUG
+ AssertReturnTypeMatchesJitinfo(info, args.rval());
+#endif
+ return true;
+}
+
+bool
+GenericBindingMethod(JSContext* cx, unsigned argc, JS::Value* vp)
+{
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
+ prototypes::ID protoID = static_cast<prototypes::ID>(info->protoID);
+ if (!args.thisv().isObject()) {
+ return ThrowInvalidThis(cx, args, false, protoID);
+ }
+ JS::Rooted<JSObject*> obj(cx, &args.thisv().toObject());
+
+ // NOTE: we want to leave obj in its initial compartment, so don't want to
+ // pass it to UnwrapObject.
+ JS::Rooted<JSObject*> rootSelf(cx, obj);
+ void* self;
+ {
+ binding_detail::MutableObjectHandleWrapper wrapper(&rootSelf);
+ nsresult rv = binding_detail::UnwrapObjectInternal<void, true>(wrapper,
+ self,
+ protoID,
+ info->depth);
+ if (NS_FAILED(rv)) {
+ return ThrowInvalidThis(cx, args,
+ rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO,
+ protoID);
+ }
+ }
+ MOZ_ASSERT(info->type() == JSJitInfo::Method);
+ JSJitMethodOp method = info->method;
+ bool ok = method(cx, obj, self, JSJitMethodCallArgs(args));
+#ifdef DEBUG
+ if (ok) {
+ AssertReturnTypeMatchesJitinfo(info, args.rval());
+ }
+#endif
+ return ok;
+}
+
+bool
+GenericPromiseReturningBindingMethod(JSContext* cx, unsigned argc, JS::Value* vp)
+{
+ // Make sure to save the callee before someone maybe messes with rval().
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ JS::Rooted<JSObject*> callee(cx, &args.callee());
+
+ // We could invoke GenericBindingMethod here, but that involves an
+ // extra call. Manually inline it instead.
+ const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
+ prototypes::ID protoID = static_cast<prototypes::ID>(info->protoID);
+ if (!args.thisv().isObject()) {
+ ThrowInvalidThis(cx, args, false, protoID);
+ return ConvertExceptionToPromise(cx, xpc::XrayAwareCalleeGlobal(callee),
+ args.rval());
+ }
+ JS::Rooted<JSObject*> obj(cx, &args.thisv().toObject());
+
+ // NOTE: we want to leave obj in its initial compartment, so don't want to
+ // pass it to UnwrapObject.
+ JS::Rooted<JSObject*> rootSelf(cx, obj);
+ void* self;
+ {
+ binding_detail::MutableObjectHandleWrapper wrapper(&rootSelf);
+ nsresult rv = binding_detail::UnwrapObjectInternal<void, true>(wrapper,
+ self,
+ protoID,
+ info->depth);
+ if (NS_FAILED(rv)) {
+ ThrowInvalidThis(cx, args, rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO,
+ protoID);
+ return ConvertExceptionToPromise(cx, xpc::XrayAwareCalleeGlobal(callee),
+ args.rval());
+ }
+ }
+ MOZ_ASSERT(info->type() == JSJitInfo::Method);
+ JSJitMethodOp method = info->method;
+ bool ok = method(cx, obj, self, JSJitMethodCallArgs(args));
+ if (ok) {
+#ifdef DEBUG
+ AssertReturnTypeMatchesJitinfo(info, args.rval());
+#endif
+ return true;
+ }
+
+ // Promise-returning methods always return objects
+ MOZ_ASSERT(info->returnType() == JSVAL_TYPE_OBJECT);
+ return ConvertExceptionToPromise(cx, xpc::XrayAwareCalleeGlobal(callee),
+ args.rval());
+}
+
+bool
+StaticMethodPromiseWrapper(JSContext* cx, unsigned argc, JS::Value* vp)
+{
+ // Make sure to save the callee before someone maybe messes with rval().
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ JS::Rooted<JSObject*> callee(cx, &args.callee());
+
+ const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
+ MOZ_ASSERT(info);
+ MOZ_ASSERT(info->type() == JSJitInfo::StaticMethod);
+
+ bool ok = info->staticMethod(cx, argc, vp);
+ if (ok) {
+ return true;
+ }
+
+ return ConvertExceptionToPromise(cx, xpc::XrayAwareCalleeGlobal(callee),
+ args.rval());
+}
+
+bool
+ConvertExceptionToPromise(JSContext* cx,
+ JSObject* promiseScope,
+ JS::MutableHandle<JS::Value> rval)
+{
+#ifndef SPIDERMONKEY_PROMISE
+ GlobalObject global(cx, promiseScope);
+ if (global.Failed()) {
+ return false;
+ }
+
+ JS::Rooted<JS::Value> exn(cx);
+ if (!JS_GetPendingException(cx, &exn)) {
+ // This is very important: if there is no pending exception here but we're
+ // ending up in this code, that means the callee threw an uncatchable
+ // exception. Just propagate that out as-is.
+ return false;
+ }
+
+ JS_ClearPendingException(cx);
+
+ nsCOMPtr<nsIGlobalObject> globalObj =
+ do_QueryInterface(global.GetAsSupports());
+ if (!globalObj) {
+ ErrorResult rv;
+ rv.Throw(NS_ERROR_UNEXPECTED);
+ return !rv.MaybeSetPendingException(cx);
+ }
+
+ ErrorResult rv;
+ RefPtr<Promise> promise = Promise::Reject(globalObj, cx, exn, rv);
+ if (rv.MaybeSetPendingException(cx)) {
+ // We just give up. We put the exception from the ErrorResult on
+ // the JSContext just to make sure to not leak memory on the
+ // ErrorResult, but now just put the original exception back.
+ JS_SetPendingException(cx, exn);
+ return false;
+ }
+
+ return GetOrCreateDOMReflector(cx, promise, rval);
+#else // SPIDERMONKEY_PROMISE
+ {
+ JSAutoCompartment ac(cx, promiseScope);
+
+ JS::Rooted<JS::Value> exn(cx);
+ if (!JS_GetPendingException(cx, &exn)) {
+ // This is very important: if there is no pending exception here but we're
+ // ending up in this code, that means the callee threw an uncatchable
+ // exception. Just propagate that out as-is.
+ return false;
+ }
+
+ JS_ClearPendingException(cx);
+
+ JSObject* promise = JS::CallOriginalPromiseReject(cx, exn);
+ if (!promise) {
+ // We just give up. Put the exception back.
+ JS_SetPendingException(cx, exn);
+ return false;
+ }
+
+ rval.setObject(*promise);
+ }
+
+ // Now make sure we rewrap promise back into the compartment we want
+ return JS_WrapValue(cx, rval);
+#endif // SPIDERMONKEY_PROMISE
+}
+
+/* static */
+void
+CreateGlobalOptions<nsGlobalWindow>::TraceGlobal(JSTracer* aTrc, JSObject* aObj)
+{
+ xpc::TraceXPCGlobal(aTrc, aObj);
+}
+
+static bool sRegisteredDOMNames = false;
+
+nsresult
+RegisterDOMNames()
+{
+ if (sRegisteredDOMNames) {
+ return NS_OK;
+ }
+
+ // Register new DOM bindings
+ WebIDLGlobalNameHash::Init();
+
+ nsresult rv = nsDOMClassInfo::Init();
+ if (NS_FAILED(rv)) {
+ NS_ERROR("Could not initialize nsDOMClassInfo");
+ return rv;
+ }
+
+ sRegisteredDOMNames = true;
+
+ return NS_OK;
+}
+
+/* static */
+bool
+CreateGlobalOptions<nsGlobalWindow>::PostCreateGlobal(JSContext* aCx,
+ JS::Handle<JSObject*> aGlobal)
+{
+ nsresult rv = RegisterDOMNames();
+ if (NS_FAILED(rv)) {
+ return Throw(aCx, rv);
+ }
+
+ // Invoking the XPCWrappedNativeScope constructor automatically hooks it
+ // up to the compartment of aGlobal.
+ (void) new XPCWrappedNativeScope(aCx, aGlobal);
+ return true;
+}
+
+#ifdef DEBUG
+void
+AssertReturnTypeMatchesJitinfo(const JSJitInfo* aJitInfo,
+ JS::Handle<JS::Value> aValue)
+{
+ switch (aJitInfo->returnType()) {
+ case JSVAL_TYPE_UNKNOWN:
+ // Any value is good.
+ break;
+ case JSVAL_TYPE_DOUBLE:
+ // The value could actually be an int32 value as well.
+ MOZ_ASSERT(aValue.isNumber());
+ break;
+ case JSVAL_TYPE_INT32:
+ MOZ_ASSERT(aValue.isInt32());
+ break;
+ case JSVAL_TYPE_UNDEFINED:
+ MOZ_ASSERT(aValue.isUndefined());
+ break;
+ case JSVAL_TYPE_BOOLEAN:
+ MOZ_ASSERT(aValue.isBoolean());
+ break;
+ case JSVAL_TYPE_STRING:
+ MOZ_ASSERT(aValue.isString());
+ break;
+ case JSVAL_TYPE_NULL:
+ MOZ_ASSERT(aValue.isNull());
+ break;
+ case JSVAL_TYPE_OBJECT:
+ MOZ_ASSERT(aValue.isObject());
+ break;
+ default:
+ // Someone messed up their jitinfo type.
+ MOZ_ASSERT(false, "Unexpected JSValueType stored in jitinfo");
+ break;
+ }
+}
+#endif
+
+bool
+CallerSubsumes(JSObject *aObject)
+{
+ nsIPrincipal* objPrin = nsContentUtils::ObjectPrincipal(js::UncheckedUnwrap(aObject));
+ return nsContentUtils::SubjectPrincipal()->Subsumes(objPrin);
+}
+
+nsresult
+UnwrapArgImpl(JS::Handle<JSObject*> src,
+ const nsIID &iid,
+ void **ppArg)
+{
+ if (!NS_IsMainThread()) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ nsCOMPtr<nsISupports> iface = xpc::UnwrapReflectorToISupports(src);
+ if (iface) {
+ if (NS_FAILED(iface->QueryInterface(iid, ppArg))) {
+ return NS_ERROR_XPC_BAD_CONVERT_JS;
+ }
+
+ return NS_OK;
+ }
+
+ // Only allow XPCWrappedJS stuff in system code. Ideally we would remove this
+ // even there, but that involves converting some things to WebIDL callback
+ // interfaces and making some other things builtinclass...
+ if (!nsContentUtils::IsCallerChrome()) {
+ return NS_ERROR_XPC_BAD_CONVERT_JS;
+ }
+
+ RefPtr<nsXPCWrappedJS> wrappedJS;
+ nsresult rv = nsXPCWrappedJS::GetNewOrUsed(src, iid, getter_AddRefs(wrappedJS));
+ if (NS_FAILED(rv) || !wrappedJS) {
+ return rv;
+ }
+
+ // We need to go through the QueryInterface logic to make this return
+ // the right thing for the various 'special' interfaces; e.g.
+ // nsIPropertyBag. We must use AggregatedQueryInterface in cases where
+ // there is an outer to avoid nasty recursion.
+ return wrappedJS->QueryInterface(iid, ppArg);
+}
+
+nsresult
+UnwrapXPConnectImpl(JSContext* cx,
+ JS::MutableHandle<JS::Value> src,
+ const nsIID &iid,
+ void **ppArg)
+{
+ if (!NS_IsMainThread()) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ MOZ_ASSERT(src.isObject());
+ // Unwrap ourselves, because we're going to want access to the unwrapped
+ // object.
+ JS::Rooted<JSObject*> obj(cx,
+ js::CheckedUnwrap(&src.toObject(),
+ /* stopAtWindowProxy = */ false));
+ if (!obj) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ nsCOMPtr<nsISupports> iface = xpc::UnwrapReflectorToISupports(obj);
+ if (!iface) {
+ return NS_ERROR_XPC_BAD_CONVERT_JS;
+ }
+
+ if (NS_FAILED(iface->QueryInterface(iid, ppArg))) {
+ return NS_ERROR_XPC_BAD_CONVERT_JS;
+ }
+
+ // Now update our source to keep rooting our object.
+ src.setObject(*obj);
+ return NS_OK;
+}
+
+nsresult
+UnwrapWindowProxyImpl(JS::Handle<JSObject*> src,
+ nsPIDOMWindowOuter** ppArg)
+{
+ nsCOMPtr<nsPIDOMWindowInner> inner;
+ nsresult rv = UnwrapArg<nsPIDOMWindowInner>(src, getter_AddRefs(inner));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsPIDOMWindowOuter> outer = inner->GetOuterWindow();
+ outer.forget(ppArg);
+ return NS_OK;
+}
+
+bool
+SystemGlobalResolve(JSContext* cx, JS::Handle<JSObject*> obj,
+ JS::Handle<jsid> id, bool* resolvedp)
+{
+ if (!ResolveGlobal(cx, obj, id, resolvedp)) {
+ return false;
+ }
+
+ if (*resolvedp) {
+ return true;
+ }
+
+ return ResolveSystemBinding(cx, obj, id, resolvedp);
+}
+
+bool
+SystemGlobalEnumerate(JSContext* cx, JS::Handle<JSObject*> obj)
+{
+ bool ignored = false;
+ return EnumerateGlobal(cx, obj) &&
+ ResolveSystemBinding(cx, obj, JSID_VOIDHANDLE, &ignored);
+}
+
+template<decltype(JS::NewMapObject) Method>
+bool
+GetMaplikeSetlikeBackingObject(JSContext* aCx, JS::Handle<JSObject*> aObj,
+ size_t aSlotIndex,
+ JS::MutableHandle<JSObject*> aBackingObj,
+ bool* aBackingObjCreated)
+{
+ JS::Rooted<JSObject*> reflector(aCx);
+ reflector = IsDOMObject(aObj) ? aObj : js::UncheckedUnwrap(aObj,
+ /* stopAtWindowProxy = */ false);
+
+ // Retrieve the backing object from the reserved slot on the maplike/setlike
+ // object. If it doesn't exist yet, create it.
+ JS::Rooted<JS::Value> slotValue(aCx);
+ slotValue = js::GetReservedSlot(reflector, aSlotIndex);
+ if (slotValue.isUndefined()) {
+ // Since backing object access can happen in non-originating compartments,
+ // make sure to create the backing object in reflector compartment.
+ {
+ JSAutoCompartment ac(aCx, reflector);
+ JS::Rooted<JSObject*> newBackingObj(aCx);
+ newBackingObj.set(Method(aCx));
+ if (NS_WARN_IF(!newBackingObj)) {
+ return false;
+ }
+ js::SetReservedSlot(reflector, aSlotIndex, JS::ObjectValue(*newBackingObj));
+ }
+ slotValue = js::GetReservedSlot(reflector, aSlotIndex);
+ *aBackingObjCreated = true;
+ } else {
+ *aBackingObjCreated = false;
+ }
+ if (!MaybeWrapNonDOMObjectValue(aCx, &slotValue)) {
+ return false;
+ }
+ aBackingObj.set(&slotValue.toObject());
+ return true;
+}
+
+bool
+GetMaplikeBackingObject(JSContext* aCx, JS::Handle<JSObject*> aObj,
+ size_t aSlotIndex,
+ JS::MutableHandle<JSObject*> aBackingObj,
+ bool* aBackingObjCreated)
+{
+ return GetMaplikeSetlikeBackingObject<JS::NewMapObject>(aCx, aObj, aSlotIndex,
+ aBackingObj,
+ aBackingObjCreated);
+}
+
+bool
+GetSetlikeBackingObject(JSContext* aCx, JS::Handle<JSObject*> aObj,
+ size_t aSlotIndex,
+ JS::MutableHandle<JSObject*> aBackingObj,
+ bool* aBackingObjCreated)
+{
+ return GetMaplikeSetlikeBackingObject<JS::NewSetObject>(aCx, aObj, aSlotIndex,
+ aBackingObj,
+ aBackingObjCreated);
+}
+
+bool
+ForEachHandler(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
+{
+ JS::CallArgs args = CallArgsFromVp(aArgc, aVp);
+ // Unpack callback and object from slots
+ JS::Rooted<JS::Value>
+ callbackFn(aCx, js::GetFunctionNativeReserved(&args.callee(),
+ FOREACH_CALLBACK_SLOT));
+ JS::Rooted<JS::Value>
+ maplikeOrSetlikeObj(aCx,
+ js::GetFunctionNativeReserved(&args.callee(),
+ FOREACH_MAPLIKEORSETLIKEOBJ_SLOT));
+ MOZ_ASSERT(aArgc == 3);
+ JS::AutoValueVector newArgs(aCx);
+ // Arguments are passed in as value, key, object. Keep value and key, replace
+ // object with the maplike/setlike object.
+ if (!newArgs.append(args.get(0))) {
+ return false;
+ }
+ if (!newArgs.append(args.get(1))) {
+ return false;
+ }
+ if (!newArgs.append(maplikeOrSetlikeObj)) {
+ return false;
+ }
+ JS::Rooted<JS::Value> rval(aCx, JS::UndefinedValue());
+ // Now actually call the user specified callback
+ return JS::Call(aCx, args.thisv(), callbackFn, newArgs, &rval);
+}
+
+static inline prototypes::ID
+GetProtoIdForNewtarget(JS::Handle<JSObject*> aNewTarget)
+{
+ const js::Class* newTargetClass = js::GetObjectClass(aNewTarget);
+ if (IsDOMIfaceAndProtoClass(newTargetClass)) {
+ const DOMIfaceAndProtoJSClass* newTargetIfaceClass =
+ DOMIfaceAndProtoJSClass::FromJSClass(newTargetClass);
+ if (newTargetIfaceClass->mType == eInterface) {
+ return newTargetIfaceClass->mPrototypeID;
+ }
+ } else if (JS_IsNativeFunction(aNewTarget, Constructor)) {
+ return GetNativePropertyHooksFromConstructorFunction(aNewTarget)->mPrototypeID;
+ }
+
+ return prototypes::id::_ID_Count;
+}
+
+bool
+GetDesiredProto(JSContext* aCx, const JS::CallArgs& aCallArgs,
+ JS::MutableHandle<JSObject*> aDesiredProto)
+{
+ if (!aCallArgs.isConstructing()) {
+ aDesiredProto.set(nullptr);
+ return true;
+ }
+
+ // The desired prototype depends on the actual constructor that was invoked,
+ // which is passed to us as the newTarget in the callargs. We want to do
+ // something akin to the ES6 specification's GetProtototypeFromConstructor (so
+ // get .prototype on the newTarget, with a fallback to some sort of default).
+
+ // First, a fast path for the case when the the constructor is in fact one of
+ // our DOM constructors. This is safe because on those the "constructor"
+ // property is non-configurable and non-writable, so we don't have to do the
+ // slow JS_GetProperty call.
+ JS::Rooted<JSObject*> newTarget(aCx, &aCallArgs.newTarget().toObject());
+ JS::Rooted<JSObject*> originalNewTarget(aCx, newTarget);
+ // See whether we have a known DOM constructor here, such that we can take a
+ // fast path.
+ prototypes::ID protoID = GetProtoIdForNewtarget(newTarget);
+ if (protoID == prototypes::id::_ID_Count) {
+ // We might still have a cross-compartment wrapper for a known DOM
+ // constructor.
+ newTarget = js::CheckedUnwrap(newTarget);
+ if (newTarget && newTarget != originalNewTarget) {
+ protoID = GetProtoIdForNewtarget(newTarget);
+ }
+ }
+
+ if (protoID != prototypes::id::_ID_Count) {
+ ProtoAndIfaceCache& protoAndIfaceCache =
+ *GetProtoAndIfaceCache(js::GetGlobalForObjectCrossCompartment(newTarget));
+ aDesiredProto.set(protoAndIfaceCache.EntrySlotMustExist(protoID));
+ if (newTarget != originalNewTarget) {
+ return JS_WrapObject(aCx, aDesiredProto);
+ }
+ return true;
+ }
+
+ // Slow path. This basically duplicates the ES6 spec's
+ // GetPrototypeFromConstructor except that instead of taking a string naming
+ // the fallback prototype we just fall back to using null and assume that our
+ // caller will then pick the right default. The actual defaulting behavior
+ // here still needs to be defined in the Web IDL specification.
+ //
+ // Note that it's very important to do this property get on originalNewTarget,
+ // not our unwrapped newTarget, since we want to get Xray behavior here as
+ // needed.
+ // XXXbz for speed purposes, using a preinterned id here sure would be nice.
+ JS::Rooted<JS::Value> protoVal(aCx);
+ if (!JS_GetProperty(aCx, originalNewTarget, "prototype", &protoVal)) {
+ return false;
+ }
+
+ if (!protoVal.isObject()) {
+ aDesiredProto.set(nullptr);
+ return true;
+ }
+
+ aDesiredProto.set(&protoVal.toObject());
+ return true;
+}
+
+#ifdef DEBUG
+namespace binding_detail {
+void
+AssertReflectorHasGivenProto(JSContext* aCx, JSObject* aReflector,
+ JS::Handle<JSObject*> aGivenProto)
+{
+ if (!aGivenProto) {
+ // Nothing to assert here
+ return;
+ }
+
+ JS::Rooted<JSObject*> reflector(aCx, aReflector);
+ JSAutoCompartment ac(aCx, reflector);
+ JS::Rooted<JSObject*> reflectorProto(aCx);
+ bool ok = JS_GetPrototype(aCx, reflector, &reflectorProto);
+ MOZ_ASSERT(ok);
+ // aGivenProto may not be in the right compartment here, so we
+ // have to wrap it to compare.
+ JS::Rooted<JSObject*> givenProto(aCx, aGivenProto);
+ ok = JS_WrapObject(aCx, &givenProto);
+ MOZ_ASSERT(ok);
+ MOZ_ASSERT(givenProto == reflectorProto,
+ "How are we supposed to change the proto now?");
+}
+} // namespace binding_detail
+#endif // DEBUG
+
+void
+SetDocumentAndPageUseCounter(JSContext* aCx, JSObject* aObject,
+ UseCounter aUseCounter)
+{
+ nsGlobalWindow* win = xpc::WindowGlobalOrNull(js::UncheckedUnwrap(aObject));
+ if (win && win->GetDocument()) {
+ win->GetDocument()->SetDocumentAndPageUseCounter(aUseCounter);
+ }
+}
+
+namespace {
+
+// This runnable is used to write a deprecation message from a worker to the
+// console running on the main-thread.
+class DeprecationWarningRunnable final : public WorkerProxyToMainThreadRunnable
+{
+ nsIDocument::DeprecatedOperations mOperation;
+
+public:
+ DeprecationWarningRunnable(WorkerPrivate* aWorkerPrivate,
+ nsIDocument::DeprecatedOperations aOperation)
+ : WorkerProxyToMainThreadRunnable(aWorkerPrivate)
+ , mOperation(aOperation)
+ {
+ MOZ_ASSERT(aWorkerPrivate);
+ aWorkerPrivate->AssertIsOnWorkerThread();
+ }
+
+private:
+ void
+ RunOnMainThread() override
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // Walk up to our containing page
+ WorkerPrivate* wp = mWorkerPrivate;
+ while (wp->GetParent()) {
+ wp = wp->GetParent();
+ }
+
+ nsPIDOMWindowInner* window = wp->GetWindow();
+ if (window && window->GetExtantDoc()) {
+ window->GetExtantDoc()->WarnOnceAbout(mOperation);
+ }
+ }
+
+ void
+ RunBackOnWorkerThread() override
+ {}
+};
+
+} // anonymous namespace
+
+void
+DeprecationWarning(JSContext* aCx, JSObject* aObject,
+ nsIDocument::DeprecatedOperations aOperation)
+{
+ GlobalObject global(aCx, aObject);
+ if (global.Failed()) {
+ NS_ERROR("Could not create global for DeprecationWarning");
+ return;
+ }
+
+ if (NS_IsMainThread()) {
+ nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(global.GetAsSupports());
+ if (window && window->GetExtantDoc()) {
+ window->GetExtantDoc()->WarnOnceAbout(aOperation);
+ }
+
+ return;
+ }
+
+ WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
+ if (!workerPrivate) {
+ return;
+ }
+
+ RefPtr<DeprecationWarningRunnable> runnable =
+ new DeprecationWarningRunnable(workerPrivate, aOperation);
+ runnable->Dispatch();
+}
+
+namespace binding_detail {
+JSObject*
+UnprivilegedJunkScopeOrWorkerGlobal()
+{
+ if (NS_IsMainThread()) {
+ return xpc::UnprivilegedJunkScope();
+ }
+
+ return GetCurrentThreadWorkerGlobal();
+}
+} // namespace binding_detail
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/bindings/BindingUtils.h b/dom/bindings/BindingUtils.h
new file mode 100644
index 000000000..3e58390c9
--- /dev/null
+++ b/dom/bindings/BindingUtils.h
@@ -0,0 +1,3441 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "jsfriendapi.h"
+#include "jswrapper.h"
+#include "js/Conversions.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Alignment.h"
+#include "mozilla/Array.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/CycleCollectedJSContext.h"
+#include "mozilla/DeferredFinalize.h"
+#include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/dom/CallbackObject.h"
+#include "mozilla/dom/DOMJSClass.h"
+#include "mozilla/dom/DOMJSProxyHandler.h"
+#include "mozilla/dom/Exceptions.h"
+#include "mozilla/dom/NonRefcountedDOMObject.h"
+#include "mozilla/dom/Nullable.h"
+#include "mozilla/dom/RootedDictionary.h"
+#include "mozilla/SegmentedVector.h"
+#include "mozilla/dom/workers/Workers.h"
+#include "mozilla/ErrorResult.h"
+#include "mozilla/Likely.h"
+#include "mozilla/MemoryReporting.h"
+#include "nsAutoPtr.h"
+#include "nsIDocument.h"
+#include "nsIGlobalObject.h"
+#include "nsIXPConnect.h"
+#include "nsJSUtils.h"
+#include "nsISupportsImpl.h"
+#include "qsObjectHelper.h"
+#include "xpcpublic.h"
+#include "nsIVariant.h"
+#include "mozilla/dom/FakeString.h"
+
+#include "nsWrapperCacheInlines.h"
+
+class nsIJSID;
+
+namespace mozilla {
+
+enum UseCounter : int16_t;
+
+namespace dom {
+template<typename DataType> class MozMap;
+
+nsresult
+UnwrapArgImpl(JS::Handle<JSObject*> src, const nsIID& iid, void** ppArg);
+
+nsresult
+UnwrapWindowProxyImpl(JS::Handle<JSObject*> src, nsPIDOMWindowOuter** ppArg);
+
+/** Convert a jsval to an XPCOM pointer. Caller must not assume that src will
+ keep the XPCOM pointer rooted. */
+template <class Interface>
+inline nsresult
+UnwrapArg(JS::Handle<JSObject*> src, Interface** ppArg)
+{
+ return UnwrapArgImpl(src, NS_GET_TEMPLATE_IID(Interface),
+ reinterpret_cast<void**>(ppArg));
+}
+
+template <>
+inline nsresult
+UnwrapArg<nsPIDOMWindowOuter>(JS::Handle<JSObject*> src, nsPIDOMWindowOuter** ppArg)
+{
+ return UnwrapWindowProxyImpl(src, ppArg);
+}
+
+nsresult
+UnwrapXPConnectImpl(JSContext* cx, JS::MutableHandle<JS::Value> src,
+ const nsIID& iid, void** ppArg);
+
+/*
+ * Convert a jsval being used as a Web IDL interface implementation to an XPCOM
+ * pointer; this is only used for Web IDL interfaces that specify
+ * hasXPConnectImpls. This is not the same as UnwrapArg because caller _can_
+ * assume that if unwrapping succeeds "val" will be updated so it's rooting the
+ * XPCOM pointer. Also, UnwrapXPConnect doesn't need to worry about doing
+ * XPCWrappedJS things.
+ *
+ * val must be an ObjectValue.
+ */
+template<class Interface>
+inline nsresult
+UnwrapXPConnect(JSContext* cx, JS::MutableHandle<JS::Value> val,
+ Interface** ppThis)
+{
+ return UnwrapXPConnectImpl(cx, val, NS_GET_TEMPLATE_IID(Interface),
+ reinterpret_cast<void**>(ppThis));
+}
+
+bool
+ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs,
+ bool aSecurityError, const char* aInterfaceName);
+
+bool
+ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs,
+ bool aSecurityError, prototypes::ID aProtoId);
+
+// Returns true if the JSClass is used for DOM objects.
+inline bool
+IsDOMClass(const JSClass* clasp)
+{
+ return clasp->flags & JSCLASS_IS_DOMJSCLASS;
+}
+
+inline bool
+IsDOMClass(const js::Class* clasp)
+{
+ return IsDOMClass(Jsvalify(clasp));
+}
+
+// Return true if the JSClass is used for non-proxy DOM objects.
+inline bool
+IsNonProxyDOMClass(const js::Class* clasp)
+{
+ return IsDOMClass(clasp) && !clasp->isProxy();
+}
+
+inline bool
+IsNonProxyDOMClass(const JSClass* clasp)
+{
+ return IsNonProxyDOMClass(js::Valueify(clasp));
+}
+
+// Returns true if the JSClass is used for DOM interface and interface
+// prototype objects.
+inline bool
+IsDOMIfaceAndProtoClass(const JSClass* clasp)
+{
+ return clasp->flags & JSCLASS_IS_DOMIFACEANDPROTOJSCLASS;
+}
+
+inline bool
+IsDOMIfaceAndProtoClass(const js::Class* clasp)
+{
+ return IsDOMIfaceAndProtoClass(Jsvalify(clasp));
+}
+
+static_assert(DOM_OBJECT_SLOT == 0,
+ "DOM_OBJECT_SLOT doesn't match the proxy private slot. "
+ "Expect bad things");
+template <class T>
+inline T*
+UnwrapDOMObject(JSObject* obj)
+{
+ MOZ_ASSERT(IsDOMClass(js::GetObjectClass(obj)),
+ "Don't pass non-DOM objects to this function");
+
+ JS::Value val = js::GetReservedOrProxyPrivateSlot(obj, DOM_OBJECT_SLOT);
+ return static_cast<T*>(val.toPrivate());
+}
+
+template <class T>
+inline T*
+UnwrapPossiblyNotInitializedDOMObject(JSObject* obj)
+{
+ // This is used by the OjectMoved JSClass hook which can be called before
+ // JS_NewObject has returned and so before we have a chance to set
+ // DOM_OBJECT_SLOT to anything useful.
+
+ MOZ_ASSERT(IsDOMClass(js::GetObjectClass(obj)),
+ "Don't pass non-DOM objects to this function");
+
+ JS::Value val = js::GetReservedOrProxyPrivateSlot(obj, DOM_OBJECT_SLOT);
+ if (val.isUndefined()) {
+ return nullptr;
+ }
+ return static_cast<T*>(val.toPrivate());
+}
+
+inline const DOMJSClass*
+GetDOMClass(const js::Class* clasp)
+{
+ return IsDOMClass(clasp) ? DOMJSClass::FromJSClass(clasp) : nullptr;
+}
+
+inline const DOMJSClass*
+GetDOMClass(JSObject* obj)
+{
+ return GetDOMClass(js::GetObjectClass(obj));
+}
+
+inline nsISupports*
+UnwrapDOMObjectToISupports(JSObject* aObject)
+{
+ const DOMJSClass* clasp = GetDOMClass(aObject);
+ if (!clasp || !clasp->mDOMObjectIsISupports) {
+ return nullptr;
+ }
+
+ return UnwrapPossiblyNotInitializedDOMObject<nsISupports>(aObject);
+}
+
+inline bool
+IsDOMObject(JSObject* obj)
+{
+ return IsDOMClass(js::GetObjectClass(obj));
+}
+
+// There are two valid ways to use UNWRAP_OBJECT: Either obj needs to
+// be a MutableHandle<JSObject*>, or value needs to be a strong-reference
+// smart pointer type (OwningNonNull or RefPtr or nsCOMPtr), in which case obj
+// can be anything that converts to JSObject*.
+#define UNWRAP_OBJECT(Interface, obj, value) \
+ mozilla::dom::UnwrapObject<mozilla::dom::prototypes::id::Interface, \
+ mozilla::dom::Interface##Binding::NativeType>(obj, value)
+
+// Test whether the given object is an instance of the given interface.
+#define IS_INSTANCE_OF(Interface, obj) \
+ mozilla::dom::IsInstanceOf<mozilla::dom::prototypes::id::Interface, \
+ mozilla::dom::Interface##Binding::NativeType>(obj)
+
+// Unwrap the given non-wrapper object. This can be used with any obj that
+// converts to JSObject*; as long as that JSObject* is live the return value
+// will be valid.
+#define UNWRAP_NON_WRAPPER_OBJECT(Interface, obj, value) \
+ mozilla::dom::UnwrapNonWrapperObject<mozilla::dom::prototypes::id::Interface, \
+ mozilla::dom::Interface##Binding::NativeType>(obj, value)
+
+// 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 RefPtr<T>).
+//
+// The obj argument will be mutated to point to CheckedUnwrap of itself if the
+// passed-in value is not a DOM object and CheckedUnwrap succeeds.
+//
+// If mayBeWrapper is true, there are three valid ways to invoke
+// UnwrapObjectInternal: Either obj needs to be a class wrapping a
+// MutableHandle<JSObject*>, with an assignment operator that sets the handle to
+// the given object, or U needs to be a strong-reference smart pointer type
+// (OwningNonNull or RefPtr or nsCOMPtr), or the value being stored in "value"
+// must not escape past being tested for falsiness immediately after the
+// UnwrapObjectInternal call.
+//
+// If mayBeWrapper is false, obj can just be a JSObject*, and U anything that a
+// T* can be assigned to.
+namespace binding_detail {
+template <class T, bool mayBeWrapper, typename U, typename V>
+MOZ_ALWAYS_INLINE nsresult
+UnwrapObjectInternal(V& obj, U& value, prototypes::ID protoID,
+ uint32_t protoDepth)
+{
+ /* First check to see whether we have a DOM object */
+ const DOMJSClass* domClass = GetDOMClass(obj);
+ if (domClass) {
+ /* 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[protoDepth] == protoID) {
+ value = UnwrapDOMObject<T>(obj);
+ return NS_OK;
+ }
+ }
+
+ /* Maybe we have a security wrapper or outer window? */
+ if (!mayBeWrapper || !js::IsWrapper(obj)) {
+ /* Not a DOM object, not a wrapper, just bail */
+ return NS_ERROR_XPC_BAD_CONVERT_JS;
+ }
+
+ JSObject* unwrappedObj =
+ js::CheckedUnwrap(obj, /* stopAtWindowProxy = */ false);
+ if (!unwrappedObj) {
+ return NS_ERROR_XPC_SECURITY_MANAGER_VETO;
+ }
+ MOZ_ASSERT(!js::IsWrapper(unwrappedObj));
+ // Recursive call is OK, because now we're using false for mayBeWrapper and
+ // we never reach this code if that boolean is false, so can't keep calling
+ // ourselves.
+ //
+ // Unwrap into a temporary pointer, because in general unwrapping into
+ // something of type U might trigger GC (e.g. release the value currently
+ // stored in there, with arbitrary consequences) and invalidate the
+ // "unwrappedObj" pointer.
+ T* tempValue;
+ nsresult rv = UnwrapObjectInternal<T, false>(unwrappedObj, tempValue,
+ protoID, protoDepth);
+ if (NS_SUCCEEDED(rv)) {
+ // It's very important to not update "obj" with the "unwrappedObj" value
+ // until we know the unwrap has succeeded. Otherwise, in a situation in
+ // which we have an overload of object and primitive we could end up
+ // converting to the primitive from the unwrappedObj, whereas we want to do
+ // it from the original object.
+ obj = unwrappedObj;
+ // And now assign to "value"; at this point we don't care if a GC happens
+ // and invalidates unwrappedObj.
+ value = tempValue;
+ return NS_OK;
+ }
+
+ /* It's the wrong sort of DOM object */
+ return NS_ERROR_XPC_BAD_CONVERT_JS;
+}
+
+struct MutableObjectHandleWrapper {
+ explicit MutableObjectHandleWrapper(JS::MutableHandle<JSObject*> aHandle)
+ : mHandle(aHandle)
+ {
+ }
+
+ void operator=(JSObject* aObject)
+ {
+ MOZ_ASSERT(aObject);
+ mHandle.set(aObject);
+ }
+
+ operator JSObject*() const
+ {
+ return mHandle;
+ }
+
+private:
+ JS::MutableHandle<JSObject*> mHandle;
+};
+
+struct MutableValueHandleWrapper {
+ explicit MutableValueHandleWrapper(JS::MutableHandle<JS::Value> aHandle)
+ : mHandle(aHandle)
+ {
+ }
+
+ void operator=(JSObject* aObject)
+ {
+ MOZ_ASSERT(aObject);
+ mHandle.setObject(*aObject);
+ }
+
+ operator JSObject*() const
+ {
+ return &mHandle.toObject();
+ }
+
+private:
+ JS::MutableHandle<JS::Value> mHandle;
+};
+
+} // namespace binding_detail
+
+// UnwrapObject overloads that ensure we have a MutableHandle to keep it alive.
+template<prototypes::ID PrototypeID, class T, typename U>
+MOZ_ALWAYS_INLINE nsresult
+UnwrapObject(JS::MutableHandle<JSObject*> obj, U& value)
+{
+ binding_detail::MutableObjectHandleWrapper wrapper(obj);
+ return binding_detail::UnwrapObjectInternal<T, true>(
+ wrapper, value, PrototypeID, PrototypeTraits<PrototypeID>::Depth);
+}
+
+template<prototypes::ID PrototypeID, class T, typename U>
+MOZ_ALWAYS_INLINE nsresult
+UnwrapObject(JS::MutableHandle<JS::Value> obj, U& value)
+{
+ MOZ_ASSERT(obj.isObject());
+ binding_detail::MutableValueHandleWrapper wrapper(obj);
+ return binding_detail::UnwrapObjectInternal<T, true>(
+ wrapper, value, PrototypeID, PrototypeTraits<PrototypeID>::Depth);
+}
+
+// UnwrapObject overloads that ensure we have a strong ref to keep it alive.
+template<prototypes::ID PrototypeID, class T, typename U>
+MOZ_ALWAYS_INLINE nsresult
+UnwrapObject(JSObject* obj, RefPtr<U>& value)
+{
+ return binding_detail::UnwrapObjectInternal<T, true>(
+ obj, value, PrototypeID, PrototypeTraits<PrototypeID>::Depth);
+}
+
+template<prototypes::ID PrototypeID, class T, typename U>
+MOZ_ALWAYS_INLINE nsresult
+UnwrapObject(JSObject* obj, nsCOMPtr<U>& value)
+{
+ return binding_detail::UnwrapObjectInternal<T, true>(
+ obj, value, PrototypeID, PrototypeTraits<PrototypeID>::Depth);
+}
+
+template<prototypes::ID PrototypeID, class T, typename U>
+MOZ_ALWAYS_INLINE nsresult
+UnwrapObject(JSObject* obj, OwningNonNull<U>& value)
+{
+ return binding_detail::UnwrapObjectInternal<T, true>(
+ obj, value, PrototypeID, PrototypeTraits<PrototypeID>::Depth);
+}
+
+// An UnwrapObject overload that just calls one of the JSObject* ones.
+template<prototypes::ID PrototypeID, class T, typename U>
+MOZ_ALWAYS_INLINE nsresult
+UnwrapObject(JS::Handle<JS::Value> obj, U& value)
+{
+ MOZ_ASSERT(obj.isObject());
+ return UnwrapObject<PrototypeID, T>(&obj.toObject(), value);
+}
+
+template<prototypes::ID PrototypeID, class T>
+MOZ_ALWAYS_INLINE bool
+IsInstanceOf(JSObject* obj)
+{
+ void* ignored;
+ nsresult unwrapped = binding_detail::UnwrapObjectInternal<T, true>(
+ obj, ignored, PrototypeID, PrototypeTraits<PrototypeID>::Depth);
+ return NS_SUCCEEDED(unwrapped);
+}
+
+template<prototypes::ID PrototypeID, class T, typename U>
+MOZ_ALWAYS_INLINE nsresult
+UnwrapNonWrapperObject(JSObject* obj, U& value)
+{
+ MOZ_ASSERT(!js::IsWrapper(obj));
+ return binding_detail::UnwrapObjectInternal<T, false>(
+ obj, value, PrototypeID, PrototypeTraits<PrototypeID>::Depth);
+}
+
+inline bool
+IsNotDateOrRegExp(JSContext* cx, JS::Handle<JSObject*> obj,
+ bool* notDateOrRegExp)
+{
+ MOZ_ASSERT(obj);
+
+ js::ESClass cls;
+ if (!js::GetBuiltinClass(cx, obj, &cls)) {
+ return false;
+ }
+
+ *notDateOrRegExp = cls != js::ESClass::Date && cls != js::ESClass::RegExp;
+ return true;
+}
+
+MOZ_ALWAYS_INLINE bool
+IsObjectValueConvertibleToDictionary(JSContext* cx,
+ JS::Handle<JS::Value> objVal,
+ bool* convertible)
+{
+ JS::Rooted<JSObject*> obj(cx, &objVal.toObject());
+ return IsNotDateOrRegExp(cx, obj, convertible);
+}
+
+MOZ_ALWAYS_INLINE bool
+IsConvertibleToDictionary(JSContext* cx, JS::Handle<JS::Value> val,
+ bool* convertible)
+{
+ if (val.isNullOrUndefined()) {
+ *convertible = true;
+ return true;
+ }
+ if (!val.isObject()) {
+ *convertible = false;
+ return true;
+ }
+ return IsObjectValueConvertibleToDictionary(cx, val, convertible);
+}
+
+MOZ_ALWAYS_INLINE bool
+IsConvertibleToCallbackInterface(JSContext* cx, JS::Handle<JSObject*> obj,
+ bool* convertible)
+{
+ return IsNotDateOrRegExp(cx, obj, convertible);
+}
+
+// The items in the protoAndIfaceCache are indexed by the prototypes::id::ID,
+// constructors::id::ID and namedpropertiesobjects::id::ID enums, in that order.
+// The end of the prototype objects should be the start of the interface
+// objects, and the end of the interface objects should be the start of the
+// named properties objects.
+static_assert((size_t)constructors::id::_ID_Start ==
+ (size_t)prototypes::id::_ID_Count &&
+ (size_t)namedpropertiesobjects::id::_ID_Start ==
+ (size_t)constructors::id::_ID_Count,
+ "Overlapping or discontiguous indexes.");
+const size_t kProtoAndIfaceCacheCount = namedpropertiesobjects::id::_ID_Count;
+
+class ProtoAndIfaceCache
+{
+ // The caching strategy we use depends on what sort of global we're dealing
+ // with. For a window-like global, we want everything to be as fast as
+ // possible, so we use a flat array, indexed by prototype/constructor ID.
+ // For everything else (e.g. globals for JSMs), space is more important than
+ // speed, so we use a two-level lookup table.
+
+ class ArrayCache : public Array<JS::Heap<JSObject*>, kProtoAndIfaceCacheCount>
+ {
+ public:
+ JSObject* EntrySlotIfExists(size_t i) {
+ return (*this)[i];
+ }
+
+ JS::Heap<JSObject*>& EntrySlotOrCreate(size_t i) {
+ return (*this)[i];
+ }
+
+ JS::Heap<JSObject*>& EntrySlotMustExist(size_t i) {
+ return (*this)[i];
+ }
+
+ void Trace(JSTracer* aTracer) {
+ for (size_t i = 0; i < ArrayLength(*this); ++i) {
+ JS::TraceEdge(aTracer, &(*this)[i], "protoAndIfaceCache[i]");
+ }
+ }
+
+ size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) {
+ return aMallocSizeOf(this);
+ }
+ };
+
+ class PageTableCache
+ {
+ public:
+ PageTableCache() {
+ memset(&mPages, 0, sizeof(mPages));
+ }
+
+ ~PageTableCache() {
+ for (size_t i = 0; i < ArrayLength(mPages); ++i) {
+ delete mPages[i];
+ }
+ }
+
+ JSObject* EntrySlotIfExists(size_t i) {
+ MOZ_ASSERT(i < kProtoAndIfaceCacheCount);
+ size_t pageIndex = i / kPageSize;
+ size_t leafIndex = i % kPageSize;
+ Page* p = mPages[pageIndex];
+ if (!p) {
+ return nullptr;
+ }
+ return (*p)[leafIndex];
+ }
+
+ JS::Heap<JSObject*>& EntrySlotOrCreate(size_t i) {
+ MOZ_ASSERT(i < kProtoAndIfaceCacheCount);
+ size_t pageIndex = i / kPageSize;
+ size_t leafIndex = i % kPageSize;
+ Page* p = mPages[pageIndex];
+ if (!p) {
+ p = new Page;
+ mPages[pageIndex] = p;
+ }
+ return (*p)[leafIndex];
+ }
+
+ JS::Heap<JSObject*>& EntrySlotMustExist(size_t i) {
+ MOZ_ASSERT(i < kProtoAndIfaceCacheCount);
+ size_t pageIndex = i / kPageSize;
+ size_t leafIndex = i % kPageSize;
+ Page* p = mPages[pageIndex];
+ MOZ_ASSERT(p);
+ return (*p)[leafIndex];
+ }
+
+ void Trace(JSTracer* trc) {
+ for (size_t i = 0; i < ArrayLength(mPages); ++i) {
+ Page* p = mPages[i];
+ if (p) {
+ for (size_t j = 0; j < ArrayLength(*p); ++j) {
+ JS::TraceEdge(trc, &(*p)[j], "protoAndIfaceCache[i]");
+ }
+ }
+ }
+ }
+
+ size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) {
+ size_t n = aMallocSizeOf(this);
+ for (size_t i = 0; i < ArrayLength(mPages); ++i) {
+ n += aMallocSizeOf(mPages[i]);
+ }
+ return n;
+ }
+
+ private:
+ static const size_t kPageSize = 16;
+ typedef Array<JS::Heap<JSObject*>, kPageSize> Page;
+ static const size_t kNPages = kProtoAndIfaceCacheCount / kPageSize +
+ size_t(bool(kProtoAndIfaceCacheCount % kPageSize));
+ Array<Page*, kNPages> mPages;
+ };
+
+public:
+ enum Kind {
+ WindowLike,
+ NonWindowLike
+ };
+
+ explicit ProtoAndIfaceCache(Kind aKind) : mKind(aKind) {
+ MOZ_COUNT_CTOR(ProtoAndIfaceCache);
+ if (aKind == WindowLike) {
+ mArrayCache = new ArrayCache();
+ } else {
+ mPageTableCache = new PageTableCache();
+ }
+ }
+
+ ~ProtoAndIfaceCache() {
+ if (mKind == WindowLike) {
+ delete mArrayCache;
+ } else {
+ delete mPageTableCache;
+ }
+ MOZ_COUNT_DTOR(ProtoAndIfaceCache);
+ }
+
+#define FORWARD_OPERATION(opName, args) \
+ do { \
+ if (mKind == WindowLike) { \
+ return mArrayCache->opName args; \
+ } else { \
+ return mPageTableCache->opName args; \
+ } \
+ } while(0)
+
+ // Return the JSObject stored in slot i, if that slot exists. If
+ // the slot does not exist, return null.
+ JSObject* EntrySlotIfExists(size_t i) {
+ FORWARD_OPERATION(EntrySlotIfExists, (i));
+ }
+
+ // Return a reference to slot i, creating it if necessary. There
+ // may not be an object in the returned slot.
+ JS::Heap<JSObject*>& EntrySlotOrCreate(size_t i) {
+ FORWARD_OPERATION(EntrySlotOrCreate, (i));
+ }
+
+ // Return a reference to slot i, which is guaranteed to already
+ // exist. There may not be an object in the slot, if prototype and
+ // constructor initialization for one of our bindings failed.
+ JS::Heap<JSObject*>& EntrySlotMustExist(size_t i) {
+ FORWARD_OPERATION(EntrySlotMustExist, (i));
+ }
+
+ void Trace(JSTracer *aTracer) {
+ FORWARD_OPERATION(Trace, (aTracer));
+ }
+
+ size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) {
+ size_t n = aMallocSizeOf(this);
+ n += (mKind == WindowLike
+ ? mArrayCache->SizeOfIncludingThis(aMallocSizeOf)
+ : mPageTableCache->SizeOfIncludingThis(aMallocSizeOf));
+ return n;
+ }
+#undef FORWARD_OPERATION
+
+private:
+ union {
+ ArrayCache *mArrayCache;
+ PageTableCache *mPageTableCache;
+ };
+ Kind mKind;
+};
+
+inline void
+AllocateProtoAndIfaceCache(JSObject* obj, ProtoAndIfaceCache::Kind aKind)
+{
+ MOZ_ASSERT(js::GetObjectClass(obj)->flags & JSCLASS_DOM_GLOBAL);
+ MOZ_ASSERT(js::GetReservedSlot(obj, DOM_PROTOTYPE_SLOT).isUndefined());
+
+ ProtoAndIfaceCache* protoAndIfaceCache = new ProtoAndIfaceCache(aKind);
+
+ js::SetReservedSlot(obj, DOM_PROTOTYPE_SLOT,
+ JS::PrivateValue(protoAndIfaceCache));
+}
+
+#ifdef DEBUG
+struct VerifyTraceProtoAndIfaceCacheCalledTracer : public JS::CallbackTracer
+{
+ bool ok;
+
+ explicit VerifyTraceProtoAndIfaceCacheCalledTracer(JSContext* cx)
+ : JS::CallbackTracer(cx), ok(false)
+ {}
+
+ void onChild(const JS::GCCellPtr&) override {
+ // We don't do anything here, we only want to verify that
+ // TraceProtoAndIfaceCache was called.
+ }
+
+ TracerKind getTracerKind() const override { return TracerKind::VerifyTraceProtoAndIface; }
+};
+#endif
+
+inline void
+TraceProtoAndIfaceCache(JSTracer* trc, JSObject* obj)
+{
+ MOZ_ASSERT(js::GetObjectClass(obj)->flags & JSCLASS_DOM_GLOBAL);
+
+#ifdef DEBUG
+ if (trc->isCallbackTracer() &&
+ (trc->asCallbackTracer()->getTracerKind() ==
+ JS::CallbackTracer::TracerKind::VerifyTraceProtoAndIface)) {
+ // We don't do anything here, we only want to verify that
+ // TraceProtoAndIfaceCache was called.
+ static_cast<VerifyTraceProtoAndIfaceCacheCalledTracer*>(trc)->ok = true;
+ return;
+ }
+#endif
+
+ if (!DOMGlobalHasProtoAndIFaceCache(obj))
+ return;
+ ProtoAndIfaceCache* protoAndIfaceCache = GetProtoAndIfaceCache(obj);
+ protoAndIfaceCache->Trace(trc);
+}
+
+inline void
+DestroyProtoAndIfaceCache(JSObject* obj)
+{
+ MOZ_ASSERT(js::GetObjectClass(obj)->flags & JSCLASS_DOM_GLOBAL);
+
+ if (!DOMGlobalHasProtoAndIFaceCache(obj)) {
+ return;
+ }
+
+ ProtoAndIfaceCache* protoAndIfaceCache = GetProtoAndIfaceCache(obj);
+
+ delete protoAndIfaceCache;
+}
+
+/**
+ * Add constants to an object.
+ */
+bool
+DefineConstants(JSContext* cx, JS::Handle<JSObject*> obj,
+ const ConstantSpec* cs);
+
+struct JSNativeHolder
+{
+ JSNative mNative;
+ const NativePropertyHooks* mPropertyHooks;
+};
+
+struct NamedConstructor
+{
+ const char* mName;
+ const JSNativeHolder mHolder;
+ unsigned mNargs;
+};
+
+/*
+ * 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
+ * protoProto is the prototype to use for the interface prototype object.
+ * interfaceProto is the prototype to use for the interface object. This can be
+ * null if both constructorClass and constructor are null (as in,
+ * if we're not creating an interface object at all).
+ * protoClass is the JSClass to use for the interface prototype object.
+ * This is null if we should not create an interface prototype
+ * object.
+ * protoCache a pointer to a JSObject pointer where we should cache the
+ * interface prototype object. This must be null if protoClass is and
+ * vice versa.
+ * 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 holds the JSNative to back the interface object which should be a
+ * Function, unless constructorClass is non-null in which case it is
+ * ignored. If this is null and constructorClass is also null then
+ * we should not create an interface object at all.
+ * ctorNargs is the length of the constructor function; 0 if no constructor
+ * constructorCache a pointer to a JSObject pointer where we should cache the
+ * interface object. This must be null if both constructorClass
+ * and constructor are null, and non-null otherwise.
+ * properties contains the methods, attributes and constants to be defined on
+ * objects in any compartment.
+ * chromeProperties contains the methods, attributes and constants to be defined
+ * on objects in chrome compartments. This must be null if the
+ * interface doesn't have any ChromeOnly properties or if the
+ * object is being created in non-chrome compartment.
+ * defineOnGlobal controls whether properties should be defined on the given
+ * global for the interface object (if any) and named
+ * constructors (if any) for this interface. This can be
+ * false in situations where we want the properties to only
+ * appear on privileged Xrays but not on the unprivileged
+ * underlying global.
+ * unscopableNames if not null it points to a null-terminated list of const
+ * char* names of the unscopable properties for this interface.
+ * isGlobal if true, we're creating interface objects for a [Global] or
+ * [PrimaryGlobal] interface, and hence shouldn't define properties on
+ * the prototype object.
+ *
+ * At least one of protoClass, constructorClass or constructor should be
+ * non-null. If constructorClass or constructor are non-null, the resulting
+ * interface object will be defined on the given global with property name
+ * |name|, which must also be non-null.
+ */
+void
+CreateInterfaceObjects(JSContext* cx, JS::Handle<JSObject*> global,
+ JS::Handle<JSObject*> protoProto,
+ const js::Class* protoClass, JS::Heap<JSObject*>* protoCache,
+ JS::Handle<JSObject*> interfaceProto,
+ const js::Class* constructorClass,
+ unsigned ctorNargs, const NamedConstructor* namedConstructors,
+ JS::Heap<JSObject*>* constructorCache,
+ const NativeProperties* regularProperties,
+ const NativeProperties* chromeOnlyProperties,
+ const char* name, bool defineOnGlobal,
+ const char* const* unscopableNames,
+ bool isGlobal);
+
+/**
+ * Define the properties (regular and chrome-only) on obj.
+ *
+ * obj the object to instal the properties on. This should be the interface
+ * prototype object for regular interfaces and the instance object for
+ * interfaces marked with Global.
+ * properties contains the methods, attributes and constants to be defined on
+ * objects in any compartment.
+ * chromeProperties contains the methods, attributes and constants to be defined
+ * on objects in chrome compartments. This must be null if the
+ * interface doesn't have any ChromeOnly properties or if the
+ * object is being created in non-chrome compartment.
+ */
+bool
+DefineProperties(JSContext* cx, JS::Handle<JSObject*> obj,
+ const NativeProperties* properties,
+ const NativeProperties* chromeOnlyProperties);
+
+/*
+ * Define the unforgeable methods on an object.
+ */
+bool
+DefineUnforgeableMethods(JSContext* cx, JS::Handle<JSObject*> obj,
+ const Prefable<const JSFunctionSpec>* props);
+
+/*
+ * Define the unforgeable attributes on an object.
+ */
+bool
+DefineUnforgeableAttributes(JSContext* cx, JS::Handle<JSObject*> obj,
+ const Prefable<const JSPropertySpec>* props);
+
+#define HAS_MEMBER_TYPEDEFS \
+private: \
+ typedef char yes[1]; \
+ typedef char no[2]
+
+#ifdef _MSC_VER
+#define HAS_MEMBER_CHECK(_name) \
+ template<typename V> static yes& Check##_name(char (*)[(&V::_name == 0) + 1])
+#else
+#define HAS_MEMBER_CHECK(_name) \
+ template<typename V> static yes& Check##_name(char (*)[sizeof(&V::_name) + 1])
+#endif
+
+#define HAS_MEMBER(_memberName, _valueName) \
+private: \
+ HAS_MEMBER_CHECK(_memberName); \
+ template<typename V> static no& Check##_memberName(...); \
+ \
+public: \
+ static bool const _valueName = \
+ sizeof(Check##_memberName<T>(nullptr)) == sizeof(yes)
+
+template<class T>
+struct NativeHasMember
+{
+ HAS_MEMBER_TYPEDEFS;
+
+ HAS_MEMBER(GetParentObject, GetParentObject);
+ HAS_MEMBER(WrapObject, WrapObject);
+};
+
+template<class T>
+struct IsSmartPtr
+{
+ HAS_MEMBER_TYPEDEFS;
+
+ HAS_MEMBER(get, value);
+};
+
+template<class T>
+struct IsRefcounted
+{
+ HAS_MEMBER_TYPEDEFS;
+
+ HAS_MEMBER(AddRef, HasAddref);
+ HAS_MEMBER(Release, HasRelease);
+
+public:
+ static bool const value = HasAddref && HasRelease;
+
+private:
+ // This struct only works if T is fully declared (not just forward declared).
+ // The IsBaseOf check will ensure that, we don't really need it for any other
+ // reason (the static assert will of course always be true).
+ static_assert(!IsBaseOf<nsISupports, T>::value || IsRefcounted::value,
+ "Classes derived from nsISupports are refcounted!");
+
+};
+
+#undef HAS_MEMBER
+#undef HAS_MEMBER_CHECK
+#undef HAS_MEMBER_TYPEDEFS
+
+#ifdef DEBUG
+template <class T, bool isISupports=IsBaseOf<nsISupports, T>::value>
+struct
+CheckWrapperCacheCast
+{
+ static bool Check()
+ {
+ return reinterpret_cast<uintptr_t>(
+ static_cast<nsWrapperCache*>(
+ reinterpret_cast<T*>(1))) == 1;
+ }
+};
+template <class T>
+struct
+CheckWrapperCacheCast<T, true>
+{
+ static bool Check()
+ {
+ return true;
+ }
+};
+#endif
+
+MOZ_ALWAYS_INLINE bool
+CouldBeDOMBinding(void*)
+{
+ return true;
+}
+
+MOZ_ALWAYS_INLINE bool
+CouldBeDOMBinding(nsWrapperCache* aCache)
+{
+ return aCache->IsDOMBinding();
+}
+
+inline bool
+TryToOuterize(JS::MutableHandle<JS::Value> rval)
+{
+ if (js::IsWindow(&rval.toObject())) {
+ JSObject* obj = js::ToWindowProxyIfWindow(&rval.toObject());
+ MOZ_ASSERT(obj);
+ rval.set(JS::ObjectValue(*obj));
+ }
+
+ return true;
+}
+
+// Make sure to wrap the given string value into the right compartment, as
+// needed.
+MOZ_ALWAYS_INLINE
+bool
+MaybeWrapStringValue(JSContext* cx, JS::MutableHandle<JS::Value> rval)
+{
+ MOZ_ASSERT(rval.isString());
+ JSString* str = rval.toString();
+ if (JS::GetStringZone(str) != js::GetContextZone(cx)) {
+ return JS_WrapValue(cx, rval);
+ }
+ return true;
+}
+
+// Make sure to wrap the given object value into the right compartment as
+// needed. This will work correctly, but possibly slowly, on all objects.
+MOZ_ALWAYS_INLINE
+bool
+MaybeWrapObjectValue(JSContext* cx, JS::MutableHandle<JS::Value> rval)
+{
+ MOZ_ASSERT(rval.isObject());
+
+ // Cross-compartment always requires wrapping.
+ JSObject* obj = &rval.toObject();
+ if (js::GetObjectCompartment(obj) != js::GetContextCompartment(cx)) {
+ return JS_WrapValue(cx, rval);
+ }
+
+ // We're same-compartment, but even then we might need to wrap
+ // objects specially. Check for that.
+ if (IsDOMObject(obj)) {
+ return TryToOuterize(rval);
+ }
+
+ // It's not a WebIDL object, so it's OK to just leave it as-is: only WebIDL
+ // objects (specifically only windows) require outerization.
+ return true;
+}
+
+// Like MaybeWrapObjectValue, but also allows null
+MOZ_ALWAYS_INLINE
+bool
+MaybeWrapObjectOrNullValue(JSContext* cx, JS::MutableHandle<JS::Value> rval)
+{
+ MOZ_ASSERT(rval.isObjectOrNull());
+ if (rval.isNull()) {
+ return true;
+ }
+ return MaybeWrapObjectValue(cx, rval);
+}
+
+// Wrapping for objects that are known to not be DOM or XPConnect objects
+MOZ_ALWAYS_INLINE
+bool
+MaybeWrapNonDOMObjectValue(JSContext* cx, JS::MutableHandle<JS::Value> rval)
+{
+ MOZ_ASSERT(rval.isObject());
+ MOZ_ASSERT(!GetDOMClass(&rval.toObject()));
+ MOZ_ASSERT(!(js::GetObjectClass(&rval.toObject())->flags &
+ JSCLASS_PRIVATE_IS_NSISUPPORTS));
+
+ JSObject* obj = &rval.toObject();
+ if (js::GetObjectCompartment(obj) == js::GetContextCompartment(cx)) {
+ return true;
+ }
+ return JS_WrapValue(cx, rval);
+}
+
+// Like MaybeWrapNonDOMObjectValue but allows null
+MOZ_ALWAYS_INLINE
+bool
+MaybeWrapNonDOMObjectOrNullValue(JSContext* cx, JS::MutableHandle<JS::Value> rval)
+{
+ MOZ_ASSERT(rval.isObjectOrNull());
+ if (rval.isNull()) {
+ return true;
+ }
+ return MaybeWrapNonDOMObjectValue(cx, rval);
+}
+
+// If rval is a gcthing and is not in the compartment of cx, wrap rval
+// into the compartment of cx (typically by replacing it with an Xray or
+// cross-compartment wrapper around the original object).
+MOZ_ALWAYS_INLINE bool
+MaybeWrapValue(JSContext* cx, JS::MutableHandle<JS::Value> rval)
+{
+ if (rval.isString()) {
+ return MaybeWrapStringValue(cx, rval);
+ }
+
+ if (!rval.isObject()) {
+ return true;
+ }
+
+ return MaybeWrapObjectValue(cx, rval);
+}
+
+namespace binding_detail {
+enum GetOrCreateReflectorWrapBehavior {
+ eWrapIntoContextCompartment,
+ eDontWrapIntoContextCompartment
+};
+
+template <class T>
+struct TypeNeedsOuterization
+{
+ // We only need to outerize Window objects, so anything inheriting from
+ // nsGlobalWindow (which inherits from EventTarget itself).
+ static const bool value =
+ IsBaseOf<nsGlobalWindow, T>::value || IsSame<EventTarget, T>::value;
+};
+
+#ifdef DEBUG
+template<typename T, bool isISupports=IsBaseOf<nsISupports, T>::value>
+struct CheckWrapperCacheTracing
+{
+ static inline void Check(T* aObject)
+ {
+ }
+};
+
+template<typename T>
+struct CheckWrapperCacheTracing<T, true>
+{
+ static void Check(T* aObject)
+ {
+ // Rooting analysis thinks QueryInterface may GC, but we're dealing with
+ // a subset of QueryInterface, C++ only types here.
+ JS::AutoSuppressGCAnalysis nogc;
+
+ nsWrapperCache* wrapperCacheFromQI = nullptr;
+ aObject->QueryInterface(NS_GET_IID(nsWrapperCache),
+ reinterpret_cast<void**>(&wrapperCacheFromQI));
+
+ MOZ_ASSERT(wrapperCacheFromQI,
+ "Missing nsWrapperCache from QueryInterface implementation?");
+
+ if (!wrapperCacheFromQI->GetWrapperPreserveColor()) {
+ // Can't assert that we trace the wrapper, since we don't have any
+ // wrapper to trace.
+ return;
+ }
+
+ nsISupports* ccISupports = nullptr;
+ aObject->QueryInterface(NS_GET_IID(nsCycleCollectionISupports),
+ reinterpret_cast<void**>(&ccISupports));
+ MOZ_ASSERT(ccISupports,
+ "nsWrapperCache object which isn't cycle collectable?");
+
+ nsXPCOMCycleCollectionParticipant* participant = nullptr;
+ CallQueryInterface(ccISupports, &participant);
+ MOZ_ASSERT(participant, "Can't QI to CycleCollectionParticipant?");
+
+ bool wasPreservingWrapper = wrapperCacheFromQI->PreservingWrapper();
+ wrapperCacheFromQI->SetPreservingWrapper(true);
+ wrapperCacheFromQI->CheckCCWrapperTraversal(ccISupports, participant);
+ wrapperCacheFromQI->SetPreservingWrapper(wasPreservingWrapper);
+ }
+};
+
+void
+AssertReflectorHasGivenProto(JSContext* aCx, JSObject* aReflector,
+ JS::Handle<JSObject*> aGivenProto);
+#endif // DEBUG
+
+template <class T, GetOrCreateReflectorWrapBehavior wrapBehavior>
+MOZ_ALWAYS_INLINE bool
+DoGetOrCreateDOMReflector(JSContext* cx, T* value,
+ JS::Handle<JSObject*> givenProto,
+ JS::MutableHandle<JS::Value> rval)
+{
+ MOZ_ASSERT(value);
+ // We can get rid of this when we remove support for hasXPConnectImpls.
+ bool couldBeDOMBinding = CouldBeDOMBinding(value);
+ JSObject* obj = value->GetWrapper();
+ if (obj) {
+#ifdef DEBUG
+ AssertReflectorHasGivenProto(cx, obj, givenProto);
+ // Have to reget obj because AssertReflectorHasGivenProto can
+ // trigger gc so the pointer may now be invalid.
+ obj = value->GetWrapper();
+#endif
+ } else {
+ // Inline this here while we have non-dom objects in wrapper caches.
+ if (!couldBeDOMBinding) {
+ return false;
+ }
+
+ obj = value->WrapObject(cx, givenProto);
+ if (!obj) {
+ // At this point, obj is null, so just return false.
+ // Callers seem to be testing JS_IsExceptionPending(cx) to
+ // figure out whether WrapObject() threw.
+ return false;
+ }
+
+#ifdef DEBUG
+ if (IsBaseOf<nsWrapperCache, T>::value) {
+ CheckWrapperCacheTracing<T>::Check(value);
+ }
+#endif
+ }
+
+#ifdef DEBUG
+ const DOMJSClass* clasp = GetDOMClass(obj);
+ // clasp can be null if the cache contained a non-DOM object.
+ if (clasp) {
+ // Some sanity asserts about our object. Specifically:
+ // 1) If our class claims we're nsISupports, we better be nsISupports
+ // XXXbz ideally, we could assert that reinterpret_cast to nsISupports
+ // does the right thing, but I don't see a way to do it. :(
+ // 2) If our class doesn't claim we're nsISupports we better be
+ // reinterpret_castable to nsWrapperCache.
+ MOZ_ASSERT(clasp, "What happened here?");
+ MOZ_ASSERT_IF(clasp->mDOMObjectIsISupports, (IsBaseOf<nsISupports, T>::value));
+ MOZ_ASSERT(CheckWrapperCacheCast<T>::Check());
+ }
+#endif
+
+ rval.set(JS::ObjectValue(*obj));
+
+ bool sameCompartment =
+ js::GetObjectCompartment(obj) == js::GetContextCompartment(cx);
+ if (sameCompartment && couldBeDOMBinding) {
+ return TypeNeedsOuterization<T>::value ? TryToOuterize(rval) : true;
+ }
+
+ if (wrapBehavior == eDontWrapIntoContextCompartment) {
+ if (TypeNeedsOuterization<T>::value) {
+ JSAutoCompartment ac(cx, obj);
+ return TryToOuterize(rval);
+ }
+
+ return true;
+ }
+
+ return JS_WrapValue(cx, rval);
+}
+
+} // namespace binding_detail
+
+// Create a JSObject wrapping "value", if there isn't one already, and store it
+// in rval. "value" must be a concrete class that implements a
+// GetWrapperPreserveColor() which can return its existing wrapper, if any, and
+// a WrapObject() which will try to create a wrapper. Typically, this is done by
+// having "value" inherit from nsWrapperCache.
+//
+// The value stored in rval will be ready to be exposed to whatever JS
+// is running on cx right now. In particular, it will be in the
+// compartment of cx, and outerized as needed.
+template <class T>
+MOZ_ALWAYS_INLINE bool
+GetOrCreateDOMReflector(JSContext* cx, T* value,
+ JS::MutableHandle<JS::Value> rval,
+ JS::Handle<JSObject*> givenProto = nullptr)
+{
+ using namespace binding_detail;
+ return DoGetOrCreateDOMReflector<T, eWrapIntoContextCompartment>(cx, value,
+ givenProto,
+ rval);
+}
+
+// Like GetOrCreateDOMReflector but doesn't wrap into the context compartment,
+// and hence does not actually require cx to be in a compartment.
+template <class T>
+MOZ_ALWAYS_INLINE bool
+GetOrCreateDOMReflectorNoWrap(JSContext* cx, T* value,
+ JS::MutableHandle<JS::Value> rval)
+{
+ using namespace binding_detail;
+ return DoGetOrCreateDOMReflector<T, eDontWrapIntoContextCompartment>(cx,
+ value,
+ nullptr,
+ rval);
+}
+
+// Create a JSObject wrapping "value", for cases when "value" is a
+// non-wrapper-cached object using WebIDL bindings. "value" must implement a
+// WrapObject() method taking a JSContext and a scope.
+template <class T>
+inline bool
+WrapNewBindingNonWrapperCachedObject(JSContext* cx,
+ JS::Handle<JSObject*> scopeArg,
+ T* value,
+ JS::MutableHandle<JS::Value> rval,
+ JS::Handle<JSObject*> givenProto = nullptr)
+{
+ static_assert(IsRefcounted<T>::value, "Don't pass owned classes in here.");
+ MOZ_ASSERT(value);
+ // We try to wrap in the compartment of the underlying object of "scope"
+ JS::Rooted<JSObject*> obj(cx);
+ {
+ // scope for the JSAutoCompartment so that we restore the compartment
+ // before we call JS_WrapValue.
+ Maybe<JSAutoCompartment> ac;
+ // Maybe<Handle> doesn't so much work, and in any case, adding
+ // more Maybe (one for a Rooted and one for a Handle) adds more
+ // code (and branches!) than just adding a single rooted.
+ JS::Rooted<JSObject*> scope(cx, scopeArg);
+ JS::Rooted<JSObject*> proto(cx, givenProto);
+ if (js::IsWrapper(scope)) {
+ scope = js::CheckedUnwrap(scope, /* stopAtWindowProxy = */ false);
+ if (!scope)
+ return false;
+ ac.emplace(cx, scope);
+ if (!JS_WrapObject(cx, &proto)) {
+ return false;
+ }
+ }
+
+ MOZ_ASSERT(js::IsObjectInContextCompartment(scope, cx));
+ if (!value->WrapObject(cx, proto, &obj)) {
+ return false;
+ }
+ }
+
+ // We can end up here in all sorts of compartments, per above. Make
+ // sure to JS_WrapValue!
+ rval.set(JS::ObjectValue(*obj));
+ return MaybeWrapObjectValue(cx, rval);
+}
+
+// Create a JSObject wrapping "value", for cases when "value" is a
+// non-wrapper-cached owned object using WebIDL bindings. "value" must implement a
+// WrapObject() method taking a JSContext, a scope, and a boolean outparam that
+// is true if the JSObject took ownership
+template <class T>
+inline bool
+WrapNewBindingNonWrapperCachedObject(JSContext* cx,
+ JS::Handle<JSObject*> scopeArg,
+ nsAutoPtr<T>& value,
+ JS::MutableHandle<JS::Value> rval,
+ JS::Handle<JSObject*> givenProto = nullptr)
+{
+ static_assert(!IsRefcounted<T>::value, "Only pass owned classes in here.");
+ // We do a runtime check on value, because otherwise we might in
+ // fact end up wrapping a null and invoking methods on it later.
+ if (!value) {
+ NS_RUNTIMEABORT("Don't try to wrap null objects");
+ }
+ // We try to wrap in the compartment of the underlying object of "scope"
+ JS::Rooted<JSObject*> obj(cx);
+ {
+ // scope for the JSAutoCompartment so that we restore the compartment
+ // before we call JS_WrapValue.
+ Maybe<JSAutoCompartment> ac;
+ // Maybe<Handle> doesn't so much work, and in any case, adding
+ // more Maybe (one for a Rooted and one for a Handle) adds more
+ // code (and branches!) than just adding a single rooted.
+ JS::Rooted<JSObject*> scope(cx, scopeArg);
+ JS::Rooted<JSObject*> proto(cx, givenProto);
+ if (js::IsWrapper(scope)) {
+ scope = js::CheckedUnwrap(scope, /* stopAtWindowProxy = */ false);
+ if (!scope)
+ return false;
+ ac.emplace(cx, scope);
+ if (!JS_WrapObject(cx, &proto)) {
+ return false;
+ }
+ }
+
+ MOZ_ASSERT(js::IsObjectInContextCompartment(scope, cx));
+ if (!value->WrapObject(cx, proto, &obj)) {
+ return false;
+ }
+
+ value.forget();
+ }
+
+ // We can end up here in all sorts of compartments, per above. Make
+ // sure to JS_WrapValue!
+ rval.set(JS::ObjectValue(*obj));
+ return MaybeWrapObjectValue(cx, rval);
+}
+
+// Helper for smart pointers (nsRefPtr/nsCOMPtr).
+template <template <typename> class SmartPtr, typename T,
+ typename U=typename EnableIf<IsRefcounted<T>::value, T>::Type,
+ typename V=typename EnableIf<IsSmartPtr<SmartPtr<T>>::value, T>::Type>
+inline bool
+WrapNewBindingNonWrapperCachedObject(JSContext* cx, JS::Handle<JSObject*> scope,
+ const SmartPtr<T>& value,
+ JS::MutableHandle<JS::Value> rval,
+ JS::Handle<JSObject*> givenProto = nullptr)
+{
+ return WrapNewBindingNonWrapperCachedObject(cx, scope, value.get(), rval,
+ givenProto);
+}
+
+// Helper for object references (as opposed to pointers).
+template <typename T,
+ typename U=typename EnableIf<!IsSmartPtr<T>::value, T>::Type>
+inline bool
+WrapNewBindingNonWrapperCachedObject(JSContext* cx, JS::Handle<JSObject*> scope,
+ T& value,
+ JS::MutableHandle<JS::Value> rval,
+ JS::Handle<JSObject*> givenProto = nullptr)
+{
+ return WrapNewBindingNonWrapperCachedObject(cx, scope, &value, rval,
+ givenProto);
+}
+
+// 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
+NativeInterface2JSObjectAndThrowIfFailed(JSContext* aCx,
+ JS::Handle<JSObject*> aScope,
+ JS::MutableHandle<JS::Value> aRetval,
+ xpcObjectHelper& aHelper,
+ const nsIID* aIID,
+ bool aAllowNativeWrapper);
+
+/**
+ * A method to handle new-binding wrap failure, by possibly falling back to
+ * wrapping as a non-new-binding object.
+ */
+template <class T>
+MOZ_ALWAYS_INLINE bool
+HandleNewBindingWrappingFailure(JSContext* cx, JS::Handle<JSObject*> scope,
+ T* value, JS::MutableHandle<JS::Value> rval)
+{
+ if (JS_IsExceptionPending(cx)) {
+ return false;
+ }
+
+ qsObjectHelper helper(value, GetWrapperCache(value));
+ return NativeInterface2JSObjectAndThrowIfFailed(cx, scope, rval,
+ helper, nullptr, true);
+}
+
+// Helper for calling HandleNewBindingWrappingFailure with smart pointers
+// (nsAutoPtr/nsRefPtr/nsCOMPtr) or references.
+
+template <class T, bool isSmartPtr=IsSmartPtr<T>::value>
+struct HandleNewBindingWrappingFailureHelper
+{
+ static inline bool Wrap(JSContext* cx, JS::Handle<JSObject*> scope,
+ const T& value, JS::MutableHandle<JS::Value> rval)
+ {
+ return HandleNewBindingWrappingFailure(cx, scope, value.get(), rval);
+ }
+};
+
+template <class T>
+struct HandleNewBindingWrappingFailureHelper<T, false>
+{
+ static inline bool Wrap(JSContext* cx, JS::Handle<JSObject*> scope, T& value,
+ JS::MutableHandle<JS::Value> rval)
+ {
+ return HandleNewBindingWrappingFailure(cx, scope, &value, rval);
+ }
+};
+
+template<class T>
+inline bool
+HandleNewBindingWrappingFailure(JSContext* cx, JS::Handle<JSObject*> scope,
+ T& value, JS::MutableHandle<JS::Value> rval)
+{
+ return HandleNewBindingWrappingFailureHelper<T>::Wrap(cx, scope, value, rval);
+}
+
+template<bool Fatal>
+inline bool
+EnumValueNotFound(JSContext* cx, JS::HandleString str, const char* type,
+ const char* sourceDescription);
+
+template<>
+inline bool
+EnumValueNotFound<false>(JSContext* cx, JS::HandleString str, const char* type,
+ const char* sourceDescription)
+{
+ // TODO: Log a warning to the console.
+ return true;
+}
+
+template<>
+inline bool
+EnumValueNotFound<true>(JSContext* cx, JS::HandleString str, const char* type,
+ const char* sourceDescription)
+{
+ JSAutoByteString deflated;
+ if (!deflated.encodeUtf8(cx, str)) {
+ return false;
+ }
+ return ThrowErrorMessage(cx, MSG_INVALID_ENUM_VALUE, sourceDescription,
+ deflated.ptr(), type);
+}
+
+template<typename CharT>
+inline int
+FindEnumStringIndexImpl(const CharT* chars, size_t length, const EnumEntry* values)
+{
+ 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) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+template<bool InvalidValueFatal>
+inline bool
+FindEnumStringIndex(JSContext* cx, JS::Handle<JS::Value> v, const EnumEntry* values,
+ const char* type, const char* sourceDescription, int* index)
+{
+ // JS_StringEqualsAscii is slow as molasses, so don't use it here.
+ JS::RootedString str(cx, JS::ToString(cx, v));
+ if (!str) {
+ return false;
+ }
+
+ {
+ size_t length;
+ JS::AutoCheckCannotGC nogc;
+ if (js::StringHasLatin1Chars(str)) {
+ const JS::Latin1Char* chars = JS_GetLatin1StringCharsAndLength(cx, nogc, str,
+ &length);
+ if (!chars) {
+ return false;
+ }
+ *index = FindEnumStringIndexImpl(chars, length, values);
+ } else {
+ const char16_t* chars = JS_GetTwoByteStringCharsAndLength(cx, nogc, str,
+ &length);
+ if (!chars) {
+ return false;
+ }
+ *index = FindEnumStringIndexImpl(chars, length, values);
+ }
+ if (*index >= 0) {
+ return true;
+ }
+ }
+
+ return EnumValueNotFound<InvalidValueFatal>(cx, str, type, sourceDescription);
+}
+
+inline nsWrapperCache*
+GetWrapperCache(const ParentObject& aParentObject)
+{
+ return aParentObject.mWrapperCache;
+}
+
+template<class T>
+inline T*
+GetParentPointer(T* aObject)
+{
+ return aObject;
+}
+
+inline nsISupports*
+GetParentPointer(const ParentObject& aObject)
+{
+ return aObject.mObject;
+}
+
+template <typename T>
+inline bool
+GetUseXBLScope(T* aParentObject)
+{
+ return false;
+}
+
+inline bool
+GetUseXBLScope(const ParentObject& aParentObject)
+{
+ return aParentObject.mUseXBLScope;
+}
+
+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);
+}
+
+template<class T>
+inline void
+UpdateWrapper(T* p, nsWrapperCache* cache, JSObject* obj, const JSObject* old)
+{
+ JS::AutoAssertGCCallback inCallback(obj);
+ cache->UpdateWrapper(obj, old);
+}
+
+template<class T>
+inline void
+UpdateWrapper(T* p, void*, JSObject* obj, const JSObject* old)
+{
+ JS::AutoAssertGCCallback inCallback(obj);
+ nsWrapperCache* cache;
+ CallQueryInterface(p, &cache);
+ UpdateWrapper(p, cache, obj, old);
+}
+
+// Attempt to preserve the wrapper, if any, for a Paris DOM bindings object.
+// Return true if we successfully preserved the wrapper, or there is no wrapper
+// to preserve. In the latter case we don't need to preserve the wrapper, because
+// the object can only be obtained by JS once, or they cannot be meaningfully
+// owned from the native side.
+//
+// This operation will return false only for non-nsISupports cycle-collected
+// objects, because we cannot determine if they are wrappercached or not.
+bool
+TryPreserveWrapper(JSObject* obj);
+
+// Can only be called with a DOM JSClass.
+bool
+InstanceClassHasProtoAtDepth(const js::Class* clasp,
+ 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, JS::Handle<JSObject*> scope,
+ xpcObjectHelper& helper, const nsIID* iid,
+ bool allowNativeWrapper, JS::MutableHandle<JS::Value> rval);
+
+// Special-cased wrapping for variants
+bool
+VariantToJsval(JSContext* aCx, nsIVariant* aVariant,
+ JS::MutableHandle<JS::Value> aRetval);
+
+// Wrap an object "p" which is not using WebIDL bindings yet. This _will_
+// actually work on WebIDL binding objects that are wrappercached, but will be
+// much slower than GetOrCreateDOMReflector. "cache" must either be null or be
+// the nsWrapperCache for "p".
+template<class T>
+inline bool
+WrapObject(JSContext* cx, T* p, nsWrapperCache* cache, const nsIID* iid,
+ JS::MutableHandle<JS::Value> rval)
+{
+ if (xpc_FastGetCachedWrapper(cx, cache, rval))
+ return true;
+ qsObjectHelper helper(p, cache);
+ JS::Rooted<JSObject*> scope(cx, JS::CurrentGlobalOrNull(cx));
+ return XPCOMObjectToJsval(cx, scope, helper, iid, true, rval);
+}
+
+// A specialization of the above for nsIVariant, because that needs to
+// do something different.
+template<>
+inline bool
+WrapObject<nsIVariant>(JSContext* cx, nsIVariant* p,
+ nsWrapperCache* cache, const nsIID* iid,
+ JS::MutableHandle<JS::Value> rval)
+{
+ MOZ_ASSERT(iid);
+ MOZ_ASSERT(iid->Equals(NS_GET_IID(nsIVariant)));
+ return VariantToJsval(cx, p, rval);
+}
+
+// Wrap an object "p" which is not using WebIDL bindings yet. Just like the
+// variant that takes an nsWrapperCache above, but will try to auto-derive the
+// nsWrapperCache* from "p".
+template<class T>
+inline bool
+WrapObject(JSContext* cx, T* p, const nsIID* iid,
+ JS::MutableHandle<JS::Value> rval)
+{
+ return WrapObject(cx, p, GetWrapperCache(p), iid, rval);
+}
+
+// Just like the WrapObject above, but without requiring you to pick which
+// interface you're wrapping as. This should only be used for objects that have
+// classinfo, for which it doesn't matter what IID is used to wrap.
+template<class T>
+inline bool
+WrapObject(JSContext* cx, T* p, JS::MutableHandle<JS::Value> rval)
+{
+ return WrapObject(cx, p, nullptr, rval);
+}
+
+// Helper to make it possible to wrap directly out of an nsCOMPtr
+template<class T>
+inline bool
+WrapObject(JSContext* cx, const nsCOMPtr<T>& p,
+ const nsIID* iid, JS::MutableHandle<JS::Value> rval)
+{
+ return WrapObject(cx, p.get(), iid, rval);
+}
+
+// Helper to make it possible to wrap directly out of an nsCOMPtr
+template<class T>
+inline bool
+WrapObject(JSContext* cx, const nsCOMPtr<T>& p,
+ JS::MutableHandle<JS::Value> rval)
+{
+ return WrapObject(cx, p, nullptr, rval);
+}
+
+// Helper to make it possible to wrap directly out of an nsRefPtr
+template<class T>
+inline bool
+WrapObject(JSContext* cx, const RefPtr<T>& p,
+ const nsIID* iid, JS::MutableHandle<JS::Value> rval)
+{
+ return WrapObject(cx, p.get(), iid, rval);
+}
+
+// Helper to make it possible to wrap directly out of an nsRefPtr
+template<class T>
+inline bool
+WrapObject(JSContext* cx, const RefPtr<T>& p,
+ JS::MutableHandle<JS::Value> rval)
+{
+ return WrapObject(cx, p, nullptr, rval);
+}
+
+// Specialization to make it easy to use WrapObject in codegen.
+template<>
+inline bool
+WrapObject<JSObject>(JSContext* cx, JSObject* p,
+ JS::MutableHandle<JS::Value> rval)
+{
+ rval.set(JS::ObjectOrNullValue(p));
+ return true;
+}
+
+inline bool
+WrapObject(JSContext* cx, JSObject& p, JS::MutableHandle<JS::Value> rval)
+{
+ rval.set(JS::ObjectValue(p));
+ return true;
+}
+
+// Given an object "p" that inherits from nsISupports, wrap it and return the
+// result. Null is returned on wrapping failure. This is somewhat similar to
+// WrapObject() above, but does NOT allow Xrays around the result, since we
+// don't want those for our parent object.
+template<typename T>
+static inline JSObject*
+WrapNativeISupports(JSContext* cx, T* p, nsWrapperCache* cache)
+{
+ qsObjectHelper helper(ToSupports(p), cache);
+ JS::Rooted<JSObject*> scope(cx, JS::CurrentGlobalOrNull(cx));
+ JS::Rooted<JS::Value> v(cx);
+ return XPCOMObjectToJsval(cx, scope, helper, nullptr, false, &v) ?
+ v.toObjectOrNull() :
+ nullptr;
+}
+
+
+// Fallback for when our parent is not a WebIDL binding object.
+template<typename T, bool isISupports=IsBaseOf<nsISupports, T>::value>
+struct WrapNativeFallback
+{
+ static inline JSObject* Wrap(JSContext* cx, T* parent, nsWrapperCache* cache)
+ {
+ return nullptr;
+ }
+};
+
+// Fallback for when our parent is not a WebIDL binding object but _is_ an
+// nsISupports object.
+template<typename T >
+struct WrapNativeFallback<T, true >
+{
+ static inline JSObject* Wrap(JSContext* cx, T* parent, nsWrapperCache* cache)
+ {
+ return WrapNativeISupports(cx, parent, cache);
+ }
+};
+
+// Wrapping of our native parent, for cases when it's a WebIDL object (though
+// possibly preffed off).
+template<typename T, bool hasWrapObject=NativeHasMember<T>::WrapObject>
+struct WrapNativeHelper
+{
+ static inline JSObject* Wrap(JSContext* cx, T* parent, nsWrapperCache* cache)
+ {
+ MOZ_ASSERT(cache);
+
+ JSObject* obj;
+ if ((obj = cache->GetWrapper())) {
+ // GetWrapper always unmarks gray.
+ MOZ_ASSERT(!JS::ObjectIsMarkedGray(obj));
+ return obj;
+ }
+
+ // Inline this here while we have non-dom objects in wrapper caches.
+ if (!CouldBeDOMBinding(parent)) {
+ // WrapNativeFallback never returns a gray thing.
+ obj = WrapNativeFallback<T>::Wrap(cx, parent, cache);
+ MOZ_ASSERT_IF(obj, !JS::ObjectIsMarkedGray(obj));
+ } else {
+ // WrapObject never returns a gray thing.
+ obj = parent->WrapObject(cx, nullptr);
+ MOZ_ASSERT_IF(obj, !JS::ObjectIsMarkedGray(obj));
+ }
+
+ return obj;
+ }
+};
+
+// Wrapping of our native parent, for cases when it's not a WebIDL object. In
+// this case it must be nsISupports.
+template<typename T>
+struct WrapNativeHelper<T, false>
+{
+ static inline JSObject* Wrap(JSContext* cx, T* parent, nsWrapperCache* cache)
+ {
+ JSObject* obj;
+ if (cache && (obj = cache->GetWrapper())) {
+#ifdef DEBUG
+ JS::Rooted<JSObject*> rootedObj(cx, obj);
+ NS_ASSERTION(WrapNativeISupports(cx, parent, cache) == rootedObj,
+ "Unexpected object in nsWrapperCache");
+ obj = rootedObj;
+#endif
+ MOZ_ASSERT(!JS::ObjectIsMarkedGray(obj));
+ return obj;
+ }
+
+ obj = WrapNativeISupports(cx, parent, cache);
+ MOZ_ASSERT_IF(obj, !JS::ObjectIsMarkedGray(obj));
+ return obj;
+ }
+};
+
+// Finding the associated global for an object.
+template<typename T>
+static inline JSObject*
+FindAssociatedGlobal(JSContext* cx, T* p, nsWrapperCache* cache,
+ bool useXBLScope = false)
+{
+ if (!p) {
+ return JS::CurrentGlobalOrNull(cx);
+ }
+
+ JSObject* obj = WrapNativeHelper<T>::Wrap(cx, p, cache);
+ if (!obj) {
+ return nullptr;
+ }
+ MOZ_ASSERT(!JS::ObjectIsMarkedGray(obj));
+
+ obj = js::GetGlobalForObjectCrossCompartment(obj);
+
+ if (!useXBLScope) {
+ return obj;
+ }
+
+ // If useXBLScope is true, it means that the canonical reflector for this
+ // native object should live in the content XBL scope. Note that we never put
+ // anonymous content inside an add-on scope.
+ if (xpc::IsInContentXBLScope(obj)) {
+ return obj;
+ }
+ JS::Rooted<JSObject*> rootedObj(cx, obj);
+ JSObject* xblScope = xpc::GetXBLScope(cx, rootedObj);
+ MOZ_ASSERT_IF(xblScope, JS_IsGlobalObject(xblScope));
+ MOZ_ASSERT_IF(xblScope, !JS::ObjectIsMarkedGray(xblScope));
+ return xblScope;
+}
+
+// Finding of the associated global for an object, when we don't want to
+// explicitly pass in things like the nsWrapperCache for it.
+template<typename T>
+static inline JSObject*
+FindAssociatedGlobal(JSContext* cx, const T& p)
+{
+ return FindAssociatedGlobal(cx, GetParentPointer(p), GetWrapperCache(p), GetUseXBLScope(p));
+}
+
+// Specialization for the case of nsIGlobalObject, since in that case
+// we can just get the JSObject* directly.
+template<>
+inline JSObject*
+FindAssociatedGlobal(JSContext* cx, nsIGlobalObject* const& p)
+{
+ if (!p) {
+ return JS::CurrentGlobalOrNull(cx);
+ }
+
+ JSObject* global = p->GetGlobalJSObject();
+ if (!global) {
+ return nullptr;
+ }
+
+ MOZ_ASSERT(JS_IsGlobalObject(global));
+ // This object could be gray if the nsIGlobalObject is the only thing keeping
+ // it alive.
+ JS::ExposeObjectToActiveJS(global);
+ return global;
+}
+
+template<typename T,
+ bool hasAssociatedGlobal=NativeHasMember<T>::GetParentObject>
+struct FindAssociatedGlobalForNative
+{
+ static JSObject* Get(JSContext* cx, JS::Handle<JSObject*> obj)
+ {
+ MOZ_ASSERT(js::IsObjectInContextCompartment(obj, cx));
+ T* native = UnwrapDOMObject<T>(obj);
+ return FindAssociatedGlobal(cx, native->GetParentObject());
+ }
+};
+
+template<typename T>
+struct FindAssociatedGlobalForNative<T, false>
+{
+ static JSObject* Get(JSContext* cx, JS::Handle<JSObject*> obj)
+ {
+ MOZ_CRASH();
+ return nullptr;
+ }
+};
+
+// Helper for calling GetOrCreateDOMReflector with smart pointers
+// (nsAutoPtr/nsRefPtr/nsCOMPtr) or references.
+template <class T, bool isSmartPtr=IsSmartPtr<T>::value>
+struct GetOrCreateDOMReflectorHelper
+{
+ static inline bool GetOrCreate(JSContext* cx, const T& value,
+ JS::Handle<JSObject*> givenProto,
+ JS::MutableHandle<JS::Value> rval)
+ {
+ return GetOrCreateDOMReflector(cx, value.get(), rval, givenProto);
+ }
+};
+
+template <class T>
+struct GetOrCreateDOMReflectorHelper<T, false>
+{
+ static inline bool GetOrCreate(JSContext* cx, T& value,
+ JS::Handle<JSObject*> givenProto,
+ JS::MutableHandle<JS::Value> rval)
+ {
+ static_assert(IsRefcounted<T>::value, "Don't pass owned classes in here.");
+ return GetOrCreateDOMReflector(cx, &value, rval, givenProto);
+ }
+};
+
+template<class T>
+inline bool
+GetOrCreateDOMReflector(JSContext* cx, T& value,
+ JS::MutableHandle<JS::Value> rval,
+ JS::Handle<JSObject*> givenProto = nullptr)
+{
+ return GetOrCreateDOMReflectorHelper<T>::GetOrCreate(cx, value, givenProto,
+ rval);
+}
+
+// Helper for calling GetOrCreateDOMReflectorNoWrap with smart pointers
+// (nsAutoPtr/nsRefPtr/nsCOMPtr) or references.
+template <class T, bool isSmartPtr=IsSmartPtr<T>::value>
+struct GetOrCreateDOMReflectorNoWrapHelper
+{
+ static inline bool GetOrCreate(JSContext* cx, const T& value,
+ JS::MutableHandle<JS::Value> rval)
+ {
+ return GetOrCreateDOMReflectorNoWrap(cx, value.get(), rval);
+ }
+};
+
+template <class T>
+struct GetOrCreateDOMReflectorNoWrapHelper<T, false>
+{
+ static inline bool GetOrCreate(JSContext* cx, T& value,
+ JS::MutableHandle<JS::Value> rval)
+ {
+ return GetOrCreateDOMReflectorNoWrap(cx, &value, rval);
+ }
+};
+
+template<class T>
+inline bool
+GetOrCreateDOMReflectorNoWrap(JSContext* cx, T& value,
+ JS::MutableHandle<JS::Value> rval)
+{
+ return
+ GetOrCreateDOMReflectorNoWrapHelper<T>::GetOrCreate(cx, value, rval);
+}
+
+template <class T>
+inline JSObject*
+GetCallbackFromCallbackObject(T* aObj)
+{
+ return aObj->Callback();
+}
+
+// Helper for getting the callback JSObject* of a smart ptr around a
+// CallbackObject or a reference to a CallbackObject or something like
+// that.
+template <class T, bool isSmartPtr=IsSmartPtr<T>::value>
+struct GetCallbackFromCallbackObjectHelper
+{
+ static inline JSObject* Get(const T& aObj)
+ {
+ return GetCallbackFromCallbackObject(aObj.get());
+ }
+};
+
+template <class T>
+struct GetCallbackFromCallbackObjectHelper<T, false>
+{
+ static inline JSObject* Get(T& aObj)
+ {
+ return GetCallbackFromCallbackObject(&aObj);
+ }
+};
+
+template<class T>
+inline JSObject*
+GetCallbackFromCallbackObject(T& aObj)
+{
+ return GetCallbackFromCallbackObjectHelper<T>::Get(aObj);
+}
+
+static inline bool
+AtomizeAndPinJSString(JSContext* cx, jsid& id, const char* chars)
+{
+ if (JSString *str = ::JS_AtomizeAndPinString(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, const 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 (!JS::PropertySpecNameToPermanentId(cx, spec->name, ids)) {
+ 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;
+}
+
+bool
+QueryInterface(JSContext* cx, unsigned argc, JS::Value* vp);
+
+template <class T>
+struct
+WantsQueryInterface
+{
+ static_assert(IsBaseOf<nsISupports, T>::value,
+ "QueryInterface can't work without an nsISupports.");
+ static bool Enabled(JSContext* aCx, JSObject* aGlobal)
+ {
+ return NS_IsMainThread() && IsChromeOrXBL(aCx, aGlobal);
+ }
+};
+
+void
+GetInterfaceImpl(JSContext* aCx, nsIInterfaceRequestor* aRequestor,
+ nsWrapperCache* aCache, nsIJSID* aIID,
+ JS::MutableHandle<JS::Value> aRetval, ErrorResult& aError);
+
+template<class T>
+void
+GetInterface(JSContext* aCx, T* aThis, nsIJSID* aIID,
+ JS::MutableHandle<JS::Value> aRetval, ErrorResult& aError)
+{
+ GetInterfaceImpl(aCx, aThis, aThis, aIID, aRetval, aError);
+}
+
+bool
+UnforgeableValueOf(JSContext* cx, unsigned argc, JS::Value* vp);
+
+bool
+ThrowingConstructor(JSContext* cx, unsigned argc, JS::Value* vp);
+
+bool
+ThrowConstructorWithoutNew(JSContext* cx, const char* name);
+
+bool
+GetPropertyOnPrototype(JSContext* cx, JS::Handle<JSObject*> proxy,
+ JS::Handle<JS::Value> receiver, JS::Handle<jsid> id,
+ bool* found, JS::MutableHandle<JS::Value> vp);
+
+//
+bool
+HasPropertyOnPrototype(JSContext* cx, JS::Handle<JSObject*> proxy,
+ JS::Handle<jsid> id, bool* has);
+
+
+// Append the property names in "names" to "props". If
+// shadowPrototypeProperties is false then skip properties that are also
+// present on the proto chain of proxy. If shadowPrototypeProperties is true,
+// then the "proxy" argument is ignored.
+bool
+AppendNamedPropertyIds(JSContext* cx, JS::Handle<JSObject*> proxy,
+ nsTArray<nsString>& names,
+ bool shadowPrototypeProperties, JS::AutoIdVector& props);
+
+namespace binding_detail {
+
+class FastErrorResult :
+ public mozilla::binding_danger::TErrorResult<
+ mozilla::binding_danger::JustAssertCleanupPolicy>
+{
+};
+
+} // namespace binding_detail
+
+enum StringificationBehavior {
+ eStringify,
+ eEmpty,
+ eNull
+};
+
+template<typename T>
+static inline bool
+ConvertJSValueToString(JSContext* cx, JS::Handle<JS::Value> v,
+ StringificationBehavior nullBehavior,
+ StringificationBehavior undefinedBehavior,
+ T& 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.SetIsVoid(true);
+ }
+ return true;
+ }
+
+ s = JS::ToString(cx, v);
+ if (!s) {
+ return false;
+ }
+ }
+
+ return AssignJSString(cx, result, s);
+}
+
+void
+NormalizeUSVString(JSContext* aCx, nsAString& aString);
+
+void
+NormalizeUSVString(JSContext* aCx, binding_detail::FakeString& aString);
+
+template<typename T>
+inline bool
+ConvertIdToString(JSContext* cx, JS::HandleId id, T& result, bool& isSymbol)
+{
+ if (MOZ_LIKELY(JSID_IS_STRING(id))) {
+ if (!AssignJSString(cx, result, JSID_TO_STRING(id))) {
+ return false;
+ }
+ } else if (JSID_IS_SYMBOL(id)) {
+ isSymbol = true;
+ return true;
+ } else {
+ JS::RootedValue nameVal(cx, js::IdToValue(id));
+ if (!ConvertJSValueToString(cx, nameVal, eStringify, eStringify, result)) {
+ return false;
+ }
+ }
+ isSymbol = false;
+ return true;
+}
+
+bool
+ConvertJSValueToByteString(JSContext* cx, JS::Handle<JS::Value> v,
+ bool nullable, nsACString& result);
+
+template<typename T>
+void DoTraceSequence(JSTracer* trc, FallibleTArray<T>& seq);
+template<typename T>
+void DoTraceSequence(JSTracer* trc, InfallibleTArray<T>& seq);
+
+// Class used to trace sequences, with specializations for various
+// sequence types.
+template<typename T,
+ bool isDictionary=IsBaseOf<DictionaryBase, T>::value,
+ bool isTypedArray=IsBaseOf<AllTypedArraysBase, T>::value,
+ bool isOwningUnion=IsBaseOf<AllOwningUnionBase, T>::value>
+class SequenceTracer
+{
+ explicit SequenceTracer() = delete; // Should never be instantiated
+};
+
+// sequence<object> or sequence<object?>
+template<>
+class SequenceTracer<JSObject*, false, false, false>
+{
+ explicit SequenceTracer() = delete; // Should never be instantiated
+
+public:
+ static void TraceSequence(JSTracer* trc, JSObject** objp, JSObject** end) {
+ for (; objp != end; ++objp) {
+ JS::UnsafeTraceRoot(trc, objp, "sequence<object>");
+ }
+ }
+};
+
+// sequence<any>
+template<>
+class SequenceTracer<JS::Value, false, false, false>
+{
+ explicit SequenceTracer() = delete; // Should never be instantiated
+
+public:
+ static void TraceSequence(JSTracer* trc, JS::Value* valp, JS::Value* end) {
+ for (; valp != end; ++valp) {
+ JS::UnsafeTraceRoot(trc, valp, "sequence<any>");
+ }
+ }
+};
+
+// sequence<sequence<T>>
+template<typename T>
+class SequenceTracer<Sequence<T>, false, false, false>
+{
+ explicit SequenceTracer() = delete; // Should never be instantiated
+
+public:
+ static void TraceSequence(JSTracer* trc, Sequence<T>* seqp, Sequence<T>* end) {
+ for (; seqp != end; ++seqp) {
+ DoTraceSequence(trc, *seqp);
+ }
+ }
+};
+
+// sequence<sequence<T>> as return value
+template<typename T>
+class SequenceTracer<nsTArray<T>, false, false, false>
+{
+ explicit SequenceTracer() = delete; // Should never be instantiated
+
+public:
+ static void TraceSequence(JSTracer* trc, nsTArray<T>* seqp, nsTArray<T>* end) {
+ for (; seqp != end; ++seqp) {
+ DoTraceSequence(trc, *seqp);
+ }
+ }
+};
+
+// sequence<someDictionary>
+template<typename T>
+class SequenceTracer<T, true, false, false>
+{
+ explicit SequenceTracer() = delete; // Should never be instantiated
+
+public:
+ static void TraceSequence(JSTracer* trc, T* dictp, T* end) {
+ for (; dictp != end; ++dictp) {
+ dictp->TraceDictionary(trc);
+ }
+ }
+};
+
+// sequence<SomeTypedArray>
+template<typename T>
+class SequenceTracer<T, false, true, false>
+{
+ explicit SequenceTracer() = delete; // Should never be instantiated
+
+public:
+ static void TraceSequence(JSTracer* trc, T* arrayp, T* end) {
+ for (; arrayp != end; ++arrayp) {
+ arrayp->TraceSelf(trc);
+ }
+ }
+};
+
+// sequence<SomeOwningUnion>
+template<typename T>
+class SequenceTracer<T, false, false, true>
+{
+ explicit SequenceTracer() = delete; // Should never be instantiated
+
+public:
+ static void TraceSequence(JSTracer* trc, T* arrayp, T* end) {
+ for (; arrayp != end; ++arrayp) {
+ arrayp->TraceUnion(trc);
+ }
+ }
+};
+
+// sequence<T?> with T? being a Nullable<T>
+template<typename T>
+class SequenceTracer<Nullable<T>, false, false, false>
+{
+ explicit SequenceTracer() = delete; // Should never be instantiated
+
+public:
+ static void TraceSequence(JSTracer* trc, Nullable<T>* seqp,
+ Nullable<T>* end) {
+ for (; seqp != end; ++seqp) {
+ if (!seqp->IsNull()) {
+ // Pretend like we actually have a length-one sequence here so
+ // we can do template instantiation correctly for T.
+ T& val = seqp->Value();
+ T* ptr = &val;
+ SequenceTracer<T>::TraceSequence(trc, ptr, ptr+1);
+ }
+ }
+ }
+};
+
+template<typename T>
+static void
+TraceMozMapValue(T* aValue, void* aClosure)
+{
+ JSTracer* trc = static_cast<JSTracer*>(aClosure);
+ // Act like it's a one-element sequence to leverage all that infrastructure.
+ SequenceTracer<T>::TraceSequence(trc, aValue, aValue + 1);
+}
+
+template<typename T>
+void TraceMozMap(JSTracer* trc, MozMap<T>& map)
+{
+ map.EnumerateValues(TraceMozMapValue<T>, trc);
+}
+
+// sequence<MozMap>
+template<typename T>
+class SequenceTracer<MozMap<T>, false, false, false>
+{
+ explicit SequenceTracer() = delete; // Should never be instantiated
+
+public:
+ static void TraceSequence(JSTracer* trc, MozMap<T>* seqp, MozMap<T>* end) {
+ for (; seqp != end; ++seqp) {
+ seqp->EnumerateValues(TraceMozMapValue<T>, trc);
+ }
+ }
+};
+
+template<typename T>
+void DoTraceSequence(JSTracer* trc, FallibleTArray<T>& seq)
+{
+ SequenceTracer<T>::TraceSequence(trc, seq.Elements(),
+ seq.Elements() + seq.Length());
+}
+
+template<typename T>
+void DoTraceSequence(JSTracer* trc, InfallibleTArray<T>& seq)
+{
+ SequenceTracer<T>::TraceSequence(trc, seq.Elements(),
+ seq.Elements() + seq.Length());
+}
+
+// Rooter class for sequences; this is what we mostly use in the codegen
+template<typename T>
+class MOZ_RAII SequenceRooter final : private JS::CustomAutoRooter
+{
+public:
+ SequenceRooter(JSContext *aCx, FallibleTArray<T>* aSequence
+ MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+ : JS::CustomAutoRooter(aCx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT),
+ mFallibleArray(aSequence),
+ mSequenceType(eFallibleArray)
+ {
+ }
+
+ SequenceRooter(JSContext *aCx, InfallibleTArray<T>* aSequence
+ MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+ : JS::CustomAutoRooter(aCx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT),
+ mInfallibleArray(aSequence),
+ mSequenceType(eInfallibleArray)
+ {
+ }
+
+ SequenceRooter(JSContext *aCx, Nullable<nsTArray<T> >* aSequence
+ MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+ : JS::CustomAutoRooter(aCx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT),
+ mNullableArray(aSequence),
+ mSequenceType(eNullableArray)
+ {
+ }
+
+ private:
+ enum SequenceType {
+ eInfallibleArray,
+ eFallibleArray,
+ eNullableArray
+ };
+
+ virtual void trace(JSTracer *trc) override
+ {
+ if (mSequenceType == eFallibleArray) {
+ DoTraceSequence(trc, *mFallibleArray);
+ } else if (mSequenceType == eInfallibleArray) {
+ DoTraceSequence(trc, *mInfallibleArray);
+ } else {
+ MOZ_ASSERT(mSequenceType == eNullableArray);
+ if (!mNullableArray->IsNull()) {
+ DoTraceSequence(trc, mNullableArray->Value());
+ }
+ }
+ }
+
+ union {
+ InfallibleTArray<T>* mInfallibleArray;
+ FallibleTArray<T>* mFallibleArray;
+ Nullable<nsTArray<T> >* mNullableArray;
+ };
+
+ SequenceType mSequenceType;
+};
+
+// Rooter class for MozMap; this is what we mostly use in the codegen.
+template<typename T>
+class MOZ_RAII MozMapRooter final : private JS::CustomAutoRooter
+{
+public:
+ MozMapRooter(JSContext *aCx, MozMap<T>* aMozMap
+ MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+ : JS::CustomAutoRooter(aCx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT),
+ mMozMap(aMozMap),
+ mMozMapType(eMozMap)
+ {
+ }
+
+ MozMapRooter(JSContext *aCx, Nullable<MozMap<T>>* aMozMap
+ MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+ : JS::CustomAutoRooter(aCx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT),
+ mNullableMozMap(aMozMap),
+ mMozMapType(eNullableMozMap)
+ {
+ }
+
+private:
+ enum MozMapType {
+ eMozMap,
+ eNullableMozMap
+ };
+
+ virtual void trace(JSTracer *trc) override
+ {
+ if (mMozMapType == eMozMap) {
+ TraceMozMap(trc, *mMozMap);
+ } else {
+ MOZ_ASSERT(mMozMapType == eNullableMozMap);
+ if (!mNullableMozMap->IsNull()) {
+ TraceMozMap(trc, mNullableMozMap->Value());
+ }
+ }
+ }
+
+ union {
+ MozMap<T>* mMozMap;
+ Nullable<MozMap<T>>* mNullableMozMap;
+ };
+
+ MozMapType mMozMapType;
+};
+
+template<typename T>
+class MOZ_RAII RootedUnion : public T,
+ private JS::CustomAutoRooter
+{
+public:
+ explicit RootedUnion(JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) :
+ T(),
+ JS::CustomAutoRooter(cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT)
+ {
+ }
+
+ virtual void trace(JSTracer *trc) override
+ {
+ this->TraceUnion(trc);
+ }
+};
+
+template<typename T>
+class MOZ_STACK_CLASS NullableRootedUnion : public Nullable<T>,
+ private JS::CustomAutoRooter
+{
+public:
+ explicit NullableRootedUnion(JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) :
+ Nullable<T>(),
+ JS::CustomAutoRooter(cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT)
+ {
+ }
+
+ virtual void trace(JSTracer *trc) override
+ {
+ if (!this->IsNull()) {
+ this->Value().TraceUnion(trc);
+ }
+ }
+};
+
+inline bool
+IdEquals(jsid id, const char* string)
+{
+ return JSID_IS_STRING(id) &&
+ JS_FlatStringEqualsAscii(JSID_TO_FLAT_STRING(id), string);
+}
+
+inline bool
+AddStringToIDVector(JSContext* cx, JS::AutoIdVector& vector, const char* name)
+{
+ return vector.growBy(1) &&
+ AtomizeAndPinJSString(cx, *(vector[vector.length() - 1]).address(), name);
+}
+
+// We use one constructor JSNative to represent all DOM interface objects (so
+// we can easily detect when we need to wrap them in an Xray wrapper). We store
+// the real JSNative in the mNative member of a JSNativeHolder in the
+// CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT slot of the JSFunction object for a
+// specific interface object. We also store the NativeProperties in the
+// JSNativeHolder.
+// Note that some interface objects are not yet a JSFunction but a normal
+// JSObject with a DOMJSClass, those do not use these slots.
+
+enum {
+ CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT = 0
+};
+
+bool
+Constructor(JSContext* cx, unsigned argc, JS::Value* vp);
+
+// Implementation of the bits that XrayWrapper needs
+
+/**
+ * This resolves operations, attributes and constants of the interfaces for obj.
+ *
+ * wrapper is the Xray JS object.
+ * obj is the target object of the Xray, a binding's instance object or a
+ * interface or interface prototype object.
+ */
+bool
+XrayResolveOwnProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
+ JS::Handle<JSObject*> obj,
+ JS::Handle<jsid> id,
+ JS::MutableHandle<JS::PropertyDescriptor> desc,
+ bool& cacheOnHolder);
+
+/**
+ * Define a property on obj through an Xray wrapper.
+ *
+ * wrapper is the Xray JS object.
+ * obj is the target object of the Xray, a binding's instance object or a
+ * interface or interface prototype object.
+ * id and desc are the parameters for the property to be defined.
+ * result is the out-parameter indicating success (read it only if
+ * this returns true and also sets *defined to true).
+ * defined will be set to true if a property was set as a result of this call.
+ */
+bool
+XrayDefineProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
+ JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
+ JS::Handle<JS::PropertyDescriptor> desc,
+ JS::ObjectOpResult &result,
+ bool *defined);
+
+/**
+ * Add to props the property keys of all indexed or named properties of obj and
+ * operations, attributes and constants of the interfaces for obj.
+ *
+ * wrapper is the Xray JS object.
+ * obj is the target object of the Xray, a binding's instance object or a
+ * interface or interface prototype object.
+ * flags are JSITER_* flags.
+ */
+bool
+XrayOwnPropertyKeys(JSContext* cx, JS::Handle<JSObject*> wrapper,
+ JS::Handle<JSObject*> obj,
+ unsigned flags, JS::AutoIdVector& props);
+
+/**
+ * Returns the prototype to use for an Xray for a DOM object, wrapped in cx's
+ * compartment. This always returns the prototype that would be used for a DOM
+ * object if we ignore any changes that might have been done to the prototype
+ * chain by JS, the XBL code or plugins.
+ *
+ * cx should be in the Xray's compartment.
+ * obj is the target object of the Xray, a binding's instance object or an
+ * interface or interface prototype object.
+ */
+inline bool
+XrayGetNativeProto(JSContext* cx, JS::Handle<JSObject*> obj,
+ JS::MutableHandle<JSObject*> protop)
+{
+ JS::Rooted<JSObject*> global(cx, js::GetGlobalForObjectCrossCompartment(obj));
+ {
+ JSAutoCompartment ac(cx, global);
+ const DOMJSClass* domClass = GetDOMClass(obj);
+ if (domClass) {
+ ProtoHandleGetter protoGetter = domClass->mGetProto;
+ if (protoGetter) {
+ protop.set(protoGetter(cx));
+ } else {
+ protop.set(JS::GetRealmObjectPrototype(cx));
+ }
+ } else if (JS_ObjectIsFunction(cx, obj)) {
+ MOZ_ASSERT(JS_IsNativeFunction(obj, Constructor));
+ protop.set(JS::GetRealmFunctionPrototype(cx));
+ } else {
+ const js::Class* clasp = js::GetObjectClass(obj);
+ MOZ_ASSERT(IsDOMIfaceAndProtoClass(clasp));
+ ProtoGetter protoGetter =
+ DOMIfaceAndProtoJSClass::FromJSClass(clasp)->mGetParentProto;
+ protop.set(protoGetter(cx));
+ }
+ }
+
+ return JS_WrapObject(cx, protop);
+}
+
+/**
+ * Get the Xray expando class to use for the given DOM object.
+ */
+const JSClass*
+XrayGetExpandoClass(JSContext* cx, JS::Handle<JSObject*> obj);
+
+/**
+ * Delete a named property, if any. Return value is false if exception thrown,
+ * true otherwise. The caller should not do any more work after calling this
+ * function, because it has no way whether a deletion was performed and hence
+ * opresult already has state set on it. If callers ever need to change that,
+ * add a "bool* found" argument and change the generated DeleteNamedProperty to
+ * use it instead of a local variable.
+ */
+bool
+XrayDeleteNamedProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
+ JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
+ JS::ObjectOpResult& opresult);
+
+/**
+ * Get the object which should be used to cache the return value of a property
+ * getter in the case of a [Cached] or [StoreInSlot] property. `obj` is the
+ * `this` value for our property getter that we're working with.
+ *
+ * This function can return null on failure to allocate the object, throwing on
+ * the JSContext in the process.
+ *
+ * The isXray outparam will be set to true if obj is an Xray and false
+ * otherwise.
+ *
+ * Note that the Slow version should only be called from
+ * GetCachedSlotStorageObject.
+ */
+JSObject*
+GetCachedSlotStorageObjectSlow(JSContext* cx, JS::Handle<JSObject*> obj,
+ bool* isXray);
+
+inline JSObject*
+GetCachedSlotStorageObject(JSContext* cx, JS::Handle<JSObject*> obj,
+ bool* isXray) {
+ if (IsDOMObject(obj)) {
+ *isXray = false;
+ return obj;
+ }
+
+ return GetCachedSlotStorageObjectSlow(cx, obj, isXray);
+}
+
+extern NativePropertyHooks sEmptyNativePropertyHooks;
+
+extern const js::ClassOps sBoringInterfaceObjectClassClassOps;
+
+extern const js::ObjectOps sInterfaceObjectClassObjectOps;
+
+inline bool
+UseDOMXray(JSObject* obj)
+{
+ const js::Class* clasp = js::GetObjectClass(obj);
+ return IsDOMClass(clasp) ||
+ JS_IsNativeFunction(obj, Constructor) ||
+ IsDOMIfaceAndProtoClass(clasp);
+}
+
+#ifdef DEBUG
+inline bool
+HasConstructor(JSObject* obj)
+{
+ return JS_IsNativeFunction(obj, Constructor) ||
+ js::GetObjectClass(obj)->getConstruct();
+}
+#endif
+
+// Helpers for creating a const version of a type.
+template<typename T>
+const T& Constify(T& arg)
+{
+ return arg;
+}
+
+// Helper for turning (Owning)NonNull<T> into T&
+template<typename T>
+T& NonNullHelper(T& aArg)
+{
+ return aArg;
+}
+
+template<typename T>
+T& NonNullHelper(NonNull<T>& aArg)
+{
+ return aArg;
+}
+
+template<typename T>
+const T& NonNullHelper(const NonNull<T>& aArg)
+{
+ return aArg;
+}
+
+template<typename T>
+T& NonNullHelper(OwningNonNull<T>& aArg)
+{
+ return aArg;
+}
+
+template<typename T>
+const T& NonNullHelper(const OwningNonNull<T>& aArg)
+{
+ return aArg;
+}
+
+inline
+void NonNullHelper(NonNull<binding_detail::FakeString>& aArg)
+{
+ // This overload is here to make sure that we never end up applying
+ // NonNullHelper to a NonNull<binding_detail::FakeString>. If we
+ // try to, it should fail to compile, since presumably the caller will try to
+ // use our nonexistent return value.
+}
+
+inline
+void NonNullHelper(const NonNull<binding_detail::FakeString>& aArg)
+{
+ // This overload is here to make sure that we never end up applying
+ // NonNullHelper to a NonNull<binding_detail::FakeString>. If we
+ // try to, it should fail to compile, since presumably the caller will try to
+ // use our nonexistent return value.
+}
+
+inline
+void NonNullHelper(binding_detail::FakeString& aArg)
+{
+ // This overload is here to make sure that we never end up applying
+ // NonNullHelper to a FakeString before we've constified it. If we
+ // try to, it should fail to compile, since presumably the caller will try to
+ // use our nonexistent return value.
+}
+
+MOZ_ALWAYS_INLINE
+const nsAString& NonNullHelper(const binding_detail::FakeString& aArg)
+{
+ return aArg;
+}
+
+// Reparent the wrapper of aObj to whatever its native now thinks its
+// parent should be.
+nsresult
+ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObj);
+
+/**
+ * Used to implement the Symbol.hasInstance property of an interface object.
+ */
+bool
+InterfaceHasInstance(JSContext* cx, unsigned argc, JS::Value* vp);
+
+bool
+InterfaceHasInstance(JSContext* cx, int prototypeID, int depth,
+ JS::Handle<JSObject*> instance,
+ bool* bp);
+
+// Helper for lenient getters/setters to report to console. If this
+// returns false, we couldn't even get a global.
+bool
+ReportLenientThisUnwrappingFailure(JSContext* cx, JSObject* obj);
+
+// Given a JSObject* that represents the chrome side of a JS-implemented WebIDL
+// interface, get the nsIGlobalObject corresponding to the content side, if any.
+// A false return means an exception was thrown.
+bool
+GetContentGlobalForJSImplementedObject(JSContext* cx, JS::Handle<JSObject*> obj,
+ nsIGlobalObject** global);
+
+void
+ConstructJSImplementation(const char* aContractId,
+ nsIGlobalObject* aGlobal,
+ JS::MutableHandle<JSObject*> aObject,
+ ErrorResult& aRv);
+
+already_AddRefed<nsIGlobalObject>
+ConstructJSImplementation(const char* aContractId,
+ const GlobalObject& aGlobal,
+ JS::MutableHandle<JSObject*> aObject,
+ ErrorResult& aRv);
+
+/**
+ * Convert an nsCString to jsval, returning true on success.
+ * These functions are intended for ByteString implementations.
+ * As such, the string is not UTF-8 encoded. Any UTF8 strings passed to these
+ * methods will be mangled.
+ */
+bool NonVoidByteStringToJsval(JSContext *cx, const nsACString &str,
+ JS::MutableHandle<JS::Value> rval);
+inline bool ByteStringToJsval(JSContext *cx, const nsACString &str,
+ JS::MutableHandle<JS::Value> rval)
+{
+ if (str.IsVoid()) {
+ rval.setNull();
+ return true;
+ }
+ return NonVoidByteStringToJsval(cx, str, rval);
+}
+
+template<class T, bool isISupports=IsBaseOf<nsISupports, T>::value>
+struct PreserveWrapperHelper
+{
+ static void PreserveWrapper(T* aObject)
+ {
+ aObject->PreserveWrapper(aObject, NS_CYCLE_COLLECTION_PARTICIPANT(T));
+ }
+};
+
+template<class T>
+struct PreserveWrapperHelper<T, true>
+{
+ static void PreserveWrapper(T* aObject)
+ {
+ aObject->PreserveWrapper(reinterpret_cast<nsISupports*>(aObject));
+ }
+};
+
+template<class T>
+void PreserveWrapper(T* aObject)
+{
+ PreserveWrapperHelper<T>::PreserveWrapper(aObject);
+}
+
+template<class T, bool isISupports=IsBaseOf<nsISupports, T>::value>
+struct CastingAssertions
+{
+ static bool ToSupportsIsCorrect(T*)
+ {
+ return true;
+ }
+ static bool ToSupportsIsOnPrimaryInheritanceChain(T*, nsWrapperCache*)
+ {
+ return true;
+ }
+};
+
+template<class T>
+struct CastingAssertions<T, true>
+{
+ static bool ToSupportsIsCorrect(T* aObject)
+ {
+ return ToSupports(aObject) == reinterpret_cast<nsISupports*>(aObject);
+ }
+ static bool ToSupportsIsOnPrimaryInheritanceChain(T* aObject,
+ nsWrapperCache* aCache)
+ {
+ return reinterpret_cast<void*>(aObject) != aCache;
+ }
+};
+
+template<class T>
+bool
+ToSupportsIsCorrect(T* aObject)
+{
+ return CastingAssertions<T>::ToSupportsIsCorrect(aObject);
+}
+
+template<class T>
+bool
+ToSupportsIsOnPrimaryInheritanceChain(T* aObject, nsWrapperCache* aCache)
+{
+ return CastingAssertions<T>::ToSupportsIsOnPrimaryInheritanceChain(aObject,
+ aCache);
+}
+
+// The BindingJSObjectCreator class is supposed to be used by a caller that
+// wants to create and initialise a binding JSObject. After initialisation has
+// been successfully completed it should call ForgetObject().
+// The BindingJSObjectCreator object will root the JSObject until ForgetObject()
+// is called on it. If the native object for the binding is refcounted it will
+// also hold a strong reference to it, that reference is transferred to the
+// JSObject (which holds the native in a slot) when ForgetObject() is called. If
+// the BindingJSObjectCreator object is destroyed and ForgetObject() was never
+// called on it then the JSObject's slot holding the native will be set to
+// undefined, and for a refcounted native the strong reference will be released.
+template<class T>
+class MOZ_STACK_CLASS BindingJSObjectCreator
+{
+public:
+ explicit BindingJSObjectCreator(JSContext* aCx)
+ : mReflector(aCx)
+ {
+ }
+
+ ~BindingJSObjectCreator()
+ {
+ if (mReflector) {
+ js::SetReservedOrProxyPrivateSlot(mReflector, DOM_OBJECT_SLOT,
+ JS::UndefinedValue());
+ }
+ }
+
+ void
+ CreateProxyObject(JSContext* aCx, const js::Class* aClass,
+ const DOMProxyHandler* aHandler,
+ JS::Handle<JSObject*> aProto, T* aNative,
+ JS::MutableHandle<JSObject*> aReflector)
+ {
+ js::ProxyOptions options;
+ options.setClass(aClass);
+ JS::Rooted<JS::Value> proxyPrivateVal(aCx, JS::PrivateValue(aNative));
+ aReflector.set(js::NewProxyObject(aCx, aHandler, proxyPrivateVal, aProto,
+ options));
+ if (aReflector) {
+ mNative = aNative;
+ mReflector = aReflector;
+ }
+ }
+
+ void
+ CreateObject(JSContext* aCx, const JSClass* aClass,
+ JS::Handle<JSObject*> aProto,
+ T* aNative, JS::MutableHandle<JSObject*> aReflector)
+ {
+ aReflector.set(JS_NewObjectWithGivenProto(aCx, aClass, aProto));
+ if (aReflector) {
+ js::SetReservedSlot(aReflector, DOM_OBJECT_SLOT, JS::PrivateValue(aNative));
+ mNative = aNative;
+ mReflector = aReflector;
+ }
+ }
+
+ void
+ InitializationSucceeded()
+ {
+ void* dummy;
+ mNative.forget(&dummy);
+ mReflector = nullptr;
+ }
+
+private:
+ struct OwnedNative
+ {
+ // Make sure the native objects inherit from NonRefcountedDOMObject so
+ // that we log their ctor and dtor.
+ static_assert(IsBaseOf<NonRefcountedDOMObject, T>::value,
+ "Non-refcounted objects with DOM bindings should inherit "
+ "from NonRefcountedDOMObject.");
+
+ OwnedNative&
+ operator=(T* aNative)
+ {
+ return *this;
+ }
+
+ // This signature sucks, but it's the only one that will make a nsRefPtr
+ // just forget about its pointer without warning.
+ void
+ forget(void**)
+ {
+ }
+ };
+
+ JS::Rooted<JSObject*> mReflector;
+ typename Conditional<IsRefcounted<T>::value, RefPtr<T>, OwnedNative>::Type mNative;
+};
+
+template<class T>
+struct DeferredFinalizerImpl
+{
+ typedef typename Conditional<IsSame<T, nsISupports>::value,
+ nsCOMPtr<T>,
+ typename Conditional<IsRefcounted<T>::value,
+ RefPtr<T>,
+ nsAutoPtr<T>>::Type>::Type SmartPtr;
+ typedef SegmentedVector<SmartPtr> SmartPtrArray;
+
+ static_assert(IsSame<T, nsISupports>::value || !IsBaseOf<nsISupports, T>::value,
+ "nsISupports classes should all use the nsISupports instantiation");
+
+ static inline void
+ AppendAndTake(SegmentedVector<nsCOMPtr<nsISupports>>& smartPtrArray, nsISupports* ptr)
+ {
+ smartPtrArray.InfallibleAppend(dont_AddRef(ptr));
+ }
+ template<class U>
+ static inline void
+ AppendAndTake(SegmentedVector<RefPtr<U>>& smartPtrArray, U* ptr)
+ {
+ smartPtrArray.InfallibleAppend(dont_AddRef(ptr));
+ }
+ template<class U>
+ static inline void
+ AppendAndTake(SegmentedVector<nsAutoPtr<U>>& smartPtrArray, U* ptr)
+ {
+ smartPtrArray.InfallibleAppend(ptr);
+ }
+
+ static void*
+ AppendDeferredFinalizePointer(void* aData, void* aObject)
+ {
+ SmartPtrArray* pointers = static_cast<SmartPtrArray*>(aData);
+ if (!pointers) {
+ pointers = new SmartPtrArray();
+ }
+ AppendAndTake(*pointers, static_cast<T*>(aObject));
+ return pointers;
+ }
+ static bool
+ DeferredFinalize(uint32_t aSlice, void* aData)
+ {
+ MOZ_ASSERT(aSlice > 0, "nonsensical/useless call with aSlice == 0");
+ SmartPtrArray* pointers = static_cast<SmartPtrArray*>(aData);
+ uint32_t oldLen = pointers->Length();
+ if (oldLen < aSlice) {
+ aSlice = oldLen;
+ }
+ uint32_t newLen = oldLen - aSlice;
+ pointers->PopLastN(aSlice);
+ if (newLen == 0) {
+ delete pointers;
+ return true;
+ }
+ return false;
+ }
+};
+
+template<class T,
+ bool isISupports=IsBaseOf<nsISupports, T>::value>
+struct DeferredFinalizer
+{
+ static void
+ AddForDeferredFinalization(T* aObject)
+ {
+ typedef DeferredFinalizerImpl<T> Impl;
+ DeferredFinalize(Impl::AppendDeferredFinalizePointer,
+ Impl::DeferredFinalize, aObject);
+ }
+};
+
+template<class T>
+struct DeferredFinalizer<T, true>
+{
+ static void
+ AddForDeferredFinalization(T* aObject)
+ {
+ DeferredFinalize(reinterpret_cast<nsISupports*>(aObject));
+ }
+};
+
+template<class T>
+static void
+AddForDeferredFinalization(T* aObject)
+{
+ DeferredFinalizer<T>::AddForDeferredFinalization(aObject);
+}
+
+// This returns T's CC participant if it participates in CC or null if it
+// doesn't. This also returns null for classes that don't inherit from
+// nsISupports (QI should be used to get the participant for those).
+template<class T, bool isISupports=IsBaseOf<nsISupports, T>::value>
+class GetCCParticipant
+{
+ // Helper for GetCCParticipant for classes that participate in CC.
+ template<class U>
+ static constexpr nsCycleCollectionParticipant*
+ GetHelper(int, typename U::NS_CYCLE_COLLECTION_INNERCLASS* dummy=nullptr)
+ {
+ return T::NS_CYCLE_COLLECTION_INNERCLASS::GetParticipant();
+ }
+ // Helper for GetCCParticipant for classes that don't participate in CC.
+ template<class U>
+ static constexpr nsCycleCollectionParticipant*
+ GetHelper(double)
+ {
+ return nullptr;
+ }
+
+public:
+ static constexpr nsCycleCollectionParticipant*
+ Get()
+ {
+ // Passing int() here will try to call the GetHelper that takes an int as
+ // its firt argument. If T doesn't participate in CC then substitution for
+ // the second argument (with a default value) will fail and because of
+ // SFINAE the next best match (the variant taking a double) will be called.
+ return GetHelper<T>(int());
+ }
+};
+
+template<class T>
+class GetCCParticipant<T, true>
+{
+public:
+ static constexpr nsCycleCollectionParticipant*
+ Get()
+ {
+ return nullptr;
+ }
+};
+
+void
+FinalizeGlobal(JSFreeOp* aFop, JSObject* aObj);
+
+bool
+ResolveGlobal(JSContext* aCx, JS::Handle<JSObject*> aObj,
+ JS::Handle<jsid> aId, bool* aResolvedp);
+
+bool
+MayResolveGlobal(const JSAtomState& aNames, jsid aId, JSObject* aMaybeObj);
+
+bool
+EnumerateGlobal(JSContext* aCx, JS::Handle<JSObject*> aObj);
+
+template <class T>
+struct CreateGlobalOptions
+{
+ static constexpr ProtoAndIfaceCache::Kind ProtoAndIfaceCacheKind =
+ ProtoAndIfaceCache::NonWindowLike;
+ static void TraceGlobal(JSTracer* aTrc, JSObject* aObj)
+ {
+ mozilla::dom::TraceProtoAndIfaceCache(aTrc, aObj);
+ }
+ static bool PostCreateGlobal(JSContext* aCx, JS::Handle<JSObject*> aGlobal)
+ {
+ MOZ_ALWAYS_TRUE(TryPreserveWrapper(aGlobal));
+
+ return true;
+ }
+};
+
+template <>
+struct CreateGlobalOptions<nsGlobalWindow>
+{
+ static constexpr ProtoAndIfaceCache::Kind ProtoAndIfaceCacheKind =
+ ProtoAndIfaceCache::WindowLike;
+ static void TraceGlobal(JSTracer* aTrc, JSObject* aObj);
+ static bool PostCreateGlobal(JSContext* aCx, JS::Handle<JSObject*> aGlobal);
+};
+
+nsresult
+RegisterDOMNames();
+
+// The return value is true if we created and successfully performed our part of
+// the setup for the global, false otherwise.
+//
+// Typically this method's caller will want to ensure that
+// xpc::InitGlobalObjectOptions is called before, and xpc::InitGlobalObject is
+// called after, this method, to ensure that this global object and its
+// compartment are consistent with other global objects.
+template <class T, ProtoHandleGetter GetProto>
+bool
+CreateGlobal(JSContext* aCx, T* aNative, nsWrapperCache* aCache,
+ const JSClass* aClass, JS::CompartmentOptions& aOptions,
+ JSPrincipals* aPrincipal, bool aInitStandardClasses,
+ JS::MutableHandle<JSObject*> aGlobal)
+{
+ aOptions.creationOptions().setTrace(CreateGlobalOptions<T>::TraceGlobal);
+ if (xpc::SharedMemoryEnabled()) {
+ aOptions.creationOptions().setSharedMemoryAndAtomicsEnabled(true);
+ }
+
+ aGlobal.set(JS_NewGlobalObject(aCx, aClass, aPrincipal,
+ JS::DontFireOnNewGlobalHook, aOptions));
+ if (!aGlobal) {
+ NS_WARNING("Failed to create global");
+ return false;
+ }
+
+ JSAutoCompartment ac(aCx, aGlobal);
+
+ {
+ js::SetReservedSlot(aGlobal, DOM_OBJECT_SLOT, JS::PrivateValue(aNative));
+ NS_ADDREF(aNative);
+
+ aCache->SetWrapper(aGlobal);
+
+ dom::AllocateProtoAndIfaceCache(aGlobal,
+ CreateGlobalOptions<T>::ProtoAndIfaceCacheKind);
+
+ if (!CreateGlobalOptions<T>::PostCreateGlobal(aCx, aGlobal)) {
+ return false;
+ }
+ }
+
+ if (aInitStandardClasses &&
+ !JS_InitStandardClasses(aCx, aGlobal)) {
+ NS_WARNING("Failed to init standard classes");
+ return false;
+ }
+
+ JS::Handle<JSObject*> proto = GetProto(aCx);
+ if (!proto || !JS_SplicePrototype(aCx, aGlobal, proto)) {
+ NS_WARNING("Failed to set proto");
+ return false;
+ }
+
+ bool succeeded;
+ if (!JS_SetImmutablePrototype(aCx, aGlobal, &succeeded)) {
+ return false;
+ }
+ MOZ_ASSERT(succeeded,
+ "making a fresh global object's [[Prototype]] immutable can "
+ "internally fail, but it should never be unsuccessful");
+
+ return true;
+}
+
+/*
+ * Holds a jsid that is initialized to a pinned string, with automatic
+ * conversion to Handle<jsid>, as it is held live forever by pinning.
+ */
+class PinnedStringId
+{
+ jsid id;
+
+ public:
+ PinnedStringId() : id(JSID_VOID) {}
+
+ bool init(JSContext *cx, const char *string) {
+ JSString* str = JS_AtomizeAndPinString(cx, string);
+ if (!str)
+ return false;
+ id = INTERNED_STRING_TO_JSID(cx, str);
+ return true;
+ }
+
+ operator const jsid& () {
+ return id;
+ }
+
+ operator JS::Handle<jsid> () {
+ /* This is safe because we have pinned the string. */
+ return JS::Handle<jsid>::fromMarkedLocation(&id);
+ }
+};
+
+bool
+GenericBindingGetter(JSContext* cx, unsigned argc, JS::Value* vp);
+
+bool
+GenericBindingSetter(JSContext* cx, unsigned argc, JS::Value* vp);
+
+bool
+GenericBindingMethod(JSContext* cx, unsigned argc, JS::Value* vp);
+
+bool
+GenericPromiseReturningBindingMethod(JSContext* cx, unsigned argc, JS::Value* vp);
+
+bool
+StaticMethodPromiseWrapper(JSContext* cx, unsigned argc, JS::Value* vp);
+
+// ConvertExceptionToPromise should only be called when we have an error
+// condition (e.g. returned false from a JSAPI method). Note that there may be
+// no exception on cx, in which case this is an uncatchable failure that will
+// simply be propagated. Otherwise this method will attempt to convert the
+// exception to a Promise rejected with the exception that it will store in
+// rval.
+//
+// promiseScope should be the scope in which the Promise should be created.
+bool
+ConvertExceptionToPromise(JSContext* cx,
+ JSObject* promiseScope,
+ JS::MutableHandle<JS::Value> rval);
+
+#ifdef DEBUG
+void
+AssertReturnTypeMatchesJitinfo(const JSJitInfo* aJitinfo,
+ JS::Handle<JS::Value> aValue);
+#endif
+
+// This function is called by the bindings layer for methods/getters/setters
+// that are not safe to be called in prerendering mode. It checks to make sure
+// that the |this| object is not running in a global that is in prerendering
+// mode. Otherwise, it aborts execution of timers and event handlers, and
+// returns false which gets converted to an uncatchable exception by the
+// bindings layer.
+bool
+EnforceNotInPrerendering(JSContext* aCx, JSObject* aObj);
+
+// Handles the violation of a blacklisted action in prerendering mode by
+// aborting the scripts, and preventing timers and event handlers from running
+// in the window in the future.
+void
+HandlePrerenderingViolation(nsPIDOMWindowInner* aWindow);
+
+bool
+CallerSubsumes(JSObject* aObject);
+
+MOZ_ALWAYS_INLINE bool
+CallerSubsumes(JS::Handle<JS::Value> aValue)
+{
+ if (!aValue.isObject()) {
+ return true;
+ }
+ return CallerSubsumes(&aValue.toObject());
+}
+
+template<class T>
+inline bool
+WrappedJSToDictionary(JSContext* aCx, nsISupports* aObject, T& aDictionary)
+{
+ nsCOMPtr<nsIXPConnectWrappedJS> wrappedObj = do_QueryInterface(aObject);
+ if (!wrappedObj) {
+ return false;
+ }
+
+ JS::Rooted<JSObject*> obj(aCx, wrappedObj->GetJSObject());
+ if (!obj) {
+ return false;
+ }
+
+ JSAutoCompartment ac(aCx, obj);
+ JS::Rooted<JS::Value> v(aCx, JS::ObjectValue(*obj));
+ return aDictionary.Init(aCx, v);
+}
+
+template<class T>
+inline bool
+WrappedJSToDictionary(nsISupports* aObject, T& aDictionary)
+{
+ nsCOMPtr<nsIXPConnectWrappedJS> wrappedObj = do_QueryInterface(aObject);
+ NS_ENSURE_TRUE(wrappedObj, false);
+ JS::Rooted<JSObject*> obj(RootingCx(), wrappedObj->GetJSObject());
+ NS_ENSURE_TRUE(obj, false);
+
+ nsIGlobalObject* global = xpc::NativeGlobal(obj);
+ NS_ENSURE_TRUE(global, false);
+
+ // we need this AutoEntryScript here because the spec requires us to execute
+ // getters when parsing a dictionary
+ AutoEntryScript aes(global, "WebIDL dictionary creation");
+
+ JS::Rooted<JS::Value> v(aes.cx(), JS::ObjectValue(*obj));
+ return aDictionary.Init(aes.cx(), v);
+}
+
+
+template<class T, class S>
+inline RefPtr<T>
+StrongOrRawPtr(already_AddRefed<S>&& aPtr)
+{
+ return aPtr.template downcast<T>();
+}
+
+template<class T,
+ class ReturnType=typename Conditional<IsRefcounted<T>::value, T*,
+ nsAutoPtr<T>>::Type>
+inline ReturnType
+StrongOrRawPtr(T* aPtr)
+{
+ return ReturnType(aPtr);
+}
+
+template<class T, template<typename> class SmartPtr, class S>
+inline void
+StrongOrRawPtr(SmartPtr<S>&& aPtr) = delete;
+
+template<class T>
+struct StrongPtrForMember
+{
+ typedef typename Conditional<IsRefcounted<T>::value,
+ RefPtr<T>, nsAutoPtr<T>>::Type Type;
+};
+
+namespace binding_detail {
+inline
+JSObject*
+GetHackedNamespaceProtoObject(JSContext* aCx)
+{
+ return JS_NewPlainObject(aCx);
+}
+} // namespace binding_detail
+
+// Resolve an id on the given global object that wants to be included in
+// Exposed=System webidl annotations. False return value means exception
+// thrown.
+bool SystemGlobalResolve(JSContext* cx, JS::Handle<JSObject*> obj,
+ JS::Handle<jsid> id, bool* resolvedp);
+
+// Enumerate all ids on the given global object that wants to be included in
+// Exposed=System webidl annotations. False return value means exception
+// thrown.
+bool SystemGlobalEnumerate(JSContext* cx, JS::Handle<JSObject*> obj);
+
+// Slot indexes for maplike/setlike forEach functions
+#define FOREACH_CALLBACK_SLOT 0
+#define FOREACH_MAPLIKEORSETLIKEOBJ_SLOT 1
+
+// Backing function for running .forEach() on maplike/setlike interfaces.
+// Unpacks callback and maplike/setlike object from reserved slots, then runs
+// callback for each key (and value, for maplikes)
+bool ForEachHandler(JSContext* aCx, unsigned aArgc, JS::Value* aVp);
+
+// Unpacks backing object (ES6 map/set) from the reserved slot of a reflector
+// for a maplike/setlike interface. If backing object does not exist, creates
+// backing object in the compartment of the reflector involved, making this safe
+// to use across compartments/via xrays. Return values of these methods will
+// always be in the context compartment.
+bool GetMaplikeBackingObject(JSContext* aCx, JS::Handle<JSObject*> aObj,
+ size_t aSlotIndex,
+ JS::MutableHandle<JSObject*> aBackingObj,
+ bool* aBackingObjCreated);
+bool GetSetlikeBackingObject(JSContext* aCx, JS::Handle<JSObject*> aObj,
+ size_t aSlotIndex,
+ JS::MutableHandle<JSObject*> aBackingObj,
+ bool* aBackingObjCreated);
+
+// Get the desired prototype object for an object construction from the given
+// CallArgs. Null is returned if the default prototype should be used.
+bool
+GetDesiredProto(JSContext* aCx, const JS::CallArgs& aCallArgs,
+ JS::MutableHandle<JSObject*> aDesiredProto);
+
+void
+SetDocumentAndPageUseCounter(JSContext* aCx, JSObject* aObject,
+ UseCounter aUseCounter);
+
+// Warnings
+void
+DeprecationWarning(JSContext* aCx, JSObject* aObject,
+ nsIDocument::DeprecatedOperations aOperation);
+
+// A callback to perform funToString on an interface object
+JSString*
+InterfaceObjectToString(JSContext* aCx, JS::Handle<JSObject*> aObject,
+ unsigned /* indent */);
+
+namespace binding_detail {
+// Get a JS global object that can be used for some temporary allocations. The
+// idea is that this should be used for situations when you need to operate in
+// _some_ compartment but don't care which one. A typical example is when you
+// have non-JS input, non-JS output, but have to go through some sort of JS
+// representation in the middle, so need a compartment to allocate things in.
+//
+// It's VERY important that any consumers of this function only do things that
+// are guaranteed to be side-effect-free, even in the face of a script
+// environment controlled by a hostile adversary. This is because in the worker
+// case the global is in fact the worker global, so it and its standard objects
+// are controlled by the worker script. This is why this function is in the
+// binding_detail namespace. Any use of this function MUST be very carefully
+// reviewed by someone who is sufficiently devious and has a very good
+// understanding of all the code that will run while we're using the return
+// value, including the SpiderMonkey parts.
+JSObject* UnprivilegedJunkScopeOrWorkerGlobal();
+} // namespace binding_detail
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_BindingUtils_h__ */
diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf
new file mode 100644
index 000000000..aa7f26ad6
--- /dev/null
+++ b/dom/bindings/Bindings.conf
@@ -0,0 +1,1701 @@
+# -*- Mode:Python; tab-width:8; indent-tabs-mode:nil -*- */
+# vim: set ts=8 sts=4 et sw=4 tw=80: */
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.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/webidl. For interfaces requiring
+# special handling, there are corresponding entries in the configuration table
+# below. The configuration table maps each interface name to a |descriptor|.
+#
+# Valid fields for all descriptors:
+# * nativeType - The native type (concrete class or XPCOM interface) that
+# instances of this interface will unwrap to. If not
+# specified, defaults to 'nsIDOM' followed by the interface
+# name for external interfaces and
+# 'mozilla::dom::InterfaceName' for everything else.
+# * headerFile - The file in which the nativeType is declared (defaults
+# to an educated guess).
+# * concrete - Indicates whether there exist JS objects with this interface as
+# their primary interface (and hence whose prototype is this
+# interface's prototype object). Always False for callback
+# interfaces. Defaults to True otherwise.
+# * notflattened - The native type does not have nsIClassInfo, so when
+# wrapping it the right IID needs to be passed in.
+# Only relevant for callback interfaces.
+# * register - True if this binding should be registered. Defaults to true.
+# * binaryNames - Dict for mapping method and attribute names to different
+# names when calling the native methods (defaults to an empty
+# dict). The keys are the property names as they appear in the
+# .webidl file and the values are the names as they should be
+# in the WebIDL.
+# * wrapperCache: True if this object is a wrapper cache. Objects that are
+# not can only be returned from a limited set of methods,
+# cannot be prefable, and must ensure that they disallow
+# XPConnect wrapping. Always false for callback interfaces.
+# Defaults to true for non-callback descriptors.
+#
+# The following fields are either a string, an array (defaults to an empty
+# array) or a dictionary with three possible keys (all, getterOnly and
+# setterOnly) each having such an array as the value
+#
+# * implicitJSContext - attributes and methods specified in the .webidl file
+# that require a JSContext as the first argument
+#
+# The value for an interface is a dictionary which specifies the
+# descriptor to use when generating that interface's binding.
+
+DOMInterfaces = {
+
+'AbstractWorker': {
+ 'concrete': False
+},
+
+'AddonManagerPermissions': {
+ 'wrapperCache': False,
+ 'concrete': False
+},
+
+'AnimationEffectReadOnly': {
+ 'concrete': False
+},
+
+'AnimationTimeline': {
+ 'concrete': False
+},
+
+'AnonymousContent': {
+ 'wrapperCache': False
+},
+
+'ArchiveReader': {
+ 'nativeType': 'mozilla::dom::archivereader::ArchiveReader',
+},
+
+'ArchiveRequest': {
+ 'nativeType': 'mozilla::dom::archivereader::ArchiveRequest',
+},
+
+'AudioChannelManager': {
+ 'nativeType': 'mozilla::dom::system::AudioChannelManager',
+ 'headerFile': 'AudioChannelManager.h'
+},
+
+'AudioBuffer': {
+ 'implicitJSContext': [ 'copyToChannel' ],
+},
+
+'AudioBufferSourceNode': {
+ 'implicitJSContext': [ 'buffer' ],
+},
+
+'AudioNode' : {
+ 'concrete': False,
+ 'binaryNames': {
+ 'channelCountMode': 'channelCountModeValue',
+ 'channelInterpretation': 'channelInterpretationValue',
+ },
+},
+
+'BarProp': {
+ 'headerFile': 'mozilla/dom/BarProps.h',
+},
+
+'Blob': {
+ 'headerFile': 'mozilla/dom/File.h',
+},
+
+'BatteryManager': {
+ 'nativeType': 'mozilla::dom::battery::BatteryManager',
+ 'headerFile': 'BatteryManager.h'
+},
+
+'BoxObject': {
+ 'resultNotAddRefed': ['element'],
+},
+
+'Cache': {
+ 'implicitJSContext': [ 'add', 'addAll' ],
+ 'nativeType': 'mozilla::dom::cache::Cache',
+},
+
+'CacheStorage': {
+ 'nativeType': 'mozilla::dom::cache::CacheStorage',
+},
+
+'CanvasRenderingContext2D': {
+ 'implicitJSContext': [
+ 'createImageData', 'getImageData'
+ ],
+ 'binaryNames': {
+ 'mozImageSmoothingEnabled': 'imageSmoothingEnabled'
+ }
+},
+
+'CaretPosition' : {
+ 'nativeType': 'nsDOMCaretPosition',
+},
+
+'CharacterData': {
+ 'nativeType': 'nsGenericDOMDataNode',
+ 'concrete': False
+},
+
+'ChromeUtils': {
+ # The codegen is dumb, and doesn't understand that this interface is only a
+ # collection of static methods, so we have this `concrete: False` hack.
+ 'concrete': False,
+},
+
+'ChromeWindow': {
+ 'concrete': False,
+},
+
+'ChromeWorker': {
+ 'headerFile': 'mozilla/dom/WorkerPrivate.h',
+ 'nativeType': 'mozilla::dom::workers::ChromeWorkerPrivate',
+},
+
+'Client': {
+ 'nativeType': 'mozilla::dom::workers::ServiceWorkerClient',
+ 'headerFile': 'mozilla/dom/workers/bindings/ServiceWorkerClient.h',
+},
+
+'Clients': {
+ 'nativeType': 'mozilla::dom::workers::ServiceWorkerClients',
+ 'headerFile': 'mozilla/dom/workers/bindings/ServiceWorkerClients.h',
+},
+
+'console': {
+ 'nativeType': 'mozilla::dom::Console',
+},
+
+'ConvolverNode': {
+ 'implicitJSContext': [ 'buffer' ],
+},
+
+'Coordinates': {
+ 'headerFile': 'nsGeoPosition.h'
+},
+
+'Crypto' : {
+ 'headerFile': 'Crypto.h'
+},
+
+'CSS': {
+ 'concrete': False,
+},
+
+'CSS2Properties': {
+ 'nativeType': 'nsDOMCSSDeclaration'
+},
+
+'CSSLexer': {
+ 'wrapperCache': False
+},
+
+'CSSPrimitiveValue': {
+ 'nativeType': 'nsROCSSPrimitiveValue',
+},
+
+'CSSStyleDeclaration': {
+ 'nativeType': 'nsICSSDeclaration'
+},
+
+'CSSStyleSheet': {
+ 'nativeType': 'mozilla::StyleSheet',
+ 'binaryNames': { 'ownerRule': 'DOMOwnerRule' },
+},
+
+'CSSValue': {
+ 'concrete': False
+},
+
+'CSSValueList': {
+ 'nativeType': 'nsDOMCSSValueList'
+},
+
+'DataChannel': {
+ 'nativeType': 'nsDOMDataChannel',
+},
+
+'DedicatedWorkerGlobalScope': {
+ 'headerFile': 'mozilla/dom/WorkerScope.h',
+},
+
+'DeviceAcceleration': {
+ 'headerFile': 'mozilla/dom/DeviceMotionEvent.h',
+},
+
+'DeviceRotationRate': {
+ 'headerFile': 'mozilla/dom/DeviceMotionEvent.h',
+},
+
+'Document': {
+ 'nativeType': 'nsIDocument',
+ 'binaryNames': {
+ 'documentURI': 'documentURIFromJS',
+ 'URL': 'documentURIFromJS'
+ }
+},
+
+'DominatorTree': {
+ 'nativeType': 'mozilla::devtools::DominatorTree'
+},
+
+'DOMException': {
+ 'binaryNames': {
+ 'message': 'messageMoz',
+ },
+ 'implicitJSContext': [ 'filename', 'lineNumber', 'stack' ],
+},
+
+'DOMMatrixReadOnly': {
+ 'headerFile': 'mozilla/dom/DOMMatrix.h',
+ 'concrete': False,
+},
+
+'DOMPointReadOnly': {
+ 'headerFile': 'mozilla/dom/DOMPoint.h',
+ 'concrete': False,
+},
+
+'DOMRectList': {
+ 'headerFile': 'mozilla/dom/DOMRect.h',
+},
+
+'DOMRectReadOnly': {
+ 'headerFile': 'mozilla/dom/DOMRect.h',
+},
+
+'DOMRequest': {
+ 'implicitJSContext': [ 'then' ],
+},
+
+'DOMStringMap': {
+ 'nativeType': 'nsDOMStringMap'
+},
+
+'DOMTokenList': {
+ 'nativeType': 'nsDOMTokenList',
+},
+
+'DynamicsCompressorNode': {
+ 'binaryNames': {
+ 'release': 'getRelease'
+ },
+},
+
+'Event': {
+ 'implicitJSContext': [ 'defaultPrevented', 'preventDefault' ],
+},
+
+'EventTarget': {
+ # When we get rid of hasXPConnectImpls, we can get rid of the
+ # couldBeDOMBinding stuff in GetOrCreateDOMReflector.
+ #
+ # We can also get rid of the UnwrapArg bits in
+ # the dom QueryInterface (in BindingUtils.cpp) at that point.
+ 'hasXPConnectImpls': True,
+ 'concrete': False,
+ 'jsImplParent': 'mozilla::DOMEventTargetHelper',
+ 'implicitJSContext': [ 'dispatchEvent' ]
+},
+
+'Exception': {
+ 'headerFile': 'mozilla/dom/DOMException.h',
+ 'binaryNames': {
+ 'message': 'messageMoz',
+ },
+ 'implicitJSContext': [ '__stringifier', 'filename', 'lineNumber', 'stack' ],
+},
+
+'ExtendableEvent': {
+ 'headerFile': 'mozilla/dom/ServiceWorkerEvents.h',
+ 'nativeType': 'mozilla::dom::workers::ExtendableEvent',
+ 'implicitJSContext': [ 'waitUntil' ],
+},
+
+'ExtendableMessageEvent': {
+ 'headerFile': 'mozilla/dom/ServiceWorkerEvents.h',
+ 'nativeType': 'mozilla::dom::workers::ExtendableMessageEvent',
+},
+
+'FetchEvent': {
+ 'headerFile': 'ServiceWorkerEvents.h',
+ 'nativeType': 'mozilla::dom::workers::FetchEvent',
+ 'binaryNames': {
+ 'request': 'request_'
+ },
+ 'implicitJSContext': [ 'respondWith' ],
+},
+
+'FileReader': {
+ 'implicitJSContext': [ 'readAsArrayBuffer' ],
+},
+
+'FileReaderSync': {
+ 'wrapperCache': False,
+},
+
+'FlyWebFetchEvent': {
+ 'headerFile': 'FlyWebServerEvents.h',
+},
+
+'FlyWebWebSocketEvent': {
+ 'headerFile': 'FlyWebServerEvents.h',
+},
+
+'FontFaceSet': {
+ 'implicitJSContext': [ 'load' ],
+},
+
+'FontFaceSetIterator': {
+ 'wrapperCache': False,
+},
+
+'Geolocation': {
+ 'headerFile': 'nsGeolocation.h'
+},
+
+'HeapSnapshot': {
+ 'nativeType': 'mozilla::devtools::HeapSnapshot'
+},
+
+'History': {
+ 'headerFile': 'nsHistory.h',
+ 'nativeType': 'nsHistory'
+},
+
+'HTMLAppletElement': {
+ 'nativeType': 'mozilla::dom::HTMLSharedObjectElement'
+},
+
+'HTMLBaseElement': {
+ 'nativeType': 'mozilla::dom::HTMLSharedElement'
+},
+
+'HTMLCollection': {
+ 'nativeType': 'nsIHTMLCollection',
+ # nsContentList.h pulls in nsIHTMLCollection.h
+ 'headerFile': 'nsContentList.h',
+},
+
+'HTMLDirectoryElement': {
+ 'nativeType': 'mozilla::dom::HTMLSharedElement'
+},
+
+'HTMLDListElement': {
+ 'nativeType' : 'mozilla::dom::HTMLSharedListElement'
+},
+
+'HTMLDocument': {
+ 'nativeType': 'nsHTMLDocument',
+ 'implicitJSContext': [ 'open', 'write', 'writeln' ]
+},
+
+'HTMLElement': {
+ 'nativeType': 'nsGenericHTMLElement',
+},
+
+'HTMLEmbedElement': {
+ 'nativeType': 'mozilla::dom::HTMLSharedObjectElement'
+},
+
+'HTMLHeadElement': {
+ 'nativeType': 'mozilla::dom::HTMLSharedElement'
+},
+
+'HTMLHtmlElement': {
+ 'nativeType': 'mozilla::dom::HTMLSharedElement'
+},
+
+'HTMLMediaElement': {
+ 'concrete': False
+},
+
+'HTMLOListElement': {
+ 'nativeType' : 'mozilla::dom::HTMLSharedListElement'
+},
+
+'HTMLParamElement': {
+ 'nativeType': 'mozilla::dom::HTMLSharedElement'
+},
+
+'HTMLQuoteElement': {
+ 'nativeType': 'mozilla::dom::HTMLSharedElement'
+},
+
+'HTMLTextAreaElement': {
+ 'binaryNames': {
+ 'textLength': 'getTextLength'
+ }
+},
+
+'HTMLUListElement': {
+ 'nativeType' : 'mozilla::dom::HTMLSharedListElement'
+},
+
+'IDBCursor': {
+ 'implicitJSContext': [ 'delete' ],
+ 'binaryNames': {
+ 'direction': 'getDirection'
+ }
+},
+
+'IDBCursorWithValue': {
+ 'nativeType': 'mozilla::dom::IDBCursor',
+},
+
+'IDBDatabase': {
+ 'implicitJSContext': [ 'transaction', 'createMutableFile',
+ 'mozCreateFileHandle' ],
+},
+
+'IDBFactory': {
+ 'implicitJSContext': [ 'open', 'deleteDatabase', 'openForPrincipal',
+ 'deleteForPrincipal' ],
+},
+
+'IDBIndex': {
+ 'binaryNames': {
+ 'mozGetAll': 'getAll',
+ 'mozGetAllKeys': 'getAllKeys',
+ }
+},
+
+'IDBKeyRange': {
+ 'wrapperCache': False,
+},
+
+'IDBLocaleAwareKeyRange': {
+ 'headerFile': 'IDBKeyRange.h',
+ 'wrapperCache': False,
+},
+
+'IDBObjectStore': {
+ 'binaryNames': {
+ 'mozGetAll': 'getAll'
+ },
+ 'implicitJSContext': [ 'clear' ],
+},
+
+'IDBOpenDBRequest': {
+ 'headerFile': 'IDBRequest.h'
+},
+
+'IDBVersionChangeEvent': {
+ 'headerFile': 'IDBEvents.h',
+},
+
+'IID': {
+ 'nativeType': 'nsIJSID',
+ 'headerFile': 'xpcjsid.h',
+},
+
+'ImageBitmap': {
+ 'implicitJSContext': [ 'mapDataInto' ],
+},
+
+'ImageCapture': {
+ 'binaryNames': { 'videoStreamTrack': 'GetVideoStreamTrack' }
+},
+
+'ImageData': {
+ 'wrapperCache': False,
+},
+
+'InputStream': {
+ 'nativeType': 'nsIInputStream',
+ 'notflattened': True
+},
+
+'IntersectionObserver': {
+ 'nativeType': 'mozilla::dom::DOMIntersectionObserver',
+},
+
+'IntersectionObserverEntry': {
+ 'nativeType': 'mozilla::dom::DOMIntersectionObserverEntry',
+ 'headerFile': 'DOMIntersectionObserver.h',
+},
+
+'KeyEvent': {
+ 'concrete': False
+},
+
+'KeyframeEffect': {
+ 'implicitJSContext': { 'setterOnly': [ 'spacing' ] }
+},
+
+'LegacyMozTCPSocket': {
+ 'headerFile': 'TCPSocket.h',
+ 'wrapperCache': False,
+},
+
+'LocalMediaStream': {
+ 'headerFile': 'DOMMediaStream.h',
+ 'nativeType': 'mozilla::DOMLocalMediaStream'
+},
+
+'MediaList': {
+ 'nativeType': 'nsMediaList',
+ 'headerFile': 'nsIMediaList.h',
+},
+
+'MediaKeys' : {
+ 'implicitJSContext': [ 'createSession']
+},
+
+'MediaStream': {
+ 'headerFile': 'DOMMediaStream.h',
+ 'nativeType': 'mozilla::DOMMediaStream'
+},
+
+'MediaStreamAudioDestinationNode': {
+ 'binaryNames': { 'stream': 'DOMStream' }
+},
+
+'MediaStreamList': {
+ 'headerFile': 'MediaStreamList.h',
+},
+
+'MediaStreamTrack': {
+ 'concrete': False
+},
+
+'MediaRecorder': {
+ 'headerFile': 'MediaRecorder.h',
+},
+
+'MimeType': {
+ 'headerFile' : 'nsMimeTypeArray.h',
+ 'nativeType': 'nsMimeType',
+},
+
+'MimeTypeArray': {
+ 'nativeType': 'nsMimeTypeArray',
+},
+
+'MozCanvasPrintState': {
+ 'headerFile': 'mozilla/dom/HTMLCanvasElement.h',
+ 'nativeType': 'mozilla::dom::HTMLCanvasPrintState',
+},
+
+'MozChannel': {
+ 'nativeType': 'nsIChannel',
+ 'notflattened': True
+},
+
+'MozSpeakerManager': {
+ 'nativeType': 'mozilla::dom::SpeakerManager',
+ 'headerFile': 'SpeakerManager.h'
+},
+
+'MozPowerManager': {
+ 'nativeType': 'mozilla::dom::PowerManager',
+},
+
+'MozWakeLock': {
+ 'nativeType': 'mozilla::dom::WakeLock',
+},
+
+'MozTimeManager': {
+ 'nativeType': 'mozilla::dom::time::TimeManager',
+},
+
+'MutationObserver': {
+ 'nativeType': 'nsDOMMutationObserver',
+},
+
+'MutationRecord': {
+ 'nativeType': 'nsDOMMutationRecord',
+ 'headerFile': 'nsDOMMutationObserver.h',
+},
+
+'NamedNodeMap': {
+ 'nativeType': 'nsDOMAttributeMap',
+},
+
+'NetworkInformation': {
+ 'nativeType': 'mozilla::dom::network::Connection',
+},
+
+'Node': {
+ 'nativeType': 'nsINode',
+ 'concrete': False,
+ 'binaryNames': {
+ 'baseURI': 'baseURIFromJS'
+ }
+},
+
+'NodeIterator': {
+ 'wrapperCache': False,
+},
+
+'NodeList': {
+ 'nativeType': 'nsINodeList',
+},
+
+'NotificationEvent': {
+ 'headerFile': 'mozilla/dom/NotificationEvent.h',
+ 'nativeType': 'mozilla::dom::workers::NotificationEvent',
+ 'binaryNames': {
+ 'notification': 'notification_'
+ }
+},
+
+'OfflineAudioContext': {
+ 'nativeType': 'mozilla::dom::AudioContext',
+},
+
+'OfflineResourceList': {
+ 'nativeType': 'nsDOMOfflineResourceList',
+},
+
+'PaintRequestList': {
+ 'headerFile': 'mozilla/dom/PaintRequest.h',
+},
+
+'Path2D': {
+ 'nativeType': 'mozilla::dom::CanvasPath',
+ 'headerFile': 'CanvasPath.h'
+},
+
+'PeerConnectionImpl': {
+ 'nativeType': 'mozilla::PeerConnectionImpl',
+ 'headerFile': 'PeerConnectionImpl.h',
+ 'wrapperCache': False
+},
+
+'Plugin': {
+ 'headerFile' : 'nsPluginArray.h',
+ 'nativeType': 'nsPluginElement',
+},
+
+'PluginArray': {
+ 'nativeType': 'nsPluginArray',
+},
+
+'PluginTag': {
+ 'nativeType': 'nsIPluginTag',
+},
+
+'PopupBoxObject': {
+ 'resultNotAddRefed': ['triggerNode', 'anchorNode'],
+},
+
+'Position': {
+ 'headerFile': 'nsGeoPosition.h'
+},
+
+'PositionError': {
+ 'headerFile': 'nsGeolocation.h'
+},
+
+'Promise': {
+ 'implicitJSContext': [ 'then', 'catch' ],
+},
+
+'PromiseDebugging': {
+ 'concrete': False,
+},
+
+'PromiseNativeHandler': {
+ 'wrapperCache': False,
+},
+
+'PushEvent': {
+ 'headerFile': 'ServiceWorkerEvents.h',
+ 'nativeType': 'mozilla::dom::workers::PushEvent',
+},
+
+'PushMessageData': {
+ 'headerFile': 'ServiceWorkerEvents.h',
+ 'nativeType': 'mozilla::dom::workers::PushMessageData',
+},
+
+'Range': {
+ 'nativeType': 'nsRange',
+ 'binaryNames': {
+ '__stringifier': 'ToString'
+ }
+},
+
+'Rect': {
+ 'nativeType': 'nsDOMCSSRect',
+},
+
+'Request': {
+ 'binaryNames': {
+ 'headers': 'headers_',
+ 'referrerPolicy': 'referrerPolicy_'
+ },
+},
+
+'Response': {
+ 'binaryNames': { 'headers': 'headers_' },
+},
+
+'RGBColor': {
+ 'nativeType': 'nsDOMCSSRGBColor',
+},
+
+'Screen': {
+ 'nativeType': 'nsScreen',
+},
+
+'ServiceWorker': {
+ 'nativeType': 'mozilla::dom::workers::ServiceWorker',
+ 'headerFile': 'mozilla/dom/workers/bindings/ServiceWorker.h',
+},
+
+'ServiceWorkerGlobalScope': {
+ 'headerFile': 'mozilla/dom/WorkerScope.h',
+},
+
+'ServiceWorkerRegistration': {
+ 'implicitJSContext': [ 'pushManager' ],
+},
+
+'SharedWorker': {
+ 'nativeType': 'mozilla::dom::workers::SharedWorker',
+ 'headerFile': 'mozilla/dom/workers/bindings/SharedWorker.h',
+ 'implicitJSContext': [ 'constructor' ],
+},
+
+'SharedWorkerGlobalScope': {
+ 'headerFile': 'mozilla/dom/WorkerScope.h',
+},
+
+'Storage': {
+ 'nativeType': 'mozilla::dom::DOMStorage',
+},
+
+'StyleSheet': {
+ 'nativeType': 'mozilla::StyleSheet',
+ 'headerFile': 'mozilla/StyleSheetInlines.h',
+},
+
+'SVGAnimatedLengthList': {
+ 'nativeType': 'mozilla::DOMSVGAnimatedLengthList',
+ 'headerFile': 'DOMSVGAnimatedLengthList.h',
+},
+
+'SVGAnimatedNumberList': {
+ 'nativeType': 'mozilla::DOMSVGAnimatedNumberList',
+ 'headerFile': 'DOMSVGAnimatedNumberList.h'
+},
+
+'SVGAnimatedPreserveAspectRatio': {
+ 'nativeType': 'mozilla::dom::DOMSVGAnimatedPreserveAspectRatio',
+ 'headerFile': 'SVGAnimatedPreserveAspectRatio.h'
+},
+
+'SVGAnimationElement': {
+ 'concrete': False
+},
+
+'SVGComponentTransferFunctionElement': {
+ 'concrete': False,
+},
+
+'SVGElement': {
+ 'nativeType': 'nsSVGElement',
+},
+
+'SVGFEFuncAElement': {
+ 'headerFile': 'mozilla/dom/SVGComponentTransferFunctionElement.h',
+},
+
+'SVGFEFuncBElement': {
+ 'headerFile': 'mozilla/dom/SVGComponentTransferFunctionElement.h',
+},
+
+'SVGFEFuncGElement': {
+ 'headerFile': 'mozilla/dom/SVGComponentTransferFunctionElement.h',
+},
+
+'SVGFEFuncRElement': {
+ 'headerFile': 'mozilla/dom/SVGComponentTransferFunctionElement.h',
+},
+
+'SVGGraphicsElement': {
+ 'concrete': False,
+},
+
+'SVGGradientElement': {
+ 'concrete': False,
+},
+
+'SVGLength': {
+ 'nativeType': 'mozilla::DOMSVGLength',
+ 'headerFile': 'DOMSVGLength.h'
+},
+
+'SVGLengthList': {
+ 'nativeType': 'mozilla::DOMSVGLengthList',
+ 'headerFile': 'DOMSVGLengthList.h'
+},
+
+'SVGLinearGradientElement': {
+ 'headerFile': 'mozilla/dom/SVGGradientElement.h',
+},
+
+'SVGNumber': {
+ 'nativeType': 'mozilla::DOMSVGNumber',
+ 'headerFile': 'DOMSVGNumber.h',
+},
+
+'SVGNumberList': {
+ 'nativeType': 'mozilla::DOMSVGNumberList',
+ 'headerFile': 'DOMSVGNumberList.h'
+},
+
+'SVGPathSeg': {
+ 'nativeType': 'mozilla::DOMSVGPathSeg',
+ 'headerFile': 'DOMSVGPathSeg.h',
+ 'concrete': False,
+},
+
+'SVGPathSegClosePath': {
+ 'nativeType': 'mozilla::DOMSVGPathSegClosePath',
+ 'headerFile': 'DOMSVGPathSeg.h'
+},
+
+'SVGPathSegMovetoAbs': {
+ 'nativeType': 'mozilla::DOMSVGPathSegMovetoAbs',
+ 'headerFile': 'DOMSVGPathSeg.h'
+},
+
+'SVGPathSegMovetoRel': {
+ 'nativeType': 'mozilla::DOMSVGPathSegMovetoRel',
+ 'headerFile': 'DOMSVGPathSeg.h'
+},
+
+'SVGPathSegLinetoAbs': {
+ 'nativeType': 'mozilla::DOMSVGPathSegLinetoAbs',
+ 'headerFile': 'DOMSVGPathSeg.h'
+},
+
+'SVGPathSegLinetoRel': {
+ 'nativeType': 'mozilla::DOMSVGPathSegLinetoRel',
+ 'headerFile': 'DOMSVGPathSeg.h'
+},
+
+'SVGPathSegCurvetoCubicAbs': {
+ 'nativeType': 'mozilla::DOMSVGPathSegCurvetoCubicAbs',
+ 'headerFile': 'DOMSVGPathSeg.h'
+},
+
+'SVGPathSegCurvetoCubicRel': {
+ 'nativeType': 'mozilla::DOMSVGPathSegCurvetoCubicRel',
+ 'headerFile': 'DOMSVGPathSeg.h'
+},
+
+'SVGPathSegCurvetoQuadraticAbs': {
+ 'nativeType': 'mozilla::DOMSVGPathSegCurvetoQuadraticAbs',
+ 'headerFile': 'DOMSVGPathSeg.h'
+},
+
+'SVGPathSegCurvetoQuadraticRel': {
+ 'nativeType': 'mozilla::DOMSVGPathSegCurvetoQuadraticRel',
+ 'headerFile': 'DOMSVGPathSeg.h'
+},
+
+'SVGPathSegArcAbs': {
+ 'nativeType': 'mozilla::DOMSVGPathSegArcAbs',
+ 'headerFile': 'DOMSVGPathSeg.h'
+},
+
+'SVGPathSegArcRel': {
+ 'nativeType': 'mozilla::DOMSVGPathSegArcRel',
+ 'headerFile': 'DOMSVGPathSeg.h'
+},
+
+'SVGPathSegLinetoHorizontalAbs': {
+ 'nativeType': 'mozilla::DOMSVGPathSegLinetoHorizontalAbs',
+ 'headerFile': 'DOMSVGPathSeg.h'
+},
+
+'SVGPathSegLinetoHorizontalRel': {
+ 'nativeType': 'mozilla::DOMSVGPathSegLinetoHorizontalRel',
+ 'headerFile': 'DOMSVGPathSeg.h'
+},
+
+'SVGPathSegLinetoVerticalAbs': {
+ 'nativeType': 'mozilla::DOMSVGPathSegLinetoVerticalAbs',
+ 'headerFile': 'DOMSVGPathSeg.h'
+},
+
+'SVGPathSegLinetoVerticalRel': {
+ 'nativeType': 'mozilla::DOMSVGPathSegLinetoVerticalRel',
+ 'headerFile': 'DOMSVGPathSeg.h'
+},
+
+'SVGPathSegCurvetoCubicSmoothAbs': {
+ 'nativeType': 'mozilla::DOMSVGPathSegCurvetoCubicSmoothAbs',
+ 'headerFile': 'DOMSVGPathSeg.h'
+},
+
+'SVGPathSegCurvetoCubicSmoothRel': {
+ 'nativeType': 'mozilla::DOMSVGPathSegCurvetoCubicSmoothRel',
+ 'headerFile': 'DOMSVGPathSeg.h'
+},
+
+'SVGPathSegCurvetoQuadraticSmoothAbs': {
+ 'nativeType': 'mozilla::DOMSVGPathSegCurvetoQuadraticSmoothAbs',
+ 'headerFile': 'DOMSVGPathSeg.h'
+},
+
+'SVGPathSegCurvetoQuadraticSmoothRel': {
+ 'nativeType': 'mozilla::DOMSVGPathSegCurvetoQuadraticSmoothRel',
+ 'headerFile': 'DOMSVGPathSeg.h'
+},
+
+'SVGPathSegList': {
+ 'nativeType': 'mozilla::DOMSVGPathSegList',
+ 'headerFile': 'DOMSVGPathSegList.h'
+},
+
+'SVGPoint': {
+ 'nativeType': 'mozilla::nsISVGPoint',
+ 'headerFile': 'nsISVGPoint.h'
+},
+
+'SVGPointList': {
+ 'nativeType': 'mozilla::DOMSVGPointList',
+ 'headerFile': 'DOMSVGPointList.h'
+},
+
+'SVGPreserveAspectRatio': {
+ 'nativeType': 'mozilla::dom::DOMSVGPreserveAspectRatio',
+ 'headerFile': 'SVGPreserveAspectRatio.h'
+},
+
+'SVGRadialGradientElement': {
+ 'headerFile': 'mozilla/dom/SVGGradientElement.h',
+},
+
+'SVGRect': {
+ 'nativeType': 'mozilla::dom::SVGIRect'
+},
+
+'SVGTextContentElement': {
+ 'concrete': False
+},
+
+'SVGTextPositioningElement': {
+ 'concrete': False
+},
+
+'SVGTransform': {
+ 'binaryNames': {
+ "matrix": "GetMatrix"
+ }
+},
+
+'SVGTransformList': {
+ 'nativeType': 'mozilla::DOMSVGTransformList',
+ 'headerFile': 'DOMSVGTransformList.h'
+},
+
+'SVGStringList': {
+ 'nativeType': 'mozilla::DOMSVGStringList',
+ 'headerFile': 'DOMSVGStringList.h',
+},
+
+'SVGUnitTypes' : {
+ 'concrete': False,
+},
+
+'SVGZoomAndPan' : {
+ 'concrete': False,
+},
+
+'TestFunctions': {
+ 'wrapperCache': False
+},
+
+'Text': {
+ # Total hack to allow binding code to realize that nsTextNode can
+ # in fact be cast to Text.
+ 'headerFile': 'nsTextNode.h',
+},
+
+'TextDecoder': {
+ 'wrapperCache': False
+},
+
+'TextEncoder': {
+ 'wrapperCache': False
+},
+
+'TextMetrics': {
+ 'wrapperCache': False
+},
+
+'TCPSocket': {
+ 'implicitJSContext': ['send']
+},
+
+'ThreadSafeChromeUtils': {
+ # The codegen is dumb, and doesn't understand that this interface is only a
+ # collection of static methods, so we have this `concrete: False` hack.
+ 'concrete': False,
+ 'headerFile': 'mozilla/dom/ChromeUtils.h',
+},
+
+'TouchList': {
+ 'headerFile': 'mozilla/dom/TouchEvent.h',
+},
+
+'TreeColumn': {
+ 'nativeType': 'nsTreeColumn',
+ 'headerFile': 'nsTreeColumns.h',
+},
+
+'TreeColumns': {
+ 'nativeType': 'nsTreeColumns',
+},
+
+'TreeWalker': {
+ 'wrapperCache': False,
+},
+
+'VTTCue': {
+ 'nativeType': 'mozilla::dom::TextTrackCue'
+},
+
+'VTTRegion': {
+ 'nativeType': 'mozilla::dom::TextTrackRegion',
+},
+
+'WindowClient': {
+ 'nativeType': 'mozilla::dom::workers::ServiceWorkerWindowClient',
+ 'headerFile': 'mozilla/dom/workers/bindings/ServiceWorkerWindowClient.h',
+},
+
+'WebGLActiveInfo': {
+ 'nativeType': 'mozilla::WebGLActiveInfo',
+ 'headerFile': 'WebGLActiveInfo.h'
+},
+
+'WebGLBuffer': {
+ 'nativeType': 'mozilla::WebGLBuffer',
+ 'headerFile': 'WebGLBuffer.h'
+},
+
+'WEBGL_compressed_texture_atc': {
+ 'nativeType': 'mozilla::WebGLExtensionCompressedTextureATC',
+ 'headerFile': 'WebGLExtensions.h'
+},
+
+'WEBGL_compressed_texture_etc': {
+ 'nativeType': 'mozilla::WebGLExtensionCompressedTextureES3',
+ 'headerFile': 'WebGLExtensions.h'
+},
+
+'WEBGL_compressed_texture_etc1': {
+ 'nativeType': 'mozilla::WebGLExtensionCompressedTextureETC1',
+ 'headerFile': 'WebGLExtensions.h'
+},
+
+'WEBGL_compressed_texture_pvrtc': {
+ 'nativeType': 'mozilla::WebGLExtensionCompressedTexturePVRTC',
+ 'headerFile': 'WebGLExtensions.h'
+},
+
+'WEBGL_compressed_texture_s3tc': {
+ 'nativeType': 'mozilla::WebGLExtensionCompressedTextureS3TC',
+ 'headerFile': 'WebGLExtensions.h'
+},
+
+'WEBGL_depth_texture': {
+ 'nativeType': 'mozilla::WebGLExtensionDepthTexture',
+ 'headerFile': 'WebGLExtensions.h'
+},
+
+'WEBGL_debug_renderer_info': {
+ 'nativeType': 'mozilla::WebGLExtensionDebugRendererInfo',
+ 'headerFile': 'WebGLExtensions.h'
+},
+
+'WEBGL_debug_shaders': {
+ 'nativeType': 'mozilla::WebGLExtensionDebugShaders',
+ 'headerFile': 'WebGLExtensions.h'
+},
+
+'OES_element_index_uint': {
+ 'nativeType': 'mozilla::WebGLExtensionElementIndexUint',
+ 'headerFile': 'WebGLExtensions.h'
+},
+
+'EXT_frag_depth': {
+ 'nativeType': 'mozilla::WebGLExtensionFragDepth',
+ 'headerFile': 'WebGLExtensions.h'
+},
+
+'WEBGL_lose_context': {
+ 'nativeType': 'mozilla::WebGLExtensionLoseContext',
+ 'headerFile': 'WebGLExtensions.h'
+},
+
+'EXT_sRGB': {
+ 'nativeType': 'mozilla::WebGLExtensionSRGB',
+ 'headerFile': 'WebGLExtensions.h'
+},
+
+'OES_standard_derivatives': {
+ 'nativeType': 'mozilla::WebGLExtensionStandardDerivatives',
+ 'headerFile': 'WebGLExtensions.h'
+},
+
+'EXT_shader_texture_lod': {
+ 'nativeType': 'mozilla::WebGLExtensionShaderTextureLod',
+ 'headerFile': 'WebGLExtensions.h'
+},
+
+'EXT_texture_filter_anisotropic': {
+ 'nativeType': 'mozilla::WebGLExtensionTextureFilterAnisotropic',
+ 'headerFile': 'WebGLExtensions.h'
+},
+
+'OES_texture_float': {
+ 'nativeType': 'mozilla::WebGLExtensionTextureFloat',
+ 'headerFile': 'WebGLExtensions.h'
+},
+
+'OES_texture_float_linear': {
+ 'nativeType': 'mozilla::WebGLExtensionTextureFloatLinear',
+ 'headerFile': 'WebGLExtensions.h'
+},
+
+'OES_texture_half_float': {
+ 'nativeType': 'mozilla::WebGLExtensionTextureHalfFloat',
+ 'headerFile': 'WebGLExtensions.h'
+},
+
+'OES_texture_half_float_linear': {
+ 'nativeType': 'mozilla::WebGLExtensionTextureHalfFloatLinear',
+ 'headerFile': 'WebGLExtensions.h'
+},
+
+'WEBGL_color_buffer_float': {
+ 'nativeType': 'mozilla::WebGLExtensionColorBufferFloat',
+ 'headerFile': 'WebGLExtensions.h'
+},
+
+'EXT_color_buffer_half_float': {
+ 'nativeType': 'mozilla::WebGLExtensionColorBufferHalfFloat',
+ 'headerFile': 'WebGLExtensions.h'
+},
+
+'EXT_color_buffer_float': {
+ 'nativeType': 'mozilla::WebGLExtensionEXTColorBufferFloat',
+ 'headerFile': 'WebGLExtensions.h'
+},
+
+'WEBGL_draw_buffers': {
+ 'nativeType': 'mozilla::WebGLExtensionDrawBuffers',
+ 'headerFile': 'WebGLExtensions.h'
+},
+
+'OES_vertex_array_object': {
+ 'nativeType': 'mozilla::WebGLExtensionVertexArray',
+ 'headerFile': 'WebGLExtensions.h'
+},
+
+'ANGLE_instanced_arrays': {
+ 'nativeType': 'mozilla::WebGLExtensionInstancedArrays',
+ 'headerFile': 'WebGLExtensions.h'
+},
+
+'EXT_blend_minmax': {
+ 'nativeType': 'mozilla::WebGLExtensionBlendMinMax',
+ 'headerFile': 'WebGLExtensions.h'
+},
+
+'EXT_disjoint_timer_query': {
+ 'nativeType': 'mozilla::WebGLExtensionDisjointTimerQuery',
+ 'headerFile': 'WebGLExtensions.h'
+},
+
+'WebGLFramebuffer': {
+ 'nativeType': 'mozilla::WebGLFramebuffer',
+ 'headerFile': 'WebGLFramebuffer.h'
+},
+
+'WebGLProgram': {
+ 'nativeType': 'mozilla::WebGLProgram',
+ 'headerFile': 'WebGLProgram.h'
+},
+
+'WebGLQuery': {
+ 'nativeType': 'mozilla::WebGLQuery',
+ 'headerFile': 'WebGLQuery.h'
+},
+
+'WebGLRenderbuffer': {
+ 'nativeType': 'mozilla::WebGLRenderbuffer',
+ 'headerFile': 'WebGLRenderbuffer.h'
+},
+
+'WebGLRenderingContext': {
+ 'nativeType': 'mozilla::WebGLContext',
+ 'headerFile': 'WebGLContext.h',
+},
+
+'WebGL2RenderingContext': {
+ 'nativeType': 'mozilla::WebGL2Context',
+ 'headerFile': 'WebGL2Context.h',
+},
+
+'WebGLSampler': {
+ 'nativeType': 'mozilla::WebGLSampler',
+ 'headerFile': 'WebGLSampler.h'
+},
+
+'WebGLShader': {
+ 'nativeType': 'mozilla::WebGLShader',
+ 'headerFile': 'WebGLShader.h'
+},
+
+'WebGLShaderPrecisionFormat': {
+ 'nativeType': 'mozilla::WebGLShaderPrecisionFormat',
+ 'headerFile': 'WebGLShaderPrecisionFormat.h',
+ 'wrapperCache': False
+},
+
+'WebGLSync': {
+ 'nativeType': 'mozilla::WebGLSync',
+ 'headerFile': 'WebGLSync.h'
+},
+
+'WebGLTexture': {
+ 'nativeType': 'mozilla::WebGLTexture',
+ 'headerFile': 'WebGLTexture.h'
+},
+
+'WebGLTransformFeedback': {
+ 'nativeType': 'mozilla::WebGLTransformFeedback',
+ 'headerFile': 'WebGLTransformFeedback.h'
+},
+
+'WebGLUniformLocation': {
+ 'nativeType': 'mozilla::WebGLUniformLocation',
+ 'headerFile': 'WebGLUniformLocation.h'
+},
+
+'WebGLVertexArrayObject': {
+ 'nativeType': 'mozilla::WebGLVertexArray',
+ 'headerFile': 'WebGLVertexArray.h'
+},
+
+'WebrtcGlobalInformation': {
+ 'nativeType': 'mozilla::dom::WebrtcGlobalInformation',
+ 'headerFile': 'WebrtcGlobalInformation.h',
+ 'wrapperCache': False,
+ 'concrete': False,
+},
+
+'Window': {
+ 'nativeType': 'nsGlobalWindow',
+ 'binaryNames': {
+ 'postMessage': 'postMessageMoz',
+ },
+ 'implicitJSContext': [
+ 'requestIdleCallback'
+ ],
+},
+
+'WindowProxy': {
+ 'nativeType': 'nsPIDOMWindowOuter',
+ 'headerFile': 'nsPIDOMWindow.h',
+ 'concrete': False
+},
+
+'WindowRoot': {
+ 'nativeType': 'nsWindowRoot'
+},
+
+'Worker': {
+ 'headerFile': 'mozilla/dom/WorkerPrivate.h',
+ 'nativeType': 'mozilla::dom::workers::WorkerPrivate',
+},
+
+'WorkerDebuggerGlobalScope': {
+ 'headerFile': 'mozilla/dom/WorkerScope.h',
+ 'implicitJSContext': [
+ 'dump', 'global', 'reportError', 'setConsoleEventHandler',
+ ],
+},
+
+'WorkerGlobalScope': {
+ 'headerFile': 'mozilla/dom/WorkerScope.h',
+ 'concrete': False,
+ 'implicitJSContext': [
+ 'close',
+ ],
+ # Rename a few things so we don't have both classes and methods
+ # with the same name
+ 'binaryNames': {
+ 'performance': 'getPerformance',
+ },
+},
+
+'XMLHttpRequest': {
+ 'implicitJSContext': [ 'send'],
+},
+
+'XMLHttpRequestEventTarget': {
+ 'concrete': False
+},
+
+'XMLSerializer': {
+ 'nativeType': 'nsDOMSerializer',
+},
+
+'XPathEvaluator': {
+ 'wrapperCache': False
+},
+
+'XPathExpression': {
+ 'wrapperCache': False,
+},
+
+'XSLTProcessor': {
+ 'nativeType': 'txMozillaXSLTProcessor',
+},
+
+'XULDocument': {
+ 'headerFile': 'XULDocument.h'
+},
+
+'XULElement': {
+ 'nativeType': 'nsXULElement',
+},
+
+####################################
+# Test Interfaces of various sorts #
+####################################
+
+'TestInterface' : {
+ # Keep this in sync with TestExampleInterface
+ 'headerFile': 'TestBindingHeader.h',
+ 'register': False,
+ 'binaryNames': { 'methodRenamedFrom': 'methodRenamedTo',
+ 'attributeGetterRenamedFrom': 'attributeGetterRenamedTo',
+ 'attributeRenamedFrom': 'attributeRenamedTo' }
+ },
+
+'TestParentInterface' : {
+ 'headerFile': 'TestBindingHeader.h',
+ 'register': False,
+ },
+
+'TestChildInterface' : {
+ 'headerFile': 'TestBindingHeader.h',
+ 'register': False,
+ },
+
+'TestCImplementedInterface' : {
+ 'headerFile': 'TestCImplementedInterface.h',
+ 'register': False,
+ },
+
+'TestCImplementedInterface2' : {
+ 'headerFile': 'TestCImplementedInterface.h',
+ 'register': False,
+ },
+
+'TestJSImplInterface' : {
+ # Keep this in sync with TestExampleInterface
+ 'headerFile': 'TestJSImplGenBinding.h',
+ 'register': False,
+ 'binaryNames': { 'methodRenamedFrom': 'methodRenamedTo',
+ 'attributeGetterRenamedFrom': 'attributeGetterRenamedTo',
+ 'attributeRenamedFrom': 'attributeRenamedTo' }
+ },
+
+'TestJSImplInterface2' : {
+ 'headerFile': 'TestJSImplGenBinding.h',
+ 'register': False
+ },
+
+'TestJSImplInterface3' : {
+ 'headerFile': 'TestJSImplGenBinding.h',
+ 'register': False
+ },
+
+'TestJSImplInterface4' : {
+ 'headerFile': 'TestJSImplGenBinding.h',
+ 'register': False
+ },
+
+'TestJSImplInterface5' : {
+ 'headerFile': 'TestJSImplGenBinding.h',
+ 'register': False
+ },
+
+'TestJSImplInterface6' : {
+ 'headerFile': 'TestJSImplGenBinding.h',
+ 'register': False
+ },
+
+'TestNavigator' : {
+ 'headerFile': 'TestJSImplGenBinding.h',
+ 'register' : False
+ },
+
+'TestNavigatorWithConstructor' : {
+ 'headerFile': 'TestJSImplGenBinding.h',
+ 'register' : False
+ },
+
+'TestExternalInterface' : {
+ 'nativeType': 'mozilla::dom::TestExternalInterface',
+ 'headerFile': 'TestBindingHeader.h',
+ 'register': False
+ },
+
+'TestNonWrapperCacheInterface' : {
+ 'headerFile': 'TestBindingHeader.h',
+ 'register': False,
+ 'wrapperCache': False
+ },
+
+'IndirectlyImplementedInterface': {
+ 'headerFile': 'TestBindingHeader.h',
+ 'register': False,
+ 'castable': False,
+ 'concrete': False
+ },
+
+'OnlyForUseInConstructor' : {
+ 'headerFile': 'TestBindingHeader.h',
+ 'register': False
+ },
+
+'ImplementedInterface' : {
+ 'headerFile': 'TestBindingHeader.h',
+ 'concrete': False,
+ 'register': False,
+ },
+
+'ImplementedInterfaceParent' : {
+ 'headerFile': 'TestBindingHeader.h',
+ 'concrete': False,
+ 'register': False
+ },
+
+'DiamondImplements' : {
+ 'headerFile': 'TestBindingHeader.h',
+ 'concrete': False,
+ 'register': False
+ },
+
+'DiamondBranch1A' : {
+ 'headerFile': 'TestBindingHeader.h',
+ 'concrete': False,
+ 'register': False
+ },
+
+'DiamondBranch1B' : {
+ 'headerFile': 'TestBindingHeader.h',
+ 'concrete': False,
+ 'register': False
+ },
+
+'DiamondBranch2A' : {
+ 'headerFile': 'TestBindingHeader.h',
+ 'concrete': False,
+ 'register': False
+ },
+
+'DiamondBranch2B' : {
+ 'headerFile': 'TestBindingHeader.h',
+ 'concrete': False,
+ 'register': False
+ },
+
+'TestIndexedGetterInterface' : {
+ 'headerFile': 'TestBindingHeader.h',
+ 'register': False
+ },
+
+'TestNamedGetterInterface' : {
+ 'headerFile': 'TestBindingHeader.h',
+ 'register': False
+ },
+
+'TestIndexedGetterAndSetterAndNamedGetterInterface' : {
+ 'headerFile': 'TestBindingHeader.h',
+ 'register': False
+ },
+
+'TestIndexedAndNamedGetterInterface' : {
+ 'headerFile': 'TestBindingHeader.h',
+ 'register': False
+ },
+
+'TestIndexedSetterInterface' : {
+ 'headerFile': 'TestBindingHeader.h',
+ 'register': False
+ },
+
+'TestNamedSetterInterface' : {
+ 'headerFile': 'TestBindingHeader.h',
+ 'register': False
+ },
+
+'TestIndexedAndNamedSetterInterface' : {
+ 'headerFile': 'TestBindingHeader.h',
+ 'register': False
+ },
+
+'TestIndexedAndNamedGetterAndSetterInterface' : {
+ 'headerFile': 'TestBindingHeader.h',
+ 'register': False,
+ },
+
+'TestRenamedInterface' : {
+ 'headerFile': 'TestBindingHeader.h',
+ 'register': False,
+ 'nativeType': 'nsRenamedInterface'
+ },
+
+'TestNamedDeleterInterface' : {
+ 'headerFile': 'TestBindingHeader.h',
+ 'register': False
+ },
+
+'TestNamedDeleterWithRetvalInterface' : {
+ 'headerFile': 'TestBindingHeader.h',
+ 'register': False
+ },
+
+'TestCppKeywordNamedMethodsInterface' : {
+ 'headerFile': 'TestBindingHeader.h',
+ 'register': False
+ },
+
+'TestExampleInterface' : {
+ # Keep this in sync with TestInterface
+ 'headerFile': 'TestExampleInterface-example.h',
+ 'register': False,
+ 'binaryNames': { 'methodRenamedFrom': 'methodRenamedTo',
+ 'attributeGetterRenamedFrom': 'attributeGetterRenamedTo',
+ 'attributeRenamedFrom': 'attributeRenamedTo' }
+ },
+
+'TestExampleWorkerInterface' : {
+ 'headerFile': 'TestExampleWorkerInterface-example.h',
+ 'register': False,
+ },
+
+'TestExampleProxyInterface' : {
+ 'headerFile': 'TestExampleProxyInterface-example.h',
+ 'register': False
+ },
+
+'TestDeprecatedInterface' : {
+ # Keep this in sync with TestExampleInterface
+ 'headerFile': 'TestBindingHeader.h',
+ 'register': False
+ },
+
+'TestInterfaceWithPromiseConstructorArg' : {
+ 'headerFile': 'TestBindingHeader.h',
+ 'register': False,
+ },
+
+'TestSecureContextInterface' : {
+ # Keep this in sync with TestExampleInterface
+ 'headerFile': 'TestBindingHeader.h',
+ 'register': False
+ },
+
+'TestNamespace' : {
+ 'headerFile': 'TestBindingHeader.h',
+ 'register': False,
+ },
+
+'TestRenamedNamespace' : {
+ 'headerFile': 'TestBindingHeader.h',
+ 'register': False,
+ },
+
+'TestProtoObjectHackedNamespace' : {
+ 'headerFile': 'TestBindingHeader.h',
+ 'register': False,
+ },
+
+'TestWorkerExposedInterface' : {
+ 'headerFile': 'TestBindingHeader.h',
+ 'register': False,
+ },
+
+}
+
+# These are temporary, until they've been converted to use new DOM bindings
+def addExternalIface(iface, nativeType=None, headerFile=None,
+ notflattened=False):
+ if iface in DOMInterfaces:
+ raise Exception('Interface declared both as WebIDL and External interface')
+ domInterface = {
+ 'concrete': False
+ }
+ if not nativeType is None:
+ domInterface['nativeType'] = nativeType
+ if not headerFile is None:
+ domInterface['headerFile'] = headerFile
+ domInterface['notflattened'] = notflattened
+ DOMInterfaces[iface] = domInterface
+
+addExternalIface('ApplicationCache', nativeType='nsIDOMOfflineResourceList')
+addExternalIface('Counter')
+addExternalIface('CSSRule')
+addExternalIface('RTCDataChannel', nativeType='nsIDOMDataChannel')
+addExternalIface('HitRegionOptions', nativeType='nsISupports')
+addExternalIface('imgINotificationObserver', nativeType='imgINotificationObserver')
+addExternalIface('imgIRequest', nativeType='imgIRequest', notflattened=True)
+addExternalIface('MenuBuilder', nativeType='nsIMenuBuilder', notflattened=True)
+addExternalIface('MozControllers', nativeType='nsIControllers')
+addExternalIface('MozFrameLoader', nativeType='nsIFrameLoader', notflattened=True)
+addExternalIface('MozObserver', nativeType='nsIObserver', notflattened=True)
+addExternalIface('MozRDFCompositeDataSource', nativeType='nsIRDFCompositeDataSource',
+ notflattened=True)
+addExternalIface('MozRDFResource', nativeType='nsIRDFResource', notflattened=True)
+addExternalIface('MozTreeView', nativeType='nsITreeView',
+ headerFile='nsITreeView.h', notflattened=True)
+addExternalIface('MozWakeLockListener', headerFile='nsIDOMWakeLockListener.h')
+addExternalIface('MozXULTemplateBuilder', nativeType='nsIXULTemplateBuilder')
+addExternalIface('nsIBrowserDOMWindow', nativeType='nsIBrowserDOMWindow',
+ notflattened=True)
+addExternalIface('nsIControllers', nativeType='nsIControllers')
+addExternalIface('nsIDOMCrypto', nativeType='nsIDOMCrypto',
+ headerFile='Crypto.h')
+addExternalIface('nsIFile', nativeType='nsIFile', notflattened=True)
+addExternalIface('nsILoadGroup', nativeType='nsILoadGroup',
+ headerFile='nsILoadGroup.h', notflattened=True)
+addExternalIface('nsIMessageBroadcaster', nativeType='nsIMessageBroadcaster',
+ headerFile='nsIMessageManager.h', notflattened=True)
+addExternalIface('nsISelectionListener', nativeType='nsISelectionListener')
+addExternalIface('nsIStreamListener', nativeType='nsIStreamListener', notflattened=True)
+addExternalIface('nsITransportProvider', nativeType='nsITransportProvider')
+addExternalIface('nsISupports', nativeType='nsISupports')
+addExternalIface('nsIDocShell', nativeType='nsIDocShell', notflattened=True)
+addExternalIface('nsIEditor', nativeType='nsIEditor', notflattened=True)
+addExternalIface('nsIVariant', nativeType='nsIVariant', notflattened=True)
+addExternalIface('nsIScriptableRegion', nativeType='nsIScriptableRegion', notflattened=True)
+addExternalIface('OutputStream', nativeType='nsIOutputStream',
+ notflattened=True)
+addExternalIface('Principal', nativeType='nsIPrincipal',
+ headerFile='nsIPrincipal.h', notflattened=True)
+addExternalIface('StackFrame', nativeType='nsIStackFrame',
+ headerFile='nsIException.h', notflattened=True)
+addExternalIface('URI', nativeType='nsIURI', headerFile='nsIURI.h',
+ notflattened=True)
+addExternalIface('XULCommandDispatcher')
diff --git a/dom/bindings/CallbackFunction.h b/dom/bindings/CallbackFunction.h
new file mode 100644
index 000000000..ac2b3e8b3
--- /dev/null
+++ b/dom/bindings/CallbackFunction.h
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 common base class for representing WebIDL callback function types in C++.
+ *
+ * This class implements common functionality like lifetime
+ * management, initialization with the callable, and setup of the call
+ * environment. Subclasses corresponding to particular callback
+ * function types should provide a Call() method that actually does
+ * the call.
+ */
+
+#ifndef mozilla_dom_CallbackFunction_h
+#define mozilla_dom_CallbackFunction_h
+
+#include "mozilla/dom/CallbackObject.h"
+
+namespace mozilla {
+namespace dom {
+
+class CallbackFunction : public CallbackObject
+{
+public:
+ // See CallbackObject for an explanation of the arguments.
+ explicit CallbackFunction(JSContext* aCx, JS::Handle<JSObject*> aCallable,
+ nsIGlobalObject* aIncumbentGlobal)
+ : CallbackObject(aCx, aCallable, aIncumbentGlobal)
+ {
+ }
+
+ // See CallbackObject for an explanation of the arguments.
+ explicit CallbackFunction(JS::Handle<JSObject*> aCallable,
+ JS::Handle<JSObject*> aAsyncStack,
+ nsIGlobalObject* aIncumbentGlobal)
+ : CallbackObject(aCallable, aAsyncStack, aIncumbentGlobal)
+ {
+ }
+
+ JS::Handle<JSObject*> Callable() const
+ {
+ return Callback();
+ }
+
+ JS::Handle<JSObject*> CallablePreserveColor() const
+ {
+ return CallbackPreserveColor();
+ }
+
+ bool HasGrayCallable() const
+ {
+ // Play it safe in case this gets called after unlink.
+ return mCallback && JS::ObjectIsMarkedGray(mCallback);
+ }
+
+protected:
+ explicit CallbackFunction(CallbackFunction* aCallbackFunction)
+ : CallbackObject(aCallbackFunction)
+ {
+ }
+
+ // See CallbackObject for an explanation of the arguments.
+ CallbackFunction(JSContext* aCx, JS::Handle<JSObject*> aCallable,
+ nsIGlobalObject* aIncumbentGlobal,
+ const FastCallbackConstructor&)
+ : CallbackObject(aCx, aCallable, aIncumbentGlobal,
+ FastCallbackConstructor())
+ {
+ }
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_CallbackFunction_h
diff --git a/dom/bindings/CallbackInterface.cpp b/dom/bindings/CallbackInterface.cpp
new file mode 100644
index 000000000..55c1d4d7a
--- /dev/null
+++ b/dom/bindings/CallbackInterface.cpp
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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/dom/CallbackInterface.h"
+#include "jsapi.h"
+#include "mozilla/dom/BindingUtils.h"
+#include "nsPrintfCString.h"
+
+namespace mozilla {
+namespace dom {
+
+bool
+CallbackInterface::GetCallableProperty(JSContext* cx, JS::Handle<jsid> aPropId,
+ JS::MutableHandle<JS::Value> aCallable)
+{
+ if (!JS_GetPropertyById(cx, CallbackKnownNotGray(), aPropId, aCallable)) {
+ return false;
+ }
+ if (!aCallable.isObject() ||
+ !JS::IsCallable(&aCallable.toObject())) {
+ char* propName =
+ JS_EncodeString(cx, JS_FORGET_STRING_FLATNESS(JSID_TO_FLAT_STRING(aPropId)));
+ nsPrintfCString description("Property '%s'", propName);
+ JS_free(cx, propName);
+ ThrowErrorMessage(cx, MSG_NOT_CALLABLE, description.get());
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/bindings/CallbackInterface.h b/dom/bindings/CallbackInterface.h
new file mode 100644
index 000000000..3ba5182e7
--- /dev/null
+++ b/dom/bindings/CallbackInterface.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 common base class for representing WebIDL callback interface types in C++.
+ *
+ * This class implements common functionality like lifetime management,
+ * initialization with the callback object, and setup of the call environment.
+ * Subclasses corresponding to particular callback interface types should
+ * provide methods that actually do the various necessary calls.
+ */
+
+#ifndef mozilla_dom_CallbackInterface_h
+#define mozilla_dom_CallbackInterface_h
+
+#include "mozilla/dom/CallbackObject.h"
+
+namespace mozilla {
+namespace dom {
+
+class CallbackInterface : public CallbackObject
+{
+public:
+ // See CallbackObject for an explanation of the arguments.
+ explicit CallbackInterface(JSContext* aCx, JS::Handle<JSObject*> aCallback,
+ nsIGlobalObject* aIncumbentGlobal)
+ : CallbackObject(aCx, aCallback, aIncumbentGlobal)
+ {
+ }
+
+ // See CallbackObject for an explanation of the arguments.
+ explicit CallbackInterface(JS::Handle<JSObject*> aCallback,
+ JS::Handle<JSObject*> aAsyncStack,
+ nsIGlobalObject* aIncumbentGlobal)
+ : CallbackObject(aCallback, aAsyncStack, aIncumbentGlobal)
+ {
+ }
+
+protected:
+ bool GetCallableProperty(JSContext* cx, JS::Handle<jsid> aPropId,
+ JS::MutableHandle<JS::Value> aCallable);
+
+ // See CallbackObject for an explanation of the arguments.
+ CallbackInterface(JSContext* aCx, JS::Handle<JSObject*> aCallable,
+ nsIGlobalObject* aIncumbentGlobal,
+ const FastCallbackConstructor&)
+ : CallbackObject(aCx, aCallable, aIncumbentGlobal,
+ FastCallbackConstructor())
+ {
+ }
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_CallbackFunction_h
diff --git a/dom/bindings/CallbackObject.cpp b/dom/bindings/CallbackObject.cpp
new file mode 100644
index 000000000..7c7d2c6b4
--- /dev/null
+++ b/dom/bindings/CallbackObject.cpp
@@ -0,0 +1,331 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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/dom/CallbackObject.h"
+#include "mozilla/dom/BindingUtils.h"
+#include "jsfriendapi.h"
+#include "nsIScriptGlobalObject.h"
+#include "nsIXPConnect.h"
+#include "nsIScriptContext.h"
+#include "nsPIDOMWindow.h"
+#include "nsJSUtils.h"
+#include "xpcprivate.h"
+#include "WorkerPrivate.h"
+#include "nsGlobalWindow.h"
+#include "WorkerScope.h"
+#include "jsapi.h"
+#include "nsJSPrincipals.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CallbackObject)
+ NS_INTERFACE_MAP_ENTRY(mozilla::dom::CallbackObject)
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(CallbackObject)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(CallbackObject)
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(CallbackObject)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CallbackObject)
+ tmp->DropJSObjects();
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mIncumbentGlobal)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(CallbackObject)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIncumbentGlobal)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(CallbackObject)
+ NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCallback)
+ NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCreationStack)
+ NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mIncumbentJSGlobal)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+void
+CallbackObject::Trace(JSTracer* aTracer)
+{
+ JS::TraceEdge(aTracer, &mCallback, "CallbackObject.mCallback");
+ JS::TraceEdge(aTracer, &mCreationStack, "CallbackObject.mCreationStack");
+ JS::TraceEdge(aTracer, &mIncumbentJSGlobal,
+ "CallbackObject.mIncumbentJSGlobal");
+}
+
+void
+CallbackObject::HoldJSObjectsIfMoreThanOneOwner()
+{
+ MOZ_ASSERT(mRefCnt.get() > 0);
+ if (mRefCnt.get() > 1) {
+ mozilla::HoldJSObjects(this);
+ } else {
+ // We can just forget all our stuff.
+ ClearJSReferences();
+ }
+}
+
+CallbackObject::CallSetup::CallSetup(CallbackObject* aCallback,
+ ErrorResult& aRv,
+ const char* aExecutionReason,
+ ExceptionHandling aExceptionHandling,
+ JSCompartment* aCompartment,
+ bool aIsJSImplementedWebIDL)
+ : mCx(nullptr)
+ , mCompartment(aCompartment)
+ , mErrorResult(aRv)
+ , mExceptionHandling(aExceptionHandling)
+ , mIsMainThread(NS_IsMainThread())
+{
+ if (mIsMainThread) {
+ nsContentUtils::EnterMicroTask();
+ }
+
+ // Compute the caller's subject principal (if necessary) early, before we
+ // do anything that might perturb the relevant state.
+ nsIPrincipal* webIDLCallerPrincipal = nullptr;
+ if (aIsJSImplementedWebIDL) {
+ webIDLCallerPrincipal = nsContentUtils::SubjectPrincipalOrSystemIfNativeCaller();
+ }
+
+ // First, find the real underlying callback.
+ JSObject* realCallback = js::UncheckedUnwrap(aCallback->CallbackPreserveColor());
+ nsIGlobalObject* globalObject = nullptr;
+
+ JSContext* cx;
+ {
+ // Bug 955660: we cannot do "proper" rooting here because we need the
+ // global to get a context. Everything here is simple getters that cannot
+ // GC, so just paper over the necessary dataflow inversion.
+ JS::AutoSuppressGCAnalysis nogc;
+
+ // Now get the global for this callback. Note that for the case of
+ // JS-implemented WebIDL we never have a window here.
+ nsGlobalWindow* win = mIsMainThread && !aIsJSImplementedWebIDL
+ ? xpc::WindowGlobalOrNull(realCallback)
+ : nullptr;
+ if (win) {
+ MOZ_ASSERT(win->IsInnerWindow());
+ // We don't want to run script in windows that have been navigated away
+ // from.
+ if (!win->AsInner()->HasActiveDocument()) {
+ aRv.ThrowDOMException(NS_ERROR_DOM_NOT_SUPPORTED_ERR,
+ NS_LITERAL_CSTRING("Refusing to execute function from window "
+ "whose document is no longer active."));
+ return;
+ }
+ globalObject = win;
+ } else {
+ // No DOM Window. Store the global.
+ JSObject* global = js::GetGlobalForObjectCrossCompartment(realCallback);
+ globalObject = xpc::NativeGlobal(global);
+ MOZ_ASSERT(globalObject);
+ }
+
+ // Bail out if there's no useful global. This seems to happen intermittently
+ // on gaia-ui tests, probably because nsInProcessTabChildGlobal is returning
+ // null in some kind of teardown state.
+ if (!globalObject->GetGlobalJSObject()) {
+ aRv.ThrowDOMException(NS_ERROR_DOM_NOT_SUPPORTED_ERR,
+ NS_LITERAL_CSTRING("Refusing to execute function from global which is "
+ "being torn down."));
+ return;
+ }
+
+ mAutoEntryScript.emplace(globalObject, aExecutionReason, mIsMainThread);
+ mAutoEntryScript->SetWebIDLCallerPrincipal(webIDLCallerPrincipal);
+ nsIGlobalObject* incumbent = aCallback->IncumbentGlobalOrNull();
+ if (incumbent) {
+ // The callback object traces its incumbent JS global, so in general it
+ // should be alive here. However, it's possible that we could run afoul
+ // of the same IPC global weirdness described above, wherein the
+ // nsIGlobalObject has severed its reference to the JS global. Let's just
+ // be safe here, so that nobody has to waste a day debugging gaia-ui tests.
+ if (!incumbent->GetGlobalJSObject()) {
+ aRv.ThrowDOMException(NS_ERROR_DOM_NOT_SUPPORTED_ERR,
+ NS_LITERAL_CSTRING("Refusing to execute function because our "
+ "incumbent global is being torn down."));
+ return;
+ }
+ mAutoIncumbentScript.emplace(incumbent);
+ }
+
+ cx = mAutoEntryScript->cx();
+
+ // Unmark the callable (by invoking Callback() and not the CallbackPreserveColor()
+ // variant), and stick it in a Rooted before it can go gray again.
+ // Nothing before us in this function can trigger a CC, so it's safe to wait
+ // until here it do the unmark. This allows us to construct mRootedCallable
+ // with the cx from mAutoEntryScript, avoiding the cost of finding another
+ // JSContext. (Rooted<> does not care about requests or compartments.)
+ mRootedCallable.emplace(cx, aCallback->Callback());
+ }
+
+ // JS-implemented WebIDL is always OK to run, since it runs with Chrome
+ // privileges anyway.
+ if (mIsMainThread && !aIsJSImplementedWebIDL) {
+ // Check that it's ok to run this callback at all.
+ // Make sure to use realCallback to get the global of the callback object,
+ // not the wrapper.
+ bool allowed = xpc::Scriptability::Get(realCallback).Allowed();
+
+ if (!allowed) {
+ aRv.ThrowDOMException(NS_ERROR_DOM_NOT_SUPPORTED_ERR,
+ NS_LITERAL_CSTRING("Refusing to execute function from global in which "
+ "script is disabled."));
+ return;
+ }
+ }
+
+ mAsyncStack.emplace(cx, aCallback->GetCreationStack());
+ if (*mAsyncStack) {
+ mAsyncStackSetter.emplace(cx, *mAsyncStack, aExecutionReason);
+ }
+
+ // Enter the compartment of our callback, so we can actually work with it.
+ //
+ // Note that if the callback is a wrapper, this will not be the same
+ // compartment that we ended up in with mAutoEntryScript above, because the
+ // entry point is based off of the unwrapped callback (realCallback).
+ mAc.emplace(cx, *mRootedCallable);
+
+ // And now we're ready to go.
+ mCx = cx;
+}
+
+bool
+CallbackObject::CallSetup::ShouldRethrowException(JS::Handle<JS::Value> aException)
+{
+ if (mExceptionHandling == eRethrowExceptions) {
+ if (!mCompartment) {
+ // Caller didn't ask us to filter for only exceptions we subsume.
+ return true;
+ }
+
+ // On workers, we don't have nsIPrincipals to work with. But we also only
+ // have one compartment, so check whether mCompartment is the same as the
+ // current compartment of mCx.
+ if (mCompartment == js::GetContextCompartment(mCx)) {
+ return true;
+ }
+
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // At this point mCx is in the compartment of our unwrapped callback, so
+ // just check whether the principal of mCompartment subsumes that of the
+ // current compartment/global of mCx.
+ nsIPrincipal* callerPrincipal =
+ nsJSPrincipals::get(JS_GetCompartmentPrincipals(mCompartment));
+ nsIPrincipal* calleePrincipal = nsContentUtils::SubjectPrincipal();
+ if (callerPrincipal->SubsumesConsideringDomain(calleePrincipal)) {
+ return true;
+ }
+ }
+
+ MOZ_ASSERT(mCompartment);
+
+ // Now we only want to throw an exception to the caller if the object that was
+ // thrown is in the caller compartment (which we stored in mCompartment).
+
+ if (!aException.isObject()) {
+ return false;
+ }
+
+ JS::Rooted<JSObject*> obj(mCx, &aException.toObject());
+ obj = js::UncheckedUnwrap(obj, /* stopAtWindowProxy = */ false);
+ return js::GetObjectCompartment(obj) == mCompartment;
+}
+
+CallbackObject::CallSetup::~CallSetup()
+{
+ // To get our nesting right we have to destroy our JSAutoCompartment first.
+ // In particular, we want to do this before we try reporting any exceptions,
+ // so we end up reporting them while in the compartment of our entry point,
+ // not whatever cross-compartment wrappper mCallback might be.
+ // Be careful: the JSAutoCompartment might not have been constructed at all!
+ mAc.reset();
+
+ // Now, if we have a JSContext, report any pending errors on it, unless we
+ // were told to re-throw them.
+ if (mCx) {
+ bool needToDealWithException = mAutoEntryScript->HasException();
+ if ((mCompartment && mExceptionHandling == eRethrowContentExceptions) ||
+ mExceptionHandling == eRethrowExceptions) {
+ mErrorResult.MightThrowJSException();
+ if (needToDealWithException) {
+ JS::Rooted<JS::Value> exn(mCx);
+ if (mAutoEntryScript->PeekException(&exn) &&
+ ShouldRethrowException(exn)) {
+ mAutoEntryScript->ClearException();
+ MOZ_ASSERT(!mAutoEntryScript->HasException());
+ mErrorResult.ThrowJSException(mCx, exn);
+ needToDealWithException = false;
+ }
+ }
+ }
+
+ if (needToDealWithException) {
+ // Either we're supposed to report our exceptions, or we're supposed to
+ // re-throw them but we failed to get the exception value. Either way,
+ // we'll just report the pending exception, if any, once ~mAutoEntryScript
+ // runs. Note that we've already run ~mAc, effectively, so we don't have
+ // to worry about ordering here.
+ if (mErrorResult.IsJSContextException()) {
+ // XXXkhuey bug 1117269. When this is fixed, please consider fixing
+ // ThrowExceptionValueIfSafe over in Exceptions.cpp in the same way.
+
+ // IsJSContextException shouldn't be true anymore because we will report
+ // the exception on the JSContext ... so throw something else.
+ mErrorResult.Throw(NS_ERROR_UNEXPECTED);
+ }
+ }
+ }
+
+ mAutoIncumbentScript.reset();
+ mAutoEntryScript.reset();
+
+ // It is important that this is the last thing we do, after leaving the
+ // compartment and undoing all our entry/incumbent script changes
+ if (mIsMainThread) {
+ nsContentUtils::LeaveMicroTask();
+ }
+}
+
+already_AddRefed<nsISupports>
+CallbackObjectHolderBase::ToXPCOMCallback(CallbackObject* aCallback,
+ const nsIID& aIID) const
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!aCallback) {
+ return nullptr;
+ }
+
+ // We don't init the AutoJSAPI with our callback because we don't want it
+ // reporting errors to its global's onerror handlers.
+ AutoJSAPI jsapi;
+ jsapi.Init();
+ JSContext* cx = jsapi.cx();
+
+ JS::Rooted<JSObject*> callback(cx, aCallback->Callback());
+
+ JSAutoCompartment ac(cx, callback);
+ RefPtr<nsXPCWrappedJS> wrappedJS;
+ nsresult rv =
+ nsXPCWrappedJS::GetNewOrUsed(callback, aIID, getter_AddRefs(wrappedJS));
+ if (NS_FAILED(rv) || !wrappedJS) {
+ return nullptr;
+ }
+
+ nsCOMPtr<nsISupports> retval;
+ rv = wrappedJS->QueryInterface(aIID, getter_AddRefs(retval));
+ if (NS_FAILED(rv)) {
+ return nullptr;
+ }
+
+ return retval.forget();
+}
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/bindings/CallbackObject.h b/dom/bindings/CallbackObject.h
new file mode 100644
index 000000000..8a3d45dfc
--- /dev/null
+++ b/dom/bindings/CallbackObject.h
@@ -0,0 +1,607 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 common base class for representing WebIDL callback function and
+ * callback interface types in C++.
+ *
+ * This class implements common functionality like lifetime
+ * management, initialization with the JS object, and setup of the
+ * call environment. Subclasses are responsible for providing methods
+ * that do the call into JS as needed.
+ */
+
+#ifndef mozilla_dom_CallbackObject_h
+#define mozilla_dom_CallbackObject_h
+
+#include "nsISupports.h"
+#include "nsISupportsImpl.h"
+#include "nsCycleCollectionParticipant.h"
+#include "jswrapper.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/ErrorResult.h"
+#include "mozilla/HoldDropJSObjects.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/OwningNonNull.h"
+#include "mozilla/dom/ScriptSettings.h"
+#include "nsWrapperCache.h"
+#include "nsJSEnvironment.h"
+#include "xpcpublic.h"
+#include "jsapi.h"
+#include "js/TracingAPI.h"
+
+namespace mozilla {
+namespace dom {
+
+#define DOM_CALLBACKOBJECT_IID \
+{ 0xbe74c190, 0x6d76, 0x4991, \
+ { 0x84, 0xb9, 0x65, 0x06, 0x99, 0xe6, 0x93, 0x2b } }
+
+class CallbackObject : public nsISupports
+{
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(DOM_CALLBACKOBJECT_IID)
+
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CallbackObject)
+
+ // The caller may pass a global object which will act as an override for the
+ // incumbent script settings object when the callback is invoked (overriding
+ // the entry point computed from aCallback). If no override is required, the
+ // caller should pass null. |aCx| is used to capture the current
+ // stack, which is later used as an async parent when the callback
+ // is invoked. aCx can be nullptr, in which case no stack is
+ // captured.
+ explicit CallbackObject(JSContext* aCx, JS::Handle<JSObject*> aCallback,
+ nsIGlobalObject* aIncumbentGlobal)
+ {
+ if (aCx && JS::ContextOptionsRef(aCx).asyncStack()) {
+ JS::RootedObject stack(aCx);
+ if (!JS::CaptureCurrentStack(aCx, &stack)) {
+ JS_ClearPendingException(aCx);
+ }
+ Init(aCallback, stack, aIncumbentGlobal);
+ } else {
+ Init(aCallback, nullptr, aIncumbentGlobal);
+ }
+ }
+
+ // Instead of capturing the current stack to use as an async parent when the
+ // callback is invoked, the caller can use this overload to pass in a stack
+ // for that purpose.
+ explicit CallbackObject(JS::Handle<JSObject*> aCallback,
+ JS::Handle<JSObject*> aAsyncStack,
+ nsIGlobalObject* aIncumbentGlobal)
+ {
+ Init(aCallback, aAsyncStack, aIncumbentGlobal);
+ }
+
+ JS::Handle<JSObject*> Callback() const
+ {
+ mCallback.exposeToActiveJS();
+ return CallbackPreserveColor();
+ }
+
+ JSObject* GetCreationStack() const
+ {
+ return mCreationStack;
+ }
+
+ void MarkForCC()
+ {
+ mCallback.exposeToActiveJS();
+ mCreationStack.exposeToActiveJS();
+ }
+
+ /*
+ * This getter does not change the color of the JSObject meaning that the
+ * object returned is not guaranteed to be kept alive past the next CC.
+ *
+ * This should only be called if you are certain that the return value won't
+ * be passed into a JS API function and that it won't be stored without being
+ * rooted (or otherwise signaling the stored value to the CC).
+ */
+ JS::Handle<JSObject*> CallbackPreserveColor() const
+ {
+ // Calling fromMarkedLocation() is safe because we trace our mCallback, and
+ // because the value of mCallback cannot change after if has been set.
+ return JS::Handle<JSObject*>::fromMarkedLocation(mCallback.address());
+ }
+
+ /*
+ * If the callback is known to be non-gray, then this method can be
+ * used instead of Callback() to avoid the overhead of
+ * ExposeObjectToActiveJS().
+ */
+ JS::Handle<JSObject*> CallbackKnownNotGray() const
+ {
+ MOZ_ASSERT(!JS::ObjectIsMarkedGray(mCallback));
+ return CallbackPreserveColor();
+ }
+
+ nsIGlobalObject* IncumbentGlobalOrNull() const
+ {
+ return mIncumbentGlobal;
+ }
+
+ enum ExceptionHandling {
+ // Report any exception and don't throw it to the caller code.
+ eReportExceptions,
+ // Throw an exception to the caller code if the thrown exception is a
+ // binding object for a DOMError or DOMException from the caller's scope,
+ // otherwise report it.
+ eRethrowContentExceptions,
+ // Throw exceptions to the caller code, unless the caller compartment is
+ // provided, the exception is not a DOMError or DOMException from the
+ // caller compartment, and the caller compartment does not subsume our
+ // unwrapped callback.
+ eRethrowExceptions
+ };
+
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+ {
+ return aMallocSizeOf(this);
+ }
+
+protected:
+ virtual ~CallbackObject()
+ {
+ DropJSObjects();
+ }
+
+ explicit CallbackObject(CallbackObject* aCallbackObject)
+ {
+ Init(aCallbackObject->mCallback, aCallbackObject->mCreationStack,
+ aCallbackObject->mIncumbentGlobal);
+ }
+
+ bool operator==(const CallbackObject& aOther) const
+ {
+ JSObject* thisObj =
+ js::UncheckedUnwrap(CallbackPreserveColor());
+ JSObject* otherObj =
+ js::UncheckedUnwrap(aOther.CallbackPreserveColor());
+ return thisObj == otherObj;
+ }
+
+private:
+ inline void InitNoHold(JSObject* aCallback, JSObject* aCreationStack,
+ nsIGlobalObject* aIncumbentGlobal)
+ {
+ MOZ_ASSERT(aCallback && !mCallback);
+ // Set script objects before we hold, on the off chance that a GC could
+ // somehow happen in there... (which would be pretty odd, granted).
+ mCallback = aCallback;
+ mCreationStack = aCreationStack;
+ if (aIncumbentGlobal) {
+ mIncumbentGlobal = aIncumbentGlobal;
+ mIncumbentJSGlobal = aIncumbentGlobal->GetGlobalJSObject();
+ }
+ }
+
+ inline void Init(JSObject* aCallback, JSObject* aCreationStack,
+ nsIGlobalObject* aIncumbentGlobal)
+ {
+ InitNoHold(aCallback, aCreationStack, aIncumbentGlobal);
+ mozilla::HoldJSObjects(this);
+ }
+
+ inline void ClearJSReferences()
+ {
+ mCallback = nullptr;
+ mCreationStack = nullptr;
+ mIncumbentJSGlobal = nullptr;
+ }
+
+ CallbackObject(const CallbackObject&) = delete;
+ CallbackObject& operator =(const CallbackObject&) = delete;
+
+protected:
+ void DropJSObjects()
+ {
+ MOZ_ASSERT_IF(mIncumbentJSGlobal, mCallback);
+ if (mCallback) {
+ ClearJSReferences();
+ mozilla::DropJSObjects(this);
+ }
+ }
+
+ // For use from subclasses that want to be usable with Rooted.
+ void Trace(JSTracer* aTracer);
+
+ // For use from subclasses that want to be traced for a bit then possibly
+ // switch to HoldJSObjects. If we have more than one owner, this will
+ // HoldJSObjects; otherwise it will just forget all our JS references.
+ void HoldJSObjectsIfMoreThanOneOwner();
+
+ // Struct used as a way to force a CallbackObject constructor to not call
+ // HoldJSObjects. We're putting it here so that CallbackObject subclasses will
+ // have access to it, but outside code will not.
+ //
+ // Places that use this need to ensure that the callback is traced (e.g. via a
+ // Rooted) until the HoldJSObjects call happens.
+ struct FastCallbackConstructor {
+ };
+
+ // Just like the public version without the FastCallbackConstructor argument,
+ // except for not calling HoldJSObjects. If you use this, you MUST ensure
+ // that the object is traced until the HoldJSObjects happens!
+ CallbackObject(JSContext* aCx, JS::Handle<JSObject*> aCallback,
+ nsIGlobalObject* aIncumbentGlobal,
+ const FastCallbackConstructor&)
+ {
+ if (aCx && JS::ContextOptionsRef(aCx).asyncStack()) {
+ JS::RootedObject stack(aCx);
+ if (!JS::CaptureCurrentStack(aCx, &stack)) {
+ JS_ClearPendingException(aCx);
+ }
+ InitNoHold(aCallback, stack, aIncumbentGlobal);
+ } else {
+ InitNoHold(aCallback, nullptr, aIncumbentGlobal);
+ }
+ }
+
+ // mCallback is not unwrapped, so it can be a cross-compartment-wrapper.
+ // This is done to ensure that, if JS code can't call a callback f(), or get
+ // its members, directly itself, this code won't call f(), or get its members,
+ // on the code's behalf.
+ JS::Heap<JSObject*> mCallback;
+ JS::Heap<JSObject*> mCreationStack;
+ // Ideally, we'd just hold a reference to the nsIGlobalObject, since that's
+ // what we need to pass to AutoIncumbentScript. Unfortunately, that doesn't
+ // hold the actual JS global alive. So we maintain an additional pointer to
+ // the JS global itself so that we can trace it.
+ //
+ // At some point we should consider trying to make native globals hold their
+ // scripted global alive, at which point we can get rid of the duplication
+ // here.
+ nsCOMPtr<nsIGlobalObject> mIncumbentGlobal;
+ JS::TenuredHeap<JSObject*> mIncumbentJSGlobal;
+
+ class MOZ_STACK_CLASS CallSetup
+ {
+ /**
+ * A class that performs whatever setup we need to safely make a
+ * call while this class is on the stack, After the constructor
+ * returns, the call is safe to make if GetContext() returns
+ * non-null.
+ */
+ public:
+ // If aExceptionHandling == eRethrowContentExceptions then aCompartment
+ // needs to be set to the compartment in which exceptions will be rethrown.
+ //
+ // If aExceptionHandling == eRethrowExceptions then aCompartment may be set
+ // to the compartment in which exceptions will be rethrown. In that case
+ // they will only be rethrown if that compartment's principal subsumes the
+ // principal of our (unwrapped) callback.
+ CallSetup(CallbackObject* aCallback, ErrorResult& aRv,
+ const char* aExecutionReason,
+ ExceptionHandling aExceptionHandling,
+ JSCompartment* aCompartment = nullptr,
+ bool aIsJSImplementedWebIDL = false);
+ ~CallSetup();
+
+ JSContext* GetContext() const
+ {
+ return mCx;
+ }
+
+ private:
+ // We better not get copy-constructed
+ CallSetup(const CallSetup&) = delete;
+
+ bool ShouldRethrowException(JS::Handle<JS::Value> aException);
+
+ // Members which can go away whenever
+ JSContext* mCx;
+
+ // Caller's compartment. This will only have a sensible value if
+ // mExceptionHandling == eRethrowContentExceptions or eRethrowExceptions.
+ JSCompartment* mCompartment;
+
+ // And now members whose construction/destruction order we need to control.
+ Maybe<AutoEntryScript> mAutoEntryScript;
+ Maybe<AutoIncumbentScript> mAutoIncumbentScript;
+
+ Maybe<JS::Rooted<JSObject*> > mRootedCallable;
+
+ // Members which are used to set the async stack.
+ Maybe<JS::Rooted<JSObject*>> mAsyncStack;
+ Maybe<JS::AutoSetAsyncStackForNewCalls> mAsyncStackSetter;
+
+ // Can't construct a JSAutoCompartment without a JSContext either. Also,
+ // Put mAc after mAutoEntryScript so that we exit the compartment before
+ // we pop the JSContext. Though in practice we'll often manually order
+ // those two things.
+ Maybe<JSAutoCompartment> mAc;
+
+ // An ErrorResult to possibly re-throw exceptions on and whether
+ // we should re-throw them.
+ ErrorResult& mErrorResult;
+ const ExceptionHandling mExceptionHandling;
+ const bool mIsMainThread;
+ };
+};
+
+template<class WebIDLCallbackT, class XPCOMCallbackT>
+class CallbackObjectHolder;
+
+template<class T, class U>
+void ImplCycleCollectionUnlink(CallbackObjectHolder<T, U>& aField);
+
+class CallbackObjectHolderBase
+{
+protected:
+ // Returns null on all failures
+ already_AddRefed<nsISupports> ToXPCOMCallback(CallbackObject* aCallback,
+ const nsIID& aIID) const;
+};
+
+template<class WebIDLCallbackT, class XPCOMCallbackT>
+class CallbackObjectHolder : CallbackObjectHolderBase
+{
+ /**
+ * A class which stores either a WebIDLCallbackT* or an XPCOMCallbackT*. Both
+ * types must inherit from nsISupports. The pointer that's stored can be
+ * null.
+ *
+ * When storing a WebIDLCallbackT*, mPtrBits is set to the pointer value.
+ * When storing an XPCOMCallbackT*, mPtrBits is the pointer value with low bit
+ * set.
+ */
+public:
+ explicit CallbackObjectHolder(WebIDLCallbackT* aCallback)
+ : mPtrBits(reinterpret_cast<uintptr_t>(aCallback))
+ {
+ NS_IF_ADDREF(aCallback);
+ }
+
+ explicit CallbackObjectHolder(XPCOMCallbackT* aCallback)
+ : mPtrBits(reinterpret_cast<uintptr_t>(aCallback) | XPCOMCallbackFlag)
+ {
+ NS_IF_ADDREF(aCallback);
+ }
+
+ CallbackObjectHolder(CallbackObjectHolder&& aOther)
+ : mPtrBits(aOther.mPtrBits)
+ {
+ aOther.mPtrBits = 0;
+ static_assert(sizeof(CallbackObjectHolder) == sizeof(void*),
+ "This object is expected to be as small as a pointer, and it "
+ "is currently passed by value in various places. If it is "
+ "bloating, we may want to pass it by reference then.");
+ }
+
+ CallbackObjectHolder(const CallbackObjectHolder& aOther) = delete;
+
+ CallbackObjectHolder()
+ : mPtrBits(0)
+ {}
+
+ ~CallbackObjectHolder()
+ {
+ UnlinkSelf();
+ }
+
+ void operator=(WebIDLCallbackT* aCallback)
+ {
+ UnlinkSelf();
+ mPtrBits = reinterpret_cast<uintptr_t>(aCallback);
+ NS_IF_ADDREF(aCallback);
+ }
+
+ void operator=(XPCOMCallbackT* aCallback)
+ {
+ UnlinkSelf();
+ mPtrBits = reinterpret_cast<uintptr_t>(aCallback) | XPCOMCallbackFlag;
+ NS_IF_ADDREF(aCallback);
+ }
+
+ void operator=(CallbackObjectHolder&& aOther)
+ {
+ UnlinkSelf();
+ mPtrBits = aOther.mPtrBits;
+ aOther.mPtrBits = 0;
+ }
+
+ void operator=(const CallbackObjectHolder& aOther) = delete;
+
+ nsISupports* GetISupports() const
+ {
+ return reinterpret_cast<nsISupports*>(mPtrBits & ~XPCOMCallbackFlag);
+ }
+
+ // Boolean conversion operator so people can use this in boolean tests
+ explicit operator bool() const
+ {
+ return GetISupports();
+ }
+
+ CallbackObjectHolder Clone() const
+ {
+ CallbackObjectHolder result;
+ result.mPtrBits = mPtrBits;
+ NS_IF_ADDREF(GetISupports());
+ return result;
+ }
+
+ // Even if HasWebIDLCallback returns true, GetWebIDLCallback() might still
+ // return null.
+ bool HasWebIDLCallback() const
+ {
+ return !(mPtrBits & XPCOMCallbackFlag);
+ }
+
+ WebIDLCallbackT* GetWebIDLCallback() const
+ {
+ MOZ_ASSERT(HasWebIDLCallback());
+ return reinterpret_cast<WebIDLCallbackT*>(mPtrBits);
+ }
+
+ XPCOMCallbackT* GetXPCOMCallback() const
+ {
+ MOZ_ASSERT(!HasWebIDLCallback());
+ return reinterpret_cast<XPCOMCallbackT*>(mPtrBits & ~XPCOMCallbackFlag);
+ }
+
+ bool operator==(WebIDLCallbackT* aOtherCallback) const
+ {
+ if (!aOtherCallback) {
+ // If other is null, then we must be null to be equal.
+ return !GetISupports();
+ }
+
+ if (!HasWebIDLCallback() || !GetWebIDLCallback()) {
+ // If other is non-null, then we can't be equal if we have a
+ // non-WebIDL callback or a null callback.
+ return false;
+ }
+
+ return *GetWebIDLCallback() == *aOtherCallback;
+ }
+
+ bool operator==(XPCOMCallbackT* aOtherCallback) const
+ {
+ return (!aOtherCallback && !GetISupports()) ||
+ (!HasWebIDLCallback() && GetXPCOMCallback() == aOtherCallback);
+ }
+
+ bool operator==(const CallbackObjectHolder& aOtherCallback) const
+ {
+ if (aOtherCallback.HasWebIDLCallback()) {
+ return *this == aOtherCallback.GetWebIDLCallback();
+ }
+
+ return *this == aOtherCallback.GetXPCOMCallback();
+ }
+
+ // Try to return an XPCOMCallbackT version of this object.
+ already_AddRefed<XPCOMCallbackT> ToXPCOMCallback() const
+ {
+ if (!HasWebIDLCallback()) {
+ RefPtr<XPCOMCallbackT> callback = GetXPCOMCallback();
+ return callback.forget();
+ }
+
+ nsCOMPtr<nsISupports> supp =
+ CallbackObjectHolderBase::ToXPCOMCallback(GetWebIDLCallback(),
+ NS_GET_TEMPLATE_IID(XPCOMCallbackT));
+ // ToXPCOMCallback already did the right QI for us.
+ return supp.forget().downcast<XPCOMCallbackT>();
+ }
+
+ // Try to return a WebIDLCallbackT version of this object.
+ already_AddRefed<WebIDLCallbackT> ToWebIDLCallback() const
+ {
+ if (HasWebIDLCallback()) {
+ RefPtr<WebIDLCallbackT> callback = GetWebIDLCallback();
+ return callback.forget();
+ }
+ return nullptr;
+ }
+
+private:
+ static const uintptr_t XPCOMCallbackFlag = 1u;
+
+ friend void
+ ImplCycleCollectionUnlink<WebIDLCallbackT,
+ XPCOMCallbackT>(CallbackObjectHolder& aField);
+
+ void UnlinkSelf()
+ {
+ // NS_IF_RELEASE because we might have been unlinked before
+ nsISupports* ptr = GetISupports();
+ NS_IF_RELEASE(ptr);
+ mPtrBits = 0;
+ }
+
+ uintptr_t mPtrBits;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(CallbackObject, DOM_CALLBACKOBJECT_IID)
+
+template<class T, class U>
+inline void
+ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
+ CallbackObjectHolder<T, U>& aField,
+ const char* aName,
+ uint32_t aFlags = 0)
+{
+ CycleCollectionNoteChild(aCallback, aField.GetISupports(), aName, aFlags);
+}
+
+template<class T, class U>
+void
+ImplCycleCollectionUnlink(CallbackObjectHolder<T, U>& aField)
+{
+ aField.UnlinkSelf();
+}
+
+// T is expected to be a RefPtr or OwningNonNull around a CallbackObject
+// subclass. This class is used in bindings to safely handle Fast* callbacks;
+// it ensures that the callback is traced, and that if something is holding onto
+// the callback when we're done with it HoldJSObjects is called.
+template<typename T>
+class RootedCallback : public JS::Rooted<T>
+{
+public:
+ explicit RootedCallback(JSContext* cx)
+ : JS::Rooted<T>(cx)
+ {}
+
+ // We need a way to make assignment from pointers (how we're normally used)
+ // work.
+ template<typename S>
+ void operator=(S* arg)
+ {
+ this->get().operator=(arg);
+ }
+
+ // But nullptr can't use the above template, because it doesn't know which S
+ // to select. So we need a special overload for nullptr.
+ void operator=(decltype(nullptr) arg)
+ {
+ this->get().operator=(arg);
+ }
+
+ // Codegen relies on being able to do Callback() on us.
+ JS::Handle<JSObject*> Callback() const
+ {
+ return this->get()->Callback();
+ }
+
+ ~RootedCallback()
+ {
+ // Ensure that our callback starts holding on to its own JS objects as
+ // needed. Having to null-check here when T is OwningNonNull is a bit
+ // silly, but it's simpler than creating two separate RootedCallback
+ // instantiations for OwningNonNull and RefPtr.
+ if (IsInitialized(this->get())) {
+ this->get()->HoldJSObjectsIfMoreThanOneOwner();
+ }
+ }
+
+private:
+ template<typename U>
+ static bool IsInitialized(U& aArg); // Not implemented
+
+ template<typename U>
+ static bool IsInitialized(RefPtr<U>& aRefPtr)
+ {
+ return aRefPtr;
+ }
+
+ template<typename U>
+ static bool IsInitialized(OwningNonNull<U>& aOwningNonNull)
+ {
+ return aOwningNonNull.isInitialized();
+ }
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_CallbackObject_h
diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py
new file mode 100644
index 000000000..3174c37dd
--- /dev/null
+++ b/dom/bindings/Codegen.py
@@ -0,0 +1,17364 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.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 re
+import string
+import math
+import textwrap
+import functools
+
+from WebIDL import BuiltinTypes, IDLBuiltinType, IDLNullValue, IDLSequenceType, IDLType, IDLAttribute, IDLInterfaceMember, IDLUndefinedValue, IDLEmptySequenceValue, IDLDictionary
+from Configuration import NoSuchDescriptorError, getTypesFromDescriptor, getTypesFromDictionary, getTypesFromCallback, getAllTypes, Descriptor, MemberIsUnforgeable, iteratorNativeType
+
+AUTOGENERATED_WARNING_COMMENT = \
+ "/* THIS FILE IS AUTOGENERATED BY Codegen.py - DO NOT EDIT */\n\n"
+AUTOGENERATED_WITH_SOURCE_WARNING_COMMENT = \
+ "/* THIS FILE IS AUTOGENERATED FROM %s BY Codegen.py - DO NOT EDIT */\n\n"
+ADDPROPERTY_HOOK_NAME = '_addProperty'
+FINALIZE_HOOK_NAME = '_finalize'
+OBJECT_MOVED_HOOK_NAME = '_objectMoved'
+CONSTRUCT_HOOK_NAME = '_constructor'
+LEGACYCALLER_HOOK_NAME = '_legacycaller'
+HASINSTANCE_HOOK_NAME = '_hasInstance'
+RESOLVE_HOOK_NAME = '_resolve'
+MAY_RESOLVE_HOOK_NAME = '_mayResolve'
+ENUMERATE_HOOK_NAME = '_enumerate'
+ENUM_ENTRY_VARIABLE_NAME = 'strings'
+INSTANCE_RESERVED_SLOTS = 1
+
+
+def memberReservedSlot(member, descriptor):
+ return ("(DOM_INSTANCE_RESERVED_SLOTS + %d)" %
+ member.slotIndices[descriptor.interface.identifier.name])
+
+
+def memberXrayExpandoReservedSlot(member, descriptor):
+ return ("(xpc::JSSLOT_EXPANDO_COUNT + %d)" %
+ member.slotIndices[descriptor.interface.identifier.name])
+
+
+def mayUseXrayExpandoSlots(descriptor, attr):
+ assert not attr.getExtendedAttribute("NewObject")
+ # For attributes whose type is a Gecko interface we always use
+ # slots on the reflector for caching. Also, for interfaces that
+ # don't want Xrays we obviously never use the Xray expando slot.
+ return descriptor.wantsXrays and not attr.type.isGeckoInterface()
+
+
+def toStringBool(arg):
+ return str(not not arg).lower()
+
+
+def toBindingNamespace(arg):
+ return arg + "Binding"
+
+
+def isTypeCopyConstructible(type):
+ # Nullable and sequence stuff doesn't affect copy-constructibility
+ type = type.unroll()
+ return (type.isPrimitive() or type.isString() or type.isEnum() or
+ (type.isUnion() and
+ CGUnionStruct.isUnionCopyConstructible(type)) or
+ (type.isDictionary() and
+ CGDictionary.isDictionaryCopyConstructible(type.inner)) or
+ # Interface types are only copy-constructible if they're Gecko
+ # interfaces. SpiderMonkey interfaces are not copy-constructible
+ # because of rooting issues.
+ (type.isInterface() and type.isGeckoInterface()))
+
+
+def idlTypeNeedsCycleCollection(type):
+ type = type.unroll() # Takes care of sequences and nullables
+ if ((type.isPrimitive() and type.tag() in builtinNames) or
+ type.isEnum() or
+ type.isString() or
+ type.isAny() or
+ type.isObject() or
+ type.isSpiderMonkeyInterface()):
+ return False
+ elif type.isCallback() or type.isGeckoInterface():
+ return True
+ elif type.isUnion():
+ return any(idlTypeNeedsCycleCollection(t) for t in type.flatMemberTypes)
+ elif type.isMozMap():
+ if idlTypeNeedsCycleCollection(type.inner):
+ raise TypeError("Cycle collection for type %s is not supported" % type)
+ return False
+ elif type.isDictionary():
+ if any(idlTypeNeedsCycleCollection(m.type) for m in type.inner.members):
+ raise TypeError("Cycle collection for type %s is not supported" % type)
+ return False
+ else:
+ raise TypeError("Don't know whether to cycle-collect type %s" % type)
+
+
+def wantsAddProperty(desc):
+ return (desc.concrete and desc.wrapperCache and not desc.isGlobal())
+
+
+# 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)
+
+
+def indent(s, indentLevel=2):
+ """
+ Indent C++ code.
+
+ Weird secret feature: this doesn't indent lines that start with # (such as
+ #include lines or #ifdef/#endif).
+ """
+ if s == "":
+ return s
+ return re.sub(lineStartDetector, indentLevel * " ", s)
+
+
+# dedent() and fill() are often called on the same string multiple
+# times. We want to memoize their return values so we don't keep
+# recomputing them all the time.
+def memoize(fn):
+ """
+ Decorator to memoize a function of one argument. The cache just
+ grows without bound.
+ """
+ cache = {}
+
+ @functools.wraps(fn)
+ def wrapper(arg):
+ retval = cache.get(arg)
+ if retval is None:
+ retval = cache[arg] = fn(arg)
+ return retval
+ return wrapper
+
+
+@memoize
+def dedent(s):
+ """
+ Remove all leading whitespace from s, and remove a blank line
+ at the beginning.
+ """
+ if s.startswith('\n'):
+ s = s[1:]
+ return textwrap.dedent(s)
+
+
+# This works by transforming the fill()-template to an equivalent
+# string.Template.
+fill_multiline_substitution_re = re.compile(r"( *)\$\*{(\w+)}(\n)?")
+
+
+find_substitutions = re.compile(r"\${")
+
+
+@memoize
+def compile_fill_template(template):
+ """
+ Helper function for fill(). Given the template string passed to fill(),
+ do the reusable part of template processing and return a pair (t,
+ argModList) that can be used every time fill() is called with that
+ template argument.
+
+ argsModList is list of tuples that represent modifications to be
+ made to args. Each modification has, in order: i) the arg name,
+ ii) the modified name, iii) the indent depth.
+ """
+ t = dedent(template)
+ assert t.endswith("\n") or "\n" not in t
+ argModList = []
+
+ def replace(match):
+ """
+ Replaces a line like ' $*{xyz}\n' with '${xyz_n}',
+ where n is the indent depth, and add a corresponding entry to
+ argModList.
+
+ Note that this needs to close over argModList, so it has to be
+ defined inside compile_fill_template().
+ """
+ indentation, name, nl = match.groups()
+ depth = len(indentation)
+
+ # Check that $*{xyz} appears by itself on a line.
+ prev = match.string[:match.start()]
+ if (prev and not prev.endswith("\n")) or nl is None:
+ raise ValueError("Invalid fill() template: $*{%s} must appear by itself on a line" % name)
+
+ # Now replace this whole line of template with the indented equivalent.
+ modified_name = name + "_" + str(depth)
+ argModList.append((name, modified_name, depth))
+ return "${" + modified_name + "}"
+
+ t = re.sub(fill_multiline_substitution_re, replace, t)
+ if not re.search(find_substitutions, t):
+ raise TypeError("Using fill() when dedent() would do.")
+ return (string.Template(t), argModList)
+
+
+def fill(template, **args):
+ """
+ Convenience function for filling in a multiline template.
+
+ `fill(template, name1=v1, name2=v2)` is a lot like
+ `string.Template(template).substitute({"name1": v1, "name2": v2})`.
+
+ However, it's shorter, and has a few nice features:
+
+ * If `template` is indented, fill() automatically dedents it!
+ This makes code using fill() with Python's multiline strings
+ much nicer to look at.
+
+ * If `template` starts with a blank line, fill() strips it off.
+ (Again, convenient with multiline strings.)
+
+ * fill() recognizes a special kind of substitution
+ of the form `$*{name}`.
+
+ Use this to paste in, and automatically indent, multiple lines.
+ (Mnemonic: The `*` is for "multiple lines").
+
+ A `$*` substitution must appear by itself on a line, with optional
+ preceding indentation (spaces only). The whole line is replaced by the
+ corresponding keyword argument, indented appropriately. If the
+ argument is an empty string, no output is generated, not even a blank
+ line.
+ """
+
+ t, argModList = compile_fill_template(template)
+ # Now apply argModList to args
+ for (name, modified_name, depth) in argModList:
+ if not (args[name] == "" or args[name].endswith("\n")):
+ raise ValueError("Argument %s with value %r is missing a newline" % (name, args[name]))
+ args[modified_name] = indent(args[name], depth)
+
+ return t.substitute(args)
+
+
+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!
+
+ def deps(self):
+ """Produce the deps for a pp file"""
+ assert False # Override me!
+
+
+class CGStringTable(CGThing):
+ """
+ Generate a string table for the given strings with a function accessor:
+
+ const char *accessorName(unsigned int index) {
+ static const char table[] = "...";
+ static const uint16_t indices = { ... };
+ return &table[indices[index]];
+ }
+
+ This is more efficient than the more natural:
+
+ const char *table[] = {
+ ...
+ };
+
+ The uint16_t indices are smaller than the pointer equivalents, and the
+ string table requires no runtime relocations.
+ """
+ def __init__(self, accessorName, strings):
+ CGThing.__init__(self)
+ self.accessorName = accessorName
+ self.strings = strings
+
+ def declare(self):
+ return "extern const char *%s(unsigned int aIndex);\n" % self.accessorName
+
+ def define(self):
+ table = ' "\\0" '.join('"%s"' % s for s in self.strings)
+ indices = []
+ currentIndex = 0
+ for s in self.strings:
+ indices.append(currentIndex)
+ currentIndex += len(s) + 1 # for the null terminator
+ return fill(
+ """
+ const char *${name}(unsigned int aIndex)
+ {
+ static const char table[] = ${table};
+ static const uint16_t indices[] = { ${indices} };
+ static_assert(${currentIndex} <= UINT16_MAX, "string table overflow!");
+ return &table[indices[aIndex]];
+ }
+ """,
+ name=self.accessorName,
+ table=table,
+ indices=", ".join("%d" % index for index in indices),
+ currentIndex=currentIndex)
+
+
+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 declare(self):
+ if not self.descriptor.wantsXrays:
+ return ""
+ return dedent("""
+ // We declare this as an array so that retrieving a pointer to this
+ // binding's property hooks only requires compile/link-time resolvable
+ // address arithmetic. Declaring it as a pointer instead would require
+ // doing a run-time load to fetch a pointer to this binding's property
+ // hooks. And then structures which embedded a pointer to this structure
+ // would require a run-time load for proper initialization, which would
+ // then induce static constructors. Lots of static constructors.
+ extern const NativePropertyHooks sNativePropertyHooks[];
+ """)
+
+ def define(self):
+ if not self.descriptor.wantsXrays:
+ return ""
+ deleteNamedProperty = "nullptr"
+ if self.descriptor.concrete and self.descriptor.proxy:
+ resolveOwnProperty = "ResolveOwnProperty"
+ enumerateOwnProperties = "EnumerateOwnProperties"
+ if self.descriptor.needsXrayNamedDeleterHook():
+ deleteNamedProperty = "DeleteNamedProperty"
+ elif self.descriptor.needsXrayResolveHooks():
+ resolveOwnProperty = "ResolveOwnPropertyViaResolve"
+ enumerateOwnProperties = "EnumerateOwnPropertiesViaGetOwnPropertyNames"
+ else:
+ resolveOwnProperty = "nullptr"
+ enumerateOwnProperties = "nullptr"
+ if self.properties.hasNonChromeOnly():
+ regular = "sNativeProperties.Upcast()"
+ else:
+ regular = "nullptr"
+ if self.properties.hasChromeOnly():
+ chrome = "sChromeOnlyNativeProperties.Upcast()"
+ else:
+ chrome = "nullptr"
+ constructorID = "constructors::id::"
+ if self.descriptor.interface.hasInterfaceObject():
+ constructorID += self.descriptor.name
+ else:
+ constructorID += "_ID_Count"
+ prototypeID = "prototypes::id::"
+ if self.descriptor.interface.hasInterfacePrototypeObject():
+ prototypeID += self.descriptor.name
+ else:
+ prototypeID += "_ID_Count"
+ parentProtoName = self.descriptor.parentPrototypeName
+ parentHooks = (toBindingNamespace(parentProtoName) + "::sNativePropertyHooks"
+ if parentProtoName else 'nullptr')
+
+ if self.descriptor.wantsXrayExpandoClass:
+ expandoClass = "&sXrayExpandoObjectClass"
+ else:
+ expandoClass = "&DefaultXrayExpandoObjectClass"
+
+ return fill(
+ """
+ const NativePropertyHooks sNativePropertyHooks[] = { {
+ ${resolveOwnProperty},
+ ${enumerateOwnProperties},
+ ${deleteNamedProperty},
+ { ${regular}, ${chrome} },
+ ${prototypeID},
+ ${constructorID},
+ ${parentHooks},
+ ${expandoClass}
+ } };
+ """,
+ resolveOwnProperty=resolveOwnProperty,
+ enumerateOwnProperties=enumerateOwnProperties,
+ deleteNamedProperty=deleteNamedProperty,
+ regular=regular,
+ chrome=chrome,
+ prototypeID=prototypeID,
+ constructorID=constructorID,
+ parentHooks=parentHooks,
+ expandoClass=expandoClass)
+
+
+def NativePropertyHooks(descriptor):
+ return "&sEmptyNativePropertyHooks" if not descriptor.wantsXrays else "sNativePropertyHooks"
+
+
+def DOMClass(descriptor):
+ protoList = ['prototypes::id::' + proto for proto in descriptor.prototypeNameChain]
+ # 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)))
+
+ return fill(
+ """
+ { ${protoChain} },
+ IsBaseOf<nsISupports, ${nativeType} >::value,
+ ${hooks},
+ FindAssociatedGlobalForNative<${nativeType}>::Get,
+ GetProtoObjectHandle,
+ GetCCParticipant<${nativeType}>::Get()
+ """,
+ protoChain=', '.join(protoList),
+ nativeType=descriptor.nativeType,
+ hooks=NativePropertyHooks(descriptor))
+
+
+class CGDOMJSClass(CGThing):
+ """
+ Generate a DOMJSClass for a given descriptor
+ """
+ def __init__(self, descriptor):
+ CGThing.__init__(self)
+ self.descriptor = descriptor
+
+ def declare(self):
+ return ""
+
+ def define(self):
+ callHook = LEGACYCALLER_HOOK_NAME if self.descriptor.operations["LegacyCaller"] else 'nullptr'
+ objectMovedHook = OBJECT_MOVED_HOOK_NAME if self.descriptor.wrapperCache else 'nullptr'
+ slotCount = INSTANCE_RESERVED_SLOTS + self.descriptor.interface.totalMembersInSlots
+ classFlags = "JSCLASS_IS_DOMJSCLASS | JSCLASS_FOREGROUND_FINALIZE | "
+ if self.descriptor.isGlobal():
+ classFlags += "JSCLASS_DOM_GLOBAL | JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(DOM_GLOBAL_SLOTS)"
+ traceHook = "JS_GlobalObjectTraceHook"
+ reservedSlots = "JSCLASS_GLOBAL_APPLICATION_SLOTS"
+ else:
+ classFlags += "JSCLASS_HAS_RESERVED_SLOTS(%d)" % slotCount
+ traceHook = 'nullptr'
+ reservedSlots = slotCount
+ if self.descriptor.interface.isProbablyShortLivingObject():
+ classFlags += " | JSCLASS_SKIP_NURSERY_FINALIZE"
+ if self.descriptor.interface.getExtendedAttribute("NeedResolve"):
+ resolveHook = RESOLVE_HOOK_NAME
+ mayResolveHook = MAY_RESOLVE_HOOK_NAME
+ enumerateHook = ENUMERATE_HOOK_NAME
+ elif self.descriptor.isGlobal():
+ resolveHook = "mozilla::dom::ResolveGlobal"
+ mayResolveHook = "mozilla::dom::MayResolveGlobal"
+ enumerateHook = "mozilla::dom::EnumerateGlobal"
+ else:
+ resolveHook = "nullptr"
+ mayResolveHook = "nullptr"
+ enumerateHook = "nullptr"
+
+ return fill(
+ """
+ static const js::ClassOps sClassOps = {
+ ${addProperty}, /* addProperty */
+ nullptr, /* delProperty */
+ nullptr, /* getProperty */
+ nullptr, /* setProperty */
+ ${enumerate}, /* enumerate */
+ ${resolve}, /* resolve */
+ ${mayResolve}, /* mayResolve */
+ ${finalize}, /* finalize */
+ ${call}, /* call */
+ nullptr, /* hasInstance */
+ nullptr, /* construct */
+ ${trace}, /* trace */
+ };
+
+ static const js::ClassExtension sClassExtension = {
+ nullptr, /* weakmapKeyDelegateOp */
+ ${objectMoved} /* objectMovedOp */
+ };
+
+ static const DOMJSClass sClass = {
+ { "${name}",
+ ${flags},
+ &sClassOps,
+ JS_NULL_CLASS_SPEC,
+ &sClassExtension,
+ JS_NULL_OBJECT_OPS
+ },
+ $*{descriptor}
+ };
+ static_assert(${instanceReservedSlots} == DOM_INSTANCE_RESERVED_SLOTS,
+ "Must have the right minimal number of reserved slots.");
+ static_assert(${reservedSlots} >= ${slotCount},
+ "Must have enough reserved slots.");
+ """,
+ name=self.descriptor.interface.identifier.name,
+ flags=classFlags,
+ addProperty=ADDPROPERTY_HOOK_NAME if wantsAddProperty(self.descriptor) else 'nullptr',
+ enumerate=enumerateHook,
+ resolve=resolveHook,
+ mayResolve=mayResolveHook,
+ finalize=FINALIZE_HOOK_NAME,
+ call=callHook,
+ trace=traceHook,
+ objectMoved=objectMovedHook,
+ descriptor=DOMClass(self.descriptor),
+ instanceReservedSlots=INSTANCE_RESERVED_SLOTS,
+ reservedSlots=reservedSlots,
+ slotCount=slotCount)
+
+
+class CGDOMProxyJSClass(CGThing):
+ """
+ Generate a DOMJSClass for a given proxy descriptor
+ """
+ def __init__(self, descriptor):
+ CGThing.__init__(self)
+ self.descriptor = descriptor
+
+ def declare(self):
+ return ""
+
+ def define(self):
+ flags = ["JSCLASS_IS_DOMJSCLASS"]
+ # We don't use an IDL annotation for JSCLASS_EMULATES_UNDEFINED because
+ # we don't want people ever adding that to any interface other than
+ # HTMLAllCollection. So just hardcode it here.
+ if self.descriptor.interface.identifier.name == "HTMLAllCollection":
+ flags.append("JSCLASS_EMULATES_UNDEFINED")
+ objectMovedHook = OBJECT_MOVED_HOOK_NAME if self.descriptor.wrapperCache else 'nullptr'
+ return fill(
+ """
+ static const js::ClassExtension sClassExtension = PROXY_MAKE_EXT(
+ ${objectMoved}
+ );
+
+ static const DOMJSClass sClass = {
+ PROXY_CLASS_WITH_EXT("${name}",
+ ${flags},
+ &sClassExtension),
+ $*{descriptor}
+ };
+ """,
+ name=self.descriptor.interface.identifier.name,
+ flags=" | ".join(flags),
+ objectMoved=objectMovedHook,
+ descriptor=DOMClass(self.descriptor))
+
+
+class CGXrayExpandoJSClass(CGThing):
+ """
+ Generate a JSClass for an Xray expando object. This is only
+ needed if we have members in slots (for [Cached] or [StoreInSlot]
+ stuff).
+ """
+ def __init__(self, descriptor):
+ assert descriptor.interface.totalMembersInSlots != 0
+ assert descriptor.wantsXrays
+ assert descriptor.wantsXrayExpandoClass
+ CGThing.__init__(self)
+ self.descriptor = descriptor;
+
+ def declare(self):
+ return ""
+
+ def define(self):
+ return fill(
+ """
+ // This may allocate too many slots, because we only really need
+ // slots for our non-interface-typed members that we cache. But
+ // allocating slots only for those would make the slot index
+ // computations much more complicated, so let's do this the simple
+ // way for now.
+ DEFINE_XRAY_EXPANDO_CLASS(static, sXrayExpandoObjectClass, ${memberSlots});
+ """,
+ memberSlots=self.descriptor.interface.totalMembersInSlots)
+
+
+def PrototypeIDAndDepth(descriptor):
+ prototypeID = "prototypes::id::"
+ if descriptor.interface.hasInterfacePrototypeObject():
+ prototypeID += descriptor.interface.identifier.name
+ depth = "PrototypeTraits<%s>::Depth" % prototypeID
+ else:
+ prototypeID += "_ID_Count"
+ depth = "0"
+ return (prototypeID, depth)
+
+
+def InterfacePrototypeObjectProtoGetter(descriptor):
+ """
+ Returns a tuple with two elements:
+
+ 1) The name of the function to call to get the prototype to use for the
+ interface prototype object as a JSObject*.
+
+ 2) The name of the function to call to get the prototype to use for the
+ interface prototype object as a JS::Handle<JSObject*> or None if no
+ such function exists.
+ """
+ parentProtoName = descriptor.parentPrototypeName
+ if descriptor.hasNamedPropertiesObject:
+ protoGetter = "GetNamedPropertiesObject"
+ protoHandleGetter = None
+ elif parentProtoName is None:
+ if descriptor.interface.getExtendedAttribute("ArrayClass"):
+ protoGetter = "JS::GetRealmArrayPrototype"
+ elif descriptor.interface.getExtendedAttribute("ExceptionClass"):
+ protoGetter = "JS::GetRealmErrorPrototype"
+ elif descriptor.interface.isIteratorInterface():
+ protoGetter = "JS::GetRealmIteratorPrototype"
+ else:
+ protoGetter = "JS::GetRealmObjectPrototype"
+ protoHandleGetter = None
+ else:
+ prefix = toBindingNamespace(parentProtoName)
+ protoGetter = prefix + "::GetProtoObject"
+ protoHandleGetter = prefix + "::GetProtoObjectHandle"
+
+ return (protoGetter, protoHandleGetter)
+
+
+class CGPrototypeJSClass(CGThing):
+ def __init__(self, descriptor, properties):
+ CGThing.__init__(self)
+ self.descriptor = descriptor
+ self.properties = properties
+
+ def declare(self):
+ # We're purely for internal consumption
+ return ""
+
+ def define(self):
+ prototypeID, depth = PrototypeIDAndDepth(self.descriptor)
+ slotCount = "DOM_INTERFACE_PROTO_SLOTS_BASE"
+ # Globals handle unforgeables directly in Wrap() instead of
+ # via a holder.
+ if (self.descriptor.hasUnforgeableMembers and
+ not self.descriptor.isGlobal()):
+ slotCount += " + 1 /* slot for the JSObject holding the unforgeable properties */"
+ (protoGetter, _) = InterfacePrototypeObjectProtoGetter(self.descriptor)
+ type = "eGlobalInterfacePrototype" if self.descriptor.isGlobal() else "eInterfacePrototype"
+ return fill(
+ """
+ static const DOMIfaceAndProtoJSClass sPrototypeClass = {
+ {
+ "${name}Prototype",
+ JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(${slotCount}),
+ JS_NULL_CLASS_OPS,
+ JS_NULL_CLASS_SPEC,
+ JS_NULL_CLASS_EXT,
+ JS_NULL_OBJECT_OPS
+ },
+ ${type},
+ false,
+ ${prototypeID},
+ ${depth},
+ ${hooks},
+ "[object ${name}Prototype]",
+ ${protoGetter}
+ };
+ """,
+ name=self.descriptor.interface.identifier.name,
+ slotCount=slotCount,
+ type=type,
+ hooks=NativePropertyHooks(self.descriptor),
+ prototypeID=prototypeID,
+ depth=depth,
+ protoGetter=protoGetter)
+
+
+def NeedsGeneratedHasInstance(descriptor):
+ assert descriptor.interface.hasInterfaceObject()
+ return descriptor.hasXPConnectImpls or descriptor.interface.isConsequential()
+
+
+def InterfaceObjectProtoGetter(descriptor, forXrays=False):
+ """
+ Returns a tuple with two elements:
+
+ 1) The name of the function to call to get the prototype to use for the
+ interface object as a JSObject*.
+
+ 2) The name of the function to call to get the prototype to use for the
+ interface prototype as a JS::Handle<JSObject*> or None if no such
+ function exists.
+ """
+ parentInterface = descriptor.interface.parent
+ if parentInterface:
+ assert not descriptor.interface.isNamespace()
+ parentIfaceName = parentInterface.identifier.name
+ parentDesc = descriptor.getDescriptor(parentIfaceName)
+ prefix = toBindingNamespace(parentDesc.name)
+ protoGetter = prefix + "::GetConstructorObject"
+ protoHandleGetter = prefix + "::GetConstructorObjectHandle"
+ elif descriptor.interface.isNamespace():
+ if (forXrays or
+ not descriptor.interface.getExtendedAttribute("ProtoObjectHack")):
+ protoGetter = "JS::GetRealmObjectPrototype"
+ else:
+ protoGetter = "binding_detail::GetHackedNamespaceProtoObject"
+ protoHandleGetter = None
+ else:
+ protoGetter = "JS::GetRealmFunctionPrototype"
+ protoHandleGetter = None
+ return (protoGetter, protoHandleGetter)
+
+
+class CGInterfaceObjectJSClass(CGThing):
+ def __init__(self, descriptor, properties):
+ CGThing.__init__(self)
+ self.descriptor = descriptor
+ self.properties = properties
+
+ def declare(self):
+ # We're purely for internal consumption
+ return ""
+
+ def define(self):
+ if self.descriptor.interface.ctor():
+ assert not self.descriptor.interface.isNamespace()
+ ctorname = CONSTRUCT_HOOK_NAME
+ elif self.descriptor.interface.isNamespace():
+ ctorname = "nullptr"
+ else:
+ ctorname = "ThrowingConstructor"
+ needsHasInstance = (
+ not NeedsGeneratedHasInstance(self.descriptor) and
+ self.descriptor.interface.hasInterfacePrototypeObject())
+
+ prototypeID, depth = PrototypeIDAndDepth(self.descriptor)
+ slotCount = "DOM_INTERFACE_SLOTS_BASE"
+ if len(self.descriptor.interface.namedConstructors) > 0:
+ slotCount += (" + %i /* slots for the named constructors */" %
+ len(self.descriptor.interface.namedConstructors))
+ (protoGetter, _) = InterfaceObjectProtoGetter(self.descriptor,
+ forXrays=True)
+
+ if ctorname == "ThrowingConstructor":
+ ret = ""
+ classOpsPtr = "&sBoringInterfaceObjectClassClassOps"
+ elif ctorname == "nullptr":
+ ret = ""
+ classOpsPtr = "JS_NULL_CLASS_OPS"
+ else:
+ ret = fill(
+ """
+ static const js::ClassOps sInterfaceObjectClassOps = {
+ nullptr, /* addProperty */
+ nullptr, /* delProperty */
+ nullptr, /* getProperty */
+ nullptr, /* setProperty */
+ nullptr, /* enumerate */
+ nullptr, /* resolve */
+ nullptr, /* mayResolve */
+ nullptr, /* finalize */
+ ${ctorname}, /* call */
+ nullptr, /* hasInstance */
+ ${ctorname}, /* construct */
+ nullptr, /* trace */
+ };
+
+ """,
+ ctorname=ctorname)
+ classOpsPtr = "&sInterfaceObjectClassOps"
+
+ if self.descriptor.interface.isNamespace():
+ classString = self.descriptor.interface.getExtendedAttribute("ClassString")
+ if classString is None:
+ classString = "Object"
+ else:
+ classString = classString[0]
+ toStringResult = "[object %s]" % classString
+ objectOps = "JS_NULL_OBJECT_OPS"
+ else:
+ classString = "Function"
+ toStringResult = ("function %s() {\\n [native code]\\n}" %
+ self.descriptor.interface.identifier.name)
+ # We need non-default ObjectOps so we can actually make
+ # use of our toStringResult.
+ objectOps = "&sInterfaceObjectClassObjectOps"
+
+ ret = ret + fill(
+ """
+ static const DOMIfaceAndProtoJSClass sInterfaceObjectClass = {
+ {
+ "${classString}",
+ JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(${slotCount}),
+ ${classOpsPtr},
+ JS_NULL_CLASS_SPEC,
+ JS_NULL_CLASS_EXT,
+ ${objectOps}
+ },
+ eInterface,
+ ${needsHasInstance},
+ ${prototypeID},
+ ${depth},
+ ${hooks},
+ "${toStringResult}",
+ ${protoGetter}
+ };
+ """,
+ classString=classString,
+ slotCount=slotCount,
+ classOpsPtr=classOpsPtr,
+ hooks=NativePropertyHooks(self.descriptor),
+ objectOps=objectOps,
+ needsHasInstance=toStringBool(needsHasInstance),
+ prototypeID=prototypeID,
+ depth=depth,
+ toStringResult=toStringResult,
+ protoGetter=protoGetter)
+ return ret
+
+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)
+ # Make a copy of the kids into a list, because if someone passes in a
+ # generator we won't be able to both declare and define ourselves, or
+ # define ourselves more than once!
+ self.children = list(children)
+ self.joiner = joiner
+
+ def append(self, child):
+ self.children.append(child)
+
+ def prepend(self, child):
+ self.children.insert(0, child)
+
+ def extend(self, kids):
+ self.children.extend(kids)
+
+ def join(self, iterable):
+ return self.joiner.join(s for s in iterable if len(s) > 0)
+
+ 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)
+
+ def deps(self):
+ deps = set()
+ for child in self.children:
+ if child is None:
+ continue
+ deps = deps.union(child.deps())
+ return deps
+
+ def __len__(self):
+ return len(self.children)
+
+
+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
+
+ def deps(self):
+ return set()
+
+
+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):
+ assert isinstance(child, CGThing)
+ CGThing.__init__(self)
+ self.child = child
+ self.indentLevel = indentLevel
+ self.declareOnly = declareOnly
+
+ def declare(self):
+ return indent(self.child.declare(), self.indentLevel)
+
+ def define(self):
+ defn = self.child.define()
+ if self.declareOnly:
+ return defn
+ else:
+ return indent(defn, self.indentLevel)
+
+
+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:
+ decl = self.reindentString(decl, self.declarePre)
+ return self.declarePre + decl + self.declarePost
+
+ def define(self):
+ if self.declareOnly:
+ return ''
+ defn = self.child.define()
+ if self.reindent:
+ defn = self.reindentString(defn, self.definePre)
+ return self.definePre + defn + self.definePost
+
+ @staticmethod
+ def reindentString(stringToIndent, widthString):
+ # We don't use lineStartDetector because we don't want to
+ # insert whitespace at the beginning of our _first_ line.
+ # Use the length of the last line of width string, in case
+ # it is a multiline string.
+ lastLineWidth = len(widthString.splitlines()[-1])
+ return stripTrailingWhitespace(
+ stringToIndent.replace("\n", "\n" + (" " * lastLineWidth)))
+
+ def deps(self):
+ return self.child.deps()
+
+
+class CGIfWrapper(CGList):
+ def __init__(self, child, condition):
+ CGList.__init__(self, [
+ CGWrapper(CGGeneric(condition), pre="if (", post=") {\n", reindent=True),
+ CGIndenter(child),
+ CGGeneric("}\n")
+ ])
+
+
+class CGIfElseWrapper(CGList):
+ def __init__(self, condition, ifTrue, ifFalse):
+ CGList.__init__(self, [
+ CGWrapper(CGGeneric(condition), pre="if (", post=") {\n", reindent=True),
+ CGIndenter(ifTrue),
+ CGGeneric("} else {\n"),
+ CGIndenter(ifFalse),
+ CGGeneric("}\n")
+ ])
+
+
+class CGElseChain(CGThing):
+ """
+ Concatenate if statements in an if-else-if-else chain.
+ """
+ def __init__(self, children):
+ self.children = [c for c in children if c is not None]
+
+ def declare(self):
+ assert False
+
+ def define(self):
+ if not self.children:
+ return ""
+ s = self.children[0].define()
+ assert s.endswith("\n")
+ for child in self.children[1:]:
+ code = child.define()
+ assert code.startswith("if") or code.startswith("{")
+ assert code.endswith("\n")
+ s = s.rstrip() + " else " + code
+ return s
+
+
+class CGTemplatedType(CGWrapper):
+ def __init__(self, templateName, child, isConst=False, isReference=False):
+ const = "const " if isConst else ""
+ pre = "%s%s<" % (const, templateName)
+ ref = "&" if isReference else ""
+ post = ">%s" % ref
+ CGWrapper.__init__(self, child, pre=pre, post=post)
+
+
+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)
+
+
+class CGHeaders(CGWrapper):
+ """
+ Generates the appropriate include statements.
+ """
+ def __init__(self, descriptors, dictionaries, callbacks,
+ callbackDescriptors,
+ declareIncludes, defineIncludes, prefix, child,
+ config=None, jsImplementedDescriptors=[]):
+ """
+ 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.
+
+ |prefix| contains the basename of the file that we generate include
+ statements for.
+ """
+
+ # Determine the filenames for which we need headers.
+ interfaceDeps = [d.interface for d in descriptors]
+ ancestors = []
+ for iface in interfaceDeps:
+ if iface.parent:
+ # We're going to need our parent's prototype, to use as the
+ # prototype of our prototype object.
+ ancestors.append(iface.parent)
+ # And if we have an interface object, we'll need the nearest
+ # ancestor with an interface object too, so we can use its
+ # interface object as the proto of our interface object.
+ if iface.hasInterfaceObject():
+ parent = iface.parent
+ while parent and not parent.hasInterfaceObject():
+ parent = parent.parent
+ if parent:
+ ancestors.append(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 if d.needsHeaderInclude())
+
+ # Grab the includes for checking hasInstance
+ interfacesImplementingSelf = set()
+ for d in descriptors:
+ interfacesImplementingSelf |= d.interface.interfacesImplementingSelf
+ implementationIncludes |= set(self.getDeclarationFilename(i) for i in
+ interfacesImplementingSelf)
+
+ # Grab the includes for the things that involve XPCOM interfaces
+ hasInstanceIncludes = set("nsIDOM" + d.interface.identifier.name + ".h" for d
+ in descriptors if
+ d.interface.hasInterfaceObject() and
+ NeedsGeneratedHasInstance(d) and
+ d.interface.hasInterfacePrototypeObject())
+ if len(hasInstanceIncludes) > 0:
+ hasInstanceIncludes.add("nsContentUtils.h")
+
+ # Now find all the things we'll need as arguments because we
+ # need to wrap or unwrap them.
+ bindingHeaders = set()
+ declareIncludes = set(declareIncludes)
+
+ def addHeadersForType((t, dictionary)):
+ """
+ Add the relevant headers for this type. We use dictionary, if
+ passed, to decide what to do with interface types.
+ """
+ # Dictionaries have members that need to be actually
+ # declared, not just forward-declared.
+ if dictionary:
+ headerSet = declareIncludes
+ else:
+ headerSet = bindingHeaders
+ if t.nullable():
+ # Need to make sure that Nullable as a dictionary
+ # member works.
+ headerSet.add("mozilla/dom/Nullable.h")
+ unrolled = t.unroll()
+ if unrolled.isUnion():
+ headerSet.add(self.getUnionDeclarationFilename(config, unrolled))
+ bindingHeaders.add("mozilla/dom/UnionConversions.h")
+ elif unrolled.isDate():
+ if dictionary or jsImplementedDescriptors:
+ declareIncludes.add("mozilla/dom/Date.h")
+ else:
+ bindingHeaders.add("mozilla/dom/Date.h")
+ elif unrolled.isInterface():
+ if unrolled.isSpiderMonkeyInterface():
+ bindingHeaders.add("jsfriendapi.h")
+ if jsImplementedDescriptors:
+ # Since we can't forward-declare typed array types
+ # (because they're typedefs), we have to go ahead and
+ # just include their header if we need to have functions
+ # taking references to them declared in that header.
+ headerSet = declareIncludes
+ headerSet.add("mozilla/dom/TypedArray.h")
+ else:
+ try:
+ typeDesc = config.getDescriptor(unrolled.inner.identifier.name)
+ except NoSuchDescriptorError:
+ return
+ # Dictionaries with interface members rely on the
+ # actual class definition of that interface member
+ # being visible in the binding header, because they
+ # store them in RefPtr and have inline
+ # constructors/destructors.
+ #
+ # XXXbz maybe dictionaries with interface members
+ # should just have out-of-line constructors and
+ # destructors?
+ headerSet.add(typeDesc.headerFile)
+ elif unrolled.isDictionary():
+ headerSet.add(self.getDeclarationFilename(unrolled.inner))
+ elif unrolled.isCallback():
+ headerSet.add(self.getDeclarationFilename(unrolled.callback))
+ elif unrolled.isFloat() and not unrolled.isUnrestricted():
+ # Restricted floats are tested for finiteness
+ bindingHeaders.add("mozilla/FloatingPoint.h")
+ bindingHeaders.add("mozilla/dom/PrimitiveConversions.h")
+ elif unrolled.isEnum():
+ filename = self.getDeclarationFilename(unrolled.inner)
+ declareIncludes.add(filename)
+ elif unrolled.isPrimitive():
+ bindingHeaders.add("mozilla/dom/PrimitiveConversions.h")
+ elif unrolled.isMozMap():
+ if dictionary or jsImplementedDescriptors:
+ declareIncludes.add("mozilla/dom/MozMap.h")
+ else:
+ bindingHeaders.add("mozilla/dom/MozMap.h")
+ # Also add headers for the type the MozMap is
+ # parametrized over, if needed.
+ addHeadersForType((t.inner, dictionary))
+
+ map(addHeadersForType,
+ getAllTypes(descriptors + callbackDescriptors, dictionaries,
+ callbacks))
+
+ # Now make sure we're not trying to include the header from inside itself
+ declareIncludes.discard(prefix + ".h")
+
+ def addHeaderForFunc(func, desc):
+ if func is None:
+ return
+ # Include the right class header, which we can only do
+ # if this is a class member function.
+ if desc is not None and not desc.headerIsDefault:
+ # An explicit header file was provided, assume that we know
+ # what we're doing.
+ return
+
+ if "::" in func:
+ # Strip out the function name and convert "::" to "/"
+ bindingHeaders.add("/".join(func.split("::")[:-1]) + ".h")
+
+ # Now for non-callback descriptors make sure we include any
+ # headers needed by Func declarations and other things like that.
+ for desc in descriptors:
+ # If this is an iterator interface generated for a seperate
+ # iterable interface, skip generating type includes, as we have
+ # what we need in IterableIterator.h
+ if desc.interface.isExternal() or desc.interface.isIteratorInterface():
+ continue
+
+ for m in desc.interface.members:
+ addHeaderForFunc(PropertyDefiner.getStringAttr(m, "Func"), desc)
+ staticTypeOverride = PropertyDefiner.getStringAttr(m, "StaticClassOverride")
+ if staticTypeOverride:
+ bindingHeaders.add("/".join(staticTypeOverride.split("::")) + ".h")
+ # getExtendedAttribute() returns a list, extract the entry.
+ funcList = desc.interface.getExtendedAttribute("Func")
+ if funcList is not None:
+ addHeaderForFunc(funcList[0], desc)
+
+ if desc.interface.maplikeOrSetlikeOrIterable:
+ # We need ToJSValue.h for maplike/setlike type conversions
+ bindingHeaders.add("mozilla/dom/ToJSValue.h")
+ # Add headers for the key and value types of the
+ # maplike/setlike/iterable, since they'll be needed for
+ # convenience functions
+ if desc.interface.maplikeOrSetlikeOrIterable.hasKeyType():
+ addHeadersForType((desc.interface.maplikeOrSetlikeOrIterable.keyType,
+ None))
+ if desc.interface.maplikeOrSetlikeOrIterable.hasValueType():
+ addHeadersForType((desc.interface.maplikeOrSetlikeOrIterable.valueType,
+ None))
+
+ for d in dictionaries:
+ if d.parent:
+ declareIncludes.add(self.getDeclarationFilename(d.parent))
+ bindingHeaders.add(self.getDeclarationFilename(d))
+ for m in d.members:
+ addHeaderForFunc(PropertyDefiner.getStringAttr(m, "Func"),
+ None)
+ # No need to worry about Func on members of ancestors, because that
+ # will happen automatically in whatever files those ancestors live
+ # in.
+
+ for c in callbacks:
+ bindingHeaders.add(self.getDeclarationFilename(c))
+
+ for c in callbackDescriptors:
+ bindingHeaders.add(self.getDeclarationFilename(c.interface))
+
+ if len(callbacks) != 0:
+ # We need CallbackFunction to serve as our parent class
+ declareIncludes.add("mozilla/dom/CallbackFunction.h")
+ # And we need ToJSValue.h so we can wrap "this" objects
+ declareIncludes.add("mozilla/dom/ToJSValue.h")
+
+ if len(callbackDescriptors) != 0 or len(jsImplementedDescriptors) != 0:
+ # We need CallbackInterface to serve as our parent class
+ declareIncludes.add("mozilla/dom/CallbackInterface.h")
+ # And we need ToJSValue.h so we can wrap "this" objects
+ declareIncludes.add("mozilla/dom/ToJSValue.h")
+
+ # Also need to include the headers for ancestors of
+ # JS-implemented interfaces.
+ for jsImplemented in jsImplementedDescriptors:
+ jsParent = jsImplemented.interface.parent
+ if jsParent:
+ parentDesc = jsImplemented.getDescriptor(jsParent.identifier.name)
+ declareIncludes.add(parentDesc.jsImplParentHeader)
+
+ # 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 |
+ hasInstanceIncludes |
+ 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')
+
+ @staticmethod
+ def getUnionDeclarationFilename(config, unionType):
+ assert unionType.isUnion()
+ assert unionType.unroll() == unionType
+ # If a union is "defined" in multiple files, it goes in UnionTypes.h.
+ if len(config.filenamesPerUnion[unionType.name]) > 1:
+ return "mozilla/dom/UnionTypes.h"
+ # If a union is defined by a built-in typedef, it also goes in
+ # UnionTypes.h.
+ assert len(config.filenamesPerUnion[unionType.name]) == 1
+ if "<unknown>" in config.filenamesPerUnion[unionType.name]:
+ return "mozilla/dom/UnionTypes.h"
+ return CGHeaders.getDeclarationFilename(unionType)
+
+
+def SortedDictValues(d):
+ """
+ Returns a list of values from the dict sorted by key.
+ """
+ return [v for k, v in sorted(d.items())]
+
+
+def UnionsForFile(config, webIDLFile):
+ """
+ Returns a list of union types for all union types that are only used in
+ webIDLFile. If webIDLFile is None this will return the list of tuples for
+ union types that are used in more than one WebIDL file.
+ """
+ return config.unionsPerFilename.get(webIDLFile, [])
+
+
+def UnionTypes(unionTypes, config):
+ """
+ The unionTypes argument should be a list of union types. This is typically
+ the list generated by UnionsForFile.
+
+ Returns a tuple containing a set of header filenames to include in
+ the header for the types in unionTypes, a set of header filenames to
+ include in the implementation file for the types in unionTypes, a set
+ of tuples containing a type declaration and a boolean if the type is a
+ struct for member types of the union, a list of traverse methods,
+ unlink methods and a list of union types. These last three lists only
+ contain unique union types.
+ """
+
+ headers = set()
+ implheaders = set()
+ declarations = set()
+ unionStructs = dict()
+ traverseMethods = dict()
+ unlinkMethods = dict()
+
+ for t in unionTypes:
+ name = str(t)
+ if name not in unionStructs:
+ unionStructs[name] = t
+
+ def addHeadersForType(f):
+ if f.nullable():
+ headers.add("mozilla/dom/Nullable.h")
+ isSequence = f.isSequence()
+ f = f.unroll()
+ if f.isInterface():
+ if f.isSpiderMonkeyInterface():
+ headers.add("jsfriendapi.h")
+ headers.add("mozilla/dom/TypedArray.h")
+ else:
+ try:
+ typeDesc = config.getDescriptor(f.inner.identifier.name)
+ except NoSuchDescriptorError:
+ return
+ if typeDesc.interface.isCallback() or isSequence:
+ # Callback interfaces always use strong refs, so
+ # we need to include the right header to be able
+ # to Release() in our inlined code.
+ #
+ # Similarly, sequences always contain strong
+ # refs, so we'll need the header to handler
+ # those.
+ headers.add(typeDesc.headerFile)
+ else:
+ declarations.add((typeDesc.nativeType, False))
+ implheaders.add(typeDesc.headerFile)
+ elif f.isDictionary():
+ # For a dictionary, we need to see its declaration in
+ # UnionTypes.h so we have its sizeof and know how big to
+ # make our union.
+ headers.add(CGHeaders.getDeclarationFilename(f.inner))
+ # And if it needs rooting, we need RootedDictionary too
+ if typeNeedsRooting(f):
+ headers.add("mozilla/dom/RootedDictionary.h")
+ elif f.isEnum():
+ # Need to see the actual definition of the enum,
+ # unfortunately.
+ headers.add(CGHeaders.getDeclarationFilename(f.inner))
+ elif f.isCallback():
+ # Callbacks always use strong refs, so we need to include
+ # the right header to be able to Release() in our inlined
+ # code.
+ headers.add(CGHeaders.getDeclarationFilename(f.callback))
+ elif f.isMozMap():
+ headers.add("mozilla/dom/MozMap.h")
+ # And add headers for the type we're parametrized over
+ addHeadersForType(f.inner)
+
+ implheaders.add(CGHeaders.getUnionDeclarationFilename(config, t))
+ for f in t.flatMemberTypes:
+ assert not f.nullable()
+ addHeadersForType(f)
+
+ if idlTypeNeedsCycleCollection(t):
+ declarations.add(("mozilla::dom::%s" % CGUnionStruct.unionTypeName(t, True), False))
+ traverseMethods[name] = CGCycleCollectionTraverseForOwningUnionMethod(t)
+ unlinkMethods[name] = CGCycleCollectionUnlinkForOwningUnionMethod(t)
+
+ # The order of items in CGList is important.
+ # Since the union structs friend the unlinkMethods, the forward-declaration
+ # for these methods should come before the class declaration. Otherwise
+ # some compilers treat the friend declaration as a forward-declaration in
+ # the class scope.
+ return (headers, implheaders, declarations,
+ SortedDictValues(traverseMethods), SortedDictValues(unlinkMethods),
+ SortedDictValues(unionStructs))
+
+
+def UnionConversions(unionTypes, config):
+ """
+ The unionTypes argument should be a list of tuples, each containing two
+ elements: a union type and a descriptor. This is typically the list
+ generated by UnionsForFile.
+
+ Returns a tuple containing a list of headers and a CGThing to declare all
+ union argument conversion helper structs.
+ """
+ headers = set()
+ unionConversions = dict()
+
+ for t in unionTypes:
+ name = str(t)
+ if name not in unionConversions:
+ unionConversions[name] = CGUnionConversionStruct(t, config)
+
+ def addHeadersForType(f):
+ f = f.unroll()
+ if f.isInterface():
+ if f.isSpiderMonkeyInterface():
+ headers.add("jsfriendapi.h")
+ headers.add("mozilla/dom/TypedArray.h")
+ elif f.inner.isExternal():
+ try:
+ typeDesc = config.getDescriptor(f.inner.identifier.name)
+ except NoSuchDescriptorError:
+ return
+ headers.add(typeDesc.headerFile)
+ else:
+ headers.add(CGHeaders.getDeclarationFilename(f.inner))
+ elif f.isDictionary():
+ headers.add(CGHeaders.getDeclarationFilename(f.inner))
+ elif f.isPrimitive():
+ headers.add("mozilla/dom/PrimitiveConversions.h")
+ elif f.isMozMap():
+ headers.add("mozilla/dom/MozMap.h")
+ # And the internal type of the MozMap
+ addHeadersForType(f.inner)
+
+ # We plan to include UnionTypes.h no matter what, so it's
+ # OK if we throw it into the set here.
+ headers.add(CGHeaders.getUnionDeclarationFilename(config, t))
+
+ for f in t.flatMemberTypes:
+ addHeadersForType(f)
+
+ return (headers,
+ 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, default=None):
+ self.argType = argType
+ self.name = name
+ self.default = default
+
+ def declare(self):
+ string = self.argType + ' ' + self.name
+ 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.
+
+ 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, declare):
+ return ', '.join([a.declare() if declare else a.define() 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(True)
+ return "%s%s%s(%s);\n" % (self._template(), self._decorators(), self.name, self._argstring(True))
+
+ def indent_body(self, body):
+ """
+ Indent the code returned by self.definition_body(). Most classes
+ simply indent everything two spaces. This is here for
+ CGRegisterProtos, which needs custom indentation.
+ """
+ return indent(body)
+
+ def _define(self, fromDeclare=False):
+ return (self.definition_prologue(fromDeclare) +
+ self.indent_body(self.definition_body()) +
+ self.definition_epilogue())
+
+ def define(self):
+ return "" if self.inline else self._define()
+
+ def definition_prologue(self, fromDeclare):
+ return "%s%s%s(%s)\n{\n" % (self._template(), self._decorators(),
+ self.name, self._argstring(fromDeclare))
+
+ def definition_epilogue(self):
+ return "}\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 = UnwrapPossiblyNotInitializedDOMObject<%s>(obj);\n" %
+ (self.descriptor.nativeType, self.descriptor.nativeType))
+
+ def definition_body(self):
+ return self.definition_body_prologue() + self.generate_code()
+
+ def generate_code(self):
+ assert False # Override me!
+
+
+class CGGetJSClassMethod(CGAbstractMethod):
+ def __init__(self, descriptor):
+ CGAbstractMethod.__init__(self, descriptor, 'GetJSClass', 'const JSClass*',
+ [])
+
+ def definition_body(self):
+ return "return sClass.ToJSClass();\n"
+
+
+class CGAddPropertyHook(CGAbstractClassHook):
+ """
+ A hook for addProperty, used to preserve our wrapper from GC.
+ """
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'),
+ Argument('JS::Handle<JSObject*>', 'obj'),
+ Argument('JS::Handle<jsid>', 'id'),
+ Argument('JS::Handle<JS::Value>', 'val')]
+ CGAbstractClassHook.__init__(self, descriptor, ADDPROPERTY_HOOK_NAME,
+ 'bool', args)
+
+ def generate_code(self):
+ assert self.descriptor.wrapperCache
+ return dedent("""
+ // We don't want to preserve if we don't have a wrapper, and we
+ // obviously can't preserve if we're not initialized.
+ if (self && self->GetWrapperPreserveColor()) {
+ PreserveWrapper(self);
+ }
+ return true;
+ """)
+
+
+def finalizeHook(descriptor, hookName, freeOp):
+ finalize = ""
+ if descriptor.wrapperCache:
+ finalize += "ClearWrapper(self, self);\n"
+ if descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
+ finalize += "self->mExpandoAndGeneration.expando = JS::UndefinedValue();\n"
+ if descriptor.isGlobal():
+ finalize += "mozilla::dom::FinalizeGlobal(CastToJSFreeOp(%s), obj);\n" % freeOp
+ finalize += ("AddForDeferredFinalization<%s>(self);\n" %
+ descriptor.nativeType)
+ return CGIfWrapper(CGGeneric(finalize), "self")
+
+
+class CGClassFinalizeHook(CGAbstractClassHook):
+ """
+ A hook for finalize, used to release our native object.
+ """
+ def __init__(self, descriptor):
+ args = [Argument('js::FreeOp*', 'fop'), Argument('JSObject*', 'obj')]
+ CGAbstractClassHook.__init__(self, descriptor, FINALIZE_HOOK_NAME,
+ 'void', args)
+
+ def generate_code(self):
+ return finalizeHook(self.descriptor, self.name, self.args[0].name).define()
+
+
+class CGClassObjectMovedHook(CGAbstractClassHook):
+ """
+ A hook for objectMovedOp, used to update the wrapper cache when an object it
+ is holding moves.
+ """
+ def __init__(self, descriptor):
+ args = [Argument('JSObject*', 'obj'), Argument('const JSObject*', 'old')]
+ CGAbstractClassHook.__init__(self, descriptor, OBJECT_MOVED_HOOK_NAME,
+ 'void', args)
+
+ def generate_code(self):
+ assert self.descriptor.wrapperCache
+ return CGIfWrapper(CGGeneric("UpdateWrapper(self, self, obj, old);\n"),
+ "self").define()
+
+
+def JSNativeArguments():
+ return [Argument('JSContext*', 'cx'),
+ Argument('unsigned', 'argc'),
+ Argument('JS::Value*', 'vp')]
+
+
+class CGClassConstructor(CGAbstractStaticMethod):
+ """
+ JS-visible constructor for our objects
+ """
+ def __init__(self, descriptor, ctor, name=CONSTRUCT_HOOK_NAME):
+ CGAbstractStaticMethod.__init__(self, descriptor, name, 'bool',
+ JSNativeArguments())
+ self._ctor = 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):
+ # [ChromeOnly] interfaces may only be constructed by chrome.
+ chromeOnlyCheck = ""
+ if isChromeOnly(self._ctor):
+ chromeOnlyCheck = dedent("""
+ if (!nsContentUtils::ThreadsafeIsCallerChrome()) {
+ return ThrowingConstructor(cx, argc, vp);
+ }
+
+ """)
+
+ # Additionally, we want to throw if a caller does a bareword invocation
+ # of a constructor without |new|. We don't enforce this for chrome in
+ # realease builds to avoid the addon compat fallout of making that
+ # change. See bug 916644.
+ #
+ # Figure out the name of our constructor for error reporting purposes.
+ # For unnamed webidl constructors, identifier.name is "constructor" but
+ # the name JS sees is the interface name; for named constructors
+ # identifier.name is the actual name.
+ name = self._ctor.identifier.name
+ if name != "constructor":
+ ctorName = name
+ else:
+ ctorName = self.descriptor.interface.identifier.name
+
+ preamble = fill(
+ """
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ JS::Rooted<JSObject*> obj(cx, &args.callee());
+ $*{chromeOnlyCheck}
+ if (!args.isConstructing()) {
+ // XXXbz wish I could get the name from the callee instead of
+ // Adding more relocations
+ return ThrowConstructorWithoutNew(cx, "${ctorName}");
+ }
+ JS::Rooted<JSObject*> desiredProto(cx);
+ if (!GetDesiredProto(cx, args, &desiredProto)) {
+ return false;
+ }
+ """,
+ chromeOnlyCheck=chromeOnlyCheck,
+ ctorName=ctorName)
+
+ name = self._ctor.identifier.name
+ nativeName = MakeNativeName(self.descriptor.binaryNameFor(name))
+ callGenerator = CGMethodCall(nativeName, True, self.descriptor,
+ self._ctor, isConstructor=True,
+ constructorName=ctorName)
+ return preamble + "\n" + callGenerator.define()
+
+
+# Encapsulate the constructor in a helper method to share genConstructorBody with CGJSImplMethod.
+class CGConstructNavigatorObject(CGAbstractMethod):
+ """
+ Construct a new JS-implemented WebIDL DOM object, for use on navigator.
+ """
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'),
+ Argument('JS::Handle<JSObject*>', 'obj'),
+ Argument('ErrorResult&', 'aRv')]
+ rtype = 'already_AddRefed<%s>' % descriptor.name
+ CGAbstractMethod.__init__(self, descriptor, "ConstructNavigatorObject",
+ rtype, args)
+
+ def definition_body(self):
+ if not self.descriptor.interface.isJSImplemented():
+ raise TypeError("Only JS-implemented classes are currently supported "
+ "on navigator. See bug 856820.")
+
+ return dedent(
+ """
+ GlobalObject global(cx, obj);
+ if (global.Failed()) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return nullptr;
+ }
+ """) + genConstructorBody(self.descriptor)
+
+
+def NamedConstructorName(m):
+ return '_' + m.identifier.name
+
+
+class CGNamedConstructors(CGThing):
+ def __init__(self, descriptor):
+ self.descriptor = descriptor
+ CGThing.__init__(self)
+
+ def declare(self):
+ return ""
+
+ def define(self):
+ if len(self.descriptor.interface.namedConstructors) == 0:
+ return ""
+
+ constructorID = "constructors::id::"
+ if self.descriptor.interface.hasInterfaceObject():
+ constructorID += self.descriptor.name
+ else:
+ constructorID += "_ID_Count"
+
+ namedConstructors = ""
+ for n in self.descriptor.interface.namedConstructors:
+ namedConstructors += (
+ "{ \"%s\", { %s, &sNamedConstructorNativePropertyHooks }, %i },\n" %
+ (n.identifier.name, NamedConstructorName(n), methodLength(n)))
+
+ return fill(
+ """
+ const NativePropertyHooks sNamedConstructorNativePropertyHooks = {
+ nullptr,
+ nullptr,
+ nullptr,
+ { nullptr, nullptr },
+ prototypes::id::${name},
+ ${constructorID},
+ nullptr
+ };
+
+ static const NamedConstructor namedConstructors[] = {
+ $*{namedConstructors}
+ { nullptr, { nullptr, nullptr }, 0 }
+ };
+ """,
+ name=self.descriptor.name,
+ constructorID=constructorID,
+ namedConstructors=namedConstructors)
+
+
+class CGHasInstanceHook(CGAbstractStaticMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'),
+ Argument('unsigned', 'argc'),
+ Argument('JS::Value*', 'vp')]
+ assert descriptor.interface.hasInterfaceObject()
+ assert NeedsGeneratedHasInstance(descriptor)
+ CGAbstractStaticMethod.__init__(self, descriptor, HASINSTANCE_HOOK_NAME,
+ 'bool', args)
+
+ def define(self):
+ return CGAbstractStaticMethod.define(self)
+
+ def definition_body(self):
+ return self.generate_code()
+
+ def generate_code(self):
+ header = dedent("""
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ if (!args.get(0).isObject()) {
+ args.rval().setBoolean(false);
+ return true;
+ }
+
+ JS::Rooted<JSObject*> instance(cx, &args[0].toObject());
+ """)
+ if self.descriptor.interface.hasInterfacePrototypeObject():
+ return (
+ header +
+ fill(
+ """
+
+ static_assert(IsBaseOf<nsISupports, ${nativeType}>::value,
+ "HasInstance only works for nsISupports-based classes.");
+
+ bool ok = InterfaceHasInstance(cx, argc, vp);
+ if (!ok || args.rval().toBoolean()) {
+ return ok;
+ }
+
+ // FIXME Limit this to chrome by checking xpc::AccessCheck::isChrome(obj).
+ nsCOMPtr<nsISupports> native =
+ xpc::UnwrapReflectorToISupports(js::UncheckedUnwrap(instance, /* stopAtWindowProxy = */ false));
+ nsCOMPtr<nsIDOM${name}> qiResult = do_QueryInterface(native);
+ args.rval().setBoolean(!!qiResult);
+ return true;
+ """,
+ nativeType=self.descriptor.nativeType,
+ name=self.descriptor.interface.identifier.name))
+
+ hasInstanceCode = dedent("""
+
+ const DOMJSClass* domClass = GetDOMClass(js::UncheckedUnwrap(instance, /* stopAtWindowProxy = */ false));
+ if (!domClass) {
+ // Not a DOM object, so certainly not an instance of this interface
+ args.rval().setBoolean(false);
+ return true;
+ }
+ """)
+ if self.descriptor.interface.identifier.name == "ChromeWindow":
+ setRval = "args.rval().setBoolean(UnwrapDOMObject<nsGlobalWindow>(js::UncheckedUnwrap(instance, /* stopAtWindowProxy = */ false))->IsChromeWindow())"
+ else:
+ setRval = "args.rval().setBoolean(true)"
+ # Sort interaces implementing self by name so we get stable output.
+ for iface in sorted(self.descriptor.interface.interfacesImplementingSelf,
+ key=lambda iface: iface.identifier.name):
+ hasInstanceCode += fill(
+ """
+
+ if (domClass->mInterfaceChain[PrototypeTraits<prototypes::id::${name}>::Depth] == prototypes::id::${name}) {
+ ${setRval};
+ return true;
+ }
+ """,
+ name=iface.identifier.name,
+ setRval=setRval)
+ hasInstanceCode += ("args.rval().setBoolean(false);\n"
+ "return true;\n")
+ return header + hasInstanceCode
+
+
+def isChromeOnly(m):
+ return m.getExtendedAttribute("ChromeOnly")
+
+
+class MemberCondition:
+ """
+ An object representing the condition for a member to actually be
+ exposed. Any of the arguments can be None. If not
+ None, they should have the following types:
+
+ pref: The name of the preference.
+ func: The name of the function.
+ secureContext: A bool indicating whether a secure context is required.
+ nonExposedGlobals: A set of names of globals. Can be empty, in which case
+ it's treated the same way as None.
+ """
+ def __init__(self, pref=None, func=None, secureContext=False,
+ nonExposedGlobals=None):
+ assert pref is None or isinstance(pref, str)
+ assert func is None or isinstance(func, str)
+ assert isinstance(secureContext, bool)
+ assert nonExposedGlobals is None or isinstance(nonExposedGlobals, set)
+ self.pref = pref
+ self.secureContext = secureContext
+
+ def toFuncPtr(val):
+ if val is None:
+ return "nullptr"
+ return "&" + val
+ self.func = toFuncPtr(func)
+
+ if nonExposedGlobals:
+ # Nonempty set
+ self.nonExposedGlobals = " | ".join(
+ map(lambda g: "GlobalNames::%s" % g,
+ sorted(nonExposedGlobals)))
+ else:
+ self.nonExposedGlobals = "0"
+
+ def __eq__(self, other):
+ return (self.pref == other.pref and self.func == other.func and
+ self.secureContext == other.secureContext and
+ self.nonExposedGlobals == other.nonExposedGlobals)
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def hasDisablers(self):
+ return (self.pref is not None or
+ self.secureContext or
+ self.func != "nullptr" or
+ self.nonExposedGlobals != "0")
+
+
+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 only exposed to chrome and self.regular to the list of things exposed
+ to both chrome and web pages.
+ """
+ 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) > 0
+
+ def hasNonChromeOnly(self):
+ return len(self.regular) > 0
+
+ def variableName(self, chrome):
+ if chrome:
+ if self.hasChromeOnly():
+ return "sChrome" + self.name
+ else:
+ if self.hasNonChromeOnly():
+ return "s" + self.name
+ return "nullptr"
+
+ def usedForXrays(self):
+ return self.descriptor.wantsXrays
+
+ 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())
+ if self.hasChromeOnly():
+ str += self.generateArray(self.chrome, self.variableName(True),
+ self.usedForXrays())
+ return str
+
+ @staticmethod
+ def getStringAttr(member, name):
+ attr = member.getExtendedAttribute(name)
+ if attr is None:
+ return None
+ # It's a list of strings
+ assert len(attr) == 1
+ assert attr[0] is not None
+ return attr[0]
+
+ @staticmethod
+ def getControllingCondition(interfaceMember, descriptor):
+ interface = descriptor.interface
+ nonExposureSet = interface.exposureSet - interfaceMember.exposureSet
+
+ return MemberCondition(
+ PropertyDefiner.getStringAttr(interfaceMember,
+ "Pref"),
+ PropertyDefiner.getStringAttr(interfaceMember,
+ "Func"),
+ interfaceMember.getExtendedAttribute("SecureContext") is not None,
+ nonExposureSet)
+
+ def generatePrefableArray(self, array, name, specFormatter, specTerminator,
+ specType, getCondition, 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
+
+ specFormatter is a function that takes a single argument, a tuple,
+ and returns a string, a spec array entry
+
+ 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
+
+ getCondition is a callback function that takes an array entry and
+ returns the corresponding MemberCondition.
+
+ getDataTuple is a callback function that takes an array entry and
+ returns a tuple suitable to be passed to specFormatter.
+ """
+
+ # 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) != 0
+ # So we won't put a specTerminator at the very front of the list:
+ lastCondition = getCondition(array[0], self.descriptor)
+
+ specs = []
+ disablers = []
+ prefableSpecs = []
+
+ disablersTemplate = dedent(
+ """
+ static PrefableDisablers %s_disablers%d = {
+ true, %s, %s, %s
+ };
+ """)
+ prefableWithDisablersTemplate = ' { &%s_disablers%d, &%s_specs[%d] }'
+ prefableWithoutDisablersTemplate = ' { nullptr, &%s_specs[%d] }'
+ prefCacheTemplate = '&%s[%d].disablers->enabled'
+
+ def switchToCondition(props, condition):
+ # Remember the info about where our pref-controlled
+ # booleans live.
+ if condition.pref is not None:
+ props.prefCacheData.append(
+ (condition.pref,
+ prefCacheTemplate % (name, len(prefableSpecs))))
+ # Set up pointers to the new sets of specs inside prefableSpecs
+ if condition.hasDisablers():
+ prefableSpecs.append(prefableWithDisablersTemplate %
+ (name, len(specs), name, len(specs)))
+ disablers.append(disablersTemplate %
+ (name, len(specs),
+ toStringBool(condition.secureContext),
+ condition.nonExposedGlobals,
+ condition.func))
+ else:
+ prefableSpecs.append(prefableWithoutDisablersTemplate %
+ (name, len(specs)))
+
+ switchToCondition(self, lastCondition)
+
+ for member in array:
+ curCondition = getCondition(member, self.descriptor)
+ if lastCondition != curCondition:
+ # Terminate previous list
+ specs.append(specTerminator)
+ # And switch to our new condition
+ switchToCondition(self, curCondition)
+ lastCondition = curCondition
+ # And the actual spec
+ specs.append(specFormatter(getDataTuple(member)))
+ specs.append(specTerminator)
+ prefableSpecs.append(" { nullptr, nullptr }")
+
+ specType = "const " + specType
+ arrays = fill(
+ """
+ static ${specType} ${name}_specs[] = {
+ ${specs}
+ };
+
+ ${disablers}
+ // Can't be const because the pref-enabled boolean needs to be writable
+ static Prefable<${specType}> ${name}[] = {
+ ${prefableSpecs}
+ };
+
+ """,
+ specType=specType,
+ name=name,
+ disablers='\n'.join(disablers),
+ specs=',\n'.join(specs),
+ prefableSpecs=',\n'.join(prefableSpecs))
+ if doIdArrays:
+ arrays += "static jsid %s_ids[%i];\n\n" % (name, len(specs))
+ return arrays
+
+
+# The length of a method is the minimum of the lengths of the
+# argument lists of all its overloads.
+def overloadLength(arguments):
+ i = len(arguments)
+ while i > 0 and arguments[i - 1].optional:
+ i -= 1
+ return i
+
+
+def methodLength(method):
+ signatures = method.signatures()
+ return min(overloadLength(arguments) for retType, arguments in signatures)
+
+
+def clearableCachedAttrs(descriptor):
+ return (m for m in descriptor.interface.members if
+ m.isAttr() and
+ # Constants should never need clearing!
+ m.dependsOn != "Nothing" and
+ m.slotIndices is not None)
+
+
+def MakeClearCachedValueNativeName(member):
+ return "ClearCached%sValue" % MakeNativeName(member.identifier.name)
+
+
+def MakeJSImplClearCachedValueNativeName(member):
+ return "_" + MakeClearCachedValueNativeName(member)
+
+
+def IDLToCIdentifier(name):
+ return name.replace("-", "_")
+
+
+class MethodDefiner(PropertyDefiner):
+ """
+ A class for defining methods on a prototype object.
+ """
+ def __init__(self, descriptor, name, static, unforgeable=False):
+ assert not (static and unforgeable)
+ 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 __
+
+ # Ignore non-static methods for interfaces without a proto object
+ if descriptor.interface.hasInterfacePrototypeObject() or static:
+ methods = [m for m in descriptor.interface.members if
+ m.isMethod() and m.isStatic() == static and
+ MemberIsUnforgeable(m, descriptor) == unforgeable and
+ not m.isIdentifierLess()]
+ else:
+ methods = []
+ self.chrome = []
+ self.regular = []
+ for m in methods:
+ if m.identifier.name == 'queryInterface':
+ if m.isStatic():
+ raise TypeError("Legacy queryInterface member shouldn't be static")
+ signatures = m.signatures()
+
+ def argTypeIsIID(arg):
+ return arg.type.inner.isExternal() and arg.type.inner.identifier.name == 'IID'
+ if len(signatures) > 1 or len(signatures[0][1]) > 1 or not argTypeIsIID(signatures[0][1][0]):
+ raise TypeError("There should be only one queryInterface method with 1 argument of type IID")
+
+ # Make sure to not stick QueryInterface on abstract interfaces that
+ # have hasXPConnectImpls (like EventTarget). So only put it on
+ # interfaces that are concrete and all of whose ancestors are abstract.
+ def allAncestorsAbstract(iface):
+ if not iface.parent:
+ return True
+ desc = self.descriptor.getDescriptor(iface.parent.identifier.name)
+ if desc.concrete:
+ return False
+ return allAncestorsAbstract(iface.parent)
+ if (not self.descriptor.interface.hasInterfacePrototypeObject() or
+ not self.descriptor.concrete or
+ not allAncestorsAbstract(self.descriptor.interface)):
+ raise TypeError("QueryInterface is only supported on "
+ "interfaces that are concrete and all "
+ "of whose ancestors are abstract: " +
+ self.descriptor.name)
+ condition = "WantsQueryInterface<%s>::Enabled" % descriptor.nativeType
+ self.regular.append({
+ "name": 'QueryInterface',
+ "methodInfo": False,
+ "length": 1,
+ "flags": "0",
+ "condition": MemberCondition(func=condition)
+ })
+ continue
+
+ # Iterable methods should be enumerable, maplike/setlike methods
+ # should not.
+ isMaplikeOrSetlikeMethod = (m.isMaplikeOrSetlikeOrIterableMethod() and
+ (m.maplikeOrSetlikeOrIterable.isMaplike() or
+ m.maplikeOrSetlikeOrIterable.isSetlike()))
+ method = {
+ "name": m.identifier.name,
+ "methodInfo": not m.isStatic(),
+ "length": methodLength(m),
+ # Methods generated for a maplike/setlike declaration are not
+ # enumerable.
+ "flags": "JSPROP_ENUMERATE" if not isMaplikeOrSetlikeMethod else "0",
+ "condition": PropertyDefiner.getControllingCondition(m, descriptor),
+ "allowCrossOriginThis": m.getExtendedAttribute("CrossOriginCallable"),
+ "returnsPromise": m.returnsPromise(),
+ "hasIteratorAlias": "@@iterator" in m.aliases
+ }
+
+ if m.isStatic():
+ method["nativeName"] = CppKeywords.checkMethodName(IDLToCIdentifier(m.identifier.name))
+
+ if isChromeOnly(m):
+ self.chrome.append(method)
+ else:
+ self.regular.append(method)
+
+ # TODO: Once iterable is implemented, use tiebreak rules instead of
+ # failing. Also, may be more tiebreak rules to implement once spec bug
+ # is resolved.
+ # https://www.w3.org/Bugs/Public/show_bug.cgi?id=28592
+ def hasIterator(methods, regular):
+ return (any("@@iterator" in m.aliases for m in methods) or
+ any("@@iterator" == r["name"] for r in regular))
+
+ # Check whether we need to output an @@iterator due to having an indexed
+ # getter. We only do this while outputting non-static and
+ # non-unforgeable methods, since the @@iterator function will be
+ # neither.
+ if (not static and
+ not unforgeable and
+ descriptor.supportsIndexedProperties()):
+ if hasIterator(methods, self.regular):
+ raise TypeError("Cannot have indexed getter/attr on "
+ "interface %s with other members "
+ "that generate @@iterator, such as "
+ "maplike/setlike or aliased functions." %
+ self.descriptor.interface.identifier.name)
+ self.regular.append({
+ "name": "@@iterator",
+ "methodInfo": False,
+ "selfHostedName": "ArrayValues",
+ "length": 0,
+ "flags": "JSPROP_ENUMERATE",
+ "condition": MemberCondition()
+ })
+
+ if (static and
+ not unforgeable and
+ descriptor.interface.hasInterfaceObject() and
+ NeedsGeneratedHasInstance(descriptor)):
+ self.regular.append({
+ "name": "@@hasInstance",
+ "methodInfo": False,
+ "nativeName": HASINSTANCE_HOOK_NAME,
+ "length": 1,
+ # Flags match those of Function[Symbol.hasInstance]
+ "flags": "JSPROP_READONLY | JSPROP_PERMANENT",
+ "condition": MemberCondition()
+ })
+
+ # Generate the keys/values/entries aliases for value iterables.
+ maplikeOrSetlikeOrIterable = descriptor.interface.maplikeOrSetlikeOrIterable
+ if (not static and
+ not unforgeable and
+ maplikeOrSetlikeOrIterable and
+ maplikeOrSetlikeOrIterable.isIterable() and
+ maplikeOrSetlikeOrIterable.isValueIterator()):
+ # Add our keys/values/entries/forEach
+ self.regular.append({
+ "name": "keys",
+ "methodInfo": False,
+ "selfHostedName": "ArrayKeys",
+ "length": 0,
+ "flags": "JSPROP_ENUMERATE",
+ "condition": PropertyDefiner.getControllingCondition(m,
+ descriptor)
+ })
+ self.regular.append({
+ "name": "values",
+ "methodInfo": False,
+ "selfHostedName": "ArrayValues",
+ "length": 0,
+ "flags": "JSPROP_ENUMERATE",
+ "condition": PropertyDefiner.getControllingCondition(m,
+ descriptor)
+ })
+ self.regular.append({
+ "name": "entries",
+ "methodInfo": False,
+ "selfHostedName": "ArrayEntries",
+ "length": 0,
+ "flags": "JSPROP_ENUMERATE",
+ "condition": PropertyDefiner.getControllingCondition(m,
+ descriptor)
+ })
+ self.regular.append({
+ "name": "forEach",
+ "methodInfo": False,
+ "selfHostedName": "ArrayForEach",
+ "length": 1,
+ "flags": "JSPROP_ENUMERATE",
+ "condition": PropertyDefiner.getControllingCondition(m,
+ descriptor)
+ })
+
+ if not static:
+ stringifier = descriptor.operations['Stringifier']
+ if (stringifier and
+ unforgeable == MemberIsUnforgeable(stringifier, descriptor)):
+ toStringDesc = {
+ "name": "toString",
+ "nativeName": stringifier.identifier.name,
+ "length": 0,
+ "flags": "JSPROP_ENUMERATE",
+ "condition": PropertyDefiner.getControllingCondition(stringifier, descriptor)
+ }
+ if isChromeOnly(stringifier):
+ self.chrome.append(toStringDesc)
+ else:
+ self.regular.append(toStringDesc)
+ jsonifier = descriptor.operations['Jsonifier']
+ if (jsonifier and
+ unforgeable == MemberIsUnforgeable(jsonifier, descriptor)):
+ toJSONDesc = {
+ "name": "toJSON",
+ "nativeName": jsonifier.identifier.name,
+ "length": 0,
+ "flags": "JSPROP_ENUMERATE",
+ "condition": PropertyDefiner.getControllingCondition(jsonifier, descriptor)
+ }
+ if isChromeOnly(jsonifier):
+ self.chrome.append(toJSONDesc)
+ else:
+ self.regular.append(toJSONDesc)
+ if (unforgeable and
+ descriptor.interface.getExtendedAttribute("Unforgeable")):
+ # Synthesize our valueOf method
+ self.regular.append({
+ "name": 'valueOf',
+ "nativeName": "UnforgeableValueOf",
+ "methodInfo": False,
+ "length": 0,
+ "flags": "JSPROP_ENUMERATE", # readonly/permanent added
+ # automatically.
+ "condition": MemberCondition()
+ })
+
+ if descriptor.interface.isJSImplemented():
+ if static:
+ if descriptor.interface.hasInterfaceObject():
+ self.chrome.append({
+ "name": '_create',
+ "nativeName": ("%s::_Create" % descriptor.name),
+ "methodInfo": False,
+ "length": 2,
+ "flags": "0",
+ "condition": MemberCondition()
+ })
+ else:
+ for m in clearableCachedAttrs(descriptor):
+ attrName = MakeNativeName(m.identifier.name)
+ self.chrome.append({
+ "name": "_clearCached%sValue" % attrName,
+ "nativeName": MakeJSImplClearCachedValueNativeName(m),
+ "methodInfo": False,
+ "length": "0",
+ "flags": "0",
+ "condition": MemberCondition()
+ })
+
+ self.unforgeable = unforgeable
+
+ 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 condition(m, d):
+ return m["condition"]
+
+ def flags(m):
+ unforgeable = " | JSPROP_PERMANENT | JSPROP_READONLY" if self.unforgeable else ""
+ return m["flags"] + unforgeable
+
+ def specData(m):
+ if "selfHostedName" in m:
+ selfHostedName = '"%s"' % m["selfHostedName"]
+ assert not m.get("methodInfo", True)
+ accessor = "nullptr"
+ jitinfo = "nullptr"
+ else:
+ selfHostedName = "nullptr"
+ # When defining symbols, function name may not match symbol name
+ methodName = m.get("methodName", m["name"])
+ accessor = m.get("nativeName", IDLToCIdentifier(methodName))
+ if m.get("methodInfo", True):
+ # Cast this in case the methodInfo is a
+ # JSTypedMethodJitInfo.
+ jitinfo = ("reinterpret_cast<const JSJitInfo*>(&%s_methodinfo)" % accessor)
+ if m.get("allowCrossOriginThis", False):
+ if m.get("returnsPromise", False):
+ raise TypeError("%s returns a Promise but should "
+ "be allowed cross-origin?" %
+ accessor)
+ accessor = "genericCrossOriginMethod"
+ elif self.descriptor.needsSpecialGenericOps():
+ if m.get("returnsPromise", False):
+ accessor = "genericPromiseReturningMethod"
+ else:
+ accessor = "genericMethod"
+ elif m.get("returnsPromise", False):
+ accessor = "GenericPromiseReturningBindingMethod"
+ else:
+ accessor = "GenericBindingMethod"
+ else:
+ if m.get("returnsPromise", False):
+ jitinfo = "&%s_methodinfo" % accessor
+ accessor = "StaticMethodPromiseWrapper"
+ else:
+ jitinfo = "nullptr"
+
+ return (m["name"], accessor, jitinfo, m["length"], flags(m), selfHostedName)
+
+ def formatSpec(fields):
+ if fields[0].startswith("@@"):
+ fields = (fields[0][2:],) + fields[1:]
+ return ' JS_SYM_FNSPEC(%s, %s, %s, %s, %s, %s)' % fields
+ return ' JS_FNSPEC("%s", %s, %s, %s, %s, %s)' % fields
+
+ return self.generatePrefableArray(
+ array, name,
+ formatSpec,
+ ' JS_FS_END',
+ 'JSFunctionSpec',
+ condition, specData, doIdArrays)
+
+
+def IsCrossOriginWritable(attr, descriptor):
+ """
+ Return whether the IDLAttribute in question is cross-origin writable on the
+ interface represented by descriptor. This is needed to handle the fact that
+ some, but not all, interfaces implementing URLUtils want a cross-origin
+ writable .href.
+ """
+ crossOriginWritable = attr.getExtendedAttribute("CrossOriginWritable")
+ if not crossOriginWritable:
+ return False
+ if crossOriginWritable is True:
+ return True
+ assert (isinstance(crossOriginWritable, list) and
+ len(crossOriginWritable) == 1)
+ return crossOriginWritable[0] == descriptor.interface.identifier.name
+
+def isNonExposedNavigatorObjectGetter(attr, descriptor):
+ return (attr.navigatorObjectGetter and
+ not descriptor.getDescriptor(attr.type.inner.identifier.name).register)
+
+class AttrDefiner(PropertyDefiner):
+ def __init__(self, descriptor, name, static, unforgeable=False):
+ assert not (static and unforgeable)
+ PropertyDefiner.__init__(self, descriptor, name)
+ self.name = name
+ # Ignore non-static attributes for interfaces without a proto object
+ if descriptor.interface.hasInterfacePrototypeObject() or static:
+ attributes = [m for m in descriptor.interface.members if
+ m.isAttr() and m.isStatic() == static and
+ MemberIsUnforgeable(m, descriptor) == unforgeable and
+ not isNonExposedNavigatorObjectGetter(m, descriptor)]
+ else:
+ attributes = []
+ self.chrome = [m for m in attributes if isChromeOnly(m)]
+ self.regular = [m for m in attributes if not isChromeOnly(m)]
+ self.static = static
+ self.unforgeable = unforgeable
+
+ if static:
+ if not descriptor.interface.hasInterfaceObject():
+ # static attributes go on the interface object
+ assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
+ else:
+ if not descriptor.interface.hasInterfacePrototypeObject():
+ # non-static attributes 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 flags(attr):
+ unforgeable = " | JSPROP_PERMANENT" if self.unforgeable else ""
+ # Attributes generated as part of a maplike/setlike declaration are
+ # not enumerable.
+ enumerable = " | JSPROP_ENUMERATE" if not attr.isMaplikeOrSetlikeAttr() else ""
+ return ("JSPROP_SHARED" + enumerable + unforgeable)
+
+ def getter(attr):
+ if self.static:
+ accessor = 'get_' + IDLToCIdentifier(attr.identifier.name)
+ jitinfo = "nullptr"
+ else:
+ if attr.hasLenientThis():
+ accessor = "genericLenientGetter"
+ elif attr.getExtendedAttribute("CrossOriginReadable"):
+ accessor = "genericCrossOriginGetter"
+ elif self.descriptor.needsSpecialGenericOps():
+ accessor = "genericGetter"
+ else:
+ accessor = "GenericBindingGetter"
+ jitinfo = ("&%s_getterinfo" %
+ IDLToCIdentifier(attr.identifier.name))
+ return "{ { %s, %s } }" % \
+ (accessor, jitinfo)
+
+ def setter(attr):
+ if (attr.readonly and
+ attr.getExtendedAttribute("PutForwards") is None and
+ attr.getExtendedAttribute("Replaceable") is None and
+ attr.getExtendedAttribute("LenientSetter") is None):
+ return "JSNATIVE_WRAPPER(nullptr)"
+ if self.static:
+ accessor = 'set_' + IDLToCIdentifier(attr.identifier.name)
+ jitinfo = "nullptr"
+ else:
+ if attr.hasLenientThis():
+ accessor = "genericLenientSetter"
+ elif IsCrossOriginWritable(attr, self.descriptor):
+ accessor = "genericCrossOriginSetter"
+ elif self.descriptor.needsSpecialGenericOps():
+ accessor = "genericSetter"
+ else:
+ accessor = "GenericBindingSetter"
+ jitinfo = "&%s_setterinfo" % IDLToCIdentifier(attr.identifier.name)
+ return "{ { %s, %s } }" % \
+ (accessor, jitinfo)
+
+ def specData(attr):
+ return (attr.identifier.name, flags(attr), getter(attr),
+ setter(attr))
+
+ return self.generatePrefableArray(
+ array, name,
+ lambda fields: ' { "%s", %s, { { %s, %s } } }' % fields,
+ ' JS_PS_END',
+ 'JSPropertySpec',
+ PropertyDefiner.getControllingCondition, 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
+ constants = [m for m in descriptor.interface.members if m.isConst()]
+ self.chrome = [m for m in constants if isChromeOnly(m)]
+ self.regular = [m for m in constants 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,
+ lambda fields: ' { "%s", %s }' % fields,
+ ' { 0, JS::UndefinedValue() }',
+ 'ConstantSpec',
+ PropertyDefiner.getControllingCondition, specData, doIdArrays)
+
+
+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.unforgeableMethods = MethodDefiner(descriptor, "UnforgeableMethods",
+ static=False, unforgeable=True)
+ self.unforgeableAttrs = AttrDefiner(descriptor, "UnforgeableAttributes",
+ static=False, unforgeable=True)
+ self.consts = ConstDefiner(descriptor, "Constants")
+
+ @staticmethod
+ def arrayNames():
+ return ["staticMethods", "staticAttrs", "methods", "attrs",
+ "unforgeableMethods", "unforgeableAttrs", "consts"]
+
+ def hasChromeOnly(self):
+ return any(getattr(self, a).hasChromeOnly() for a in self.arrayNames())
+
+ def hasNonChromeOnly(self):
+ return any(getattr(self, a).hasNonChromeOnly() for a in self.arrayNames())
+
+ def __str__(self):
+ define = ""
+ for array in self.arrayNames():
+ define += str(getattr(self, array))
+ return define
+
+
+class CGNativeProperties(CGList):
+ def __init__(self, descriptor, properties):
+ def generateNativeProperties(name, chrome):
+ def check(p):
+ return p.hasChromeOnly() if chrome else p.hasNonChromeOnly()
+
+ nativePropsInts = []
+ nativePropsTrios = []
+
+ iteratorAliasIndex = -1
+ for index, item in enumerate(properties.methods.regular):
+ if item.get("hasIteratorAlias"):
+ iteratorAliasIndex = index
+ break
+ nativePropsInts.append(CGGeneric(str(iteratorAliasIndex)))
+
+ offset = 0
+ for array in properties.arrayNames():
+ propertyArray = getattr(properties, array)
+ if check(propertyArray):
+ varName = propertyArray.variableName(chrome)
+ bitfields = "true, %d /* %s */" % (offset, varName)
+ offset += 1
+ nativePropsInts.append(CGGeneric(bitfields))
+
+ if propertyArray.usedForXrays():
+ ids = "%(name)s_ids"
+ else:
+ ids = "nullptr"
+ trio = "{ %(name)s, " + ids + ", %(name)s_specs }"
+ trio = trio % {'name': varName}
+ nativePropsTrios.append(CGGeneric(trio))
+ else:
+ bitfields = "false, 0"
+ nativePropsInts.append(CGGeneric(bitfields))
+
+ nativePropsTrios = \
+ [CGWrapper(CGIndenter(CGList(nativePropsTrios, ",\n")),
+ pre='{\n', post='\n}')]
+ nativeProps = nativePropsInts + nativePropsTrios
+ pre = ("static const NativePropertiesN<%d> %s = {\n" %
+ (offset, name))
+ return CGWrapper(CGIndenter(CGList(nativeProps, ",\n")),
+ pre=pre, post="\n};\n")
+
+ nativeProperties = []
+ if properties.hasNonChromeOnly():
+ nativeProperties.append(
+ generateNativeProperties("sNativeProperties", False))
+ if properties.hasChromeOnly():
+ nativeProperties.append(
+ generateNativeProperties("sChromeOnlyNativeProperties", True))
+
+ CGList.__init__(self, nativeProperties, "\n")
+
+ def declare(self):
+ return ""
+
+ def define(self):
+ return CGList.define(self)
+
+
+class CGJsonifyAttributesMethod(CGAbstractMethod):
+ """
+ Generate the JsonifyAttributes method for an interface descriptor
+ """
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'aCx'),
+ Argument('JS::Handle<JSObject*>', 'obj'),
+ Argument('%s*' % descriptor.nativeType, 'self'),
+ Argument('JS::Rooted<JSObject*>&', 'aResult')]
+ CGAbstractMethod.__init__(self, descriptor, 'JsonifyAttributes', 'bool', args)
+
+ def definition_body(self):
+ ret = ''
+ interface = self.descriptor.interface
+ for m in interface.members:
+ if m.isAttr() and not m.isStatic() and m.type.isSerializable():
+ ret += fill(
+ """
+ { // scope for "temp"
+ JS::Rooted<JS::Value> temp(aCx);
+ if (!get_${name}(aCx, obj, self, JSJitGetterCallArgs(&temp))) {
+ return false;
+ }
+ if (!JS_DefineProperty(aCx, aResult, "${name}", temp, JSPROP_ENUMERATE)) {
+ return false;
+ }
+ }
+ """,
+ name=IDLToCIdentifier(m.identifier.name))
+ ret += 'return true;\n'
+ return ret
+
+
+class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
+ """
+ Generate the CreateInterfaceObjects method for an interface descriptor.
+
+ properties should be a PropertyArrays instance.
+ """
+ def __init__(self, descriptor, properties, haveUnscopables):
+ args = [Argument('JSContext*', 'aCx'),
+ Argument('JS::Handle<JSObject*>', 'aGlobal'),
+ Argument('ProtoAndIfaceCache&', 'aProtoAndIfaceCache'),
+ Argument('bool', 'aDefineOnGlobal')]
+ CGAbstractMethod.__init__(self, descriptor, 'CreateInterfaceObjects', 'void', args)
+ self.properties = properties
+ self.haveUnscopables = haveUnscopables
+
+ def definition_body(self):
+ (protoGetter, protoHandleGetter) = InterfacePrototypeObjectProtoGetter(self.descriptor)
+ if protoHandleGetter is None:
+ parentProtoType = "Rooted"
+ getParentProto = "aCx, " + protoGetter
+ else:
+ parentProtoType = "Handle"
+ getParentProto = protoHandleGetter
+ getParentProto = getParentProto + "(aCx)"
+
+ (protoGetter, protoHandleGetter) = InterfaceObjectProtoGetter(self.descriptor)
+ if protoHandleGetter is None:
+ getConstructorProto = "aCx, " + protoGetter
+ constructorProtoType = "Rooted"
+ else:
+ getConstructorProto = protoHandleGetter
+ constructorProtoType = "Handle"
+ getConstructorProto += "(aCx)"
+
+ 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
+
+ getParentProto = fill(
+ """
+ JS::${type}<JSObject*> parentProto(${getParentProto});
+ if (!parentProto) {
+ return;
+ }
+ """,
+ type=parentProtoType,
+ getParentProto=getParentProto)
+
+ getConstructorProto = fill(
+ """
+ JS::${type}<JSObject*> constructorProto(${getConstructorProto});
+ if (!constructorProto) {
+ return;
+ }
+ """,
+ type=constructorProtoType,
+ getConstructorProto=getConstructorProto)
+
+ idsToInit = []
+ # There is no need to init any IDs in bindings that don't want Xrays.
+ if self.descriptor.wantsXrays:
+ for var in self.properties.arrayNames():
+ 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))
+ if props.hasNonChromeOnly():
+ idsToInit.append(props.variableName(False))
+ if len(idsToInit) > 0:
+ initIdCalls = ["!InitIds(aCx, %s, %s_ids)" % (varname, varname)
+ for varname in idsToInit]
+ idsInitedFlag = CGGeneric("static bool sIdsInited = false;\n")
+ setFlag = CGGeneric("sIdsInited = true;\n")
+ initIdConditionals = [CGIfWrapper(CGGeneric("return;\n"), call)
+ for call in initIdCalls]
+ initIds = CGList([idsInitedFlag,
+ CGIfWrapper(CGList(initIdConditionals + [setFlag]),
+ "!sIdsInited && NS_IsMainThread()")])
+ else:
+ initIds = None
+
+ prefCacheData = []
+ for var in self.properties.arrayNames():
+ props = getattr(self.properties, var)
+ prefCacheData.extend(props.prefCacheData)
+ if len(prefCacheData) != 0:
+ prefCacheData = [
+ CGGeneric('Preferences::AddBoolVarCache(%s, "%s");\n' % (ptr, pref))
+ for pref, ptr in prefCacheData]
+ prefCache = CGWrapper(CGIndenter(CGList(prefCacheData)),
+ pre=("static bool sPrefCachesInited = false;\n"
+ "if (!sPrefCachesInited && NS_IsMainThread()) {\n"
+ " sPrefCachesInited = true;\n"),
+ post="}\n")
+ else:
+ prefCache = None
+
+ if self.descriptor.interface.ctor():
+ constructArgs = methodLength(self.descriptor.interface.ctor())
+ else:
+ constructArgs = 0
+ if len(self.descriptor.interface.namedConstructors) > 0:
+ namedConstructors = "namedConstructors"
+ else:
+ namedConstructors = "nullptr"
+
+ if needInterfacePrototypeObject:
+ protoClass = "&sPrototypeClass.mBase"
+ protoCache = "&aProtoAndIfaceCache.EntrySlotOrCreate(prototypes::id::%s)" % self.descriptor.name
+ parentProto = "parentProto"
+ getParentProto = CGGeneric(getParentProto)
+ else:
+ protoClass = "nullptr"
+ protoCache = "nullptr"
+ parentProto = "nullptr"
+ getParentProto = None
+
+ if needInterfaceObject:
+ interfaceClass = "&sInterfaceObjectClass.mBase"
+ interfaceCache = "&aProtoAndIfaceCache.EntrySlotOrCreate(constructors::id::%s)" % self.descriptor.name
+ getConstructorProto = CGGeneric(getConstructorProto)
+ constructorProto = "constructorProto"
+ else:
+ # We don't have slots to store the named constructors.
+ assert len(self.descriptor.interface.namedConstructors) == 0
+ interfaceClass = "nullptr"
+ interfaceCache = "nullptr"
+ getConstructorProto = None
+ constructorProto = "nullptr"
+
+ isGlobal = self.descriptor.isGlobal() is not None
+ if self.properties.hasNonChromeOnly():
+ properties = "sNativeProperties.Upcast()"
+ else:
+ properties = "nullptr"
+ if self.properties.hasChromeOnly():
+ chromeProperties = "nsContentUtils::ThreadsafeIsCallerChrome() ? sChromeOnlyNativeProperties.Upcast() : nullptr"
+ else:
+ chromeProperties = "nullptr"
+
+ call = fill(
+ """
+ JS::Heap<JSObject*>* protoCache = ${protoCache};
+ JS::Heap<JSObject*>* interfaceCache = ${interfaceCache};
+ dom::CreateInterfaceObjects(aCx, aGlobal, ${parentProto},
+ ${protoClass}, protoCache,
+ ${constructorProto}, ${interfaceClass}, ${constructArgs}, ${namedConstructors},
+ interfaceCache,
+ ${properties},
+ ${chromeProperties},
+ ${name}, aDefineOnGlobal,
+ ${unscopableNames},
+ ${isGlobal});
+ """,
+ protoClass=protoClass,
+ parentProto=parentProto,
+ protoCache=protoCache,
+ constructorProto=constructorProto,
+ interfaceClass=interfaceClass,
+ constructArgs=constructArgs,
+ namedConstructors=namedConstructors,
+ interfaceCache=interfaceCache,
+ properties=properties,
+ chromeProperties=chromeProperties,
+ name='"' + self.descriptor.interface.identifier.name + '"' if needInterfaceObject else "nullptr",
+ unscopableNames="unscopableNames" if self.haveUnscopables else "nullptr",
+ isGlobal=toStringBool(isGlobal))
+
+ # If we fail after here, we must clear interface and prototype caches
+ # using this code: intermediate failure must not expose the interface in
+ # partially-constructed state. Note that every case after here needs an
+ # interface prototype object.
+ failureCode = dedent(
+ """
+ *protoCache = nullptr;
+ if (interfaceCache) {
+ *interfaceCache = nullptr;
+ }
+ return;
+ """)
+
+ aliasedMembers = [m for m in self.descriptor.interface.members if m.isMethod() and m.aliases]
+ if aliasedMembers:
+ assert needInterfacePrototypeObject
+
+ def defineAlias(alias):
+ if alias == "@@iterator":
+ symbolJSID = "SYMBOL_TO_JSID(JS::GetWellKnownSymbol(aCx, JS::SymbolCode::iterator))"
+ getSymbolJSID = CGGeneric(fill("JS::Rooted<jsid> iteratorId(aCx, ${symbolJSID});",
+ symbolJSID=symbolJSID))
+ defineFn = "JS_DefinePropertyById"
+ prop = "iteratorId"
+ elif alias.startswith("@@"):
+ raise TypeError("Can't handle any well-known Symbol other than @@iterator")
+ else:
+ getSymbolJSID = None
+ defineFn = "JS_DefineProperty"
+ prop = '"%s"' % alias
+ return CGList([
+ getSymbolJSID,
+ # XXX If we ever create non-enumerable properties that can
+ # be aliased, we should consider making the aliases
+ # match the enumerability of the property being aliased.
+ CGGeneric(fill(
+ """
+ if (!${defineFn}(aCx, proto, ${prop}, aliasedVal, JSPROP_ENUMERATE)) {
+ $*{failureCode}
+ }
+ """,
+ defineFn=defineFn,
+ prop=prop,
+ failureCode=failureCode))
+ ], "\n")
+
+ def defineAliasesFor(m):
+ return CGList([
+ CGGeneric(fill(
+ """
+ if (!JS_GetProperty(aCx, proto, \"${prop}\", &aliasedVal)) {
+ $*{failureCode}
+ }
+ """,
+ failureCode=failureCode,
+ prop=m.identifier.name))
+ ] + [defineAlias(alias) for alias in sorted(m.aliases)])
+
+ defineAliases = CGList([
+ CGGeneric(fill("""
+ // Set up aliases on the interface prototype object we just created.
+ JS::Handle<JSObject*> proto = GetProtoObjectHandle(aCx);
+ if (!proto) {
+ $*{failureCode}
+ }
+
+ """,
+ failureCode=failureCode)),
+ CGGeneric("JS::Rooted<JS::Value> aliasedVal(aCx);\n\n")
+ ] + [defineAliasesFor(m) for m in sorted(aliasedMembers)])
+ else:
+ defineAliases = None
+
+ # Globals handle unforgeables directly in Wrap() instead of
+ # via a holder.
+ if self.descriptor.hasUnforgeableMembers and not self.descriptor.isGlobal():
+ assert needInterfacePrototypeObject
+
+ # We want to use the same JSClass and prototype as the object we'll
+ # end up defining the unforgeable properties on in the end, so that
+ # we can use JS_InitializePropertiesFromCompatibleNativeObject to do
+ # a fast copy. In the case of proxies that's null, because the
+ # expando object is a vanilla object, but in the case of other DOM
+ # objects it's whatever our class is.
+ if self.descriptor.proxy:
+ holderClass = "nullptr"
+ holderProto = "nullptr"
+ else:
+ holderClass = "sClass.ToJSClass()"
+ holderProto = "*protoCache"
+ createUnforgeableHolder = CGGeneric(fill(
+ """
+ JS::Rooted<JSObject*> unforgeableHolder(aCx);
+ {
+ JS::Rooted<JSObject*> holderProto(aCx, ${holderProto});
+ unforgeableHolder = JS_NewObjectWithoutMetadata(aCx, ${holderClass}, holderProto);
+ if (!unforgeableHolder) {
+ $*{failureCode}
+ }
+ }
+ """,
+ holderProto=holderProto,
+ holderClass=holderClass,
+ failureCode=failureCode))
+ defineUnforgeables = InitUnforgeablePropertiesOnHolder(self.descriptor,
+ self.properties,
+ failureCode)
+ createUnforgeableHolder = CGList(
+ [createUnforgeableHolder, defineUnforgeables])
+
+ installUnforgeableHolder = CGGeneric(dedent(
+ """
+ if (*protoCache) {
+ js::SetReservedSlot(*protoCache, DOM_INTERFACE_PROTO_SLOTS_BASE,
+ JS::ObjectValue(*unforgeableHolder));
+ }
+ """))
+
+ unforgeableHolderSetup = CGList(
+ [createUnforgeableHolder, installUnforgeableHolder], "\n")
+ else:
+ unforgeableHolderSetup = None
+
+ if self.descriptor.name == "Promise":
+ speciesSetup = CGGeneric(fill(
+ """
+ #ifndef SPIDERMONKEY_PROMISE
+ JS::Rooted<JSObject*> promiseConstructor(aCx, *interfaceCache);
+ JS::Rooted<jsid> species(aCx,
+ SYMBOL_TO_JSID(JS::GetWellKnownSymbol(aCx, JS::SymbolCode::species)));
+ if (!JS_DefinePropertyById(aCx, promiseConstructor, species, JS::UndefinedHandleValue,
+ JSPROP_SHARED, Promise::PromiseSpecies, nullptr)) {
+ $*{failureCode}
+ }
+ #endif // SPIDERMONKEY_PROMISE
+ """,
+ failureCode=failureCode))
+ else:
+ speciesSetup = None
+
+ if (self.descriptor.interface.isOnGlobalProtoChain() and
+ needInterfacePrototypeObject):
+ makeProtoPrototypeImmutable = CGGeneric(fill(
+ """
+ if (*${protoCache}) {
+ bool succeeded;
+ JS::Handle<JSObject*> prot = GetProtoObjectHandle(aCx);
+ if (!JS_SetImmutablePrototype(aCx, prot, &succeeded)) {
+ $*{failureCode}
+ }
+
+ MOZ_ASSERT(succeeded,
+ "making a fresh prototype object's [[Prototype]] "
+ "immutable can internally fail, but it should "
+ "never be unsuccessful");
+ }
+ """,
+ protoCache=protoCache,
+ failureCode=failureCode))
+ else:
+ makeProtoPrototypeImmutable = None
+
+ return CGList(
+ [getParentProto, getConstructorProto, initIds,
+ prefCache, CGGeneric(call), defineAliases, unforgeableHolderSetup,
+ speciesSetup, makeProtoPrototypeImmutable],
+ "\n").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="", extraArgs=[]):
+ args = [Argument('JSContext*', 'aCx')] + extraArgs
+ CGAbstractMethod.__init__(self, descriptor, name,
+ 'JS::Handle<JSObject*>', args)
+ self.id = idPrefix + "id::" + self.descriptor.name
+
+ def definition_body(self):
+ return fill(
+ """
+ /* Make sure our global is sane. Hopefully we can remove this sometime */
+ JSObject* global = JS::CurrentGlobalOrNull(aCx);
+ if (!(js::GetObjectClass(global)->flags & JSCLASS_DOM_GLOBAL)) {
+ return nullptr;
+ }
+
+ /* Check to see whether the interface objects are already installed */
+ ProtoAndIfaceCache& protoAndIfaceCache = *GetProtoAndIfaceCache(global);
+ if (!protoAndIfaceCache.EntrySlotIfExists(${id})) {
+ JS::Rooted<JSObject*> rootedGlobal(aCx, global);
+ CreateInterfaceObjects(aCx, rootedGlobal, protoAndIfaceCache, aDefineOnGlobal);
+ }
+
+ /*
+ * The object might _still_ be null, but that's OK.
+ *
+ * Calling fromMarkedLocation() is safe because protoAndIfaceCache is
+ * traced by TraceProtoAndIfaceCache() and its contents are never
+ * changed after they have been set.
+ *
+ * Calling address() avoids the read read barrier that does gray
+ * unmarking, but it's not possible for the object to be gray here.
+ */
+
+ const JS::Heap<JSObject*>& entrySlot = protoAndIfaceCache.EntrySlotMustExist(${id});
+ MOZ_ASSERT_IF(entrySlot, !JS::ObjectIsMarkedGray(entrySlot));
+ return JS::Handle<JSObject*>::fromMarkedLocation(entrySlot.address());
+ """,
+ id=self.id)
+
+
+class CGGetProtoObjectHandleMethod(CGGetPerInterfaceObject):
+ """
+ A method for getting the interface prototype object.
+ """
+ def __init__(self, descriptor):
+ CGGetPerInterfaceObject.__init__(self, descriptor, "GetProtoObjectHandle",
+ "prototypes::")
+
+ def definition_body(self):
+ return dedent("""
+ /* Get the interface prototype object for this class. This will create the
+ object as needed. */
+ bool aDefineOnGlobal = true;
+
+ """) + CGGetPerInterfaceObject.definition_body(self)
+
+
+class CGGetProtoObjectMethod(CGAbstractMethod):
+ """
+ A method for getting the interface prototype object.
+ """
+ def __init__(self, descriptor):
+ CGAbstractMethod.__init__(
+ self, descriptor, "GetProtoObject", "JSObject*",
+ [Argument('JSContext*', 'aCx')])
+
+ def definition_body(self):
+ return "return GetProtoObjectHandle(aCx);\n"
+
+
+class CGGetConstructorObjectHandleMethod(CGGetPerInterfaceObject):
+ """
+ A method for getting the interface constructor object.
+ """
+ def __init__(self, descriptor):
+ CGGetPerInterfaceObject.__init__(
+ self, descriptor, "GetConstructorObjectHandle",
+ "constructors::",
+ extraArgs=[Argument("bool", "aDefineOnGlobal", "true")])
+
+ def definition_body(self):
+ return dedent("""
+ /* Get the interface object for this class. This will create the object as
+ needed. */
+
+ """) + CGGetPerInterfaceObject.definition_body(self)
+
+
+class CGGetConstructorObjectMethod(CGAbstractMethod):
+ """
+ A method for getting the interface constructor object.
+ """
+ def __init__(self, descriptor):
+ CGAbstractMethod.__init__(
+ self, descriptor, "GetConstructorObject", "JSObject*",
+ [Argument('JSContext*', 'aCx')])
+
+ def definition_body(self):
+ return "return GetConstructorObjectHandle(aCx);\n"
+
+
+class CGGetNamedPropertiesObjectMethod(CGAbstractStaticMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'aCx')]
+ CGAbstractStaticMethod.__init__(self, descriptor,
+ 'GetNamedPropertiesObject',
+ 'JSObject*', args)
+
+ def definition_body(self):
+ parentProtoName = self.descriptor.parentPrototypeName
+ if parentProtoName is None:
+ getParentProto = ""
+ parentProto = "nullptr"
+ else:
+ getParentProto = fill(
+ """
+ JS::Rooted<JSObject*> parentProto(aCx, ${parent}::GetProtoObjectHandle(aCx));
+ if (!parentProto) {
+ return nullptr;
+ }
+ """,
+ parent=toBindingNamespace(parentProtoName))
+ parentProto = "parentProto"
+ return fill(
+ """
+ /* Make sure our global is sane. Hopefully we can remove this sometime */
+ JSObject* global = JS::CurrentGlobalOrNull(aCx);
+ if (!(js::GetObjectClass(global)->flags & JSCLASS_DOM_GLOBAL)) {
+ return nullptr;
+ }
+
+ /* Check to see whether the named properties object has already been created */
+ ProtoAndIfaceCache& protoAndIfaceCache = *GetProtoAndIfaceCache(global);
+
+ JS::Heap<JSObject*>& namedPropertiesObject = protoAndIfaceCache.EntrySlotOrCreate(namedpropertiesobjects::id::${ifaceName});
+ if (!namedPropertiesObject) {
+ $*{getParentProto}
+ namedPropertiesObject = ${nativeType}::CreateNamedPropertiesObject(aCx, ${parentProto});
+ DebugOnly<const DOMIfaceAndProtoJSClass*> clasp =
+ DOMIfaceAndProtoJSClass::FromJSClass(js::GetObjectClass(namedPropertiesObject));
+ MOZ_ASSERT(clasp->mType == eNamedPropertiesObject,
+ "Expected ${nativeType}::CreateNamedPropertiesObject to return a named properties object");
+ MOZ_ASSERT(clasp->mNativeHooks,
+ "The named properties object for ${nativeType} should have NativePropertyHooks.");
+ MOZ_ASSERT(clasp->mNativeHooks->mResolveOwnProperty,
+ "Don't know how to resolve the properties of the named properties object for ${nativeType}.");
+ MOZ_ASSERT(clasp->mNativeHooks->mEnumerateOwnProperties,
+ "Don't know how to enumerate the properties of the named properties object for ${nativeType}.");
+ }
+ return namedPropertiesObject.get();
+ """,
+ getParentProto=getParentProto,
+ ifaceName=self.descriptor.name,
+ parentProto=parentProto,
+ nativeType=self.descriptor.nativeType)
+
+
+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('JS::Handle<JSObject*>', 'aGlobal'),
+ Argument('JS::Handle<jsid>', 'id'),
+ Argument('bool', 'aDefineOnGlobal')]
+ CGAbstractMethod.__init__(self, descriptor, 'DefineDOMInterface', 'JSObject*', args)
+
+ def definition_body(self):
+ if len(self.descriptor.interface.namedConstructors) > 0:
+ getConstructor = dedent("""
+ JSObject* interfaceObject = GetConstructorObjectHandle(aCx, aDefineOnGlobal);
+ if (!interfaceObject) {
+ return nullptr;
+ }
+ for (unsigned slot = DOM_INTERFACE_SLOTS_BASE; slot < JSCLASS_RESERVED_SLOTS(&sInterfaceObjectClass.mBase); ++slot) {
+ JSObject* constructor = &js::GetReservedSlot(interfaceObject, slot).toObject();
+ if (JS_GetFunctionId(JS_GetObjectFunction(constructor)) == JSID_TO_STRING(id)) {
+ return constructor;
+ }
+ }
+ return interfaceObject;
+ """)
+ else:
+ getConstructor = "return GetConstructorObjectHandle(aCx, aDefineOnGlobal);\n"
+ return getConstructor
+
+
+def getConditionList(idlobj, cxName, objName):
+ """
+ Get the list of conditions for idlobj (to be used in "is this enabled"
+ checks). This will be returned as a CGList with " &&\n" as the separator,
+ for readability.
+
+ objName is the name of the object that we're working with, because some of
+ our test functions want that.
+ """
+ conditions = []
+ pref = idlobj.getExtendedAttribute("Pref")
+ if pref:
+ assert isinstance(pref, list) and len(pref) == 1
+ conditions.append('Preferences::GetBool("%s")' % pref[0])
+ if idlobj.getExtendedAttribute("ChromeOnly"):
+ conditions.append("nsContentUtils::ThreadsafeIsCallerChrome()")
+ func = idlobj.getExtendedAttribute("Func")
+ if func:
+ assert isinstance(func, list) and len(func) == 1
+ conditions.append("%s(%s, %s)" % (func[0], cxName, objName))
+ if idlobj.getExtendedAttribute("SecureContext"):
+ conditions.append("mozilla::dom::IsSecureContextOrObjectIsFromSecureContext(%s, %s)" % (cxName, objName))
+
+ return CGList((CGGeneric(cond) for cond in conditions), " &&\n")
+
+
+class CGConstructorEnabled(CGAbstractMethod):
+ """
+ A method for testing whether we should be exposing this interface
+ object or navigator property. This can perform various tests
+ depending on what conditions are specified on the interface.
+ """
+ def __init__(self, descriptor):
+ CGAbstractMethod.__init__(self, descriptor,
+ 'ConstructorEnabled', 'bool',
+ [Argument("JSContext*", "aCx"),
+ Argument("JS::Handle<JSObject*>", "aObj")])
+
+ def definition_body(self):
+ body = CGList([], "\n")
+
+ iface = self.descriptor.interface
+
+ if not iface.isExposedOnMainThread():
+ exposedInWindowCheck = dedent(
+ """
+ MOZ_ASSERT(!NS_IsMainThread(), "Why did we even get called?");
+ """)
+ body.append(CGGeneric(exposedInWindowCheck))
+
+ if iface.isExposedInSomeButNotAllWorkers():
+ workerGlobals = sorted(iface.getWorkerExposureSet())
+ workerCondition = CGList((CGGeneric('strcmp(name, "%s")' % workerGlobal)
+ for workerGlobal in workerGlobals), " && ")
+ exposedInWorkerCheck = fill(
+ """
+ const char* name = js::GetObjectClass(aObj)->name;
+ if (${workerCondition}) {
+ return false;
+ }
+ """, workerCondition=workerCondition.define())
+ exposedInWorkerCheck = CGGeneric(exposedInWorkerCheck)
+ if iface.isExposedOnMainThread():
+ exposedInWorkerCheck = CGIfWrapper(exposedInWorkerCheck,
+ "!NS_IsMainThread()")
+ body.append(exposedInWorkerCheck)
+
+ conditions = getConditionList(iface, "aCx", "aObj")
+
+ # We should really have some conditions
+ assert len(body) or len(conditions)
+
+ conditionsWrapper = ""
+ if len(conditions):
+ conditionsWrapper = CGWrapper(conditions,
+ pre="return ",
+ post=";\n",
+ reindent=True)
+ else:
+ conditionsWrapper = CGGeneric("return true;\n")
+
+ body.append(conditionsWrapper)
+ return body.define()
+
+
+def CreateBindingJSObject(descriptor, properties):
+ objDecl = "BindingJSObjectCreator<%s> creator(aCx);\n" % descriptor.nativeType
+
+ # We don't always need to root obj, but there are a variety
+ # of cases where we do, so for simplicity, just always root it.
+ if descriptor.proxy:
+ create = dedent(
+ """
+ creator.CreateProxyObject(aCx, &sClass.mBase, DOMProxyHandler::getInstance(),
+ proto, aObject, aReflector);
+ if (!aReflector) {
+ return false;
+ }
+
+ """)
+ if descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
+ create += dedent("""
+ js::SetProxyExtra(aReflector, JSPROXYSLOT_EXPANDO,
+ JS::PrivateValue(&aObject->mExpandoAndGeneration));
+
+ """)
+ else:
+ create = dedent(
+ """
+ creator.CreateObject(aCx, sClass.ToJSClass(), proto, aObject, aReflector);
+ if (!aReflector) {
+ return false;
+ }
+ """)
+ return objDecl + create
+
+
+def InitUnforgeablePropertiesOnHolder(descriptor, properties, failureCode,
+ holderName="unforgeableHolder"):
+ """
+ Define the unforgeable properties on the unforgeable holder for
+ the interface represented by descriptor.
+
+ properties is a PropertyArrays instance.
+
+ """
+ assert (properties.unforgeableAttrs.hasNonChromeOnly() or
+ properties.unforgeableAttrs.hasChromeOnly() or
+ properties.unforgeableMethods.hasNonChromeOnly() or
+ properties.unforgeableMethods.hasChromeOnly())
+
+ unforgeables = []
+
+ defineUnforgeableAttrs = fill(
+ """
+ if (!DefineUnforgeableAttributes(aCx, ${holderName}, %s)) {
+ $*{failureCode}
+ }
+ """,
+ failureCode=failureCode,
+ holderName=holderName)
+ defineUnforgeableMethods = fill(
+ """
+ if (!DefineUnforgeableMethods(aCx, ${holderName}, %s)) {
+ $*{failureCode}
+ }
+ """,
+ failureCode=failureCode,
+ holderName=holderName)
+
+ unforgeableMembers = [
+ (defineUnforgeableAttrs, properties.unforgeableAttrs),
+ (defineUnforgeableMethods, properties.unforgeableMethods)
+ ]
+ for (template, array) in unforgeableMembers:
+ if array.hasNonChromeOnly():
+ unforgeables.append(CGGeneric(template % array.variableName(False)))
+ if array.hasChromeOnly():
+ unforgeables.append(
+ CGIfWrapper(CGGeneric(template % array.variableName(True)),
+ "nsContentUtils::ThreadsafeIsCallerChrome()"))
+
+ if descriptor.interface.getExtendedAttribute("Unforgeable"):
+ # We do our undefined toJSON and toPrimitive here, not as a regular
+ # property because we don't have a concept of value props anywhere in
+ # IDL.
+ unforgeables.append(CGGeneric(fill(
+ """
+ JS::RootedId toPrimitive(aCx,
+ SYMBOL_TO_JSID(JS::GetWellKnownSymbol(aCx, JS::SymbolCode::toPrimitive)));
+ if (!JS_DefinePropertyById(aCx, ${holderName}, toPrimitive,
+ JS::UndefinedHandleValue,
+ JSPROP_READONLY | JSPROP_PERMANENT) ||
+ !JS_DefineProperty(aCx, ${holderName}, "toJSON",
+ JS::UndefinedHandleValue,
+ JSPROP_READONLY | JSPROP_ENUMERATE | JSPROP_PERMANENT)) {
+ $*{failureCode}
+ }
+ """,
+ failureCode=failureCode,
+ holderName=holderName)))
+
+ return CGWrapper(CGList(unforgeables), pre="\n")
+
+
+def CopyUnforgeablePropertiesToInstance(descriptor, failureCode):
+ """
+ Copy the unforgeable properties from the unforgeable holder for
+ this interface to the instance object we have.
+ """
+ assert not descriptor.isGlobal();
+
+ if not descriptor.hasUnforgeableMembers:
+ return ""
+
+ copyCode = [
+ CGGeneric(dedent(
+ """
+ // Important: do unforgeable property setup after we have handed
+ // over ownership of the C++ object to obj as needed, so that if
+ // we fail and it ends up GCed it won't have problems in the
+ // finalizer trying to drop its ownership of the C++ object.
+ """))
+ ]
+
+ # For proxies, we want to define on the expando object, not directly on the
+ # reflector, so we can make sure we don't get confused by named getters.
+ if descriptor.proxy:
+ copyCode.append(CGGeneric(fill(
+ """
+ JS::Rooted<JSObject*> expando(aCx,
+ DOMProxyHandler::EnsureExpandoObject(aCx, aReflector));
+ if (!expando) {
+ $*{failureCode}
+ }
+ """,
+ failureCode=failureCode)))
+ obj = "expando"
+ else:
+ obj = "aReflector"
+
+ copyCode.append(CGGeneric(fill(
+ """
+ JS::Rooted<JSObject*> unforgeableHolder(aCx,
+ &js::GetReservedSlot(canonicalProto, DOM_INTERFACE_PROTO_SLOTS_BASE).toObject());
+ if (!JS_InitializePropertiesFromCompatibleNativeObject(aCx, ${obj}, unforgeableHolder)) {
+ $*{failureCode}
+ }
+ """,
+ obj=obj,
+ failureCode=failureCode)))
+
+ return CGWrapper(CGList(copyCode), pre="\n").define()
+
+
+def AssertInheritanceChain(descriptor):
+ asserts = ""
+ iface = descriptor.interface
+ while iface:
+ desc = descriptor.getDescriptor(iface.identifier.name)
+ asserts += (
+ "MOZ_ASSERT(static_cast<%s*>(aObject) == \n"
+ " reinterpret_cast<%s*>(aObject),\n"
+ " \"Multiple inheritance for %s is broken.\");\n" %
+ (desc.nativeType, desc.nativeType, desc.nativeType))
+ iface = iface.parent
+ asserts += "MOZ_ASSERT(ToSupportsIsCorrect(aObject));\n"
+ return asserts
+
+
+def InitMemberSlots(descriptor, failureCode):
+ """
+ Initialize member slots on our JS object if we're supposed to have some.
+
+ Note that this is called after the SetWrapper() call in the
+ wrapperCache case, since that can affect how our getters behave
+ and we plan to invoke them here. So if we fail, we need to
+ ClearWrapper.
+ """
+ if not descriptor.interface.hasMembersInSlots():
+ return ""
+ return fill(
+ """
+ if (!UpdateMemberSlots(aCx, aReflector, aObject)) {
+ $*{failureCode}
+ }
+ """,
+ failureCode=failureCode)
+
+
+def SetImmutablePrototype(descriptor, failureCode):
+ if not descriptor.hasNonOrdinaryGetPrototypeOf():
+ return ""
+
+ return fill(
+ """
+ bool succeeded;
+ if (!JS_SetImmutablePrototype(aCx, aReflector, &succeeded)) {
+ ${failureCode}
+ }
+ MOZ_ASSERT(succeeded,
+ "Making a fresh reflector instance have an immutable "
+ "prototype can internally fail, but it should never be "
+ "unsuccessful");
+ """,
+ failureCode=failureCode)
+
+
+def DeclareProto():
+ """
+ Declare the canonicalProto and proto we have for our wrapping operation.
+ """
+ return dedent(
+ """
+ JS::Handle<JSObject*> canonicalProto = GetProtoObjectHandle(aCx);
+ if (!canonicalProto) {
+ return false;
+ }
+ JS::Rooted<JSObject*> proto(aCx);
+ if (aGivenProto) {
+ proto = aGivenProto;
+ // Unfortunately, while aGivenProto was in the compartment of aCx
+ // coming in, we changed compartments to that of "parent" so may need
+ // to wrap the proto here.
+ if (js::GetContextCompartment(aCx) != js::GetObjectCompartment(proto)) {
+ if (!JS_WrapObject(aCx, &proto)) {
+ return false;
+ }
+ }
+ } else {
+ proto = canonicalProto;
+ }
+ """)
+
+
+class CGWrapWithCacheMethod(CGAbstractMethod):
+ """
+ Create a wrapper JSObject for a given native that implements nsWrapperCache.
+
+ properties should be a PropertyArrays instance.
+ """
+ def __init__(self, descriptor, properties):
+ assert descriptor.interface.hasInterfacePrototypeObject()
+ args = [Argument('JSContext*', 'aCx'),
+ Argument(descriptor.nativeType + '*', 'aObject'),
+ Argument('nsWrapperCache*', 'aCache'),
+ Argument('JS::Handle<JSObject*>', 'aGivenProto'),
+ Argument('JS::MutableHandle<JSObject*>', 'aReflector')]
+ CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'bool', args)
+ self.properties = properties
+
+ def definition_body(self):
+ if self.descriptor.proxy:
+ preserveWrapper = dedent(
+ """
+ // For DOM proxies, the only reliable way to preserve the wrapper
+ // is to force creation of the expando object.
+ JS::Rooted<JSObject*> unused(aCx,
+ DOMProxyHandler::EnsureExpandoObject(aCx, aReflector));
+ """)
+ else:
+ preserveWrapper = "PreserveWrapper(aObject);\n"
+
+ failureCode = dedent(
+ """
+ aCache->ReleaseWrapper(aObject);
+ aCache->ClearWrapper();
+ return false;
+ """)
+
+ return fill(
+ """
+ $*{assertInheritance}
+ MOZ_ASSERT(!aCache->GetWrapper(),
+ "You should probably not be using Wrap() directly; use "
+ "GetOrCreateDOMReflector instead");
+
+ MOZ_ASSERT(ToSupportsIsOnPrimaryInheritanceChain(aObject, aCache),
+ "nsISupports must be on our primary inheritance chain");
+
+ JS::Rooted<JSObject*> global(aCx, FindAssociatedGlobal(aCx, aObject->GetParentObject()));
+ if (!global) {
+ return false;
+ }
+ MOZ_ASSERT(JS_IsGlobalObject(global));
+ MOZ_ASSERT(!JS::ObjectIsMarkedGray(global));
+
+ // That might have ended up wrapping us already, due to the wonders
+ // of XBL. Check for that, and bail out as needed.
+ aReflector.set(aCache->GetWrapper());
+ if (aReflector) {
+ #ifdef DEBUG
+ binding_detail::AssertReflectorHasGivenProto(aCx, aReflector, aGivenProto);
+ #endif // DEBUG
+ return true;
+ }
+
+ JSAutoCompartment ac(aCx, global);
+ $*{declareProto}
+
+ $*{createObject}
+
+ aCache->SetWrapper(aReflector);
+ $*{unforgeable}
+ $*{slots}
+ $*{setImmutablePrototype}
+ creator.InitializationSucceeded();
+
+ MOZ_ASSERT(aCache->GetWrapperPreserveColor() &&
+ aCache->GetWrapperPreserveColor() == aReflector);
+ // If proto != canonicalProto, we have to preserve our wrapper;
+ // otherwise we won't be able to properly recreate it later, since
+ // we won't know what proto to use. Note that we don't check
+ // aGivenProto here, since it's entirely possible (and even
+ // somewhat common) to have a non-null aGivenProto which is the
+ // same as canonicalProto.
+ if (proto != canonicalProto) {
+ $*{preserveWrapper}
+ }
+
+ return true;
+ """,
+ assertInheritance=AssertInheritanceChain(self.descriptor),
+ declareProto=DeclareProto(),
+ createObject=CreateBindingJSObject(self.descriptor, self.properties),
+ unforgeable=CopyUnforgeablePropertiesToInstance(self.descriptor,
+ failureCode),
+ slots=InitMemberSlots(self.descriptor, failureCode),
+ setImmutablePrototype=SetImmutablePrototype(self.descriptor,
+ failureCode),
+ preserveWrapper=preserveWrapper)
+
+
+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('T*', 'aObject'),
+ Argument('JS::Handle<JSObject*>', 'aGivenProto')]
+ CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'JSObject*', args,
+ inline=True, templateArgs=["class T"])
+
+ def definition_body(self):
+ return dedent("""
+ JS::Rooted<JSObject*> reflector(aCx);
+ return Wrap(aCx, aObject, aObject, aGivenProto, &reflector) ? reflector.get() : nullptr;
+ """)
+
+
+class CGWrapNonWrapperCacheMethod(CGAbstractMethod):
+ """
+ Create a wrapper JSObject for a given native that does not implement
+ nsWrapperCache.
+
+ properties should be a PropertyArrays instance.
+ """
+ def __init__(self, descriptor, properties):
+ # XXX can we wrap if we don't have an interface prototype object?
+ assert descriptor.interface.hasInterfacePrototypeObject()
+ args = [Argument('JSContext*', 'aCx'),
+ Argument(descriptor.nativeType + '*', 'aObject'),
+ Argument('JS::Handle<JSObject*>', 'aGivenProto'),
+ Argument('JS::MutableHandle<JSObject*>', 'aReflector')]
+ CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'bool', args)
+ self.properties = properties
+
+ def definition_body(self):
+ failureCode = "return false;\n"
+
+ return fill(
+ """
+ $*{assertions}
+
+ JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
+ $*{declareProto}
+
+ $*{createObject}
+
+ $*{unforgeable}
+
+ $*{slots}
+
+ $*{setImmutablePrototype}
+ creator.InitializationSucceeded();
+ return true;
+ """,
+ assertions=AssertInheritanceChain(self.descriptor),
+ declareProto=DeclareProto(),
+ createObject=CreateBindingJSObject(self.descriptor, self.properties),
+ unforgeable=CopyUnforgeablePropertiesToInstance(self.descriptor,
+ failureCode),
+ slots=InitMemberSlots(self.descriptor, failureCode),
+ setImmutablePrototype=SetImmutablePrototype(self.descriptor,
+ failureCode))
+
+
+class CGWrapGlobalMethod(CGAbstractMethod):
+ """
+ Create a wrapper JSObject for a global. The global must implement
+ nsWrapperCache.
+
+ properties should be a PropertyArrays instance.
+ """
+ def __init__(self, descriptor, properties):
+ assert descriptor.interface.hasInterfacePrototypeObject()
+ args = [Argument('JSContext*', 'aCx'),
+ Argument(descriptor.nativeType + '*', 'aObject'),
+ Argument('nsWrapperCache*', 'aCache'),
+ Argument('JS::CompartmentOptions&', 'aOptions'),
+ Argument('JSPrincipals*', 'aPrincipal'),
+ Argument('bool', 'aInitStandardClasses'),
+ Argument('JS::MutableHandle<JSObject*>', 'aReflector')]
+ CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'bool', args)
+ self.descriptor = descriptor
+ self.properties = properties
+
+ def definition_body(self):
+ if self.properties.hasNonChromeOnly():
+ properties = "sNativeProperties.Upcast()"
+ else:
+ properties = "nullptr"
+ if self.properties.hasChromeOnly():
+ chromeProperties = "nsContentUtils::ThreadsafeIsCallerChrome() ? sChromeOnlyNativeProperties.Upcast() : nullptr"
+ else:
+ chromeProperties = "nullptr"
+
+ failureCode = dedent(
+ """
+ aCache->ReleaseWrapper(aObject);
+ aCache->ClearWrapper();
+ return false;
+ """);
+
+ if self.descriptor.hasUnforgeableMembers:
+ unforgeable = InitUnforgeablePropertiesOnHolder(
+ self.descriptor, self.properties, failureCode,
+ "aReflector").define();
+ else:
+ unforgeable = ""
+
+ return fill(
+ """
+ $*{assertions}
+ MOZ_ASSERT(ToSupportsIsOnPrimaryInheritanceChain(aObject, aCache),
+ "nsISupports must be on our primary inheritance chain");
+
+ if (!CreateGlobal<${nativeType}, GetProtoObjectHandle>(aCx,
+ aObject,
+ aCache,
+ sClass.ToJSClass(),
+ aOptions,
+ aPrincipal,
+ aInitStandardClasses,
+ aReflector)) {
+ $*{failureCode}
+ }
+
+ // aReflector is a new global, so has a new compartment. Enter it
+ // before doing anything with it.
+ JSAutoCompartment ac(aCx, aReflector);
+
+ if (!DefineProperties(aCx, aReflector, ${properties}, ${chromeProperties})) {
+ $*{failureCode}
+ }
+ $*{unforgeable}
+
+ $*{slots}
+
+ return true;
+ """,
+ assertions=AssertInheritanceChain(self.descriptor),
+ nativeType=self.descriptor.nativeType,
+ properties=properties,
+ chromeProperties=chromeProperties,
+ failureCode=failureCode,
+ unforgeable=unforgeable,
+ slots=InitMemberSlots(self.descriptor, failureCode))
+
+
+class CGUpdateMemberSlotsMethod(CGAbstractStaticMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'aCx'),
+ Argument('JS::Handle<JSObject*>', 'aWrapper'),
+ Argument(descriptor.nativeType + '*', 'aObject')]
+ CGAbstractStaticMethod.__init__(self, descriptor, 'UpdateMemberSlots', 'bool', args)
+
+ def definition_body(self):
+ body = ("JS::Rooted<JS::Value> temp(aCx);\n"
+ "JSJitGetterCallArgs args(&temp);\n")
+ for m in self.descriptor.interface.members:
+ if m.isAttr() and m.getExtendedAttribute("StoreInSlot"):
+ # Skip doing this for the "window" and "self" attributes on the
+ # Window interface, because those can't be gotten safely until
+ # we have hooked it up correctly to the outer window. The
+ # window code handles doing the get itself.
+ if (self.descriptor.interface.identifier.name == "Window" and
+ (m.identifier.name == "window" or m.identifier.name == "self")):
+ continue
+ body += fill(
+ """
+
+ if (!get_${member}(aCx, aWrapper, aObject, args)) {
+ return false;
+ }
+ // Getter handled setting our reserved slots
+ """,
+ member=m.identifier.name)
+
+ body += "\nreturn true;\n"
+ return body
+
+
+class CGClearCachedValueMethod(CGAbstractMethod):
+ def __init__(self, descriptor, member):
+ self.member = member
+ # If we're StoreInSlot, we'll need to call the getter
+ if member.getExtendedAttribute("StoreInSlot"):
+ args = [Argument('JSContext*', 'aCx')]
+ returnType = 'bool'
+ else:
+ args = []
+ returnType = 'void'
+ args.append(Argument(descriptor.nativeType + '*', 'aObject'))
+ name = MakeClearCachedValueNativeName(member)
+ CGAbstractMethod.__init__(self, descriptor, name, returnType, args)
+
+ def definition_body(self):
+ slotIndex = memberReservedSlot(self.member, self.descriptor)
+ if self.member.getExtendedAttribute("StoreInSlot"):
+ # We have to root things and save the old value in case
+ # regetting fails, so we can restore it.
+ declObj = "JS::Rooted<JSObject*> obj(aCx);\n"
+ noopRetval = " true"
+ saveMember = (
+ "JS::Rooted<JS::Value> oldValue(aCx, js::GetReservedSlot(obj, %s));\n" %
+ slotIndex)
+ regetMember = fill(
+ """
+ JS::Rooted<JS::Value> temp(aCx);
+ JSJitGetterCallArgs args(&temp);
+ JSAutoCompartment ac(aCx, obj);
+ if (!get_${name}(aCx, obj, aObject, args)) {
+ js::SetReservedSlot(obj, ${slotIndex}, oldValue);
+ return false;
+ }
+ return true;
+ """,
+ name=self.member.identifier.name,
+ slotIndex=slotIndex)
+ else:
+ declObj = "JSObject* obj;\n"
+ noopRetval = ""
+ saveMember = ""
+ regetMember = ""
+
+ if self.descriptor.wantsXrays:
+ clearXrayExpandoSlots = fill(
+ """
+ xpc::ClearXrayExpandoSlots(obj, ${xraySlotIndex});
+ """,
+ xraySlotIndex=memberXrayExpandoReservedSlot(self.member,
+ self.descriptor))
+ else :
+ clearXrayExpandoSlots = ""
+
+ return fill(
+ """
+ $*{declObj}
+ obj = aObject->GetWrapper();
+ if (!obj) {
+ return${noopRetval};
+ }
+ $*{saveMember}
+ js::SetReservedSlot(obj, ${slotIndex}, JS::UndefinedValue());
+ $*{clearXrayExpandoSlots}
+ $*{regetMember}
+ """,
+ declObj=declObj,
+ noopRetval=noopRetval,
+ saveMember=saveMember,
+ slotIndex=slotIndex,
+ clearXrayExpandoSlots=clearXrayExpandoSlots,
+ regetMember=regetMember)
+
+
+class CGIsPermittedMethod(CGAbstractMethod):
+ """
+ crossOriginGetters/Setters/Methods are sets of names of the relevant members.
+ """
+ def __init__(self, descriptor, crossOriginGetters, crossOriginSetters,
+ crossOriginMethods):
+ self.crossOriginGetters = crossOriginGetters
+ self.crossOriginSetters = crossOriginSetters
+ self.crossOriginMethods = crossOriginMethods
+ args = [Argument("JSFlatString*", "prop"),
+ Argument("char16_t", "propFirstChar"),
+ Argument("bool", "set")]
+ CGAbstractMethod.__init__(self, descriptor, "IsPermitted", "bool", args,
+ inline=True)
+
+ def definition_body(self):
+ allNames = self.crossOriginGetters | self.crossOriginSetters | self.crossOriginMethods
+ readwrite = self.crossOriginGetters & self.crossOriginSetters
+ readonly = (self.crossOriginGetters - self.crossOriginSetters) | self.crossOriginMethods
+ writeonly = self.crossOriginSetters - self.crossOriginGetters
+ cases = {}
+ for name in sorted(allNames):
+ cond = 'JS_FlatStringEqualsAscii(prop, "%s")' % name
+ if name in readonly:
+ cond = "!set && %s" % cond
+ elif name in writeonly:
+ cond = "set && %s" % cond
+ else:
+ assert name in readwrite
+ firstLetter = name[0]
+ case = cases.get(firstLetter, CGList([]))
+ case.append(CGGeneric("if (%s) {\n"
+ " return true;\n"
+ "}\n" % cond))
+ cases[firstLetter] = case
+ caseList = []
+ for firstLetter in sorted(cases.keys()):
+ caseList.append(CGCase("'%s'" % firstLetter, cases[firstLetter]))
+ switch = CGSwitch("propFirstChar", caseList)
+ return switch.define() + "\nreturn false;\n"
+
+
+class CGCycleCollectionTraverseForOwningUnionMethod(CGAbstractMethod):
+ """
+ ImplCycleCollectionUnlink for owning union type.
+ """
+ def __init__(self, type):
+ self.type = type
+ args = [Argument("nsCycleCollectionTraversalCallback&", "aCallback"),
+ Argument("%s&" % CGUnionStruct.unionTypeName(type, True), "aUnion"),
+ Argument("const char*", "aName"),
+ Argument("uint32_t", "aFlags", "0")]
+ CGAbstractMethod.__init__(self, None, "ImplCycleCollectionTraverse", "void", args)
+
+ def deps(self):
+ return self.type.getDeps()
+
+ def definition_body(self):
+ memberNames = [getUnionMemberName(t)
+ for t in self.type.flatMemberTypes
+ if idlTypeNeedsCycleCollection(t)]
+ assert memberNames
+
+ conditionTemplate = 'aUnion.Is%s()'
+ functionCallTemplate = 'ImplCycleCollectionTraverse(aCallback, aUnion.GetAs%s(), "m%s", aFlags);\n'
+
+ ifStaments = (CGIfWrapper(CGGeneric(functionCallTemplate % (m, m)),
+ conditionTemplate % m)
+ for m in memberNames)
+
+ return CGElseChain(ifStaments).define()
+
+
+class CGCycleCollectionUnlinkForOwningUnionMethod(CGAbstractMethod):
+ """
+ ImplCycleCollectionUnlink for owning union type.
+ """
+ def __init__(self, type):
+ self.type = type
+ args = [Argument("%s&" % CGUnionStruct.unionTypeName(type, True), "aUnion")]
+ CGAbstractMethod.__init__(self, None, "ImplCycleCollectionUnlink", "void", args)
+
+ def deps(self):
+ return self.type.getDeps()
+
+ def definition_body(self):
+ return "aUnion.Uninit();\n"
+
+
+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.unrestricted_float: 'float',
+ IDLType.Tags.float: 'float',
+ IDLType.Tags.unrestricted_double: 'double',
+ IDLType.Tags.double: 'double'
+}
+
+numericSuffixes = {
+ IDLType.Tags.int8: '',
+ IDLType.Tags.uint8: '',
+ IDLType.Tags.int16: '',
+ IDLType.Tags.uint16: '',
+ IDLType.Tags.int32: '',
+ IDLType.Tags.uint32: 'U',
+ IDLType.Tags.int64: 'LL',
+ IDLType.Tags.uint64: 'ULL',
+ IDLType.Tags.unrestricted_float: 'F',
+ IDLType.Tags.float: 'F',
+ IDLType.Tags.unrestricted_double: '',
+ IDLType.Tags.double: ''
+}
+
+
+def numericValue(t, v):
+ if (t == IDLType.Tags.unrestricted_double or
+ t == IDLType.Tags.unrestricted_float):
+ typeName = builtinNames[t]
+ if v == float("inf"):
+ return "mozilla::PositiveInfinity<%s>()" % typeName
+ if v == float("-inf"):
+ return "mozilla::NegativeInfinity<%s>()" % typeName
+ if math.isnan(v):
+ return "mozilla::UnspecifiedNaN<%s>()" % typeName
+ return "%s%s" % (v, numericSuffixes[t])
+
+
+class CastableObjectUnwrapper():
+ """
+ A class for unwrapping an object stored in a JS Value (or
+ MutableHandle<Value> or Handle<Value>) named by the "source" and
+ "mutableSource" arguments based on the passed-in descriptor and storing it
+ in a variable called by the name in the "target" argument. The "source"
+ argument should be able to produce a Value or Handle<Value>; the
+ "mutableSource" argument should be able to produce a MutableHandle<Value>
+
+ codeOnFailure is the code to run if unwrapping fails.
+
+ If isCallbackReturnValue is "JSImpl" and our descriptor is also
+ JS-implemented, fall back to just creating the right object if what we
+ have isn't one already.
+
+ If allowCrossOriginObj is True, then we'll first do an
+ UncheckedUnwrap and then operate on the result.
+ """
+ def __init__(self, descriptor, source, mutableSource, target, codeOnFailure,
+ exceptionCode=None, isCallbackReturnValue=False,
+ allowCrossOriginObj=False):
+ self.substitution = {
+ "type": descriptor.nativeType,
+ "protoID": "prototypes::id::" + descriptor.name,
+ "target": target,
+ "codeOnFailure": codeOnFailure,
+ }
+ # Supporting both the "cross origin object" case and the "has
+ # XPConnect impls" case at the same time is a pain, so let's
+ # not do that. That allows us to assume that our source is
+ # always a Handle or MutableHandle.
+ if allowCrossOriginObj and descriptor.hasXPConnectImpls:
+ raise TypeError("Interface %s both allows a cross-origin 'this' "
+ "and has XPConnect impls. We don't support that" %
+ descriptor.name)
+ if allowCrossOriginObj:
+ self.substitution["uncheckedObjDecl"] = fill(
+ """
+ JS::Rooted<JSObject*> maybeUncheckedObj(cx, &${source}.toObject());
+ """,
+ source=source)
+ self.substitution["uncheckedObjGet"] = fill(
+ """
+ if (xpc::WrapperFactory::IsXrayWrapper(maybeUncheckedObj)) {
+ maybeUncheckedObj = js::UncheckedUnwrap(maybeUncheckedObj);
+ } else {
+ maybeUncheckedObj = js::CheckedUnwrap(maybeUncheckedObj);
+ if (!maybeUncheckedObj) {
+ $*{codeOnFailure}
+ }
+ }
+ """,
+ codeOnFailure=(codeOnFailure % { 'securityError': 'true'}))
+ self.substitution["source"] = "maybeUncheckedObj"
+ self.substitution["mutableSource"] = "&maybeUncheckedObj"
+ # No need to set up xpconnectUnwrap, since it won't be
+ # used in the allowCrossOriginObj case.
+ else:
+ self.substitution["uncheckedObjDecl"] = ""
+ self.substitution["uncheckedObjGet"] = ""
+ self.substitution["source"] = source
+ self.substitution["mutableSource"] = mutableSource
+ xpconnectUnwrap = (
+ "nsresult rv = UnwrapXPConnect<${type}>(cx, ${mutableSource}, getter_AddRefs(objPtr));\n")
+
+ if descriptor.hasXPConnectImpls:
+ self.substitution["codeOnFailure"] = string.Template(
+ "RefPtr<${type}> objPtr;\n" +
+ xpconnectUnwrap +
+ "if (NS_FAILED(rv)) {\n"
+ "${indentedCodeOnFailure}"
+ "}\n"
+ "// We should have an object\n"
+ "MOZ_ASSERT(objPtr);\n"
+ "${target} = objPtr;\n"
+ ).substitute(self.substitution,
+ indentedCodeOnFailure=indent(codeOnFailure))
+ elif (isCallbackReturnValue == "JSImpl" and
+ descriptor.interface.isJSImplemented()):
+ exceptionCode = exceptionCode or codeOnFailure
+ self.substitution["codeOnFailure"] = fill(
+ """
+ // Be careful to not wrap random DOM objects here, even if
+ // they're wrapped in opaque security wrappers for some reason.
+ // XXXbz Wish we could check for a JS-implemented object
+ // that already has a content reflection...
+ if (!IsDOMObject(js::UncheckedUnwrap(&${source}.toObject()))) {
+ nsCOMPtr<nsIGlobalObject> contentGlobal;
+ if (!GetContentGlobalForJSImplementedObject(cx, Callback(), getter_AddRefs(contentGlobal))) {
+ $*{exceptionCode}
+ }
+ JS::Rooted<JSObject*> jsImplSourceObj(cx, &${source}.toObject());
+ ${target} = new ${type}(jsImplSourceObj, contentGlobal);
+ } else {
+ $*{codeOnFailure}
+ }
+ """,
+ exceptionCode=exceptionCode,
+ **self.substitution)
+ else:
+ self.substitution["codeOnFailure"] = codeOnFailure
+
+ def __str__(self):
+ substitution = self.substitution.copy()
+ substitution["codeOnFailure"] %= {
+ 'securityError': 'rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO'
+ }
+ return fill(
+ """
+ $*{uncheckedObjDecl}
+ {
+ $*{uncheckedObjGet}
+ nsresult rv = UnwrapObject<${protoID}, ${type}>(${mutableSource}, ${target});
+ if (NS_FAILED(rv)) {
+ $*{codeOnFailure}
+ }
+ }
+ """,
+ **substitution)
+
+
+class FailureFatalCastableObjectUnwrapper(CastableObjectUnwrapper):
+ """
+ As CastableObjectUnwrapper, but defaulting to throwing if unwrapping fails
+ """
+ def __init__(self, descriptor, source, mutableSource, target, exceptionCode,
+ isCallbackReturnValue, sourceDescription):
+ CastableObjectUnwrapper.__init__(
+ self, descriptor, source, mutableSource, target,
+ 'ThrowErrorMessage(cx, MSG_DOES_NOT_IMPLEMENT_INTERFACE, "%s", "%s");\n'
+ '%s' % (sourceDescription, descriptor.interface.identifier.name,
+ exceptionCode),
+ exceptionCode,
+ isCallbackReturnValue)
+
+
+class CGCallbackTempRoot(CGGeneric):
+ def __init__(self, name):
+ define = dedent("""
+ { // Scope for tempRoot
+ JS::Rooted<JSObject*> tempRoot(cx, &${val}.toObject());
+ ${declName} = new %s(cx, tempRoot, mozilla::dom::GetIncumbentGlobal());
+ }
+ """) % name
+ CGGeneric.__init__(self, define=define)
+
+
+def getCallbackConversionInfo(type, idlObject, isMember, isCallbackReturnValue,
+ isOptional):
+ """
+ Returns a tuple containing the declType, declArgs, and basic
+ conversion for the given callback type, with the given callback
+ idl object in the given context (isMember/isCallbackReturnValue/isOptional).
+ """
+ name = idlObject.identifier.name
+
+ # We can't use fast callbacks if isOptional because then we get an
+ # Optional<RootedCallback> thing, which is not transparent to consumers.
+ useFastCallback = (not isMember and not isCallbackReturnValue and
+ not isOptional)
+ if useFastCallback:
+ name = "binding_detail::Fast%s" % name
+
+ if type.nullable() or isCallbackReturnValue:
+ declType = CGGeneric("RefPtr<%s>" % name)
+ else:
+ declType = CGGeneric("OwningNonNull<%s>" % name)
+
+ if useFastCallback:
+ declType = CGTemplatedType("RootedCallback", declType)
+ declArgs = "cx"
+ else:
+ declArgs = None
+
+ conversion = indent(CGCallbackTempRoot(name).define())
+ return (declType, declArgs, conversion)
+
+
+class JSToNativeConversionInfo():
+ """
+ An object representing information about a JS-to-native conversion.
+ """
+ def __init__(self, template, declType=None, holderType=None,
+ dealWithOptional=False, declArgs=None,
+ holderArgs=None):
+ """
+ template: A string representing the conversion code. This will have
+ template substitution performed on it as follows:
+
+ ${val} is a handle to the JS::Value in question
+ ${maybeMutableVal} May be a mutable handle to the JS::Value in
+ question. This is only OK to use if ${val} is
+ known to not be undefined.
+ ${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 or when True is passed for
+ checkForValue to instantiateJSToNativeConversion.
+ ${passedToJSImpl} replaced by an expression that evaluates to a boolean
+ for whether this value is being passed to a JS-
+ implemented interface.
+
+ declType: A CGThing representing the native C++ type we're converting
+ to. This is allowed to be None if the conversion code is
+ supposed to be used as-is.
+
+ holderType: A CGThing representing the type of a "holder" which will
+ hold a possible reference to the C++ thing whose type we
+ returned in declType, or None if no such holder is needed.
+
+ dealWithOptional: A boolean indicating whether the caller has to do
+ optional-argument handling. This should only be set
+ to true if the JS-to-native conversion is being done
+ for an optional argument or dictionary member with no
+ default value 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.
+
+ declArgs: If not None, the arguments to pass to the ${declName}
+ constructor. These will have template substitution performed
+ on them so you can use things like ${val}. This is a
+ single string, not a list of strings.
+
+ holderArgs: If not None, the arguments to pass to the ${holderName}
+ constructor. These will have template substitution
+ performed on them so you can use things like ${val}.
+ This is a single string, not a list of strings.
+
+ ${declName} must be in scope before the code from 'template' is entered.
+
+ If holderType is not None then ${holderName} must be in scope before
+ the code from 'template' is entered.
+ """
+ assert isinstance(template, str)
+ assert declType is None or isinstance(declType, CGThing)
+ assert holderType is None or isinstance(holderType, CGThing)
+ self.template = template
+ self.declType = declType
+ self.holderType = holderType
+ self.dealWithOptional = dealWithOptional
+ self.declArgs = declArgs
+ self.holderArgs = holderArgs
+
+
+def getHandleDefault(defaultValue):
+ tag = defaultValue.type.tag()
+ if tag in numericSuffixes:
+ # Some numeric literals require a suffix to compile without warnings
+ return numericValue(tag, defaultValue.value)
+ assert tag == IDLType.Tags.bool
+ return toStringBool(defaultValue.value)
+
+
+def handleDefaultStringValue(defaultValue, method):
+ """
+ Returns a string which ends up calling 'method' with a (char_t*, length)
+ pair that sets this string default value. This string is suitable for
+ passing as the second argument of handleDefault; in particular it does not
+ end with a ';'
+ """
+ assert defaultValue.type.isDOMString() or defaultValue.type.isByteString()
+ return ("static const %(char_t)s data[] = { %(data)s };\n"
+ "%(method)s(data, ArrayLength(data) - 1)") % {
+ 'char_t': "char" if defaultValue.type.isByteString() else "char16_t",
+ 'method': method,
+ 'data': ", ".join(["'" + char + "'" for char in
+ defaultValue.value] + ["0"])
+ }
+
+
+# If this function is modified, modify CGNativeMember.getArg and
+# CGNativeMember.getRetvalInfo accordingly. The latter cares about the decltype
+# and holdertype we end up using, because it needs to be able to return the code
+# that will convert those to the actual return value of the callback function.
+def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
+ isDefinitelyObject=False,
+ isMember=False,
+ isOptional=False,
+ invalidEnumValueFatal=True,
+ defaultValue=None,
+ treatNullAs="Default",
+ isEnforceRange=False,
+ isClamp=False,
+ isNullOrUndefined=False,
+ exceptionCode=None,
+ lenientFloatCode=None,
+ allowTreatNonCallableAsNull=False,
+ isCallbackReturnValue=False,
+ sourceDescription="value",
+ nestingLevel=""):
+ """
+ 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. However what actually happens when throwing an exception
+ can be controlled by exceptionCode. The only requirement on that is that
+ exceptionCode must end up doing a return, and every return from this
+ function must happen via exceptionCode if exceptionCode is not None.
+
+ If isDefinitelyObject is True, that means we know the value
+ isObject() and we have no need to recheck that.
+
+ if isMember is not False, 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. Callers can pass "Dictionary",
+ "Variadic", "Sequence", or "OwningUnion" to indicate that the conversion is
+ for something that is a dictionary member, a variadic argument, a sequence,
+ or an owning union respectively.
+
+ 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.
+
+ If lenientFloatCode is not None, it should be used in cases when
+ we're a non-finite float that's not unrestricted.
+
+ If allowTreatNonCallableAsNull is true, then [TreatNonCallableAsNull] and
+ [TreatNonObjectAsNull] extended attributes on nullable callback functions
+ will be honored.
+
+ If isCallbackReturnValue is "JSImpl" or "Callback", then the declType may be
+ adjusted to make it easier to return from a callback. Since that type is
+ never directly observable by any consumers of the callback code, this is OK.
+ Furthermore, if isCallbackReturnValue is "JSImpl", that affects the behavior
+ of the FailureFatalCastableObjectUnwrapper conversion; this is used for
+ implementing auto-wrapping of JS-implemented return values from a
+ JS-implemented interface.
+
+ sourceDescription is a description of what this JS value represents, to be
+ used in error reporting. Callers should assume that it might get placed in
+ the middle of a sentence. If it ends up at the beginning of a sentence, its
+ first character will be automatically uppercased.
+
+ The return value from this function is a JSToNativeConversionInfo.
+ """
+ # 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
+
+ # And we can't both be an object and be null or undefined
+ assert not isDefinitelyObject or not isNullOrUndefined
+
+ # 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 false;\n"
+
+ # 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
+ def onFailureNotAnObject(failureCode):
+ return CGGeneric(
+ failureCode or
+ ('ThrowErrorMessage(cx, MSG_NOT_OBJECT, "%s");\n'
+ '%s' % (firstCap(sourceDescription), exceptionCode)))
+
+ def onFailureBadType(failureCode, typeName):
+ return CGGeneric(
+ failureCode or
+ ('ThrowErrorMessage(cx, MSG_DOES_NOT_IMPLEMENT_INTERFACE, "%s", "%s");\n'
+ '%s' % (firstCap(sourceDescription), typeName, exceptionCode)))
+
+ def onFailureNotCallable(failureCode):
+ return CGGeneric(
+ failureCode or
+ ('ThrowErrorMessage(cx, MSG_NOT_CALLABLE, "%s");\n'
+ '%s' % (firstCap(sourceDescription), exceptionCode)))
+
+ # 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 (
+ "if (${haveValue}) {\n" +
+ indent(template) +
+ "} else {\n" +
+ indent(setDefault) +
+ "}\n")
+
+ # A helper function for wrapping up the template body for
+ # possibly-nullable objecty stuff
+ def wrapObjectTemplate(templateBody, type, codeToSetNull, failureCode=None):
+ if isNullOrUndefined and type.nullable():
+ # Just ignore templateBody and set ourselves to null.
+ # Note that we don't have to worry about default values
+ # here either, since we already examined this value.
+ return codeToSetNull
+
+ if not isDefinitelyObject:
+ # Handle the non-object cases by wrapping up the whole
+ # thing in an if cascade.
+ if type.nullable():
+ elifLine = "} else if (${val}.isNullOrUndefined()) {\n"
+ elifBody = codeToSetNull
+ else:
+ elifLine = ""
+ elifBody = ""
+
+ # Note that $${val} below expands to ${val}. This string is
+ # used as a template later, and val will be filled in then.
+ templateBody = fill(
+ """
+ if ($${val}.isObject()) {
+ $*{templateBody}
+ $*{elifLine}
+ $*{elifBody}
+ } else {
+ $*{failureBody}
+ }
+ """,
+ templateBody=templateBody,
+ elifLine=elifLine,
+ elifBody=elifBody,
+ failureBody=onFailureNotAnObject(failureCode).define())
+
+ if isinstance(defaultValue, IDLNullValue):
+ assert type.nullable() # Parser should enforce this
+ templateBody = handleDefault(templateBody, codeToSetNull)
+ elif isinstance(defaultValue, IDLEmptySequenceValue):
+ # Our caller will handle it
+ pass
+ else:
+ assert defaultValue is None
+
+ return templateBody
+
+ # A helper function for converting things that look like a JSObject*.
+ def handleJSObjectType(type, isMember, failureCode, exceptionCode, sourceDescription):
+ if not isMember:
+ if isOptional:
+ # We have a specialization of Optional that will use a
+ # Rooted for the storage here.
+ declType = CGGeneric("JS::Handle<JSObject*>")
+ else:
+ declType = CGGeneric("JS::Rooted<JSObject*>")
+ declArgs = "cx"
+ else:
+ assert (isMember in
+ ("Sequence", "Variadic", "Dictionary", "OwningUnion", "MozMap"))
+ # We'll get traced by the sequence or dictionary or union tracer
+ declType = CGGeneric("JSObject*")
+ declArgs = None
+ templateBody = "${declName} = &${val}.toObject();\n"
+
+ # For JS-implemented APIs, we refuse to allow passing objects that the
+ # API consumer does not subsume. The extra parens around
+ # ($${passedToJSImpl}) suppress unreachable code warnings when
+ # $${passedToJSImpl} is the literal `false`.
+ if not isinstance(descriptorProvider, Descriptor) or descriptorProvider.interface.isJSImplemented():
+ templateBody = fill(
+ """
+ if (($${passedToJSImpl}) && !CallerSubsumes($${val})) {
+ ThrowErrorMessage(cx, MSG_PERMISSION_DENIED_TO_PASS_ARG, "${sourceDescription}");
+ $*{exceptionCode}
+ }
+ """,
+ sourceDescription=sourceDescription,
+ exceptionCode=exceptionCode) + templateBody
+
+ setToNullCode = "${declName} = nullptr;\n"
+ template = wrapObjectTemplate(templateBody, type, setToNullCode,
+ failureCode)
+ return JSToNativeConversionInfo(template, declType=declType,
+ dealWithOptional=isOptional,
+ declArgs=declArgs)
+
+ def incrementNestingLevel():
+ if nestingLevel is "":
+ return 1
+ return nestingLevel + 1
+
+ assert not (isEnforceRange and isClamp) # These are mutually exclusive
+
+ if type.isSequence():
+ assert not isEnforceRange and not isClamp
+
+ if failureCode is None:
+ notSequence = ('ThrowErrorMessage(cx, MSG_NOT_SEQUENCE, "%s");\n'
+ "%s" % (firstCap(sourceDescription), exceptionCode))
+ else:
+ notSequence = failureCode
+
+ 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 want to use auto arrays if we can, but we have to be careful with
+ # reallocation behavior for arrays. In particular, if we use auto
+ # arrays for sequences and have a sequence of elements which are
+ # themselves sequences or have sequences as members, we have a problem.
+ # In that case, resizing the outermost AutoTArray to the right size
+ # will memmove its elements, but AutoTArrays are not memmovable and
+ # hence will end up with pointers to bogus memory, which is bad. To
+ # deal with this, we typically map WebIDL sequences to our Sequence
+ # type, which is in fact memmovable. The one exception is when we're
+ # passing in a sequence directly as an argument without any sort of
+ # optional or nullable complexity going on. In that situation, we can
+ # use an AutoSequence instead. We have to keep using Sequence in the
+ # nullable and optional cases because we don't want to leak the
+ # AutoSequence type to consumers, which would be unavoidable with
+ # Nullable<AutoSequence> or Optional<AutoSequence>.
+ if isMember or isOptional or nullable or isCallbackReturnValue:
+ sequenceClass = "Sequence"
+ else:
+ sequenceClass = "binding_detail::AutoSequence"
+
+ # XXXbz we can't include the index in the sourceDescription, because
+ # we don't really have a way to pass one in dynamically at runtime...
+ elementInfo = getJSToNativeConversionInfo(
+ elementType, descriptorProvider, isMember="Sequence",
+ exceptionCode=exceptionCode, lenientFloatCode=lenientFloatCode,
+ isCallbackReturnValue=isCallbackReturnValue,
+ sourceDescription="element of %s" % sourceDescription,
+ nestingLevel=incrementNestingLevel())
+ if elementInfo.dealWithOptional:
+ raise TypeError("Shouldn't have optional things in sequences")
+ if elementInfo.holderType is not None:
+ raise TypeError("Shouldn't need holders for sequences")
+
+ typeName = CGTemplatedType(sequenceClass, elementInfo.declType)
+ sequenceType = typeName.define()
+ if nullable:
+ typeName = CGTemplatedType("Nullable", typeName)
+ arrayRef = "${declName}.SetValue()"
+ else:
+ arrayRef = "${declName}"
+
+ elementConversion = string.Template(elementInfo.template).substitute({
+ "val": "temp" + str(nestingLevel),
+ "maybeMutableVal": "&temp" + str(nestingLevel),
+ "declName": "slot" + str(nestingLevel),
+ # We only need holderName here to handle isExternal()
+ # interfaces, which use an internal holder for the
+ # conversion even when forceOwningType ends up true.
+ "holderName": "tempHolder" + str(nestingLevel),
+ "passedToJSImpl": "${passedToJSImpl}"
+ })
+
+ # NOTE: Keep this in sync with variadic conversions as needed
+ templateBody = fill(
+ """
+ JS::ForOfIterator iter${nestingLevel}(cx);
+ if (!iter${nestingLevel}.init($${val}, JS::ForOfIterator::AllowNonIterable)) {
+ $*{exceptionCode}
+ }
+ if (!iter${nestingLevel}.valueIsIterable()) {
+ $*{notSequence}
+ }
+ ${sequenceType} &arr${nestingLevel} = ${arrayRef};
+ JS::Rooted<JS::Value> temp${nestingLevel}(cx);
+ while (true) {
+ bool done${nestingLevel};
+ if (!iter${nestingLevel}.next(&temp${nestingLevel}, &done${nestingLevel})) {
+ $*{exceptionCode}
+ }
+ if (done${nestingLevel}) {
+ break;
+ }
+ ${elementType}* slotPtr${nestingLevel} = arr${nestingLevel}.AppendElement(mozilla::fallible);
+ if (!slotPtr${nestingLevel}) {
+ JS_ReportOutOfMemory(cx);
+ $*{exceptionCode}
+ }
+ ${elementType}& slot${nestingLevel} = *slotPtr${nestingLevel};
+ $*{elementConversion}
+ }
+ """,
+ exceptionCode=exceptionCode,
+ notSequence=notSequence,
+ sequenceType=sequenceType,
+ arrayRef=arrayRef,
+ elementType=elementInfo.declType.define(),
+ elementConversion=elementConversion,
+ nestingLevel=str(nestingLevel))
+
+ templateBody = wrapObjectTemplate(templateBody, type,
+ "${declName}.SetNull();\n", notSequence)
+ if isinstance(defaultValue, IDLEmptySequenceValue):
+ if type.nullable():
+ codeToSetEmpty = "${declName}.SetValue();\n"
+ else:
+ codeToSetEmpty = "/* Array is already empty; nothing to do */\n"
+ templateBody = handleDefault(templateBody, codeToSetEmpty)
+
+ # Sequence arguments that might contain traceable things need
+ # to get traced
+ if not isMember and typeNeedsRooting(elementType):
+ holderType = CGTemplatedType("SequenceRooter", elementInfo.declType)
+ # If our sequence is nullable, this will set the Nullable to be
+ # not-null, but that's ok because we make an explicit SetNull() call
+ # on it as needed if our JS value is actually null.
+ holderArgs = "cx, &%s" % arrayRef
+ else:
+ holderType = None
+ holderArgs = None
+
+ return JSToNativeConversionInfo(templateBody, declType=typeName,
+ holderType=holderType,
+ dealWithOptional=isOptional,
+ holderArgs=holderArgs)
+
+ if type.isMozMap():
+ assert not isEnforceRange and not isClamp
+ if failureCode is None:
+ notMozMap = ('ThrowErrorMessage(cx, MSG_NOT_OBJECT, "%s");\n'
+ "%s" % (firstCap(sourceDescription), exceptionCode))
+ else:
+ notMozMap = failureCode
+
+ nullable = type.nullable()
+ # Be very careful not to change "type": we need it later
+ if nullable:
+ valueType = type.inner.inner
+ else:
+ valueType = type.inner
+
+ valueInfo = getJSToNativeConversionInfo(
+ valueType, descriptorProvider, isMember="MozMap",
+ exceptionCode=exceptionCode, lenientFloatCode=lenientFloatCode,
+ isCallbackReturnValue=isCallbackReturnValue,
+ sourceDescription="value in %s" % sourceDescription,
+ nestingLevel=incrementNestingLevel())
+ if valueInfo.dealWithOptional:
+ raise TypeError("Shouldn't have optional things in MozMap")
+ if valueInfo.holderType is not None:
+ raise TypeError("Shouldn't need holders for MozMap")
+
+ typeName = CGTemplatedType("MozMap", valueInfo.declType)
+ mozMapType = typeName.define()
+ if nullable:
+ typeName = CGTemplatedType("Nullable", typeName)
+ mozMapRef = "${declName}.SetValue()"
+ else:
+ mozMapRef = "${declName}"
+
+ valueConversion = string.Template(valueInfo.template).substitute({
+ "val": "temp",
+ "maybeMutableVal": "&temp",
+ "declName": "slot",
+ # We only need holderName here to handle isExternal()
+ # interfaces, which use an internal holder for the
+ # conversion even when forceOwningType ends up true.
+ "holderName": "tempHolder",
+ "passedToJSImpl": "${passedToJSImpl}"
+ })
+
+ templateBody = fill(
+ """
+ ${mozMapType} &mozMap = ${mozMapRef};
+
+ JS::Rooted<JSObject*> mozMapObj(cx, &$${val}.toObject());
+ JS::Rooted<JS::IdVector> ids(cx, JS::IdVector(cx));
+ if (!JS_Enumerate(cx, mozMapObj, &ids)) {
+ $*{exceptionCode}
+ }
+ JS::Rooted<JS::Value> propNameValue(cx);
+ JS::Rooted<JS::Value> temp(cx);
+ JS::Rooted<jsid> curId(cx);
+ for (size_t i = 0; i < ids.length(); ++i) {
+ // Make sure we get the value before converting the name, since
+ // getting the value can trigger GC but our name is a dependent
+ // string.
+ curId = ids[i];
+ binding_detail::FakeString propName;
+ bool isSymbol;
+ if (!ConvertIdToString(cx, curId, propName, isSymbol) ||
+ (!isSymbol && !JS_GetPropertyById(cx, mozMapObj, curId, &temp))) {
+ $*{exceptionCode}
+ }
+ if (isSymbol) {
+ continue;
+ }
+
+ ${valueType}* slotPtr = mozMap.AddEntry(propName);
+ if (!slotPtr) {
+ JS_ReportOutOfMemory(cx);
+ $*{exceptionCode}
+ }
+ ${valueType}& slot = *slotPtr;
+ $*{valueConversion}
+ }
+ """,
+ exceptionCode=exceptionCode,
+ mozMapType=mozMapType,
+ mozMapRef=mozMapRef,
+ valueType=valueInfo.declType.define(),
+ valueConversion=valueConversion)
+
+ templateBody = wrapObjectTemplate(templateBody, type,
+ "${declName}.SetNull();\n",
+ notMozMap)
+
+ declType = typeName
+ declArgs = None
+ holderType = None
+ holderArgs = None
+ # MozMap arguments that might contain traceable things need
+ # to get traced
+ if not isMember and isCallbackReturnValue:
+ # Go ahead and just convert directly into our actual return value
+ declType = CGWrapper(declType, post="&")
+ declArgs = "aRetVal"
+ elif not isMember and typeNeedsRooting(valueType):
+ holderType = CGTemplatedType("MozMapRooter", valueInfo.declType)
+ # If our MozMap is nullable, this will set the Nullable to be
+ # not-null, but that's ok because we make an explicit SetNull() call
+ # on it as needed if our JS value is actually null.
+ holderArgs = "cx, &%s" % mozMapRef
+
+ return JSToNativeConversionInfo(templateBody, declType=declType,
+ declArgs=declArgs,
+ holderType=holderType,
+ dealWithOptional=isOptional,
+ holderArgs=holderArgs)
+
+ if type.isUnion():
+ nullable = type.nullable()
+ if nullable:
+ type = type.inner
+
+ isOwningUnion = isMember or isCallbackReturnValue
+ unionArgumentObj = "${declName}" if isOwningUnion else "${holderName}"
+ if nullable:
+ # If we're owning, we're a Nullable, which hasn't been told it has
+ # a value. Otherwise we're an already-constructed Maybe.
+ unionArgumentObj += ".SetValue()" if isOwningUnion else ".ref()"
+
+ memberTypes = type.flatMemberTypes
+ names = []
+
+ interfaceMemberTypes = filter(lambda t: t.isNonCallbackInterface(), memberTypes)
+ if len(interfaceMemberTypes) > 0:
+ interfaceObject = []
+ for memberType in interfaceMemberTypes:
+ name = getUnionMemberName(memberType)
+ interfaceObject.append(
+ CGGeneric("(failed = !%s.TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext" %
+ (unionArgumentObj, name)))
+ names.append(name)
+ interfaceObject = CGWrapper(CGList(interfaceObject, " ||\n"),
+ pre="done = ", post=";\n\n", reindent=True)
+ else:
+ interfaceObject = None
+
+ sequenceObjectMemberTypes = filter(lambda t: t.isSequence(), memberTypes)
+ if len(sequenceObjectMemberTypes) > 0:
+ assert len(sequenceObjectMemberTypes) == 1
+ name = getUnionMemberName(sequenceObjectMemberTypes[0])
+ sequenceObject = CGGeneric(
+ "done = (failed = !%s.TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext;\n" %
+ (unionArgumentObj, name))
+ names.append(name)
+ else:
+ sequenceObject = None
+
+ dateObjectMemberTypes = filter(lambda t: t.isDate(), memberTypes)
+ if len(dateObjectMemberTypes) > 0:
+ assert len(dateObjectMemberTypes) == 1
+ memberType = dateObjectMemberTypes[0]
+ name = getUnionMemberName(memberType)
+ dateObject = CGGeneric("%s.SetTo%s(cx, ${val});\n"
+ "done = true;\n" % (unionArgumentObj, name))
+ dateObject = CGIfWrapper(dateObject, "JS_ObjectIsDate(cx, argObj)")
+ 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 = getUnionMemberName(memberType)
+ callbackObject = CGGeneric(
+ "done = (failed = !%s.TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext;\n" %
+ (unionArgumentObj, name))
+ names.append(name)
+ else:
+ callbackObject = None
+
+ dictionaryMemberTypes = filter(lambda t: t.isDictionary(), memberTypes)
+ if len(dictionaryMemberTypes) > 0:
+ assert len(dictionaryMemberTypes) == 1
+ name = getUnionMemberName(dictionaryMemberTypes[0])
+ setDictionary = CGGeneric(
+ "done = (failed = !%s.TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext;\n" %
+ (unionArgumentObj, name))
+ names.append(name)
+ else:
+ setDictionary = None
+
+ mozMapMemberTypes = filter(lambda t: t.isMozMap(), memberTypes)
+ if len(mozMapMemberTypes) > 0:
+ assert len(mozMapMemberTypes) == 1
+ name = getUnionMemberName(mozMapMemberTypes[0])
+ mozMapObject = CGGeneric(
+ "done = (failed = !%s.TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext;\n" %
+ (unionArgumentObj, name))
+ names.append(name)
+ else:
+ mozMapObject = None
+
+ objectMemberTypes = filter(lambda t: t.isObject(), memberTypes)
+ if len(objectMemberTypes) > 0:
+ assert len(objectMemberTypes) == 1
+ # Very important to NOT construct a temporary Rooted here, since the
+ # SetToObject call can call a Rooted constructor and we need to keep
+ # stack discipline for Rooted.
+ object = CGGeneric("if (!%s.SetToObject(cx, &${val}.toObject(), ${passedToJSImpl})) {\n"
+ "%s"
+ "}\n"
+ "done = true;\n" % (unionArgumentObj, indent(exceptionCode)))
+ names.append(objectMemberTypes[0].name)
+ else:
+ object = None
+
+ hasObjectTypes = interfaceObject or sequenceObject or dateObject or callbackObject or object or mozMapObject
+ if hasObjectTypes:
+ # "object" is not distinguishable from other types
+ assert not object or not (interfaceObject or sequenceObject or dateObject or callbackObject or mozMapObject)
+ if sequenceObject or dateObject or callbackObject:
+ # An object can be both an sequence object and a callback or
+ # dictionary, but we shouldn't have both in the union's members
+ # because they are not distinguishable.
+ assert not (sequenceObject and callbackObject)
+ templateBody = CGElseChain([sequenceObject, dateObject, callbackObject])
+ else:
+ templateBody = None
+ if interfaceObject:
+ assert not object
+ if templateBody:
+ templateBody = CGIfWrapper(templateBody, "!done")
+ templateBody = CGList([interfaceObject, templateBody])
+ else:
+ templateBody = CGList([templateBody, object])
+
+ if dateObject:
+ templateBody.prepend(CGGeneric("JS::Rooted<JSObject*> argObj(cx, &${val}.toObject());\n"))
+
+ if mozMapObject:
+ templateBody = CGList([templateBody,
+ CGIfWrapper(mozMapObject, "!done")])
+
+ templateBody = CGIfWrapper(templateBody, "${val}.isObject()")
+ else:
+ templateBody = CGGeneric()
+
+ if setDictionary:
+ assert not object
+ templateBody = CGList([templateBody,
+ CGIfWrapper(setDictionary, "!done")])
+
+ stringTypes = [t for t in memberTypes if t.isString() or t.isEnum()]
+ numericTypes = [t for t in memberTypes if t.isNumeric()]
+ booleanTypes = [t for t in memberTypes if t.isBoolean()]
+ if stringTypes or numericTypes or booleanTypes:
+ assert len(stringTypes) <= 1
+ assert len(numericTypes) <= 1
+ assert len(booleanTypes) <= 1
+
+ # We will wrap all this stuff in a do { } while (0); so we
+ # can use "break" for flow control.
+ def getStringOrPrimitiveConversion(memberType):
+ name = getUnionMemberName(memberType)
+ return CGGeneric("done = (failed = !%s.TrySetTo%s(cx, ${val}, tryNext)) || !tryNext;\n"
+ "break;\n" % (unionArgumentObj, name))
+ other = CGList([])
+ stringConversion = map(getStringOrPrimitiveConversion, stringTypes)
+ numericConversion = map(getStringOrPrimitiveConversion, numericTypes)
+ booleanConversion = map(getStringOrPrimitiveConversion, booleanTypes)
+ if stringConversion:
+ if booleanConversion:
+ other.append(CGIfWrapper(booleanConversion[0],
+ "${val}.isBoolean()"))
+ if numericConversion:
+ other.append(CGIfWrapper(numericConversion[0],
+ "${val}.isNumber()"))
+ other.append(stringConversion[0])
+ elif numericConversion:
+ if booleanConversion:
+ other.append(CGIfWrapper(booleanConversion[0],
+ "${val}.isBoolean()"))
+ other.append(numericConversion[0])
+ else:
+ assert booleanConversion
+ other.append(booleanConversion[0])
+
+ other = CGWrapper(CGIndenter(other), pre="do {\n", post="} while (0);\n")
+ if hasObjectTypes or setDictionary:
+ other = CGWrapper(CGIndenter(other), "{\n", post="}\n")
+ if object:
+ templateBody = CGElseChain([templateBody, other])
+ else:
+ other = CGWrapper(other, pre="if (!done) ")
+ templateBody = CGList([templateBody, other])
+ else:
+ assert templateBody.define() == ""
+ templateBody = other
+ else:
+ other = None
+
+ templateBody = CGWrapper(templateBody, pre="bool done = false, failed = false, tryNext;\n")
+ throw = CGGeneric(fill(
+ """
+ if (failed) {
+ $*{exceptionCode}
+ }
+ if (!done) {
+ ThrowErrorMessage(cx, MSG_NOT_IN_UNION, "${desc}", "${names}");
+ $*{exceptionCode}
+ }
+ """,
+ exceptionCode=exceptionCode,
+ desc=firstCap(sourceDescription),
+ names=", ".join(names)))
+
+ templateBody = CGWrapper(CGIndenter(CGList([templateBody, throw])), pre="{\n", post="}\n")
+
+ typeName = CGUnionStruct.unionTypeDecl(type, isOwningUnion)
+ argumentTypeName = typeName + "Argument"
+ if nullable:
+ typeName = "Nullable<" + typeName + " >"
+
+ def handleNull(templateBody, setToNullVar, extraConditionForNull=""):
+ nullTest = "%s${val}.isNullOrUndefined()" % extraConditionForNull
+ return CGIfElseWrapper(nullTest,
+ CGGeneric("%s.SetNull();\n" % setToNullVar),
+ templateBody)
+
+ if type.hasNullableType:
+ assert not nullable
+ # Make sure to handle a null default value here
+ if defaultValue and isinstance(defaultValue, IDLNullValue):
+ assert defaultValue.type == type
+ extraConditionForNull = "!(${haveValue}) || "
+ else:
+ extraConditionForNull = ""
+ templateBody = handleNull(templateBody, unionArgumentObj,
+ extraConditionForNull=extraConditionForNull)
+
+ declType = CGGeneric(typeName)
+ if isOwningUnion:
+ holderType = None
+ else:
+ holderType = CGGeneric(argumentTypeName)
+ if nullable:
+ holderType = CGTemplatedType("Maybe", holderType)
+
+ # If we're isOptional and not nullable the normal optional handling will
+ # handle lazy construction of our holder. If we're nullable and not
+ # owning we do it all by hand because we do not want our holder
+ # constructed if we're null. But if we're owning we don't have a
+ # holder anyway, so we can do the normal Optional codepath.
+ declLoc = "${declName}"
+ constructDecl = None
+ if nullable:
+ if isOptional and not isOwningUnion:
+ holderArgs = "${declName}.Value().SetValue()"
+ declType = CGTemplatedType("Optional", declType)
+ constructDecl = CGGeneric("${declName}.Construct();\n")
+ declLoc = "${declName}.Value()"
+ else:
+ holderArgs = "${declName}.SetValue()"
+ if holderType is not None:
+ constructHolder = CGGeneric("${holderName}.emplace(%s);\n" % holderArgs)
+ else:
+ constructHolder = None
+ # Don't need to pass those args when the holder is being constructed
+ holderArgs = None
+ else:
+ holderArgs = "${declName}"
+ constructHolder = None
+
+ if not isMember and isCallbackReturnValue:
+ declType = CGWrapper(declType, post="&")
+ declArgs = "aRetVal"
+ else:
+ declArgs = None
+
+ if defaultValue and not isinstance(defaultValue, IDLNullValue):
+ tag = defaultValue.type.tag()
+
+ if tag in numericSuffixes or tag is IDLType.Tags.bool:
+ defaultStr = getHandleDefault(defaultValue)
+ # Make sure we actually construct the thing inside the nullable.
+ value = declLoc + (".SetValue()" if nullable else "")
+ name = getUnionMemberName(defaultValue.type)
+ default = CGGeneric("%s.RawSetAs%s() = %s;\n" %
+ (value, name, defaultStr))
+ elif isinstance(defaultValue, IDLEmptySequenceValue):
+ name = getUnionMemberName(defaultValue.type)
+ # Make sure we actually construct the thing inside the nullable.
+ value = declLoc + (".SetValue()" if nullable else "")
+ # It's enough to set us to the right type; that will
+ # create an empty array, which is all we need here.
+ default = CGGeneric("%s.RawSetAs%s();\n" %
+ (value, name))
+ elif defaultValue.type.isEnum():
+ name = getUnionMemberName(defaultValue.type)
+ # Make sure we actually construct the thing inside the nullable.
+ value = declLoc + (".SetValue()" if nullable else "")
+ default = CGGeneric(
+ "%s.RawSetAs%s() = %s::%s;\n" %
+ (value, name,
+ defaultValue.type.inner.identifier.name,
+ getEnumValueName(defaultValue.value)))
+ else:
+ default = CGGeneric(
+ handleDefaultStringValue(
+ defaultValue, "%s.SetStringData" % unionArgumentObj) +
+ ";\n")
+
+ templateBody = CGIfElseWrapper("!(${haveValue})", default, templateBody)
+
+ templateBody = CGList([constructHolder, templateBody])
+
+ if nullable:
+ if defaultValue:
+ if isinstance(defaultValue, IDLNullValue):
+ extraConditionForNull = "!(${haveValue}) || "
+ else:
+ extraConditionForNull = "${haveValue} && "
+ else:
+ extraConditionForNull = ""
+ templateBody = handleNull(templateBody, declLoc,
+ extraConditionForNull=extraConditionForNull)
+ elif (not type.hasNullableType and defaultValue and
+ isinstance(defaultValue, IDLNullValue)):
+ assert type.hasDictionaryType()
+ assert defaultValue.type.isDictionary()
+ if not isOwningUnion and typeNeedsRooting(defaultValue.type):
+ ctorArgs = "cx"
+ else:
+ ctorArgs = ""
+ initDictionaryWithNull = CGIfWrapper(
+ CGGeneric("return false;\n"),
+ ('!%s.RawSetAs%s(%s).Init(cx, JS::NullHandleValue, "Member of %s")'
+ % (declLoc, getUnionMemberName(defaultValue.type),
+ ctorArgs, type)))
+ templateBody = CGIfElseWrapper("!(${haveValue})",
+ initDictionaryWithNull,
+ templateBody)
+
+ templateBody = CGList([constructDecl, templateBody])
+
+ return JSToNativeConversionInfo(templateBody.define(),
+ declType=declType,
+ declArgs=declArgs,
+ holderType=holderType,
+ holderArgs=holderArgs,
+ dealWithOptional=isOptional and (not nullable or isOwningUnion))
+
+ if type.isGeckoInterface():
+ assert not isEnforceRange and not isClamp
+
+ descriptor = descriptorProvider.getDescriptor(
+ type.unroll().inner.identifier.name)
+
+ assert descriptor.nativeType != 'JSObject'
+
+ if descriptor.interface.isCallback():
+ (declType, declArgs,
+ conversion) = getCallbackConversionInfo(type, descriptor.interface,
+ isMember,
+ isCallbackReturnValue,
+ isOptional)
+ template = wrapObjectTemplate(conversion, type,
+ "${declName} = nullptr;\n",
+ failureCode)
+ return JSToNativeConversionInfo(template, declType=declType,
+ declArgs=declArgs,
+ dealWithOptional=isOptional)
+
+ # 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, and
+ # use an RefPtr or raw pointer for callback return values to make
+ # them easier to return.
+ argIsPointer = (type.nullable() or type.unroll().inner.isExternal() or
+ isCallbackReturnValue)
+
+ # Sequence and dictionary members, as well as owning unions (which can
+ # appear here as return values in JS-implemented interfaces) have to
+ # hold a strong ref to the thing being passed down. Those all set
+ # isMember.
+ #
+ # Also, callback return values always end up addrefing anyway, so there
+ # is no point trying to avoid it here and it makes other things simpler
+ # since we can assume the return value is a strong ref.
+ #
+ # Finally, promises need to hold a strong ref because that's what
+ # Promise.resolve returns.
+ assert not descriptor.interface.isCallback()
+ isPromise = descriptor.interface.identifier.name == "Promise"
+ forceOwningType = isMember or isCallbackReturnValue or isPromise
+
+ 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 = "RefPtr<" + typeName + ">"
+ else:
+ declType = typePtr
+ else:
+ if forceOwningType:
+ declType = "OwningNonNull<" + typeName + ">"
+ else:
+ declType = "NonNull<" + typeName + ">"
+
+ templateBody = ""
+ if forceOwningType:
+ templateBody += 'static_assert(IsRefcounted<%s>::value, "We can only store refcounted classes.");' % typeName
+
+ if isPromise:
+ # Per spec, what we're supposed to do is take the original
+ # Promise.resolve and call it with the original Promise as this
+ # value to make a Promise out of whatever value we actually have
+ # here. The question is which global we should use. There are
+ # several cases to consider:
+ #
+ # 1) Normal call to API with a Promise argument. This is a case the
+ # spec covers, and we should be using the current Realm's
+ # Promise. That means the current compartment.
+ # 2) Call to API with a Promise argument over Xrays. In practice,
+ # this sort of thing seems to be used for giving an API
+ # implementation a way to wait for conclusion of an asyc
+ # operation, _not_ to expose the Promise to content code. So we
+ # probably want to allow callers to use such an API in a
+ # "natural" way, by passing chrome-side promises; indeed, that
+ # may be all that the caller has to represent their async
+ # operation. That means we really need to do the
+ # Promise.resolve() in the caller (chrome) compartment: if we do
+ # it in the content compartment, we will try to call .then() on
+ # the chrome promise while in the content compartment, which will
+ # throw and we'll just get a rejected Promise. Note that this is
+ # also the reason why a caller who has a chrome Promise
+ # representing an async operation can't itself convert it to a
+ # content-side Promise (at least not without some serious
+ # gyrations).
+ # 3) Promise return value from a callback or callback interface.
+ # This is in theory a case the spec covers but in practice it
+ # really doesn't define behavior here because it doesn't define
+ # what Realm we're in after the callback returns, which is when
+ # the argument conversion happens. We will use the current
+ # compartment, which is the compartment of the callable (which
+ # may itself be a cross-compartment wrapper itself), which makes
+ # as much sense as anything else. In practice, such an API would
+ # once again be providing a Promise to signal completion of an
+ # operation, which would then not be exposed to anyone other than
+ # our own implementation code.
+ # 4) Return value from a JS-implemented interface. In this case we
+ # have a problem. Our current compartment is the compartment of
+ # the JS implementation. But if the JS implementation returned
+ # a page-side Promise (which is a totally sane thing to do, and
+ # in fact the right thing to do given that this return value is
+ # going right to content script) then we don't want to
+ # Promise.resolve with our current compartment Promise, because
+ # that will wrap it up in a chrome-side Promise, which is
+ # decidedly _not_ what's desired here. So in that case we
+ # should really unwrap the return value and use the global of
+ # the result. CheckedUnwrap should be good enough for that; if
+ # it fails, then we're failing unwrap while in a
+ # system-privileged compartment, so presumably we have a dead
+ # object wrapper. Just error out. Do NOT fall back to using
+ # the current compartment instead: that will return a
+ # system-privileged rejected (because getting .then inside
+ # resolve() failed) Promise to the caller, which they won't be
+ # able to touch. That's not helpful. If we error out, on the
+ # other hand, they will get a content-side rejected promise.
+ # Same thing if the value returned is not even an object.
+ if isCallbackReturnValue == "JSImpl":
+ # Case 4 above. Note that globalObj defaults to the current
+ # compartment global. Note that we don't use $*{exceptionCode}
+ # here because that will try to aRv.Throw(NS_ERROR_UNEXPECTED)
+ # which we don't really want here.
+ assert exceptionCode == "aRv.Throw(NS_ERROR_UNEXPECTED);\nreturn nullptr;\n"
+ getPromiseGlobal = fill(
+ """
+ if (!$${val}.isObject()) {
+ aRv.ThrowTypeError<MSG_NOT_OBJECT>(NS_LITERAL_STRING("${sourceDescription}"));
+ return nullptr;
+ }
+ JSObject* unwrappedVal = js::CheckedUnwrap(&$${val}.toObject());
+ if (!unwrappedVal) {
+ // A slight lie, but not much of one, for a dead object wrapper.
+ aRv.ThrowTypeError<MSG_NOT_OBJECT>(NS_LITERAL_STRING("${sourceDescription}"));
+ return nullptr;
+ }
+ globalObj = js::GetGlobalForObjectCrossCompartment(unwrappedVal);
+ """,
+ sourceDescription=sourceDescription)
+ else:
+ getPromiseGlobal = ""
+
+ templateBody = fill(
+ """
+ { // Scope for our GlobalObject, FastErrorResult, JSAutoCompartment,
+ // etc.
+
+ JS::Rooted<JSObject*> globalObj(cx, JS::CurrentGlobalOrNull(cx));
+ $*{getPromiseGlobal}
+ JSAutoCompartment ac(cx, globalObj);
+ GlobalObject promiseGlobal(cx, globalObj);
+ if (promiseGlobal.Failed()) {
+ $*{exceptionCode}
+ }
+
+ JS::Rooted<JS::Value> valueToResolve(cx, $${val});
+ if (!JS_WrapValue(cx, &valueToResolve)) {
+ $*{exceptionCode}
+ }
+ binding_detail::FastErrorResult promiseRv;
+ #ifdef SPIDERMONKEY_PROMISE
+ nsCOMPtr<nsIGlobalObject> global =
+ do_QueryInterface(promiseGlobal.GetAsSupports());
+ if (!global) {
+ promiseRv.Throw(NS_ERROR_UNEXPECTED);
+ promiseRv.MaybeSetPendingException(cx);
+ $*{exceptionCode}
+ }
+ $${declName} = Promise::Resolve(global, cx, valueToResolve,
+ promiseRv);
+ if (promiseRv.MaybeSetPendingException(cx)) {
+ $*{exceptionCode}
+ }
+ #else
+ JS::Handle<JSObject*> promiseCtor =
+ PromiseBinding::GetConstructorObjectHandle(cx);
+ if (!promiseCtor) {
+ $*{exceptionCode}
+ }
+ JS::Rooted<JS::Value> resolveThisv(cx, JS::ObjectValue(*promiseCtor));
+ JS::Rooted<JS::Value> resolveResult(cx);
+ Promise::Resolve(promiseGlobal, resolveThisv, valueToResolve,
+ &resolveResult, promiseRv);
+ if (promiseRv.MaybeSetPendingException(cx)) {
+ $*{exceptionCode}
+ }
+ nsresult unwrapRv = UNWRAP_OBJECT(Promise, &resolveResult.toObject(), $${declName});
+ if (NS_FAILED(unwrapRv)) { // Quite odd
+ promiseRv.Throw(unwrapRv);
+ promiseRv.MaybeSetPendingException(cx);
+ $*{exceptionCode}
+ }
+ #endif // SPIDERMONKEY_PROMISE
+ }
+ """,
+ getPromiseGlobal=getPromiseGlobal,
+ exceptionCode=exceptionCode)
+ elif not descriptor.interface.isConsequential() and not descriptor.interface.isExternal():
+ if failureCode is not None:
+ templateBody += str(CastableObjectUnwrapper(
+ descriptor,
+ "${val}",
+ "${maybeMutableVal}",
+ "${declName}",
+ failureCode))
+ else:
+ templateBody += str(FailureFatalCastableObjectUnwrapper(
+ descriptor,
+ "${val}",
+ "${maybeMutableVal}",
+ "${declName}",
+ exceptionCode,
+ isCallbackReturnValue,
+ firstCap(sourceDescription)))
+ 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(RefPtr) 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 += "RefPtr<" + typeName + "> ${holderName};\n"
+ else:
+ holderType = "RefPtr<" + typeName + ">"
+ templateBody += (
+ "JS::Rooted<JSObject*> source(cx, &${val}.toObject());\n" +
+ "if (NS_FAILED(UnwrapArg<" + typeName + ">(source, getter_AddRefs(${holderName})))) {\n")
+ templateBody += CGIndenter(onFailureBadType(failureCode,
+ descriptor.interface.identifier.name)).define()
+ templateBody += ("}\n"
+ "MOZ_ASSERT(${holderName});\n")
+
+ # And store our value in ${declName}
+ templateBody += "${declName} = ${holderName};\n"
+
+ if isPromise:
+ if type.nullable():
+ codeToSetNull = "${declName} = nullptr;\n"
+ templateBody = CGIfElseWrapper(
+ "${val}.isNullOrUndefined()",
+ CGGeneric(codeToSetNull),
+ CGGeneric(templateBody)).define()
+ if isinstance(defaultValue, IDLNullValue):
+ templateBody = handleDefault(templateBody, codeToSetNull)
+ else:
+ assert defaultValue is None
+ else:
+ # Just pass failureCode, not onFailureBadType, here, so we'll report
+ # the thing as not an object as opposed to not implementing whatever
+ # our interface is.
+ templateBody = wrapObjectTemplate(templateBody, type,
+ "${declName} = nullptr;\n",
+ failureCode)
+
+ declType = CGGeneric(declType)
+ if holderType is not None:
+ holderType = CGGeneric(holderType)
+ return JSToNativeConversionInfo(templateBody,
+ declType=declType,
+ holderType=holderType,
+ dealWithOptional=isOptional)
+
+ if type.isSpiderMonkeyInterface():
+ assert not isEnforceRange and not isClamp
+ name = type.unroll().name # unroll() because it may be nullable
+ arrayType = CGGeneric(name)
+ declType = arrayType
+ if type.nullable():
+ declType = CGTemplatedType("Nullable", declType)
+ objRef = "${declName}.SetValue()"
+ else:
+ objRef = "${declName}"
+
+ # Again, this is a bit strange since we are actually building a
+ # template string here. ${objRef} and $*{badType} below are filled in
+ # right now; $${val} expands to ${val}, to be filled in later.
+ template = fill(
+ """
+ if (!${objRef}.Init(&$${val}.toObject())) {
+ $*{badType}
+ }
+ """,
+ objRef=objRef,
+ badType=onFailureBadType(failureCode, type.name).define())
+ template = wrapObjectTemplate(template, type, "${declName}.SetNull();\n",
+ failureCode)
+ if not isMember:
+ # This is a bit annoying. In a union we don't want to have a
+ # holder, since unions don't support that. But if we're optional we
+ # want to have a holder, so that the callee doesn't see
+ # Optional<RootedTypedArray<ArrayType> >. So do a holder if we're
+ # optional and use a RootedTypedArray otherwise.
+ if isOptional:
+ holderType = CGTemplatedType("TypedArrayRooter", arrayType)
+ # If our typed array is nullable, this will set the Nullable to
+ # be not-null, but that's ok because we make an explicit
+ # SetNull() call on it as needed if our JS value is actually
+ # null. XXXbz Because "Maybe" takes const refs for constructor
+ # arguments, we can't pass a reference here; have to pass a
+ # pointer.
+ holderArgs = "cx, &%s" % objRef
+ declArgs = None
+ else:
+ holderType = None
+ holderArgs = None
+ declType = CGTemplatedType("RootedTypedArray", declType)
+ declArgs = "cx"
+ else:
+ holderType = None
+ holderArgs = None
+ declArgs = None
+ return JSToNativeConversionInfo(template,
+ declType=declType,
+ holderType=holderType,
+ dealWithOptional=isOptional,
+ declArgs=declArgs,
+ holderArgs=holderArgs)
+
+ if type.isDOMString() or type.isUSVString():
+ 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 also becomes a null string.
+ undefinedBehavior = "eNull"
+ else:
+ undefinedBehavior = "eStringify"
+ nullBehavior = treatAs[treatNullAs]
+
+ def getConversionCode(varName):
+ normalizeCode = ""
+ if type.isUSVString():
+ normalizeCode = "NormalizeUSVString(cx, %s);\n" % varName
+
+ conversionCode = fill("""
+ if (!ConvertJSValueToString(cx, $${val}, ${nullBehavior}, ${undefinedBehavior}, ${varName})) {
+ $*{exceptionCode}
+ }
+ $*{normalizeCode}
+ """
+ ,
+ nullBehavior=nullBehavior,
+ undefinedBehavior=undefinedBehavior,
+ varName=varName,
+ exceptionCode=exceptionCode,
+ normalizeCode=normalizeCode)
+
+ if defaultValue is None:
+ return conversionCode
+
+ if isinstance(defaultValue, IDLNullValue):
+ assert(type.nullable())
+ defaultCode = "%s.SetIsVoid(true)" % varName
+ else:
+ defaultCode = handleDefaultStringValue(defaultValue,
+ "%s.Rebind" % varName)
+ return handleDefault(conversionCode, defaultCode + ";\n")
+
+ if isMember:
+ # Convert directly into the nsString member we have.
+ declType = CGGeneric("nsString")
+ return JSToNativeConversionInfo(
+ getConversionCode("${declName}"),
+ declType=declType,
+ dealWithOptional=isOptional)
+
+ if isOptional:
+ declType = "Optional<nsAString>"
+ holderType = CGGeneric("binding_detail::FakeString")
+ conversionCode = ("%s"
+ "${declName} = &${holderName};\n" %
+ getConversionCode("${holderName}"))
+ else:
+ declType = "binding_detail::FakeString"
+ holderType = None
+ conversionCode = getConversionCode("${declName}")
+
+ # No need to deal with optional here; we handled it already
+ return JSToNativeConversionInfo(
+ conversionCode,
+ declType=CGGeneric(declType),
+ holderType=holderType)
+
+ if type.isByteString():
+ assert not isEnforceRange and not isClamp
+
+ nullable = toStringBool(type.nullable())
+
+ conversionCode = fill("""
+ if (!ConvertJSValueToByteString(cx, $${val}, ${nullable}, $${declName})) {
+ $*{exceptionCode}
+ }
+ """,
+ nullable=nullable,
+ exceptionCode=exceptionCode)
+
+ if defaultValue is not None:
+ if isinstance(defaultValue, IDLNullValue):
+ assert(type.nullable())
+ defaultCode = "${declName}.SetIsVoid(true)"
+ else:
+ defaultCode = handleDefaultStringValue(defaultValue,
+ "${declName}.Rebind")
+ conversionCode = handleDefault(conversionCode, defaultCode + ";\n")
+
+ return JSToNativeConversionInfo(
+ conversionCode,
+ declType=CGGeneric("nsCString"),
+ dealWithOptional=isOptional)
+
+ if type.isEnum():
+ assert not isEnforceRange and not isClamp
+
+ enumName = type.unroll().inner.identifier.name
+ declType = CGGeneric(enumName)
+ if type.nullable():
+ declType = CGTemplatedType("Nullable", declType)
+ declType = declType.define()
+ enumLoc = "${declName}.SetValue()"
+ else:
+ enumLoc = "${declName}"
+ declType = declType.define()
+
+ if invalidEnumValueFatal:
+ handleInvalidEnumValueCode = "MOZ_ASSERT(index >= 0);\n"
+ else:
+ # invalidEnumValueFatal is false only for attributes. So we won't
+ # have a non-default exceptionCode here unless attribute "arg
+ # conversion" code starts passing in an exceptionCode. At which
+ # point we'll need to figure out what that even means.
+ assert exceptionCode == "return false;\n"
+ handleInvalidEnumValueCode = dedent("""
+ if (index < 0) {
+ return true;
+ }
+ """)
+
+ template = fill(
+ """
+ {
+ int index;
+ if (!FindEnumStringIndex<${invalidEnumValueFatal}>(cx, $${val}, ${values}, "${enumtype}", "${sourceDescription}", &index)) {
+ $*{exceptionCode}
+ }
+ $*{handleInvalidEnumValueCode}
+ ${enumLoc} = static_cast<${enumtype}>(index);
+ }
+ """,
+ enumtype=enumName,
+ values=enumName + "Values::" + ENUM_ENTRY_VARIABLE_NAME,
+ invalidEnumValueFatal=toStringBool(invalidEnumValueFatal),
+ handleInvalidEnumValueCode=handleInvalidEnumValueCode,
+ exceptionCode=exceptionCode,
+ enumLoc=enumLoc,
+ sourceDescription=firstCap(sourceDescription))
+
+ setNull = "${declName}.SetNull();\n"
+
+ if type.nullable():
+ template = CGIfElseWrapper("${val}.isNullOrUndefined()",
+ CGGeneric(setNull),
+ CGGeneric(template)).define()
+
+ if defaultValue is not None:
+ if isinstance(defaultValue, IDLNullValue):
+ assert type.nullable()
+ template = handleDefault(template, setNull)
+ else:
+ assert(defaultValue.type.tag() == IDLType.Tags.domstring)
+ template = handleDefault(template,
+ ("%s = %s::%s;\n" %
+ (enumLoc, enumName,
+ getEnumValueName(defaultValue.value))))
+ return JSToNativeConversionInfo(template, declType=CGGeneric(declType),
+ dealWithOptional=isOptional)
+
+ if type.isCallback():
+ assert not isEnforceRange and not isClamp
+ assert not type.treatNonCallableAsNull() or type.nullable()
+ assert not type.treatNonObjectAsNull() or type.nullable()
+ assert not type.treatNonObjectAsNull() or not type.treatNonCallableAsNull()
+
+ callback = type.unroll().callback
+ name = callback.identifier.name
+ (declType, declArgs,
+ conversion) = getCallbackConversionInfo(type, callback, isMember,
+ isCallbackReturnValue,
+ isOptional)
+
+ if allowTreatNonCallableAsNull and type.treatNonCallableAsNull():
+ haveCallable = "JS::IsCallable(&${val}.toObject())"
+ if not isDefinitelyObject:
+ haveCallable = "${val}.isObject() && " + haveCallable
+ if defaultValue is not None:
+ assert(isinstance(defaultValue, IDLNullValue))
+ haveCallable = "${haveValue} && " + haveCallable
+ template = (
+ ("if (%s) {\n" % haveCallable) +
+ conversion +
+ "} else {\n"
+ " ${declName} = nullptr;\n"
+ "}\n")
+ elif allowTreatNonCallableAsNull and type.treatNonObjectAsNull():
+ if not isDefinitelyObject:
+ haveObject = "${val}.isObject()"
+ if defaultValue is not None:
+ assert(isinstance(defaultValue, IDLNullValue))
+ haveObject = "${haveValue} && " + haveObject
+ template = CGIfElseWrapper(haveObject,
+ CGGeneric(conversion),
+ CGGeneric("${declName} = nullptr;\n")).define()
+ else:
+ template = conversion
+ else:
+ template = wrapObjectTemplate(
+ "if (JS::IsCallable(&${val}.toObject())) {\n" +
+ conversion +
+ "} else {\n" +
+ indent(onFailureNotCallable(failureCode).define()) +
+ "}\n",
+ type,
+ "${declName} = nullptr;\n",
+ failureCode)
+ return JSToNativeConversionInfo(template, declType=declType,
+ declArgs=declArgs,
+ dealWithOptional=isOptional)
+
+ if type.isAny():
+ assert not isEnforceRange and not isClamp
+
+ declArgs = None
+ if isMember in ("Variadic", "Sequence", "Dictionary", "MozMap"):
+ # Rooting is handled by the sequence and dictionary tracers.
+ declType = "JS::Value"
+ else:
+ assert not isMember
+ declType = "JS::Rooted<JS::Value>"
+ declArgs = "cx"
+
+ assert not isOptional
+ templateBody = "${declName} = ${val};\n"
+
+ # For JS-implemented APIs, we refuse to allow passing objects that the
+ # API consumer does not subsume. The extra parens around
+ # ($${passedToJSImpl}) suppress unreachable code warnings when
+ # $${passedToJSImpl} is the literal `false`.
+ if not isinstance(descriptorProvider, Descriptor) or descriptorProvider.interface.isJSImplemented():
+ templateBody = fill(
+ """
+ if (($${passedToJSImpl}) && !CallerSubsumes($${val})) {
+ ThrowErrorMessage(cx, MSG_PERMISSION_DENIED_TO_PASS_ARG, "${sourceDescription}");
+ $*{exceptionCode}
+ }
+ """,
+ sourceDescription=sourceDescription,
+ exceptionCode=exceptionCode) + templateBody
+
+ # We may not have a default value if we're being converted for
+ # a setter, say.
+ if defaultValue:
+ if isinstance(defaultValue, IDLNullValue):
+ defaultHandling = "${declName} = JS::NullValue();\n"
+ else:
+ assert isinstance(defaultValue, IDLUndefinedValue)
+ defaultHandling = "${declName} = JS::UndefinedValue();\n"
+ templateBody = handleDefault(templateBody, defaultHandling)
+ return JSToNativeConversionInfo(templateBody,
+ declType=CGGeneric(declType),
+ declArgs=declArgs)
+
+ if type.isObject():
+ assert not isEnforceRange and not isClamp
+ return handleJSObjectType(type, isMember, failureCode, exceptionCode, sourceDescription)
+
+ if type.isDictionary():
+ # There are no nullable dictionaries
+ assert not type.nullable() or isCallbackReturnValue
+ # All optional dictionaries always have default values, so we
+ # should be able to assume not isOptional here.
+ assert not isOptional
+ # In the callback return value case we never have to worry
+ # about a default value; we always have a value.
+ assert not isCallbackReturnValue or defaultValue is None
+
+ typeName = CGDictionary.makeDictionaryName(type.unroll().inner)
+ if not isMember and not isCallbackReturnValue:
+ # Since we're not a member and not nullable or optional, no one will
+ # see our real type, so we can do the fast version of the dictionary
+ # that doesn't pre-initialize members.
+ typeName = "binding_detail::Fast" + typeName
+
+ declType = CGGeneric(typeName)
+
+ # We do manual default value handling here, because we
+ # actually do want a jsval, and we only handle null anyway
+ # NOTE: if isNullOrUndefined or isDefinitelyObject are true,
+ # we know we have a value, so we don't have to worry about the
+ # default value.
+ if (not isNullOrUndefined and not isDefinitelyObject and
+ defaultValue is not None):
+ assert(isinstance(defaultValue, IDLNullValue))
+ val = "(${haveValue}) ? ${val} : JS::NullHandleValue"
+ else:
+ val = "${val}"
+
+ dictLoc = "${declName}"
+ if type.nullable():
+ dictLoc += ".SetValue()"
+
+ conversionCode = fill("""
+ if (!${dictLoc}.Init(cx, ${val}, "${desc}", $${passedToJSImpl})) {
+ $*{exceptionCode}
+ }
+ """,
+ dictLoc=dictLoc,
+ val=val,
+ desc=firstCap(sourceDescription),
+ exceptionCode=exceptionCode)
+
+ if failureCode is not None:
+ if isDefinitelyObject:
+ dictionaryTest = "IsObjectValueConvertibleToDictionary"
+ else:
+ dictionaryTest = "IsConvertibleToDictionary"
+
+ template = fill("""
+ { // scope for isConvertible
+ bool isConvertible;
+ if (!${testConvertible}(cx, ${val}, &isConvertible)) {
+ $*{exceptionCode}
+ }
+ if (!isConvertible) {
+ $*{failureCode}
+ }
+
+ $*{conversionCode}
+ }
+
+ """,
+ testConvertible=dictionaryTest,
+ val=val,
+ exceptionCode=exceptionCode,
+ failureCode=failureCode,
+ conversionCode=conversionCode)
+ else:
+ template = conversionCode
+
+ if type.nullable():
+ declType = CGTemplatedType("Nullable", declType)
+ template = CGIfElseWrapper("${val}.isNullOrUndefined()",
+ CGGeneric("${declName}.SetNull();\n"),
+ CGGeneric(template)).define()
+
+ # Dictionary arguments that might contain traceable things need to get
+ # traced
+ if not isMember and isCallbackReturnValue:
+ # Go ahead and just convert directly into our actual return value
+ declType = CGWrapper(declType, post="&")
+ declArgs = "aRetVal"
+ elif not isMember and typeNeedsRooting(type):
+ declType = CGTemplatedType("RootedDictionary", declType)
+ declArgs = "cx"
+ else:
+ declArgs = None
+
+ return JSToNativeConversionInfo(template, declType=declType,
+ declArgs=declArgs)
+
+ if type.isVoid():
+ assert not isOptional
+ # This one only happens for return values, and its easy: Just
+ # ignore the jsval.
+ return JSToNativeConversionInfo("")
+
+ if type.isDate():
+ assert not isEnforceRange and not isClamp
+
+ declType = CGGeneric("Date")
+ if type.nullable():
+ declType = CGTemplatedType("Nullable", declType)
+ dateVal = "${declName}.SetValue()"
+ else:
+ dateVal = "${declName}"
+
+ if failureCode is None:
+ notDate = ('ThrowErrorMessage(cx, MSG_NOT_DATE, "%s");\n'
+ "%s" % (firstCap(sourceDescription), exceptionCode))
+ else:
+ notDate = failureCode
+
+ conversion = fill(
+ """
+ JS::Rooted<JSObject*> possibleDateObject(cx, &$${val}.toObject());
+ { // scope for isDate
+ bool isDate;
+ if (!JS_ObjectIsDate(cx, possibleDateObject, &isDate)) {
+ $*{exceptionCode}
+ }
+ if (!isDate) {
+ $*{notDate}
+ }
+ if (!${dateVal}.SetTimeStamp(cx, possibleDateObject)) {
+ $*{exceptionCode}
+ }
+ }
+ """,
+ exceptionCode=exceptionCode,
+ dateVal=dateVal,
+ notDate=notDate)
+
+ conversion = wrapObjectTemplate(conversion, type,
+ "${declName}.SetNull();\n", notDate)
+ return JSToNativeConversionInfo(conversion,
+ declType=declType,
+ dealWithOptional=isOptional)
+
+ if not type.isPrimitive():
+ raise TypeError("Need conversion for argument type '%s'" % str(type))
+
+ typeName = builtinNames[type.tag()]
+
+ conversionBehavior = "eDefault"
+ if isEnforceRange:
+ assert type.isInteger()
+ conversionBehavior = "eEnforceRange"
+ elif isClamp:
+ assert type.isInteger()
+ conversionBehavior = "eClamp"
+
+ if type.nullable():
+ declType = CGGeneric("Nullable<" + typeName + ">")
+ writeLoc = "${declName}.SetValue()"
+ readLoc = "${declName}.Value()"
+ nullCondition = "${val}.isNullOrUndefined()"
+ if defaultValue is not None and isinstance(defaultValue, IDLNullValue):
+ nullCondition = "!(${haveValue}) || " + nullCondition
+ template = fill("""
+ if (${nullCondition}) {
+ $${declName}.SetNull();
+ } else if (!ValueToPrimitive<${typeName}, ${conversionBehavior}>(cx, $${val}, &${writeLoc})) {
+ $*{exceptionCode}
+ }
+ """,
+ nullCondition=nullCondition,
+ typeName=typeName,
+ conversionBehavior=conversionBehavior,
+ writeLoc=writeLoc,
+ exceptionCode=exceptionCode)
+ else:
+ assert(defaultValue is None or
+ not isinstance(defaultValue, IDLNullValue))
+ writeLoc = "${declName}"
+ readLoc = writeLoc
+ template = fill("""
+ if (!ValueToPrimitive<${typeName}, ${conversionBehavior}>(cx, $${val}, &${writeLoc})) {
+ $*{exceptionCode}
+ }
+ """,
+ typeName=typeName,
+ conversionBehavior=conversionBehavior,
+ writeLoc=writeLoc,
+ exceptionCode=exceptionCode)
+ declType = CGGeneric(typeName)
+
+ if type.isFloat() and not type.isUnrestricted():
+ if lenientFloatCode is not None:
+ nonFiniteCode = lenientFloatCode
+ else:
+ nonFiniteCode = ('ThrowErrorMessage(cx, MSG_NOT_FINITE, "%s");\n'
+ "%s" % (firstCap(sourceDescription), exceptionCode))
+
+ # We're appending to an if-block brace, so strip trailing whitespace
+ # and add an extra space before the else.
+ template = template.rstrip()
+ template += fill("""
+ else if (!mozilla::IsFinite(${readLoc})) {
+ $*{nonFiniteCode}
+ }
+ """,
+ readLoc=readLoc,
+ nonFiniteCode=nonFiniteCode)
+
+ 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()
+ defaultStr = getHandleDefault(defaultValue)
+ template = CGIfElseWrapper(
+ "${haveValue}",
+ CGGeneric(template),
+ CGGeneric("%s = %s;\n" % (writeLoc, defaultStr))).define()
+
+ return JSToNativeConversionInfo(template, declType=declType,
+ dealWithOptional=isOptional)
+
+
+def instantiateJSToNativeConversion(info, replacements, checkForValue=False):
+ """
+ Take a JSToNativeConversionInfo as returned by getJSToNativeConversionInfo
+ and a set of replacements as required by the strings in such an object, and
+ generate code to convert into stack C++ types.
+
+ If checkForValue is True, then the conversion will get wrapped in
+ a check for ${haveValue}.
+ """
+ templateBody, declType, holderType, dealWithOptional = (
+ info.template, info.declType, info.holderType, info.dealWithOptional)
+
+ if dealWithOptional and not checkForValue:
+ raise TypeError("Have to deal with optional things, but don't know how")
+ if checkForValue and declType is None:
+ raise TypeError("Need to predeclare optional things, so they will be "
+ "outside the check for big enough arg count!")
+
+ # We can't precompute our holder constructor arguments, since
+ # those might depend on ${declName}, which we change below. Just
+ # compute arguments at the point when we need them as we go.
+ def getArgsCGThing(args):
+ return CGGeneric(string.Template(args).substitute(replacements))
+
+ result = CGList([])
+ # Make a copy of "replacements" since we may be about to start modifying it
+ replacements = dict(replacements)
+ originalDeclName = replacements["declName"]
+ if declType is not None:
+ if dealWithOptional:
+ replacements["declName"] = "%s.Value()" % originalDeclName
+ declType = CGTemplatedType("Optional", declType)
+ declCtorArgs = None
+ elif info.declArgs is not None:
+ declCtorArgs = CGWrapper(getArgsCGThing(info.declArgs),
+ pre="(", post=")")
+ else:
+ declCtorArgs = None
+ result.append(
+ CGList([declType, CGGeneric(" "),
+ CGGeneric(originalDeclName),
+ declCtorArgs, CGGeneric(";\n")]))
+
+ originalHolderName = replacements["holderName"]
+ if holderType is not None:
+ if dealWithOptional:
+ replacements["holderName"] = "%s.ref()" % originalHolderName
+ holderType = CGTemplatedType("Maybe", holderType)
+ holderCtorArgs = None
+ elif info.holderArgs is not None:
+ holderCtorArgs = CGWrapper(getArgsCGThing(info.holderArgs),
+ pre="(", post=")")
+ else:
+ holderCtorArgs = None
+ result.append(
+ CGList([holderType, CGGeneric(" "),
+ CGGeneric(originalHolderName),
+ holderCtorArgs, CGGeneric(";\n")]))
+
+ if "maybeMutableVal" not in replacements:
+ replacements["maybeMutableVal"] = replacements["val"]
+
+ conversion = CGGeneric(
+ string.Template(templateBody).substitute(replacements))
+
+ if checkForValue:
+ if dealWithOptional:
+ declConstruct = CGIndenter(
+ CGGeneric("%s.Construct(%s);\n" %
+ (originalDeclName,
+ getArgsCGThing(info.declArgs).define() if
+ info.declArgs else "")))
+ if holderType is not None:
+ holderConstruct = CGIndenter(
+ CGGeneric("%s.emplace(%s);\n" %
+ (originalHolderName,
+ getArgsCGThing(info.holderArgs).define() if
+ info.holderArgs else "")))
+ else:
+ holderConstruct = None
+ else:
+ declConstruct = None
+ holderConstruct = None
+
+ conversion = CGList([
+ CGGeneric(
+ string.Template("if (${haveValue}) {\n").substitute(replacements)),
+ declConstruct,
+ holderConstruct,
+ CGIndenter(conversion),
+ CGGeneric("}\n")
+ ])
+
+ result.append(conversion)
+ return result
+
+
+def convertConstIDLValueToJSVal(value):
+ if isinstance(value, IDLNullValue):
+ return "JS::NullValue()"
+ if isinstance(value, IDLUndefinedValue):
+ return "JS::UndefinedValue()"
+ tag = value.type.tag()
+ if tag in [IDLType.Tags.int8, IDLType.Tags.uint8, IDLType.Tags.int16,
+ IDLType.Tags.uint16, IDLType.Tags.int32]:
+ return "JS::Int32Value(%s)" % (value.value)
+ if tag == IDLType.Tags.uint32:
+ return "JS::NumberValue(%sU)" % (value.value)
+ if tag in [IDLType.Tags.int64, IDLType.Tags.uint64]:
+ return "JS::CanonicalizedDoubleValue(%s)" % numericValue(tag, value.value)
+ if tag == IDLType.Tags.bool:
+ return "JS::BooleanValue(true)" if value.value else "JS::BooleanValue(false)"
+ if tag in [IDLType.Tags.float, IDLType.Tags.double]:
+ return "JS::CanonicalizedDoubleValue(%s)" % (value.value)
+ raise TypeError("Const value of unhandled type: %s" % value.type)
+
+
+class CGArgumentConverter(CGThing):
+ """
+ A class that takes an IDL argument object and its index in the
+ argument list and generates code to unwrap the argument to the
+ right native type.
+
+ argDescription is a description of the argument for error-reporting
+ purposes. Callers should assume that it might get placed in the middle of a
+ sentence. If it ends up at the beginning of a sentence, its first character
+ will be automatically uppercased.
+ """
+ def __init__(self, argument, index, descriptorProvider,
+ argDescription, member,
+ invalidEnumValueFatal=True, lenientFloatCode=None):
+ CGThing.__init__(self)
+ self.argument = argument
+ self.argDescription = argDescription
+ assert(not argument.defaultValue or argument.optional)
+
+ replacer = {
+ "index": index,
+ "argc": "args.length()"
+ }
+ self.replacementVariables = {
+ "declName": "arg%d" % index,
+ "holderName": ("arg%d" % index) + "_holder",
+ "obj": "obj",
+ "passedToJSImpl": toStringBool(isJSImplementedDescriptor(descriptorProvider))
+ }
+ # If we have a method generated by the maplike/setlike portion of an
+ # interface, arguments can possibly be undefined, but will need to be
+ # converted to the key/value type of the backing object. In this case,
+ # use .get() instead of direct access to the argument. This won't
+ # matter for iterable since generated functions for those interface
+ # don't take arguments.
+ if member.isMethod() and member.isMaplikeOrSetlikeOrIterableMethod():
+ self.replacementVariables["val"] = string.Template(
+ "args.get(${index})").substitute(replacer)
+ self.replacementVariables["maybeMutableVal"] = string.Template(
+ "args[${index}]").substitute(replacer)
+ else:
+ self.replacementVariables["val"] = string.Template(
+ "args[${index}]").substitute(replacer)
+ haveValueCheck = string.Template(
+ "args.hasDefined(${index})").substitute(replacer)
+ self.replacementVariables["haveValue"] = haveValueCheck
+ self.descriptorProvider = descriptorProvider
+ if self.argument.canHaveMissingValue():
+ self.argcAndIndex = replacer
+ else:
+ self.argcAndIndex = None
+ self.invalidEnumValueFatal = invalidEnumValueFatal
+ self.lenientFloatCode = lenientFloatCode
+
+ def define(self):
+ typeConversion = getJSToNativeConversionInfo(
+ self.argument.type,
+ self.descriptorProvider,
+ isOptional=(self.argcAndIndex is not None and
+ not self.argument.variadic),
+ invalidEnumValueFatal=self.invalidEnumValueFatal,
+ defaultValue=self.argument.defaultValue,
+ treatNullAs=self.argument.treatNullAs,
+ isEnforceRange=self.argument.enforceRange,
+ isClamp=self.argument.clamp,
+ lenientFloatCode=self.lenientFloatCode,
+ isMember="Variadic" if self.argument.variadic else False,
+ allowTreatNonCallableAsNull=self.argument.allowTreatNonCallableAsNull(),
+ sourceDescription=self.argDescription)
+
+ if not self.argument.variadic:
+ return instantiateJSToNativeConversion(
+ typeConversion,
+ self.replacementVariables,
+ self.argcAndIndex is not None).define()
+
+ # Variadic arguments get turned into a sequence.
+ if typeConversion.dealWithOptional:
+ raise TypeError("Shouldn't have optional things in variadics")
+ if typeConversion.holderType is not None:
+ raise TypeError("Shouldn't need holders for variadics")
+
+ replacer = dict(self.argcAndIndex, **self.replacementVariables)
+ replacer["seqType"] = CGTemplatedType("binding_detail::AutoSequence",
+ typeConversion.declType).define()
+ if typeNeedsRooting(self.argument.type):
+ rooterDecl = ("SequenceRooter<%s> ${holderName}(cx, &${declName});\n" %
+ typeConversion.declType.define())
+ else:
+ rooterDecl = ""
+ replacer["elemType"] = typeConversion.declType.define()
+
+ # NOTE: Keep this in sync with sequence conversions as needed
+ variadicConversion = string.Template(
+ "${seqType} ${declName};\n" +
+ rooterDecl +
+ dedent("""
+ if (${argc} > ${index}) {
+ if (!${declName}.SetCapacity(${argc} - ${index}, mozilla::fallible)) {
+ JS_ReportOutOfMemory(cx);
+ return false;
+ }
+ for (uint32_t variadicArg = ${index}; variadicArg < ${argc}; ++variadicArg) {
+ ${elemType}& slot = *${declName}.AppendElement(mozilla::fallible);
+ """)
+ ).substitute(replacer)
+
+ val = string.Template("args[variadicArg]").substitute(replacer)
+ variadicConversion += indent(
+ string.Template(typeConversion.template).substitute({
+ "val": val,
+ "maybeMutableVal": val,
+ "declName": "slot",
+ # We only need holderName here to handle isExternal()
+ # interfaces, which use an internal holder for the
+ # conversion even when forceOwningType ends up true.
+ "holderName": "tempHolder",
+ # Use the same ${obj} as for the variadic arg itself
+ "obj": replacer["obj"],
+ "passedToJSImpl": toStringBool(isJSImplementedDescriptor(self.descriptorProvider))
+ }), 4)
+
+ variadicConversion += (" }\n"
+ "}\n")
+ return variadicConversion
+
+
+def getMaybeWrapValueFuncForType(type):
+ # Callbacks might actually be DOM objects; nothing prevents a page from
+ # doing that.
+ if type.isCallback() or type.isCallbackInterface() or type.isObject():
+ if type.nullable():
+ return "MaybeWrapObjectOrNullValue"
+ return "MaybeWrapObjectValue"
+ # Spidermonkey interfaces are never DOM objects. Neither are sequences or
+ # dictionaries, since those are always plain JS objects.
+ if type.isSpiderMonkeyInterface() or type.isDictionary() or type.isSequence():
+ if type.nullable():
+ return "MaybeWrapNonDOMObjectOrNullValue"
+ return "MaybeWrapNonDOMObjectValue"
+ if type.isAny():
+ return "MaybeWrapValue"
+
+ # For other types, just go ahead an fall back on MaybeWrapValue for now:
+ # it's always safe to do, and shouldn't be particularly slow for any of
+ # them
+ return "MaybeWrapValue"
+
+
+sequenceWrapLevel = 0
+mozMapWrapLevel = 0
+
+
+def getWrapTemplateForType(type, descriptorProvider, result, successCode,
+ returnsNewObject, exceptionCode, typedArraysAreStructs,
+ isConstructorRetval=False):
+ """
+ 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 and must guarantee that execution of the conversion template
+ stops once the successCode has executed (e.g. by doing a 'return', or by
+ doing a 'break' if the entire conversion template is inside a block that
+ the 'break' will exit).
+
+ If typedArraysAreStructs is true, then if the type is a typed array,
+ "result" is one of the dom::TypedArray subclasses, not a JSObject*.
+
+ The resulting string should be used with string.Template. It
+ needs the following keys when substituting:
+
+ jsvalHandle: something that can be passed to methods taking a
+ JS::MutableHandle<JS::Value>. This can be a
+ JS::MutableHandle<JS::Value> or a JS::Rooted<JS::Value>*.
+ jsvalRef: something that can have .address() called on it to get a
+ JS::Value* and .set() called on it to set it to a JS::Value.
+ This can be a JS::MutableHandle<JS::Value> or a
+ JS::Rooted<JS::Value>.
+ obj: a JS::Handle<JSObject*>.
+
+ Returns (templateString, infallibility of conversion template)
+ """
+ if successCode is None:
+ successCode = "return true;\n"
+
+ def setUndefined():
+ return _setValue("", setter="setUndefined")
+
+ def setNull():
+ return _setValue("", setter="setNull")
+
+ def setInt32(value):
+ return _setValue(value, setter="setInt32")
+
+ def setString(value):
+ return _setValue(value, setter="setString")
+
+ def setObject(value, wrapAsType=None):
+ return _setValue(value, wrapAsType=wrapAsType, setter="setObject")
+
+ def setObjectOrNull(value, wrapAsType=None):
+ return _setValue(value, wrapAsType=wrapAsType, setter="setObjectOrNull")
+
+ def setUint32(value):
+ return _setValue(value, setter="setNumber")
+
+ def setDouble(value):
+ return _setValue("JS_NumberValue(%s)" % value)
+
+ def setBoolean(value):
+ return _setValue(value, setter="setBoolean")
+
+ def _setValue(value, wrapAsType=None, setter="set"):
+ """
+ Returns the code to set the jsval to value.
+
+ If wrapAsType is not None, then will wrap the resulting value using the
+ function that getMaybeWrapValueFuncForType(wrapAsType) returns.
+ Otherwise, no wrapping will be done.
+ """
+ if wrapAsType is None:
+ tail = successCode
+ else:
+ tail = fill(
+ """
+ if (!${maybeWrap}(cx, $${jsvalHandle})) {
+ $*{exceptionCode}
+ }
+ $*{successCode}
+ """,
+ maybeWrap=getMaybeWrapValueFuncForType(wrapAsType),
+ exceptionCode=exceptionCode,
+ successCode=successCode)
+ return ("${jsvalRef}.%s(%s);\n" % (setter, value)) + tail
+
+ 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:
+ failureCode = exceptionCode
+ return fill(
+ """
+ if (!${wrapCall}) {
+ $*{failureCode}
+ }
+ $*{successCode}
+ """,
+ wrapCall=wrapCall,
+ failureCode=failureCode,
+ successCode=successCode)
+
+ if type is None or type.isVoid():
+ return (setUndefined(), True)
+
+ if (type.isSequence() or type.isMozMap()) and type.nullable():
+ # These are both wrapped in Nullable<>
+ recTemplate, recInfall = getWrapTemplateForType(type.inner, descriptorProvider,
+ "%s.Value()" % result, successCode,
+ returnsNewObject, exceptionCode,
+ typedArraysAreStructs)
+ code = fill(
+ """
+
+ if (${result}.IsNull()) {
+ $*{setNull}
+ }
+ $*{recTemplate}
+ """,
+ result=result,
+ setNull=setNull(),
+ recTemplate=recTemplate)
+ return code, recInfall
+
+ if type.isSequence():
+ # Now do non-nullable sequences. Our success code is just to break to
+ # where we set the element in the array. Note that we bump the
+ # sequenceWrapLevel around this call so that nested sequence conversions
+ # will use different iteration variables.
+ global sequenceWrapLevel
+ index = "sequenceIdx%d" % sequenceWrapLevel
+ sequenceWrapLevel += 1
+ innerTemplate = wrapForType(
+ type.inner, descriptorProvider,
+ {
+ 'result': "%s[%s]" % (result, index),
+ 'successCode': "break;\n",
+ 'jsvalRef': "tmp",
+ 'jsvalHandle': "&tmp",
+ 'returnsNewObject': returnsNewObject,
+ 'exceptionCode': exceptionCode,
+ 'obj': "returnArray",
+ 'typedArraysAreStructs': typedArraysAreStructs
+ })
+ sequenceWrapLevel -= 1
+ code = fill(
+ """
+
+ uint32_t length = ${result}.Length();
+ JS::Rooted<JSObject*> returnArray(cx, JS_NewArrayObject(cx, length));
+ if (!returnArray) {
+ $*{exceptionCode}
+ }
+ // Scope for 'tmp'
+ {
+ JS::Rooted<JS::Value> tmp(cx);
+ for (uint32_t ${index} = 0; ${index} < length; ++${index}) {
+ // Control block to let us common up the JS_DefineElement calls when there
+ // are different ways to succeed at wrapping the object.
+ do {
+ $*{innerTemplate}
+ } while (0);
+ if (!JS_DefineElement(cx, returnArray, ${index}, tmp,
+ JSPROP_ENUMERATE)) {
+ $*{exceptionCode}
+ }
+ }
+ }
+ $*{set}
+ """,
+ result=result,
+ exceptionCode=exceptionCode,
+ index=index,
+ innerTemplate=innerTemplate,
+ set=setObject("*returnArray"))
+
+ return (code, False)
+
+ if type.isMozMap():
+ # Now do non-nullable MozMap. Our success code is just to break to
+ # where we define the property on the object. Note that we bump the
+ # mozMapWrapLevel around this call so that nested MozMap conversions
+ # will use different temp value names.
+ global mozMapWrapLevel
+ valueName = "mozMapValue%d" % mozMapWrapLevel
+ mozMapWrapLevel += 1
+ innerTemplate = wrapForType(
+ type.inner, descriptorProvider,
+ {
+ 'result': valueName,
+ 'successCode': "break;\n",
+ 'jsvalRef': "tmp",
+ 'jsvalHandle': "&tmp",
+ 'returnsNewObject': returnsNewObject,
+ 'exceptionCode': exceptionCode,
+ 'obj': "returnObj",
+ 'typedArraysAreStructs': typedArraysAreStructs
+ })
+ mozMapWrapLevel -= 1
+ code = fill(
+ """
+
+ nsTArray<nsString> keys;
+ ${result}.GetKeys(keys);
+ JS::Rooted<JSObject*> returnObj(cx, JS_NewPlainObject(cx));
+ if (!returnObj) {
+ $*{exceptionCode}
+ }
+ // Scope for 'tmp'
+ {
+ JS::Rooted<JS::Value> tmp(cx);
+ for (size_t idx = 0; idx < keys.Length(); ++idx) {
+ auto& ${valueName} = ${result}.Get(keys[idx]);
+ // Control block to let us common up the JS_DefineUCProperty calls when there
+ // are different ways to succeed at wrapping the value.
+ do {
+ $*{innerTemplate}
+ } while (0);
+ if (!JS_DefineUCProperty(cx, returnObj, keys[idx].get(),
+ keys[idx].Length(), tmp,
+ JSPROP_ENUMERATE)) {
+ $*{exceptionCode}
+ }
+ }
+ }
+ $*{set}
+ """,
+ result=result,
+ exceptionCode=exceptionCode,
+ valueName=valueName,
+ innerTemplate=innerTemplate,
+ set=setObject("*returnObj"))
+
+ return (code, False)
+
+ if type.isGeckoInterface() and not type.isCallbackInterface():
+ descriptor = descriptorProvider.getDescriptor(type.unroll().inner.identifier.name)
+ if type.nullable():
+ wrappingCode = ("if (!%s) {\n" % (result) +
+ indent(setNull()) +
+ "}\n")
+ else:
+ wrappingCode = ""
+
+ if not descriptor.interface.isExternal():
+ if descriptor.wrapperCache:
+ wrapMethod = "GetOrCreateDOMReflector"
+ wrapArgs = "cx, %s, ${jsvalHandle}" % result
+ else:
+ # Hack: the "Promise" interface is OK to return from
+ # non-newobject things even when it's not wrappercached; that
+ # happens when using SpiderMonkey promises, and the WrapObject()
+ # method will just return the existing reflector, which is just
+ # not stored in a wrappercache.
+ if (not returnsNewObject and
+ descriptor.interface.identifier.name != "Promise"):
+ raise MethodNotNewObjectError(descriptor.interface.identifier.name)
+ wrapMethod = "WrapNewBindingNonWrapperCachedObject"
+ wrapArgs = "cx, ${obj}, %s, ${jsvalHandle}" % result
+ if isConstructorRetval:
+ wrapArgs += ", desiredProto"
+ wrap = "%s(%s)" % (wrapMethod, wrapArgs)
+ if not descriptor.hasXPConnectImpls:
+ # Can only fail to wrap as a new-binding object
+ # if they already threw an exception.
+ # XXX Assertion disabled for now, see bug 991271.
+ failed = ("MOZ_ASSERT(true || JS_IsExceptionPending(cx));\n" +
+ exceptionCode)
+ else:
+ if descriptor.notflattened:
+ raise TypeError("%s has XPConnect impls but not flattened; "
+ "fallback won't work correctly" %
+ descriptor.interface.identifier.name)
+ # Try old-style wrapping for bindings which might be XPConnect impls.
+ failed = wrapAndSetPtr("HandleNewBindingWrappingFailure(cx, ${obj}, %s, ${jsvalHandle})" % result)
+ else:
+ if descriptor.notflattened:
+ getIID = "&NS_GET_IID(%s), " % descriptor.nativeType
+ else:
+ getIID = ""
+ wrap = "WrapObject(cx, %s, %s${jsvalHandle})" % (result, getIID)
+ failed = None
+
+ wrappingCode += wrapAndSetPtr(wrap, failed)
+ return (wrappingCode, False)
+
+ if type.isDOMString() or type.isUSVString():
+ if type.nullable():
+ return (wrapAndSetPtr("xpc::StringToJsval(cx, %s, ${jsvalHandle})" % result), False)
+ else:
+ return (wrapAndSetPtr("xpc::NonVoidStringToJsval(cx, %s, ${jsvalHandle})" % result), False)
+
+ if type.isByteString():
+ if type.nullable():
+ return (wrapAndSetPtr("ByteStringToJsval(cx, %s, ${jsvalHandle})" % result), False)
+ else:
+ return (wrapAndSetPtr("NonVoidByteStringToJsval(cx, %s, ${jsvalHandle})" % result), False)
+
+ if type.isEnum():
+ if type.nullable():
+ resultLoc = "%s.Value()" % result
+ else:
+ resultLoc = result
+ conversion = fill(
+ """
+ if (!ToJSValue(cx, ${result}, $${jsvalHandle})) {
+ $*{exceptionCode}
+ }
+ $*{successCode}
+ """,
+ result=resultLoc,
+ exceptionCode=exceptionCode,
+ successCode=successCode)
+
+ if type.nullable():
+ conversion = CGIfElseWrapper(
+ "%s.IsNull()" % result,
+ CGGeneric(setNull()),
+ CGGeneric(conversion)).define()
+ return conversion, False
+
+ if type.isCallback() or type.isCallbackInterface():
+ wrapCode = setObject(
+ "*GetCallbackFromCallbackObject(%(result)s)",
+ wrapAsType=type)
+ if type.nullable():
+ wrapCode = (
+ "if (%(result)s) {\n" +
+ indent(wrapCode) +
+ "} else {\n" +
+ indent(setNull()) +
+ "}\n")
+ wrapCode = wrapCode % {"result": result}
+ return wrapCode, False
+
+ if type.isAny():
+ # See comments in GetOrCreateDOMReflector explaining why we need
+ # to wrap here.
+ # NB: _setValue(..., type-that-is-any) calls JS_WrapValue(), so is fallible
+ head = "JS::ExposeValueToActiveJS(%s);\n" % result
+ return (head + _setValue(result, wrapAsType=type), False)
+
+ if (type.isObject() or (type.isSpiderMonkeyInterface() and
+ not typedArraysAreStructs)):
+ # See comments in GetOrCreateDOMReflector explaining why we need
+ # to wrap here.
+ if type.nullable():
+ toValue = "%s"
+ setter = setObjectOrNull
+ head = """if (%s) {
+ JS::ExposeObjectToActiveJS(%s);
+ }
+ """ % (result, result)
+ else:
+ toValue = "*%s"
+ setter = setObject
+ head = "JS::ExposeObjectToActiveJS(%s);\n" % result
+ # NB: setObject{,OrNull}(..., some-object-type) calls JS_WrapValue(), so is fallible
+ return (head + setter(toValue % result, wrapAsType=type), False)
+
+ if not (type.isUnion() or type.isPrimitive() or type.isDictionary() or
+ type.isDate() or
+ (type.isSpiderMonkeyInterface() and typedArraysAreStructs)):
+ raise TypeError("Need to learn to wrap %s" % type)
+
+ if type.nullable():
+ recTemplate, recInfal = getWrapTemplateForType(type.inner, descriptorProvider,
+ "%s.Value()" % result, successCode,
+ returnsNewObject, exceptionCode,
+ typedArraysAreStructs)
+ return ("if (%s.IsNull()) {\n" % result +
+ indent(setNull()) +
+ "}\n" +
+ recTemplate, recInfal)
+
+ if type.isSpiderMonkeyInterface():
+ assert typedArraysAreStructs
+ # See comments in GetOrCreateDOMReflector explaining why we need
+ # to wrap here.
+ # NB: setObject(..., some-object-type) calls JS_WrapValue(), so is fallible
+ return (setObject("*%s.Obj()" % result,
+ wrapAsType=type), False)
+
+ if type.isUnion():
+ return (wrapAndSetPtr("%s.ToJSVal(cx, ${obj}, ${jsvalHandle})" % result),
+ False)
+
+ if type.isDictionary():
+ return (wrapAndSetPtr("%s.ToObjectInternal(cx, ${jsvalHandle})" % result),
+ False)
+
+ if type.isDate():
+ return (wrapAndSetPtr("%s.ToDateObject(cx, ${jsvalHandle})" % result),
+ False)
+
+ tag = type.tag()
+
+ if tag in [IDLType.Tags.int8, IDLType.Tags.uint8, IDLType.Tags.int16,
+ IDLType.Tags.uint16, IDLType.Tags.int32]:
+ return (setInt32("int32_t(%s)" % result), True)
+
+ elif tag in [IDLType.Tags.int64, IDLType.Tags.uint64,
+ IDLType.Tags.unrestricted_float, IDLType.Tags.float,
+ IDLType.Tags.unrestricted_double, 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 (setDouble("double(%s)" % result), True)
+
+ elif tag == IDLType.Tags.uint32:
+ return (setUint32(result), True)
+
+ elif tag == IDLType.Tags.bool:
+ return (setBoolean(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': something that can have .address() called on it to get a
+ JS::Value* and .set() called on it to set it to a JS::Value.
+ This can be a JS::MutableHandle<JS::Value> or a
+ JS::Rooted<JS::Value>.
+ * 'jsvalHandle': something that can be passed to methods taking a
+ JS::MutableHandle<JS::Value>. This can be a
+ JS::MutableHandle<JS::Value> or a JS::Rooted<JS::Value>*.
+ * '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. The
+ successCode must ensure that once it runs no
+ more of the conversion template will be
+ executed (e.g. by doing a 'return' or 'break'
+ as appropriate).
+ * 'returnsNewObject' (optional): If true, we're wrapping for the return
+ value of a [NewObject] method. Assumed
+ false if not set.
+ * 'exceptionCode' (optional): Code to run when a JS exception is thrown.
+ The default is "return false;". The code
+ passed here must return.
+ * 'isConstructorRetval' (optional): If true, we're wrapping a constructor
+ return value.
+ """
+ wrap = getWrapTemplateForType(
+ type, descriptorProvider,
+ templateValues.get('result', 'result'),
+ templateValues.get('successCode', None),
+ templateValues.get('returnsNewObject', False),
+ templateValues.get('exceptionCode', "return false;\n"),
+ templateValues.get('typedArraysAreStructs', False),
+ isConstructorRetval=templateValues.get('isConstructorRetval', 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 returnsNewObject, 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,
+ memberReturnsNewObject(member), "return false;\n",
+ False)[1]
+
+
+def leafTypeNeedsCx(type, retVal):
+ return (type.isAny() or type.isObject() or
+ (retVal and type.isSpiderMonkeyInterface()))
+
+
+def leafTypeNeedsScopeObject(type, retVal):
+ return retVal and type.isSpiderMonkeyInterface()
+
+
+def leafTypeNeedsRooting(type):
+ return leafTypeNeedsCx(type, False) or type.isSpiderMonkeyInterface()
+
+
+def typeNeedsRooting(type):
+ return typeMatchesLambda(type,
+ lambda t: leafTypeNeedsRooting(t))
+
+
+def typeNeedsCx(type, retVal=False):
+ return typeMatchesLambda(type,
+ lambda t: leafTypeNeedsCx(t, retVal))
+
+
+def typeNeedsScopeObject(type, retVal=False):
+ return typeMatchesLambda(type,
+ lambda t: leafTypeNeedsScopeObject(t, retVal))
+
+
+def typeMatchesLambda(type, func):
+ if type is None:
+ return False
+ if type.nullable():
+ return typeMatchesLambda(type.inner, func)
+ if type.isSequence() or type.isMozMap():
+ return typeMatchesLambda(type.inner, func)
+ if type.isUnion():
+ return any(typeMatchesLambda(t, func) for t in
+ type.unroll().flatMemberTypes)
+ if type.isDictionary():
+ return dictionaryMatchesLambda(type.inner, func)
+ return func(type)
+
+
+def dictionaryMatchesLambda(dictionary, func):
+ return (any(typeMatchesLambda(m.type, func) for m in dictionary.members) or
+ (dictionary.parent and dictionaryMatchesLambda(dictionary.parent, func)))
+
+
+# Whenever this is modified, please update CGNativeMember.getRetvalInfo as
+# needed to keep the types compatible.
+def getRetvalDeclarationForType(returnType, descriptorProvider,
+ isMember=False):
+ """
+ Returns a tuple containing five things:
+
+ 1) A CGThing for the type of the return value, or None if there is no need
+ for a return value.
+
+ 2) A value indicating the kind of ourparam to pass the value as. Valid
+ options are None to not pass as an out param at all, "ref" (to pass a
+ reference as an out param), and "ptr" (to pass a pointer as an out
+ param).
+
+ 3) A CGThing for a tracer for the return value, or None if no tracing is
+ needed.
+
+ 4) An argument string to pass to the retval declaration
+ constructor or None if there are no arguments.
+
+ 5) The name of a function that needs to be called with the return value
+ before using it, or None if no function needs to be called.
+ """
+ if returnType is None or returnType.isVoid():
+ # Nothing to declare
+ return None, None, None, None, None
+ if returnType.isPrimitive() and returnType.tag() in builtinNames:
+ result = CGGeneric(builtinNames[returnType.tag()])
+ if returnType.nullable():
+ result = CGTemplatedType("Nullable", result)
+ return result, None, None, None, None
+ if returnType.isDOMString() or returnType.isUSVString():
+ if isMember:
+ return CGGeneric("nsString"), "ref", None, None, None
+ return CGGeneric("DOMString"), "ref", None, None, None
+ if returnType.isByteString():
+ return CGGeneric("nsCString"), "ref", None, None, None
+ if returnType.isEnum():
+ result = CGGeneric(returnType.unroll().inner.identifier.name)
+ if returnType.nullable():
+ result = CGTemplatedType("Nullable", result)
+ return result, None, None, None, None
+ if returnType.isGeckoInterface():
+ result = CGGeneric(descriptorProvider.getDescriptor(
+ returnType.unroll().inner.identifier.name).nativeType)
+ conversion = None
+ if isMember:
+ result = CGGeneric("StrongPtrForMember<%s>::Type" % result.define())
+ else:
+ conversion = CGGeneric("StrongOrRawPtr<%s>" % result.define())
+ result = CGGeneric("auto")
+ return result, None, None, None, conversion
+ if returnType.isCallback():
+ name = returnType.unroll().callback.identifier.name
+ return CGGeneric("RefPtr<%s>" % name), None, None, None, None
+ if returnType.isAny():
+ if isMember:
+ return CGGeneric("JS::Value"), None, None, None, None
+ return CGGeneric("JS::Rooted<JS::Value>"), "ptr", None, "cx", None
+ if returnType.isObject() or returnType.isSpiderMonkeyInterface():
+ if isMember:
+ return CGGeneric("JSObject*"), None, None, None, None
+ return CGGeneric("JS::Rooted<JSObject*>"), "ptr", None, "cx", None
+ if returnType.isSequence():
+ nullable = returnType.nullable()
+ if nullable:
+ returnType = returnType.inner
+ result, _, _, _, _ = getRetvalDeclarationForType(returnType.inner,
+ descriptorProvider,
+ isMember="Sequence")
+ # While we have our inner type, set up our rooter, if needed
+ if not isMember and typeNeedsRooting(returnType):
+ rooter = CGGeneric("SequenceRooter<%s > resultRooter(cx, &result);\n" %
+ result.define())
+ else:
+ rooter = None
+ result = CGTemplatedType("nsTArray", result)
+ if nullable:
+ result = CGTemplatedType("Nullable", result)
+ return result, "ref", rooter, None, None
+ if returnType.isMozMap():
+ nullable = returnType.nullable()
+ if nullable:
+ returnType = returnType.inner
+ result, _, _, _, _ = getRetvalDeclarationForType(returnType.inner,
+ descriptorProvider,
+ isMember="MozMap")
+ # While we have our inner type, set up our rooter, if needed
+ if not isMember and typeNeedsRooting(returnType):
+ rooter = CGGeneric("MozMapRooter<%s> resultRooter(cx, &result);\n" %
+ result.define())
+ else:
+ rooter = None
+ result = CGTemplatedType("MozMap", result)
+ if nullable:
+ result = CGTemplatedType("Nullable", result)
+ return result, "ref", rooter, None, None
+ if returnType.isDictionary():
+ nullable = returnType.nullable()
+ dictName = CGDictionary.makeDictionaryName(returnType.unroll().inner)
+ result = CGGeneric(dictName)
+ if not isMember and typeNeedsRooting(returnType):
+ if nullable:
+ result = CGTemplatedType("NullableRootedDictionary", result)
+ else:
+ result = CGTemplatedType("RootedDictionary", result)
+ resultArgs = "cx"
+ else:
+ if nullable:
+ result = CGTemplatedType("Nullable", result)
+ resultArgs = None
+ return result, "ref", None, resultArgs, None
+ if returnType.isUnion():
+ result = CGGeneric(CGUnionStruct.unionTypeName(returnType.unroll(), True))
+ if not isMember and typeNeedsRooting(returnType):
+ if returnType.nullable():
+ result = CGTemplatedType("NullableRootedUnion", result)
+ else:
+ result = CGTemplatedType("RootedUnion", result)
+ resultArgs = "cx"
+ else:
+ if returnType.nullable():
+ result = CGTemplatedType("Nullable", result)
+ resultArgs = None
+ return result, "ref", None, resultArgs, None
+ if returnType.isDate():
+ result = CGGeneric("Date")
+ if returnType.nullable():
+ result = CGTemplatedType("Nullable", result)
+ return result, None, None, None, None
+ raise TypeError("Don't know how to declare return value for %s" %
+ returnType)
+
+
+def needCx(returnType, arguments, extendedAttributes, considerTypes,
+ static=False):
+ return (not static and considerTypes and
+ (typeNeedsCx(returnType, True) or
+ any(typeNeedsCx(a.type) for a in arguments)) or
+ 'implicitJSContext' in extendedAttributes)
+
+
+def needScopeObject(returnType, arguments, extendedAttributes,
+ isWrapperCached, considerTypes, isMember):
+ """
+ isMember should be true if we're dealing with an attribute
+ annotated as [StoreInSlot].
+ """
+ return (considerTypes and not isWrapperCached and
+ ((not isMember and typeNeedsScopeObject(returnType, True)) or
+ any(typeNeedsScopeObject(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.
+
+ needsSubjectPrincipal is a boolean indicating whether the call should
+ receive the subject nsIPrincipal as argument.
+
+ needsCallerType is a boolean indicating whether the call should receive
+ a PrincipalType for the caller.
+
+ isFallible is a boolean indicating whether the call should be fallible.
+
+ resultVar: If the returnType is not void, then the result of the call is
+ stored in a C++ variable named by resultVar. The caller is responsible for
+ declaring the result variable. If the caller doesn't care about the result
+ value, resultVar can be omitted.
+ """
+ def __init__(self, isFallible, needsSubjectPrincipal, needsCallerType,
+ arguments, argsPre, returnType, extendedAttributes, descriptor,
+ nativeMethodName, static, object="self", argsPost=[],
+ resultVar=None):
+ CGThing.__init__(self)
+
+ result, resultOutParam, resultRooter, resultArgs, resultConversion = \
+ getRetvalDeclarationForType(returnType, descriptor)
+
+ args = CGList([CGGeneric(arg) for arg in argsPre], ", ")
+ for a, name in arguments:
+ arg = CGGeneric(name)
+
+ # Now constify the things that need it
+ def needsConst(a):
+ if a.type.isDictionary():
+ return True
+ if a.type.isSequence():
+ return True
+ if a.type.isMozMap():
+ return True
+ # isObject() types are always a JS::Rooted, whether
+ # nullable or not, and it turns out a const JS::Rooted
+ # is not very helpful at all (in particular, it won't
+ # even convert to a JS::Handle).
+ # XXX bz Well, why not???
+ if a.type.nullable() and not a.type.isObject():
+ return True
+ if a.type.isString():
+ return True
+ if a.canHaveMissingValue():
+ # This will need an Optional or it's a variadic;
+ # in both cases it should be const.
+ return True
+ if a.type.isUnion():
+ return True
+ if a.type.isSpiderMonkeyInterface():
+ return True
+ return False
+ if needsConst(a):
+ arg = CGWrapper(arg, pre="Constify(", post=")")
+ # And convert NonNull<T> to T&
+ if (((a.type.isGeckoInterface() or a.type.isCallback()) and not a.type.nullable()) or
+ a.type.isDOMString()):
+ arg = CGWrapper(arg, pre="NonNullHelper(", post=")")
+ args.append(arg)
+
+ needResultDecl = False
+
+ # Return values that go in outparams go here
+ if resultOutParam is not None:
+ if resultVar is None:
+ needResultDecl = True
+ resultVar = "result"
+ if resultOutParam == "ref":
+ args.append(CGGeneric(resultVar))
+ else:
+ assert resultOutParam == "ptr"
+ args.append(CGGeneric("&" + resultVar))
+
+ if needsSubjectPrincipal:
+ args.append(CGGeneric("subjectPrincipal"))
+
+ if needsCallerType:
+ args.append(CGGeneric("callerType"))
+
+ if isFallible:
+ args.append(CGGeneric("rv"))
+ args.extend(CGGeneric(arg) for arg in argsPost)
+
+ # Build up our actual call
+ self.cgRoot = CGList([])
+
+ call = CGGeneric(nativeMethodName)
+ if not static:
+ call = CGWrapper(call, pre="%s->" % object)
+ call = CGList([call, CGWrapper(args, pre="(", post=")")])
+ if resultConversion is not None:
+ call = CGList([resultConversion, CGWrapper(call, pre="(", post=")")])
+ if resultVar is None and result is not None:
+ needResultDecl = True
+ resultVar = "result"
+
+ if needResultDecl:
+ if resultRooter is not None:
+ self.cgRoot.prepend(resultRooter)
+ if resultArgs is not None:
+ resultArgsStr = "(%s)" % resultArgs
+ else:
+ resultArgsStr = ""
+ result = CGWrapper(result, post=(" %s%s" % (resultVar, resultArgsStr)))
+ if resultOutParam is None and resultArgs is None:
+ call = CGList([result, CGWrapper(call, pre="(", post=")")])
+ else:
+ self.cgRoot.prepend(CGWrapper(result, post=";\n"))
+ if resultOutParam is None:
+ call = CGWrapper(call, pre=resultVar + " = ")
+ elif result is not None:
+ assert resultOutParam is None
+ call = CGWrapper(call, pre=resultVar + " = ")
+
+ call = CGWrapper(call, post=";\n")
+ self.cgRoot.append(call)
+
+ if needsSubjectPrincipal:
+ getPrincipal = dedent(
+ """
+ JSCompartment* compartment = js::GetContextCompartment(cx);
+ MOZ_ASSERT(compartment);
+ JSPrincipals* principals = JS_GetCompartmentPrincipals(compartment);
+ """)
+
+ if descriptor.interface.isExposedInAnyWorker():
+ self.cgRoot.prepend(CGGeneric(fill(
+ """
+ Maybe<nsIPrincipal*> subjectPrincipal;
+ if (NS_IsMainThread()) {
+ $*{getPrincipal}
+ subjectPrincipal.emplace(nsJSPrincipals::get(principals));
+ }
+ """,
+ getPrincipal=getPrincipal)))
+ else:
+ self.cgRoot.prepend(CGGeneric(fill(
+ """
+ $*{getPrincipal}
+ // Initializing a nonnull is pretty darn annoying...
+ NonNull<nsIPrincipal> subjectPrincipal;
+ subjectPrincipal = static_cast<nsIPrincipal*>(nsJSPrincipals::get(principals));
+ """,
+ getPrincipal=getPrincipal)))
+
+ if needsCallerType:
+ # Note that we do not want to use
+ # IsCallerChrome/ThreadsafeIsCallerChrome directly because those
+ # will pull in the check for UniversalXPConnect, which we ideally
+ # don't want to have in the new thing we're doing here. If not
+ # NS_IsMainThread(), though, we'll go ahead and call
+ # ThreasafeIsCallerChrome(), since that won't mess with
+ # UnivesalXPConnect and we don't want to worry about the right
+ # worker includes here.
+ callerCheck = CGGeneric("callerType = nsContentUtils::IsSystemPrincipal(nsContentUtils::SubjectPrincipal()) ? CallerType::System : CallerType::NonSystem;\n")
+ if descriptor.interface.isExposedInAnyWorker():
+ callerCheck = CGIfElseWrapper(
+ "NS_IsMainThread()",
+ callerCheck,
+ CGGeneric("callerType = nsContentUtils::ThreadsafeIsCallerChrome() ? CallerType::System : CallerType::NonSystem;\n"));
+ self.cgRoot.prepend(callerCheck)
+ self.cgRoot.prepend(CGGeneric("CallerType callerType;\n"))
+
+ if isFallible:
+ self.cgRoot.prepend(CGGeneric("binding_detail::FastErrorResult rv;\n"))
+ self.cgRoot.append(CGGeneric(dedent(
+ """
+ if (MOZ_UNLIKELY(rv.MaybeSetPendingException(cx))) {
+ return false;
+ }
+ """)))
+
+ self.cgRoot.append(CGGeneric("MOZ_ASSERT(!JS_IsExceptionPending(cx));\n"))
+
+ def define(self):
+ return self.cgRoot.define()
+
+
+def getUnionMemberName(type):
+ if type.isGeckoInterface():
+ return type.inner.identifier.name
+ if type.isEnum():
+ return type.inner.identifier.name
+ return type.name
+
+
+class MethodNotNewObjectError(Exception):
+ def __init__(self, typename):
+ self.typename = typename
+
+# A counter for making sure that when we're wrapping up things in
+# nested sequences we don't use the same variable name to iterate over
+# different sequences.
+sequenceWrapLevel = 0
+mapWrapLevel = 0
+
+
+def wrapTypeIntoCurrentCompartment(type, value, isMember=True):
+ """
+ Take the thing named by "value" and if it contains "any",
+ "object", or spidermonkey-interface types inside return a CGThing
+ that will wrap them into the current compartment.
+ """
+ if type.isAny():
+ assert not type.nullable()
+ if isMember:
+ value = "JS::MutableHandle<JS::Value>::fromMarkedLocation(&%s)" % value
+ else:
+ value = "&" + value
+ return CGGeneric("if (!JS_WrapValue(cx, %s)) {\n"
+ " return false;\n"
+ "}\n" % value)
+
+ if type.isObject():
+ if isMember:
+ value = "JS::MutableHandle<JSObject*>::fromMarkedLocation(&%s)" % value
+ else:
+ value = "&" + value
+ return CGGeneric("if (!JS_WrapObject(cx, %s)) {\n"
+ " return false;\n"
+ "}\n" % value)
+
+ if type.isSpiderMonkeyInterface():
+ origValue = value
+ if type.nullable():
+ value = "%s.Value()" % value
+ wrapCode = CGGeneric("if (!%s.WrapIntoNewCompartment(cx)) {\n"
+ " return false;\n"
+ "}\n" % value)
+ if type.nullable():
+ wrapCode = CGIfWrapper(wrapCode, "!%s.IsNull()" % origValue)
+ return wrapCode
+
+ if type.isSequence():
+ origValue = value
+ origType = type
+ if type.nullable():
+ type = type.inner
+ value = "%s.Value()" % value
+ global sequenceWrapLevel
+ index = "indexName%d" % sequenceWrapLevel
+ sequenceWrapLevel += 1
+ wrapElement = wrapTypeIntoCurrentCompartment(type.inner,
+ "%s[%s]" % (value, index))
+ sequenceWrapLevel -= 1
+ if not wrapElement:
+ return None
+ wrapCode = CGWrapper(CGIndenter(wrapElement),
+ pre=("for (uint32_t %s = 0; %s < %s.Length(); ++%s) {\n" %
+ (index, index, value, index)),
+ post="}\n")
+ if origType.nullable():
+ wrapCode = CGIfWrapper(wrapCode, "!%s.IsNull()" % origValue)
+ return wrapCode
+
+ if type.isMozMap():
+ origValue = value
+ origType = type
+ if type.nullable():
+ type = type.inner
+ value = "%s.Value()" % value
+ global mapWrapLevel
+ key = "mapName%d" % mapWrapLevel
+ mapWrapLevel += 1
+ wrapElement = wrapTypeIntoCurrentCompartment(type.inner,
+ "%s.Get(%sKeys[%sIndex])" % (value, key, key))
+ mapWrapLevel -= 1
+ if not wrapElement:
+ return None
+ wrapCode = CGWrapper(CGIndenter(wrapElement),
+ pre=("""
+ nsTArray<nsString> %sKeys;
+ %s.GetKeys(%sKeys);
+ for (uint32_t %sIndex = 0; %sIndex < %sKeys.Length(); ++%sIndex) {
+ """ % (key, value, key, key, key, key, key)),
+ post="}\n")
+ if origType.nullable():
+ wrapCode = CGIfWrapper(wrapCode, "!%s.IsNull()" % origValue)
+ return wrapCode
+
+ if type.isDictionary():
+ assert not type.nullable()
+ myDict = type.inner
+ memberWraps = []
+ while myDict:
+ for member in myDict.members:
+ memberWrap = wrapArgIntoCurrentCompartment(
+ member,
+ "%s.%s" % (value, CGDictionary.makeMemberName(member.identifier.name)))
+ if memberWrap:
+ memberWraps.append(memberWrap)
+ myDict = myDict.parent
+ return CGList(memberWraps) if len(memberWraps) != 0 else None
+
+ if type.isUnion():
+ memberWraps = []
+ if type.nullable():
+ type = type.inner
+ value = "%s.Value()" % value
+ for member in type.flatMemberTypes:
+ memberName = getUnionMemberName(member)
+ memberWrap = wrapTypeIntoCurrentCompartment(
+ member, "%s.GetAs%s()" % (value, memberName))
+ if memberWrap:
+ memberWrap = CGIfWrapper(
+ memberWrap, "%s.Is%s()" % (value, memberName))
+ memberWraps.append(memberWrap)
+ return CGList(memberWraps, "else ") if len(memberWraps) != 0 else None
+
+ if (type.isString() or type.isPrimitive() or type.isEnum() or
+ type.isGeckoInterface() or type.isCallback() or type.isDate()):
+ # All of these don't need wrapping
+ return None
+
+ raise TypeError("Unknown type; we don't know how to wrap it in constructor "
+ "arguments: %s" % type)
+
+
+def wrapArgIntoCurrentCompartment(arg, value, isMember=True):
+ """
+ As wrapTypeIntoCurrentCompartment but handles things being optional
+ """
+ origValue = value
+ isOptional = arg.canHaveMissingValue()
+ if isOptional:
+ value = value + ".Value()"
+ wrap = wrapTypeIntoCurrentCompartment(arg.type, value, isMember)
+ if wrap and isOptional:
+ wrap = CGIfWrapper(wrap, "%s.WasPassed()" % origValue)
+ return wrap
+
+
+def needsContainsHack(m):
+ return m.getExtendedAttribute("ReturnValueNeedsContainsHack")
+
+def needsCallerType(m):
+ return m.getExtendedAttribute("NeedsCallerType")
+
+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. Note that this only controls how
+ the method is called (|self->nativeMethodName(...)| vs
+ |nativeMethodName(...)|).
+
+ 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, arguments, nativeMethodName, static,
+ descriptor, idlNode, argConversionStartsAt=0, getter=False,
+ setter=False, isConstructor=False, useCounterName=None,
+ resultVar=None):
+ assert idlNode.isMethod() == (not getter and not setter)
+ assert idlNode.isAttr() == (getter or setter)
+ # Constructors are always static
+ assert not isConstructor or static
+
+ CGThing.__init__(self)
+ self.returnType = returnType
+ self.descriptor = descriptor
+ self.idlNode = idlNode
+ self.extendedAttributes = descriptor.getExtendedAttributes(idlNode,
+ getter=getter,
+ setter=setter)
+ self.arguments = arguments
+ self.argCount = len(arguments)
+ self.isConstructor = isConstructor
+ cgThings = []
+
+ # Here, we check if the current getter, setter, method, interface or
+ # inherited interfaces have the UnsafeInPrerendering extended attribute
+ # and if so, we add a check to make sure it is safe.
+ if (idlNode.getExtendedAttribute("UnsafeInPrerendering") or
+ descriptor.interface.getExtendedAttribute("UnsafeInPrerendering") or
+ any(i.getExtendedAttribute("UnsafeInPrerendering")
+ for i in descriptor.interface.getInheritedInterfaces())):
+ cgThings.append(CGGeneric(dedent(
+ """
+ if (!mozilla::dom::EnforceNotInPrerendering(cx, obj)) {
+ // Return false from the JSNative in order to trigger
+ // an uncatchable exception.
+ MOZ_ASSERT(!JS_IsExceptionPending(cx));
+ return false;
+ }
+ """)))
+
+ deprecated = (idlNode.getExtendedAttribute("Deprecated") or
+ (idlNode.isStatic() and descriptor.interface.getExtendedAttribute("Deprecated")))
+ if deprecated:
+ cgThings.append(CGGeneric(dedent(
+ """
+ DeprecationWarning(cx, obj, nsIDocument::e%s);
+ """ % deprecated[0])))
+
+ lenientFloatCode = None
+ if (idlNode.getExtendedAttribute('LenientFloat') is not None and
+ (setter or idlNode.isMethod())):
+ cgThings.append(CGGeneric(dedent(
+ """
+ bool foundNonFiniteFloat = false;
+ """)))
+ lenientFloatCode = "foundNonFiniteFloat = true;\n"
+
+ argsPre = []
+ if idlNode.isStatic():
+ # If we're a constructor, "obj" may not be a function, so calling
+ # XrayAwareCalleeGlobal() on it is not safe. Of course in the
+ # constructor case either "obj" is an Xray or we're already in the
+ # content compartment, not the Xray compartment, so just
+ # constructing the GlobalObject from "obj" is fine.
+ if isConstructor:
+ objForGlobalObject = "obj"
+ else:
+ objForGlobalObject = "xpc::XrayAwareCalleeGlobal(obj)"
+ cgThings.append(CGGeneric(fill(
+ """
+ GlobalObject global(cx, ${obj});
+ if (global.Failed()) {
+ return false;
+ }
+
+ """,
+ obj=objForGlobalObject)))
+ argsPre.append("global")
+
+ # For JS-implemented interfaces we do not want to base the
+ # needsCx decision on the types involved, just on our extended
+ # attributes. Also, JSContext is not needed for the static case
+ # since GlobalObject already contains the context.
+ needsCx = needCx(returnType, arguments, self.extendedAttributes,
+ not descriptor.interface.isJSImplemented(), static)
+ if needsCx:
+ argsPre.append("cx")
+
+ # Hack for making Promise.prototype.then work well over Xrays.
+ if (not idlNode.isStatic() and
+ descriptor.name == "Promise" and
+ idlNode.isMethod() and
+ idlNode.identifier.name == "then"):
+ cgThings.append(CGGeneric(dedent(
+ """
+ JS::Rooted<JSObject*> calleeGlobal(cx, xpc::XrayAwareCalleeGlobal(&args.callee()));
+ """)))
+ argsPre.append("calleeGlobal")
+
+ needsUnwrap = False
+ argsPost = []
+ if isConstructor:
+ if descriptor.name == "Promise":
+ # Hack for Promise for now: pass in our desired proto so the
+ # implementation can create the reflector with the right proto.
+ argsPost.append("desiredProto")
+ # Also, we do not want to enter the content compartment when the
+ # Promise constructor is called via Xrays, because we want to
+ # create our callback functions that we will hand to our caller
+ # in the Xray compartment. The reason we want to do that is the
+ # following situation, over Xrays:
+ #
+ # contentWindow.Promise.race([Promise.resolve(5)])
+ #
+ # Ideally this would work. Internally, race() does a
+ # contentWindow.Promise.resolve() on everything in the array.
+ # Per spec, to support subclassing,
+ # contentWindow.Promise.resolve has to do:
+ #
+ # var resolve, reject;
+ # var p = new contentWindow.Promise(function(a, b) {
+ # resolve = a;
+ # reject = b;
+ # });
+ # resolve(arg);
+ # return p;
+ #
+ # where "arg" is, in this case, the chrome-side return value of
+ # Promise.resolve(5). But if the "resolve" function in that
+ # case were created in the content compartment, then calling it
+ # would wrap "arg" in an opaque wrapper, and that function tries
+ # to get .then off the argument, which would throw. So we need
+ # to create the "resolve" function in the chrome compartment,
+ # and hence want to be running the entire Promise constructor
+ # (which creates that function) in the chrome compartment in
+ # this case. So don't set needsUnwrap here.
+ else:
+ needsUnwrap = True
+ needsUnwrappedVar = False
+ unwrappedVar = "obj"
+ elif descriptor.interface.isJSImplemented():
+ if not idlNode.isStatic():
+ needsUnwrap = True
+ needsUnwrappedVar = True
+ argsPost.append("js::GetObjectCompartment(unwrappedObj ? *unwrappedObj : obj)")
+ elif needScopeObject(returnType, arguments, self.extendedAttributes,
+ descriptor.wrapperCache, True,
+ idlNode.getExtendedAttribute("StoreInSlot")):
+ needsUnwrap = True
+ needsUnwrappedVar = True
+ argsPre.append("unwrappedObj ? *unwrappedObj : obj")
+
+ if idlNode.isStatic() and not isConstructor and descriptor.name == "Promise":
+ # Hack for Promise for now: pass in the "this" value to
+ # Promise static methods.
+ argsPre.append("args.thisv()")
+
+ if needsUnwrap and needsUnwrappedVar:
+ # We cannot assign into obj because it's a Handle, not a
+ # MutableHandle, so we need a separate Rooted.
+ cgThings.append(CGGeneric("Maybe<JS::Rooted<JSObject*> > unwrappedObj;\n"))
+ unwrappedVar = "unwrappedObj.ref()"
+
+ if idlNode.isMethod() and idlNode.isLegacycaller():
+ # If we can have legacycaller with identifier, we can't
+ # just use the idlNode to determine whether we're
+ # generating code for the legacycaller or not.
+ assert idlNode.isIdentifierLess()
+ # Pass in our thisVal
+ argsPre.append("args.thisv()")
+
+ ourName = "%s.%s" % (descriptor.interface.identifier.name,
+ idlNode.identifier.name)
+ if idlNode.isMethod():
+ argDescription = "argument %(index)d of " + ourName
+ elif setter:
+ argDescription = "value being assigned to %s" % ourName
+ else:
+ assert self.argCount == 0
+
+ if needsUnwrap:
+ # It's very important that we construct our unwrappedObj, if we need
+ # to do it, before we might start setting up Rooted things for our
+ # arguments, so that we don't violate the stack discipline Rooted
+ # depends on.
+ cgThings.append(CGGeneric(
+ "bool objIsXray = xpc::WrapperFactory::IsXrayWrapper(obj);\n"))
+ if needsUnwrappedVar:
+ cgThings.append(CGIfWrapper(
+ CGGeneric("unwrappedObj.emplace(cx, obj);\n"),
+ "objIsXray"))
+
+ for i in range(argConversionStartsAt, self.argCount):
+ cgThings.append(
+ CGArgumentConverter(arguments[i], i, self.descriptor,
+ argDescription % {"index": i + 1},
+ idlNode, invalidEnumValueFatal=not setter,
+ lenientFloatCode=lenientFloatCode))
+
+ # Now that argument processing is done, enforce the LenientFloat stuff
+ if lenientFloatCode:
+ if setter:
+ foundNonFiniteFloatBehavior = "return true;\n"
+ else:
+ assert idlNode.isMethod()
+ foundNonFiniteFloatBehavior = dedent(
+ """
+ args.rval().setUndefined();
+ return true;
+ """)
+ cgThings.append(CGGeneric(fill(
+ """
+ if (foundNonFiniteFloat) {
+ $*{returnSteps}
+ }
+ """,
+ returnSteps=foundNonFiniteFloatBehavior)))
+
+ if needsUnwrap:
+ # Something depends on having the unwrapped object, so unwrap it now.
+ xraySteps = []
+ # XXXkhuey we should be able to MOZ_ASSERT that ${obj} is
+ # not null.
+ xraySteps.append(
+ CGGeneric(fill(
+ """
+ ${obj} = js::CheckedUnwrap(${obj});
+ if (!${obj}) {
+ return false;
+ }
+ """,
+ obj=unwrappedVar)))
+ if isConstructor:
+ # If we're called via an xray, we need to enter the underlying
+ # object's compartment and then wrap up all of our arguments into
+ # that compartment as needed. This is all happening after we've
+ # already done the conversions from JS values to WebIDL (C++)
+ # values, so we only need to worry about cases where there are 'any'
+ # or 'object' types, or other things that we represent as actual
+ # JSAPI types, present. Effectively, we're emulating a
+ # CrossCompartmentWrapper, but working with the C++ types, not the
+ # original list of JS::Values.
+ cgThings.append(CGGeneric("Maybe<JSAutoCompartment> ac;\n"))
+ xraySteps.append(CGGeneric("ac.emplace(cx, obj);\n"))
+ xraySteps.append(CGGeneric(dedent(
+ """
+ if (!JS_WrapObject(cx, &desiredProto)) {
+ return false;
+ }
+ """)))
+ xraySteps.extend(
+ wrapArgIntoCurrentCompartment(arg, argname, isMember=False)
+ for arg, argname in self.getArguments())
+
+ cgThings.append(
+ CGIfWrapper(CGList(xraySteps),
+ "objIsXray"))
+
+ # If this is a method that was generated by a maplike/setlike
+ # interface, use the maplike/setlike generator to fill in the body.
+ # Otherwise, use CGCallGenerator to call the native method.
+ if idlNode.isMethod() and idlNode.isMaplikeOrSetlikeOrIterableMethod():
+ if (idlNode.maplikeOrSetlikeOrIterable.isMaplike() or
+ idlNode.maplikeOrSetlikeOrIterable.isSetlike()):
+ cgThings.append(CGMaplikeOrSetlikeMethodGenerator(descriptor,
+ idlNode.maplikeOrSetlikeOrIterable,
+ idlNode.identifier.name))
+ else:
+ cgThings.append(CGIterableMethodGenerator(descriptor,
+ idlNode.maplikeOrSetlikeOrIterable,
+ idlNode.identifier.name))
+ else:
+ cgThings.append(CGCallGenerator(
+ self.isFallible(),
+ idlNode.getExtendedAttribute('NeedsSubjectPrincipal'),
+ needsCallerType(idlNode),
+ self.getArguments(), argsPre, returnType,
+ self.extendedAttributes, descriptor,
+ nativeMethodName,
+ static, argsPost=argsPost, resultVar=resultVar))
+
+ if useCounterName:
+ # Generate a telemetry call for when [UseCounter] is used.
+ code = "SetDocumentAndPageUseCounter(cx, obj, eUseCounter_%s);\n" % useCounterName
+ cgThings.append(CGGeneric(code))
+
+ self.cgRoot = CGList(cgThings)
+
+ def getArguments(self):
+ return [(a, "arg" + str(i)) for i, a in enumerate(self.arguments)]
+
+ def isFallible(self):
+ return 'infallible' not in self.extendedAttributes
+
+ def wrap_return_value(self):
+ wrapCode = ""
+
+ returnsNewObject = memberReturnsNewObject(self.idlNode)
+ if (returnsNewObject and
+ self.returnType.isGeckoInterface()):
+ wrapCode += dedent(
+ """
+ static_assert(!IsPointer<decltype(result)>::value,
+ "NewObject implies that we need to keep the object alive with a strong reference.");
+ """)
+
+ setSlot = self.idlNode.isAttr() and self.idlNode.slotIndices is not None
+ if setSlot:
+ # For attributes in slots, we want to do some
+ # post-processing once we've wrapped them.
+ successCode = "break;\n"
+ else:
+ successCode = None
+
+ resultTemplateValues = {
+ 'jsvalRef': 'args.rval()',
+ 'jsvalHandle': 'args.rval()',
+ 'returnsNewObject': returnsNewObject,
+ 'isConstructorRetval': self.isConstructor,
+ 'successCode': successCode,
+ # 'obj' in this dictionary is the thing whose compartment we are
+ # trying to do the to-JS conversion in. We're going to put that
+ # thing in a variable named "conversionScope" if setSlot is true.
+ # Otherwise, just use "obj" for lack of anything better.
+ 'obj': "conversionScope" if setSlot else "obj"
+ }
+ try:
+ wrapCode += wrapForType(self.returnType, self.descriptor, resultTemplateValues)
+ except MethodNotNewObjectError, err:
+ assert not returnsNewObject
+ raise TypeError("%s being returned from non-NewObject method or property %s.%s" %
+ (err.typename,
+ self.descriptor.interface.identifier.name,
+ self.idlNode.identifier.name))
+ if setSlot:
+ # When using a slot on the Xray expando, we need to make sure that
+ # our initial conversion to a JS::Value is done in the caller
+ # compartment. When using a slot on our reflector, we want to do
+ # the conversion in the compartment of that reflector (that is,
+ # slotStorage). In both cases we want to make sure that we finally
+ # set up args.rval() to be in the caller compartment. We also need
+ # to make sure that the conversion steps happen inside a do/while
+ # that they can break out of on success.
+ #
+ # Of course we always have to wrap the value into the slotStorage
+ # compartment before we store it in slotStorage.
+
+ # postConversionSteps are the steps that run while we're still in
+ # the compartment we do our conversion in but after we've finished
+ # the initial conversion into args.rval().
+ postConversionSteps = ""
+ if needsContainsHack(self.idlNode):
+ # Define a .contains on the object that has the same value as
+ # .includes; needed for backwards compat in extensions as we
+ # migrate some DOMStringLists to FrozenArray.
+ postConversionSteps += dedent(
+ """
+ if (args.rval().isObject() && nsContentUtils::ThreadsafeIsCallerChrome()) {
+ JS::Rooted<JSObject*> rvalObj(cx, &args.rval().toObject());
+ JS::Rooted<JS::Value> includesVal(cx);
+ if (!JS_GetProperty(cx, rvalObj, "includes", &includesVal) ||
+ !JS_DefineProperty(cx, rvalObj, "contains", includesVal, JSPROP_ENUMERATE)) {
+ return false;
+ }
+ }
+
+ """)
+ if self.idlNode.getExtendedAttribute("Frozen"):
+ assert self.idlNode.type.isSequence() or self.idlNode.type.isDictionary()
+ freezeValue = CGGeneric(
+ "JS::Rooted<JSObject*> rvalObj(cx, &args.rval().toObject());\n"
+ "if (!JS_FreezeObject(cx, rvalObj)) {\n"
+ " return false;\n"
+ "}\n")
+ if self.idlNode.type.nullable():
+ freezeValue = CGIfWrapper(freezeValue,
+ "args.rval().isObject()")
+ postConversionSteps += freezeValue.define()
+
+ # slotStorageSteps are steps that run once we have entered the
+ # slotStorage compartment.
+ slotStorageSteps= fill(
+ """
+ // Make a copy so that we don't do unnecessary wrapping on args.rval().
+ JS::Rooted<JS::Value> storedVal(cx, args.rval());
+ if (!${maybeWrap}(cx, &storedVal)) {
+ return false;
+ }
+ js::SetReservedSlot(slotStorage, slotIndex, storedVal);
+ """,
+ maybeWrap=getMaybeWrapValueFuncForType(self.idlNode.type))
+
+ checkForXray = mayUseXrayExpandoSlots(self.descriptor, self.idlNode)
+
+ # For the case of Cached attributes, go ahead and preserve our
+ # wrapper if needed. We need to do this because otherwise the
+ # wrapper could get garbage-collected and the cached value would
+ # suddenly disappear, but the whole premise of cached values is that
+ # they never change without explicit action on someone's part. We
+ # don't do this for StoreInSlot, since those get dealt with during
+ # wrapper setup, and failure would involve us trying to clear an
+ # already-preserved wrapper.
+ if (self.idlNode.getExtendedAttribute("Cached") and
+ self.descriptor.wrapperCache):
+ preserveWrapper = dedent(
+ """
+ PreserveWrapper(self);
+ """)
+ if checkForXray:
+ preserveWrapper = fill(
+ """
+ if (!isXray) {
+ // In the Xray case we don't need to do this, because getting the
+ // expando object already preserved our wrapper.
+ $*{preserveWrapper}
+ }
+ """,
+ preserveWrapper=preserveWrapper)
+ slotStorageSteps += preserveWrapper
+
+ if checkForXray:
+ conversionScope = "isXray ? obj : slotStorage"
+ else:
+ conversionScope = "slotStorage"
+
+ wrapCode = fill(
+ """
+ {
+ JS::Rooted<JSObject*> conversionScope(cx, ${conversionScope});
+ JSAutoCompartment ac(cx, conversionScope);
+ do { // block we break out of when done wrapping
+ $*{wrapCode}
+ } while (0);
+ $*{postConversionSteps}
+ }
+ { // And now store things in the compartment of our slotStorage.
+ JSAutoCompartment ac(cx, slotStorage);
+ $*{slotStorageSteps}
+ }
+ // And now make sure args.rval() is in the caller compartment
+ return ${maybeWrap}(cx, args.rval());
+ """,
+ conversionScope=conversionScope,
+ wrapCode=wrapCode,
+ postConversionSteps=postConversionSteps,
+ slotStorageSteps=slotStorageSteps,
+ maybeWrap=getMaybeWrapValueFuncForType(self.idlNode.type))
+ return wrapCode
+
+ def define(self):
+ return (self.cgRoot.define() + 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])
+ self.prepend(CGGeneric("switch (" + expression + ") {\n"))
+ if default is not None:
+ self.append(
+ CGIndenter(
+ CGWrapper(
+ CGIndenter(default),
+ pre="default: {\n",
+ post=" break;\n}\n")))
+
+ self.append(CGGeneric("}\n"))
+
+
+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, [])
+ self.append(CGGeneric("case " + expression + ": {\n"))
+ bodyList = CGList([body])
+ if fallThrough:
+ bodyList.append(CGGeneric("MOZ_FALLTHROUGH;\n"))
+ else:
+ bodyList.append(CGGeneric("break;\n"))
+ self.append(CGIndenter(bodyList))
+ self.append(CGGeneric("}\n"))
+
+
+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, nativeMethodName, static, descriptor, method,
+ isConstructor=False, constructorName=None):
+ CGThing.__init__(self)
+
+ if isConstructor:
+ assert constructorName is not None
+ methodName = constructorName
+ else:
+ methodName = "%s.%s" % (descriptor.interface.identifier.name, method.identifier.name)
+ argDesc = "argument %d of " + methodName
+
+ if method.getExtendedAttribute("UseCounter"):
+ useCounterName = methodName.replace(".", "_")
+ else:
+ useCounterName = None
+
+ if method.isStatic():
+ nativeType = descriptor.nativeType
+ staticTypeOverride = PropertyDefiner.getStringAttr(method, "StaticClassOverride")
+ if (staticTypeOverride):
+ nativeType = staticTypeOverride
+ nativeMethodName = "%s::%s" % (nativeType, nativeMethodName)
+
+ 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], signature[1],
+ nativeMethodName, static, descriptor,
+ method,
+ argConversionStartsAt=argConversionStartsAt,
+ isConstructor=isConstructor,
+ useCounterName=useCounterName)
+
+ 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)
+
+ # Skip required arguments check for maplike/setlike interfaces, as
+ # they can have arguments which are not passed, and are treated as
+ # if undefined had been explicitly passed.
+ if requiredArgs > 0 and not method.isMaplikeOrSetlikeOrIterableMethod():
+ code = fill(
+ """
+ if (MOZ_UNLIKELY(args.length() < ${requiredArgs})) {
+ return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "${methodName}");
+ }
+ """,
+ requiredArgs=requiredArgs,
+ methodName=methodName)
+ self.cgRoot.prepend(CGGeneric(code))
+ return
+
+ # Need to find the right overload
+ maxArgCount = method.maxArgCount
+ allowedArgCounts = method.allowedArgCounts
+
+ argCountCases = []
+ for argCountIdx, argCount in enumerate(allowedArgCounts):
+ possibleSignatures = method.signaturesForArgCount(argCount)
+
+ # Try to optimize away cases when the next argCount in the list
+ # will have the same code as us; if it does, we can fall through to
+ # that case.
+ if argCountIdx+1 < len(allowedArgCounts):
+ nextPossibleSignatures = method.signaturesForArgCount(allowedArgCounts[argCountIdx+1])
+ else:
+ nextPossibleSignatures = None
+ if possibleSignatures == nextPossibleSignatures:
+ # Same set of signatures means we better have the same
+ # distinguishing index. So we can in fact just fall through to
+ # the next case here.
+ assert (len(possibleSignatures) == 1 or
+ (method.distinguishingIndexForArgCount(argCount) ==
+ method.distinguishingIndexForArgCount(allowedArgCounts[argCountIdx+1])))
+ argCountCases.append(CGCase(str(argCount), None, True))
+ continue
+
+ if len(possibleSignatures) == 1:
+ # easy case!
+ signature = possibleSignatures[0]
+ argCountCases.append(
+ CGCase(str(argCount), getPerSignatureCall(signature)))
+ continue
+
+ distinguishingIndex = method.distinguishingIndexForArgCount(argCount)
+
+ def distinguishingArgument(signature):
+ args = signature[1]
+ if distinguishingIndex < len(args):
+ return args[distinguishingIndex]
+ assert args[-1].variadic
+ return args[-1]
+
+ def distinguishingType(signature):
+ return distinguishingArgument(signature).type
+
+ for sig in possibleSignatures:
+ # We should not have "any" args at distinguishingIndex,
+ # since we have multiple possible signatures remaining,
+ # but "any" is never distinguishable from anything else.
+ assert not distinguishingType(sig).isAny()
+ # We can't handle unions at the distinguishing index.
+ if distinguishingType(sig).isUnion():
+ raise TypeError("No support for unions as distinguishing "
+ "arguments yet: %s" %
+ distinguishingArgument(sig).location)
+ # We don't support variadics as the distinguishingArgument yet.
+ # If you want to add support, consider this case:
+ #
+ # void(long... foo);
+ # void(long bar, Int32Array baz);
+ #
+ # in which we have to convert argument 0 to long before picking
+ # an overload... but all the variadic stuff needs to go into a
+ # single array in case we pick that overload, so we have to have
+ # machinery for converting argument 0 to long and then either
+ # placing it in the variadic bit or not. Or something. We may
+ # be able to loosen this restriction if the variadic arg is in
+ # fact at distinguishingIndex, perhaps. Would need to
+ # double-check.
+ if distinguishingArgument(sig).variadic:
+ raise TypeError("No support for variadics as distinguishing "
+ "arguments yet: %s" %
+ distinguishingArgument(sig).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 = [CGArgumentConverter(possibleSignatures[0][1][i],
+ i, descriptor,
+ argDesc % (i + 1), method)
+ for i in range(0, distinguishingIndex)]
+
+ # Select the right overload from our set.
+ distinguishingArg = "args[%d]" % distinguishingIndex
+
+ def tryCall(signature, indent, isDefinitelyObject=False,
+ isNullOrUndefined=False):
+ assert not isDefinitelyObject or not isNullOrUndefined
+ assert isDefinitelyObject or isNullOrUndefined
+ if isDefinitelyObject:
+ failureCode = "break;\n"
+ else:
+ failureCode = None
+ type = distinguishingType(signature)
+ # 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. But note that we
+ # still want to claim that optional arguments are optional, in
+ # case undefined was passed in.
+ argIsOptional = distinguishingArgument(signature).canHaveMissingValue()
+ testCode = instantiateJSToNativeConversion(
+ getJSToNativeConversionInfo(type, descriptor,
+ failureCode=failureCode,
+ isDefinitelyObject=isDefinitelyObject,
+ isNullOrUndefined=isNullOrUndefined,
+ isOptional=argIsOptional,
+ sourceDescription=(argDesc % (distinguishingIndex + 1))),
+ {
+ "declName": "arg%d" % distinguishingIndex,
+ "holderName": ("arg%d" % distinguishingIndex) + "_holder",
+ "val": distinguishingArg,
+ "obj": "obj",
+ "haveValue": "args.hasDefined(%d)" % distinguishingIndex,
+ "passedToJSImpl": toStringBool(isJSImplementedDescriptor(descriptor))
+ },
+ checkForValue=argIsOptional)
+ caseBody.append(CGIndenter(testCode, indent))
+
+ # If we got this far, we know we unwrapped to the right
+ # C++ type, so just do the call. Start conversion with
+ # distinguishingIndex + 1, since we already converted
+ # distinguishingIndex.
+ caseBody.append(CGIndenter(
+ getPerSignatureCall(signature, distinguishingIndex + 1),
+ indent))
+
+ def hasConditionalConversion(type):
+ """
+ Return whether the argument conversion for this type will be
+ conditional on the type of incoming JS value. For example, for
+ interface types the conversion is conditional on the incoming
+ value being isObject().
+
+ For the types for which this returns false, we do not have to
+ output extra isUndefined() or isNullOrUndefined() cases, because
+ null/undefined values will just fall through into our
+ unconditional conversion.
+ """
+ if type.isString() or type.isEnum():
+ return False
+ if type.isBoolean():
+ distinguishingTypes = (distinguishingType(s) for s in
+ possibleSignatures)
+ return any(t.isString() or t.isEnum() or t.isNumeric()
+ for t in distinguishingTypes)
+ if type.isNumeric():
+ distinguishingTypes = (distinguishingType(s) for s in
+ possibleSignatures)
+ return any(t.isString() or t.isEnum()
+ for t in distinguishingTypes)
+ return True
+
+ def needsNullOrUndefinedCase(type):
+ """
+ Return true if the type needs a special isNullOrUndefined() case
+ """
+ return ((type.nullable() and
+ hasConditionalConversion(type)) or
+ type.isDictionary())
+
+ # First check for undefined and optional distinguishing arguments
+ # and output a special branch for that case. Note that we don't
+ # use distinguishingArgument here because we actualy want to
+ # exclude variadic arguments. Also note that we skip this check if
+ # we plan to output a isNullOrUndefined() special case for this
+ # argument anyway, since that will subsume our isUndefined() check.
+ # This is safe, because there can be at most one nullable
+ # distinguishing argument, so if we're it we'll definitely get
+ # picked up by the nullable handling. Also, we can skip this check
+ # if the argument has an unconditional conversion later on.
+ undefSigs = [s for s in possibleSignatures if
+ distinguishingIndex < len(s[1]) and
+ s[1][distinguishingIndex].optional and
+ hasConditionalConversion(s[1][distinguishingIndex].type) and
+ not needsNullOrUndefinedCase(s[1][distinguishingIndex].type)]
+ # Can't have multiple signatures with an optional argument at the
+ # same index.
+ assert len(undefSigs) < 2
+ if len(undefSigs) > 0:
+ caseBody.append(CGGeneric("if (%s.isUndefined()) {\n" %
+ distinguishingArg))
+ tryCall(undefSigs[0], 2, isNullOrUndefined=True)
+ caseBody.append(CGGeneric("}\n"))
+
+ # Next, check for null or undefined. That means looking for
+ # nullable arguments at the distinguishing index and outputting a
+ # separate branch for them. But if the nullable argument has an
+ # unconditional conversion, we don't need to do that. The reason
+ # for that is that at most one argument at the distinguishing index
+ # is nullable (since two nullable arguments are not
+ # distinguishable), and null/undefined values will always fall
+ # through to the unconditional conversion we have, if any, since
+ # they will fail whatever the conditions on the input value are for
+ # our other conversions.
+ nullOrUndefSigs = [s for s in possibleSignatures
+ if needsNullOrUndefinedCase(distinguishingType(s))]
+ # Can't have multiple nullable types here
+ assert len(nullOrUndefSigs) < 2
+ if len(nullOrUndefSigs) > 0:
+ caseBody.append(CGGeneric("if (%s.isNullOrUndefined()) {\n" %
+ distinguishingArg))
+ tryCall(nullOrUndefSigs[0], 2, isNullOrUndefined=True)
+ caseBody.append(CGGeneric("}\n"))
+
+ # Now check for distinguishingArg being various kinds of objects.
+ # The spec says to check for the following things in order:
+ # 1) A platform object that's not a platform array object, being
+ # passed to an interface or "object" arg.
+ # 2) A Date object being passed to a Date or "object" arg.
+ # 3) A RegExp object being passed to a RegExp or "object" arg.
+ # 4) A callable object being passed to a callback or "object" arg.
+ # 5) An iterable object being passed to a sequence arg.
+ # 6) Any non-Date and non-RegExp object being passed to a
+ # array or callback interface or dictionary or
+ # "object" arg.
+
+ # First grab all the overloads that have a non-callback interface
+ # (which includes typed arrays and arraybuffers) at the
+ # distinguishing index. We can also include the ones that have an
+ # "object" here, since if those are present no other object-typed
+ # argument will be.
+ objectSigs = [
+ s for s in possibleSignatures
+ if (distinguishingType(s).isObject() or
+ distinguishingType(s).isNonCallbackInterface())]
+
+ # And all the overloads that take Date
+ objectSigs.extend(s for s in possibleSignatures
+ if distinguishingType(s).isDate())
+
+ # And all the overloads that take callbacks
+ objectSigs.extend(s for s in possibleSignatures
+ if distinguishingType(s).isCallback())
+
+ # And all the overloads that take sequences
+ objectSigs.extend(s for s in possibleSignatures
+ if distinguishingType(s).isSequence())
+
+ # Now append all the overloads that take a dictionary or callback
+ # interface or MozMap. There should be only one of these!
+ genericObjectSigs = [
+ s for s in possibleSignatures
+ if (distinguishingType(s).isDictionary() or
+ distinguishingType(s).isMozMap() or
+ distinguishingType(s).isCallbackInterface())]
+ assert len(genericObjectSigs) <= 1
+ objectSigs.extend(genericObjectSigs)
+
+ # There might be more than one thing in objectSigs; we need to check
+ # which ones we unwrap to.
+ if len(objectSigs) > 0:
+ # Here it's enough to guard on our argument being an object. The
+ # code for unwrapping non-callback interfaces, typed arrays,
+ # sequences, and Dates will just bail out and move on to
+ # the next overload if the object fails to unwrap correctly,
+ # while "object" accepts any object anyway. 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()) {\n" %
+ distinguishingArg))
+ for sig in objectSigs:
+ caseBody.append(CGIndenter(CGGeneric("do {\n")))
+ # Indent by 4, since we need to indent further
+ # than our "do" statement
+ tryCall(sig, 4, isDefinitelyObject=True)
+ caseBody.append(CGIndenter(CGGeneric("} while (0);\n")))
+
+ caseBody.append(CGGeneric("}\n"))
+
+ # Now we only have to consider booleans, numerics, and strings. If
+ # we only have one of them, then we can just output it. But if not,
+ # then we need to output some of the cases conditionally: if we have
+ # a string overload, then boolean and numeric are conditional, and
+ # if not then boolean is conditional if we have a numeric overload.
+ def findUniqueSignature(filterLambda):
+ sigs = filter(filterLambda, possibleSignatures)
+ assert len(sigs) < 2
+ if len(sigs) > 0:
+ return sigs[0]
+ return None
+
+ stringSignature = findUniqueSignature(
+ lambda s: (distinguishingType(s).isString() or
+ distinguishingType(s).isEnum()))
+ numericSignature = findUniqueSignature(
+ lambda s: distinguishingType(s).isNumeric())
+ booleanSignature = findUniqueSignature(
+ lambda s: distinguishingType(s).isBoolean())
+
+ if stringSignature or numericSignature:
+ booleanCondition = "%s.isBoolean()"
+ else:
+ booleanCondition = None
+
+ if stringSignature:
+ numericCondition = "%s.isNumber()"
+ else:
+ numericCondition = None
+
+ def addCase(sig, condition):
+ sigCode = getPerSignatureCall(sig, distinguishingIndex)
+ if condition:
+ sigCode = CGIfWrapper(sigCode,
+ condition % distinguishingArg)
+ caseBody.append(sigCode)
+
+ if booleanSignature:
+ addCase(booleanSignature, booleanCondition)
+ if numericSignature:
+ addCase(numericSignature, numericCondition)
+ if stringSignature:
+ addCase(stringSignature, None)
+
+ if (not booleanSignature and not numericSignature and
+ not stringSignature):
+ # Just throw; we have no idea what we're supposed to
+ # do with this.
+ caseBody.append(CGGeneric(
+ 'return ThrowErrorMessage(cx, MSG_OVERLOAD_RESOLUTION_FAILED, "%d", "%d", "%s");\n' %
+ (distinguishingIndex + 1, argCount, methodName)))
+
+ argCountCases.append(CGCase(str(argCount), CGList(caseBody)))
+
+ overloadCGThings = []
+ overloadCGThings.append(
+ CGGeneric("unsigned argcount = std::min(args.length(), %du);\n" %
+ maxArgCount))
+ overloadCGThings.append(
+ CGSwitch("argcount",
+ argCountCases,
+ CGGeneric('return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "%s");\n' %
+ methodName)))
+ overloadCGThings.append(
+ CGGeneric('MOZ_CRASH("We have an always-returning default case");\n'
+ 'return false;\n'))
+ self.cgRoot = CGList(overloadCGThings)
+
+ 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):
+ if attr.getExtendedAttribute("UseCounter"):
+ useCounterName = "%s_%s_getter" % (descriptor.interface.identifier.name,
+ attr.identifier.name)
+ else:
+ useCounterName = None
+ if attr.isStatic():
+ nativeMethodName = "%s::%s" % (descriptor.nativeType, nativeMethodName)
+ CGPerSignatureCall.__init__(self, returnType, [], nativeMethodName,
+ attr.isStatic(), descriptor, attr,
+ getter=True, useCounterName=useCounterName)
+
+
+class CGNavigatorGetterCall(CGPerSignatureCall):
+ """
+ A class to generate a native object getter call for an IDL getter for a
+ property generated by NavigatorProperty.
+ """
+ def __init__(self, returnType, _, descriptor, attr):
+ nativeMethodName = "%s::ConstructNavigatorObject" % (toBindingNamespace(returnType.inner.identifier.name))
+ CGPerSignatureCall.__init__(self, returnType, [], nativeMethodName,
+ True, descriptor, attr, getter=True)
+
+ def getArguments(self):
+ # The navigator object should be associated with the global of
+ # the navigator it's coming from, which will be the global of
+ # the object whose slot it gets cached in. That's stored in
+ # "slotStorage".
+ return [(FakeArgument(BuiltinTypes[IDLBuiltinType.Types.object],
+ self.idlNode),
+ "slotStorage")]
+
+
+class FakeIdentifier():
+ def __init__(self, name):
+ self.name = name
+
+
+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, name="arg", allowTreatNonCallableAsNull=False):
+ self.type = type
+ self.optional = False
+ self.variadic = False
+ self.defaultValue = None
+ self._allowTreatNonCallableAsNull = allowTreatNonCallableAsNull
+ # For FakeArguments generated by maplike/setlike convenience functions,
+ # we won't have an interfaceMember to pass in.
+ if interfaceMember:
+ self.treatNullAs = interfaceMember.treatNullAs
+ else:
+ self.treatNullAs = "Default"
+ if isinstance(interfaceMember, IDLAttribute):
+ self.enforceRange = interfaceMember.enforceRange
+ self.clamp = interfaceMember.clamp
+ else:
+ self.enforceRange = False
+ self.clamp = False
+
+ self.identifier = FakeIdentifier(name)
+
+ def allowTreatNonCallableAsNull(self):
+ return self._allowTreatNonCallableAsNull
+
+ def canHaveMissingValue(self):
+ return 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):
+ if attr.getExtendedAttribute("UseCounter"):
+ useCounterName = "%s_%s_setter" % (descriptor.interface.identifier.name,
+ attr.identifier.name)
+ else:
+ useCounterName = None
+ if attr.isStatic():
+ nativeMethodName = "%s::%s" % (descriptor.nativeType, nativeMethodName)
+ CGPerSignatureCall.__init__(self, None,
+ [FakeArgument(argType, attr, allowTreatNonCallableAsNull=True)],
+ nativeMethodName, attr.isStatic(),
+ descriptor, attr, setter=True, useCounterName=useCounterName)
+
+ def wrap_return_value(self):
+ attr = self.idlNode
+ if self.descriptor.wrapperCache and attr.slotIndices is not None:
+ if attr.getExtendedAttribute("StoreInSlot"):
+ args = "cx, self"
+ else:
+ args = "self"
+ clearSlot = ("%s(%s);\n" %
+ (MakeClearCachedValueNativeName(self.idlNode), args))
+ else:
+ clearSlot = ""
+
+ # We have no return value
+ return ("\n"
+ "%s"
+ "return true;\n" % clearSlot)
+
+
+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.
+
+ getThisObj should be code for getting a JSObject* for the binding
+ object. If this is None, we will auto-generate code based on
+ descriptor to do the right thing. "" can be passed in if the
+ binding object is already stored in 'obj'.
+
+ callArgs should be code for getting a JS::CallArgs into a variable
+ called 'args'. This can be "" if there is already such a variable
+ around.
+
+ If allowCrossOriginThis is true, then this-unwrapping will first do an
+ UncheckedUnwrap and after that operate on the result.
+ """
+ def __init__(self, descriptor, name, args, unwrapFailureCode=None,
+ getThisObj=None,
+ callArgs="JS::CallArgs args = JS::CallArgsFromVp(argc, vp);\n",
+ allowCrossOriginThis=False):
+ CGAbstractStaticMethod.__init__(self, descriptor, name, "bool", args)
+
+ if unwrapFailureCode is None:
+ self.unwrapFailureCode = 'return ThrowErrorMessage(cx, MSG_THIS_DOES_NOT_IMPLEMENT_INTERFACE, "Value", "%s");\n' % descriptor.interface.identifier.name
+ else:
+ self.unwrapFailureCode = unwrapFailureCode
+
+ if getThisObj == "":
+ self.getThisObj = None
+ else:
+ if getThisObj is None:
+ if descriptor.interface.isOnGlobalProtoChain():
+ ensureCondition = "!args.thisv().isNullOrUndefined() && !args.thisv().isObject()"
+ getThisObj = "args.thisv().isObject() ? &args.thisv().toObject() : js::GetGlobalForObjectCrossCompartment(&args.callee())"
+ else:
+ ensureCondition = "!args.thisv().isObject()"
+ getThisObj = "&args.thisv().toObject()"
+ unwrapFailureCode = self.unwrapFailureCode % {'securityError': 'false'}
+ ensureThisObj = CGIfWrapper(CGGeneric(unwrapFailureCode),
+ ensureCondition)
+ else:
+ ensureThisObj = None
+ self.getThisObj = CGList(
+ [ensureThisObj,
+ CGGeneric("JS::Rooted<JSObject*> obj(cx, %s);\n" %
+ getThisObj)])
+ self.callArgs = callArgs
+ self.allowCrossOriginThis = allowCrossOriginThis
+
+ def definition_body(self):
+ body = self.callArgs
+ if self.getThisObj is not None:
+ body += self.getThisObj.define() + "\n"
+ body += "%s* self;\n" % self.descriptor.nativeType
+ body += dedent(
+ """
+ JS::Rooted<JS::Value> rootSelf(cx, JS::ObjectValue(*obj));
+ """)
+
+ # 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 CastableObjectUnwrapper.
+ body += str(CastableObjectUnwrapper(
+ self.descriptor,
+ "rootSelf",
+ "&rootSelf",
+ "self",
+ self.unwrapFailureCode,
+ allowCrossOriginObj=self.allowCrossOriginThis))
+
+ return body + self.generate_code().define()
+
+ def generate_code(self):
+ assert False # Override me
+
+
+class CGAbstractStaticBindingMethod(CGAbstractStaticMethod):
+ """
+ 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):
+ CGAbstractStaticMethod.__init__(self, descriptor, name, "bool",
+ JSNativeArguments())
+
+ def definition_body(self):
+ # Make sure that "obj" is in the same compartment as "cx", since we'll
+ # later use it to wrap return values.
+ unwrap = dedent("""
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ JS::Rooted<JSObject*> obj(cx, &args.callee());
+
+ """)
+ return unwrap + self.generate_code().define()
+
+ def generate_code(self):
+ assert False # Override me
+
+
+def MakeNativeName(name):
+ return name[0].upper() + IDLToCIdentifier(name[1:])
+
+
+class CGGenericMethod(CGAbstractBindingMethod):
+ """
+ A class for generating the C++ code for an IDL method.
+
+ If allowCrossOriginThis is true, then this-unwrapping will first do an
+ UncheckedUnwrap and after that operate on the result.
+ """
+ def __init__(self, descriptor, allowCrossOriginThis=False):
+ unwrapFailureCode = (
+ 'return ThrowInvalidThis(cx, args, %%(securityError)s, "%s");\n' %
+ descriptor.interface.identifier.name)
+ name = "genericCrossOriginMethod" if allowCrossOriginThis else "genericMethod"
+ CGAbstractBindingMethod.__init__(self, descriptor, name,
+ JSNativeArguments(),
+ unwrapFailureCode=unwrapFailureCode,
+ allowCrossOriginThis=allowCrossOriginThis)
+
+ def generate_code(self):
+ return CGGeneric(dedent("""
+ const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
+ MOZ_ASSERT(info->type() == JSJitInfo::Method);
+ JSJitMethodOp method = info->method;
+ bool ok = method(cx, obj, self, JSJitMethodCallArgs(args));
+ #ifdef DEBUG
+ if (ok) {
+ AssertReturnTypeMatchesJitinfo(info, args.rval());
+ }
+ #endif
+ return ok;
+ """))
+
+
+class CGGenericPromiseReturningMethod(CGAbstractBindingMethod):
+ """
+ A class for generating the C++ code for an IDL method that returns a Promise.
+
+ Does not handle cross-origin this.
+ """
+ def __init__(self, descriptor):
+ unwrapFailureCode = dedent("""
+ ThrowInvalidThis(cx, args, %%(securityError)s, "%s");\n
+ return ConvertExceptionToPromise(cx, xpc::XrayAwareCalleeGlobal(callee),
+ args.rval());\n""" %
+ descriptor.interface.identifier.name)
+
+ name = "genericPromiseReturningMethod"
+ customCallArgs = dedent("""
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ // Make sure to save the callee before someone maybe messes with rval().
+ JS::Rooted<JSObject*> callee(cx, &args.callee());
+ """)
+
+ CGAbstractBindingMethod.__init__(self, descriptor, name,
+ JSNativeArguments(),
+ callArgs=customCallArgs,
+ unwrapFailureCode=unwrapFailureCode)
+
+ def generate_code(self):
+ return CGGeneric(dedent("""
+ const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
+ MOZ_ASSERT(info->type() == JSJitInfo::Method);
+ JSJitMethodOp method = info->method;
+ bool ok = method(cx, obj, self, JSJitMethodCallArgs(args));
+ if (ok) {
+ #ifdef DEBUG
+ AssertReturnTypeMatchesJitinfo(info, args.rval());
+ #endif
+ return true;
+ }
+
+ MOZ_ASSERT(info->returnType() == JSVAL_TYPE_OBJECT);
+ return ConvertExceptionToPromise(cx, xpc::XrayAwareCalleeGlobal(callee),
+ args.rval());
+ """))
+
+
+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 = CppKeywords.checkMethodName(IDLToCIdentifier(method.identifier.name))
+ args = [Argument('JSContext*', 'cx'),
+ Argument('JS::Handle<JSObject*>', 'obj'),
+ Argument('%s*' % descriptor.nativeType, 'self'),
+ Argument('const JSJitMethodCallArgs&', 'args')]
+ CGAbstractStaticMethod.__init__(self, descriptor, name, 'bool', args)
+
+ def definition_body(self):
+ nativeName = CGSpecializedMethod.makeNativeName(self.descriptor,
+ self.method)
+ return CGMethodCall(nativeName, self.method.isStatic(), self.descriptor,
+ self.method).define()
+
+ @staticmethod
+ def makeNativeName(descriptor, method):
+ name = method.identifier.name
+ return MakeNativeName(descriptor.binaryNameFor(name))
+
+
+class CGMethodPromiseWrapper(CGAbstractStaticMethod):
+ """
+ A class for generating a wrapper around another method that will
+ convert exceptions to promises.
+ """
+ def __init__(self, descriptor, methodToWrap):
+ self.method = methodToWrap
+ name = self.makeName(methodToWrap.name)
+ args = list(methodToWrap.args)
+ CGAbstractStaticMethod.__init__(self, descriptor, name, 'bool', args)
+
+ def definition_body(self):
+ return fill(
+ """
+ // Make sure to save the callee before someone maybe messes
+ // with rval().
+ JS::Rooted<JSObject*> callee(cx, &args.callee());
+ bool ok = ${methodName}(${args});
+ if (ok) {
+ return true;
+ }
+ return ConvertExceptionToPromise(cx, xpc::XrayAwareCalleeGlobal(callee),
+ args.rval());
+ """,
+ methodName=self.method.name,
+ args=", ".join(arg.name for arg in self.args))
+
+ @staticmethod
+ def makeName(methodName):
+ return methodName + "_promiseWrapper"
+
+
+class CGJsonifierMethod(CGSpecializedMethod):
+ def __init__(self, descriptor, method):
+ assert method.isJsonifier()
+ CGSpecializedMethod.__init__(self, descriptor, method)
+
+ def definition_body(self):
+ ret = dedent("""
+ JS::Rooted<JSObject*> result(cx, JS_NewPlainObject(cx));
+ if (!result) {
+ return false;
+ }
+ """)
+
+ jsonDescriptors = [self.descriptor]
+ interface = self.descriptor.interface.parent
+ while interface:
+ descriptor = self.descriptor.getDescriptor(interface.identifier.name)
+ if descriptor.operations['Jsonifier']:
+ jsonDescriptors.append(descriptor)
+ interface = interface.parent
+
+ # Iterate the array in reverse: oldest ancestor first
+ for descriptor in jsonDescriptors[::-1]:
+ ret += fill(
+ """
+ if (!${parentclass}::JsonifyAttributes(cx, obj, self, result)) {
+ return false;
+ }
+ """,
+ parentclass=toBindingNamespace(descriptor.name)
+ )
+ ret += ('args.rval().setObject(*result);\n'
+ 'return true;\n')
+ return ret
+
+
+class CGLegacyCallHook(CGAbstractBindingMethod):
+ """
+ Call hook for our object
+ """
+ def __init__(self, descriptor):
+ self._legacycaller = descriptor.operations["LegacyCaller"]
+ # Our "self" is actually the callee in this case, not the thisval.
+ CGAbstractBindingMethod.__init__(
+ self, descriptor, LEGACYCALLER_HOOK_NAME,
+ JSNativeArguments(), getThisObj="&args.callee()")
+
+ def define(self):
+ if not self._legacycaller:
+ return ""
+ return CGAbstractBindingMethod.define(self)
+
+ def generate_code(self):
+ name = self._legacycaller.identifier.name
+ nativeName = MakeNativeName(self.descriptor.binaryNameFor(name))
+ return CGMethodCall(nativeName, False, self.descriptor,
+ self._legacycaller)
+
+
+class CGResolveHook(CGAbstractClassHook):
+ """
+ Resolve hook for objects that have the NeedResolve extended attribute.
+ """
+ def __init__(self, descriptor):
+ assert descriptor.interface.getExtendedAttribute("NeedResolve")
+
+ args = [Argument('JSContext*', 'cx'),
+ Argument('JS::Handle<JSObject*>', 'obj'),
+ Argument('JS::Handle<jsid>', 'id'),
+ Argument('bool*', 'resolvedp')]
+ CGAbstractClassHook.__init__(self, descriptor, RESOLVE_HOOK_NAME,
+ "bool", args)
+
+ def generate_code(self):
+ return dedent("""
+ JS::Rooted<JS::PropertyDescriptor> desc(cx);
+ if (!self->DoResolve(cx, obj, id, &desc)) {
+ return false;
+ }
+ if (!desc.object()) {
+ return true;
+ }
+ // If desc.value() is undefined, then the DoResolve call
+ // has already defined it on the object. Don't try to also
+ // define it.
+ if (!desc.value().isUndefined()) {
+ desc.attributesRef() |= JSPROP_RESOLVING;
+ if (!JS_DefinePropertyById(cx, obj, id, desc)) {
+ return false;
+ }
+ }
+ *resolvedp = true;
+ return true;
+ """)
+
+ def definition_body(self):
+ if self.descriptor.isGlobal():
+ # Resolve standard classes
+ prefix = dedent("""
+ if (!ResolveGlobal(cx, obj, id, resolvedp)) {
+ return false;
+ }
+ if (*resolvedp) {
+ return true;
+ }
+
+ """)
+ else:
+ prefix = ""
+ return prefix + CGAbstractClassHook.definition_body(self)
+
+
+class CGMayResolveHook(CGAbstractStaticMethod):
+ """
+ Resolve hook for objects that have the NeedResolve extended attribute.
+ """
+ def __init__(self, descriptor):
+ assert descriptor.interface.getExtendedAttribute("NeedResolve")
+
+ args = [Argument('const JSAtomState&', 'names'),
+ Argument('jsid', 'id'),
+ Argument('JSObject*', 'maybeObj')]
+ CGAbstractStaticMethod.__init__(self, descriptor, MAY_RESOLVE_HOOK_NAME,
+ "bool", args)
+
+ def definition_body(self):
+ if self.descriptor.isGlobal():
+ # Check whether this would resolve as a standard class.
+ prefix = dedent("""
+ if (MayResolveGlobal(names, id, maybeObj)) {
+ return true;
+ }
+
+ """)
+ else:
+ prefix = ""
+ return (prefix +
+ "return %s::MayResolve(id);\n" % self.descriptor.nativeType)
+
+
+class CGEnumerateHook(CGAbstractBindingMethod):
+ """
+ Enumerate hook for objects with custom hooks.
+ """
+ def __init__(self, descriptor):
+ assert descriptor.interface.getExtendedAttribute("NeedResolve")
+
+ args = [Argument('JSContext*', 'cx'),
+ Argument('JS::Handle<JSObject*>', 'obj')]
+ # Our "self" is actually the "obj" argument in this case, not the thisval.
+ CGAbstractBindingMethod.__init__(
+ self, descriptor, ENUMERATE_HOOK_NAME,
+ args, getThisObj="", callArgs="")
+
+ def generate_code(self):
+ return CGGeneric(dedent("""
+ AutoTArray<nsString, 8> names;
+ binding_detail::FastErrorResult rv;
+ self->GetOwnPropertyNames(cx, names, rv);
+ if (rv.MaybeSetPendingException(cx)) {
+ return false;
+ }
+ bool dummy;
+ for (uint32_t i = 0; i < names.Length(); ++i) {
+ if (!JS_HasUCProperty(cx, obj, names[i].get(), names[i].Length(), &dummy)) {
+ return false;
+ }
+ }
+ return true;
+ """))
+
+ def definition_body(self):
+ if self.descriptor.isGlobal():
+ # Enumerate standard classes
+ prefix = dedent("""
+ if (!EnumerateGlobal(cx, obj)) {
+ return false;
+ }
+
+ """)
+ else:
+ prefix = ""
+ return prefix + CGAbstractBindingMethod.definition_body(self)
+
+
+class CppKeywords():
+ """
+ A class for checking if method names declared in webidl
+ are not in conflict with C++ keywords.
+ """
+ keywords = frozenset([
+ 'alignas', 'alignof', 'and', 'and_eq', 'asm', 'assert', 'auto', 'bitand', 'bitor', 'bool',
+ 'break', 'case', 'catch', 'char', 'char16_t', 'char32_t', 'class', 'compl', 'const',
+ 'constexpr', 'const_cast', 'continue', 'decltype', 'default', 'delete', 'do', 'double',
+ 'dynamic_cast', 'else', 'enum', 'explicit', 'export', 'extern', 'false', 'final', 'float',
+ 'for', 'friend', 'goto', 'if', 'inline', 'int', 'long', 'mutable', 'namespace', 'new',
+ 'noexcept', 'not', 'not_eq', 'nullptr', 'operator', 'or', 'or_eq', 'override', 'private',
+ 'protected', 'public', 'register', 'reinterpret_cast', 'return', 'short', 'signed',
+ 'sizeof', 'static', 'static_assert', 'static_cast', 'struct', 'switch', 'template', 'this',
+ 'thread_local', 'throw', 'true', 'try', 'typedef', 'typeid', 'typename', 'union',
+ 'unsigned', 'using', 'virtual', 'void', 'volatile', 'wchar_t', 'while', 'xor', 'xor_eq'])
+
+ @staticmethod
+ def checkMethodName(name):
+ # Double '_' because 'assert' and '_assert' cannot be used in MS2013 compiler.
+ # Bug 964892 and bug 963560.
+ if name in CppKeywords.keywords:
+ name = '_' + name + '_'
+ return name
+
+
+class CGStaticMethod(CGAbstractStaticBindingMethod):
+ """
+ A class for generating the C++ code for an IDL static method.
+ """
+ def __init__(self, descriptor, method):
+ self.method = method
+ name = CppKeywords.checkMethodName(IDLToCIdentifier(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, allowCrossOriginThis=False):
+ if lenientThis:
+ name = "genericLenientGetter"
+ unwrapFailureCode = dedent("""
+ MOZ_ASSERT(!JS_IsExceptionPending(cx));
+ if (!ReportLenientThisUnwrappingFailure(cx, &args.callee())) {
+ return false;
+ }
+ args.rval().set(JS::UndefinedValue());
+ return true;
+ """)
+ else:
+ if allowCrossOriginThis:
+ name = "genericCrossOriginGetter"
+ else:
+ name = "genericGetter"
+ unwrapFailureCode = (
+ 'return ThrowInvalidThis(cx, args, %%(securityError)s, "%s");\n' %
+ descriptor.interface.identifier.name)
+ CGAbstractBindingMethod.__init__(self, descriptor, name, JSNativeArguments(),
+ unwrapFailureCode,
+ allowCrossOriginThis=allowCrossOriginThis)
+
+ def generate_code(self):
+ return CGGeneric(dedent("""
+ const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
+ MOZ_ASSERT(info->type() == JSJitInfo::Getter);
+ JSJitGetterOp getter = info->getter;
+ bool ok = getter(cx, obj, self, JSJitGetterCallArgs(args));
+ #ifdef DEBUG
+ if (ok) {
+ AssertReturnTypeMatchesJitinfo(info, args.rval());
+ }
+ #endif
+ return ok;
+ """))
+
+
+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_' + IDLToCIdentifier(attr.identifier.name)
+ args = [
+ Argument('JSContext*', 'cx'),
+ Argument('JS::Handle<JSObject*>', 'obj'),
+ Argument('%s*' % descriptor.nativeType, 'self'),
+ Argument('JSJitGetterCallArgs', 'args')
+ ]
+ CGAbstractStaticMethod.__init__(self, descriptor, name, "bool", args)
+
+ def definition_body(self):
+ if self.attr.isMaplikeOrSetlikeAttr():
+ # If the interface is maplike/setlike, there will be one getter
+ # method for the size property of the backing object. Due to having
+ # to unpack the backing object from the slot, this requires its own
+ # generator.
+ return getMaplikeOrSetlikeSizeGetterBody(self.descriptor, self.attr)
+ nativeName = CGSpecializedGetter.makeNativeName(self.descriptor,
+ self.attr)
+ if self.attr.slotIndices is not None:
+ if self.descriptor.hasXPConnectImpls:
+ raise TypeError("Interface '%s' has XPConnect impls, so we "
+ "can't use our slot for property '%s'!" %
+ (self.descriptor.interface.identifier.name,
+ self.attr.identifier.name))
+
+ # We're going to store this return value in a slot on some object,
+ # to cache it. The question is, which object? For dictionary and
+ # sequence return values, we want to use a slot on the Xray expando
+ # if we're called via Xrays, and a slot on our reflector otherwise.
+ # On the other hand, when dealing with some interfacce types
+ # (navigator properties, window.document) we want to avoid calling
+ # the getter more than once. In the case of navigator properties
+ # that's because the getter actually creates a new object each time.
+ # In the case of window.document, it's because the getter can start
+ # returning null, which would get hidden in he non-Xray case by the
+ # fact that it's [StoreOnSlot], so the cached version is always
+ # around.
+ #
+ # The upshot is that we use the reflector slot for any getter whose
+ # type is a gecko interface, whether we're called via Xrays or not.
+ # Since [Cached] and [StoreInSlot] cannot be used with "NewObject",
+ # we know that in the interface type case the returned object is
+ # wrappercached. So creating Xrays to it is reasonable.
+ if mayUseXrayExpandoSlots(self.descriptor, self.attr):
+ prefix = fill(
+ """
+ // Have to either root across the getter call or reget after.
+ bool isXray;
+ JS::Rooted<JSObject*> slotStorage(cx, GetCachedSlotStorageObject(cx, obj, &isXray));
+ if (!slotStorage) {
+ return false;
+ }
+ const size_t slotIndex = isXray ? ${xraySlotIndex} : ${slotIndex};
+ """,
+ xraySlotIndex=memberXrayExpandoReservedSlot(self.attr,
+ self.descriptor),
+ slotIndex=memberReservedSlot(self.attr, self.descriptor))
+ else:
+ prefix = fill(
+ """
+ // Have to either root across the getter call or reget after.
+ JS::Rooted<JSObject*> slotStorage(cx, js::UncheckedUnwrap(obj, /* stopAtWindowProxy = */ false));
+ MOZ_ASSERT(IsDOMObject(slotStorage));
+ const size_t slotIndex = ${slotIndex};
+ """,
+ slotIndex=memberReservedSlot(self.attr, self.descriptor))
+
+ prefix += fill(
+ """
+ MOZ_ASSERT(JSCLASS_RESERVED_SLOTS(js::GetObjectClass(slotStorage)) > slotIndex);
+ {
+ // Scope for cachedVal
+ JS::Value cachedVal = js::GetReservedSlot(slotStorage, slotIndex);
+ if (!cachedVal.isUndefined()) {
+ args.rval().set(cachedVal);
+ // The cached value is in the compartment of slotStorage,
+ // so wrap into the caller compartment as needed.
+ return ${maybeWrap}(cx, args.rval());
+ }
+ }
+
+ """,
+ maybeWrap=getMaybeWrapValueFuncForType(self.attr.type))
+ else:
+ prefix = ""
+
+ if self.attr.navigatorObjectGetter:
+ cgGetterCall = CGNavigatorGetterCall
+ else:
+ cgGetterCall = CGGetterCall
+ return (prefix +
+ cgGetterCall(self.attr.type, nativeName,
+ self.descriptor, self.attr).define())
+
+ @staticmethod
+ def makeNativeName(descriptor, attr):
+ name = attr.identifier.name
+ nativeName = MakeNativeName(descriptor.binaryNameFor(name))
+ _, resultOutParam, _, _, _ = getRetvalDeclarationForType(attr.type,
+ descriptor)
+ infallible = ('infallible' in
+ descriptor.getExtendedAttributes(attr, getter=True))
+ if resultOutParam or attr.type.nullable() or not infallible:
+ nativeName = "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_' + IDLToCIdentifier(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 C++ code for an IDL attribute setter.
+ """
+ def __init__(self, descriptor, lenientThis=False, allowCrossOriginThis=False):
+ if lenientThis:
+ name = "genericLenientSetter"
+ unwrapFailureCode = dedent("""
+ MOZ_ASSERT(!JS_IsExceptionPending(cx));
+ if (!ReportLenientThisUnwrappingFailure(cx, &args.callee())) {
+ return false;
+ }
+ args.rval().set(JS::UndefinedValue());
+ return true;
+ """)
+ else:
+ if allowCrossOriginThis:
+ name = "genericCrossOriginSetter"
+ else:
+ name = "genericSetter"
+ unwrapFailureCode = (
+ 'return ThrowInvalidThis(cx, args, %%(securityError)s, "%s");\n' %
+ descriptor.interface.identifier.name)
+
+ CGAbstractBindingMethod.__init__(self, descriptor, name, JSNativeArguments(),
+ unwrapFailureCode,
+ allowCrossOriginThis=allowCrossOriginThis)
+
+ def generate_code(self):
+ return CGGeneric(fill(
+ """
+ if (args.length() == 0) {
+ return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "${name} attribute setter");
+ }
+ const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
+ MOZ_ASSERT(info->type() == JSJitInfo::Setter);
+ JSJitSetterOp setter = info->setter;
+ if (!setter(cx, obj, self, JSJitSetterCallArgs(args))) {
+ return false;
+ }
+ args.rval().setUndefined();
+ #ifdef DEBUG
+ AssertReturnTypeMatchesJitinfo(info, args.rval());
+ #endif
+ return true;
+ """,
+ name=self.descriptor.interface.identifier.name))
+
+
+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_' + IDLToCIdentifier(attr.identifier.name)
+ args = [Argument('JSContext*', 'cx'),
+ Argument('JS::Handle<JSObject*>', 'obj'),
+ Argument('%s*' % descriptor.nativeType, 'self'),
+ Argument('JSJitSetterCallArgs', 'args')]
+ CGAbstractStaticMethod.__init__(self, descriptor, name, "bool", args)
+
+ def definition_body(self):
+ nativeName = CGSpecializedSetter.makeNativeName(self.descriptor,
+ self.attr)
+ return CGSetterCall(self.attr.type, nativeName, self.descriptor,
+ self.attr).define()
+
+ @staticmethod
+ def makeNativeName(descriptor, attr):
+ name = attr.identifier.name
+ return "Set" + MakeNativeName(descriptor.binaryNameFor(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_' + IDLToCIdentifier(attr.identifier.name)
+ CGAbstractStaticBindingMethod.__init__(self, descriptor, name)
+
+ def generate_code(self):
+ nativeName = CGSpecializedSetter.makeNativeName(self.descriptor,
+ self.attr)
+ checkForArg = CGGeneric(fill(
+ """
+ if (args.length() == 0) {
+ return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "${name} setter");
+ }
+ """,
+ name=self.attr.identifier.name))
+ call = CGSetterCall(self.attr.type, nativeName, self.descriptor,
+ self.attr)
+ return CGList([checkForArg, call])
+
+
+class CGSpecializedForwardingSetter(CGSpecializedSetter):
+ """
+ A class for generating the code for a specialized attribute setter with
+ PutForwards that the JIT can call with lower overhead.
+ """
+ def __init__(self, descriptor, attr):
+ CGSpecializedSetter.__init__(self, descriptor, attr)
+
+ def definition_body(self):
+ attrName = self.attr.identifier.name
+ forwardToAttrName = self.attr.getExtendedAttribute("PutForwards")[0]
+ # JS_GetProperty and JS_SetProperty can only deal with ASCII
+ assert all(ord(c) < 128 for c in attrName)
+ assert all(ord(c) < 128 for c in forwardToAttrName)
+ return fill(
+ """
+ JS::Rooted<JS::Value> v(cx);
+ if (!JS_GetProperty(cx, obj, "${attr}", &v)) {
+ return false;
+ }
+
+ if (!v.isObject()) {
+ return ThrowErrorMessage(cx, MSG_NOT_OBJECT, "${interface}.${attr}");
+ }
+
+ JS::Rooted<JSObject*> targetObj(cx, &v.toObject());
+ return JS_SetProperty(cx, targetObj, "${forwardToAttrName}", args[0]);
+ """,
+ attr=attrName,
+ interface=self.descriptor.interface.identifier.name,
+ forwardToAttrName=forwardToAttrName)
+
+
+class CGSpecializedReplaceableSetter(CGSpecializedSetter):
+ """
+ A class for generating the code for a specialized attribute setter with
+ Replaceable that the JIT can call with lower overhead.
+ """
+ def __init__(self, descriptor, attr):
+ CGSpecializedSetter.__init__(self, descriptor, attr)
+
+ def definition_body(self):
+ attrName = self.attr.identifier.name
+ # JS_DefineProperty can only deal with ASCII
+ assert all(ord(c) < 128 for c in attrName)
+ return ('return JS_DefineProperty(cx, obj, "%s", args[0], JSPROP_ENUMERATE);\n' %
+ attrName)
+
+
+class CGSpecializedLenientSetter(CGSpecializedSetter):
+ """
+ A class for generating the code for a specialized attribute setter with
+ LenientSetter that the JIT can call with lower overhead.
+ """
+ def __init__(self, descriptor, attr):
+ CGSpecializedSetter.__init__(self, descriptor, attr)
+
+ def definition_body(self):
+ attrName = self.attr.identifier.name
+ # JS_DefineProperty can only deal with ASCII
+ assert all(ord(c) < 128 for c in attrName)
+ return dedent("""
+ DeprecationWarning(cx, obj, nsIDocument::eLenientSetter);
+ return true;
+ """)
+
+
+def memberReturnsNewObject(member):
+ return member.getExtendedAttribute("NewObject") 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, opType, infallible, movable,
+ eliminatable, aliasSet, alwaysInSlot, lazilyInSlot,
+ slotIndex, returnTypes, args):
+ """
+ aliasSet is a JSJitInfo::AliasSet value, without the "JSJitInfo::" bit.
+
+ args is None if we don't want to output argTypes for some
+ reason (e.g. we have overloads or we're not a method) and
+ otherwise an iterable of the arguments for this method.
+ """
+ assert(not movable or aliasSet != "AliasEverything") # Can't move write-aliasing things
+ assert(not alwaysInSlot or movable) # Things always in slots had better be movable
+ assert(not eliminatable or aliasSet != "AliasEverything") # Can't eliminate write-aliasing things
+ assert(not alwaysInSlot or eliminatable) # Things always in slots had better be eliminatable
+
+ def jitInfoInitializer(isTypedMethod):
+ initializer = fill(
+ """
+ {
+ { ${opName} },
+ { prototypes::id::${name} },
+ { PrototypeTraits<prototypes::id::${name}>::Depth },
+ JSJitInfo::${opType},
+ JSJitInfo::${aliasSet}, /* aliasSet. Not relevant for setters. */
+ ${returnType}, /* returnType. Not relevant for setters. */
+ ${isInfallible}, /* isInfallible. False in setters. */
+ ${isMovable}, /* isMovable. Not relevant for setters. */
+ ${isEliminatable}, /* isEliminatable. Not relevant for setters. */
+ ${isAlwaysInSlot}, /* isAlwaysInSlot. Only relevant for getters. */
+ ${isLazilyCachedInSlot}, /* isLazilyCachedInSlot. Only relevant for getters. */
+ ${isTypedMethod}, /* isTypedMethod. Only relevant for methods. */
+ ${slotIndex} /* Reserved slot index, if we're stored in a slot, else 0. */
+ }
+ """,
+ opName=opName,
+ name=self.descriptor.name,
+ opType=opType,
+ aliasSet=aliasSet,
+ returnType=reduce(CGMemberJITInfo.getSingleReturnType, returnTypes,
+ ""),
+ isInfallible=toStringBool(infallible),
+ isMovable=toStringBool(movable),
+ isEliminatable=toStringBool(eliminatable),
+ isAlwaysInSlot=toStringBool(alwaysInSlot),
+ isLazilyCachedInSlot=toStringBool(lazilyInSlot),
+ isTypedMethod=toStringBool(isTypedMethod),
+ slotIndex=slotIndex)
+ return initializer.rstrip()
+
+ slotAssert = fill(
+ """
+ static_assert(${slotIndex} <= JSJitInfo::maxSlotIndex, "We won't fit");
+ static_assert(${slotIndex} < ${classReservedSlots}, "There is no slot for us");
+ """,
+ slotIndex=slotIndex,
+ classReservedSlots=INSTANCE_RESERVED_SLOTS + self.descriptor.interface.totalMembersInSlots)
+ if args is not None:
+ argTypes = "%s_argTypes" % infoName
+ args = [CGMemberJITInfo.getJSArgType(arg.type) for arg in args]
+ args.append("JSJitInfo::ArgTypeListEnd")
+ argTypesDecl = (
+ "static const JSJitInfo::ArgType %s[] = { %s };\n" %
+ (argTypes, ", ".join(args)))
+ return fill(
+ """
+ $*{argTypesDecl}
+ static const JSTypedMethodJitInfo ${infoName} = {
+ ${jitInfo},
+ ${argTypes}
+ };
+ $*{slotAssert}
+ """,
+ argTypesDecl=argTypesDecl,
+ infoName=infoName,
+ jitInfo=indent(jitInfoInitializer(True)),
+ argTypes=argTypes,
+ slotAssert=slotAssert)
+
+ return fill(
+ """
+ static const JSJitInfo ${infoName} = ${jitInfo};
+ $*{slotAssert}
+ """,
+ infoName=infoName,
+ jitInfo=jitInfoInitializer(False),
+ slotAssert=slotAssert)
+
+ def define(self):
+ if self.member.isAttr():
+ getterinfo = ("%s_getterinfo" %
+ IDLToCIdentifier(self.member.identifier.name))
+ # We need the cast here because JSJitGetterOp has a "void* self"
+ # while we have the right type.
+ getter = ("(JSJitGetterOp)get_%s" %
+ IDLToCIdentifier(self.member.identifier.name))
+ getterinfal = "infallible" in self.descriptor.getExtendedAttributes(self.member, getter=True)
+
+ movable = self.mayBeMovable() and getterinfal
+ eliminatable = self.mayBeEliminatable() and getterinfal
+ aliasSet = self.aliasSet()
+
+ getterinfal = getterinfal and infallibleForMember(self.member, self.member.type, self.descriptor)
+ isAlwaysInSlot = self.member.getExtendedAttribute("StoreInSlot")
+ if self.member.slotIndices is not None:
+ assert isAlwaysInSlot or self.member.getExtendedAttribute("Cached")
+ isLazilyCachedInSlot = not isAlwaysInSlot
+ slotIndex = memberReservedSlot(self.member, self.descriptor)
+ # We'll statically assert that this is not too big in
+ # CGUpdateMemberSlotsMethod, in the case when
+ # isAlwaysInSlot is true.
+ else:
+ isLazilyCachedInSlot = False
+ slotIndex = "0"
+
+ result = self.defineJitInfo(getterinfo, getter, "Getter",
+ getterinfal, movable, eliminatable,
+ aliasSet, isAlwaysInSlot,
+ isLazilyCachedInSlot, slotIndex,
+ [self.member.type], None)
+ if (not self.member.readonly or
+ self.member.getExtendedAttribute("PutForwards") is not None or
+ self.member.getExtendedAttribute("Replaceable") is not None or
+ self.member.getExtendedAttribute("LenientSetter") is not None):
+ setterinfo = ("%s_setterinfo" %
+ IDLToCIdentifier(self.member.identifier.name))
+ # Actually a JSJitSetterOp, but JSJitGetterOp is first in the
+ # union.
+ setter = ("(JSJitGetterOp)set_%s" %
+ IDLToCIdentifier(self.member.identifier.name))
+ # Setters are always fallible, since they have to do a typed unwrap.
+ result += self.defineJitInfo(setterinfo, setter, "Setter",
+ False, False, False, "AliasEverything",
+ False, False, "0",
+ [BuiltinTypes[IDLBuiltinType.Types.void]],
+ None)
+ return result
+ if self.member.isMethod():
+ methodinfo = ("%s_methodinfo" %
+ IDLToCIdentifier(self.member.identifier.name))
+ name = CppKeywords.checkMethodName(
+ IDLToCIdentifier(self.member.identifier.name))
+ if self.member.returnsPromise():
+ name = CGMethodPromiseWrapper.makeName(name)
+ # Actually a JSJitMethodOp, but JSJitGetterOp is first in the union.
+ method = ("(JSJitGetterOp)%s" % 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.
+ 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.
+ methodInfal = False
+ args = None
+ movable = False
+ eliminatable = False
+ else:
+ sig = sigs[0]
+ # For methods that affect nothing, it's OK to set movable to our
+ # notion of infallible on the C++ side, without considering
+ # argument conversions, since argument conversions that can
+ # reliably throw would be effectful anyway and the jit doesn't
+ # move effectful things.
+ hasInfallibleImpl = "infallible" in self.descriptor.getExtendedAttributes(self.member)
+ movable = self.mayBeMovable() and hasInfallibleImpl
+ eliminatable = self.mayBeEliminatable() and hasInfallibleImpl
+ # XXXbz can we move the smarts about fallibility due to arg
+ # conversions into the JIT, using our new args stuff?
+ if (len(sig[1]) != 0 or
+ not infallibleForMember(self.member, sig[0], self.descriptor)):
+ # We have arguments or our return-value boxing can fail
+ methodInfal = False
+ else:
+ methodInfal = hasInfallibleImpl
+ # For now, only bother to output args if we're side-effect-free.
+ if self.member.affects == "Nothing":
+ args = sig[1]
+ else:
+ args = None
+
+ aliasSet = self.aliasSet()
+ result = self.defineJitInfo(methodinfo, method, "Method",
+ methodInfal, movable, eliminatable,
+ aliasSet, False, False, "0",
+ [s[0] for s in sigs], args)
+ return result
+ raise TypeError("Illegal member type to CGPropertyJITInfo")
+
+ def mayBeMovable(self):
+ """
+ Returns whether this attribute or method may be movable, just
+ based on Affects/DependsOn annotations.
+ """
+ affects = self.member.affects
+ dependsOn = self.member.dependsOn
+ assert affects in IDLInterfaceMember.AffectsValues
+ assert dependsOn in IDLInterfaceMember.DependsOnValues
+ # Things that are DependsOn=DeviceState are not movable, because we
+ # don't want them coalesced with each other or loop-hoisted, since
+ # their return value can change even if nothing is going on from our
+ # point of view.
+ return (affects == "Nothing" and
+ (dependsOn != "Everything" and dependsOn != "DeviceState"))
+
+ def mayBeEliminatable(self):
+ """
+ Returns whether this attribute or method may be eliminatable, just
+ based on Affects/DependsOn annotations.
+ """
+ # dependsOn shouldn't affect this decision at all, except in jitinfo we
+ # have no way to express "Depends on everything, affects nothing",
+ # because we only have three alias set values: AliasNone ("depends on
+ # nothing, affects nothing"), AliasDOMSets ("depends on DOM sets,
+ # affects nothing"), AliasEverything ("depends on everything, affects
+ # everything"). So the [Affects=Nothing, DependsOn=Everything] case
+ # gets encoded as AliasEverything and defineJitInfo asserts that if our
+ # alias state is AliasEverything then we're not eliminatable (because it
+ # thinks we might have side-effects at that point). Bug 1155796 is
+ # tracking possible solutions for this.
+ affects = self.member.affects
+ dependsOn = self.member.dependsOn
+ assert affects in IDLInterfaceMember.AffectsValues
+ assert dependsOn in IDLInterfaceMember.DependsOnValues
+ return affects == "Nothing" and dependsOn != "Everything"
+
+ def aliasSet(self):
+ """
+ Returns the alias set to store in the jitinfo. This may not be the
+ effective alias set the JIT uses, depending on whether we have enough
+ information about our args to allow the JIT to prove that effectful
+ argument conversions won't happen.
+ """
+ dependsOn = self.member.dependsOn
+ assert dependsOn in IDLInterfaceMember.DependsOnValues
+
+ if dependsOn == "Nothing" or dependsOn == "DeviceState":
+ assert self.member.affects == "Nothing"
+ return "AliasNone"
+
+ if dependsOn == "DOMState":
+ assert self.member.affects == "Nothing"
+ return "AliasDOMSets"
+
+ return "AliasEverything"
+
+ @staticmethod
+ def getJSReturnTypeTag(t):
+ if t.nullable():
+ # Sometimes it might return null, sometimes not
+ return "JSVAL_TYPE_UNKNOWN"
+ if t.isVoid():
+ # No return, every time
+ return "JSVAL_TYPE_UNDEFINED"
+ if t.isSequence():
+ return "JSVAL_TYPE_OBJECT"
+ if t.isMozMap():
+ return "JSVAL_TYPE_OBJECT"
+ if t.isGeckoInterface():
+ return "JSVAL_TYPE_OBJECT"
+ if t.isString():
+ return "JSVAL_TYPE_STRING"
+ if t.isEnum():
+ return "JSVAL_TYPE_STRING"
+ if t.isCallback():
+ return "JSVAL_TYPE_OBJECT"
+ if t.isAny():
+ # The whole point is to return various stuff
+ return "JSVAL_TYPE_UNKNOWN"
+ if t.isObject():
+ return "JSVAL_TYPE_OBJECT"
+ if t.isSpiderMonkeyInterface():
+ return "JSVAL_TYPE_OBJECT"
+ if t.isUnion():
+ u = t.unroll()
+ if u.hasNullableType:
+ # Might be null or not
+ return "JSVAL_TYPE_UNKNOWN"
+ return reduce(CGMemberJITInfo.getSingleReturnType,
+ u.flatMemberTypes, "")
+ if t.isDictionary():
+ return "JSVAL_TYPE_OBJECT"
+ if t.isDate():
+ return "JSVAL_TYPE_OBJECT"
+ if not t.isPrimitive():
+ raise TypeError("No idea what type " + str(t) + " is.")
+ tag = t.tag()
+ if tag == IDLType.Tags.bool:
+ return "JSVAL_TYPE_BOOLEAN"
+ if tag in [IDLType.Tags.int8, IDLType.Tags.uint8,
+ IDLType.Tags.int16, IDLType.Tags.uint16,
+ IDLType.Tags.int32]:
+ return "JSVAL_TYPE_INT32"
+ if tag in [IDLType.Tags.int64, IDLType.Tags.uint64,
+ IDLType.Tags.unrestricted_float, IDLType.Tags.float,
+ IDLType.Tags.unrestricted_double, IDLType.Tags.double]:
+ # These all use JS_NumberValue, which can return int or double.
+ # But TI treats "double" as meaning "int or double", so we're
+ # good to return JSVAL_TYPE_DOUBLE here.
+ return "JSVAL_TYPE_DOUBLE"
+ if tag != IDLType.Tags.uint32:
+ raise TypeError("No idea what type " + str(t) + " is.")
+ # uint32 is sometimes int and sometimes double.
+ return "JSVAL_TYPE_DOUBLE"
+
+ @staticmethod
+ def getSingleReturnType(existingType, t):
+ type = CGMemberJITInfo.getJSReturnTypeTag(t)
+ if existingType == "":
+ # First element of the list; just return its type
+ return type
+
+ if type == existingType:
+ return existingType
+ if ((type == "JSVAL_TYPE_DOUBLE" and
+ existingType == "JSVAL_TYPE_INT32") or
+ (existingType == "JSVAL_TYPE_DOUBLE" and
+ type == "JSVAL_TYPE_INT32")):
+ # Promote INT32 to DOUBLE as needed
+ return "JSVAL_TYPE_DOUBLE"
+ # Different types
+ return "JSVAL_TYPE_UNKNOWN"
+
+ @staticmethod
+ def getJSArgType(t):
+ assert not t.isVoid()
+ if t.nullable():
+ # Sometimes it might return null, sometimes not
+ return "JSJitInfo::ArgType(JSJitInfo::Null | %s)" % CGMemberJITInfo.getJSArgType(t.inner)
+ if t.isSequence():
+ return "JSJitInfo::Object"
+ if t.isGeckoInterface():
+ return "JSJitInfo::Object"
+ if t.isString():
+ return "JSJitInfo::String"
+ if t.isEnum():
+ return "JSJitInfo::String"
+ if t.isCallback():
+ return "JSJitInfo::Object"
+ if t.isAny():
+ # The whole point is to return various stuff
+ return "JSJitInfo::Any"
+ if t.isObject():
+ return "JSJitInfo::Object"
+ if t.isSpiderMonkeyInterface():
+ return "JSJitInfo::Object"
+ if t.isUnion():
+ u = t.unroll()
+ type = "JSJitInfo::Null" if u.hasNullableType else ""
+ return ("JSJitInfo::ArgType(%s)" %
+ reduce(CGMemberJITInfo.getSingleArgType,
+ u.flatMemberTypes, type))
+ if t.isDictionary():
+ return "JSJitInfo::Object"
+ if t.isDate():
+ return "JSJitInfo::Object"
+ if not t.isPrimitive():
+ raise TypeError("No idea what type " + str(t) + " is.")
+ tag = t.tag()
+ if tag == IDLType.Tags.bool:
+ return "JSJitInfo::Boolean"
+ if tag in [IDLType.Tags.int8, IDLType.Tags.uint8,
+ IDLType.Tags.int16, IDLType.Tags.uint16,
+ IDLType.Tags.int32]:
+ return "JSJitInfo::Integer"
+ if tag in [IDLType.Tags.int64, IDLType.Tags.uint64,
+ IDLType.Tags.unrestricted_float, IDLType.Tags.float,
+ IDLType.Tags.unrestricted_double, IDLType.Tags.double]:
+ # These all use JS_NumberValue, which can return int or double.
+ # But TI treats "double" as meaning "int or double", so we're
+ # good to return JSVAL_TYPE_DOUBLE here.
+ return "JSJitInfo::Double"
+ if tag != IDLType.Tags.uint32:
+ raise TypeError("No idea what type " + str(t) + " is.")
+ # uint32 is sometimes int and sometimes double.
+ return "JSJitInfo::Double"
+
+ @staticmethod
+ def getSingleArgType(existingType, t):
+ type = CGMemberJITInfo.getJSArgType(t)
+ if existingType == "":
+ # First element of the list; just return its type
+ return type
+
+ if type == existingType:
+ return existingType
+ return "%s | %s" % (existingType, type)
+
+
+class CGStaticMethodJitinfo(CGGeneric):
+ """
+ A class for generating the JITInfo for a promise-returning static method.
+ """
+ def __init__(self, method):
+ CGGeneric.__init__(
+ self,
+ "\n"
+ "static const JSJitInfo %s_methodinfo = {\n"
+ " { (JSJitGetterOp)%s },\n"
+ " { prototypes::id::_ID_Count }, { 0 }, JSJitInfo::StaticMethod,\n"
+ " JSJitInfo::AliasEverything, JSVAL_TYPE_MISSING, false, false,\n"
+ " false, false, 0\n"
+ "};\n" %
+ (IDLToCIdentifier(method.identifier.name),
+ CppKeywords.checkMethodName(
+ IDLToCIdentifier(method.identifier.name))))
+
+
+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):
+ return '_' + value
+ 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"
+ nativeName = MakeNativeName(value)
+ if nativeName == "EndGuard_":
+ raise SyntaxError('Enum value "' + value + '" cannot be used because it'
+ ' collides with our internal EndGuard_ value. Please'
+ ' rename our internal EndGuard_ to something else')
+ return nativeName
+
+class CGEnumToJSValue(CGAbstractMethod):
+ def __init__(self, enum):
+ enumType = enum.identifier.name
+ self.stringsArray = enumType + "Values::" + ENUM_ENTRY_VARIABLE_NAME
+ CGAbstractMethod.__init__(self, None, "ToJSValue", "bool",
+ [Argument("JSContext*", "aCx"),
+ Argument(enumType, "aArgument"),
+ Argument("JS::MutableHandle<JS::Value>",
+ "aValue")])
+
+ def definition_body(self):
+ return fill(
+ """
+ MOZ_ASSERT(uint32_t(aArgument) < ArrayLength(${strings}));
+ JSString* resultStr =
+ JS_NewStringCopyN(aCx, ${strings}[uint32_t(aArgument)].value,
+ ${strings}[uint32_t(aArgument)].length);
+ if (!resultStr) {
+ return false;
+ }
+ aValue.setString(resultStr);
+ return true;
+ """,
+ strings=self.stringsArray)
+
+
+class CGEnum(CGThing):
+ def __init__(self, enum):
+ CGThing.__init__(self)
+ self.enum = enum
+ strings = CGNamespace(
+ self.stringsNamespace(),
+ CGGeneric(declare=("extern const EnumEntry %s[%d];\n" %
+ (ENUM_ENTRY_VARIABLE_NAME, self.nEnumStrings())),
+ define=fill(
+ """
+ extern const EnumEntry ${name}[${count}] = {
+ $*{entries}
+ { nullptr, 0 }
+ };
+ """,
+ name=ENUM_ENTRY_VARIABLE_NAME,
+ count=self.nEnumStrings(),
+ entries=''.join('{"%s", %d},\n' % (val, len(val))
+ for val in self.enum.values()))))
+ toJSValue = CGEnumToJSValue(enum)
+ self.cgThings = CGList([strings, toJSValue], "\n")
+
+ def stringsNamespace(self):
+ return self.enum.identifier.name + "Values"
+
+ def nEnumStrings(self):
+ return len(self.enum.values()) + 1
+
+ def declare(self):
+ decl = fill(
+ """
+ enum class ${name} : uint32_t {
+ $*{enums}
+ EndGuard_
+ };
+ """,
+ name=self.enum.identifier.name,
+ enums=",\n".join(map(getEnumValueName, self.enum.values())) + ",\n")
+ strings = CGNamespace(self.stringsNamespace(),
+ CGGeneric(declare="extern const EnumEntry %s[%d];\n"
+ % (ENUM_ENTRY_VARIABLE_NAME, self.nEnumStrings())))
+ return decl + "\n" + self.cgThings.declare()
+
+ def define(self):
+ return self.cgThings.define()
+
+ def deps(self):
+ return self.enum.getDeps()
+
+
+def getUnionAccessorSignatureType(type, descriptorProvider):
+ """
+ Returns the types that are used in the getter and setter signatures for
+ union types
+ """
+ # Flat member types have already unwrapped nullables.
+ assert not type.nullable()
+
+ if type.isSequence() or type.isMozMap():
+ if type.isSequence():
+ wrapperType = "Sequence"
+ else:
+ wrapperType = "MozMap"
+ # We don't use the returned template here, so it's OK to just pass no
+ # sourceDescription.
+ elementInfo = getJSToNativeConversionInfo(type.inner,
+ descriptorProvider,
+ isMember=wrapperType)
+ return CGTemplatedType(wrapperType, elementInfo.declType,
+ isConst=True, isReference=True)
+
+ # Nested unions are unwrapped automatically into our flatMemberTypes.
+ assert not type.isUnion()
+
+ if type.isGeckoInterface():
+ descriptor = descriptorProvider.getDescriptor(
+ type.unroll().inner.identifier.name)
+ typeName = CGGeneric(descriptor.nativeType)
+ # Allow null pointers for old-binding classes.
+ if type.unroll().inner.isExternal():
+ typeName = CGWrapper(typeName, post="*")
+ else:
+ typeName = CGWrapper(typeName, post="&")
+ return typeName
+
+ if type.isSpiderMonkeyInterface():
+ typeName = CGGeneric(type.name)
+ return CGWrapper(typeName, post=" const &")
+
+ if type.isDOMString() or type.isUSVString():
+ return CGGeneric("const nsAString&")
+
+ if type.isByteString():
+ return CGGeneric("const nsCString&")
+
+ if type.isEnum():
+ return CGGeneric(type.inner.identifier.name)
+
+ if type.isCallback():
+ return CGGeneric("%s&" % type.unroll().callback.identifier.name)
+
+ if type.isAny():
+ return CGGeneric("JS::Value")
+
+ if type.isObject():
+ return CGGeneric("JSObject*")
+
+ if type.isDictionary():
+ return CGGeneric("const %s&" % type.inner.identifier.name)
+
+ if not type.isPrimitive():
+ raise TypeError("Need native type for argument type '%s'" % str(type))
+
+ return CGGeneric(builtinNames[type.tag()])
+
+
+def getUnionTypeTemplateVars(unionType, type, descriptorProvider,
+ ownsMembers=False):
+ name = getUnionMemberName(type)
+ holderName = "m" + name + "Holder"
+
+ # By the time tryNextCode is invoked, we're guaranteed the union has been
+ # constructed as some type, since we've been trying to convert into the
+ # corresponding member.
+ prefix = "" if ownsMembers else "mUnion."
+ tryNextCode = ("$*{destroyHolder}\n"
+ "%sDestroy%s();\n"
+ "tryNext = true;\n"
+ "return true;\n" % (prefix, name))
+
+ conversionInfo = getJSToNativeConversionInfo(
+ type, descriptorProvider, failureCode=tryNextCode,
+ isDefinitelyObject=not type.isDictionary(),
+ isMember=("OwningUnion" if ownsMembers else None),
+ sourceDescription="member of %s" % unionType)
+
+ if conversionInfo.holderType is not None:
+ assert not ownsMembers
+ destroyHolder = "%s.reset();\n" % holderName
+ else:
+ destroyHolder = ""
+
+ ctorNeedsCx = conversionInfo.declArgs == "cx"
+ ctorArgs = "cx" if ctorNeedsCx else ""
+
+ structType = conversionInfo.declType.define()
+ externalType = getUnionAccessorSignatureType(type, descriptorProvider).define()
+
+ if type.isObject():
+ if ownsMembers:
+ body = dedent("""
+ MOZ_ASSERT(mType == eUninitialized);
+ mValue.mObject.SetValue(obj);
+ mType = eObject;
+ """)
+ else:
+ body = dedent("""
+ MOZ_ASSERT(mUnion.mType == mUnion.eUninitialized);
+ mUnion.mValue.mObject.SetValue(cx, obj);
+ mUnion.mType = mUnion.eObject;
+ """)
+
+ # It's a bit sketchy to do the security check after setting the value,
+ # but it keeps the code cleaner and lets us avoid rooting |obj| over the
+ # call to CallerSubsumes().
+ body = body + dedent("""
+ if (passedToJSImpl && !CallerSubsumes(obj)) {
+ ThrowErrorMessage(cx, MSG_PERMISSION_DENIED_TO_PASS_ARG, "%s");
+ return false;
+ }
+ return true;
+ """)
+
+ setter = ClassMethod("SetToObject", "bool",
+ [Argument("JSContext*", "cx"),
+ Argument("JSObject*", "obj"),
+ Argument("bool", "passedToJSImpl", default="false")],
+ inline=True, bodyInHeader=True,
+ body=body)
+
+ else:
+ # Important: we need to not have our declName involve
+ # maybe-GCing operations.
+ if conversionInfo.holderType is not None:
+ holderArgs = conversionInfo.holderArgs
+ if holderArgs is None:
+ holderArgs = ""
+ initHolder = "%s.emplace(%s);\n" % (holderName, holderArgs)
+ else:
+ initHolder = ""
+
+ jsConversion = fill(
+ initHolder + conversionInfo.template,
+ val="value",
+ maybeMutableVal="value",
+ declName="memberSlot",
+ holderName=(holderName if ownsMembers else "%s.ref()" % holderName),
+ destroyHolder=destroyHolder,
+ passedToJSImpl="passedToJSImpl")
+
+ jsConversion = fill(
+ """
+ tryNext = false;
+ { // scope for memberSlot
+ ${structType}& memberSlot = RawSetAs${name}(${ctorArgs});
+ $*{jsConversion}
+ }
+ return true;
+ """,
+ structType=structType,
+ name=name,
+ ctorArgs=ctorArgs,
+ jsConversion=jsConversion)
+
+ if ownsMembers:
+ handleType = "JS::Handle<JS::Value>"
+ else:
+ handleType = "JS::MutableHandle<JS::Value>"
+
+ setter = ClassMethod("TrySetTo" + name, "bool",
+ [Argument("JSContext*", "cx"),
+ Argument(handleType, "value"),
+ Argument("bool&", "tryNext"),
+ Argument("bool", "passedToJSImpl", default="false")],
+ inline=not ownsMembers,
+ bodyInHeader=not ownsMembers,
+ body=jsConversion)
+
+ return {
+ "name": name,
+ "structType": structType,
+ "externalType": externalType,
+ "setter": setter,
+ "holderType": conversionInfo.holderType.define() if conversionInfo.holderType else None,
+ "ctorArgs": ctorArgs,
+ "ctorArgList": [Argument("JSContext*", "cx")] if ctorNeedsCx else []
+ }
+
+
+class CGUnionStruct(CGThing):
+ def __init__(self, type, descriptorProvider, ownsMembers=False):
+ CGThing.__init__(self)
+ self.type = type.unroll()
+ self.descriptorProvider = descriptorProvider
+ self.ownsMembers = ownsMembers
+ self.struct = self.getStruct()
+
+ def declare(self):
+ return self.struct.declare()
+
+ def define(self):
+ return self.struct.define()
+
+ def deps(self):
+ return self.type.getDeps()
+
+ def getStruct(self):
+
+ members = [ClassMember("mType", "Type", body="eUninitialized"),
+ ClassMember("mValue", "Value")]
+ ctor = ClassConstructor([], bodyInHeader=True, visibility="public",
+ explicit=True)
+
+ methods = []
+ enumValues = ["eUninitialized"]
+ toJSValCases = [CGCase("eUninitialized", CGGeneric("return false;\n"))]
+ destructorCases = [CGCase("eUninitialized", None)]
+ assignmentCases = [
+ CGCase("eUninitialized",
+ CGGeneric('MOZ_ASSERT(mType == eUninitialized,\n'
+ ' "We need to destroy ourselves?");\n'))]
+ traceCases = []
+ unionValues = []
+ if self.type.hasNullableType:
+ enumValues.append("eNull")
+ methods.append(ClassMethod("IsNull", "bool", [], const=True, inline=True,
+ body="return mType == eNull;\n",
+ bodyInHeader=True))
+ methods.append(ClassMethod("SetNull", "void", [], inline=True,
+ body=("Uninit();\n"
+ "mType = eNull;\n"),
+ bodyInHeader=True))
+ destructorCases.append(CGCase("eNull", None))
+ assignmentCases.append(CGCase("eNull",
+ CGGeneric("MOZ_ASSERT(mType == eUninitialized);\n"
+ "mType = eNull;\n")))
+ toJSValCases.append(CGCase("eNull", CGGeneric("rval.setNull();\n"
+ "return true;\n")))
+
+ hasObjectType = any(t.isObject() for t in self.type.flatMemberTypes)
+ for t in self.type.flatMemberTypes:
+ vars = getUnionTypeTemplateVars(self.type,
+ t, self.descriptorProvider,
+ ownsMembers=self.ownsMembers)
+ if vars["name"] != "Object" or self.ownsMembers:
+ body = fill(
+ """
+ if (mType == e${name}) {
+ return mValue.m${name}.Value();
+ }
+ %s
+ mType = e${name};
+ return mValue.m${name}.SetValue(${ctorArgs});
+ """,
+ **vars)
+
+ # bodyInHeader must be false for return values because they own
+ # their union members and we don't want include headers in
+ # UnionTypes.h just to call Addref/Release
+ methods.append(ClassMethod(
+ "RawSetAs" + vars["name"],
+ vars["structType"] + "&",
+ vars["ctorArgList"],
+ bodyInHeader=not self.ownsMembers,
+ body=body % "MOZ_ASSERT(mType == eUninitialized);"))
+ uninit = "Uninit();"
+ if hasObjectType and not self.ownsMembers:
+ uninit = 'MOZ_ASSERT(mType != eObject, "This will not play well with Rooted");\n' + uninit
+ methods.append(ClassMethod(
+ "SetAs" + vars["name"],
+ vars["structType"] + "&",
+ vars["ctorArgList"],
+ bodyInHeader=not self.ownsMembers,
+ body=body % uninit))
+ if self.ownsMembers:
+ methods.append(vars["setter"])
+ # Provide a SetStringData() method to support string defaults.
+ if t.isByteString():
+ methods.append(
+ ClassMethod("SetStringData", "void",
+ [Argument("const nsCString::char_type*", "aData"),
+ Argument("nsCString::size_type", "aLength")],
+ inline=True, bodyInHeader=True,
+ body="RawSetAs%s().Assign(aData, aLength);\n" % t.name))
+ elif t.isString():
+ methods.append(
+ ClassMethod("SetStringData", "void",
+ [Argument("const nsString::char_type*", "aData"),
+ Argument("nsString::size_type", "aLength")],
+ inline=True, bodyInHeader=True,
+ body="RawSetAs%s().Assign(aData, aLength);\n" % t.name))
+
+ body = fill(
+ """
+ MOZ_ASSERT(Is${name}(), "Wrong type!");
+ mValue.m${name}.Destroy();
+ mType = eUninitialized;
+ """,
+ **vars)
+ methods.append(ClassMethod("Destroy" + vars["name"],
+ "void",
+ [],
+ visibility="private",
+ bodyInHeader=not self.ownsMembers,
+ body=body))
+
+ body = fill("return mType == e${name};\n", **vars)
+ methods.append(ClassMethod("Is" + vars["name"],
+ "bool",
+ [],
+ const=True,
+ bodyInHeader=True,
+ body=body))
+
+ body = fill(
+ """
+ MOZ_ASSERT(Is${name}(), "Wrong type!");
+ return mValue.m${name}.Value();
+ """,
+ **vars)
+ # The non-const version of GetAs* returns our internal type
+ getterReturnType = "%s&" % vars["structType"]
+ methods.append(ClassMethod("GetAs" + vars["name"],
+ getterReturnType,
+ [],
+ bodyInHeader=True,
+ body=body))
+ # The const version of GetAs* returns our internal type
+ # for owning unions, but our external type for non-owning
+ # ones.
+ if self.ownsMembers:
+ getterReturnType = "%s const &" % vars["structType"]
+ else:
+ getterReturnType = vars["externalType"]
+ methods.append(ClassMethod("GetAs" + vars["name"],
+ getterReturnType,
+ [],
+ const=True,
+ bodyInHeader=True,
+ body=body))
+
+ unionValues.append(
+ fill("UnionMember<${structType} > m${name}", **vars))
+ enumValues.append("e" + vars["name"])
+
+ skipToJSVal = False
+ try:
+ toJSValCases.append(
+ CGCase("e" + vars["name"],
+ self.getConversionToJS(vars, t)))
+ except MethodNotNewObjectError:
+ # If we can't have a ToJSVal() because one of our members can
+ # only be returned from [NewObject] methods, then just skip
+ # generating ToJSVal.
+ skipToJSVal = True
+ destructorCases.append(
+ CGCase("e" + vars["name"],
+ CGGeneric("Destroy%s();\n" % vars["name"])))
+ assignmentCases.append(
+ CGCase("e" + vars["name"],
+ CGGeneric("SetAs%s() = aOther.GetAs%s();\n" %
+ (vars["name"], vars["name"]))))
+ if self.ownsMembers and typeNeedsRooting(t):
+ if t.isObject():
+ traceCases.append(
+ CGCase("e" + vars["name"],
+ CGGeneric('JS::UnsafeTraceRoot(trc, %s, "%s");\n' %
+ ("&mValue.m" + vars["name"] + ".Value()",
+ "mValue.m" + vars["name"]))))
+ elif t.isDictionary():
+ traceCases.append(
+ CGCase("e" + vars["name"],
+ CGGeneric("mValue.m%s.Value().TraceDictionary(trc);\n" %
+ vars["name"])))
+ elif t.isSequence():
+ traceCases.append(
+ CGCase("e" + vars["name"],
+ CGGeneric("DoTraceSequence(trc, mValue.m%s.Value());\n" %
+ vars["name"])))
+ elif t.isMozMap():
+ traceCases.append(
+ CGCase("e" + vars["name"],
+ CGGeneric("TraceMozMap(trc, mValue.m%s.Value());\n" %
+ vars["name"])))
+ else:
+ assert t.isSpiderMonkeyInterface()
+ traceCases.append(
+ CGCase("e" + vars["name"],
+ CGGeneric("mValue.m%s.Value().TraceSelf(trc);\n" %
+ vars["name"])))
+
+ dtor = CGSwitch("mType", destructorCases).define()
+
+ methods.append(ClassMethod("Uninit", "void", [],
+ visibility="public", body=dtor,
+ bodyInHeader=not self.ownsMembers,
+ inline=not self.ownsMembers))
+
+ if not skipToJSVal:
+ methods.append(
+ ClassMethod(
+ "ToJSVal",
+ "bool",
+ [
+ Argument("JSContext*", "cx"),
+ Argument("JS::Handle<JSObject*>", "scopeObj"),
+ Argument("JS::MutableHandle<JS::Value>", "rval")
+ ],
+ body=CGSwitch("mType", toJSValCases,
+ default=CGGeneric("return false;\n")).define() + "\nreturn false;\n",
+ const=True))
+
+ constructors = [ctor]
+ selfName = CGUnionStruct.unionTypeName(self.type, self.ownsMembers)
+ if self.ownsMembers:
+ if traceCases:
+ traceBody = CGSwitch("mType", traceCases,
+ default=CGGeneric("")).define()
+ else:
+ traceBody = ""
+ methods.append(ClassMethod("TraceUnion", "void",
+ [Argument("JSTracer*", "trc")],
+ body=traceBody))
+ if CGUnionStruct.isUnionCopyConstructible(self.type):
+ constructors.append(
+ ClassConstructor(
+ [Argument("const %s&" % selfName, "aOther")],
+ bodyInHeader=True,
+ visibility="public",
+ explicit=True,
+ body="*this = aOther;\n"))
+ methods.append(ClassMethod(
+ "operator=", "void",
+ [Argument("const %s&" % selfName, "aOther")],
+ body=CGSwitch("aOther.mType", assignmentCases).define()))
+ disallowCopyConstruction = False
+ else:
+ disallowCopyConstruction = True
+ else:
+ disallowCopyConstruction = True
+
+ if self.ownsMembers:
+ friend = " friend void ImplCycleCollectionUnlink(%s& aUnion);\n" % CGUnionStruct.unionTypeName(self.type, True)
+ else:
+ friend = " friend class %sArgument;\n" % str(self.type)
+
+ bases = [ClassBase("AllOwningUnionBase")] if self.ownsMembers else []
+ return CGClass(selfName,
+ bases=bases,
+ members=members,
+ constructors=constructors,
+ methods=methods,
+ disallowCopyConstruction=disallowCopyConstruction,
+ extradeclarations=friend,
+ destructor=ClassDestructor(visibility="public",
+ body="Uninit();\n",
+ bodyInHeader=True),
+ enums=[ClassEnum("Type", enumValues, visibility="private")],
+ unions=[ClassUnion("Value", unionValues, visibility="private")])
+
+ def getConversionToJS(self, templateVars, type):
+ assert not type.nullable() # flatMemberTypes never has nullable types
+ val = "mValue.m%(name)s.Value()" % templateVars
+ wrapCode = wrapForType(
+ type, self.descriptorProvider,
+ {
+ "jsvalRef": "rval",
+ "jsvalHandle": "rval",
+ "obj": "scopeObj",
+ "result": val,
+ "typedArraysAreStructs": True
+ })
+ return CGGeneric(wrapCode)
+
+ @staticmethod
+ def isUnionCopyConstructible(type):
+ return all(isTypeCopyConstructible(t) for t in type.flatMemberTypes)
+
+ @staticmethod
+ def unionTypeName(type, ownsMembers):
+ """
+ Returns a string name for this known union type.
+ """
+ assert type.isUnion() and not type.nullable()
+ return ("Owning" if ownsMembers else "") + type.name
+
+ @staticmethod
+ def unionTypeDecl(type, ownsMembers):
+ """
+ Returns a string for declaring this possibly-nullable union type.
+ """
+ assert type.isUnion()
+ nullable = type.nullable()
+ if nullable:
+ type = type.inner
+ decl = CGGeneric(CGUnionStruct.unionTypeName(type, ownsMembers))
+ if nullable:
+ decl = CGTemplatedType("Nullable", decl)
+ return decl.define()
+
+
+class CGUnionConversionStruct(CGThing):
+ def __init__(self, type, descriptorProvider):
+ CGThing.__init__(self)
+ self.type = type.unroll()
+ self.descriptorProvider = descriptorProvider
+
+ def declare(self):
+
+ structName = str(self.type)
+ members = [ClassMember("mUnion", structName + "&",
+ body="const_cast<%s&>(aUnion)" % structName)]
+ # Argument needs to be a const ref because that's all Maybe<> allows
+ ctor = ClassConstructor([Argument("const %s&" % structName, "aUnion")],
+ bodyInHeader=True,
+ visibility="public",
+ explicit=True)
+ methods = []
+
+ if self.type.hasNullableType:
+ methods.append(ClassMethod("SetNull", "bool", [],
+ body=("MOZ_ASSERT(mUnion.mType == mUnion.eUninitialized);\n"
+ "mUnion.mType = mUnion.eNull;\n"
+ "return true;\n"),
+ inline=True, bodyInHeader=True))
+
+ for t in self.type.flatMemberTypes:
+ vars = getUnionTypeTemplateVars(self.type,
+ t, self.descriptorProvider)
+ methods.append(vars["setter"])
+ if vars["name"] != "Object":
+ body = fill(
+ """
+ MOZ_ASSERT(mUnion.mType == mUnion.eUninitialized);
+ mUnion.mType = mUnion.e${name};
+ return mUnion.mValue.m${name}.SetValue(${ctorArgs});
+ """,
+ **vars)
+ methods.append(ClassMethod("RawSetAs" + vars["name"],
+ vars["structType"] + "&",
+ vars["ctorArgList"],
+ bodyInHeader=True,
+ body=body,
+ visibility="private"))
+ # Provide a SetStringData() method to support string defaults.
+ if t.isByteString():
+ methods.append(
+ ClassMethod("SetStringData", "void",
+ [Argument("const nsDependentCString::char_type*", "aData"),
+ Argument("nsDependentCString::size_type", "aLength")],
+ inline=True, bodyInHeader=True,
+ body="RawSetAs%s().Rebind(aData, aLength);\n" % t.name))
+ elif t.isString():
+ methods.append(
+ ClassMethod("SetStringData", "void",
+ [Argument("const nsDependentString::char_type*", "aData"),
+ Argument("nsDependentString::size_type", "aLength")],
+ inline=True, bodyInHeader=True,
+ body="RawSetAs%s().Rebind(aData, aLength);\n" % t.name))
+
+ if vars["holderType"] is not None:
+ holderType = CGTemplatedType("Maybe",
+ CGGeneric(vars["holderType"])).define()
+ members.append(ClassMember("m%sHolder" % vars["name"],
+ holderType))
+
+ return CGClass(structName + "Argument",
+ members=members,
+ constructors=[ctor],
+ methods=methods,
+ disallowCopyConstruction=True).declare()
+
+ def define(self):
+ return ""
+
+ def deps(self):
+ return set()
+
+
+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,
+ breakAfterReturnDecl="\n",
+ breakAfterSelf="\n", override=False):
+ """
+ override indicates whether to flag the method as override
+ """
+ assert not override or virtual
+ assert not (override and static)
+ 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
+ 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 = ('template <%s>\n' % ', '.join(self.templateArgs)
+ if self.bodyInHeader and self.templateArgs else '')
+ args = ', '.join([a.declare() for a in self.args])
+ if self.bodyInHeader:
+ body = indent(self.getBody())
+ body = '\n{\n' + body + '}\n'
+ else:
+ body = ';\n'
+
+ return fill(
+ "${templateClause}${decorators}${returnType}${breakAfterReturnDecl}"
+ "${name}(${args})${const}${override}${body}"
+ "${breakAfterSelf}",
+ templateClause=templateClause,
+ decorators=self.getDecorators(True),
+ returnType=self.returnType,
+ breakAfterReturnDecl=self.breakAfterReturnDecl,
+ name=self.name,
+ args=args,
+ const=' const' if self.const else '',
+ override=' override' if self.override else '',
+ body=body,
+ breakAfterSelf=self.breakAfterSelf)
+
+ 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 = ''
+
+ return fill(
+ """
+ ${templateClause}${decorators}${returnType}
+ ${className}::${name}(${args})${const}
+ {
+ $*{body}
+ }
+ """,
+ templateClause=templateClause,
+ decorators=self.getDecorators(False),
+ returnType=self.returnType,
+ className=cgClass.getNameString(),
+ name=self.name,
+ args=', '.join([a.define() for a in self.args]),
+ const=' const' if self.const else '',
+ body=self.getBody())
+
+
+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 "using %s::%s;\n\n" % (self.baseClass, 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="private", explicit=False, constexpr=False, baseConstructors=None,
+ body=""):
+ assert not (inline and constexpr)
+ assert not (bodyInHeader and constexpr)
+ self.args = args
+ self.inline = inline or bodyInHeader
+ self.bodyInHeader = bodyInHeader or constexpr
+ self.explicit = explicit
+ self.constexpr = constexpr
+ 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 self.constexpr and declaring:
+ decorators.append('constexpr')
+ 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):
+ return self.body
+
+ def declare(self, cgClass):
+ args = ', '.join([a.declare() for a in self.args])
+ if self.bodyInHeader:
+ body = self.getInitializationList(cgClass) + '\n{\n' + indent(self.getBody()) + '}\n'
+ else:
+ body = ';\n'
+
+ return fill(
+ "${decorators}${className}(${args})${body}\n",
+ decorators=self.getDecorators(True),
+ className=cgClass.getNameString(),
+ args=args,
+ body=body)
+
+ def define(self, cgClass):
+ if self.bodyInHeader:
+ return ''
+
+ return fill(
+ """
+ ${decorators}
+ ${className}::${className}(${args})${initializationList}
+ {
+ $*{body}
+ }
+ """,
+ decorators=self.getDecorators(False),
+ className=cgClass.getNameString(),
+ args=', '.join([a.define() for a in self.args]),
+ initializationList=self.getInitializationList(cgClass),
+ body=self.getBody())
+
+
+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 = '\n{\n' + indent(self.getBody()) + '}\n'
+ else:
+ body = ';\n'
+
+ return fill(
+ "${decorators}~${className}()${body}\n",
+ decorators=self.getDecorators(True),
+ className=cgClass.getNameString(),
+ body=body)
+
+ def define(self, cgClass):
+ if self.bodyInHeader:
+ return ''
+ return fill(
+ """
+ ${decorators}
+ ${className}::~${className}()
+ {
+ $*{body}
+ }
+ """,
+ decorators=self.getDecorators(False),
+ className=cgClass.getNameString(),
+ body=self.getBody())
+
+
+class ClassMember(ClassItem):
+ def __init__(self, name, type, visibility="private", static=False,
+ body=None, hasIgnoreInitCheckFlag=False):
+ self.type = type
+ self.static = static
+ self.body = body
+ self.hasIgnoreInitCheckFlag = hasIgnoreInitCheckFlag;
+ ClassItem.__init__(self, name, visibility)
+
+ def declare(self, cgClass):
+ return '%s%s%s %s;\n' % ('static ' if self.static else '',
+ 'MOZ_INIT_OUTSIDE_CTOR '
+ if self.hasIgnoreInitCheckFlag 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 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, indent(',\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 + ";\n" for entry in entries]
+ ClassItem.__init__(self, name, visibility)
+
+ def declare(self, cgClass):
+ return "union %s\n{\n%s\n};\n" % (self.name, indent(''.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.defaultVisibility = 'public' if isStruct else 'private'
+ self.decorators = decorators
+ self.extradeclarations = extradeclarations
+ self.extradefinitions = extradefinitions
+
+ def getNameString(self):
+ className = self.name
+ if self.templateSpecialization:
+ className += '<%s>' % ', '.join([str(a)
+ for a in self.templateSpecialization])
+ return className
+
+ def declare(self):
+ result = ''
+ if self.templateArgs:
+ templateArgs = [a.declare() for a in self.templateArgs]
+ templateArgs = templateArgs[len(self.templateSpecialization):]
+ result += ('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 = ''
+
+ myself = '%s %s%s' % (type, self.name, specialization)
+ if self.decorators != '':
+ myself += " " + self.decorators
+ result += myself
+
+ if self.bases:
+ inherit = ' : '
+ result += inherit
+ # Grab our first base
+ baseItems = [CGGeneric(b.declare(self)) for b in self.bases]
+ bases = baseItems[:1]
+ # Indent the rest
+ bases.extend(CGIndenter(b, len(myself) + len(inherit))
+ for b in baseItems[1:])
+ result += ",\n".join(b.define() for b in bases)
+
+ result += '\n{\n'
+
+ result += self.extradeclarations
+
+ def declareMembers(cgClass, memberList, defaultVisibility):
+ 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:
+ result += visibility + ':\n'
+ for member in list:
+ result += indent(member.declare(cgClass))
+ lastVisibility = visibility
+ return (result, lastVisibility)
+
+ if self.disallowCopyConstruction:
+ class DisallowedCopyConstructor(object):
+ def __init__(self):
+ self.visibility = "private"
+
+ def declare(self, cgClass):
+ name = cgClass.getNameString()
+ return ("%s(const %s&) = delete;\n"
+ "void operator=(const %s&) = delete;\n" % (name, name, name))
+
+ disallowedCopyConstructors = [DisallowedCopyConstructor()]
+ else:
+ disallowedCopyConstructors = []
+
+ order = [self.enums, self.unions,
+ self.typedefs, self.members,
+ self.constructors + disallowedCopyConstructors,
+ self.destructors, self.methods]
+
+ lastVisibility = self.defaultVisibility
+ pieces = []
+ for memberList in order:
+ code, lastVisibility = declareMembers(self, memberList, lastVisibility)
+
+ if code:
+ code = code.rstrip() + "\n" # remove extra blank lines at the end
+ pieces.append(code)
+
+ result += '\n'.join(pieces)
+ result += '};\n'
+ result = indent(result, len(self.indent))
+ return result
+
+ def define(self):
+ def defineMembers(cgClass, memberList, itemCount, separator=''):
+ result = ''
+ for member in memberList:
+ if itemCount != 0:
+ result = result + separator
+ definition = member.define(cgClass)
+ if definition:
+ # Member variables would only produce empty lines here.
+ result += definition
+ itemCount += 1
+ return (result, itemCount)
+
+ order = [(self.members, ''), (self.constructors, '\n'),
+ (self.destructors, '\n'), (self.methods, '\n')]
+
+ result = self.extradefinitions
+ itemCount = 0
+ for memberList, separator in order:
+ memberString, itemCount = defineMembers(self, memberList,
+ itemCount, separator)
+ result = result + memberString
+ return result
+
+
+class CGResolveOwnProperty(CGAbstractStaticMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'),
+ Argument('JS::Handle<JSObject*>', 'wrapper'),
+ Argument('JS::Handle<JSObject*>', 'obj'),
+ Argument('JS::Handle<jsid>', 'id'),
+ Argument('JS::MutableHandle<JS::PropertyDescriptor>', 'desc'),
+ ]
+ CGAbstractStaticMethod.__init__(self, descriptor, "ResolveOwnProperty",
+ "bool", args)
+
+ def definition_body(self):
+ return "return js::GetProxyHandler(obj)->getOwnPropertyDescriptor(cx, wrapper, id, desc);\n"
+
+
+class CGResolveOwnPropertyViaResolve(CGAbstractBindingMethod):
+ """
+ An implementation of Xray ResolveOwnProperty stuff for things that have a
+ resolve hook.
+ """
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'),
+ Argument('JS::Handle<JSObject*>', 'wrapper'),
+ Argument('JS::Handle<JSObject*>', 'obj'),
+ Argument('JS::Handle<jsid>', 'id'),
+ Argument('JS::MutableHandle<JS::PropertyDescriptor>', 'desc')]
+ CGAbstractBindingMethod.__init__(self, descriptor,
+ "ResolveOwnPropertyViaResolve",
+ args, getThisObj="",
+ callArgs="")
+
+ def generate_code(self):
+ return CGGeneric(dedent("""
+ {
+ // Since we're dealing with an Xray, do the resolve on the
+ // underlying object first. That gives it a chance to
+ // define properties on the actual object as needed, and
+ // then use the fact that it created the objects as a flag
+ // to avoid re-resolving the properties if someone deletes
+ // them.
+ JSAutoCompartment ac(cx, obj);
+ JS::Rooted<JS::PropertyDescriptor> objDesc(cx);
+ if (!self->DoResolve(cx, obj, id, &objDesc)) {
+ return false;
+ }
+ // If desc.value() is undefined, then the DoResolve call
+ // has already defined the property on the object. Don't
+ // try to also define it.
+ if (objDesc.object() &&
+ !objDesc.value().isUndefined() &&
+ !JS_DefinePropertyById(cx, obj, id, objDesc)) {
+ return false;
+ }
+ }
+ return self->DoResolve(cx, wrapper, id, desc);
+ """))
+
+
+class CGEnumerateOwnProperties(CGAbstractStaticMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'),
+ Argument('JS::Handle<JSObject*>', 'wrapper'),
+ Argument('JS::Handle<JSObject*>', 'obj'),
+ Argument('JS::AutoIdVector&', 'props')]
+ CGAbstractStaticMethod.__init__(self, descriptor,
+ "EnumerateOwnProperties", "bool", args)
+
+ def definition_body(self):
+ return "return js::GetProxyHandler(obj)->ownPropertyKeys(cx, wrapper, props);\n"
+
+
+class CGEnumerateOwnPropertiesViaGetOwnPropertyNames(CGAbstractBindingMethod):
+ """
+ An implementation of Xray EnumerateOwnProperties stuff for things
+ that have a resolve hook.
+ """
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'),
+ Argument('JS::Handle<JSObject*>', 'wrapper'),
+ Argument('JS::Handle<JSObject*>', 'obj'),
+ Argument('JS::AutoIdVector&', 'props')]
+ CGAbstractBindingMethod.__init__(self, descriptor,
+ "EnumerateOwnPropertiesViaGetOwnPropertyNames",
+ args, getThisObj="",
+ callArgs="")
+
+ def generate_code(self):
+ return CGGeneric(dedent("""
+ AutoTArray<nsString, 8> names;
+ binding_detail::FastErrorResult rv;
+ self->GetOwnPropertyNames(cx, names, rv);
+ if (rv.MaybeSetPendingException(cx)) {
+ return false;
+ }
+ // OK to pass null as "proxy" because it's ignored if
+ // shadowPrototypeProperties is true
+ return AppendNamedPropertyIds(cx, nullptr, names, true, 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()])]
+ CGClass.__init__(self, 'PrototypeTraits', indent=indent,
+ templateArgs=templateArgs,
+ templateSpecialization=templateSpecialization,
+ enums=enums, isStruct=True)
+
+ def deps(self):
+ return set()
+
+
+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 ''
+
+ def deps(self):
+ return set()
+
+
+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).
+
+ If checkFound is False, will just assert that the prop is found instead of
+ checking that it is before wrapping the value.
+
+ resultVar: See the docstring for CGCallGenerator.
+
+ foundVar: For getters and deleters, the generated code can also set a bool
+ variable, declared by the caller, if the given indexed or named property
+ already existed. If the caller wants this, it should pass the name of the
+ bool variable as the foundVar keyword argument to the constructor. The
+ caller is responsible for declaring the variable and initializing it to
+ false.
+ """
+ def __init__(self, descriptor, operation, checkFound=True,
+ argumentHandleValue=None, resultVar=None, foundVar=None):
+ self.checkFound = checkFound
+ self.foundVar = foundVar or "found"
+
+ nativeName = MakeNativeName(descriptor.binaryNameFor(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), resultVar=resultVar)
+
+ if operation.isSetter() or operation.isCreator():
+ # arguments[0] is the index or name of the item that we're setting.
+ argument = arguments[1]
+ info = getJSToNativeConversionInfo(
+ argument.type, descriptor,
+ treatNullAs=argument.treatNullAs,
+ sourceDescription=("value being assigned to %s setter" %
+ descriptor.interface.identifier.name))
+ if argumentHandleValue is None:
+ argumentHandleValue = "desc.value()"
+ rootedValue = fill(
+ """
+ JS::Rooted<JS::Value> rootedValue(cx, ${argumentHandleValue});
+ """,
+ argumentHandleValue = argumentHandleValue)
+ templateValues = {
+ "declName": argument.identifier.name,
+ "holderName": argument.identifier.name + "_holder",
+ "val": argumentHandleValue,
+ "maybeMutableVal": "&rootedValue",
+ "obj": "obj",
+ "passedToJSImpl": "false"
+ }
+ self.cgRoot.prepend(instantiateJSToNativeConversion(info, templateValues))
+ # rootedValue needs to come before the conversion, so we
+ # need to prepend it last.
+ self.cgRoot.prepend(CGGeneric(rootedValue))
+ elif operation.isGetter() or operation.isDeleter():
+ if foundVar is None:
+ self.cgRoot.prepend(CGGeneric("bool found = false;\n"))
+
+ def getArguments(self):
+ args = [(a, a.identifier.name) for a in self.arguments]
+ if self.idlNode.isGetter() or self.idlNode.isDeleter():
+ args.append((FakeArgument(BuiltinTypes[IDLBuiltinType.Types.boolean],
+ self.idlNode),
+ self.foundVar))
+ 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))
+ if self.checkFound:
+ wrap = CGIfWrapper(wrap, self.foundVar)
+ else:
+ wrap = CGList([CGGeneric("MOZ_ASSERT(" + self.foundVar + ");\n"), wrap])
+ return "\n" + wrap.define()
+
+
+class CGProxyIndexedOperation(CGProxySpecialOperation):
+ """
+ Class to generate a call to an indexed operation.
+
+ If doUnwrap is False, the caller is responsible for making sure a variable
+ named 'self' holds the C++ object somewhere where the code we generate
+ will see it.
+
+ If checkFound is False, will just assert that the prop is found instead of
+ checking that it is before wrapping the value.
+
+ resultVar: See the docstring for CGCallGenerator.
+
+ foundVar: See the docstring for CGProxySpecialOperation.
+ """
+ def __init__(self, descriptor, name, doUnwrap=True, checkFound=True,
+ argumentHandleValue=None, resultVar=None, foundVar=None):
+ self.doUnwrap = doUnwrap
+ CGProxySpecialOperation.__init__(self, descriptor, name, checkFound,
+ argumentHandleValue=argumentHandleValue,
+ resultVar=resultVar,
+ foundVar=foundVar)
+
+ def define(self):
+ # Our first argument is the id we're getting.
+ argName = self.arguments[0].identifier.name
+ if argName == "index":
+ # We already have our index in a variable with that name
+ setIndex = ""
+ else:
+ setIndex = "uint32_t %s = index;\n" % argName
+ if self.doUnwrap:
+ unwrap = "%s* self = UnwrapProxy(proxy);\n" % self.descriptor.nativeType
+ else:
+ unwrap = ""
+ return (setIndex + unwrap +
+ CGProxySpecialOperation.define(self))
+
+
+class CGProxyIndexedGetter(CGProxyIndexedOperation):
+ """
+ Class to generate a call to an indexed getter. If templateValues is not None
+ the returned value will be wrapped with wrapForType using templateValues.
+
+ If doUnwrap is False, the caller is responsible for making sure a variable
+ named 'self' holds the C++ object somewhere where the code we generate
+ will see it.
+
+ If checkFound is False, will just assert that the prop is found instead of
+ checking that it is before wrapping the value.
+
+ foundVar: See the docstring for CGProxySpecialOperation.
+ """
+ def __init__(self, descriptor, templateValues=None, doUnwrap=True,
+ checkFound=True, foundVar=None):
+ self.templateValues = templateValues
+ CGProxyIndexedOperation.__init__(self, descriptor, 'IndexedGetter',
+ doUnwrap, checkFound, foundVar=foundVar)
+
+
+class CGProxyIndexedPresenceChecker(CGProxyIndexedGetter):
+ """
+ Class to generate a call that checks whether an indexed property exists.
+
+ For now, we just delegate to CGProxyIndexedGetter
+
+ foundVar: See the docstring for CGProxySpecialOperation.
+ """
+ def __init__(self, descriptor, foundVar):
+ CGProxyIndexedGetter.__init__(self, descriptor, foundVar=foundVar)
+ self.cgRoot.append(CGGeneric("(void)result;\n"))
+
+
+class CGProxyIndexedSetter(CGProxyIndexedOperation):
+ """
+ Class to generate a call to an indexed setter.
+ """
+ def __init__(self, descriptor, argumentHandleValue=None):
+ CGProxyIndexedOperation.__init__(self, descriptor, 'IndexedSetter',
+ argumentHandleValue=argumentHandleValue)
+
+
+class CGProxyNamedOperation(CGProxySpecialOperation):
+ """
+ Class to generate a call to a named operation.
+
+ 'value' is the jsval to use for the name; None indicates that it should be
+ gotten from the property id.
+
+ resultVar: See the docstring for CGCallGenerator.
+
+ foundVar: See the docstring for CGProxySpecialOperation.
+ """
+ def __init__(self, descriptor, name, value=None, argumentHandleValue=None,
+ resultVar=None, foundVar=None):
+ CGProxySpecialOperation.__init__(self, descriptor, name,
+ argumentHandleValue=argumentHandleValue,
+ resultVar=resultVar,
+ foundVar=foundVar)
+ self.value = value
+
+ def define(self):
+ # Our first argument is the id we're getting.
+ argName = self.arguments[0].identifier.name
+ if argName == "id":
+ # deal with the name collision
+ decls = "JS::Rooted<jsid> id_(cx, id);\n"
+ idName = "id_"
+ else:
+ decls = ""
+ idName = "id"
+
+ decls += "binding_detail::FakeString %s;\n" % argName
+
+ main = fill(
+ """
+ ${nativeType}* self = UnwrapProxy(proxy);
+ $*{op}
+ """,
+ nativeType=self.descriptor.nativeType,
+ op=CGProxySpecialOperation.define(self))
+
+ if self.value is None:
+ return fill(
+ """
+ $*{decls}
+ bool isSymbol;
+ if (!ConvertIdToString(cx, ${idName}, ${argName}, isSymbol)) {
+ return false;
+ }
+ if (!isSymbol) {
+ $*{main}
+ }
+ """,
+ decls=decls,
+ idName=idName,
+ argName=argName,
+ main=main)
+
+ # Sadly, we have to set up nameVal even if we have an atom id,
+ # because we don't know for sure, and we can end up needing it
+ # so it needs to be higher up the stack. Using a Maybe here
+ # seems like probable overkill.
+ return fill(
+ """
+ $*{decls}
+ JS::Rooted<JS::Value> nameVal(cx, ${value});
+ if (!nameVal.isSymbol()) {
+ if (!ConvertJSValueToString(cx, nameVal, eStringify, eStringify,
+ ${argName})) {
+ return false;
+ }
+ $*{main}
+ }
+ """,
+ decls=decls,
+ value=self.value,
+ argName=argName,
+ main=main)
+
+
+class CGProxyNamedGetter(CGProxyNamedOperation):
+ """
+ Class to generate a call to an named getter. If templateValues is not None
+ the returned value will be wrapped with wrapForType using templateValues.
+ 'value' is the jsval to use for the name; None indicates that it should be
+ gotten from the property id.
+
+ foundVar: See the docstring for CGProxySpecialOperation.
+ """
+ def __init__(self, descriptor, templateValues=None, value=None,
+ foundVar=None):
+ self.templateValues = templateValues
+ CGProxyNamedOperation.__init__(self, descriptor, 'NamedGetter', value,
+ foundVar=foundVar)
+
+
+class CGProxyNamedPresenceChecker(CGProxyNamedGetter):
+ """
+ Class to generate a call that checks whether a named property exists.
+
+ For now, we just delegate to CGProxyNamedGetter
+
+ foundVar: See the docstring for CGProxySpecialOperation.
+ """
+ def __init__(self, descriptor, foundVar=None):
+ CGProxyNamedGetter.__init__(self, descriptor, foundVar=foundVar)
+ self.cgRoot.append(CGGeneric("(void)result;\n"))
+
+
+class CGProxyNamedSetter(CGProxyNamedOperation):
+ """
+ Class to generate a call to a named setter.
+ """
+ def __init__(self, descriptor, argumentHandleValue=None):
+ CGProxyNamedOperation.__init__(self, descriptor, 'NamedSetter',
+ argumentHandleValue=argumentHandleValue)
+
+
+class CGProxyNamedDeleter(CGProxyNamedOperation):
+ """
+ Class to generate a call to a named deleter.
+
+ resultVar: See the docstring for CGCallGenerator.
+
+ foundVar: See the docstring for CGProxySpecialOperation.
+ """
+ def __init__(self, descriptor, resultVar=None, foundVar=None):
+ CGProxyNamedOperation.__init__(self, descriptor, 'NamedDeleter',
+ resultVar=resultVar,
+ foundVar=foundVar)
+
+
+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();\n"
+
+
+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 fill(
+ """
+ MOZ_ASSERT(js::IsProxy(obj));
+ if (js::GetProxyHandler(obj) != DOMProxyHandler::getInstance()) {
+ MOZ_ASSERT(xpc::WrapperFactory::IsXrayWrapper(obj));
+ obj = js::UncheckedUnwrap(obj);
+ }
+ MOZ_ASSERT(IsProxy(obj));
+ return static_cast<${type}*>(js::GetProxyPrivate(obj).toPrivate());
+ """,
+ type=self.descriptor.nativeType)
+
+
+class CGDOMJSProxyHandler_getOwnPropDescriptor(ClassMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'),
+ Argument('JS::Handle<JSObject*>', 'proxy'),
+ Argument('JS::Handle<jsid>', 'id'),
+ Argument('bool', 'ignoreNamedProps'),
+ Argument('JS::MutableHandle<JS::PropertyDescriptor>', 'desc')]
+ ClassMethod.__init__(self, "getOwnPropDescriptor", "bool", args,
+ virtual=True, override=True, const=True)
+ self.descriptor = descriptor
+
+ def getBody(self):
+ indexedGetter = self.descriptor.operations['IndexedGetter']
+ indexedSetter = self.descriptor.operations['IndexedSetter']
+
+ if self.descriptor.supportsIndexedProperties():
+ readonly = toStringBool(indexedSetter is None)
+ fillDescriptor = "FillPropertyDescriptor(desc, proxy, %s);\nreturn true;\n" % readonly
+ templateValues = {
+ 'jsvalRef': 'desc.value()',
+ 'jsvalHandle': 'desc.value()',
+ 'obj': 'proxy',
+ 'successCode': fillDescriptor
+ }
+ getIndexed = fill(
+ """
+ uint32_t index = GetArrayIndexFromId(cx, id);
+ if (IsArrayIndex(index)) {
+ $*{callGetter}
+ }
+
+ """,
+ callGetter=CGProxyIndexedGetter(self.descriptor, templateValues).define())
+ else:
+ getIndexed = ""
+
+ if self.descriptor.supportsNamedProperties():
+ operations = self.descriptor.operations
+ readonly = toStringBool(operations['NamedSetter'] is None)
+ fillDescriptor = (
+ "FillPropertyDescriptor(desc, proxy, %s, %s);\n"
+ "return true;\n" %
+ (readonly,
+ toStringBool(self.descriptor.namedPropertiesEnumerable)))
+ templateValues = {'jsvalRef': 'desc.value()', 'jsvalHandle': 'desc.value()',
+ 'obj': 'proxy', 'successCode': fillDescriptor}
+
+ computeCondition = dedent("""
+ bool hasOnProto;
+ if (!HasPropertyOnPrototype(cx, proxy, id, &hasOnProto)) {
+ return false;
+ }
+ callNamedGetter = !hasOnProto;
+ """)
+ if self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
+ computeCondition = fill(
+ """
+ if (!isXray) {
+ callNamedGetter = true;
+ } else {
+ $*{hasOnProto}
+ }
+ """,
+ hasOnProto=computeCondition)
+
+ outerCondition = "!ignoreNamedProps"
+ if self.descriptor.supportsIndexedProperties():
+ outerCondition = "!IsArrayIndex(index) && " + outerCondition
+
+ namedGetCode = CGProxyNamedGetter(self.descriptor,
+ templateValues).define()
+ namedGet = fill("""
+ bool callNamedGetter = false;
+ if (${outerCondition}) {
+ $*{computeCondition}
+ }
+ if (callNamedGetter) {
+ $*{namedGetCode}
+ }
+ """,
+ outerCondition=outerCondition,
+ computeCondition=computeCondition,
+ namedGetCode=namedGetCode)
+ namedGet += "\n"
+ else:
+ namedGet = ""
+
+ return fill(
+ """
+ bool isXray = xpc::WrapperFactory::IsXrayWrapper(proxy);
+ $*{getIndexed}
+ JS::Rooted<JSObject*> expando(cx);
+ if (!isXray && (expando = GetExpandoObject(proxy))) {
+ if (!JS_GetOwnPropertyDescriptorById(cx, expando, id, desc)) {
+ return false;
+ }
+ if (desc.object()) {
+ // Pretend the property lives on the wrapper.
+ desc.object().set(proxy);
+ return true;
+ }
+ }
+
+ $*{namedGet}
+ desc.object().set(nullptr);
+ return true;
+ """,
+ getIndexed=getIndexed,
+ namedGet=namedGet)
+
+
+class CGDOMJSProxyHandler_defineProperty(ClassMethod):
+ def __init__(self, descriptor):
+ # The usual convention is to name the ObjectOpResult out-parameter
+ # `result`, but that name is a bit overloaded around here.
+ args = [Argument('JSContext*', 'cx'),
+ Argument('JS::Handle<JSObject*>', 'proxy'),
+ Argument('JS::Handle<jsid>', 'id'),
+ Argument('JS::Handle<JS::PropertyDescriptor>', 'desc'),
+ Argument('JS::ObjectOpResult&', 'opresult'),
+ Argument('bool*', 'defined')]
+ ClassMethod.__init__(self, "defineProperty", "bool", args, virtual=True, override=True, const=True)
+ self.descriptor = descriptor
+
+ def getBody(self):
+ set = ""
+
+ indexedSetter = self.descriptor.operations['IndexedSetter']
+ if indexedSetter:
+ if self.descriptor.operations['IndexedCreator'] is not indexedSetter:
+ raise TypeError("Can't handle creator that's different from the setter")
+ set += fill(
+ """
+ uint32_t index = GetArrayIndexFromId(cx, id);
+ if (IsArrayIndex(index)) {
+ *defined = true;
+ $*{callSetter}
+ return opresult.succeed();
+ }
+ """,
+ callSetter=CGProxyIndexedSetter(self.descriptor).define())
+ elif self.descriptor.supportsIndexedProperties():
+ # We allow untrusted content to prevent Xrays from setting a
+ # property if that property is an indexed property and we have no
+ # indexed setter. That's how the object would normally behave if
+ # you tried to set the property on it. That means we don't need to
+ # do anything special for Xrays here.
+ set += dedent(
+ """
+ if (IsArrayIndex(GetArrayIndexFromId(cx, id))) {
+ *defined = true;
+ return opresult.failNoIndexedSetter();
+ }
+ """)
+
+ namedSetter = self.descriptor.operations['NamedSetter']
+ if namedSetter:
+ if self.descriptor.hasUnforgeableMembers:
+ raise TypeError("Can't handle a named setter on an interface "
+ "that has unforgeables. Figure out how that "
+ "should work!")
+ if self.descriptor.operations['NamedCreator'] is not namedSetter:
+ raise TypeError("Can't handle creator that's different from the setter")
+ # If we support indexed properties, we won't get down here for
+ # indices, so we can just do our setter unconditionally here.
+ set += fill(
+ """
+ *defined = true;
+ $*{callSetter}
+
+ return opresult.succeed();
+ """,
+ callSetter=CGProxyNamedSetter(self.descriptor).define())
+ else:
+ # We allow untrusted content to prevent Xrays from setting a
+ # property if that property is already a named property on the
+ # object and we have no named setter. That's how the object would
+ # normally behave if you tried to set the property on it. That
+ # means we don't need to do anything special for Xrays here.
+ if self.descriptor.supportsNamedProperties():
+ set += fill(
+ """
+ bool found = false;
+ $*{presenceChecker}
+
+ if (found) {
+ *defined = true;
+ return opresult.failNoNamedSetter();
+ }
+ """,
+ presenceChecker=CGProxyNamedPresenceChecker(self.descriptor, foundVar="found").define())
+ set += ("return mozilla::dom::DOMProxyHandler::defineProperty(%s);\n" %
+ ", ".join(a.name for a in self.args))
+ return set
+
+
+def getDeleterBody(descriptor, type, foundVar=None):
+ """
+ type should be "Named" or "Indexed"
+
+ The possible outcomes:
+ - an error happened (the emitted code returns false)
+ - own property not found (foundVar=false, deleteSucceeded=true)
+ - own property found and deleted (foundVar=true, deleteSucceeded=true)
+ - own property found but can't be deleted (foundVar=true, deleteSucceeded=false)
+ """
+ assert type in ("Named", "Indexed")
+ deleter = descriptor.operations[type + 'Deleter']
+ if deleter:
+ assert type == "Named"
+ assert foundVar is not None
+ if descriptor.hasUnforgeableMembers:
+ raise TypeError("Can't handle a deleter on an interface "
+ "that has unforgeables. Figure out how "
+ "that should work!")
+ # See if the deleter method is fallible.
+ t = deleter.signatures()[0][0]
+ if t.isPrimitive() and not t.nullable() and t.tag() == IDLType.Tags.bool:
+ # The deleter method has a boolean return value. When a
+ # property is found, the return value indicates whether it
+ # was successfully deleted.
+ setDS = fill(
+ """
+ if (!${foundVar}) {
+ deleteSucceeded = true;
+ }
+ """,
+ foundVar=foundVar)
+ else:
+ # No boolean return value: if a property is found,
+ # deleting it always succeeds.
+ setDS = "deleteSucceeded = true;\n"
+
+ body = (CGProxyNamedDeleter(descriptor,
+ resultVar="deleteSucceeded",
+ foundVar=foundVar).define() +
+ setDS)
+ elif getattr(descriptor, "supports%sProperties" % type)():
+ presenceCheckerClass = globals()["CGProxy%sPresenceChecker" % type]
+ foundDecl = ""
+ if foundVar is None:
+ foundVar = "found"
+ foundDecl = "bool found = false;\n"
+ body = fill(
+ """
+ $*{foundDecl}
+ $*{presenceChecker}
+ deleteSucceeded = !${foundVar};
+ """,
+ foundDecl=foundDecl,
+ presenceChecker=presenceCheckerClass(descriptor, foundVar=foundVar).define(),
+ foundVar=foundVar)
+ else:
+ body = None
+ return body
+
+
+class CGDeleteNamedProperty(CGAbstractStaticMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'),
+ Argument('JS::Handle<JSObject*>', 'xray'),
+ Argument('JS::Handle<JSObject*>', 'proxy'),
+ Argument('JS::Handle<jsid>', 'id'),
+ Argument('JS::ObjectOpResult&', 'opresult')]
+ CGAbstractStaticMethod.__init__(self, descriptor, "DeleteNamedProperty",
+ "bool", args)
+
+ def definition_body(self):
+ return fill(
+ """
+ MOZ_ASSERT(xpc::WrapperFactory::IsXrayWrapper(xray));
+ MOZ_ASSERT(js::IsProxy(proxy));
+ MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy));
+ JSAutoCompartment ac(cx, proxy);
+ bool deleteSucceeded;
+ bool found = false;
+ $*{namedBody}
+ if (!found || deleteSucceeded) {
+ return opresult.succeed();
+ }
+ return opresult.failCantDelete();
+ """,
+ namedBody=getDeleterBody(self.descriptor, "Named", foundVar="found"))
+
+
+class CGDOMJSProxyHandler_delete(ClassMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'),
+ Argument('JS::Handle<JSObject*>', 'proxy'),
+ Argument('JS::Handle<jsid>', 'id'),
+ Argument('JS::ObjectOpResult&', 'opresult')]
+ ClassMethod.__init__(self, "delete_", "bool", args,
+ virtual=True, override=True, const=True)
+ self.descriptor = descriptor
+
+ def getBody(self):
+ delete = dedent("""
+ MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
+ "Should not have a XrayWrapper here");
+
+ """)
+
+ indexedBody = getDeleterBody(self.descriptor, "Indexed")
+ if indexedBody is not None:
+ delete += fill(
+ """
+ uint32_t index = GetArrayIndexFromId(cx, id);
+ if (IsArrayIndex(index)) {
+ bool deleteSucceeded;
+ $*{indexedBody}
+ return deleteSucceeded ? opresult.succeed() : opresult.failCantDelete();
+ }
+ """,
+ indexedBody=indexedBody)
+
+ namedBody = getDeleterBody(self.descriptor, "Named", foundVar="found")
+ if namedBody is not None:
+ delete += dedent(
+ """
+ // Try named delete only if the named property visibility
+ // algorithm says the property is visible.
+ bool tryNamedDelete = true;
+ { // Scope for expando
+ JS::Rooted<JSObject*> expando(cx, DOMProxyHandler::GetExpandoObject(proxy));
+ if (expando) {
+ bool hasProp;
+ if (!JS_HasPropertyById(cx, expando, id, &hasProp)) {
+ return false;
+ }
+ tryNamedDelete = !hasProp;
+ }
+ }
+ """)
+
+ if not self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
+ delete += dedent(
+ """
+ if (tryNamedDelete) {
+ bool hasOnProto;
+ if (!HasPropertyOnPrototype(cx, proxy, id, &hasOnProto)) {
+ return false;
+ }
+ tryNamedDelete = !hasOnProto;
+ }
+ """)
+
+ # We always return above for an index id in the case when we support
+ # indexed properties, so we can just treat the id as a name
+ # unconditionally here.
+ delete += fill(
+ """
+ if (tryNamedDelete) {
+ bool found = false;
+ bool deleteSucceeded;
+ $*{namedBody}
+ if (found) {
+ return deleteSucceeded ? opresult.succeed() : opresult.failCantDelete();
+ }
+ }
+ """,
+ namedBody=namedBody)
+
+ delete += dedent("""
+
+ return dom::DOMProxyHandler::delete_(cx, proxy, id, opresult);
+ """)
+
+ return delete
+
+
+class CGDOMJSProxyHandler_ownPropNames(ClassMethod):
+ def __init__(self, descriptor, ):
+ args = [Argument('JSContext*', 'cx'),
+ Argument('JS::Handle<JSObject*>', 'proxy'),
+ Argument('unsigned', 'flags'),
+ Argument('JS::AutoIdVector&', 'props')]
+ ClassMethod.__init__(self, "ownPropNames", "bool", args,
+ virtual=True, override=True, const=True)
+ self.descriptor = descriptor
+
+ def getBody(self):
+ # Per spec, we do indices, then named props, then everything else
+ if self.descriptor.supportsIndexedProperties():
+ addIndices = dedent("""
+
+ 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 = ""
+
+ if self.descriptor.supportsNamedProperties():
+ if self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
+ shadow = "!isXray"
+ else:
+ shadow = "false"
+ addNames = fill(
+ """
+ nsTArray<nsString> names;
+ UnwrapProxy(proxy)->GetSupportedNames(names);
+ if (!AppendNamedPropertyIds(cx, proxy, names, ${shadow}, props)) {
+ return false;
+ }
+ """,
+ shadow=shadow)
+ if not self.descriptor.namedPropertiesEnumerable:
+ addNames = CGIfWrapper(CGGeneric(addNames),
+ "flags & JSITER_HIDDEN").define()
+ addNames = "\n" + addNames
+ else:
+ addNames = ""
+
+ return fill(
+ """
+ bool isXray = xpc::WrapperFactory::IsXrayWrapper(proxy);
+ $*{addIndices}
+ $*{addNames}
+
+ JS::Rooted<JSObject*> expando(cx);
+ if (!isXray && (expando = DOMProxyHandler::GetExpandoObject(proxy)) &&
+ !js::GetPropertyKeys(cx, expando, flags, &props)) {
+ return false;
+ }
+
+ return true;
+ """,
+ addIndices=addIndices,
+ addNames=addNames)
+
+
+class CGDOMJSProxyHandler_hasOwn(ClassMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'),
+ Argument('JS::Handle<JSObject*>', 'proxy'),
+ Argument('JS::Handle<jsid>', 'id'),
+ Argument('bool*', 'bp')]
+ ClassMethod.__init__(self, "hasOwn", "bool", args,
+ virtual=True, override=True, const=True)
+ self.descriptor = descriptor
+
+ def getBody(self):
+ if self.descriptor.supportsIndexedProperties():
+ indexed = fill(
+ """
+ uint32_t index = GetArrayIndexFromId(cx, id);
+ if (IsArrayIndex(index)) {
+ bool found = false;
+ $*{presenceChecker}
+
+ *bp = found;
+ return true;
+ }
+
+ """,
+ presenceChecker=CGProxyIndexedPresenceChecker(self.descriptor, foundVar="found").define())
+ else:
+ indexed = ""
+
+ if self.descriptor.supportsNamedProperties():
+ # If we support indexed properties we always return above for index
+ # property names, so no need to check for those here.
+ named = fill(
+ """
+ bool found = false;
+ $*{presenceChecker}
+
+ *bp = found;
+ """,
+ presenceChecker=CGProxyNamedPresenceChecker(self.descriptor, foundVar="found").define())
+ if not self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
+ named = fill(
+ """
+ bool hasOnProto;
+ if (!HasPropertyOnPrototype(cx, proxy, id, &hasOnProto)) {
+ return false;
+ }
+ if (!hasOnProto) {
+ $*{protoLacksProperty}
+ return true;
+ }
+ """,
+ protoLacksProperty=named)
+ named += "*bp = false;\n"
+ else:
+ named += "\n"
+ else:
+ named = "*bp = false;\n"
+
+ return fill(
+ """
+ MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
+ "Should not have a XrayWrapper here");
+
+ $*{indexed}
+
+ JS::Rooted<JSObject*> expando(cx, GetExpandoObject(proxy));
+ if (expando) {
+ bool b = true;
+ bool ok = JS_HasPropertyById(cx, expando, id, &b);
+ *bp = !!b;
+ if (!ok || *bp) {
+ return ok;
+ }
+ }
+
+ $*{named}
+ return true;
+ """,
+ indexed=indexed,
+ named=named)
+
+
+class CGDOMJSProxyHandler_get(ClassMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'),
+ Argument('JS::Handle<JSObject*>', 'proxy'),
+ Argument('JS::Handle<JS::Value>', 'receiver'),
+ Argument('JS::Handle<jsid>', 'id'),
+ Argument('JS::MutableHandle<JS::Value>', 'vp')]
+ ClassMethod.__init__(self, "get", "bool", args,
+ virtual=True, override=True, const=True)
+ self.descriptor = descriptor
+
+ def getBody(self):
+ getUnforgeableOrExpando = dedent("""
+ { // Scope for expando
+ JS::Rooted<JSObject*> expando(cx, DOMProxyHandler::GetExpandoObject(proxy));
+ if (expando) {
+ bool hasProp;
+ if (!JS_HasPropertyById(cx, expando, id, &hasProp)) {
+ return false;
+ }
+
+ if (hasProp) {
+ // Forward the get to the expando object, but our receiver is whatever our
+ // receiver is.
+ return JS_ForwardGetPropertyTo(cx, expando, id, receiver, vp);
+ }
+ }
+ }
+ """)
+
+ templateValues = {'jsvalRef': 'vp', 'jsvalHandle': 'vp', 'obj': 'proxy'}
+
+ if self.descriptor.supportsIndexedProperties():
+ getIndexedOrExpando = fill(
+ """
+ uint32_t index = GetArrayIndexFromId(cx, id);
+ if (IsArrayIndex(index)) {
+ $*{callGetter}
+ // Even if we don't have this index, we don't forward the
+ // get on to our expando object.
+ } else {
+ $*{getUnforgeableOrExpando}
+ }
+ """,
+ callGetter=CGProxyIndexedGetter(self.descriptor, templateValues).define(),
+ getUnforgeableOrExpando=getUnforgeableOrExpando)
+ else:
+ getIndexedOrExpando = getUnforgeableOrExpando
+
+ if self.descriptor.supportsNamedProperties():
+ getNamed = CGProxyNamedGetter(self.descriptor, templateValues)
+ if self.descriptor.supportsIndexedProperties():
+ getNamed = CGIfWrapper(getNamed, "!IsArrayIndex(index)")
+ getNamed = getNamed.define() + "\n"
+ else:
+ getNamed = ""
+
+ getOnPrototype = dedent("""
+ bool foundOnPrototype;
+ if (!GetPropertyOnPrototype(cx, proxy, receiver, id, &foundOnPrototype, vp)) {
+ return false;
+ }
+
+ if (foundOnPrototype) {
+ return true;
+ }
+
+ """)
+ if self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
+ getNamed = getNamed + getOnPrototype
+ else:
+ getNamed = getOnPrototype + getNamed
+
+ return fill(
+ """
+ MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
+ "Should not have a XrayWrapper here");
+
+ $*{indexedOrExpando}
+
+ $*{named}
+ vp.setUndefined();
+ return true;
+ """,
+ indexedOrExpando=getIndexedOrExpando,
+ named=getNamed)
+
+
+class CGDOMJSProxyHandler_setCustom(ClassMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'),
+ Argument('JS::Handle<JSObject*>', 'proxy'),
+ Argument('JS::Handle<jsid>', 'id'),
+ Argument('JS::Handle<JS::Value>', 'v'),
+ Argument('bool*', 'done')]
+ ClassMethod.__init__(self, "setCustom", "bool", args, virtual=True, override=True, const=True)
+ self.descriptor = descriptor
+
+ def getBody(self):
+ assertion = ("MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),\n"
+ ' "Should not have a XrayWrapper here");\n')
+
+ # Correctness first. If we have a NamedSetter and [OverrideBuiltins],
+ # always call the NamedSetter and never do anything else.
+ namedSetter = self.descriptor.operations['NamedSetter']
+ if (namedSetter is not None and
+ self.descriptor.interface.getExtendedAttribute('OverrideBuiltins')):
+ # Check assumptions.
+ if self.descriptor.supportsIndexedProperties():
+ raise ValueError("In interface " + self.descriptor.name + ": " +
+ "Can't cope with [OverrideBuiltins] and an indexed getter")
+ if self.descriptor.operations['NamedCreator'] is not namedSetter:
+ raise ValueError("In interface " + self.descriptor.name + ": " +
+ "Can't cope with named setter that is not also a named creator")
+ if self.descriptor.hasUnforgeableMembers:
+ raise ValueError("In interface " + self.descriptor.name + ": " +
+ "Can't cope with [OverrideBuiltins] and unforgeable members")
+
+ callSetter = CGProxyNamedSetter(self.descriptor, argumentHandleValue="v")
+ return (assertion +
+ callSetter.define() +
+ "*done = true;\n"
+ "return true;\n")
+
+ # As an optimization, if we are going to call an IndexedSetter, go
+ # ahead and call it and have done.
+ indexedSetter = self.descriptor.operations['IndexedSetter']
+ if indexedSetter is not None:
+ if self.descriptor.operations['IndexedCreator'] is not indexedSetter:
+ raise ValueError("In interface " + self.descriptor.name + ": " +
+ "Can't cope with indexed setter that is not " +
+ "also an indexed creator")
+ setIndexed = fill(
+ """
+ uint32_t index = GetArrayIndexFromId(cx, id);
+ if (IsArrayIndex(index)) {
+ $*{callSetter}
+ *done = true;
+ return true;
+ }
+
+ """,
+ callSetter=CGProxyIndexedSetter(self.descriptor,
+ argumentHandleValue="v").define())
+ else:
+ setIndexed = ""
+
+ return (assertion +
+ setIndexed +
+ "*done = false;\n"
+ "return true;\n")
+
+
+class CGDOMJSProxyHandler_className(ClassMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSContext*', 'cx'),
+ Argument('JS::Handle<JSObject*>', 'proxy')]
+ ClassMethod.__init__(self, "className", "const char*", args,
+ virtual=True, override=True, const=True)
+ self.descriptor = descriptor
+
+ def getBody(self):
+ return 'return "%s";\n' % self.descriptor.name
+
+
+class CGDOMJSProxyHandler_finalizeInBackground(ClassMethod):
+ def __init__(self, descriptor):
+ args = [Argument('const JS::Value&', 'priv')]
+ ClassMethod.__init__(self, "finalizeInBackground", "bool", args,
+ virtual=True, override=True, const=True)
+ self.descriptor = descriptor
+
+ def getBody(self):
+ return "return false;\n"
+
+
+class CGDOMJSProxyHandler_finalize(ClassMethod):
+ def __init__(self, descriptor):
+ args = [Argument('JSFreeOp*', 'fop'), Argument('JSObject*', 'proxy')]
+ ClassMethod.__init__(self, "finalize", "void", args,
+ virtual=True, override=True, const=True)
+ self.descriptor = descriptor
+
+ def getBody(self):
+ return (("%s* self = UnwrapPossiblyNotInitializedDOMObject<%s>(proxy);\n" %
+ (self.descriptor.nativeType, self.descriptor.nativeType)) +
+ finalizeHook(self.descriptor, FINALIZE_HOOK_NAME, self.args[0].name).define())
+
+
+class CGDOMJSProxyHandler_getElements(ClassMethod):
+ def __init__(self, descriptor):
+ assert descriptor.supportsIndexedProperties()
+
+ args = [Argument('JSContext*', 'cx'),
+ Argument('JS::Handle<JSObject*>', 'proxy'),
+ Argument('uint32_t', 'begin'),
+ Argument('uint32_t', 'end'),
+ Argument('js::ElementAdder*', 'adder')]
+ ClassMethod.__init__(self, "getElements", "bool", args, virtual=True, override=True, const=True)
+ self.descriptor = descriptor
+
+ def getBody(self):
+ # Just like ownPropertyKeys we'll assume that we have no holes, so
+ # we have all properties from 0 to length. If that ever changes
+ # (unlikely), we'll need to do something a bit more clever with how we
+ # forward on to our ancestor.
+
+ templateValues = {
+ 'jsvalRef': 'temp',
+ 'jsvalHandle': '&temp',
+ 'obj': 'proxy',
+ 'successCode': ("if (!adder->append(cx, temp)) return false;\n"
+ "continue;\n")
+ }
+ get = CGProxyIndexedGetter(self.descriptor, templateValues, False, False).define()
+
+ return fill(
+ """
+ JS::Rooted<JS::Value> temp(cx);
+ MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
+ "Should not have a XrayWrapper here");
+
+ ${nativeType}* self = UnwrapProxy(proxy);
+ uint32_t length = self->Length();
+ // Compute the end of the indices we'll get ourselves
+ uint32_t ourEnd = std::max(begin, std::min(end, length));
+
+ for (uint32_t index = begin; index < ourEnd; ++index) {
+ $*{get}
+ }
+
+ if (end > ourEnd) {
+ JS::Rooted<JSObject*> proto(cx);
+ if (!js::GetObjectProto(cx, proxy, &proto)) {
+ return false;
+ }
+ return js::GetElementsWithAdder(cx, proto, proxy, ourEnd, end, adder);
+ }
+
+ return true;
+ """,
+ nativeType=self.descriptor.nativeType,
+ get=get)
+
+
+class CGDOMJSProxyHandler_getInstance(ClassMethod):
+ def __init__(self):
+ ClassMethod.__init__(self, "getInstance", "const DOMProxyHandler*", [], static=True)
+
+ def getBody(self):
+ return dedent("""
+ static const DOMProxyHandler instance;
+ return &instance;
+ """)
+
+
+class CGDOMJSProxyHandler_getPrototypeIfOrdinary(ClassMethod):
+ def __init__(self):
+ args = [Argument('JSContext*', 'cx'),
+ Argument('JS::Handle<JSObject*>', 'proxy'),
+ Argument('bool*', 'isOrdinary'),
+ Argument('JS::MutableHandle<JSObject*>', 'proto')]
+
+ ClassMethod.__init__(self, "getPrototypeIfOrdinary", "bool", args,
+ virtual=True, override=True, const=True)
+
+ def getBody(self):
+ return dedent("""
+ *isOrdinary = false;
+ return true;
+ """)
+
+
+class CGDOMJSProxyHandler_call(ClassMethod):
+ def __init__(self):
+ args = [Argument('JSContext*', 'cx'),
+ Argument('JS::Handle<JSObject*>', 'proxy'),
+ Argument('const JS::CallArgs&', 'args')]
+
+ ClassMethod.__init__(self, "call", "bool", args, virtual=True, override=True, const=True)
+
+ def getBody(self):
+ return fill(
+ """
+ return js::ForwardToNative(cx, ${legacyCaller}, args);
+ """,
+ legacyCaller=LEGACYCALLER_HOOK_NAME)
+
+
+class CGDOMJSProxyHandler_isCallable(ClassMethod):
+ def __init__(self):
+ ClassMethod.__init__(self, "isCallable", "bool",
+ [Argument('JSObject*', 'obj')],
+ virtual=True, override=True, const=True)
+
+ def getBody(self):
+ return dedent("""
+ return true;
+ """)
+
+
+class CGDOMJSProxyHandler(CGClass):
+ def __init__(self, descriptor):
+ assert (descriptor.supportsIndexedProperties() or
+ descriptor.supportsNamedProperties() or
+ descriptor.hasNonOrdinaryGetPrototypeOf())
+ methods = [CGDOMJSProxyHandler_getOwnPropDescriptor(descriptor),
+ CGDOMJSProxyHandler_defineProperty(descriptor),
+ ClassUsingDeclaration("mozilla::dom::DOMProxyHandler",
+ "defineProperty"),
+ CGDOMJSProxyHandler_ownPropNames(descriptor),
+ CGDOMJSProxyHandler_hasOwn(descriptor),
+ CGDOMJSProxyHandler_get(descriptor),
+ CGDOMJSProxyHandler_className(descriptor),
+ CGDOMJSProxyHandler_finalizeInBackground(descriptor),
+ CGDOMJSProxyHandler_finalize(descriptor),
+ CGDOMJSProxyHandler_getInstance(),
+ CGDOMJSProxyHandler_delete(descriptor)]
+ constructors = [
+ ClassConstructor(
+ [],
+ constexpr=True,
+ visibility="public",
+ explicit=True)
+ ]
+
+ if descriptor.supportsIndexedProperties():
+ methods.append(CGDOMJSProxyHandler_getElements(descriptor))
+ if (descriptor.operations['IndexedSetter'] is not None or
+ (descriptor.operations['NamedSetter'] is not None and
+ descriptor.interface.getExtendedAttribute('OverrideBuiltins'))):
+ methods.append(CGDOMJSProxyHandler_setCustom(descriptor))
+ if descriptor.hasNonOrdinaryGetPrototypeOf():
+ methods.append(CGDOMJSProxyHandler_getPrototypeIfOrdinary())
+ if descriptor.operations['LegacyCaller']:
+ methods.append(CGDOMJSProxyHandler_call())
+ methods.append(CGDOMJSProxyHandler_isCallable())
+
+ if descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
+ parentClass = 'ShadowingDOMProxyHandler'
+ else:
+ parentClass = 'mozilla::dom::DOMProxyHandler'
+
+ CGClass.__init__(self, 'DOMProxyHandler',
+ bases=[ClassBase(parentClass)],
+ constructors=constructors,
+ methods=methods)
+
+
+class CGDOMJSProxyHandlerDeclarer(CGThing):
+ """
+ A class for declaring a DOMProxyHandler.
+ """
+ def __init__(self, handlerThing):
+ self.handlerThing = handlerThing
+
+ def declare(self):
+ # Our class declaration should happen when we're defining
+ return ""
+
+ def define(self):
+ return self.handlerThing.declare()
+
+
+class CGDOMJSProxyHandlerDefiner(CGThing):
+ """
+ A class for defining a DOMProxyHandler.
+ """
+ def __init__(self, handlerThing):
+ self.handlerThing = handlerThing
+
+ def declare(self):
+ return ""
+
+ def define(self):
+ return self.handlerThing.define()
+
+
+def stripTrailingWhitespace(text):
+ tail = '\n' if text.endswith('\n') else ''
+ lines = text.splitlines()
+ return '\n'.join(line.rstrip() for line in lines) + tail
+
+
+class MemberProperties:
+ def __init__(self):
+ self.isGenericMethod = False
+ self.isCrossOriginMethod = False
+ self.isPromiseReturningMethod = False
+ self.isGenericGetter = False
+ self.isLenientGetter = False
+ self.isCrossOriginGetter = False
+ self.isGenericSetter = False
+ self.isLenientSetter = False
+ self.isCrossOriginSetter = False
+ self.isJsonifier = False
+
+
+def memberProperties(m, descriptor):
+ props = MemberProperties()
+ if m.isMethod():
+ if m == descriptor.operations['Jsonifier']:
+ props.isGenericMethod = descriptor.needsSpecialGenericOps()
+ props.isJsonifier = True
+ elif (not m.isIdentifierLess() or m == descriptor.operations['Stringifier']):
+ if not m.isStatic() and descriptor.interface.hasInterfacePrototypeObject():
+ if descriptor.needsSpecialGenericOps():
+ if m.returnsPromise():
+ props.isPromiseReturningMethod = True
+ else:
+ props.isGenericMethod = True
+ if m.getExtendedAttribute("CrossOriginCallable"):
+ props.isCrossOriginMethod = True
+ elif m.isAttr():
+ if not m.isStatic() and descriptor.interface.hasInterfacePrototypeObject():
+ if m.hasLenientThis():
+ props.isLenientGetter = True
+ elif m.getExtendedAttribute("CrossOriginReadable"):
+ props.isCrossOriginGetter = True
+ elif descriptor.needsSpecialGenericOps():
+ props.isGenericGetter = True
+ if not m.readonly:
+ if not m.isStatic() and descriptor.interface.hasInterfacePrototypeObject():
+ if m.hasLenientThis():
+ props.isLenientSetter = True
+ elif IsCrossOriginWritable(m, descriptor):
+ props.isCrossOriginSetter = True
+ elif descriptor.needsSpecialGenericOps():
+ props.isGenericSetter = True
+ elif m.getExtendedAttribute("PutForwards"):
+ if IsCrossOriginWritable(m, descriptor):
+ props.isCrossOriginSetter = True
+ elif descriptor.needsSpecialGenericOps():
+ props.isGenericSetter = True
+ elif (m.getExtendedAttribute("Replaceable") or
+ m.getExtendedAttribute("LenientSetter")):
+ if descriptor.needsSpecialGenericOps():
+ props.isGenericSetter = True
+
+ return props
+
+
+class CGDescriptor(CGThing):
+ def __init__(self, descriptor):
+ CGThing.__init__(self)
+
+ assert not descriptor.concrete or descriptor.interface.hasInterfacePrototypeObject()
+
+ self._deps = descriptor.interface.getDeps()
+
+ cgThings = []
+ cgThings.append(CGGeneric(declare="typedef %s NativeType;\n" %
+ descriptor.nativeType))
+ parent = descriptor.interface.parent
+ if parent:
+ cgThings.append(CGGeneric("static_assert(IsRefcounted<NativeType>::value == IsRefcounted<%s::NativeType>::value,\n"
+ " \"Can't inherit from an interface with a different ownership model.\");\n" %
+ toBindingNamespace(descriptor.parentPrototypeName)))
+
+ # These are set to true if at least one non-static
+ # method/getter/setter or jsonifier exist on the interface.
+ (hasMethod, hasGetter, hasLenientGetter, hasSetter, hasLenientSetter,
+ hasPromiseReturningMethod) = False, False, False, False, False, False
+ jsonifierMethod = None
+ crossOriginMethods, crossOriginGetters, crossOriginSetters = set(), set(), set()
+ unscopableNames = list()
+ for n in descriptor.interface.namedConstructors:
+ cgThings.append(CGClassConstructor(descriptor, n,
+ NamedConstructorName(n)))
+ for m in descriptor.interface.members:
+ if m.isMethod() and m.identifier.name == 'queryInterface':
+ continue
+
+ props = memberProperties(m, descriptor)
+
+ if m.isMethod():
+ if m.getExtendedAttribute("Unscopable"):
+ assert not m.isStatic()
+ unscopableNames.append(m.identifier.name)
+ if props.isJsonifier:
+ jsonifierMethod = m
+ elif not m.isIdentifierLess() or m == descriptor.operations['Stringifier']:
+ if m.isStatic():
+ assert descriptor.interface.hasInterfaceObject()
+ cgThings.append(CGStaticMethod(descriptor, m))
+ if m.returnsPromise():
+ cgThings.append(CGStaticMethodJitinfo(m))
+ elif descriptor.interface.hasInterfacePrototypeObject():
+ specializedMethod = CGSpecializedMethod(descriptor, m)
+ cgThings.append(specializedMethod)
+ if m.returnsPromise():
+ cgThings.append(CGMethodPromiseWrapper(descriptor, specializedMethod))
+ cgThings.append(CGMemberJITInfo(descriptor, m))
+ if props.isCrossOriginMethod:
+ crossOriginMethods.add(m.identifier.name)
+ # If we've hit the maplike/setlike member itself, go ahead and
+ # generate its convenience functions.
+ elif m.isMaplikeOrSetlike():
+ cgThings.append(CGMaplikeOrSetlikeHelperGenerator(descriptor, m))
+ elif m.isAttr():
+ if m.stringifier:
+ raise TypeError("Stringifier attributes not supported yet. "
+ "See bug 824857.\n"
+ "%s" % m.location)
+ if m.getExtendedAttribute("Unscopable"):
+ assert not m.isStatic()
+ unscopableNames.append(m.identifier.name)
+ if m.isStatic():
+ assert descriptor.interface.hasInterfaceObject()
+ cgThings.append(CGStaticGetter(descriptor, m))
+ elif descriptor.interface.hasInterfacePrototypeObject():
+ if isNonExposedNavigatorObjectGetter(m, descriptor):
+ continue
+ cgThings.append(CGSpecializedGetter(descriptor, m))
+ if props.isCrossOriginGetter:
+ crossOriginGetters.add(m.identifier.name)
+ if not m.readonly:
+ if m.isStatic():
+ assert descriptor.interface.hasInterfaceObject()
+ cgThings.append(CGStaticSetter(descriptor, m))
+ elif descriptor.interface.hasInterfacePrototypeObject():
+ cgThings.append(CGSpecializedSetter(descriptor, m))
+ if props.isCrossOriginSetter:
+ crossOriginSetters.add(m.identifier.name)
+ elif m.getExtendedAttribute("PutForwards"):
+ cgThings.append(CGSpecializedForwardingSetter(descriptor, m))
+ if props.isCrossOriginSetter:
+ crossOriginSetters.add(m.identifier.name)
+ elif m.getExtendedAttribute("Replaceable"):
+ cgThings.append(CGSpecializedReplaceableSetter(descriptor, m))
+ elif m.getExtendedAttribute("LenientSetter"):
+ cgThings.append(CGSpecializedLenientSetter(descriptor, m))
+ if (not m.isStatic() and
+ descriptor.interface.hasInterfacePrototypeObject()):
+ cgThings.append(CGMemberJITInfo(descriptor, m))
+
+ hasMethod = hasMethod or props.isGenericMethod
+ hasPromiseReturningMethod = (hasPromiseReturningMethod or
+ props.isPromiseReturningMethod)
+ hasGetter = hasGetter or props.isGenericGetter
+ hasLenientGetter = hasLenientGetter or props.isLenientGetter
+ hasSetter = hasSetter or props.isGenericSetter
+ hasLenientSetter = hasLenientSetter or props.isLenientSetter
+
+ if jsonifierMethod:
+ cgThings.append(CGJsonifyAttributesMethod(descriptor))
+ cgThings.append(CGJsonifierMethod(descriptor, jsonifierMethod))
+ cgThings.append(CGMemberJITInfo(descriptor, jsonifierMethod))
+ if hasMethod:
+ cgThings.append(CGGenericMethod(descriptor))
+ if hasPromiseReturningMethod:
+ cgThings.append(CGGenericPromiseReturningMethod(descriptor))
+ if len(crossOriginMethods):
+ cgThings.append(CGGenericMethod(descriptor,
+ allowCrossOriginThis=True))
+ if hasGetter:
+ cgThings.append(CGGenericGetter(descriptor))
+ if hasLenientGetter:
+ cgThings.append(CGGenericGetter(descriptor, lenientThis=True))
+ if len(crossOriginGetters):
+ cgThings.append(CGGenericGetter(descriptor,
+ allowCrossOriginThis=True))
+ if hasSetter:
+ cgThings.append(CGGenericSetter(descriptor))
+ if hasLenientSetter:
+ cgThings.append(CGGenericSetter(descriptor, lenientThis=True))
+ if len(crossOriginSetters):
+ cgThings.append(CGGenericSetter(descriptor,
+ allowCrossOriginThis=True))
+
+ if descriptor.interface.isNavigatorProperty():
+ cgThings.append(CGConstructNavigatorObject(descriptor))
+
+ if descriptor.concrete and not descriptor.proxy:
+ if wantsAddProperty(descriptor):
+ cgThings.append(CGAddPropertyHook(descriptor))
+
+ # Always have a finalize hook, regardless of whether the class
+ # wants a custom hook.
+ cgThings.append(CGClassFinalizeHook(descriptor))
+
+ if descriptor.concrete and descriptor.wrapperCache:
+ cgThings.append(CGClassObjectMovedHook(descriptor))
+
+ # Generate the _ClearCachedFooValue methods before the property arrays that use them.
+ if descriptor.interface.isJSImplemented():
+ for m in clearableCachedAttrs(descriptor):
+ cgThings.append(CGJSImplClearCachedValueMethod(descriptor, m))
+
+ # Need to output our generated hasinstance bits before
+ # PropertyArrays tries to use them.
+ if (descriptor.interface.hasInterfaceObject() and
+ NeedsGeneratedHasInstance(descriptor)):
+ cgThings.append(CGHasInstanceHook(descriptor))
+
+ properties = PropertyArrays(descriptor)
+ cgThings.append(CGGeneric(define=str(properties)))
+ cgThings.append(CGNativeProperties(descriptor, properties))
+
+ if descriptor.interface.hasInterfaceObject():
+ cgThings.append(CGClassConstructor(descriptor,
+ descriptor.interface.ctor()))
+ cgThings.append(CGInterfaceObjectJSClass(descriptor, properties))
+ cgThings.append(CGNamedConstructors(descriptor))
+
+ cgThings.append(CGLegacyCallHook(descriptor))
+ if descriptor.interface.getExtendedAttribute("NeedResolve"):
+ cgThings.append(CGResolveHook(descriptor))
+ cgThings.append(CGMayResolveHook(descriptor))
+ cgThings.append(CGEnumerateHook(descriptor))
+
+ if descriptor.hasNamedPropertiesObject:
+ cgThings.append(CGGetNamedPropertiesObjectMethod(descriptor))
+
+ if descriptor.interface.hasInterfacePrototypeObject():
+ cgThings.append(CGPrototypeJSClass(descriptor, properties))
+
+ if ((descriptor.interface.hasInterfaceObject() or descriptor.interface.isNavigatorProperty()) and
+ not descriptor.interface.isExternal() and
+ descriptor.isExposedConditionally()):
+ cgThings.append(CGConstructorEnabled(descriptor))
+
+ if descriptor.registersGlobalNamesOnWindow:
+ cgThings.append(CGDefineDOMInterfaceMethod(descriptor))
+
+ if (descriptor.interface.hasMembersInSlots() and
+ descriptor.interface.hasChildInterfaces()):
+ raise TypeError("We don't support members in slots on "
+ "non-leaf interfaces like %s" %
+ descriptor.interface.identifier.name)
+
+ if descriptor.concrete:
+ if descriptor.proxy:
+ if descriptor.interface.totalMembersInSlots != 0:
+ raise TypeError("We can't have extra reserved slots for "
+ "proxy interface %s" %
+ descriptor.interface.identifier.name)
+ cgThings.append(CGGeneric(fill(
+ """
+ static_assert(IsBaseOf<nsISupports, ${nativeType} >::value,
+ "We don't support non-nsISupports native classes for "
+ "proxy-based bindings yet");
+
+ """,
+ nativeType=descriptor.nativeType)))
+ if not descriptor.wrapperCache:
+ raise TypeError("We need a wrappercache to support expandos for proxy-based "
+ "bindings (" + descriptor.name + ")")
+ handlerThing = CGDOMJSProxyHandler(descriptor)
+ cgThings.append(CGDOMJSProxyHandlerDeclarer(handlerThing))
+ cgThings.append(CGProxyIsProxy(descriptor))
+ cgThings.append(CGProxyUnwrap(descriptor))
+ cgThings.append(CGDOMJSProxyHandlerDefiner(handlerThing))
+ cgThings.append(CGDOMProxyJSClass(descriptor))
+ else:
+ cgThings.append(CGDOMJSClass(descriptor))
+ cgThings.append(CGGetJSClassMethod(descriptor))
+ if descriptor.interface.hasMembersInSlots():
+ cgThings.append(CGUpdateMemberSlotsMethod(descriptor))
+
+ if descriptor.isGlobal():
+ assert descriptor.wrapperCache
+ cgThings.append(CGWrapGlobalMethod(descriptor, properties))
+ elif descriptor.wrapperCache:
+ cgThings.append(CGWrapWithCacheMethod(descriptor, properties))
+ cgThings.append(CGWrapMethod(descriptor))
+ else:
+ cgThings.append(CGWrapNonWrapperCacheMethod(descriptor,
+ properties))
+
+ # Set up our Xray callbacks as needed. This needs to come
+ # after we have our DOMProxyHandler defined.
+ if descriptor.wantsXrays:
+ if descriptor.concrete and descriptor.proxy:
+ cgThings.append(CGResolveOwnProperty(descriptor))
+ cgThings.append(CGEnumerateOwnProperties(descriptor))
+ if descriptor.needsXrayNamedDeleterHook():
+ cgThings.append(CGDeleteNamedProperty(descriptor))
+ elif descriptor.needsXrayResolveHooks():
+ cgThings.append(CGResolveOwnPropertyViaResolve(descriptor))
+ cgThings.append(CGEnumerateOwnPropertiesViaGetOwnPropertyNames(descriptor))
+ if descriptor.wantsXrayExpandoClass:
+ cgThings.append(CGXrayExpandoJSClass(descriptor))
+
+ # Now that we have our ResolveOwnProperty/EnumerateOwnProperties stuff
+ # done, set up our NativePropertyHooks.
+ cgThings.append(CGNativePropertyHooks(descriptor, properties))
+
+ # If we're not wrappercached, we don't know how to clear our
+ # cached values, since we can't get at the JSObject.
+ if descriptor.wrapperCache:
+ cgThings.extend(CGClearCachedValueMethod(descriptor, m) for
+ m in clearableCachedAttrs(descriptor))
+
+ haveUnscopables = (len(unscopableNames) != 0 and
+ descriptor.interface.hasInterfacePrototypeObject())
+ if haveUnscopables:
+ cgThings.append(
+ CGList([CGGeneric("static const char* const unscopableNames[] = {"),
+ CGIndenter(CGList([CGGeneric('"%s"' % name) for
+ name in unscopableNames] +
+ [CGGeneric("nullptr")], ",\n")),
+ CGGeneric("};\n")], "\n"))
+
+ # CGCreateInterfaceObjectsMethod needs to come after our
+ # CGDOMJSClass and unscopables, if any.
+ cgThings.append(CGCreateInterfaceObjectsMethod(descriptor, properties,
+ haveUnscopables))
+
+ # CGGetProtoObjectMethod and CGGetConstructorObjectMethod need
+ # to come after CGCreateInterfaceObjectsMethod.
+ if descriptor.interface.hasInterfacePrototypeObject():
+ cgThings.append(CGGetProtoObjectHandleMethod(descriptor))
+ if descriptor.interface.hasChildInterfaces():
+ cgThings.append(CGGetProtoObjectMethod(descriptor))
+ if descriptor.interface.hasInterfaceObject():
+ cgThings.append(CGGetConstructorObjectHandleMethod(descriptor))
+ cgThings.append(CGGetConstructorObjectMethod(descriptor))
+
+ # See whether we need we need to generate an IsPermitted method
+ if crossOriginGetters or crossOriginSetters or crossOriginMethods:
+ cgThings.append(CGIsPermittedMethod(descriptor,
+ crossOriginGetters,
+ crossOriginSetters,
+ crossOriginMethods))
+
+ 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()
+
+ def deps(self):
+ return self._deps
+
+
+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 : uint16_t\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):
+ return ""
+
+
+def initIdsClassMethod(identifiers, atomCacheName):
+ idinit = ['!atomsCache->%s.init(cx, "%s")' %
+ (CGDictionary.makeIdName(id),
+ id)
+ for id in identifiers]
+ idinit.reverse()
+ body = fill(
+ """
+ MOZ_ASSERT(!*reinterpret_cast<jsid**>(atomsCache));
+
+ // Initialize these in reverse order so that any failure leaves the first one
+ // uninitialized.
+ if (${idinit}) {
+ return false;
+ }
+ return true;
+ """,
+ idinit=" ||\n ".join(idinit))
+ return ClassMethod("InitIds", "bool", [
+ Argument("JSContext*", "cx"),
+ Argument("%s*" % atomCacheName, "atomsCache")
+ ], static=True, body=body, visibility="private")
+
+
+class CGDictionary(CGThing):
+ def __init__(self, dictionary, descriptorProvider):
+ self.dictionary = dictionary
+ self.descriptorProvider = descriptorProvider
+ self.needToInitIds = len(dictionary.members) > 0
+ self.memberInfo = [
+ (member,
+ getJSToNativeConversionInfo(
+ member.type,
+ descriptorProvider,
+ isEnforceRange=member.enforceRange,
+ isClamp=member.clamp,
+ isMember="Dictionary",
+ isOptional=member.canHaveMissingValue(),
+ defaultValue=member.defaultValue,
+ sourceDescription=self.getMemberSourceDescription(member)))
+ for member in dictionary.members]
+
+ # If we have a union member containing something in the same
+ # file as us, bail: the C++ includes won't work out.
+ for member in dictionary.members:
+ type = member.type.unroll()
+ if type.isUnion():
+ for t in type.flatMemberTypes:
+ if (t.isDictionary() and
+ CGHeaders.getDeclarationFilename(t.inner) ==
+ CGHeaders.getDeclarationFilename(dictionary)):
+ raise TypeError(
+ "Dictionary contains a union that contains a "
+ "dictionary in the same WebIDL file. This won't "
+ "compile. Move the inner dictionary to a "
+ "different file.\n%s\n%s" %
+ (t.location, t.inner.location))
+ self.structs = self.getStructs()
+
+ def declare(self):
+ return self.structs.declare()
+
+ def define(self):
+ return self.structs.define()
+
+ def base(self):
+ if self.dictionary.parent:
+ return self.makeClassName(self.dictionary.parent)
+ return "DictionaryBase"
+
+ def initMethod(self):
+ """
+ This function outputs the body of the Init() method for the dictionary.
+
+ For the most part, this is some bookkeeping for our atoms so
+ we can avoid atomizing strings all the time, then we just spit
+ out the getMemberConversion() output for each member,
+ separated by newlines.
+
+ """
+ body = dedent("""
+ // Passing a null JSContext is OK only if we're initing from null,
+ // Since in that case we will not have to do any property gets
+ MOZ_ASSERT_IF(!cx, val.isNull());
+ """)
+
+ if self.needToInitIds:
+ body += fill(
+ """
+ ${dictName}Atoms* atomsCache = nullptr;
+ if (cx) {
+ atomsCache = GetAtomCache<${dictName}Atoms>(cx);
+ if (!*reinterpret_cast<jsid**>(atomsCache) && !InitIds(cx, atomsCache)) {
+ return false;
+ }
+ }
+
+ """,
+ dictName=self.makeClassName(self.dictionary))
+
+ if self.dictionary.parent:
+ body += fill(
+ """
+ // Per spec, we init the parent's members first
+ if (!${dictName}::Init(cx, val)) {
+ return false;
+ }
+
+ """,
+ dictName=self.makeClassName(self.dictionary.parent))
+ else:
+ body += dedent(
+ """
+ { // scope for isConvertible
+ bool isConvertible;
+ if (!IsConvertibleToDictionary(cx, val, &isConvertible)) {
+ return false;
+ }
+ if (!isConvertible) {
+ return ThrowErrorMessage(cx, MSG_NOT_DICTIONARY, sourceDescription);
+ }
+ }
+
+ """)
+
+ memberInits = [self.getMemberConversion(m).define()
+ for m in self.memberInfo]
+ if memberInits:
+ body += fill(
+ """
+ bool isNull = val.isNullOrUndefined();
+ // We only need these if !isNull, in which case we have |cx|.
+ Maybe<JS::Rooted<JSObject *> > object;
+ Maybe<JS::Rooted<JS::Value> > temp;
+ if (!isNull) {
+ MOZ_ASSERT(cx);
+ object.emplace(cx, &val.toObject());
+ temp.emplace(cx);
+ }
+ $*{memberInits}
+ """,
+ memberInits="\n".join(memberInits))
+
+ body += "return true;\n"
+
+ return ClassMethod("Init", "bool", [
+ Argument('JSContext*', 'cx'),
+ Argument('JS::Handle<JS::Value>', 'val'),
+ Argument('const char*', 'sourceDescription', default='"Value"'),
+ Argument('bool', 'passedToJSImpl', default='false')
+ ], body=body)
+
+ def initFromJSONMethod(self):
+ return ClassMethod(
+ "Init", "bool",
+ [Argument('const nsAString&', 'aJSON')],
+ body=dedent("""
+ AutoJSAPI jsapi;
+ JSObject* cleanGlobal = SimpleGlobalObject::Create(SimpleGlobalObject::GlobalType::BindingDetail);
+ if (!cleanGlobal) {
+ return false;
+ }
+ if (!jsapi.Init(cleanGlobal)) {
+ return false;
+ }
+ JSContext* cx = jsapi.cx();
+ JS::Rooted<JS::Value> json(cx);
+ bool ok = ParseJSON(cx, aJSON, &json);
+ NS_ENSURE_TRUE(ok, false);
+ return Init(cx, json);
+ """))
+
+ def toJSONMethod(self):
+ return ClassMethod(
+ "ToJSON", "bool",
+ [Argument('nsAString&', 'aJSON')],
+ body=dedent("""
+ AutoJSAPI jsapi;
+ jsapi.Init();
+ JSContext *cx = jsapi.cx();
+ // It's safe to use UnprivilegedJunkScopeOrWorkerGlobal here
+ // because we'll only be creating objects, in ways that have no
+ // side-effects, followed by a call to JS::ToJSONMaybeSafely,
+ // which likewise guarantees no side-effects for the sorts of
+ // things we will pass it.
+ JSAutoCompartment ac(cx, binding_detail::UnprivilegedJunkScopeOrWorkerGlobal());
+ JS::Rooted<JS::Value> val(cx);
+ if (!ToObjectInternal(cx, &val)) {
+ return false;
+ }
+ JS::Rooted<JSObject*> obj(cx, &val.toObject());
+ return StringifyToJSON(cx, obj, aJSON);
+ """), const=True)
+
+ def toObjectInternalMethod(self):
+ body = ""
+ if self.needToInitIds:
+ body += fill(
+ """
+ ${dictName}Atoms* atomsCache = GetAtomCache<${dictName}Atoms>(cx);
+ if (!*reinterpret_cast<jsid**>(atomsCache) && !InitIds(cx, atomsCache)) {
+ return false;
+ }
+
+ """,
+ dictName=self.makeClassName(self.dictionary))
+
+ if self.dictionary.parent:
+ body += fill(
+ """
+ // Per spec, we define the parent's members first
+ if (!${dictName}::ToObjectInternal(cx, rval)) {
+ return false;
+ }
+ JS::Rooted<JSObject*> obj(cx, &rval.toObject());
+
+ """,
+ dictName=self.makeClassName(self.dictionary.parent))
+ else:
+ body += dedent(
+ """
+ JS::Rooted<JSObject*> obj(cx, JS_NewPlainObject(cx));
+ if (!obj) {
+ return false;
+ }
+ rval.set(JS::ObjectValue(*obj));
+
+ """)
+
+ if self.memberInfo:
+ body += "\n".join(self.getMemberDefinition(m).define()
+ for m in self.memberInfo)
+ body += "\nreturn true;\n"
+
+ return ClassMethod("ToObjectInternal", "bool", [
+ Argument('JSContext*', 'cx'),
+ Argument('JS::MutableHandle<JS::Value>', 'rval'),
+ ], const=True, body=body)
+
+ def initIdsMethod(self):
+ assert self.needToInitIds
+ return initIdsClassMethod([m.identifier.name for m in self.dictionary.members],
+ "%sAtoms" % self.makeClassName(self.dictionary))
+
+ def traceDictionaryMethod(self):
+ body = ""
+ if self.dictionary.parent:
+ cls = self.makeClassName(self.dictionary.parent)
+ body += "%s::TraceDictionary(trc);\n" % cls
+
+ memberTraces = [self.getMemberTrace(m)
+ for m in self.dictionary.members
+ if typeNeedsRooting(m.type)]
+
+ if memberTraces:
+ body += "\n".join(memberTraces)
+
+ return ClassMethod("TraceDictionary", "void", [
+ Argument("JSTracer*", "trc"),
+ ], body=body)
+
+ def assignmentOperator(self):
+ body = CGList([])
+ if self.dictionary.parent:
+ body.append(CGGeneric(
+ "%s::operator=(aOther);\n" %
+ self.makeClassName(self.dictionary.parent)))
+ for m, _ in self.memberInfo:
+ memberName = self.makeMemberName(m.identifier.name)
+ if m.canHaveMissingValue():
+ memberAssign = CGGeneric(fill(
+ """
+ ${name}.Reset();
+ if (aOther.${name}.WasPassed()) {
+ ${name}.Construct(aOther.${name}.Value());
+ }
+ """,
+ name=memberName))
+ else:
+ memberAssign = CGGeneric(
+ "%s = aOther.%s;\n" % (memberName, memberName))
+ body.append(memberAssign)
+ return ClassMethod(
+ "operator=", "void",
+ [Argument("const %s&" % self.makeClassName(self.dictionary),
+ "aOther")],
+ body=body.define())
+
+ def getStructs(self):
+ d = self.dictionary
+ selfName = self.makeClassName(d)
+ members = [ClassMember(self.makeMemberName(m[0].identifier.name),
+ self.getMemberType(m),
+ visibility="public",
+ body=self.getMemberInitializer(m),
+ hasIgnoreInitCheckFlag=True)
+ for m in self.memberInfo]
+ if d.parent:
+ # We always want to init our parent with our non-initializing
+ # constructor arg, because either we're about to init ourselves (and
+ # hence our parent) or we don't want any init happening.
+ baseConstructors = [
+ "%s(%s)" % (self.makeClassName(d.parent),
+ self.getNonInitializingCtorArg())
+ ]
+ else:
+ baseConstructors = None
+ ctors = [
+ ClassConstructor(
+ [],
+ visibility="public",
+ baseConstructors=baseConstructors,
+ body=(
+ "// Safe to pass a null context if we pass a null value\n"
+ "Init(nullptr, JS::NullHandleValue);\n")),
+ ClassConstructor(
+ [Argument("const FastDictionaryInitializer&", "")],
+ visibility="public",
+ baseConstructors=baseConstructors,
+ explicit=True,
+ bodyInHeader=True,
+ body='// Do nothing here; this is used by our "Fast" subclass\n')
+ ]
+ methods = []
+
+ if self.needToInitIds:
+ methods.append(self.initIdsMethod())
+
+ methods.append(self.initMethod())
+ canBeRepresentedAsJSON = self.dictionarySafeToJSONify(self.dictionary)
+ if canBeRepresentedAsJSON:
+ methods.append(self.initFromJSONMethod())
+ try:
+ methods.append(self.toObjectInternalMethod())
+ if canBeRepresentedAsJSON:
+ methods.append(self.toJSONMethod())
+ except MethodNotNewObjectError:
+ # If we can't have a ToObjectInternal() because one of our members
+ # can only be returned from [NewObject] methods, then just skip
+ # generating ToObjectInternal() and ToJSON (since the latter depens
+ # on the former).
+ pass
+ methods.append(self.traceDictionaryMethod())
+
+ if CGDictionary.isDictionaryCopyConstructible(d):
+ disallowCopyConstruction = False
+ # Note: no base constructors because our operator= will
+ # deal with that.
+ ctors.append(ClassConstructor([Argument("const %s&" % selfName,
+ "aOther")],
+ bodyInHeader=True,
+ visibility="public",
+ explicit=True,
+ body="*this = aOther;\n"))
+ methods.append(self.assignmentOperator())
+ else:
+ disallowCopyConstruction = True
+
+ struct = CGClass(selfName,
+ bases=[ClassBase(self.base())],
+ members=members,
+ constructors=ctors,
+ methods=methods,
+ isStruct=True,
+ disallowCopyConstruction=disallowCopyConstruction)
+
+ fastDictionaryCtor = ClassConstructor(
+ [],
+ visibility="public",
+ bodyInHeader=True,
+ baseConstructors=["%s(%s)" %
+ (selfName,
+ self.getNonInitializingCtorArg())],
+ body="// Doesn't matter what int we pass to the parent constructor\n")
+
+ fastStruct = CGClass("Fast" + selfName,
+ bases=[ClassBase(selfName)],
+ constructors=[fastDictionaryCtor],
+ isStruct=True)
+
+ return CGList([struct,
+ CGNamespace('binding_detail', fastStruct)],
+ "\n")
+
+ def deps(self):
+ return self.dictionary.getDeps()
+
+ @staticmethod
+ def makeDictionaryName(dictionary):
+ return dictionary.identifier.name
+
+ def makeClassName(self, dictionary):
+ return self.makeDictionaryName(dictionary)
+
+ @staticmethod
+ def makeMemberName(name):
+ return "m" + name[0].upper() + IDLToCIdentifier(name[1:])
+
+ def getMemberType(self, memberInfo):
+ _, conversionInfo = memberInfo
+ # We can't handle having a holderType here
+ assert conversionInfo.holderType is None
+ declType = conversionInfo.declType
+ if conversionInfo.dealWithOptional:
+ declType = CGTemplatedType("Optional", declType)
+ return declType.define()
+
+ def getMemberConversion(self, memberInfo):
+ """
+ A function that outputs the initialization of a single dictionary
+ member from the given dictionary value.
+
+ We start with our conversionInfo, which tells us how to
+ convert a JS::Value to whatever type this member is. We
+ substiture the template from the conversionInfo with values
+ that point to our "temp" JS::Value and our member (which is
+ the C++ value we want to produce). The output is a string of
+ code to do the conversion. We store this string in
+ conversionReplacements["convert"].
+
+ Now we have three different ways we might use (or skip) this
+ string of code, depending on whether the value is required,
+ optional with default value, or optional without default
+ value. We set up a template in the 'conversion' variable for
+ exactly how to do this, then substitute into it from the
+ conversionReplacements dictionary.
+ """
+ member, conversionInfo = memberInfo
+ replacements = {
+ "val": "temp.ref()",
+ "maybeMutableVal": "temp.ptr()",
+ "declName": self.makeMemberName(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",
+ "passedToJSImpl": "passedToJSImpl"
+ }
+ # We can't handle having a holderType here
+ assert conversionInfo.holderType is None
+ if conversionInfo.dealWithOptional:
+ replacements["declName"] = "(" + replacements["declName"] + ".Value())"
+ if member.defaultValue:
+ replacements["haveValue"] = "!isNull && !temp->isUndefined()"
+
+ propId = self.makeIdName(member.identifier.name)
+ propGet = ("JS_GetPropertyById(cx, *object, atomsCache->%s, temp.ptr())" %
+ propId)
+
+ conversionReplacements = {
+ "prop": self.makeMemberName(member.identifier.name),
+ "convert": string.Template(conversionInfo.template).substitute(replacements),
+ "propGet": propGet
+ }
+ # The conversion code will only run where a default value or a value passed
+ # by the author needs to get converted, so we can remember if we have any
+ # members present here.
+ conversionReplacements["convert"] += "mIsAnyMemberPresent = true;\n"
+ setTempValue = CGGeneric(dedent(
+ """
+ if (!${propGet}) {
+ return false;
+ }
+ """))
+ conditions = getConditionList(member, "cx", "*object")
+ if len(conditions) != 0:
+ setTempValue = CGIfElseWrapper(conditions.define(),
+ setTempValue,
+ CGGeneric("temp->setUndefined();\n"))
+ setTempValue = CGIfWrapper(setTempValue, "!isNull")
+ conversion = setTempValue.define()
+ if member.defaultValue:
+ if (member.type.isUnion() and
+ (not member.type.nullable() or
+ not isinstance(member.defaultValue, IDLNullValue))):
+ # Since this has a default value, it might have been initialized
+ # already. Go ahead and uninit it before we try to init it
+ # again.
+ memberName = self.makeMemberName(member.identifier.name)
+ if member.type.nullable():
+ conversion += fill(
+ """
+ if (!${memberName}.IsNull()) {
+ ${memberName}.Value().Uninit();
+ }
+ """,
+ memberName=memberName)
+ else:
+ conversion += "%s.Uninit();\n" % memberName
+ conversion += "${convert}"
+ elif not conversionInfo.dealWithOptional:
+ # We're required, but have no default value. Make sure
+ # that we throw if we have no value provided.
+ conversion += dedent(
+ """
+ if (!isNull && !temp->isUndefined()) {
+ ${convert}
+ } else if (cx) {
+ // Don't error out if we have no cx. In that
+ // situation the caller is default-constructing us and we'll
+ // just assume they know what they're doing.
+ return ThrowErrorMessage(cx, MSG_MISSING_REQUIRED_DICTIONARY_MEMBER,
+ "%s");
+ }
+ """ % self.getMemberSourceDescription(member))
+ conversionReplacements["convert"] = indent(conversionReplacements["convert"]).rstrip()
+ else:
+ conversion += (
+ "if (!isNull && !temp->isUndefined()) {\n"
+ " ${prop}.Construct();\n"
+ "${convert}"
+ "}\n")
+ conversionReplacements["convert"] = indent(conversionReplacements["convert"])
+
+ return CGGeneric(
+ string.Template(conversion).substitute(conversionReplacements))
+
+ def getMemberDefinition(self, memberInfo):
+ member = memberInfo[0]
+ declType = memberInfo[1].declType
+ memberLoc = self.makeMemberName(member.identifier.name)
+ if not member.canHaveMissingValue():
+ memberData = memberLoc
+ else:
+ # The data is inside the Optional<>
+ memberData = "%s.InternalValue()" % memberLoc
+
+ # If you have to change this list (which you shouldn't!), make sure it
+ # continues to match the list in test_Object.prototype_props.html
+ if (member.identifier.name in
+ ["constructor", "toSource", "toString", "toLocaleString", "valueOf",
+ "watch", "unwatch", "hasOwnProperty", "isPrototypeOf",
+ "propertyIsEnumerable", "__defineGetter__", "__defineSetter__",
+ "__lookupGetter__", "__lookupSetter__", "__proto__"]):
+ raise TypeError("'%s' member of %s dictionary shadows "
+ "a property of Object.prototype, and Xrays to "
+ "Object can't handle that.\n"
+ "%s" %
+ (member.identifier.name,
+ self.dictionary.identifier.name,
+ member.location))
+
+ propDef = (
+ 'JS_DefinePropertyById(cx, obj, atomsCache->%s, temp, JSPROP_ENUMERATE)' %
+ self.makeIdName(member.identifier.name))
+
+ innerTemplate = wrapForType(
+ member.type, self.descriptorProvider,
+ {
+ 'result': "currentValue",
+ 'successCode': ("if (!%s) {\n"
+ " return false;\n"
+ "}\n"
+ "break;\n" % propDef),
+ 'jsvalRef': "temp",
+ 'jsvalHandle': "&temp",
+ 'returnsNewObject': False,
+ # 'obj' can just be allowed to be the string "obj", since that
+ # will be our dictionary object, which is presumably itself in
+ # the right scope.
+ 'typedArraysAreStructs': True
+ })
+ conversion = CGGeneric(innerTemplate)
+ conversion = CGWrapper(conversion,
+ pre=("JS::Rooted<JS::Value> temp(cx);\n"
+ "%s const & currentValue = %s;\n" %
+ (declType.define(), memberData)
+ ))
+
+ # Now make sure that our successCode can actually break out of the
+ # conversion. This incidentally gives us a scope for 'temp' and
+ # 'currentValue'.
+ conversion = CGWrapper(
+ CGIndenter(conversion),
+ pre=("do {\n"
+ " // block for our 'break' successCode and scope for 'temp' and 'currentValue'\n"),
+ post="} while(0);\n")
+ if member.canHaveMissingValue():
+ # Only do the conversion if we have a value
+ conversion = CGIfWrapper(conversion, "%s.WasPassed()" % memberLoc)
+ conditions = getConditionList(member, "cx", "obj")
+ if len(conditions) != 0:
+ conversion = CGIfWrapper(conversion, conditions.define())
+ return conversion
+
+ def getMemberTrace(self, member):
+ type = member.type
+ assert typeNeedsRooting(type)
+ memberLoc = self.makeMemberName(member.identifier.name)
+ if not member.canHaveMissingValue():
+ memberData = memberLoc
+ else:
+ # The data is inside the Optional<>
+ memberData = "%s.Value()" % memberLoc
+
+ memberName = "%s.%s" % (self.makeClassName(self.dictionary),
+ memberLoc)
+
+ if type.isObject():
+ trace = CGGeneric('JS::UnsafeTraceRoot(trc, %s, "%s");\n' %
+ ("&"+memberData, memberName))
+ if type.nullable():
+ trace = CGIfWrapper(trace, memberData)
+ elif type.isAny():
+ trace = CGGeneric('JS::UnsafeTraceRoot(trc, %s, "%s");\n' %
+ ("&"+memberData, memberName))
+ elif (type.isSequence() or type.isDictionary() or
+ type.isSpiderMonkeyInterface() or type.isUnion()):
+ if type.nullable():
+ memberNullable = memberData
+ memberData = "%s.Value()" % memberData
+ if type.isSequence():
+ trace = CGGeneric('DoTraceSequence(trc, %s);\n' % memberData)
+ elif type.isDictionary():
+ trace = CGGeneric('%s.TraceDictionary(trc);\n' % memberData)
+ elif type.isUnion():
+ trace = CGGeneric('%s.TraceUnion(trc);\n' % memberData)
+ else:
+ assert type.isSpiderMonkeyInterface()
+ trace = CGGeneric('%s.TraceSelf(trc);\n' % memberData)
+ if type.nullable():
+ trace = CGIfWrapper(trace, "!%s.IsNull()" % memberNullable)
+ elif type.isMozMap():
+ # If you implement this, add a MozMap<object> to
+ # TestInterfaceJSDictionary and test it in test_bug1036214.html
+ # to make sure we end up with the correct security properties.
+ assert False
+ else:
+ assert False # unknown type
+
+ if member.canHaveMissingValue():
+ trace = CGIfWrapper(trace, "%s.WasPassed()" % memberLoc)
+
+ return trace.define()
+
+ def getMemberInitializer(self, memberInfo):
+ """
+ Get the right initializer for the member. Most members don't need one,
+ but we need to pre-initialize 'any' and 'object' that have a default
+ value, so they're safe to trace at all times.
+ """
+ member, _ = memberInfo
+ if member.canHaveMissingValue():
+ # Allowed missing value means no need to set it up front, since it's
+ # inside an Optional and won't get traced until it's actually set
+ # up.
+ return None
+ type = member.type
+ if type.isAny():
+ return "JS::UndefinedValue()"
+ if type.isObject():
+ return "nullptr"
+ if type.isDictionary():
+ # When we construct ourselves, we don't want to init our member
+ # dictionaries. Either we're being constructed-but-not-initialized
+ # ourselves (and then we don't want to init them) or we're about to
+ # init ourselves and then we'll init them anyway.
+ return CGDictionary.getNonInitializingCtorArg()
+ return None
+
+ def getMemberSourceDescription(self, member):
+ return ("'%s' member of %s" %
+ (member.identifier.name, self.dictionary.identifier.name))
+
+ @staticmethod
+ def makeIdName(name):
+ return IDLToCIdentifier(name) + "_id"
+
+ @staticmethod
+ def getNonInitializingCtorArg():
+ return "FastDictionaryInitializer()"
+
+ @staticmethod
+ def isDictionaryCopyConstructible(dictionary):
+ if (dictionary.parent and
+ not CGDictionary.isDictionaryCopyConstructible(dictionary.parent)):
+ return False
+ return all(isTypeCopyConstructible(m.type) for m in dictionary.members)
+
+ @staticmethod
+ def typeSafeToJSONify(type):
+ """
+ Determine whether the given type is safe to convert to JSON. The
+ restriction is that this needs to be safe while in a global controlled
+ by an adversary, and "safe" means no side-effects when the JS
+ representation of this type is converted to JSON. That means that we
+ have to be pretty restrictive about what things we can allow. For
+ example, "object" is out, because it may have accessor properties on it.
+ """
+ if type.nullable():
+ # Converting null to JSON is always OK.
+ return CGDictionary.typeSafeToJSONify(type.inner)
+
+ if type.isSequence():
+ # Sequences are arrays we create ourselves, with no holes. They
+ # should be safe if their contents are safe, as long as we suppress
+ # invocation of .toJSON on objects.
+ return CGDictionary.typeSafeToJSONify(type.inner)
+
+ if type.isUnion():
+ # OK if everything in it is ok.
+ return all(CGDictionary.typeSafeToJSONify(t)
+ for t in type.flatMemberTypes)
+
+ if type.isDictionary():
+ # OK if the dictionary is OK
+ return CGDictionary.dictionarySafeToJSONify(type.inner)
+
+ if type.isString() or type.isEnum():
+ # Strings are always OK.
+ return True
+
+ if type.isPrimitive():
+ # Primitives (numbers and booleans) are ok, as long as
+ # they're not unrestricted float/double.
+ return not type.isFloat() or not type.isUnrestricted()
+
+ return False
+
+ @staticmethod
+ def dictionarySafeToJSONify(dictionary):
+ # The dictionary itself is OK, so we're good if all our types are.
+ return all(CGDictionary.typeSafeToJSONify(m.type)
+ for m in dictionary.members)
+
+
+class CGRegisterWorkerBindings(CGAbstractMethod):
+ def __init__(self, config):
+ CGAbstractMethod.__init__(self, None, 'RegisterWorkerBindings', 'bool',
+ [Argument('JSContext*', 'aCx'),
+ Argument('JS::Handle<JSObject*>', 'aObj')])
+ self.config = config
+
+ def definition_body(self):
+ descriptors = self.config.getDescriptors(hasInterfaceObject=True,
+ isExposedInAnyWorker=True,
+ register=True)
+ conditions = []
+ for desc in descriptors:
+ bindingNS = toBindingNamespace(desc.name)
+ condition = "!%s::GetConstructorObject(aCx)" % bindingNS
+ if desc.isExposedConditionally():
+ condition = (
+ "%s::ConstructorEnabled(aCx, aObj) && " % bindingNS
+ + condition)
+ conditions.append(condition)
+ lines = [CGIfWrapper(CGGeneric("return false;\n"), condition) for
+ condition in conditions]
+ lines.append(CGGeneric("return true;\n"))
+ return CGList(lines, "\n").define()
+
+class CGRegisterWorkerDebuggerBindings(CGAbstractMethod):
+ def __init__(self, config):
+ CGAbstractMethod.__init__(self, None, 'RegisterWorkerDebuggerBindings', 'bool',
+ [Argument('JSContext*', 'aCx'),
+ Argument('JS::Handle<JSObject*>', 'aObj')])
+ self.config = config
+
+ def definition_body(self):
+ descriptors = self.config.getDescriptors(hasInterfaceObject=True,
+ isExposedInWorkerDebugger=True,
+ register=True)
+ conditions = []
+ for desc in descriptors:
+ bindingNS = toBindingNamespace(desc.name)
+ condition = "!%s::GetConstructorObject(aCx)" % bindingNS
+ if desc.isExposedConditionally():
+ condition = (
+ "%s::ConstructorEnabled(aCx, aObj) && " % bindingNS
+ + condition)
+ conditions.append(condition)
+ lines = [CGIfWrapper(CGGeneric("return false;\n"), condition) for
+ condition in conditions]
+ lines.append(CGGeneric("return true;\n"))
+ return CGList(lines, "\n").define()
+
+class CGRegisterWorkletBindings(CGAbstractMethod):
+ def __init__(self, config):
+ CGAbstractMethod.__init__(self, None, 'RegisterWorkletBindings', 'bool',
+ [Argument('JSContext*', 'aCx'),
+ Argument('JS::Handle<JSObject*>', 'aObj')])
+ self.config = config
+
+ def definition_body(self):
+ descriptors = self.config.getDescriptors(hasInterfaceObject=True,
+ isExposedInAnyWorklet=True,
+ register=True)
+ conditions = []
+ for desc in descriptors:
+ bindingNS = toBindingNamespace(desc.name)
+ condition = "!%s::GetConstructorObject(aCx)" % bindingNS
+ if desc.isExposedConditionally():
+ condition = (
+ "%s::ConstructorEnabled(aCx, aObj) && " % bindingNS
+ + condition)
+ conditions.append(condition)
+ lines = [CGIfWrapper(CGGeneric("return false;\n"), condition) for
+ condition in conditions]
+ lines.append(CGGeneric("return true;\n"))
+ return CGList(lines, "\n").define()
+
+class CGResolveSystemBinding(CGAbstractMethod):
+ def __init__(self, config):
+ CGAbstractMethod.__init__(self, None, 'ResolveSystemBinding', 'bool',
+ [Argument('JSContext*', 'aCx'),
+ Argument('JS::Handle<JSObject*>', 'aObj'),
+ Argument('JS::Handle<jsid>', 'aId'),
+ Argument('bool*', 'aResolvedp')])
+ self.config = config
+
+ def definition_body(self):
+ descriptors = self.config.getDescriptors(hasInterfaceObject=True,
+ isExposedInSystemGlobals=True,
+ register=True)
+
+ def descNameToId(name):
+ return "s%s_id" % name
+ jsidNames = [descNameToId(desc.name) for desc in descriptors]
+ jsidDecls = CGList(CGGeneric("static jsid %s;\n" % name)
+ for name in jsidNames)
+
+ jsidInits = CGList(
+ (CGIfWrapper(
+ CGGeneric("return false;\n"),
+ '!AtomizeAndPinJSString(aCx, %s, "%s")' %
+ (descNameToId(desc.name), desc.interface.identifier.name))
+ for desc in descriptors),
+ "\n")
+ jsidInits.append(CGGeneric("idsInited = true;\n"))
+ jsidInits = CGIfWrapper(jsidInits, "!idsInited")
+ jsidInits = CGList([CGGeneric("static bool idsInited = false;\n"),
+ jsidInits])
+
+ definitions = CGList([], "\n")
+ for desc in descriptors:
+ bindingNS = toBindingNamespace(desc.name)
+ defineCode = "!%s::GetConstructorObject(aCx)" % bindingNS
+ defineCode = CGIfWrapper(CGGeneric("return false;\n"), defineCode)
+ defineCode = CGList([defineCode,
+ CGGeneric("*aResolvedp = true;\n")])
+
+ condition = "JSID_IS_VOID(aId) || aId == %s" % descNameToId(desc.name)
+ if desc.isExposedConditionally():
+ condition = "(%s) && %s::ConstructorEnabled(aCx, aObj)" % (condition, bindingNS)
+
+ definitions.append(CGIfWrapper(defineCode, condition))
+
+ return CGList([CGGeneric("MOZ_ASSERT(NS_IsMainThread());\n"),
+ jsidDecls,
+ jsidInits,
+ definitions,
+ CGGeneric("return true;\n")],
+ "\n").define()
+
+
+def getGlobalNames(config):
+ names = []
+ for desc in config.getDescriptors(registersGlobalNamesOnWindow=True):
+ names.append((desc.name, desc))
+ names.extend((n.identifier.name, desc) for n in desc.interface.namedConstructors)
+ return names
+
+class CGGlobalNamesString(CGGeneric):
+ def __init__(self, config):
+ globalNames = getGlobalNames(config)
+ currentOffset = 0
+ strings = []
+ for (name, _) in globalNames:
+ strings.append('/* %i */ "%s\\0"' % (currentOffset, name))
+ currentOffset += len(name) + 1 # Add trailing null.
+ define = fill("""
+ const uint32_t WebIDLGlobalNameHash::sCount = ${count};
+
+ const char WebIDLGlobalNameHash::sNames[] =
+ $*{strings}
+
+ """,
+ count=len(globalNames),
+ strings="\n".join(strings) + ";\n")
+
+ CGGeneric.__init__(self, define=define)
+
+
+class CGRegisterGlobalNames(CGAbstractMethod):
+ def __init__(self, config):
+ CGAbstractMethod.__init__(self, None, 'RegisterWebIDLGlobalNames',
+ 'void', [])
+ self.config = config
+
+ def definition_body(self):
+ def getCheck(desc):
+ if not desc.isExposedConditionally():
+ return "nullptr"
+ return "%sBinding::ConstructorEnabled" % desc.name
+
+ define = ""
+ currentOffset = 0
+ for (name, desc) in getGlobalNames(self.config):
+ length = len(name)
+ define += "WebIDLGlobalNameHash::Register(%i, %i, %sBinding::DefineDOMInterface, %s);\n" % (currentOffset, length, desc.name, getCheck(desc))
+ currentOffset += length + 1 # Add trailing null.
+ return define
+
+
+def dependencySortObjects(objects, dependencyGetter, nameGetter):
+ """
+ Sort IDL objects with dependencies on each other such that if A
+ depends on B then B will come before A. This is needed for
+ declaring C++ classes in the right order, for example. Objects
+ that have no dependencies are just sorted by name.
+
+ objects should be something that can produce a set of objects
+ (e.g. a set, iterator, list, etc).
+
+ dependencyGetter is something that, given an object, should return
+ the set of objects it depends on.
+ """
+ # XXXbz this will fail if we have two webidl files F1 and F2 such that F1
+ # declares an object which depends on an object in F2, and F2 declares an
+ # object (possibly a different one!) that depends on an object in F1. The
+ # good news is that I expect this to never happen.
+ sortedObjects = []
+ objects = set(objects)
+ while len(objects) != 0:
+ # Find the dictionaries that don't depend on anything else
+ # anymore and move them over.
+ toMove = [o for o in objects if
+ len(dependencyGetter(o) & objects) == 0]
+ if len(toMove) == 0:
+ raise TypeError("Loop in dependency graph\n" +
+ "\n".join(o.location for o in objects))
+ objects = objects - set(toMove)
+ sortedObjects.extend(sorted(toMove, key=nameGetter))
+ return sortedObjects
+
+
+class ForwardDeclarationBuilder:
+ """
+ Create a canonical representation of a set of namespaced forward
+ declarations.
+ """
+ def __init__(self):
+ """
+ The set of declarations is represented as a tree of nested namespaces.
+ Each tree node has a set of declarations |decls| and a dict |children|.
+ Each declaration is a pair consisting of the class name and a boolean
+ that is true iff the class is really a struct. |children| maps the
+ names of inner namespaces to the declarations in that namespace.
+ """
+ self.decls = set()
+ self.children = {}
+
+ def _ensureNonTemplateType(self, type):
+ if "<" in type:
+ # This is a templated type. We don't really know how to
+ # forward-declare those, and trying to do it naively is not going to
+ # go well (e.g. we may have :: characters inside the type we're
+ # templated on!). Just bail out.
+ raise TypeError("Attempt to use ForwardDeclarationBuilder on "
+ "templated type %s. We don't know how to do that "
+ "yet." % type)
+
+ def _listAdd(self, namespaces, name, isStruct=False):
+ """
+ Add a forward declaration, where |namespaces| is a list of namespaces.
+ |name| should not contain any other namespaces.
+ """
+ if namespaces:
+ child = self.children.setdefault(namespaces[0], ForwardDeclarationBuilder())
+ child._listAdd(namespaces[1:], name, isStruct)
+ else:
+ assert '::' not in name
+ self.decls.add((name, isStruct))
+
+ def addInMozillaDom(self, name, isStruct=False):
+ """
+ Add a forward declaration to the mozilla::dom:: namespace. |name| should not
+ contain any other namespaces.
+ """
+ self._ensureNonTemplateType(name);
+ self._listAdd(["mozilla", "dom"], name, isStruct)
+
+ def add(self, nativeType, isStruct=False):
+ """
+ Add a forward declaration, where |nativeType| is a string containing
+ the type and its namespaces, in the usual C++ way.
+ """
+ self._ensureNonTemplateType(nativeType);
+ components = nativeType.split('::')
+ self._listAdd(components[:-1], components[-1], isStruct)
+
+ def _build(self, atTopLevel):
+ """
+ Return a codegenerator for the forward declarations.
+ """
+ decls = []
+ if self.decls:
+ decls.append(CGList([CGClassForwardDeclare(cname, isStruct)
+ for cname, isStruct in sorted(self.decls)]))
+ for namespace, child in sorted(self.children.iteritems()):
+ decls.append(CGNamespace(namespace, child._build(atTopLevel=False), declareOnly=True))
+
+ cg = CGList(decls, "\n")
+ if not atTopLevel and len(decls) + len(self.decls) > 1:
+ cg = CGWrapper(cg, pre='\n', post='\n')
+ return cg
+
+ def build(self):
+ return self._build(atTopLevel=True)
+
+ def forwardDeclareForType(self, t, config):
+ t = t.unroll()
+ if t.isGeckoInterface():
+ name = t.inner.identifier.name
+ try:
+ desc = config.getDescriptor(name)
+ self.add(desc.nativeType)
+ except NoSuchDescriptorError:
+ pass
+
+ # Note: Spidermonkey interfaces are typedefs, so can't be
+ # forward-declared
+ elif t.isCallback():
+ self.addInMozillaDom(t.callback.identifier.name)
+ elif t.isDictionary():
+ self.addInMozillaDom(t.inner.identifier.name, isStruct=True)
+ elif t.isCallbackInterface():
+ self.addInMozillaDom(t.inner.identifier.name)
+ elif t.isUnion():
+ # Forward declare both the owning and non-owning version,
+ # since we don't know which one we might want
+ self.addInMozillaDom(CGUnionStruct.unionTypeName(t, False))
+ self.addInMozillaDom(CGUnionStruct.unionTypeName(t, True))
+ elif t.isMozMap():
+ self.forwardDeclareForType(t.inner, config)
+ # Don't need to do anything for void, primitive, string, any or object.
+ # There may be some other cases we are missing.
+
+
+class CGForwardDeclarations(CGWrapper):
+ """
+ Code generate the forward declarations for a header file.
+ additionalDeclarations is a list of tuples containing a classname and a
+ boolean. If the boolean is true we will declare a struct, otherwise we'll
+ declare a class.
+ """
+ def __init__(self, config, descriptors, callbacks,
+ dictionaries, callbackInterfaces, additionalDeclarations=[]):
+ builder = ForwardDeclarationBuilder()
+
+ # Needed for at least Wrap.
+ for d in descriptors:
+ # If this is a generated iterator interface, we only create these
+ # in the generated bindings, and don't need to forward declare.
+ if d.interface.isIteratorInterface():
+ continue
+ builder.add(d.nativeType)
+ # If we're an interface and we have a maplike/setlike declaration,
+ # we'll have helper functions exposed to the native side of our
+ # bindings, which will need to show up in the header. If either of
+ # our key/value types are interfaces, they'll be passed as
+ # arguments to helper functions, and they'll need to be forward
+ # declared in the header.
+ if d.interface.maplikeOrSetlikeOrIterable:
+ if d.interface.maplikeOrSetlikeOrIterable.hasKeyType():
+ builder.forwardDeclareForType(d.interface.maplikeOrSetlikeOrIterable.keyType,
+ config)
+ if d.interface.maplikeOrSetlikeOrIterable.hasValueType():
+ builder.forwardDeclareForType(d.interface.maplikeOrSetlikeOrIterable.valueType,
+ config)
+
+ # We just about always need NativePropertyHooks
+ builder.addInMozillaDom("NativePropertyHooks", isStruct=True)
+ builder.addInMozillaDom("ProtoAndIfaceCache")
+ # Add the atoms cache type, even if we don't need it.
+ for d in descriptors:
+ # Iterators have native types that are template classes, so
+ # creating an 'Atoms' cache type doesn't work for them, and is one
+ # of the cases where we don't need it anyways.
+ if d.interface.isIteratorInterface():
+ continue
+ builder.add(d.nativeType + "Atoms", isStruct=True)
+
+ for callback in callbacks:
+ builder.addInMozillaDom(callback.identifier.name)
+ for t in getTypesFromCallback(callback):
+ builder.forwardDeclareForType(t, config)
+
+ for d in callbackInterfaces:
+ builder.add(d.nativeType)
+ builder.add(d.nativeType + "Atoms", isStruct=True)
+ for t in getTypesFromDescriptor(d):
+ builder.forwardDeclareForType(t, config)
+
+ for d in dictionaries:
+ if len(d.members) > 0:
+ builder.addInMozillaDom(d.identifier.name + "Atoms", isStruct=True)
+ for t in getTypesFromDictionary(d):
+ builder.forwardDeclareForType(t, config)
+
+ for className, isStruct in additionalDeclarations:
+ builder.add(className, isStruct=isStruct)
+
+ CGWrapper.__init__(self, builder.build())
+
+
+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):
+ bindingHeaders = dict.fromkeys((
+ 'mozilla/dom/NonRefcountedDOMObject.h',
+ ),
+ True)
+ bindingDeclareHeaders = dict.fromkeys((
+ 'mozilla/dom/BindingDeclarations.h',
+ 'mozilla/dom/Nullable.h',
+ 'mozilla/ErrorResult.h',
+ ),
+ True)
+
+ descriptors = config.getDescriptors(webIDLFile=webIDLFile,
+ hasInterfaceOrInterfacePrototypeObject=True)
+
+ unionTypes = UnionsForFile(config, webIDLFile)
+
+ (unionHeaders, unionImplheaders, unionDeclarations, traverseMethods,
+ unlinkMethods, unionStructs) = UnionTypes(unionTypes, config)
+
+ bindingDeclareHeaders.update(dict.fromkeys(unionHeaders, True))
+ bindingHeaders.update(dict.fromkeys(unionImplheaders, True))
+ bindingDeclareHeaders["mozilla/dom/UnionMember.h"] = len(unionStructs) > 0
+ bindingDeclareHeaders["mozilla/dom/FakeString.h"] = len(unionStructs) > 0
+ # BindingUtils.h is only needed for SetToObject.
+ # If it stops being inlined or stops calling CallerSubsumes
+ # both this bit and the bit in UnionTypes can be removed.
+ bindingDeclareHeaders["mozilla/dom/BindingUtils.h"] = any(d.isObject() for t in unionTypes
+ for d in t.flatMemberTypes)
+ bindingDeclareHeaders["mozilla/dom/IterableIterator.h"] = any(d.interface.isIteratorInterface() or
+ d.interface.isIterable() for d in descriptors)
+
+ def descriptorHasCrossOriginProperties(desc):
+ def hasCrossOriginProperty(m):
+ props = memberProperties(m, desc)
+ return (props.isCrossOriginMethod or
+ props.isCrossOriginGetter or
+ props.isCrossOriginSetter)
+
+ return any(hasCrossOriginProperty(m) for m in desc.interface.members)
+
+ bindingDeclareHeaders["jsapi.h"] = any(descriptorHasCrossOriginProperties(d) for d in descriptors)
+ bindingDeclareHeaders["jspubtd.h"] = not bindingDeclareHeaders["jsapi.h"]
+ bindingDeclareHeaders["js/RootingAPI.h"] = not bindingDeclareHeaders["jsapi.h"]
+
+ def descriptorRequiresPreferences(desc):
+ iface = desc.interface
+ return any(m.getExtendedAttribute("Pref") for m in iface.members + [iface])
+
+ def descriptorDeprecated(desc):
+ iface = desc.interface
+ return any(m.getExtendedAttribute("Deprecated") for m in iface.members + [iface])
+
+ bindingHeaders["nsIDocument.h"] = any(
+ descriptorDeprecated(d) for d in descriptors)
+ bindingHeaders["mozilla/Preferences.h"] = any(
+ descriptorRequiresPreferences(d) for d in descriptors)
+ bindingHeaders["mozilla/dom/DOMJSProxyHandler.h"] = any(
+ d.concrete and d.proxy for d in descriptors)
+
+ def descriptorHasChromeOnly(desc):
+ ctor = desc.interface.ctor()
+
+ return (any(isChromeOnly(a) or needsContainsHack(a) or
+ needsCallerType(a)
+ for a in desc.interface.members) or
+ desc.interface.getExtendedAttribute("ChromeOnly") is not None or
+ # JS-implemented interfaces with an interface object get a
+ # chromeonly _create method. And interfaces with an
+ # interface object might have a ChromeOnly constructor.
+ (desc.interface.hasInterfaceObject() and
+ (desc.interface.isJSImplemented() or
+ (ctor and isChromeOnly(ctor)))) or
+ # JS-implemented interfaces with clearable cached
+ # attrs have chromeonly _clearFoo methods.
+ (desc.interface.isJSImplemented() and
+ any(clearableCachedAttrs(desc))))
+
+ # XXXkhuey ugly hack but this is going away soon.
+ bindingHeaders['xpcprivate.h'] = webIDLFile.endswith("EventTarget.webidl")
+
+ hasThreadChecks = any(d.hasThreadChecks() for d in descriptors)
+ bindingHeaders["nsThreadUtils.h"] = hasThreadChecks
+
+ dictionaries = config.getDictionaries(webIDLFile)
+
+ def dictionaryHasChromeOnly(dictionary):
+ while dictionary:
+ if (any(isChromeOnly(m) for m in dictionary.members)):
+ return True
+ dictionary = dictionary.parent
+ return False
+
+ bindingHeaders["nsContentUtils.h"] = (
+ any(descriptorHasChromeOnly(d) for d in descriptors) or
+ any(dictionaryHasChromeOnly(d) for d in dictionaries))
+ hasNonEmptyDictionaries = any(
+ len(dict.members) > 0 for dict in dictionaries)
+ callbacks = config.getCallbacks(webIDLFile)
+ callbackDescriptors = config.getDescriptors(webIDLFile=webIDLFile,
+ isCallback=True)
+ jsImplemented = config.getDescriptors(webIDLFile=webIDLFile,
+ isJSImplemented=True)
+ bindingDeclareHeaders["nsWeakReference.h"] = jsImplemented
+ bindingHeaders["nsIGlobalObject.h"] = jsImplemented
+ bindingHeaders["AtomList.h"] = hasNonEmptyDictionaries or jsImplemented or callbackDescriptors
+
+ def descriptorClearsPropsInSlots(descriptor):
+ if not descriptor.wrapperCache:
+ return False
+ return any(m.isAttr() and m.getExtendedAttribute("StoreInSlot")
+ for m in descriptor.interface.members)
+ bindingHeaders["nsJSUtils.h"] = any(descriptorClearsPropsInSlots(d) for d in descriptors)
+
+ # Do codegen for all the enums
+ enums = config.getEnums(webIDLFile)
+ cgthings = [CGEnum(e) for e in enums]
+
+ hasCode = (descriptors or callbackDescriptors or dictionaries or
+ callbacks)
+ bindingHeaders["mozilla/dom/BindingUtils.h"] = hasCode
+ bindingHeaders["mozilla/OwningNonNull.h"] = hasCode
+ bindingHeaders["mozilla/dom/BindingDeclarations.h"] = (
+ not hasCode and enums)
+
+ bindingHeaders["WrapperFactory.h"] = descriptors
+ bindingHeaders["mozilla/dom/DOMJSClass.h"] = descriptors
+ bindingHeaders["mozilla/dom/ScriptSettings.h"] = dictionaries # AutoJSAPI
+ # Ensure we see our enums in the generated .cpp file, for the ToJSValue
+ # method body. Also ensure that we see jsapi.h.
+ if enums:
+ bindingHeaders[CGHeaders.getDeclarationFilename(enums[0])] = True
+ bindingHeaders["jsapi.h"] = True
+
+ # For things that have [UseCounter]
+ def descriptorRequiresTelemetry(desc):
+ iface = desc.interface
+ return any(m.getExtendedAttribute("UseCounter") for m in iface.members)
+ bindingHeaders["mozilla/UseCounter.h"] = any(
+ descriptorRequiresTelemetry(d) for d in descriptors)
+ bindingHeaders["mozilla/dom/SimpleGlobalObject.h"] = any(
+ CGDictionary.dictionarySafeToJSONify(d) for d in dictionaries)
+ bindingHeaders["XrayWrapper.h"] = any(
+ d.wantsXrays and d.wantsXrayExpandoClass for d in descriptors)
+ bindingHeaders["mozilla/dom/XrayExpandoClass.h"] = any(
+ d.wantsXrays for d in descriptors)
+
+ cgthings.extend(traverseMethods)
+ cgthings.extend(unlinkMethods)
+
+ # 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.
+
+ def getDependenciesFromType(type):
+ if type.isDictionary():
+ return set([type.unroll().inner])
+ if type.isSequence():
+ return getDependenciesFromType(type.unroll())
+ if type.isUnion():
+ return set([type.unroll()])
+ return set()
+
+ def getDependencies(unionTypeOrDictionary):
+ if isinstance(unionTypeOrDictionary, IDLDictionary):
+ deps = set()
+ if unionTypeOrDictionary.parent:
+ deps.add(unionTypeOrDictionary.parent)
+ for member in unionTypeOrDictionary.members:
+ deps |= getDependenciesFromType(member.type)
+ return deps
+
+ assert unionTypeOrDictionary.isType() and unionTypeOrDictionary.isUnion()
+ deps = set()
+ for member in unionTypeOrDictionary.flatMemberTypes:
+ deps |= getDependenciesFromType(member)
+ return deps
+
+ def getName(unionTypeOrDictionary):
+ if isinstance(unionTypeOrDictionary, IDLDictionary):
+ return unionTypeOrDictionary.identifier.name
+
+ assert unionTypeOrDictionary.isType() and unionTypeOrDictionary.isUnion()
+ return unionTypeOrDictionary.name
+
+ for t in dependencySortObjects(dictionaries + unionStructs, getDependencies, getName):
+ if t.isDictionary():
+ cgthings.append(CGDictionary(t, config))
+ else:
+ assert t.isUnion()
+ cgthings.append(CGUnionStruct(t, config))
+ cgthings.append(CGUnionStruct(t, config, True))
+
+ # Do codegen for all the callbacks.
+ cgthings.extend(CGCallbackFunction(c, config) for c in callbacks)
+
+ cgthings.extend([CGNamespace('binding_detail', CGFastCallback(c))
+ for c in callbacks])
+
+ # Do codegen for all the descriptors
+ cgthings.extend([CGDescriptor(x) for x in descriptors])
+
+ # Do codegen for all the callback interfaces.
+ cgthings.extend([CGCallbackInterface(x) for x in callbackDescriptors])
+
+ cgthings.extend([CGNamespace('binding_detail',
+ CGFastCallback(x.interface))
+ for x in callbackDescriptors])
+
+ # Do codegen for JS implemented classes
+ def getParentDescriptor(desc):
+ if not desc.interface.parent:
+ return set()
+ return {desc.getDescriptor(desc.interface.parent.identifier.name)}
+ for x in dependencySortObjects(jsImplemented, getParentDescriptor,
+ lambda d: d.interface.identifier.name):
+ cgthings.append(CGCallbackInterface(x, typedArraysAreStructs=True))
+ cgthings.append(CGJSImplClass(x))
+
+ # 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([CGForwardDeclarations(config, descriptors,
+ callbacks,
+ dictionaries,
+ callbackDescriptors + jsImplemented,
+ additionalDeclarations=unionDeclarations),
+ curr],
+ "\n")
+
+ # Add header includes.
+ bindingHeaders = [header
+ for header, include in bindingHeaders.iteritems()
+ if include]
+ bindingDeclareHeaders = [header
+ for header, include in bindingDeclareHeaders.iteritems()
+ if include]
+
+ curr = CGHeaders(descriptors,
+ dictionaries,
+ callbacks,
+ callbackDescriptors,
+ bindingDeclareHeaders,
+ bindingHeaders,
+ prefix,
+ curr,
+ config,
+ jsImplemented)
+
+ # Add include guards.
+ curr = CGIncludeGuard(prefix, curr)
+
+ # Add the auto-generated comment.
+ curr = CGWrapper(
+ curr,
+ pre=(AUTOGENERATED_WITH_SOURCE_WARNING_COMMENT %
+ os.path.basename(webIDLFile)))
+
+ # Store the final result.
+ self.root = curr
+
+ def declare(self):
+ return stripTrailingWhitespace(self.root.declare())
+
+ def define(self):
+ return stripTrailingWhitespace(self.root.define())
+
+ def deps(self):
+ return self.root.deps()
+
+
+class CGNativeMember(ClassMethod):
+ def __init__(self, descriptorProvider, member, name, signature, extendedAttrs,
+ breakAfter=True, passJSBitsAsNeeded=True, visibility="public",
+ typedArraysAreStructs=True, variadicIsSequence=False,
+ resultNotAddRefed=False,
+ virtual=False,
+ override=False):
+ """
+ If typedArraysAreStructs is false, typed arrays will be passed as
+ JS::Handle<JSObject*>. If it's true they will be passed as one of the
+ dom::TypedArray subclasses.
+
+ If passJSBitsAsNeeded is false, we don't automatically pass in a
+ JSContext* or a JSObject* based on the return and argument types. We
+ can still pass it based on 'implicitJSContext' annotations.
+ """
+ self.descriptorProvider = descriptorProvider
+ self.member = member
+ self.extendedAttrs = extendedAttrs
+ self.resultAlreadyAddRefed = not resultNotAddRefed
+ self.passJSBitsAsNeeded = passJSBitsAsNeeded
+ self.typedArraysAreStructs = typedArraysAreStructs
+ 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,
+ virtual=virtual,
+ override=override)
+
+ 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 default value that can be used on error returns.
+ For cases whose behavior depends on isMember, the second element will be
+ None if isMember is true.
+
+ The third element is a template for actually returning a value stored in
+ "${declName}" and "${holderName}". 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():
+ return "void", "", ""
+ if type.isPrimitive() and type.tag() in builtinNames:
+ result = CGGeneric(builtinNames[type.tag()])
+ defaultReturnArg = "0"
+ if type.nullable():
+ result = CGTemplatedType("Nullable", result)
+ defaultReturnArg = ""
+ return (result.define(),
+ "%s(%s)" % (result.define(), defaultReturnArg),
+ "return ${declName};\n")
+ if type.isDOMString() or type.isUSVString():
+ if isMember:
+ # No need for a third element in the isMember case
+ return "nsString", None, None
+ # Outparam
+ return "void", "", "aRetVal = ${declName};\n"
+ if type.isByteString():
+ if isMember:
+ # No need for a third element in the isMember case
+ return "nsCString", None, None
+ # Outparam
+ return "void", "", "aRetVal = ${declName};\n"
+ if type.isEnum():
+ enumName = type.unroll().inner.identifier.name
+ if type.nullable():
+ enumName = CGTemplatedType("Nullable",
+ CGGeneric(enumName)).define()
+ defaultValue = "%s()" % enumName
+ else:
+ defaultValue = "%s(0)" % enumName
+ return enumName, defaultValue, "return ${declName};\n"
+ if type.isGeckoInterface():
+ iface = type.unroll().inner
+ result = CGGeneric(self.descriptorProvider.getDescriptor(
+ iface.identifier.name).prettyNativeType)
+ if self.resultAlreadyAddRefed:
+ if isMember:
+ holder = "RefPtr"
+ else:
+ holder = "already_AddRefed"
+ if memberReturnsNewObject(self.member) or isMember:
+ warning = ""
+ else:
+ warning = "// Return a raw pointer here to avoid refcounting, but make sure it's safe (the object should be kept alive by the callee).\n"
+ result = CGWrapper(result,
+ pre=("%s%s<" % (warning, holder)),
+ post=">")
+ else:
+ result = CGWrapper(result, post="*")
+ # Since we always force an owning type for callback return values,
+ # our ${declName} is an OwningNonNull or RefPtr. So we can just
+ # .forget() to get our already_AddRefed.
+ return result.define(), "nullptr", "return ${declName}.forget();\n"
+ if type.isCallback():
+ return ("already_AddRefed<%s>" % type.unroll().callback.identifier.name,
+ "nullptr", "return ${declName}.forget();\n")
+ if type.isAny():
+ if isMember:
+ # No need for a third element in the isMember case
+ return "JS::Value", None, None
+ # Outparam
+ return "void", "", "aRetVal.set(${declName});\n"
+
+ if type.isObject():
+ if isMember:
+ # No need for a third element in the isMember case
+ return "JSObject*", None, None
+ return "void", "", "aRetVal.set(${declName});\n"
+ if type.isSpiderMonkeyInterface():
+ if isMember:
+ # No need for a third element in the isMember case
+ return "JSObject*", None, None
+ if type.nullable():
+ returnCode = "${declName}.IsNull() ? nullptr : ${declName}.Value().Obj()"
+ else:
+ returnCode = "${declName}.Obj()"
+ return "void", "", "aRetVal.set(%s);\n" % returnCode
+ if 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 = dedent("""
+ if (${declName}.IsNull()) {
+ aRetVal.SetNull();
+ } else {
+ aRetVal.SetValue().SwapElements(${declName}.Value());
+ }
+ """)
+ else:
+ returnCode = "aRetVal.SwapElements(${declName});\n"
+ return "void", "", returnCode
+ if type.isMozMap():
+ # If we want to handle MozMap-of-MozMap return values, we're
+ # going to need to fix example codegen to not produce MozMap<void>
+ # for the relevant argument...
+ assert not isMember
+ # In this case we convert directly into our outparam to start with
+ return "void", "", ""
+ if type.isDate():
+ result = CGGeneric("Date")
+ if type.nullable():
+ result = CGTemplatedType("Nullable", result)
+ return (result.define(), "%s()" % result.define(),
+ "return ${declName};\n")
+ if type.isDictionary():
+ if isMember:
+ # Only the first member of the tuple matters here, but return
+ # bogus values for the others in case someone decides to use
+ # them.
+ return CGDictionary.makeDictionaryName(type.inner), None, None
+ # In this case we convert directly into our outparam to start with
+ return "void", "", ""
+ if type.isUnion():
+ if isMember:
+ # Only the first member of the tuple matters here, but return
+ # bogus values for the others in case someone decides to use
+ # them.
+ return CGUnionStruct.unionTypeDecl(type, True), None, None
+ # In this case we convert directly into our outparam to start with
+ return "void", "", ""
+
+ raise TypeError("Don't know how to declare return value for %s" %
+ type)
+
+ def getArgs(self, returnType, argList):
+ args = [self.getArg(arg) for arg in argList]
+ # Now the outparams
+ if returnType.isDOMString() or returnType.isUSVString():
+ args.append(Argument("nsString&", "aRetVal"))
+ elif returnType.isByteString():
+ args.append(Argument("nsCString&", "aRetVal"))
+ 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(), "aRetVal"))
+ elif returnType.isMozMap():
+ nullable = returnType.nullable()
+ if nullable:
+ returnType = returnType.inner
+ # And now the actual underlying type
+ elementDecl = self.getReturnType(returnType.inner, True)
+ type = CGTemplatedType("MozMap", CGGeneric(elementDecl))
+ if nullable:
+ type = CGTemplatedType("Nullable", type)
+ args.append(Argument("%s&" % type.define(), "aRetVal"))
+ elif returnType.isDictionary():
+ nullable = returnType.nullable()
+ if nullable:
+ returnType = returnType.inner
+ dictType = CGGeneric(CGDictionary.makeDictionaryName(returnType.inner))
+ if nullable:
+ dictType = CGTemplatedType("Nullable", dictType)
+ args.append(Argument("%s&" % dictType.define(), "aRetVal"))
+ elif returnType.isUnion():
+ args.append(Argument("%s&" %
+ CGUnionStruct.unionTypeDecl(returnType, True),
+ "aRetVal"))
+ elif returnType.isAny():
+ args.append(Argument("JS::MutableHandle<JS::Value>", "aRetVal"))
+ elif returnType.isObject() or returnType.isSpiderMonkeyInterface():
+ args.append(Argument("JS::MutableHandle<JSObject*>", "aRetVal"))
+
+ # And the nsIPrincipal
+ if self.member.getExtendedAttribute('NeedsSubjectPrincipal'):
+ # Cheat and assume self.descriptorProvider is a descriptor
+ if self.descriptorProvider.interface.isExposedInAnyWorker():
+ args.append(Argument("Maybe<nsIPrincipal*>", "aSubjectPrincipal"))
+ else:
+ args.append(Argument("nsIPrincipal&", "aPrincipal"))
+ # And the caller type, if desired.
+ if needsCallerType(self.member):
+ args.append(Argument("CallerType", "aCallerType"))
+ # And the ErrorResult
+ if 'infallible' not in self.extendedAttrs:
+ # Use aRv so it won't conflict with local vars named "rv"
+ args.append(Argument("ErrorResult&", "aRv"))
+ # 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("const JS::Value&", "aThisVal"))
+ # And jscontext bits.
+ if needCx(returnType, argList, self.extendedAttrs,
+ self.passJSBitsAsNeeded, self.member.isStatic()):
+ args.insert(0, Argument("JSContext*", "cx"))
+ if needScopeObject(returnType, argList, self.extendedAttrs,
+ self.descriptorProvider.wrapperCache,
+ self.passJSBitsAsNeeded,
+ self.member.getExtendedAttribute("StoreInSlot")):
+ args.insert(1, Argument("JS::Handle<JSObject*>", "obj"))
+ # 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", "Variadic",
+ "MozMap"
+ """
+ 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.isMozMap():
+ nullable = type.nullable()
+ if nullable:
+ type = type.inner
+ elementType = type.inner
+ argType = self.getArgType(elementType, False, "MozMap")[0]
+ decl = CGTemplatedType("MozMap", argType)
+ return decl.define(), True, True
+
+ if type.isUnion():
+ # unionTypeDecl will handle nullable types, so return False for
+ # auto-wrapping in Nullable
+ return CGUnionStruct.unionTypeDecl(type, isMember), True, False
+
+ if type.isGeckoInterface() and not type.isCallbackInterface():
+ iface = type.unroll().inner
+ argIsPointer = type.nullable() or iface.isExternal()
+ forceOwningType = (iface.isCallback() or isMember or
+ iface.identifier.name == "Promise")
+ if argIsPointer:
+ if (optional or isMember) and forceOwningType:
+ typeDecl = "RefPtr<%s>"
+ else:
+ typeDecl = "%s*"
+ else:
+ if optional or isMember:
+ if forceOwningType:
+ typeDecl = "OwningNonNull<%s>"
+ else:
+ typeDecl = "NonNull<%s>"
+ else:
+ typeDecl = "%s&"
+ return ((typeDecl %
+ self.descriptorProvider.getDescriptor(iface.identifier.name).prettyNativeType),
+ False, False)
+
+ if type.isSpiderMonkeyInterface():
+ if not self.typedArraysAreStructs:
+ return "JS::Handle<JSObject*>", False, False
+
+ # Unroll for the name, in case we're nullable.
+ return type.unroll().name, True, True
+
+ if type.isDOMString() or type.isUSVString():
+ if isMember:
+ declType = "nsString"
+ else:
+ declType = "nsAString"
+ 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 = "RefPtr<%s>"
+ else:
+ declType = "%s*"
+ else:
+ if forceOwningType:
+ declType = "OwningNonNull<%s>"
+ else:
+ declType = "%s&"
+ if type.isCallback():
+ name = type.unroll().callback.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 = "JS::Handle<JS::Value>"
+ 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("Optional", decl)
+ ref = True
+ return (decl, ref)
+
+ def getArg(self, arg):
+ """
+ Get the full argument declaration for an argument
+ """
+ decl, ref = self.getArgType(arg.type, arg.canHaveMissingValue(),
+ "Variadic" if arg.variadic else False)
+ if ref:
+ decl = CGWrapper(decl, pre="const ", post="&")
+
+ return Argument(decl.define(), arg.identifier.name)
+
+ def arguments(self):
+ return self.member.signatures()[0][1]
+
+
+class CGExampleMethod(CGNativeMember):
+ def __init__(self, descriptor, method, signature, isConstructor, breakAfter=True):
+ CGNativeMember.__init__(self, descriptor, method,
+ CGSpecializedMethod.makeNativeName(descriptor,
+ method),
+ signature,
+ descriptor.getExtendedAttributes(method),
+ breakAfter=breakAfter,
+ variadicIsSequence=True)
+
+ def declare(self, cgClass):
+ assert self.member.isMethod()
+ # We skip declaring ourselves if this is a maplike/setlike/iterable
+ # method, because those get implemented automatically by the binding
+ # machinery, so the implementor of the interface doesn't have to worry
+ # about it.
+ if self.member.isMaplikeOrSetlikeOrIterableMethod():
+ return ''
+ return CGNativeMember.declare(self, cgClass);
+
+ def define(self, cgClass):
+ return ''
+
+
+class CGExampleGetter(CGNativeMember):
+ def __init__(self, descriptor, attr):
+ CGNativeMember.__init__(self, descriptor, attr,
+ CGSpecializedGetter.makeNativeName(descriptor,
+ attr),
+ (attr.type, []),
+ descriptor.getExtendedAttributes(attr,
+ getter=True))
+
+ def declare(self, cgClass):
+ assert self.member.isAttr()
+ # We skip declaring ourselves if this is a maplike/setlike attr (in
+ # practice, "size"), because those get implemented automatically by the
+ # binding machinery, so the implementor of the interface doesn't have to
+ # worry about it.
+ if self.member.isMaplikeOrSetlikeAttr():
+ return ''
+ return CGNativeMember.declare(self, cgClass);
+
+ def define(self, cgClass):
+ return ''
+
+
+class CGExampleSetter(CGNativeMember):
+ def __init__(self, descriptor, attr):
+ CGNativeMember.__init__(self, descriptor, attr,
+ CGSpecializedSetter.makeNativeName(descriptor,
+ attr),
+ (BuiltinTypes[IDLBuiltinType.Types.void],
+ [FakeArgument(attr.type, attr)]),
+ descriptor.getExtendedAttributes(attr,
+ setter=True))
+
+ def define(self, cgClass):
+ return ''
+
+
+class CGBindingImplClass(CGClass):
+ """
+ Common codegen for generating a C++ implementation of a WebIDL interface
+ """
+ def __init__(self, descriptor, cgMethod, cgGetter, cgSetter, wantGetParent=True, wrapMethodName="WrapObject", skipStaticMethods=False):
+ """
+ cgMethod, cgGetter and cgSetter are classes used to codegen methods,
+ getters and setters.
+ """
+ self.descriptor = descriptor
+ self._deps = descriptor.interface.getDeps()
+
+ iface = descriptor.interface
+
+ self.methodDecls = []
+
+ def appendMethod(m, isConstructor=False):
+ sigs = m.signatures()
+ for s in sigs[:-1]:
+ # Don't put a blank line after overloads, until we
+ # get to the last one.
+ self.methodDecls.append(cgMethod(descriptor, m, s,
+ isConstructor,
+ breakAfter=False))
+ self.methodDecls.append(cgMethod(descriptor, m, sigs[-1],
+ isConstructor))
+
+ if iface.ctor():
+ appendMethod(iface.ctor(), isConstructor=True)
+ for n in iface.namedConstructors:
+ appendMethod(n, isConstructor=True)
+ for m in iface.members:
+ if m.isMethod():
+ if m.isIdentifierLess():
+ continue
+ if not m.isStatic() or not skipStaticMethods:
+ appendMethod(m)
+ elif m.isAttr():
+ self.methodDecls.append(cgGetter(descriptor, m))
+ if not m.readonly:
+ self.methodDecls.append(cgSetter(descriptor, m))
+
+ # Now do the special operations
+ def appendSpecialOperation(name, op):
+ if op is None:
+ return
+ if name == "IndexedCreator" or name == "NamedCreator":
+ # These are identical to the setters
+ return
+ assert len(op.signatures()) == 1
+ returnType, args = op.signatures()[0]
+ # Make a copy of the args, since we plan to modify them.
+ args = list(args)
+ if op.isGetter() or op.isDeleter():
+ # This is a total hack. The '&' belongs with the
+ # type, not the name! But it works, and is simpler
+ # than trying to somehow make this pretty.
+ args.append(FakeArgument(BuiltinTypes[IDLBuiltinType.Types.boolean],
+ op, name="&found"))
+ if name == "Stringifier":
+ if op.isIdentifierLess():
+ # XXXbz I wish we were consistent about our renaming here.
+ name = "Stringify"
+ else:
+ # We already added this method
+ return
+ if name == "LegacyCaller":
+ if op.isIdentifierLess():
+ # XXXbz I wish we were consistent about our renaming here.
+ name = "LegacyCall"
+ else:
+ # We already added this method
+ return
+ if name == "Jsonifier":
+ # We already added this method
+ return
+ self.methodDecls.append(
+ CGNativeMember(descriptor, op,
+ name,
+ (returnType, args),
+ descriptor.getExtendedAttributes(op)))
+ # Sort things by name so we get stable ordering in the output.
+ ops = descriptor.operations.items()
+ ops.sort(key=lambda x: x[0])
+ for name, op in ops:
+ appendSpecialOperation(name, op)
+ # If we support indexed properties, then we need a Length()
+ # method so we know which indices are supported.
+ if descriptor.supportsIndexedProperties():
+ # But we don't need it if we already have an infallible
+ # "length" attribute, which we often do.
+ haveLengthAttr = any(
+ m for m in iface.members if m.isAttr() and
+ CGSpecializedGetter.makeNativeName(descriptor, m) == "Length")
+ if not haveLengthAttr:
+ self.methodDecls.append(
+ CGNativeMember(descriptor, FakeMember(),
+ "Length",
+ (BuiltinTypes[IDLBuiltinType.Types.unsigned_long],
+ []),
+ {"infallible": True}))
+ # And if we support named properties we need to be able to
+ # enumerate the supported names.
+ if descriptor.supportsNamedProperties():
+ self.methodDecls.append(
+ CGNativeMember(
+ descriptor, FakeMember(),
+ "GetSupportedNames",
+ (IDLSequenceType(None,
+ BuiltinTypes[IDLBuiltinType.Types.domstring]),
+ []),
+ {"infallible": True}))
+
+ wrapArgs = [Argument('JSContext*', 'aCx'),
+ Argument('JS::Handle<JSObject*>', 'aGivenProto')]
+ if not descriptor.wrapperCache:
+ wrapReturnType = "bool"
+ wrapArgs.append(Argument('JS::MutableHandle<JSObject*>',
+ 'aReflector'))
+ else:
+ wrapReturnType = "JSObject*"
+ self.methodDecls.insert(0,
+ ClassMethod(wrapMethodName, wrapReturnType,
+ wrapArgs, virtual=descriptor.wrapperCache,
+ breakAfterReturnDecl=" ",
+ override=descriptor.wrapperCache,
+ body=self.getWrapObjectBody()))
+ if wantGetParent:
+ self.methodDecls.insert(0,
+ ClassMethod("GetParentObject",
+ self.getGetParentObjectReturnType(),
+ [], const=True,
+ breakAfterReturnDecl=" ",
+ body=self.getGetParentObjectBody()))
+
+ # Invoke CGClass.__init__ in any subclasses afterwards to do the actual codegen.
+
+ def getWrapObjectBody(self):
+ return None
+
+ def getGetParentObjectReturnType(self):
+ return ("// TODO: return something sensible here, and change the return type\n"
+ "%s*" % self.descriptor.nativeType.split('::')[-1])
+
+ def getGetParentObjectBody(self):
+ return None
+
+ def deps(self):
+ return self._deps
+
+
+class CGExampleClass(CGBindingImplClass):
+ """
+ Codegen for the actual example class implementation for this descriptor
+ """
+ def __init__(self, descriptor):
+ CGBindingImplClass.__init__(self, descriptor,
+ CGExampleMethod, CGExampleGetter, CGExampleSetter,
+ wantGetParent=descriptor.wrapperCache)
+
+ self.parentIface = descriptor.interface.parent
+ if self.parentIface:
+ self.parentDesc = descriptor.getDescriptor(
+ self.parentIface.identifier.name)
+ bases = [ClassBase(self.nativeLeafName(self.parentDesc))]
+ else:
+ bases = [ClassBase("nsISupports /* or NonRefcountedDOMObject if this is a non-refcounted object */")]
+ if descriptor.wrapperCache:
+ bases.append(ClassBase("nsWrapperCache /* Change wrapperCache in the binding configuration if you don't want this */"))
+
+ destructorVisibility = "protected"
+ if self.parentIface:
+ extradeclarations = (
+ "public:\n"
+ " NS_DECL_ISUPPORTS_INHERITED\n"
+ " NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(%s, %s)\n"
+ "\n" % (self.nativeLeafName(descriptor),
+ self.nativeLeafName(self.parentDesc)))
+ else:
+ extradeclarations = (
+ "public:\n"
+ " NS_DECL_CYCLE_COLLECTING_ISUPPORTS\n"
+ " NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(%s)\n"
+ "\n" % self.nativeLeafName(descriptor))
+
+ if descriptor.interface.hasChildInterfaces():
+ decorators = ""
+ else:
+ decorators = "final"
+
+ CGClass.__init__(self, self.nativeLeafName(descriptor),
+ bases=bases,
+ constructors=[ClassConstructor([],
+ visibility="public")],
+ destructor=ClassDestructor(visibility=destructorVisibility),
+ methods=self.methodDecls,
+ decorators=decorators,
+ extradeclarations=extradeclarations)
+
+ def define(self):
+ # Just override CGClass and do our own thing
+ ctordtor = dedent("""
+ ${nativeType}::${nativeType}()
+ {
+ // Add |MOZ_COUNT_CTOR(${nativeType});| for a non-refcounted object.
+ }
+
+ ${nativeType}::~${nativeType}()
+ {
+ // Add |MOZ_COUNT_DTOR(${nativeType});| for a non-refcounted object.
+ }
+ """)
+
+ if self.parentIface:
+ ccImpl = dedent("""
+
+ // Only needed for refcounted objects.
+ # error "If you don't have members that need cycle collection,
+ # then remove all the cycle collection bits from this
+ # implementation and the corresponding header. If you do, you
+ # want NS_IMPL_CYCLE_COLLECTION_INHERITED(${nativeType},
+ # ${parentType}, your, members, here)"
+ NS_IMPL_ADDREF_INHERITED(${nativeType}, ${parentType})
+ NS_IMPL_RELEASE_INHERITED(${nativeType}, ${parentType})
+ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${nativeType})
+ NS_INTERFACE_MAP_END_INHERITING(${parentType})
+
+ """)
+ else:
+ ccImpl = dedent("""
+
+ // Only needed for refcounted objects.
+ NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(${nativeType})
+ NS_IMPL_CYCLE_COLLECTING_ADDREF(${nativeType})
+ NS_IMPL_CYCLE_COLLECTING_RELEASE(${nativeType})
+ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${nativeType})
+ NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+ NS_INTERFACE_MAP_END
+
+ """)
+
+ if self.descriptor.wrapperCache:
+ reflectorArg = ""
+ reflectorPassArg = ""
+ returnType = "JSObject*"
+ else:
+ reflectorArg = ", JS::MutableHandle<JSObject*> aReflector"
+ reflectorPassArg = ", aReflector"
+ returnType = "bool"
+ classImpl = ccImpl + ctordtor + "\n" + dedent("""
+ ${returnType}
+ ${nativeType}::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto${reflectorArg})
+ {
+ return ${ifaceName}Binding::Wrap(aCx, this, aGivenProto${reflectorPassArg});
+ }
+
+ """)
+ return string.Template(classImpl).substitute(
+ ifaceName=self.descriptor.name,
+ nativeType=self.nativeLeafName(self.descriptor),
+ parentType=self.nativeLeafName(self.parentDesc) if self.parentIface else "",
+ returnType=returnType,
+ reflectorArg=reflectorArg,
+ reflectorPassArg=reflectorPassArg)
+
+ @staticmethod
+ def nativeLeafName(descriptor):
+ return descriptor.nativeType.split('::')[-1]
+
+
+class CGExampleRoot(CGThing):
+ """
+ Root codegen class for example implementation generation. Instantiate the
+ class and call declare or define to generate header or cpp code,
+ respectively.
+ """
+ def __init__(self, config, interfaceName):
+ descriptor = config.getDescriptor(interfaceName)
+
+ self.root = CGWrapper(CGExampleClass(descriptor),
+ pre="\n", post="\n")
+
+ self.root = CGNamespace.build(["mozilla", "dom"], self.root)
+
+ builder = ForwardDeclarationBuilder()
+ for member in descriptor.interface.members:
+ if not member.isAttr() and not member.isMethod():
+ continue
+ if member.isStatic():
+ builder.addInMozillaDom("GlobalObject")
+ if member.isAttr() and not member.isMaplikeOrSetlikeAttr():
+ builder.forwardDeclareForType(member.type, config)
+ else:
+ assert member.isMethod()
+ if not member.isMaplikeOrSetlikeOrIterableMethod():
+ for sig in member.signatures():
+ builder.forwardDeclareForType(sig[0], config)
+ for arg in sig[1]:
+ builder.forwardDeclareForType(arg.type, config)
+
+ self.root = CGList([builder.build(),
+ self.root], "\n")
+
+ # Throw in our #includes
+ self.root = CGHeaders([], [], [], [],
+ ["nsWrapperCache.h",
+ "nsCycleCollectionParticipant.h",
+ "mozilla/Attributes.h",
+ "mozilla/ErrorResult.h",
+ "mozilla/dom/BindingDeclarations.h",
+ "js/TypeDecls.h"],
+ ["mozilla/dom/%s.h" % interfaceName,
+ ("mozilla/dom/%s" %
+ CGHeaders.getDeclarationFilename(descriptor.interface))], "", self.root)
+
+ # And now some include guards
+ self.root = CGIncludeGuard(interfaceName, self.root)
+
+ # And our license block comes before everything else
+ self.root = CGWrapper(self.root, pre=dedent("""
+ /* -*- 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/. */
+
+ """))
+
+ def declare(self):
+ return self.root.declare()
+
+ def define(self):
+ return self.root.define()
+
+
+def jsImplName(name):
+ return name + "JSImpl"
+
+
+class CGJSImplMember(CGNativeMember):
+ """
+ Base class for generating code for the members of the implementation class
+ for a JS-implemented WebIDL interface.
+ """
+ def __init__(self, descriptorProvider, member, name, signature,
+ extendedAttrs, breakAfter=True, passJSBitsAsNeeded=True,
+ visibility="public", variadicIsSequence=False,
+ virtual=False, override=False):
+ CGNativeMember.__init__(self, descriptorProvider, member, name,
+ signature, extendedAttrs, breakAfter=breakAfter,
+ passJSBitsAsNeeded=passJSBitsAsNeeded,
+ visibility=visibility,
+ variadicIsSequence=variadicIsSequence,
+ virtual=virtual,
+ override=override)
+ self.body = self.getImpl()
+
+ def getArgs(self, returnType, argList):
+ args = CGNativeMember.getArgs(self, returnType, argList)
+ args.append(Argument("JSCompartment*", "aCompartment", "nullptr"))
+ return args
+
+
+class CGJSImplMethod(CGJSImplMember):
+ """
+ Class for generating code for the methods for a JS-implemented WebIDL
+ interface.
+ """
+ def __init__(self, descriptor, method, signature, isConstructor, breakAfter=True):
+ virtual = False
+ override = False
+ if (method.identifier.name == "eventListenerWasAdded" or
+ method.identifier.name == "eventListenerWasRemoved"):
+ virtual = True
+ override = True
+
+ self.signature = signature
+ self.descriptor = descriptor
+ self.isConstructor = isConstructor
+ CGJSImplMember.__init__(self, descriptor, method,
+ CGSpecializedMethod.makeNativeName(descriptor,
+ method),
+ signature,
+ descriptor.getExtendedAttributes(method),
+ breakAfter=breakAfter,
+ variadicIsSequence=True,
+ passJSBitsAsNeeded=False,
+ virtual=virtual,
+ override=override)
+
+ def getArgs(self, returnType, argList):
+ if self.isConstructor:
+ # Skip the JSCompartment bits for constructors; it's handled
+ # manually in getImpl.
+ return CGNativeMember.getArgs(self, returnType, argList)
+ return CGJSImplMember.getArgs(self, returnType, argList)
+
+ def getImpl(self):
+ args = self.getArgs(self.signature[0], self.signature[1])
+ if not self.isConstructor:
+ return 'return mImpl->%s(%s);\n' % (self.name, ", ".join(arg.name for arg in args))
+
+ assert self.descriptor.interface.isJSImplemented()
+ if self.name != 'Constructor':
+ raise TypeError("Named constructors are not supported for JS implemented WebIDL. See bug 851287.")
+ if len(self.signature[1]) != 0:
+ # The first two arguments to the constructor implementation are not
+ # arguments to the WebIDL constructor, so don't pass them to __Init()
+ assert args[0].argType == 'const GlobalObject&'
+ assert args[1].argType == 'JSContext*'
+ constructorArgs = [arg.name for arg in args[2:]]
+ constructorArgs.append("js::GetObjectCompartment(scopeObj)")
+ initCall = fill(
+ """
+ // Wrap the object before calling __Init so that __DOM_IMPL__ is available.
+ JS::Rooted<JSObject*> scopeObj(cx, globalHolder->GetGlobalJSObject());
+ MOZ_ASSERT(js::IsObjectInContextCompartment(scopeObj, cx));
+ JS::Rooted<JS::Value> wrappedVal(cx);
+ if (!GetOrCreateDOMReflector(cx, impl, &wrappedVal)) {
+ //XXX Assertion disabled for now, see bug 991271.
+ MOZ_ASSERT(true || JS_IsExceptionPending(cx));
+ aRv.Throw(NS_ERROR_UNEXPECTED);
+ return nullptr;
+ }
+ // Initialize the object with the constructor arguments.
+ impl->mImpl->__Init(${args});
+ if (aRv.Failed()) {
+ return nullptr;
+ }
+ """,
+ args=", ".join(constructorArgs))
+ else:
+ initCall = ""
+ return genConstructorBody(self.descriptor, initCall)
+
+
+def genConstructorBody(descriptor, initCall=""):
+ return fill(
+ """
+ JS::Rooted<JSObject*> jsImplObj(cx);
+ nsCOMPtr<nsIGlobalObject> globalHolder =
+ ConstructJSImplementation("${contractId}", global, &jsImplObj, aRv);
+ if (aRv.Failed()) {
+ return nullptr;
+ }
+ // Build the C++ implementation.
+ RefPtr<${implClass}> impl = new ${implClass}(jsImplObj, globalHolder);
+ $*{initCall}
+ return impl.forget();
+ """,
+ contractId=descriptor.interface.getJSImplementation(),
+ implClass=descriptor.name,
+ initCall=initCall)
+
+
+# We're always fallible
+def callbackGetterName(attr, descriptor):
+ return "Get" + MakeNativeName(
+ descriptor.binaryNameFor(attr.identifier.name))
+
+
+def callbackSetterName(attr, descriptor):
+ return "Set" + MakeNativeName(
+ descriptor.binaryNameFor(attr.identifier.name))
+
+
+class CGJSImplClearCachedValueMethod(CGAbstractBindingMethod):
+ def __init__(self, descriptor, attr):
+ if attr.getExtendedAttribute("StoreInSlot"):
+ raise TypeError("[StoreInSlot] is not supported for JS-implemented WebIDL. See bug 1056325.")
+
+ CGAbstractBindingMethod.__init__(self, descriptor,
+ MakeJSImplClearCachedValueNativeName(attr),
+ JSNativeArguments())
+ self.attr = attr
+
+ def generate_code(self):
+ return CGGeneric(fill(
+ """
+ ${bindingNamespace}::${fnName}(self);
+ args.rval().setUndefined();
+ return true;
+ """,
+ bindingNamespace=toBindingNamespace(self.descriptor.name),
+ fnName=MakeClearCachedValueNativeName(self.attr)))
+
+
+class CGJSImplGetter(CGJSImplMember):
+ """
+ Class for generating code for the getters of attributes for a JS-implemented
+ WebIDL interface.
+ """
+ def __init__(self, descriptor, attr):
+ CGJSImplMember.__init__(self, descriptor, attr,
+ CGSpecializedGetter.makeNativeName(descriptor,
+ attr),
+ (attr.type, []),
+ descriptor.getExtendedAttributes(attr,
+ getter=True),
+ passJSBitsAsNeeded=False)
+
+ def getImpl(self):
+ callbackArgs = [arg.name for arg in self.getArgs(self.member.type, [])]
+ return 'return mImpl->%s(%s);\n' % (
+ callbackGetterName(self.member, self.descriptorProvider),
+ ", ".join(callbackArgs))
+
+
+class CGJSImplSetter(CGJSImplMember):
+ """
+ Class for generating code for the setters of attributes for a JS-implemented
+ WebIDL interface.
+ """
+ def __init__(self, descriptor, attr):
+ CGJSImplMember.__init__(self, descriptor, attr,
+ CGSpecializedSetter.makeNativeName(descriptor,
+ attr),
+ (BuiltinTypes[IDLBuiltinType.Types.void],
+ [FakeArgument(attr.type, attr)]),
+ descriptor.getExtendedAttributes(attr,
+ setter=True),
+ passJSBitsAsNeeded=False)
+
+ def getImpl(self):
+ callbackArgs = [arg.name for arg in self.getArgs(BuiltinTypes[IDLBuiltinType.Types.void],
+ [FakeArgument(self.member.type, self.member)])]
+ return 'mImpl->%s(%s);\n' % (
+ callbackSetterName(self.member, self.descriptorProvider),
+ ", ".join(callbackArgs))
+
+
+class CGJSImplClass(CGBindingImplClass):
+ def __init__(self, descriptor):
+ CGBindingImplClass.__init__(self, descriptor, CGJSImplMethod, CGJSImplGetter, CGJSImplSetter, skipStaticMethods=True)
+
+ if descriptor.interface.parent:
+ parentClass = descriptor.getDescriptor(
+ descriptor.interface.parent.identifier.name).jsImplParent
+ baseClasses = [ClassBase(parentClass)]
+ isupportsDecl = "NS_DECL_ISUPPORTS_INHERITED\n"
+ ccDecl = ("NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(%s, %s)\n" %
+ (descriptor.name, parentClass))
+ constructorBody = dedent("""
+ // Make sure we're an nsWrapperCache already
+ MOZ_ASSERT(static_cast<nsWrapperCache*>(this));
+ // And that our ancestor has not called SetIsNotDOMBinding()
+ MOZ_ASSERT(IsDOMBinding());
+ """)
+ extradefinitions = fill(
+ """
+ NS_IMPL_CYCLE_COLLECTION_INHERITED(${ifaceName}, ${parentClass}, mImpl, mParent)
+ NS_IMPL_ADDREF_INHERITED(${ifaceName}, ${parentClass})
+ NS_IMPL_RELEASE_INHERITED(${ifaceName}, ${parentClass})
+ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(${ifaceName})
+ NS_INTERFACE_MAP_END_INHERITING(${parentClass})
+ """,
+ ifaceName=self.descriptor.name,
+ parentClass=parentClass)
+ else:
+ baseClasses = [ClassBase("nsSupportsWeakReference"),
+ ClassBase("nsWrapperCache")]
+ isupportsDecl = "NS_DECL_CYCLE_COLLECTING_ISUPPORTS\n"
+ ccDecl = ("NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(%s)\n" %
+ descriptor.name)
+ extradefinitions = fill(
+ """
+ NS_IMPL_CYCLE_COLLECTION_CLASS(${ifaceName})
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(${ifaceName})
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mImpl)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
+ tmp->ClearWeakReferences();
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(${ifaceName})
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImpl)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+ NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(${ifaceName})
+ NS_IMPL_CYCLE_COLLECTING_ADDREF(${ifaceName})
+ NS_IMPL_CYCLE_COLLECTING_RELEASE(${ifaceName})
+ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${ifaceName})
+ NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+ NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
+ NS_INTERFACE_MAP_END
+ """,
+ ifaceName=self.descriptor.name)
+
+ extradeclarations = fill(
+ """
+ public:
+ $*{isupportsDecl}
+ $*{ccDecl}
+
+ private:
+ RefPtr<${jsImplName}> mImpl;
+ nsCOMPtr<nsISupports> mParent;
+
+ """,
+ isupportsDecl=isupportsDecl,
+ ccDecl=ccDecl,
+ jsImplName=jsImplName(descriptor.name))
+
+ if descriptor.interface.hasChildInterfaces():
+ decorators = ""
+ # We need a protected virtual destructor our subclasses can use
+ destructor = ClassDestructor(virtual=True, visibility="protected")
+ else:
+ decorators = "final"
+ destructor = ClassDestructor(virtual=False, visibility="private")
+
+ baseConstructors = [
+ ("mImpl(new %s(nullptr, aJSImplObject, /* aIncumbentGlobal = */ nullptr))" %
+ jsImplName(descriptor.name)),
+ "mParent(aParent)"]
+ parentInterface = descriptor.interface.parent
+ while parentInterface:
+ if parentInterface.isJSImplemented():
+ baseConstructors.insert(
+ 0, "%s(aJSImplObject, aParent)" % parentClass)
+ break
+ parentInterface = parentInterface.parent
+ if not parentInterface and descriptor.interface.parent:
+ # We only have C++ ancestors, so only pass along the window
+ baseConstructors.insert(0,
+ "%s(aParent)" % parentClass)
+
+ constructor = ClassConstructor(
+ [Argument("JS::Handle<JSObject*>", "aJSImplObject"),
+ Argument("nsIGlobalObject*", "aParent")],
+ visibility="public",
+ baseConstructors=baseConstructors)
+
+ self.methodDecls.append(
+ ClassMethod("_Create",
+ "bool",
+ JSNativeArguments(),
+ static=True,
+ body=self.getCreateFromExistingBody()))
+
+ CGClass.__init__(self, descriptor.name,
+ bases=baseClasses,
+ constructors=[constructor],
+ destructor=destructor,
+ methods=self.methodDecls,
+ decorators=decorators,
+ extradeclarations=extradeclarations,
+ extradefinitions=extradefinitions)
+
+ def getWrapObjectBody(self):
+ return fill(
+ """
+ JS::Rooted<JSObject*> obj(aCx, ${name}Binding::Wrap(aCx, this, aGivenProto));
+ if (!obj) {
+ return nullptr;
+ }
+
+ // Now define it on our chrome object
+ JSAutoCompartment ac(aCx, mImpl->Callback());
+ if (!JS_WrapObject(aCx, &obj)) {
+ return nullptr;
+ }
+ if (!JS_DefineProperty(aCx, mImpl->Callback(), "__DOM_IMPL__", obj, 0)) {
+ return nullptr;
+ }
+ return obj;
+ """,
+ name=self.descriptor.name)
+
+ def getGetParentObjectReturnType(self):
+ return "nsISupports*"
+
+ def getGetParentObjectBody(self):
+ return "return mParent;\n"
+
+ def getCreateFromExistingBody(self):
+ # XXXbz we could try to get parts of this (e.g. the argument
+ # conversions) auto-generated by somehow creating an IDLMethod and
+ # adding it to our interface, but we'd still need to special-case the
+ # implementation slightly to have it not try to forward to the JS
+ # object...
+ return fill(
+ """
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ if (args.length() < 2) {
+ return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "${ifaceName}._create");
+ }
+ if (!args[0].isObject()) {
+ return ThrowErrorMessage(cx, MSG_NOT_OBJECT, "Argument 1 of ${ifaceName}._create");
+ }
+ if (!args[1].isObject()) {
+ return ThrowErrorMessage(cx, MSG_NOT_OBJECT, "Argument 2 of ${ifaceName}._create");
+ }
+
+ // GlobalObject will go through wrappers as needed for us, and
+ // is simpler than the right UnwrapArg incantation.
+ GlobalObject global(cx, &args[0].toObject());
+ if (global.Failed()) {
+ return false;
+ }
+ nsCOMPtr<nsIGlobalObject> globalHolder = do_QueryInterface(global.GetAsSupports());
+ MOZ_ASSERT(globalHolder);
+ JS::Rooted<JSObject*> arg(cx, &args[1].toObject());
+ RefPtr<${implName}> impl = new ${implName}(arg, globalHolder);
+ MOZ_ASSERT(js::IsObjectInContextCompartment(arg, cx));
+ return GetOrCreateDOMReflector(cx, impl, args.rval());
+ """,
+ ifaceName=self.descriptor.interface.identifier.name,
+ implName=self.descriptor.name)
+
+
+def isJSImplementedDescriptor(descriptorProvider):
+ return (isinstance(descriptorProvider, Descriptor) and
+ descriptorProvider.interface.isJSImplemented())
+
+
+class CGCallback(CGClass):
+ def __init__(self, idlObject, descriptorProvider, baseName, methods,
+ getters=[], setters=[]):
+ self.baseName = baseName
+ self._deps = idlObject.getDeps()
+ self.idlObject = idlObject
+ self.name = idlObject.identifier.name
+ if isJSImplementedDescriptor(descriptorProvider):
+ self.name = jsImplName(self.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 isinstance(method, CallbackMember) or not method.needThisHandling:
+ realMethods.append(method)
+ else:
+ realMethods.extend(self.getMethodImpls(method))
+ realMethods.append(
+ ClassMethod("operator==", "bool",
+ [Argument("const %s&" % self.name, "aOther")],
+ inline=True, bodyInHeader=True,
+ const=True,
+ body=("return %s::operator==(aOther);\n" % baseName)))
+ CGClass.__init__(self, self.name,
+ bases=[ClassBase(baseName)],
+ constructors=self.getConstructors(),
+ methods=realMethods+getters+setters)
+
+ def getConstructors(self):
+ if (not self.idlObject.isInterface() and
+ not self.idlObject._treatNonObjectAsNull):
+ body = "MOZ_ASSERT(JS::IsCallable(mCallback));\n"
+ else:
+ # Not much we can assert about it, other than not being null, and
+ # CallbackObject does that already.
+ body = ""
+ return [
+ ClassConstructor(
+ [Argument("JSContext*", "aCx"),
+ Argument("JS::Handle<JSObject*>", "aCallback"),
+ Argument("nsIGlobalObject*", "aIncumbentGlobal")],
+ bodyInHeader=True,
+ visibility="public",
+ explicit=True,
+ baseConstructors=[
+ "%s(aCx, aCallback, aIncumbentGlobal)" % self.baseName,
+ ],
+ body=body),
+ ClassConstructor(
+ [Argument("JSContext*", "aCx"),
+ Argument("JS::Handle<JSObject*>", "aCallback"),
+ Argument("nsIGlobalObject*", "aIncumbentGlobal"),
+ Argument("const FastCallbackConstructor&", "")],
+ bodyInHeader=True,
+ visibility="public",
+ explicit=True,
+ baseConstructors=[
+ "%s(aCx, aCallback, aIncumbentGlobal, FastCallbackConstructor())" % self.baseName,
+ ],
+ body=body),
+ ClassConstructor(
+ [Argument("JS::Handle<JSObject*>", "aCallback"),
+ Argument("JS::Handle<JSObject*>", "aAsyncStack"),
+ Argument("nsIGlobalObject*", "aIncumbentGlobal")],
+ bodyInHeader=True,
+ visibility="public",
+ explicit=True,
+ baseConstructors=[
+ "%s(aCallback, aAsyncStack, aIncumbentGlobal)" % self.baseName,
+ ],
+ body=body)]
+
+ 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 == "JSContext*"
+ assert args[1].name == "aThisVal" and args[1].argType == "JS::Handle<JS::Value>"
+ args = args[2:]
+
+ # Now remember which index the ErrorResult argument is at;
+ # we'll need this below.
+ assert args[-1].name == "aRv" and args[-1].argType == "ErrorResult&"
+ rvIndex = len(args) - 1
+ assert rvIndex >= 0
+
+ # 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()", "thisValJS"] + argnames
+ argnamesWithoutThis = ["s.GetContext()", "JS::UndefinedHandleValue"] + argnames
+ # Now that we've recorded the argnames for our call to our private
+ # method, insert our optional argument for the execution reason.
+ args.append(Argument("const char*", "aExecutionReason",
+ "nullptr"))
+
+ # Make copies of the arg list for the two "without rv" overloads. Note
+ # that those don't need aExceptionHandling or aCompartment arguments
+ # because those would make not sense anyway: the only sane thing to do
+ # with exceptions in the "without rv" cases is to report them.
+ argsWithoutRv = list(args)
+ argsWithoutRv.pop(rvIndex)
+ argsWithoutThisAndRv = list(argsWithoutRv)
+
+ # Add the potional argument for deciding whether the CallSetup should
+ # re-throw exceptions on aRv.
+ args.append(Argument("ExceptionHandling", "aExceptionHandling",
+ "eReportExceptions"))
+ # And the argument for communicating when exceptions should really be
+ # rethrown. In particular, even when aExceptionHandling is
+ # eRethrowExceptions they won't get rethrown if aCompartment is provided
+ # and its principal doesn't subsume either the callback or the
+ # exception.
+ args.append(Argument("JSCompartment*", "aCompartment", "nullptr"))
+ # And now insert our template argument.
+ argsWithoutThis = list(args)
+ args.insert(0, Argument("const T&", "thisVal"))
+ argsWithoutRv.insert(0, Argument("const T&", "thisVal"))
+
+ argnamesWithoutThisAndRv = [arg.name for arg in argsWithoutThisAndRv]
+ argnamesWithoutThisAndRv.insert(rvIndex, "rv");
+ # If we just leave things like that, and have no actual arguments in the
+ # IDL, we will end up trying to call the templated "without rv" overload
+ # with "rv" as the thisVal. That's no good. So explicitly append the
+ # aExceptionHandling and aCompartment values we need to end up matching
+ # the signature of our non-templated "with rv" overload.
+ argnamesWithoutThisAndRv.extend(["eReportExceptions", "nullptr"])
+
+ argnamesWithoutRv = [arg.name for arg in argsWithoutRv]
+ # Note that we need to insert at rvIndex + 1, since we inserted a
+ # thisVal arg at the start.
+ argnamesWithoutRv.insert(rvIndex + 1, "rv")
+
+ errorReturn = method.getDefaultRetval()
+
+ setupCall = fill(
+ """
+ if (!aExecutionReason) {
+ aExecutionReason = "${executionReason}";
+ }
+ CallSetup s(this, aRv, aExecutionReason, aExceptionHandling, aCompartment);
+ if (!s.GetContext()) {
+ MOZ_ASSERT(aRv.Failed());
+ return${errorReturn};
+ }
+ """,
+ errorReturn=errorReturn,
+ executionReason=method.getPrettyName())
+
+ bodyWithThis = fill(
+ """
+ $*{setupCall}
+ JS::Rooted<JS::Value> thisValJS(s.GetContext());
+ if (!ToJSValue(s.GetContext(), thisVal, &thisValJS)) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return${errorReturn};
+ }
+ return ${methodName}(${callArgs});
+ """,
+ setupCall=setupCall,
+ errorReturn=errorReturn,
+ methodName=method.name,
+ callArgs=", ".join(argnamesWithThis))
+ bodyWithoutThis = fill(
+ """
+ $*{setupCall}
+ return ${methodName}(${callArgs});
+ """,
+ setupCall=setupCall,
+ errorReturn=errorReturn,
+ methodName=method.name,
+ callArgs=", ".join(argnamesWithoutThis))
+ bodyWithThisWithoutRv = fill(
+ """
+ IgnoredErrorResult rv;
+ return ${methodName}(${callArgs});
+ """,
+ methodName=method.name,
+ callArgs=", ".join(argnamesWithoutRv))
+ bodyWithoutThisAndRv = fill(
+ """
+ IgnoredErrorResult rv;
+ return ${methodName}(${callArgs});
+ """,
+ methodName=method.name,
+ callArgs=", ".join(argnamesWithoutThisAndRv))
+
+ return [ClassMethod(method.name, method.returnType, args,
+ bodyInHeader=True,
+ templateArgs=["typename T"],
+ body=bodyWithThis),
+ ClassMethod(method.name, method.returnType, argsWithoutThis,
+ bodyInHeader=True,
+ body=bodyWithoutThis),
+ ClassMethod(method.name, method.returnType, argsWithoutRv,
+ bodyInHeader=True,
+ templateArgs=["typename T"],
+ body=bodyWithThisWithoutRv),
+ ClassMethod(method.name, method.returnType, argsWithoutThisAndRv,
+ bodyInHeader=True,
+ body=bodyWithoutThisAndRv),
+ method]
+
+ def deps(self):
+ return self._deps
+
+
+class CGCallbackFunction(CGCallback):
+ def __init__(self, callback, descriptorProvider):
+ self.callback = callback
+ CGCallback.__init__(self, callback, descriptorProvider,
+ "CallbackFunction",
+ methods=[CallCallback(callback, descriptorProvider)])
+
+ def getConstructors(self):
+ return CGCallback.getConstructors(self) + [
+ ClassConstructor(
+ [Argument("CallbackFunction*", "aOther")],
+ bodyInHeader=True,
+ visibility="public",
+ explicit=True,
+ baseConstructors=["CallbackFunction(aOther)"])]
+
+
+class CGFastCallback(CGClass):
+ def __init__(self, idlObject):
+ self._deps = idlObject.getDeps()
+ baseName = idlObject.identifier.name
+ constructor = ClassConstructor(
+ [Argument("JSContext*", "aCx"),
+ Argument("JS::Handle<JSObject*>", "aCallback"),
+ Argument("nsIGlobalObject*", "aIncumbentGlobal")],
+ bodyInHeader=True,
+ visibility="public",
+ explicit=True,
+ baseConstructors=[
+ "%s(aCx, aCallback, aIncumbentGlobal, FastCallbackConstructor())" %
+ baseName,
+ ],
+ body="")
+
+ traceMethod = ClassMethod("Trace", "void",
+ [Argument("JSTracer*", "aTracer")],
+ inline=True,
+ bodyInHeader=True,
+ visibility="public",
+ body="%s::Trace(aTracer);\n" % baseName)
+ holdMethod = ClassMethod("HoldJSObjectsIfMoreThanOneOwner", "void",
+ [],
+ inline=True,
+ bodyInHeader=True,
+ visibility="public",
+ body=(
+ "%s::HoldJSObjectsIfMoreThanOneOwner();\n" %
+ baseName))
+
+ CGClass.__init__(self, "Fast%s" % baseName,
+ bases=[ClassBase(baseName)],
+ constructors=[constructor],
+ methods=[traceMethod, holdMethod])
+
+ def deps(self):
+ return self._deps
+
+
+class CGCallbackInterface(CGCallback):
+ def __init__(self, descriptor, typedArraysAreStructs=False):
+ iface = descriptor.interface
+ attrs = [m for m in iface.members if m.isAttr() and not m.isStatic()]
+ getters = [CallbackGetter(a, descriptor, typedArraysAreStructs)
+ for a in attrs]
+ setters = [CallbackSetter(a, descriptor, typedArraysAreStructs)
+ 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, typedArraysAreStructs)
+ for m in methods for sig in m.signatures()]
+ if iface.isJSImplemented() and iface.ctor():
+ sigs = descriptor.interface.ctor().signatures()
+ if len(sigs) != 1:
+ raise TypeError("We only handle one constructor. See bug 869268.")
+ methods.append(CGJSImplInitOperation(sigs[0], descriptor))
+ if any(m.isAttr() or m.isMethod() for m in iface.members) or (iface.isJSImplemented() and iface.ctor()):
+ methods.append(initIdsClassMethod([descriptor.binaryNameFor(m.identifier.name)
+ for m in iface.members
+ if m.isAttr() or m.isMethod()] +
+ (["__init"] if iface.isJSImplemented() and iface.ctor() else []),
+ iface.identifier.name + "Atoms"))
+ CGCallback.__init__(self, iface, descriptor, "CallbackInterface",
+ methods, getters=getters, setters=setters)
+
+
+class FakeMember():
+ def __init__(self, name=None):
+ self.treatNullAs = "Default"
+ if name is not None:
+ self.identifier = FakeIdentifier(name)
+
+ def isStatic(self):
+ return False
+
+ def isAttr(self):
+ return False
+
+ def isMethod(self):
+ return False
+
+ def getExtendedAttribute(self, name):
+ # Claim to be a [NewObject] so we can avoid the "return a raw pointer"
+ # comments CGNativeMember codegen would otherwise stick in.
+ if name == "NewObject":
+ return True
+ return None
+
+
+class CallbackMember(CGNativeMember):
+ # XXXbz It's OK to use CallbackKnownNotGray for wrapScope because
+ # CallSetup already handled the unmark-gray bits for us. we don't have
+ # anything better to use for 'obj', really...
+ def __init__(self, sig, name, descriptorProvider, needThisHandling,
+ rethrowContentException=False, typedArraysAreStructs=False,
+ wrapScope='CallbackKnownNotGray()'):
+ """
+ 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 = "private" if needThisHandling else "public"
+ self.rethrowContentException = rethrowContentException
+
+ self.wrapScope = wrapScope
+ # 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,
+ typedArraysAreStructs=typedArraysAreStructs)
+ # 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 = ("aRv.Throw(NS_ERROR_UNEXPECTED);\n"
+ "return%s;\n" % self.getDefaultRetval())
+ self.body = self.getImpl()
+
+ def getImpl(self):
+ setupCall = self.getCallSetup()
+ declRval = self.getRvalDecl()
+ if self.argCount > 0:
+ argvDecl = fill(
+ """
+ JS::AutoValueVector argv(cx);
+ if (!argv.resize(${argCount})) {
+ aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
+ return${errorReturn};
+ }
+ """,
+ argCount=self.argCountStr,
+ errorReturn=self.getDefaultRetval())
+ else:
+ # Avoid weird 0-sized arrays
+ argvDecl = ""
+ convertArgs = self.getArgConversions()
+ doCall = self.getCall()
+ returnResult = self.getResultConversion()
+
+ return setupCall + declRval + argvDecl + convertArgs + doCall + returnResult
+
+ def getResultConversion(self):
+ replacements = {
+ "val": "rval",
+ "holderName": "rvalHolder",
+ "declName": "rvalDecl",
+ # We actually want to pass in a null scope object here, because
+ # wrapping things into our current compartment (that of mCallback)
+ # is what we want.
+ "obj": "nullptr",
+ "passedToJSImpl": "false"
+ }
+
+ if isJSImplementedDescriptor(self.descriptorProvider):
+ isCallbackReturnValue = "JSImpl"
+ else:
+ isCallbackReturnValue = "Callback"
+ sourceDescription = "return value of %s" % self.getPrettyName()
+ convertType = instantiateJSToNativeConversion(
+ getJSToNativeConversionInfo(self.retvalType,
+ self.descriptorProvider,
+ exceptionCode=self.exceptionCode,
+ isCallbackReturnValue=isCallbackReturnValue,
+ # Allow returning a callback type that
+ # allows non-callable objects.
+ allowTreatNonCallableAsNull=True,
+ sourceDescription=sourceDescription),
+ replacements)
+ assignRetval = string.Template(
+ self.getRetvalInfo(self.retvalType,
+ False)[2]).substitute(replacements)
+ type = convertType.define()
+ return type + assignRetval
+
+ 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])]
+ if not argConversions:
+ return "\n"
+
+ # 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="do {\n",
+ post="} while (0);\n")
+ for c in argConversions]
+ if self.argCount > 0:
+ argConversions.insert(0, self.getArgcDecl())
+ # And slap them together.
+ return CGList(argConversions, "\n").define() + "\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.canHaveMissingValue():
+ argval += ".Value()"
+ if arg.type.isDOMString():
+ # XPConnect string-to-JS conversion wants to mutate the string. So
+ # let's give it a string it can mutate
+ # XXXbz if we try to do a sequence of strings, this will kinda fail.
+ result = "mutableStr"
+ prepend = "nsString mutableStr(%s);\n" % argval
+ else:
+ result = argval
+ prepend = ""
+
+ try:
+ conversion = prepend + wrapForType(
+ arg.type, self.descriptorProvider,
+ {
+ 'result': result,
+ 'successCode': "continue;\n" if arg.variadic else "break;\n",
+ 'jsvalRef': "argv[%s]" % jsvalIndex,
+ 'jsvalHandle': "argv[%s]" % jsvalIndex,
+ 'obj': self.wrapScope,
+ 'returnsNewObject': False,
+ 'exceptionCode': self.exceptionCode,
+ 'typedArraysAreStructs': self.typedArraysAreStructs
+ })
+ except MethodNotNewObjectError as err:
+ raise TypeError("%s being passed as an argument to %s but is not "
+ "wrapper cached, so can't be reliably converted to "
+ "a JS object." %
+ (err.typename, self.getPrettyName()))
+ if arg.variadic:
+ conversion = fill(
+ """
+ for (uint32_t idx = 0; idx < ${arg}.Length(); ++idx) {
+ $*{conversion}
+ }
+ break;
+ """,
+ arg=arg.identifier.name,
+ conversion=conversion)
+ elif arg.canHaveMissingValue():
+ conversion = fill(
+ """
+ if (${argName}.WasPassed()) {
+ $*{conversion}
+ } else if (argc == ${iPlus1}) {
+ // This is our current trailing argument; reduce argc
+ --argc;
+ } else {
+ argv[${i}].setUndefined();
+ }
+ """,
+ argName=arg.identifier.name,
+ conversion=conversion,
+ iPlus1=i + 1,
+ i=i)
+ return conversion
+
+ def getDefaultRetval(self):
+ default = self.getRetvalInfo(self.retvalType, False)[1]
+ if len(default) != 0:
+ default = " " + default
+ return default
+
+ 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 not self.rethrowContentException:
+ args.append(Argument("const char*", "aExecutionReason",
+ "nullptr"))
+ args.append(Argument("ExceptionHandling", "aExceptionHandling",
+ "eReportExceptions"))
+ args.append(Argument("JSCompartment*", "aCompartment", "nullptr"))
+ return args
+ # We want to allow the caller to pass in a "this" value, as
+ # well as a JSContext.
+ return [Argument("JSContext*", "cx"),
+ Argument("JS::Handle<JS::Value>", "aThisVal")] + args
+
+ def getCallSetup(self):
+ if self.needThisHandling:
+ # It's been done for us already
+ return ""
+ callSetup = "CallSetup s(this, aRv"
+ if self.rethrowContentException:
+ # getArgs doesn't add the aExceptionHandling argument but does add
+ # aCompartment for us.
+ callSetup += ', "%s", eRethrowContentExceptions, aCompartment, /* aIsJSImplementedWebIDL = */ ' % self.getPrettyName()
+ callSetup += toStringBool(isJSImplementedDescriptor(self.descriptorProvider))
+ else:
+ callSetup += ', "%s", aExceptionHandling, aCompartment' % self.getPrettyName()
+ callSetup += ");\n"
+ return fill(
+ """
+ $*{callSetup}
+ JSContext* cx = s.GetContext();
+ if (!cx) {
+ MOZ_ASSERT(aRv.Failed());
+ return${errorReturn};
+ }
+ """,
+ callSetup=callSetup,
+ errorReturn=self.getDefaultRetval())
+
+ def getArgcDecl(self):
+ return CGGeneric("unsigned argc = %s;\n" % 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, typedArraysAreStructs=False):
+ CallbackMember.__init__(self, sig, name, descriptorProvider,
+ needThisHandling, rethrowContentException,
+ typedArraysAreStructs=typedArraysAreStructs)
+
+ def getRvalDecl(self):
+ return "JS::Rooted<JS::Value> rval(cx, JS::UndefinedValue());\n"
+
+ def getCall(self):
+ if self.argCount > 0:
+ args = "JS::HandleValueArray::subarray(argv, 0, argc)"
+ else:
+ args = "JS::HandleValueArray::empty()"
+
+ return fill(
+ """
+ $*{declCallable}
+ $*{declThis}
+ if (${callGuard}!JS::Call(cx, ${thisVal}, callable,
+ ${args}, &rval)) {
+ aRv.NoteJSContextException(cx);
+ return${errorReturn};
+ }
+ """,
+ declCallable=self.getCallableDecl(),
+ declThis=self.getThisDecl(),
+ callGuard=self.getCallGuard(),
+ thisVal=self.getThisVal(),
+ args=args,
+ errorReturn=self.getDefaultRetval())
+
+
+class CallCallback(CallbackMethod):
+ def __init__(self, callback, descriptorProvider):
+ self.callback = callback
+ CallbackMethod.__init__(self, callback.signatures()[0], "Call",
+ descriptorProvider, needThisHandling=True)
+
+ def getThisDecl(self):
+ return ""
+
+ def getThisVal(self):
+ return "aThisVal"
+
+ def getCallableDecl(self):
+ return "JS::Rooted<JS::Value> callable(cx, JS::ObjectValue(*mCallback));\n"
+
+ def getPrettyName(self):
+ return self.callback.identifier.name
+
+ def getCallGuard(self):
+ if self.callback._treatNonObjectAsNull:
+ return "JS::IsCallable(mCallback) && "
+ return ""
+
+
+class CallbackOperationBase(CallbackMethod):
+ """
+ Common class for implementing various callback operations.
+ """
+ def __init__(self, signature, jsName, nativeName, descriptor,
+ singleOperation, rethrowContentException=False,
+ typedArraysAreStructs=False):
+ self.singleOperation = singleOperation
+ self.methodName = descriptor.binaryNameFor(jsName)
+ CallbackMethod.__init__(self, signature, nativeName, descriptor,
+ singleOperation, rethrowContentException,
+ typedArraysAreStructs=typedArraysAreStructs)
+
+ def getThisDecl(self):
+ if not self.singleOperation:
+ return "JS::Rooted<JS::Value> thisValue(cx, JS::ObjectValue(*mCallback));\n"
+ # This relies on getCallableDecl declaring a boolean
+ # isCallable in the case when we're a single-operation
+ # interface.
+ return dedent("""
+ JS::Rooted<JS::Value> thisValue(cx, isCallable ? aThisVal.get()
+ : JS::ObjectValue(*mCallback));
+ """)
+
+ def getThisVal(self):
+ return "thisValue"
+
+ def getCallableDecl(self):
+ getCallableFromProp = fill(
+ """
+ ${atomCacheName}* atomsCache = GetAtomCache<${atomCacheName}>(cx);
+ if ((!*reinterpret_cast<jsid**>(atomsCache) && !InitIds(cx, atomsCache)) ||
+ !GetCallableProperty(cx, atomsCache->${methodAtomName}, &callable)) {
+ aRv.Throw(NS_ERROR_UNEXPECTED);
+ return${errorReturn};
+ }
+ """,
+ methodAtomName=CGDictionary.makeIdName(self.methodName),
+ atomCacheName=self.descriptorProvider.interface.identifier.name + "Atoms",
+ errorReturn=self.getDefaultRetval())
+ if not self.singleOperation:
+ return 'JS::Rooted<JS::Value> callable(cx);\n' + getCallableFromProp
+ return fill(
+ """
+ bool isCallable = JS::IsCallable(mCallback);
+ JS::Rooted<JS::Value> callable(cx);
+ if (isCallable) {
+ callable = JS::ObjectValue(*mCallback);
+ } else {
+ $*{getCallableFromProp}
+ }
+ """,
+ getCallableFromProp=getCallableFromProp)
+
+ def getCallGuard(self):
+ return ""
+
+
+class CallbackOperation(CallbackOperationBase):
+ """
+ Codegen actual WebIDL operations on callback interfaces.
+ """
+ def __init__(self, method, signature, descriptor, typedArraysAreStructs):
+ self.ensureASCIIName(method)
+ self.method = method
+ jsName = method.identifier.name
+ CallbackOperationBase.__init__(self, signature,
+ jsName,
+ MakeNativeName(descriptor.binaryNameFor(jsName)),
+ descriptor, descriptor.interface.isSingleOperationInterface(),
+ rethrowContentException=descriptor.interface.isJSImplemented(),
+ typedArraysAreStructs=typedArraysAreStructs)
+
+ def getPrettyName(self):
+ return "%s.%s" % (self.descriptorProvider.interface.identifier.name,
+ self.method.identifier.name)
+
+
+class CallbackAccessor(CallbackMember):
+ """
+ Shared superclass for CallbackGetter and CallbackSetter.
+ """
+ def __init__(self, attr, sig, name, descriptor, typedArraysAreStructs):
+ self.ensureASCIIName(attr)
+ self.attrName = attr.identifier.name
+ CallbackMember.__init__(self, sig, name, descriptor,
+ needThisHandling=False,
+ rethrowContentException=descriptor.interface.isJSImplemented(),
+ typedArraysAreStructs=typedArraysAreStructs)
+
+ def getPrettyName(self):
+ return "%s.%s" % (self.descriptorProvider.interface.identifier.name,
+ self.attrName)
+
+
+class CallbackGetter(CallbackAccessor):
+ def __init__(self, attr, descriptor, typedArraysAreStructs):
+ CallbackAccessor.__init__(self, attr,
+ (attr.type, []),
+ callbackGetterName(attr, descriptor),
+ descriptor,
+ typedArraysAreStructs)
+
+ def getRvalDecl(self):
+ return "JS::Rooted<JS::Value> rval(cx, JS::UndefinedValue());\n"
+
+ def getCall(self):
+ return fill(
+ """
+ JS::Rooted<JSObject *> callback(cx, mCallback);
+ ${atomCacheName}* atomsCache = GetAtomCache<${atomCacheName}>(cx);
+ if ((!*reinterpret_cast<jsid**>(atomsCache) && !InitIds(cx, atomsCache)) ||
+ !JS_GetPropertyById(cx, callback, atomsCache->${attrAtomName}, &rval)) {
+ aRv.Throw(NS_ERROR_UNEXPECTED);
+ return${errorReturn};
+ }
+ """,
+ atomCacheName=self.descriptorProvider.interface.identifier.name + "Atoms",
+ attrAtomName=CGDictionary.makeIdName(self.descriptorProvider.binaryNameFor(self.attrName)),
+ errorReturn=self.getDefaultRetval())
+
+
+class CallbackSetter(CallbackAccessor):
+ def __init__(self, attr, descriptor, typedArraysAreStructs):
+ CallbackAccessor.__init__(self, attr,
+ (BuiltinTypes[IDLBuiltinType.Types.void],
+ [FakeArgument(attr.type, attr)]),
+ callbackSetterName(attr, descriptor),
+ descriptor, typedArraysAreStructs)
+
+ def getRvalDecl(self):
+ # We don't need an rval
+ return ""
+
+ def getCall(self):
+ return fill(
+ """
+ MOZ_ASSERT(argv.length() == 1);
+ ${atomCacheName}* atomsCache = GetAtomCache<${atomCacheName}>(cx);
+ if ((!*reinterpret_cast<jsid**>(atomsCache) && !InitIds(cx, atomsCache)) ||
+ !JS_SetPropertyById(cx, CallbackKnownNotGray(), atomsCache->${attrAtomName}, argv[0])) {
+ aRv.Throw(NS_ERROR_UNEXPECTED);
+ return${errorReturn};
+ }
+ """,
+ atomCacheName=self.descriptorProvider.interface.identifier.name + "Atoms",
+ attrAtomName=CGDictionary.makeIdName(self.descriptorProvider.binaryNameFor(self.attrName)),
+ errorReturn=self.getDefaultRetval())
+
+ def getArgcDecl(self):
+ return None
+
+
+class CGJSImplInitOperation(CallbackOperationBase):
+ """
+ Codegen the __Init() method used to pass along constructor arguments for JS-implemented WebIDL.
+ """
+ def __init__(self, sig, descriptor):
+ assert sig in descriptor.interface.ctor().signatures()
+ CallbackOperationBase.__init__(self, (BuiltinTypes[IDLBuiltinType.Types.void], sig[1]),
+ "__init", "__Init", descriptor,
+ singleOperation=False,
+ rethrowContentException=True,
+ typedArraysAreStructs=True)
+
+ def getPrettyName(self):
+ return "__init"
+
+
+def getMaplikeOrSetlikeErrorReturn(helperImpl):
+ """
+ Generate return values based on whether a maplike or setlike generated
+ method is an interface method (which returns bool) or a helper function
+ (which uses ErrorResult).
+ """
+ if helperImpl:
+ return dedent(
+ """
+ aRv.Throw(NS_ERROR_UNEXPECTED);
+ return%s;
+ """ % helperImpl.getDefaultRetval())
+ return "return false;\n"
+
+
+def getMaplikeOrSetlikeBackingObject(descriptor, maplikeOrSetlike, helperImpl=None):
+ """
+ Generate code to get/create a JS backing object for a maplike/setlike
+ declaration from the declaration slot.
+ """
+ func_prefix = maplikeOrSetlike.maplikeOrSetlikeOrIterableType.title()
+ ret = fill(
+ """
+ JS::Rooted<JSObject*> backingObj(cx);
+ bool created = false;
+ if (!Get${func_prefix}BackingObject(cx, obj, ${slot}, &backingObj, &created)) {
+ $*{errorReturn}
+ }
+ if (created) {
+ PreserveWrapper<${selfType}>(self);
+ }
+ """,
+ slot=memberReservedSlot(maplikeOrSetlike, descriptor),
+ func_prefix=func_prefix,
+ errorReturn=getMaplikeOrSetlikeErrorReturn(helperImpl),
+ selfType=descriptor.nativeType)
+ return ret
+
+
+def getMaplikeOrSetlikeSizeGetterBody(descriptor, attr):
+ """
+ Creates the body for the size getter method of maplike/setlike interfaces.
+ """
+ # We should only have one declaration attribute currently
+ assert attr.identifier.name == "size"
+ assert attr.isMaplikeOrSetlikeAttr()
+ return fill(
+ """
+ $*{getBackingObj}
+ uint32_t result = JS::${funcPrefix}Size(cx, backingObj);
+ MOZ_ASSERT(!JS_IsExceptionPending(cx));
+ args.rval().setNumber(result);
+ return true;
+ """,
+ getBackingObj=getMaplikeOrSetlikeBackingObject(descriptor,
+ attr.maplikeOrSetlike),
+ funcPrefix=attr.maplikeOrSetlike.prefix)
+
+
+class CGMaplikeOrSetlikeMethodGenerator(CGThing):
+ """
+ Creates methods for maplike/setlike interfaces. It is expected that all
+ methods will be have a maplike/setlike object attached. Unwrapping/wrapping
+ will be taken care of by the usual method generation machinery in
+ CGMethodCall/CGPerSignatureCall. Functionality is filled in here instead of
+ using CGCallGenerator.
+ """
+ def __init__(self, descriptor, maplikeOrSetlike, methodName,
+ helperImpl=None):
+ CGThing.__init__(self)
+ # True if this will be the body of a C++ helper function.
+ self.helperImpl = helperImpl
+ self.descriptor = descriptor
+ self.maplikeOrSetlike = maplikeOrSetlike
+ self.cgRoot = CGList([])
+ impl_method_name = methodName
+ if impl_method_name[0] == "_":
+ # double underscore means this is a js-implemented chrome only rw
+ # function. Truncate the double underscore so calling the right
+ # underlying JSAPI function still works.
+ impl_method_name = impl_method_name[2:]
+ self.cgRoot.append(CGGeneric(
+ getMaplikeOrSetlikeBackingObject(self.descriptor,
+ self.maplikeOrSetlike,
+ self.helperImpl)))
+ self.returnStmt = getMaplikeOrSetlikeErrorReturn(self.helperImpl)
+
+ # Generates required code for the method. Method descriptions included
+ # in definitions below. Throw if we don't have a method to fill in what
+ # we're looking for.
+ try:
+ methodGenerator = getattr(self, impl_method_name)
+ except AttributeError:
+ raise TypeError("Missing %s method definition '%s'" %
+ (self.maplikeOrSetlike.maplikeOrSetlikeType,
+ methodName))
+ # Method generator returns tuple, containing:
+ #
+ # - a list of CGThings representing setup code for preparing to call
+ # the JS API function
+ # - a list of arguments needed for the JS API function we're calling
+ # - list of code CGThings needed for return value conversion.
+ (setupCode, arguments, setResult) = methodGenerator()
+
+ # Create the actual method call, and then wrap it with the code to
+ # return the value if needed.
+ funcName = (self.maplikeOrSetlike.prefix +
+ MakeNativeName(impl_method_name))
+ # Append the list of setup code CGThings
+ self.cgRoot.append(CGList(setupCode))
+ # Create the JS API call
+ self.cgRoot.append(CGWrapper(
+ CGGeneric(fill(
+ """
+ if (!JS::${funcName}(${args})) {
+ $*{errorReturn}
+ }
+ """,
+ funcName=funcName,
+ args=", ".join(["cx", "backingObj"] + arguments),
+ errorReturn=self.returnStmt))))
+ # Append result conversion
+ self.cgRoot.append(CGList(setResult))
+
+ def mergeTuples(self, a, b):
+ """
+ Expecting to take 2 tuples were all elements are lists, append the lists in
+ the second tuple to the lists in the first.
+ """
+ return tuple([x + y for x, y in zip(a, b)])
+
+ def appendArgConversion(self, name):
+ """
+ Generate code to convert arguments to JS::Values, so they can be
+ passed into JSAPI functions.
+ """
+ return CGGeneric(fill(
+ """
+ JS::Rooted<JS::Value> ${name}Val(cx);
+ if (!ToJSValue(cx, ${name}, &${name}Val)) {
+ $*{errorReturn}
+ }
+ """,
+ name=name,
+ errorReturn=self.returnStmt))
+
+ def appendKeyArgConversion(self):
+ """
+ Generates the key argument for methods. Helper functions will use
+ an AutoValueVector, while interface methods have seperate JS::Values.
+ """
+ if self.helperImpl:
+ return ([], ["argv[0]"], [])
+ return ([self.appendArgConversion("arg0")], ["arg0Val"], [])
+
+ def appendKeyAndValueArgConversion(self):
+ """
+ Generates arguments for methods that require a key and value. Helper
+ functions will use an AutoValueVector, while interface methods have
+ seperate JS::Values.
+ """
+ r = self.appendKeyArgConversion()
+ if self.helperImpl:
+ return self.mergeTuples(r, ([], ["argv[1]"], []))
+ return self.mergeTuples(r, ([self.appendArgConversion("arg1")],
+ ["arg1Val"],
+ []))
+
+ def appendIteratorResult(self):
+ """
+ Generate code to output JSObject* return values, needed for functions that
+ return iterators. Iterators cannot currently be wrapped via Xrays. If
+ something that would return an iterator is called via Xray, fail early.
+ """
+ # TODO: Bug 1173651 - Remove check once bug 1023984 is fixed.
+ code = CGGeneric(dedent(
+ """
+ // TODO (Bug 1173651): Xrays currently cannot wrap iterators. Change
+ // after bug 1023984 is fixed.
+ if (xpc::WrapperFactory::IsXrayWrapper(obj)) {
+ JS_ReportErrorASCII(cx, "Xray wrapping of iterators not supported.");
+ return false;
+ }
+ JS::Rooted<JSObject*> result(cx);
+ JS::Rooted<JS::Value> v(cx);
+ """))
+ arguments = "&v"
+ setResult = CGGeneric(dedent(
+ """
+ result = &v.toObject();
+ """))
+ return ([code], [arguments], [setResult])
+
+ def appendSelfResult(self):
+ """
+ Generate code to return the interface object itself.
+ """
+ code = CGGeneric(dedent(
+ """
+ JS::Rooted<JSObject*> result(cx);
+ """))
+ setResult = CGGeneric(dedent(
+ """
+ result = obj;
+ """))
+ return ([code], [], [setResult])
+
+ def appendBoolResult(self):
+ if self.helperImpl:
+ return ([CGGeneric()], ["&aRetVal"], [])
+ return ([CGGeneric("bool result;\n")], ["&result"], [])
+
+ def forEach(self):
+ """
+ void forEach(callback c, any thisval);
+
+ ForEach takes a callback, and a possible value to use as 'this'. The
+ callback needs to take value, key, and the interface object
+ implementing maplike/setlike. In order to make sure that the third arg
+ is our interface object instead of the map/set backing object, we
+ create a js function with the callback and original object in its
+ storage slots, then use a helper function in BindingUtils to make sure
+ the callback is called correctly.
+ """
+ assert(not self.helperImpl)
+ code = [CGGeneric(dedent(
+ """
+ // Create a wrapper function.
+ JSFunction* func = js::NewFunctionWithReserved(cx, ForEachHandler, 3, 0, nullptr);
+ if (!func) {
+ return false;
+ }
+ JS::Rooted<JSObject*> funcObj(cx, JS_GetFunctionObject(func));
+ JS::Rooted<JS::Value> funcVal(cx, JS::ObjectValue(*funcObj));
+ js::SetFunctionNativeReserved(funcObj, FOREACH_CALLBACK_SLOT,
+ JS::ObjectValue(*arg0));
+ js::SetFunctionNativeReserved(funcObj, FOREACH_MAPLIKEORSETLIKEOBJ_SLOT,
+ JS::ObjectValue(*obj));
+ """))]
+ arguments = ["funcVal", "arg1"]
+ return (code, arguments, [])
+
+ def set(self):
+ """
+ object set(key, value);
+
+ Maplike only function, takes key and sets value to it, returns
+ interface object unless being called from a C++ helper.
+ """
+ assert self.maplikeOrSetlike.isMaplike()
+ r = self.appendKeyAndValueArgConversion()
+ if self.helperImpl:
+ return r
+ return self.mergeTuples(r, self.appendSelfResult())
+
+ def add(self):
+ """
+ object add(value);
+
+ Setlike only function, adds value to set, returns interface object
+ unless being called from a C++ helper
+ """
+ assert self.maplikeOrSetlike.isSetlike()
+ r = self.appendKeyArgConversion()
+ if self.helperImpl:
+ return r
+ return self.mergeTuples(r, self.appendSelfResult())
+
+ def get(self):
+ """
+ type? get(key);
+
+ Retrieves a value from a backing object based on the key. Returns value
+ if key is in backing object, undefined otherwise.
+ """
+ assert self.maplikeOrSetlike.isMaplike()
+ r = self.appendKeyArgConversion()
+ code = [CGGeneric(dedent(
+ """
+ JS::Rooted<JS::Value> result(cx);
+ """))]
+ arguments = ["&result"]
+ return self.mergeTuples(r, (code, arguments, []))
+
+ def has(self):
+ """
+ bool has(key);
+
+ Check if an entry exists in the backing object. Returns true if value
+ exists in backing object, false otherwise.
+ """
+ return self.mergeTuples(self.appendKeyArgConversion(),
+ self.appendBoolResult())
+
+ def keys(self):
+ """
+ object keys();
+
+ Returns new object iterator with all keys from backing object.
+ """
+ return self.appendIteratorResult()
+
+ def values(self):
+ """
+ object values();
+
+ Returns new object iterator with all values from backing object.
+ """
+ return self.appendIteratorResult()
+
+ def entries(self):
+ """
+ object entries();
+
+ Returns new object iterator with all keys and values from backing
+ object. Keys will be null for set.
+ """
+ return self.appendIteratorResult()
+
+ def clear(self):
+ """
+ void clear();
+
+ Removes all entries from map/set.
+ """
+ return ([], [], [])
+
+ def delete(self):
+ """
+ bool delete(key);
+
+ Deletes an entry from the backing object. Returns true if value existed
+ in backing object, false otherwise.
+ """
+ return self.mergeTuples(self.appendKeyArgConversion(),
+ self.appendBoolResult())
+
+ def define(self):
+ return self.cgRoot.define()
+
+
+class CGMaplikeOrSetlikeHelperFunctionGenerator(CallbackMember):
+ """
+ Generates code to allow C++ to perform operations on backing objects. Gets
+ a context from the binding wrapper, turns arguments into JS::Values (via
+ CallbackMember/CGNativeMember argument conversion), then uses
+ CGMaplikeOrSetlikeMethodGenerator to generate the body.
+
+ """
+
+ class HelperFunction(CGAbstractMethod):
+ """
+ Generates context retrieval code and rooted JSObject for interface for
+ CGMaplikeOrSetlikeMethodGenerator to use
+ """
+ def __init__(self, descriptor, name, args, code, needsBoolReturn=False):
+ self.code = code
+ CGAbstractMethod.__init__(self, descriptor, name,
+ "bool" if needsBoolReturn else "void",
+ args)
+
+ def definition_body(self):
+ return self.code
+
+ def __init__(self, descriptor, maplikeOrSetlike, name, needsKeyArg=False,
+ needsValueArg=False, needsBoolReturn=False):
+ args = []
+ self.maplikeOrSetlike = maplikeOrSetlike
+ self.needsBoolReturn = needsBoolReturn
+ if needsKeyArg:
+ args.append(FakeArgument(maplikeOrSetlike.keyType, None, 'aKey'))
+ if needsValueArg:
+ assert needsKeyArg
+ args.append(FakeArgument(maplikeOrSetlike.valueType, None, 'aValue'))
+ # Run CallbackMember init function to generate argument conversion code.
+ # wrapScope is set to 'obj' when generating maplike or setlike helper
+ # functions, as we don't have access to the CallbackPreserveColor
+ # method.
+ CallbackMember.__init__(self,
+ [BuiltinTypes[IDLBuiltinType.Types.void], args],
+ name, descriptor, False,
+ wrapScope='obj')
+ # Wrap CallbackMember body code into a CGAbstractMethod to make
+ # generation easier.
+ self.implMethod = CGMaplikeOrSetlikeHelperFunctionGenerator.HelperFunction(
+ descriptor, name, self.args, self.body, needsBoolReturn)
+
+ def getCallSetup(self):
+ return dedent(
+ """
+ MOZ_ASSERT(self);
+ AutoJSAPI jsapi;
+ jsapi.Init();
+ JSContext* cx = jsapi.cx();
+ // It's safe to use UnprivilegedJunkScopeOrWorkerGlobal here because
+ // all we want is to wrap into _some_ scope and then unwrap to find
+ // the reflector, and wrapping has no side-effects.
+ JSAutoCompartment tempCompartment(cx, binding_detail::UnprivilegedJunkScopeOrWorkerGlobal());
+ JS::Rooted<JS::Value> v(cx);
+ if(!ToJSValue(cx, self, &v)) {
+ aRv.Throw(NS_ERROR_UNEXPECTED);
+ return%s;
+ }
+ // This is a reflector, but due to trying to name things
+ // similarly across method generators, it's called obj here.
+ JS::Rooted<JSObject*> obj(cx);
+ obj = js::UncheckedUnwrap(&v.toObject(), /* stopAtWindowProxy = */ false);
+ JSAutoCompartment reflectorCompartment(cx, obj);
+ """ % self.getDefaultRetval())
+
+ def getArgs(self, returnType, argList):
+ # We don't need the context or the value. We'll generate those instead.
+ args = CGNativeMember.getArgs(self, returnType, argList)
+ # Prepend a pointer to the binding object onto the arguments
+ return [Argument(self.descriptorProvider.nativeType + "*", "self")] + args
+
+ def getResultConversion(self):
+ if self.needsBoolReturn:
+ return "return aRetVal;\n"
+ return "return;\n"
+
+ def getRvalDecl(self):
+ if self.needsBoolReturn:
+ return "bool aRetVal;\n"
+ return ""
+
+ def getArgcDecl(self):
+ # Don't need argc for anything.
+ return None
+
+ def getDefaultRetval(self):
+ if self.needsBoolReturn:
+ return " false"
+ return ""
+
+ def getCall(self):
+ return CGMaplikeOrSetlikeMethodGenerator(self.descriptorProvider,
+ self.maplikeOrSetlike,
+ self.name.lower(),
+ helperImpl=self).define()
+
+ def getPrettyName(self):
+ return self.name
+
+ def declare(self):
+ return self.implMethod.declare()
+
+ def define(self):
+ return self.implMethod.define()
+
+
+class CGMaplikeOrSetlikeHelperGenerator(CGNamespace):
+ """
+ Declares and defines convenience methods for accessing backing objects on
+ setlike/maplike interface. Generates function signatures, un/packs
+ backing objects from slot, etc.
+ """
+ def __init__(self, descriptor, maplikeOrSetlike):
+ self.descriptor = descriptor
+ # Since iterables are folded in with maplike/setlike, make sure we've
+ # got the right type here.
+ assert maplikeOrSetlike.isMaplike() or maplikeOrSetlike.isSetlike()
+ self.maplikeOrSetlike = maplikeOrSetlike
+ self.namespace = "%sHelpers" % (self.maplikeOrSetlike.maplikeOrSetlikeOrIterableType.title())
+ self.helpers = [
+ CGMaplikeOrSetlikeHelperFunctionGenerator(descriptor,
+ maplikeOrSetlike,
+ "Clear"),
+ CGMaplikeOrSetlikeHelperFunctionGenerator(descriptor,
+ maplikeOrSetlike,
+ "Delete",
+ needsKeyArg=True,
+ needsBoolReturn=True),
+ CGMaplikeOrSetlikeHelperFunctionGenerator(descriptor,
+ maplikeOrSetlike,
+ "Has",
+ needsKeyArg=True,
+ needsBoolReturn=True)]
+ if self.maplikeOrSetlike.isMaplike():
+ self.helpers.append(
+ CGMaplikeOrSetlikeHelperFunctionGenerator(descriptor,
+ maplikeOrSetlike,
+ "Set",
+ needsKeyArg=True,
+ needsValueArg=True))
+ else:
+ assert(self.maplikeOrSetlike.isSetlike())
+ self.helpers.append(
+ CGMaplikeOrSetlikeHelperFunctionGenerator(descriptor,
+ maplikeOrSetlike,
+ "Add",
+ needsKeyArg=True))
+ CGNamespace.__init__(self, self.namespace, CGList(self.helpers))
+
+
+class CGIterableMethodGenerator(CGGeneric):
+ """
+ Creates methods for iterable interfaces. Unwrapping/wrapping
+ will be taken care of by the usual method generation machinery in
+ CGMethodCall/CGPerSignatureCall. Functionality is filled in here instead of
+ using CGCallGenerator.
+ """
+ def __init__(self, descriptor, iterable, methodName):
+ if methodName == "forEach":
+ CGGeneric.__init__(self, fill(
+ """
+ if (!JS::IsCallable(arg0)) {
+ ThrowErrorMessage(cx, MSG_NOT_CALLABLE, "Argument 1 of ${ifaceName}.forEach");
+ return false;
+ }
+ JS::AutoValueArray<3> callArgs(cx);
+ callArgs[2].setObject(*obj);
+ JS::Rooted<JS::Value> ignoredReturnVal(cx);
+ for (size_t i = 0; i < self->GetIterableLength(); ++i) {
+ if (!ToJSValue(cx, self->GetValueAtIndex(i), callArgs[0])) {
+ return false;
+ }
+ if (!ToJSValue(cx, self->GetKeyAtIndex(i), callArgs[1])) {
+ return false;
+ }
+ if (!JS::Call(cx, arg1, arg0, JS::HandleValueArray(callArgs),
+ &ignoredReturnVal)) {
+ return false;
+ }
+ }
+ """,
+ ifaceName=descriptor.interface.identifier.name))
+ return
+ CGGeneric.__init__(self, fill(
+ """
+ typedef ${iterClass} itrType;
+ RefPtr<itrType> result(new itrType(self,
+ itrType::IterableIteratorType::${itrMethod},
+ &${ifaceName}IteratorBinding::Wrap));
+ """,
+ iterClass=iteratorNativeType(descriptor),
+ ifaceName=descriptor.interface.identifier.name,
+ itrMethod=methodName.title()))
+
+
+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 GeneratedAtomList(config):
+ # Atom enum
+ dictionaries = config.dictionaries
+
+ structs = []
+
+ def memberToAtomCacheMember(binaryNameFor, m):
+ binaryMemberName = binaryNameFor(m.identifier.name)
+ return ClassMember(CGDictionary.makeIdName(binaryMemberName),
+ "PinnedStringId", visibility="public")
+
+ def buildAtomCacheStructure(idlobj, binaryNameFor, members):
+ classMembers = [memberToAtomCacheMember(binaryNameFor, m)
+ for m in members]
+ structName = idlobj.identifier.name + "Atoms"
+ return (structName,
+ CGWrapper(CGClass(structName,
+ bases=None,
+ isStruct=True,
+ members=classMembers), post='\n'))
+
+ for dict in dictionaries:
+ if len(dict.members) == 0:
+ continue
+
+ structs.append(buildAtomCacheStructure(dict, lambda x: x, dict.members))
+
+ for d in (config.getDescriptors(isJSImplemented=True) +
+ config.getDescriptors(isCallback=True)):
+ members = [m for m in d.interface.members if m.isAttr() or m.isMethod()]
+ if d.interface.isJSImplemented() and d.interface.ctor():
+ # We'll have an __init() method.
+ members.append(FakeMember('__init'))
+ if len(members) == 0:
+ continue
+
+ structs.append(buildAtomCacheStructure(d.interface,
+ lambda x: d.binaryNameFor(x),
+ members))
+
+ structs.sort()
+ generatedStructs = [struct for structName, struct in structs]
+ structNames = [structName for structName, struct in structs]
+
+ mainStruct = CGWrapper(CGClass("PerThreadAtomCache",
+ bases=[ClassBase(structName) for structName in structNames],
+ isStruct=True),
+ post='\n')
+
+ structs = CGList(generatedStructs + [mainStruct])
+
+ # Wrap all of that in our namespaces.
+ curr = CGNamespace.build(['mozilla', 'dom'],
+ CGWrapper(structs, pre='\n'))
+ curr = CGWrapper(curr, post='\n')
+
+ # Add include statement for PinnedStringId.
+ declareIncludes = ['mozilla/dom/BindingUtils.h']
+ curr = CGHeaders([], [], [], [], declareIncludes, [], 'GeneratedAtomList',
+ curr)
+
+ # Add include guards.
+ curr = CGIncludeGuard('GeneratedAtomList', curr)
+
+ # Add the auto-generated comment.
+ curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
+
+ # Done.
+ return curr
+
+ @staticmethod
+ def GeneratedEventList(config):
+ eventList = CGList([])
+ for generatedEvent in config.generatedEvents:
+ eventList.append(CGGeneric(declare=("GENERATED_EVENT(%s)\n" % generatedEvent)))
+ return eventList
+
+ @staticmethod
+ def PrototypeList(config):
+
+ # Prototype ID enum.
+ descriptorsWithPrototype = config.getDescriptors(hasInterfacePrototypeObject=True)
+ protos = [d.name for d in descriptorsWithPrototype]
+ idEnum = CGNamespacedEnum('id', 'ID', ['_ID_Start'] + protos,
+ [0, '_ID_Start'])
+ idEnum = CGList([idEnum])
+
+ def fieldSizeAssert(amount, jitInfoField, message):
+ maxFieldValue = "(uint64_t(1) << (sizeof(((JSJitInfo*)nullptr)->%s) * 8))" % jitInfoField
+ return CGGeneric(declare="static_assert(%s < %s, \"%s\");\n\n"
+ % (amount, maxFieldValue, message))
+
+ idEnum.append(fieldSizeAssert("id::_ID_Count", "protoID",
+ "Too many prototypes!"))
+
+ # Wrap all of that in our namespaces.
+ idEnum = CGNamespace.build(['mozilla', 'dom', 'prototypes'],
+ CGWrapper(idEnum, pre='\n'))
+ idEnum = CGWrapper(idEnum, post='\n')
+
+ curr = CGList([CGGeneric(define="#include <stdint.h>\n\n"),
+ idEnum])
+
+ # Let things know the maximum length of the prototype chain.
+ maxMacroName = "MAX_PROTOTYPE_CHAIN_LENGTH"
+ maxMacro = CGGeneric(declare="#define " + maxMacroName + " " + str(config.maxProtoChainLength))
+ curr.append(CGWrapper(maxMacro, post='\n\n'))
+ curr.append(fieldSizeAssert(maxMacroName, "depth",
+ "Some inheritance chain is too long!"))
+
+ # Constructor ID enum.
+ constructors = [d.name for d in config.getDescriptors(hasInterfaceObject=True)]
+ idEnum = CGNamespacedEnum('id', 'ID', ['_ID_Start'] + constructors,
+ ['prototypes::id::_ID_Count', '_ID_Start'])
+
+ # 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)
+
+ # Named properties object enum.
+ namedPropertiesObjects = [d.name for d in config.getDescriptors(hasNamedPropertiesObject=True)]
+ idEnum = CGNamespacedEnum('id', 'ID', ['_ID_Start'] + namedPropertiesObjects,
+ ['constructors::id::_ID_Count', '_ID_Start'])
+
+ # Wrap all of that in our namespaces.
+ idEnum = CGNamespace.build(['mozilla', 'dom', 'namedpropertiesobjects'],
+ CGWrapper(idEnum, pre='\n'))
+ idEnum = CGWrapper(idEnum, post='\n')
+
+ curr.append(idEnum)
+
+ traitsDecls = [CGGeneric(declare=dedent("""
+ template <prototypes::ID PrototypeID>
+ struct PrototypeTraits;
+ """))]
+ traitsDecls.extend(CGPrototypeTraitsClass(d) for d in descriptorsWithPrototype)
+
+ ifaceNamesWithProto = [d.interface.identifier.name
+ for d in descriptorsWithPrototype]
+ traitsDecls.append(CGStringTable("NamesOfInterfacesWithProtos",
+ ifaceNamesWithProto))
+
+ traitsDecl = CGNamespace.build(['mozilla', 'dom'],
+ CGList(traitsDecls))
+
+ 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):
+
+ curr = CGList([CGGlobalNamesString(config), CGRegisterGlobalNames(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,
+ isExposedInWindow=True,
+ register=True)]
+ defineIncludes.append('mozilla/dom/WebIDLGlobalNameHash.h')
+ defineIncludes.extend([CGHeaders.getDeclarationFilename(desc.interface)
+ for desc in config.getDescriptors(isNavigatorProperty=True,
+ register=True)])
+ curr = CGHeaders([], [], [], [], [], defineIncludes, 'RegisterBindings',
+ curr)
+
+ # Add include guards.
+ curr = CGIncludeGuard('RegisterBindings', curr)
+
+ # Done.
+ return curr
+
+ @staticmethod
+ def RegisterWorkerBindings(config):
+
+ curr = CGRegisterWorkerBindings(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,
+ register=True,
+ isExposedInAnyWorker=True)]
+
+ curr = CGHeaders([], [], [], [], [], defineIncludes,
+ 'RegisterWorkerBindings', curr)
+
+ # Add include guards.
+ curr = CGIncludeGuard('RegisterWorkerBindings', curr)
+
+ # Done.
+ return curr
+
+ @staticmethod
+ def RegisterWorkerDebuggerBindings(config):
+
+ curr = CGRegisterWorkerDebuggerBindings(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,
+ register=True,
+ isExposedInWorkerDebugger=True)]
+
+ curr = CGHeaders([], [], [], [], [], defineIncludes,
+ 'RegisterWorkerDebuggerBindings', curr)
+
+ # Add include guards.
+ curr = CGIncludeGuard('RegisterWorkerDebuggerBindings', curr)
+
+ # Done.
+ return curr
+
+ @staticmethod
+ def RegisterWorkletBindings(config):
+
+ curr = CGRegisterWorkletBindings(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,
+ register=True,
+ isExposedInAnyWorklet=True)]
+
+ curr = CGHeaders([], [], [], [], [], defineIncludes,
+ 'RegisterWorkletBindings', curr)
+
+ # Add include guards.
+ curr = CGIncludeGuard('RegisterWorkletBindings', curr)
+
+ # Done.
+ return curr
+
+ @staticmethod
+ def ResolveSystemBinding(config):
+
+ curr = CGResolveSystemBinding(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,
+ register=True,
+ isExposedInSystemGlobals=True)]
+ defineIncludes.append("nsThreadUtils.h") # For NS_IsMainThread
+ defineIncludes.append("js/Id.h") # For jsid
+ defineIncludes.append("mozilla/dom/BindingUtils.h") # AtomizeAndPinJSString
+
+ curr = CGHeaders([], [], [], [], [], defineIncludes,
+ 'ResolveSystemBinding', curr)
+
+ # Add include guards.
+ curr = CGIncludeGuard('ResolveSystemBinding', curr)
+
+ # Done.
+ return curr
+
+ @staticmethod
+ def UnionTypes(config):
+ unionTypes = UnionsForFile(config, None)
+ (includes, implincludes, declarations,
+ traverseMethods, unlinkMethods,
+ unionStructs) = UnionTypes(unionTypes, config)
+
+ unions = CGList(traverseMethods +
+ unlinkMethods +
+ [CGUnionStruct(t, config) for t in unionStructs] +
+ [CGUnionStruct(t, config, True) for t in unionStructs],
+ "\n")
+
+ includes.add("mozilla/OwningNonNull.h")
+ includes.add("mozilla/dom/UnionMember.h")
+ includes.add("mozilla/dom/BindingDeclarations.h")
+ # BindingUtils.h is only needed for SetToObject.
+ # If it stops being inlined or stops calling CallerSubsumes
+ # both this bit and the bit in CGBindingRoot can be removed.
+ includes.add("mozilla/dom/BindingUtils.h")
+ implincludes.add("mozilla/dom/PrimitiveConversions.h")
+
+ # Wrap all of that in our namespaces.
+ curr = CGNamespace.build(['mozilla', 'dom'], unions)
+
+ curr = CGWrapper(curr, post='\n')
+
+ builder = ForwardDeclarationBuilder()
+ for className, isStruct in declarations:
+ builder.add(className, isStruct=isStruct)
+
+ curr = CGList([builder.build(), curr], "\n")
+
+ curr = CGHeaders([], [], [], [], includes, implincludes, 'UnionTypes',
+ curr)
+
+ # Add include guards.
+ curr = CGIncludeGuard('UnionTypes', curr)
+
+ # Done.
+ return curr
+
+ @staticmethod
+ def UnionConversions(config):
+ unionTypes = []
+ for l in config.unionsPerFilename.itervalues():
+ unionTypes.extend(l)
+ unionTypes.sort(key=lambda u: u.name)
+ headers, unions = UnionConversions(unionTypes,
+ config)
+
+ # Wrap all of that in our namespaces.
+ curr = CGNamespace.build(['mozilla', 'dom'], unions)
+
+ curr = CGWrapper(curr, post='\n')
+
+ headers.update(["nsDebug.h", "mozilla/dom/UnionTypes.h"])
+ curr = CGHeaders([], [], [], [], headers, [], 'UnionConversions', curr)
+
+ # Add include guards.
+ curr = CGIncludeGuard('UnionConversions', curr)
+
+ # Done.
+ return curr
+
+
+# Code generator for simple events
+class CGEventGetter(CGNativeMember):
+ def __init__(self, descriptor, attr):
+ ea = descriptor.getExtendedAttributes(attr, getter=True)
+ CGNativeMember.__init__(self, descriptor, attr,
+ CGSpecializedGetter.makeNativeName(descriptor,
+ attr),
+ (attr.type, []),
+ ea,
+ resultNotAddRefed=not attr.type.isSequence())
+ self.body = self.getMethodBody()
+
+ def getArgs(self, returnType, argList):
+ if 'infallible' not in self.extendedAttrs:
+ raise TypeError("Event code generator does not support [Throws]!")
+ if not self.member.isAttr():
+ raise TypeError("Event code generator does not support methods")
+ if self.member.isStatic():
+ raise TypeError("Event code generators does not support static attributes")
+ return CGNativeMember.getArgs(self, returnType, argList)
+
+ def getMethodBody(self):
+ type = self.member.type
+ memberName = CGDictionary.makeMemberName(self.member.identifier.name)
+ if (type.isPrimitive() and type.tag() in builtinNames) or type.isEnum() or type.isGeckoInterface():
+ return "return " + memberName + ";\n"
+ if type.isDOMString() or type.isByteString() or type.isUSVString():
+ return "aRetVal = " + memberName + ";\n"
+ if type.isSpiderMonkeyInterface() or type.isObject():
+ return fill(
+ """
+ if (${memberName}) {
+ JS::ExposeObjectToActiveJS(${memberName});
+ }
+ aRetVal.set(${memberName});
+ return;
+ """,
+ memberName=memberName)
+ if type.isAny():
+ return fill(
+ """
+ ${selfName}(aRetVal);
+ """,
+ selfName=self.name)
+ if type.isUnion():
+ return "aRetVal = " + memberName + ";\n"
+ if type.isSequence():
+ return "aRetVal = " + memberName + ";\n"
+ raise TypeError("Event code generator does not support this type!")
+
+ def declare(self, cgClass):
+ if getattr(self.member, "originatingInterface",
+ cgClass.descriptor.interface) != cgClass.descriptor.interface:
+ return ""
+ return CGNativeMember.declare(self, cgClass)
+
+ def define(self, cgClass):
+ if getattr(self.member, "originatingInterface",
+ cgClass.descriptor.interface) != cgClass.descriptor.interface:
+ return ""
+ return CGNativeMember.define(self, cgClass)
+
+
+class CGEventSetter(CGNativeMember):
+ def __init__(self):
+ raise TypeError("Event code generator does not support setters!")
+
+
+class CGEventMethod(CGNativeMember):
+ def __init__(self, descriptor, method, signature, isConstructor, breakAfter=True):
+ self.isInit = False
+
+ CGNativeMember.__init__(self, descriptor, method,
+ CGSpecializedMethod.makeNativeName(descriptor,
+ method),
+ signature,
+ descriptor.getExtendedAttributes(method),
+ breakAfter=breakAfter,
+ variadicIsSequence=True)
+ self.originalArgs = list(self.args)
+
+ iface = descriptor.interface
+ allowed = isConstructor
+ if not allowed and iface.getExtendedAttribute("LegacyEventInit"):
+ # Allow it, only if it fits the initFooEvent profile exactly
+ # We could check the arg types but it's not worth the effort.
+ if (method.identifier.name == "init" + iface.identifier.name and
+ signature[1][0].type.isDOMString() and
+ signature[1][1].type.isBoolean() and
+ signature[1][2].type.isBoolean() and
+ # -3 on the left to ignore the type, bubbles, and cancelable parameters
+ # -1 on the right to ignore the .trusted property which bleeds through
+ # here because it is [Unforgeable].
+ len(signature[1]) - 3 == len(filter(lambda x: x.isAttr(), iface.members)) - 1):
+ allowed = True
+ self.isInit = True
+
+ if not allowed:
+ raise TypeError("Event code generator does not support methods!")
+
+ def getArgs(self, returnType, argList):
+ args = [self.getArg(arg) for arg in argList]
+ return args
+
+ def getArg(self, arg):
+ decl, ref = self.getArgType(arg.type,
+ arg.canHaveMissingValue(),
+ "Variadic" if arg.variadic else False)
+ if ref:
+ decl = CGWrapper(decl, pre="const ", post="&")
+
+ name = arg.identifier.name
+ name = "a" + name[0].upper() + name[1:]
+ return Argument(decl.define(), name)
+
+ def declare(self, cgClass):
+ if self.isInit:
+ constructorForNativeCaller = ""
+ else:
+ self.args = list(self.originalArgs)
+ self.args.insert(0, Argument("mozilla::dom::EventTarget*", "aOwner"))
+ constructorForNativeCaller = CGNativeMember.declare(self, cgClass)
+
+ self.args = list(self.originalArgs)
+ if needCx(None, self.arguments(), [], considerTypes=True, static=True):
+ self.args.insert(0, Argument("JSContext*", "aCx"))
+ if not self.isInit:
+ self.args.insert(0, Argument("const GlobalObject&", "aGlobal"))
+ self.args.append(Argument('ErrorResult&', 'aRv'))
+ return constructorForNativeCaller + CGNativeMember.declare(self, cgClass)
+
+ def defineInit(self, cgClass):
+ iface = self.descriptorProvider.interface
+ members = ""
+ while iface.identifier.name != "Event":
+ i = 3 # Skip the boilerplate args: type, bubble,s cancelable.
+ for m in iface.members:
+ if m.isAttr():
+ # We need to initialize all the member variables that do
+ # not come from Event.
+ if getattr(m, "originatingInterface",
+ iface).identifier.name == "Event":
+ continue
+ name = CGDictionary.makeMemberName(m.identifier.name)
+ members += "%s = %s;\n" % (name, self.args[i].name)
+ i += 1
+ iface = iface.parent
+
+ self.body = fill(
+ """
+ InitEvent(${typeArg}, ${bubblesArg}, ${cancelableArg});
+ ${members}
+ """,
+ typeArg=self.args[0].name,
+ bubblesArg=self.args[1].name,
+ cancelableArg=self.args[2].name,
+ members=members)
+
+ return CGNativeMember.define(self, cgClass)
+
+ def define(self, cgClass):
+ self.args = list(self.originalArgs)
+ if self.isInit:
+ return self.defineInit(cgClass)
+ members = ""
+ holdJS = ""
+ iface = self.descriptorProvider.interface
+ while iface.identifier.name != "Event":
+ for m in self.descriptorProvider.getDescriptor(iface.identifier.name).interface.members:
+ if m.isAttr():
+ # We initialize all the other member variables in the
+ # Constructor except those ones coming from the Event.
+ if getattr(m, "originatingInterface",
+ cgClass.descriptor.interface).identifier.name == "Event":
+ continue
+ name = CGDictionary.makeMemberName(m.identifier.name)
+ if m.type.isSequence():
+ # For sequences we may not be able to do a simple
+ # assignment because the underlying types may not match.
+ # For example, the argument can be a
+ # Sequence<OwningNonNull<SomeInterface>> while our
+ # member is an nsTArray<RefPtr<SomeInterface>>. So
+ # use AppendElements, which is actually a template on
+ # the incoming type on nsTArray and does the right thing
+ # for this case.
+ target = name
+ source = "%s.%s" % (self.args[1].name, name)
+ sequenceCopy = "e->%s.AppendElements(%s);\n"
+ if m.type.nullable():
+ sequenceCopy = CGIfWrapper(
+ CGGeneric(sequenceCopy),
+ "!%s.IsNull()" % source).define()
+ target += ".SetValue()"
+ source += ".Value()"
+ members += sequenceCopy % (target, source)
+ elif m.type.isSpiderMonkeyInterface():
+ srcname = "%s.%s" % (self.args[1].name, name)
+ if m.type.nullable():
+ members += fill(
+ """
+ if (${srcname}.IsNull()) {
+ e->${varname} = nullptr;
+ } else {
+ e->${varname} = ${srcname}.Value().Obj();
+ }
+ """,
+ varname=name,
+ srcname=srcname)
+ else:
+ members += fill(
+ """
+ e->${varname}.set(${srcname}.Obj());
+ """,
+ varname=name, srcname=srcname)
+ else:
+ members += "e->%s = %s.%s;\n" % (name, self.args[1].name, name)
+ if m.type.isAny() or m.type.isObject() or m.type.isSpiderMonkeyInterface():
+ holdJS = "mozilla::HoldJSObjects(e.get());\n"
+ iface = iface.parent
+
+ self.body = fill(
+ """
+ RefPtr<${nativeType}> e = new ${nativeType}(aOwner);
+ bool trusted = e->Init(aOwner);
+ e->InitEvent(${eventType}, ${eventInit}.mBubbles, ${eventInit}.mCancelable);
+ $*{members}
+ e->SetTrusted(trusted);
+ e->SetComposed(${eventInit}.mComposed);
+ $*{holdJS}
+ return e.forget();
+ """,
+ nativeType=self.descriptorProvider.nativeType.split('::')[-1],
+ eventType=self.args[0].name,
+ eventInit=self.args[1].name,
+ members=members,
+ holdJS=holdJS)
+
+ self.args.insert(0, Argument("mozilla::dom::EventTarget*", "aOwner"))
+ constructorForNativeCaller = CGNativeMember.define(self, cgClass) + "\n"
+ self.args = list(self.originalArgs)
+ self.body = fill(
+ """
+ nsCOMPtr<mozilla::dom::EventTarget> owner = do_QueryInterface(aGlobal.GetAsSupports());
+ return Constructor(owner, ${arg0}, ${arg1});
+ """,
+ arg0=self.args[0].name,
+ arg1=self.args[1].name)
+ if needCx(None, self.arguments(), [], considerTypes=True, static=True):
+ self.args.insert(0, Argument("JSContext*", "aCx"))
+ self.args.insert(0, Argument("const GlobalObject&", "aGlobal"))
+ self.args.append(Argument('ErrorResult&', 'aRv'))
+ return constructorForNativeCaller + CGNativeMember.define(self, cgClass)
+
+
+class CGEventClass(CGBindingImplClass):
+ """
+ Codegen for the actual Event class implementation for this descriptor
+ """
+ def __init__(self, descriptor):
+ CGBindingImplClass.__init__(self, descriptor, CGEventMethod, CGEventGetter, CGEventSetter, False, "WrapObjectInternal")
+ members = []
+ extraMethods = []
+ for m in descriptor.interface.members:
+ if m.isAttr():
+ if m.type.isAny():
+ # Add a getter that doesn't need a JSContext. Note that we
+ # don't need to do this if our originating interface is not
+ # the descriptor's interface, because in that case we
+ # wouldn't generate the getter that _does_ need a JSContext
+ # either.
+ extraMethods.append(
+ ClassMethod(
+ CGSpecializedGetter.makeNativeName(descriptor, m),
+ "void",
+ [Argument("JS::MutableHandle<JS::Value>",
+ "aRetVal")],
+ const=True,
+ body=fill(
+ """
+ JS::ExposeValueToActiveJS(${memberName});
+ aRetVal.set(${memberName});
+ """,
+ memberName=CGDictionary.makeMemberName(m.identifier.name))))
+ if getattr(m, "originatingInterface",
+ descriptor.interface) != descriptor.interface:
+ continue
+ nativeType = self.getNativeTypeForIDLType(m.type).define()
+ members.append(ClassMember(CGDictionary.makeMemberName(m.identifier.name),
+ nativeType,
+ visibility="private",
+ body="body"))
+
+ parent = self.descriptor.interface.parent
+ self.parentType = self.descriptor.getDescriptor(parent.identifier.name).nativeType.split('::')[-1]
+ baseDeclarations = fill(
+ """
+ public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(${nativeType}, ${parentType})
+ protected:
+ virtual ~${nativeType}();
+ explicit ${nativeType}(mozilla::dom::EventTarget* aOwner);
+
+ """,
+ nativeType=self.descriptor.nativeType.split('::')[-1],
+ parentType=self.parentType)
+
+ className = descriptor.nativeType.split('::')[-1]
+ asConcreteTypeMethod = ClassMethod("As%s" % className,
+ "%s*" % className,
+ [],
+ virtual=True,
+ body="return this;\n",
+ breakAfterReturnDecl=" ",
+ override=True)
+ extraMethods.append(asConcreteTypeMethod)
+
+ CGClass.__init__(self, className,
+ bases=[ClassBase(self.parentType)],
+ methods=extraMethods+self.methodDecls,
+ members=members,
+ extradeclarations=baseDeclarations)
+
+ def getWrapObjectBody(self):
+ return "return %sBinding::Wrap(aCx, this, aGivenProto);\n" % self.descriptor.name
+
+ def implTraverse(self):
+ retVal = ""
+ for m in self.descriptor.interface.members:
+ # Unroll the type so we pick up sequences of interfaces too.
+ if m.isAttr() and idlTypeNeedsCycleCollection(m.type):
+ retVal += (" NS_IMPL_CYCLE_COLLECTION_TRAVERSE(" +
+ CGDictionary.makeMemberName(m.identifier.name) +
+ ")\n")
+ return retVal
+
+ def implUnlink(self):
+ retVal = ""
+ for m in self.descriptor.interface.members:
+ if m.isAttr():
+ name = CGDictionary.makeMemberName(m.identifier.name)
+ # Unroll the type so we pick up sequences of interfaces too.
+ if idlTypeNeedsCycleCollection(m.type):
+ retVal += " NS_IMPL_CYCLE_COLLECTION_UNLINK(" + name + ")\n"
+ elif m.type.isAny():
+ retVal += " tmp->" + name + ".setUndefined();\n"
+ elif m.type.isObject() or m.type.isSpiderMonkeyInterface():
+ retVal += " tmp->" + name + " = nullptr;\n"
+ return retVal
+
+ def implTrace(self):
+ retVal = ""
+ for m in self.descriptor.interface.members:
+ if m.isAttr():
+ name = CGDictionary.makeMemberName(m.identifier.name)
+ if m.type.isAny() or m.type.isObject() or m.type.isSpiderMonkeyInterface():
+ retVal += " NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(" + name + ")\n"
+ elif typeNeedsRooting(m.type):
+ raise TypeError("Need to implement tracing for event "
+ "member of type %s" % m.type)
+ return retVal
+
+ def define(self):
+ dropJS = ""
+ for m in self.descriptor.interface.members:
+ if m.isAttr():
+ member = CGDictionary.makeMemberName(m.identifier.name)
+ if m.type.isAny():
+ dropJS += member + " = JS::UndefinedValue();\n"
+ elif m.type.isObject() or m.type.isSpiderMonkeyInterface():
+ dropJS += member + " = nullptr;\n"
+ if dropJS != "":
+ dropJS += "mozilla::DropJSObjects(this);\n"
+ # Just override CGClass and do our own thing
+ nativeType = self.descriptor.nativeType.split('::')[-1]
+ ctorParams = ("aOwner, nullptr, nullptr" if self.parentType == "Event"
+ else "aOwner")
+
+ classImpl = fill(
+ """
+
+ NS_IMPL_CYCLE_COLLECTION_CLASS(${nativeType})
+
+ NS_IMPL_ADDREF_INHERITED(${nativeType}, ${parentType})
+ NS_IMPL_RELEASE_INHERITED(${nativeType}, ${parentType})
+
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(${nativeType}, ${parentType})
+ $*{traverse}
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+ NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(${nativeType}, ${parentType})
+ $*{trace}
+ NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(${nativeType}, ${parentType})
+ $*{unlink}
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(${nativeType})
+ NS_INTERFACE_MAP_END_INHERITING(${parentType})
+
+ ${nativeType}::${nativeType}(mozilla::dom::EventTarget* aOwner)
+ : ${parentType}(${ctorParams})
+ {
+ }
+
+ ${nativeType}::~${nativeType}()
+ {
+ $*{dropJS}
+ }
+
+ """,
+ ifaceName=self.descriptor.name,
+ nativeType=nativeType,
+ ctorParams=ctorParams,
+ parentType=self.parentType,
+ traverse=self.implTraverse(),
+ unlink=self.implUnlink(),
+ trace=self.implTrace(),
+ dropJS=dropJS)
+ return classImpl + CGBindingImplClass.define(self)
+
+ def getNativeTypeForIDLType(self, type):
+ if type.isPrimitive() and type.tag() in builtinNames:
+ nativeType = CGGeneric(builtinNames[type.tag()])
+ if type.nullable():
+ nativeType = CGTemplatedType("Nullable", nativeType)
+ elif type.isEnum():
+ nativeType = CGGeneric(type.unroll().inner.identifier.name)
+ if type.nullable():
+ nativeType = CGTemplatedType("Nullable", nativeType)
+ elif type.isDOMString() or type.isUSVString():
+ nativeType = CGGeneric("nsString")
+ elif type.isByteString():
+ nativeType = CGGeneric("nsCString")
+ elif type.isGeckoInterface():
+ iface = type.unroll().inner
+ nativeType = self.descriptor.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)
+ nativeType = CGWrapper(CGGeneric("::".join(nativeType)), pre="RefPtr<", post=">")
+ elif type.isAny():
+ nativeType = CGGeneric("JS::Heap<JS::Value>")
+ elif type.isObject() or type.isSpiderMonkeyInterface():
+ nativeType = CGGeneric("JS::Heap<JSObject*>")
+ elif type.isUnion():
+ nativeType = CGGeneric(CGUnionStruct.unionTypeDecl(type, True))
+ elif type.isSequence():
+ if type.nullable():
+ innerType = type.inner.inner
+ else:
+ innerType = type.inner
+ if (not innerType.isPrimitive() and not innerType.isEnum() and
+ not innerType.isDOMString() and not innerType.isByteString() and
+ not innerType.isGeckoInterface()):
+ raise TypeError("Don't know how to properly manage GC/CC for "
+ "event member of type %s" %
+ type)
+ nativeType = CGTemplatedType(
+ "nsTArray",
+ self.getNativeTypeForIDLType(innerType))
+ if type.nullable():
+ nativeType = CGTemplatedType("Nullable", nativeType)
+ else:
+ raise TypeError("Don't know how to declare event member of type %s" %
+ type)
+ return nativeType
+
+
+class CGEventRoot(CGThing):
+ def __init__(self, config, interfaceName):
+ descriptor = config.getDescriptor(interfaceName)
+
+ self.root = CGWrapper(CGEventClass(descriptor),
+ pre="\n", post="\n")
+
+ self.root = CGNamespace.build(["mozilla", "dom"], self.root)
+
+ self.root = CGList([CGClassForwardDeclare("JSContext", isStruct=True),
+ self.root])
+
+ parent = descriptor.interface.parent.identifier.name
+
+ # Throw in our #includes
+ self.root = CGHeaders(
+ [descriptor],
+ [],
+ [],
+ [],
+ [
+ config.getDescriptor(parent).headerFile,
+ "mozilla/Attributes.h",
+ "mozilla/ErrorResult.h",
+ "mozilla/dom/%sBinding.h" % interfaceName,
+ 'mozilla/dom/BindingUtils.h',
+ ],
+ [
+ "%s.h" % interfaceName,
+ "js/GCAPI.h",
+ 'mozilla/dom/Nullable.h',
+ ],
+ "", self.root, config)
+
+ # And now some include guards
+ self.root = CGIncludeGuard(interfaceName, self.root)
+
+ self.root = CGWrapper(
+ self.root,
+ pre=(AUTOGENERATED_WITH_SOURCE_WARNING_COMMENT %
+ os.path.basename(descriptor.interface.filename())))
+
+ self.root = CGWrapper(self.root, pre=dedent("""
+ /* -*- 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/. */
+
+ """))
+
+ def declare(self):
+ return self.root.declare()
+
+ def define(self):
+ return self.root.define()
diff --git a/dom/bindings/Configuration.py b/dom/bindings/Configuration.py
new file mode 100644
index 000000000..5c96580a1
--- /dev/null
+++ b/dom/bindings/Configuration.py
@@ -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/.
+
+from WebIDL import IDLImplementsStatement
+import os
+from collections import defaultdict
+
+autogenerated_comment = "/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n"
+
+
+class DescriptorProvider:
+ """
+ A way of getting descriptors for interface names. Subclasses must
+ have a getDescriptor method callable with the interface name only.
+ """
+ def __init__(self):
+ pass
+
+
+class Configuration(DescriptorProvider):
+ """
+ Represents global configuration state based on IDL parse data and
+ the configuration file.
+ """
+ def __init__(self, filename, parseData, generatedEvents=[]):
+ DescriptorProvider.__init__(self)
+
+ # 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.descriptorsByName = {}
+ self.optimizedOutDescriptorNames = set()
+ self.generatedEvents = generatedEvents
+ self.maxProtoChainLength = 0
+ for thing in parseData:
+ if isinstance(thing, IDLImplementsStatement):
+ # Our build system doesn't support dep build involving
+ # addition/removal of "implements" statements that appear in a
+ # different .webidl file than their LHS interface. Make sure we
+ # don't have any of those.
+ #
+ # But whitelist a RHS that is LegacyQueryInterface,
+ # since people shouldn't be adding any of those.
+ if (thing.implementor.filename() != thing.filename() and
+ thing.implementee.identifier.name != "LegacyQueryInterface"):
+ raise TypeError(
+ "The binding build system doesn't really support "
+ "'implements' statements which don't appear in the "
+ "file in which the left-hand side of the statement is "
+ "defined. Don't do this unless your right-hand side "
+ "is LegacyQueryInterface.\n"
+ "%s\n"
+ "%s" %
+ (thing.location, thing.implementor.location))
+
+ assert not thing.isType()
+
+ if not thing.isInterface() and not thing.isNamespace():
+ 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():
+ self.optimizedOutDescriptorNames.add(iface.identifier.name)
+ continue
+ entry = {}
+ else:
+ entry = config[iface.identifier.name]
+ assert not isinstance(entry, list)
+ desc = Descriptor(self, iface, entry)
+ self.descriptors.append(desc)
+ # Setting up descriptorsByName while iterating through interfaces
+ # means we can get the nativeType of iterable interfaces without
+ # having to do multiple loops.
+ assert desc.interface.identifier.name not in self.descriptorsByName
+ self.descriptorsByName[desc.interface.identifier.name] = desc
+
+ # Keep the descriptor list sorted for determinism.
+ self.descriptors.sort(lambda x, y: cmp(x.name, y.name))
+
+
+ self.descriptorsByFile = {}
+ for d in self.descriptors:
+ self.descriptorsByFile.setdefault(d.interface.filename(),
+ []).append(d)
+
+ 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()]
+
+ # Dictionary mapping from a union type name to a set of filenames where
+ # union types with that name are used.
+ self.filenamesPerUnion = defaultdict(set)
+
+ # Dictionary mapping from a filename to a list of types for
+ # the union types used in that file. If a union type is used
+ # in multiple files then it will be added to the list for the
+ # None key. Note that the list contains a type for every use
+ # of a union type, so there can be multiple entries with union
+ # types that have the same name.
+ self.unionsPerFilename = defaultdict(list)
+
+ for (t, _) in getAllTypes(self.descriptors, self.dictionaries, self.callbacks):
+ while True:
+ if t.isMozMap():
+ t = t.inner
+ elif t.unroll() != t:
+ t = t.unroll()
+ elif t.isPromise():
+ t = t.promiseInnerType()
+ else:
+ break
+ if t.isUnion():
+ filenamesForUnion = self.filenamesPerUnion[t.name]
+ if t.filename() not in filenamesForUnion:
+ # We have a to be a bit careful: some of our built-in
+ # typedefs are for unions, and those unions end up with
+ # "<unknown>" as the filename. If that happens, we don't
+ # want to try associating this union with one particular
+ # filename, since there isn't one to associate it with,
+ # really.
+ if t.filename() == "<unknown>":
+ uniqueFilenameForUnion = None
+ elif len(filenamesForUnion) == 0:
+ # This is the first file that we found a union with this
+ # name in, record the union as part of the file.
+ uniqueFilenameForUnion = t.filename()
+ else:
+ # We already found a file that contains a union with
+ # this name.
+ if len(filenamesForUnion) == 1:
+ # This is the first time we found a union with this
+ # name in another file.
+ for f in filenamesForUnion:
+ # Filter out unions with this name from the
+ # unions for the file where we previously found
+ # them.
+ unionsForFilename = self.unionsPerFilename[f]
+ unionsForFilename = filter(lambda u: u.name != t.name,
+ unionsForFilename)
+ if len(unionsForFilename) == 0:
+ del self.unionsPerFilename[f]
+ else:
+ self.unionsPerFilename[f] = unionsForFilename
+ # Unions with this name appear in multiple files, record
+ # the filename as None, so that we can detect that.
+ uniqueFilenameForUnion = None
+ self.unionsPerFilename[uniqueFilenameForUnion].append(t)
+ filenamesForUnion.add(t.filename())
+
+ def getInterface(self, ifname):
+ return self.interfaces[ifname]
+
+ def getDescriptors(self, **filters):
+ """Gets the descriptors that match the given filters."""
+ curr = self.descriptors
+ # Collect up our filters, because we may have a webIDLFile filter that
+ # we always want to apply first.
+ tofilter = []
+ for key, val in filters.iteritems():
+ if key == 'webIDLFile':
+ # Special-case this part to make it fast, since most of our
+ # getDescriptors calls are conditioned on a webIDLFile. We may
+ # not have this key, in which case we have no descriptors
+ # either.
+ curr = self.descriptorsByFile.get(val, [])
+ continue
+ elif key == 'hasInterfaceObject':
+ getter = lambda x: (not x.interface.isExternal() and
+ x.interface.hasInterfaceObject())
+ elif key == 'hasInterfacePrototypeObject':
+ getter = lambda x: (not x.interface.isExternal() and
+ x.interface.hasInterfacePrototypeObject())
+ elif key == 'hasInterfaceOrInterfacePrototypeObject':
+ getter = lambda x: x.hasInterfaceOrInterfacePrototypeObject()
+ elif key == 'isCallback':
+ getter = lambda x: x.interface.isCallback()
+ elif key == 'isExternal':
+ getter = lambda x: x.interface.isExternal()
+ elif key == 'isJSImplemented':
+ getter = lambda x: x.interface.isJSImplemented()
+ elif key == 'isNavigatorProperty':
+ getter = lambda x: x.interface.isNavigatorProperty()
+ elif key == 'isExposedInAnyWorker':
+ getter = lambda x: x.interface.isExposedInAnyWorker()
+ elif key == 'isExposedInWorkerDebugger':
+ getter = lambda x: x.interface.isExposedInWorkerDebugger()
+ elif key == 'isExposedInAnyWorklet':
+ getter = lambda x: x.interface.isExposedInAnyWorklet()
+ elif key == 'isExposedInSystemGlobals':
+ getter = lambda x: x.interface.isExposedInSystemGlobals()
+ elif key == 'isExposedInWindow':
+ getter = lambda x: x.interface.isExposedInWindow()
+ else:
+ # Have to watch out: just closing over "key" is not enough,
+ # since we're about to mutate its value
+ getter = (lambda attrName: lambda x: getattr(x, attrName))(key)
+ tofilter.append((getter, val))
+ for f in tofilter:
+ curr = filter(lambda x: f[0](x) == f[1], curr)
+ return curr
+
+ def getEnums(self, webIDLFile):
+ return filter(lambda e: e.filename() == webIDLFile, self.enums)
+
+ def getDictionaries(self, webIDLFile):
+ return filter(lambda d: d.filename() == webIDLFile, self.dictionaries)
+
+ def getCallbacks(self, webIDLFile):
+ return filter(lambda c: c.filename() == webIDLFile, self.callbacks)
+
+ def getDescriptor(self, interfaceName):
+ """
+ Gets the appropriate descriptor for the given interface name.
+ """
+ # We may have optimized out this descriptor, but the chances of anyone
+ # asking about it are then slim. Put the check for that _after_ we've
+ # done our normal lookup. But that means we have to do our normal
+ # lookup in a way that will not throw if it fails.
+ d = self.descriptorsByName.get(interfaceName, None)
+ if d:
+ return d
+
+ if interfaceName in self.optimizedOutDescriptorNames:
+ raise NoSuchDescriptorError(
+ "No descriptor for '%s', which is a mixin ([NoInterfaceObject] "
+ "and a consequential interface) without an explicit "
+ "Bindings.conf annotation." % interfaceName)
+
+ raise NoSuchDescriptorError("For " + interfaceName + " found no matches")
+
+
+class NoSuchDescriptorError(TypeError):
+ def __init__(self, str):
+ TypeError.__init__(self, str)
+
+
+def methodReturnsJSObject(method):
+ assert method.isMethod()
+ if method.returnsPromise():
+ return True
+
+ for signature in method.signatures():
+ returnType = signature[0]
+ if returnType.isObject() or returnType.isSpiderMonkeyInterface():
+ return True
+
+ return False
+
+
+def MemberIsUnforgeable(member, descriptor):
+ # Note: "or" and "and" return either their LHS or RHS, not
+ # necessarily booleans. Make sure to return a boolean from this
+ # method, because callers will compare its return value to
+ # booleans.
+ return bool((member.isAttr() or member.isMethod()) and
+ not member.isStatic() and
+ (member.isUnforgeable() or
+ descriptor.interface.getExtendedAttribute("Unforgeable")))
+
+
+class Descriptor(DescriptorProvider):
+ """
+ Represents a single descriptor for an interface. See Bindings.conf.
+ """
+ def __init__(self, config, interface, desc):
+ DescriptorProvider.__init__(self)
+ self.config = config
+ self.interface = interface
+
+ self.wantsXrays = (not interface.isExternal() and
+ interface.isExposedInWindow())
+
+ if self.wantsXrays:
+ # We could try to restrict self.wantsXrayExpandoClass further. For
+ # example, we could set it to false if all of our slots store
+ # Gecko-interface-typed things, because we don't use Xray expando
+ # slots for those. But note that we would need to check the types
+ # of not only the members of "interface" but also of all its
+ # ancestors, because those can have members living in our slots too.
+ # For now, do the simple thing.
+ self.wantsXrayExpandoClass = (interface.totalMembersInSlots != 0)
+
+ # Read the desc, and fill in the relevant defaults.
+ ifaceName = self.interface.identifier.name
+ # For generated iterator interfaces for other iterable interfaces, we
+ # just use IterableIterator as the native type, templated on the
+ # nativeType of the iterable interface. That way we can have a
+ # templated implementation for all the duplicated iterator
+ # functionality.
+ if self.interface.isIteratorInterface():
+ itrName = self.interface.iterableInterface.identifier.name
+ itrDesc = self.getDescriptor(itrName)
+ nativeTypeDefault = iteratorNativeType(itrDesc)
+
+ elif self.interface.isExternal():
+ nativeTypeDefault = "nsIDOM" + ifaceName
+ else:
+ nativeTypeDefault = "mozilla::dom::" + ifaceName
+
+ self.nativeType = desc.get('nativeType', nativeTypeDefault)
+ # Now create a version of nativeType that doesn't have extra
+ # mozilla::dom:: at the beginning.
+ prettyNativeType = self.nativeType.split("::")
+ if prettyNativeType[0] == "mozilla":
+ prettyNativeType.pop(0)
+ if prettyNativeType[0] == "dom":
+ prettyNativeType.pop(0)
+ self.prettyNativeType = "::".join(prettyNativeType)
+
+ self.jsImplParent = desc.get('jsImplParent', self.nativeType)
+
+ # Do something sane for JSObject
+ if self.nativeType == "JSObject":
+ headerDefault = "js/TypeDecls.h"
+ elif self.interface.isCallback() or self.interface.isJSImplemented():
+ # A copy of CGHeaders.getDeclarationFilename; we can't
+ # import it here, sadly.
+ # 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(self.interface.filename())
+ headerDefault = basename.replace('.webidl', 'Binding.h')
+ else:
+ if not self.interface.isExternal() and self.interface.getExtendedAttribute("HeaderFile"):
+ headerDefault = self.interface.getExtendedAttribute("HeaderFile")[0]
+ elif self.interface.isIteratorInterface():
+ headerDefault = "mozilla/dom/IterableIterator.h"
+ else:
+ headerDefault = self.nativeType
+ headerDefault = headerDefault.replace("::", "/") + ".h"
+ self.headerFile = desc.get('headerFile', headerDefault)
+ self.headerIsDefault = self.headerFile == headerDefault
+ if self.jsImplParent == self.nativeType:
+ self.jsImplParentHeader = self.headerFile
+ else:
+ self.jsImplParentHeader = self.jsImplParent.replace("::", "/") + ".h"
+
+ self.notflattened = desc.get('notflattened', False)
+ self.register = desc.get('register', True)
+
+ self.hasXPConnectImpls = desc.get('hasXPConnectImpls', False)
+
+ # If we're concrete, we need to crawl our ancestor interfaces and mark
+ # them as having a concrete descendant.
+ self.concrete = (not self.interface.isExternal() and
+ not self.interface.isCallback() and
+ not self.interface.isNamespace() and
+ desc.get('concrete', True))
+ self.hasUnforgeableMembers = (self.concrete and
+ any(MemberIsUnforgeable(m, self) for m in
+ self.interface.members))
+ self.operations = {
+ 'IndexedGetter': None,
+ 'IndexedSetter': None,
+ 'IndexedCreator': None,
+ 'IndexedDeleter': None,
+ 'NamedGetter': None,
+ 'NamedSetter': None,
+ 'NamedCreator': None,
+ 'NamedDeleter': None,
+ 'Stringifier': None,
+ 'LegacyCaller': None,
+ 'Jsonifier': None
+ }
+
+ # Stringifiers and jsonifiers need to be set up whether an interface is
+ # concrete or not, because they're actually prototype methods and hence
+ # can apply to instances of descendant interfaces. Legacy callers and
+ # named/indexed operations only need to be set up on concrete
+ # interfaces, since they affect the JSClass we end up using, not the
+ # prototype object.
+ def addOperation(operation, m):
+ if not self.operations[operation]:
+ self.operations[operation] = m
+
+ # Since stringifiers go on the prototype, we only need to worry
+ # about our own stringifier, not those of our ancestor interfaces.
+ if not self.interface.isExternal():
+ for m in self.interface.members:
+ if m.isMethod() and m.isStringifier():
+ addOperation('Stringifier', m)
+ if m.isMethod() and m.isJsonifier():
+ addOperation('Jsonifier', m)
+
+ if self.concrete:
+ self.proxy = False
+ iface = self.interface
+ for m in iface.members:
+ # Don't worry about inheriting legacycallers either: in
+ # practice these are on most-derived prototypes.
+ if m.isMethod() and m.isLegacycaller():
+ if not m.isIdentifierLess():
+ raise TypeError("We don't support legacycaller with "
+ "identifier.\n%s" % m.location)
+ if len(m.signatures()) != 1:
+ raise TypeError("We don't support overloaded "
+ "legacycaller.\n%s" % m.location)
+ addOperation('LegacyCaller', m)
+ while iface:
+ for m in iface.members:
+ if not m.isMethod():
+ continue
+
+ def addIndexedOrNamedOperation(operation, m):
+ if m.isIndexed():
+ operation = 'Indexed' + operation
+ else:
+ assert m.isNamed()
+ operation = 'Named' + operation
+ addOperation(operation, m)
+
+ if m.isGetter():
+ addIndexedOrNamedOperation('Getter', m)
+ if m.isSetter():
+ addIndexedOrNamedOperation('Setter', m)
+ if m.isCreator():
+ addIndexedOrNamedOperation('Creator', m)
+ if m.isDeleter():
+ addIndexedOrNamedOperation('Deleter', m)
+ if m.isLegacycaller() and iface != self.interface:
+ raise TypeError("We don't support legacycaller on "
+ "non-leaf interface %s.\n%s" %
+ (iface, iface.location))
+
+ iface.setUserData('hasConcreteDescendant', True)
+ iface = iface.parent
+
+ self.proxy = (self.supportsIndexedProperties() or
+ (self.supportsNamedProperties() and
+ not self.hasNamedPropertiesObject) or
+ self.hasNonOrdinaryGetPrototypeOf())
+
+ if self.proxy:
+ if (not self.operations['IndexedGetter'] and
+ (self.operations['IndexedSetter'] or
+ self.operations['IndexedDeleter'] or
+ self.operations['IndexedCreator'])):
+ raise SyntaxError("%s supports indexed properties but does "
+ "not have an indexed getter.\n%s" %
+ (self.interface, self.interface.location))
+ if (not self.operations['NamedGetter'] and
+ (self.operations['NamedSetter'] or
+ self.operations['NamedDeleter'] or
+ self.operations['NamedCreator'])):
+ raise SyntaxError("%s supports named properties but does "
+ "not have a named getter.\n%s" %
+ (self.interface, self.interface.location))
+ iface = self.interface
+ while iface:
+ iface.setUserData('hasProxyDescendant', True)
+ iface = iface.parent
+
+ if desc.get('wantsQI', None) is not None:
+ self._wantsQI = desc.get('wantsQI', None)
+ self.wrapperCache = (not self.interface.isCallback() and
+ not self.interface.isIteratorInterface() and
+ desc.get('wrapperCache', True))
+ # Nasty temporary hack for supporting both DOM and SpiderMonkey promises
+ # without too much pain
+ if self.interface.identifier.name == "Promise":
+ assert self.wrapperCache
+ # But really, we're only wrappercached if we have an interface
+ # object (that is, when we're not using SpiderMonkey promises).
+ self.wrapperCache = self.interface.hasInterfaceObject()
+
+ 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)
+
+ if self.interface.isJSImplemented():
+ addExtendedAttribute('implicitJSContext', ['constructor'])
+ else:
+ for attribute in ['implicitJSContext']:
+ addExtendedAttribute(attribute, desc.get(attribute, {}))
+
+ if self.interface.identifier.name == 'Navigator':
+ for m in self.interface.members:
+ if m.isAttr() and m.navigatorObjectGetter:
+ # These getters call ConstructNavigatorObject to construct
+ # the value, and ConstructNavigatorObject needs a JSContext.
+ self.extendedAttributes['all'].setdefault(m.identifier.name, []).append('implicitJSContext')
+
+ self._binaryNames = desc.get('binaryNames', {})
+ self._binaryNames.setdefault('__legacycaller', 'LegacyCall')
+ self._binaryNames.setdefault('__stringifier', 'Stringify')
+
+ if not self.interface.isExternal():
+ def isTestInterface(iface):
+ return (iface.identifier.name in ["TestInterface",
+ "TestJSImplInterface",
+ "TestRenamedInterface"])
+
+ for member in self.interface.members:
+ if not member.isAttr() and not member.isMethod():
+ continue
+ binaryName = member.getExtendedAttribute("BinaryName")
+ if binaryName:
+ assert isinstance(binaryName, list)
+ assert len(binaryName) == 1
+ self._binaryNames.setdefault(member.identifier.name,
+ binaryName[0])
+
+ # 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 binaryNameFor(self, name):
+ return self._binaryNames.get(name, name)
+
+ @property
+ def prototypeNameChain(self):
+ return map(lambda p: self.getDescriptor(p).name, self.prototypeChain)
+
+ @property
+ def parentPrototypeName(self):
+ if len(self.prototypeChain) == 1:
+ return None
+ return self.getDescriptor(self.prototypeChain[-2]).name
+
+ def hasInterfaceOrInterfacePrototypeObject(self):
+
+ # Forward-declared interfaces don't need either interface object or
+ # interface prototype object as they're going to use QI.
+ if self.interface.isExternal():
+ return False
+
+ return self.interface.hasInterfaceObject() or self.interface.hasInterfacePrototypeObject()
+
+ @property
+ def hasNamedPropertiesObject(self):
+ if self.interface.isExternal():
+ return False
+
+ return self.isGlobal() and self.supportsNamedProperties()
+
+ def getExtendedAttributes(self, member, getter=False, setter=False):
+ def ensureValidThrowsExtendedAttribute(attr):
+ if (attr is not None and attr is not True):
+ raise TypeError("Unknown value for 'Throws': " + attr[0])
+
+ def maybeAppendInfallibleToAttrs(attrs, throws):
+ ensureValidThrowsExtendedAttribute(throws)
+ if throws is None:
+ attrs.append("infallible")
+
+ name = member.identifier.name
+ throws = self.interface.isJSImplemented() or member.getExtendedAttribute("Throws")
+ if member.isMethod():
+ # JSObject-returning [NewObject] methods must be fallible,
+ # since they have to (fallibly) allocate the new JSObject.
+ if (member.getExtendedAttribute("NewObject") and
+ methodReturnsJSObject(member)):
+ throws = True
+ attrs = self.extendedAttributes['all'].get(name, [])
+ 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, [])
+ if throws is None:
+ throwsAttr = "GetterThrows" if getter else "SetterThrows"
+ throws = member.getExtendedAttribute(throwsAttr)
+ maybeAppendInfallibleToAttrs(attrs, throws)
+ return attrs
+
+ def supportsIndexedProperties(self):
+ return self.operations['IndexedGetter'] is not None
+
+ def supportsNamedProperties(self):
+ return self.operations['NamedGetter'] is not None
+
+ def hasNonOrdinaryGetPrototypeOf(self):
+ return self.interface.getExtendedAttribute("NonOrdinaryGetPrototypeOf")
+
+ def needsHeaderInclude(self):
+ """
+ An interface doesn't need a header file if it is not concrete, not
+ pref-controlled, has no prototype object, has no static methods or
+ attributes and has no parent. The parent matters because we assert
+ things about refcounting that depend on the actual underlying type if we
+ have a parent.
+
+ """
+ return (self.interface.isExternal() or self.concrete or
+ self.interface.hasInterfacePrototypeObject() or
+ any((m.isAttr() or m.isMethod()) and m.isStatic() for m in self.interface.members) or
+ self.interface.parent)
+
+ def hasThreadChecks(self):
+ # isExposedConditionally does not necessarily imply thread checks
+ # (since at least [SecureContext] is independent of them), but we're
+ # only used to decide whether to include nsThreadUtils.h, so we don't
+ # worry about that.
+ return ((self.isExposedConditionally() and
+ not self.interface.isExposedInWindow()) or
+ self.interface.isExposedInSomeButNotAllWorkers())
+
+ def isExposedConditionally(self):
+ return (self.interface.isExposedConditionally() or
+ self.interface.isExposedInSomeButNotAllWorkers())
+
+ def needsXrayResolveHooks(self):
+ """
+ Generally, any interface with NeedResolve needs Xray
+ resolveOwnProperty and enumerateOwnProperties hooks. But for
+ the special case of plugin-loading elements, we do NOT want
+ those, because we don't want to instantiate plug-ins simply
+ due to chrome touching them and that's all those hooks do on
+ those elements. So we special-case those here.
+ """
+ return (self.interface.getExtendedAttribute("NeedResolve") and
+ self.interface.identifier.name not in ["HTMLObjectElement",
+ "HTMLEmbedElement",
+ "HTMLAppletElement"])
+ def needsXrayNamedDeleterHook(self):
+ return self.operations["NamedDeleter"] is not None
+
+ def needsSpecialGenericOps(self):
+ """
+ Returns true if this descriptor requires generic ops other than
+ GenericBindingMethod/GenericBindingGetter/GenericBindingSetter.
+
+ In practice we need to do this if our this value might be an XPConnect
+ object or if we need to coerce null/undefined to the global.
+ """
+ return self.hasXPConnectImpls or self.interface.isOnGlobalProtoChain()
+
+ 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"))
+
+ @property
+ def namedPropertiesEnumerable(self):
+ """
+ Returns whether this interface should have enumerable named properties
+ """
+ assert self.proxy
+ assert self.supportsNamedProperties()
+ iface = self.interface
+ while iface:
+ if iface.getExtendedAttribute("LegacyUnenumerableNamedProperties"):
+ return False
+ iface = iface.parent
+ return True
+
+ @property
+ def registersGlobalNamesOnWindow(self):
+ return (not self.interface.isExternal() and
+ self.interface.hasInterfaceObject() and
+ self.interface.isExposedInWindow() and
+ self.register)
+
+ def getDescriptor(self, interfaceName):
+ """
+ Gets the appropriate descriptor for the given interface name.
+ """
+ return self.config.getDescriptor(interfaceName)
+
+
+# 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())
+
+ if descriptor.interface.maplikeOrSetlikeOrIterable:
+ maplikeOrSetlikeOrIterable = descriptor.interface.maplikeOrSetlikeOrIterable
+ if maplikeOrSetlikeOrIterable.hasKeyType():
+ types.append(maplikeOrSetlikeOrIterable.keyType)
+ if maplikeOrSetlikeOrIterable.hasValueType():
+ types.append(maplikeOrSetlikeOrIterable.valueType)
+ 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
+
+
+def getAllTypes(descriptors, dictionaries, callbacks):
+ """
+ Generate all the types we're dealing with. For each type, a tuple
+ containing type, dictionary is yielded. The dictionary can be None if the
+ type does not come from a dictionary.
+ """
+ for d in descriptors:
+ if d.interface.isExternal():
+ continue
+ for t in getTypesFromDescriptor(d):
+ yield (t, None)
+ for dictionary in dictionaries:
+ for t in getTypesFromDictionary(dictionary):
+ yield (t, dictionary)
+ for callback in callbacks:
+ for t in getTypesFromCallback(callback):
+ yield (t, None)
+
+def iteratorNativeType(descriptor):
+ assert descriptor.interface.isIterable()
+ iterableDecl = descriptor.interface.maplikeOrSetlikeOrIterable
+ assert iterableDecl.isPairIterator()
+ return "mozilla::dom::IterableIterator<%s>" % descriptor.nativeType
diff --git a/dom/bindings/DOMJSClass.h b/dom/bindings/DOMJSClass.h
new file mode 100644
index 000000000..6e779840f
--- /dev/null
+++ b/dom/bindings/DOMJSClass.h
@@ -0,0 +1,467 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "jsfriendapi.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Likely.h"
+
+#include "mozilla/dom/PrototypeList.h" // auto-generated
+
+#include "mozilla/dom/JSSlots.h"
+
+class nsCycleCollectionParticipant;
+
+// All DOM globals must have a slot at DOM_PROTOTYPE_SLOT.
+#define DOM_PROTOTYPE_SLOT JSCLASS_GLOBAL_SLOT_COUNT
+
+// Keep this count up to date with any extra global slots added above.
+#define DOM_GLOBAL_SLOTS 1
+
+// We use these flag bits for the new bindings.
+#define JSCLASS_DOM_GLOBAL JSCLASS_USERBIT1
+#define JSCLASS_IS_DOMIFACEANDPROTOJSCLASS JSCLASS_USERBIT2
+
+namespace mozilla {
+namespace dom {
+
+/**
+ * Returns true if code running in the given JSContext is allowed to access
+ * [SecureContext] API on the given JSObject.
+ *
+ * [SecureContext] API exposure is restricted to use by code in a Secure
+ * Contexts:
+ *
+ * https://w3c.github.io/webappsec-secure-contexts/
+ *
+ * Since we want [SecureContext] exposure to depend on the privileges of the
+ * running code (rather than the privileges of an object's creator), this
+ * function checks to see whether the given JSContext's Compartment is flagged
+ * as a Secure Context. That allows us to make sure that system principal code
+ * (which is marked as a Secure Context) can access Secure Context API on an
+ * object in a different compartment, regardless of whether the other
+ * compartment is a Secure Context or not.
+ *
+ * Checking the JSContext's Compartment doesn't work for expanded principal
+ * globals accessing a Secure Context web page though (e.g. those used by frame
+ * scripts). To handle that we fall back to checking whether the JSObject came
+ * from a Secure Context.
+ *
+ * Note: We'd prefer this function to live in BindingUtils.h, but we need to
+ * call it in this header, and BindingUtils.h includes us (i.e. we'd have a
+ * circular dependency between headers if it lived there).
+ */
+inline bool
+IsSecureContextOrObjectIsFromSecureContext(JSContext* aCx, JSObject* aObj)
+{
+ return JS::CompartmentCreationOptionsRef(js::GetContextCompartment(aCx)).secureContext() ||
+ JS::CompartmentCreationOptionsRef(js::GetObjectCompartment(aObj)).secureContext();
+}
+
+typedef bool
+(* ResolveOwnProperty)(JSContext* cx, JS::Handle<JSObject*> wrapper,
+ JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
+ JS::MutableHandle<JS::PropertyDescriptor> desc);
+
+typedef bool
+(* EnumerateOwnProperties)(JSContext* cx, JS::Handle<JSObject*> wrapper,
+ JS::Handle<JSObject*> obj,
+ JS::AutoIdVector& props);
+
+typedef bool
+(* DeleteNamedProperty)(JSContext* cx, JS::Handle<JSObject*> wrapper,
+ JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
+ JS::ObjectOpResult& opresult);
+
+// Returns true if the given global is of a type whose bit is set in
+// aNonExposedGlobals.
+bool
+IsNonExposedGlobal(JSContext* aCx, JSObject* aGlobal,
+ uint32_t aNonExposedGlobals);
+
+struct ConstantSpec
+{
+ const char* name;
+ JS::Value value;
+};
+
+typedef bool (*PropertyEnabled)(JSContext* cx, JSObject* global);
+
+namespace GlobalNames {
+// The names of our possible globals. These are the names of the actual
+// interfaces, not of the global names used to refer to them in IDL [Exposed]
+// annotations.
+static const uint32_t Window = 1u << 0;
+static const uint32_t BackstagePass = 1u << 1;
+static const uint32_t DedicatedWorkerGlobalScope = 1u << 2;
+static const uint32_t SharedWorkerGlobalScope = 1u << 3;
+static const uint32_t ServiceWorkerGlobalScope = 1u << 4;
+static const uint32_t WorkerDebuggerGlobalScope = 1u << 5;
+static const uint32_t WorkletGlobalScope = 1u << 6;
+} // namespace GlobalNames
+
+struct PrefableDisablers {
+ inline bool isEnabled(JSContext* cx, JS::Handle<JSObject*> obj) const {
+ // Reading "enabled" on a worker thread is technically undefined behavior,
+ // because it's written only on main threads, with no barriers of any sort.
+ // So we want to avoid doing that. But we don't particularly want to make
+ // expensive NS_IsMainThread calls here.
+ //
+ // The good news is that "enabled" is only written for things that have a
+ // Pref annotation, and such things can never be exposed on non-Window
+ // globals; our IDL parser enforces that. So as long as we check our
+ // exposure set before checking "enabled" we will be ok.
+ if (nonExposedGlobals &&
+ IsNonExposedGlobal(cx, js::GetGlobalForObjectCrossCompartment(obj),
+ nonExposedGlobals)) {
+ return false;
+ }
+ if (!enabled) {
+ return false;
+ }
+ if (secureContext && !IsSecureContextOrObjectIsFromSecureContext(cx, obj)) {
+ return false;
+ }
+ if (enabledFunc &&
+ !enabledFunc(cx, js::GetGlobalForObjectCrossCompartment(obj))) {
+ return false;
+ }
+ return true;
+ }
+
+ // A boolean indicating whether this set of specs is enabled. Not const
+ // because it will change at runtime if the corresponding pref is changed.
+ bool enabled;
+
+ // A boolean indicating whether a Secure Context is required.
+ const bool secureContext;
+
+ // Bitmask of global names that we should not be exposed in.
+ const uint16_t nonExposedGlobals;
+
+ // A function pointer to a function that can say the property is disabled
+ // even if "enabled" is set to true. If the pointer is null the value of
+ // "enabled" is used as-is.
+ const PropertyEnabled enabledFunc;
+};
+
+template<typename T>
+struct Prefable {
+ inline bool isEnabled(JSContext* cx, JS::Handle<JSObject*> obj) const {
+ if (MOZ_LIKELY(!disablers)) {
+ return true;
+ }
+ return disablers->isEnabled(cx, obj);
+ }
+
+ // Things that can disable this set of specs. |nullptr| means "cannot be
+ // disabled".
+ PrefableDisablers* const disablers;
+
+ // 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.
+ const T* const specs;
+};
+
+// Conceptually, NativeProperties has seven (Prefable<T>*, jsid*, T*) trios
+// (where T is one of JSFunctionSpec, JSPropertySpec, or ConstantSpec), one for
+// each of: static methods and attributes, methods and attributes, unforgeable
+// methods and attributes, and constants.
+//
+// That's 21 pointers, but in most instances most of the trios are all null,
+// and there are many instances. To save space we use a variable-length type,
+// NativePropertiesN<N>, to hold the data and getters to access it. It has N
+// actual trios (stored in trios[]), plus four bits for each of the 7 possible
+// trios: 1 bit that states if that trio is present, and 3 that state that
+// trio's offset (if present) in trios[].
+//
+// All trio accesses should be done via the getters, which contain assertions
+// that check we don't overrun the end of the struct. (The trio data members are
+// public only so they can be statically initialized.) These assertions should
+// never fail so long as (a) accesses to the variable-length part are guarded by
+// appropriate Has*() calls, and (b) all instances are well-formed, i.e. the
+// value of N matches the number of mHas* members that are true.
+//
+// Finally, we define a typedef of NativePropertiesN<7>, NativeProperties, which
+// we use as a "base" type used to refer to all instances of NativePropertiesN.
+// (7 is used because that's the maximum valid parameter, though any other
+// value 1..6 could also be used.) This is reasonable because of the
+// aforementioned assertions in the getters. Upcast() is used to convert
+// specific instances to this "base" type.
+//
+template <int N>
+struct NativePropertiesN {
+ // Trio structs are stored in the trios[] array, and each element in the
+ // array could require a different T. Therefore, we can't use the correct
+ // type for mPrefables and mSpecs. Instead we use void* and cast to the
+ // correct type in the getters.
+ struct Trio {
+ const /*Prefable<const T>*/ void* const mPrefables;
+ const jsid* const mIds;
+ const /*T*/ void* const mSpecs;
+ };
+
+ const int32_t iteratorAliasMethodIndex;
+
+ constexpr const NativePropertiesN<7>* Upcast() const {
+ return reinterpret_cast<const NativePropertiesN<7>*>(this);
+ }
+
+#define DO(SpecT, FieldName) \
+public: \
+ /* The bitfields indicating the trio's presence and (if present) offset. */ \
+ const uint32_t mHas##FieldName##s:1; \
+ const uint32_t m##FieldName##sOffset:3; \
+private: \
+ const Trio* FieldName##sTrio() const { \
+ MOZ_ASSERT(Has##FieldName##s()); \
+ return &trios[m##FieldName##sOffset]; \
+ } \
+public: \
+ bool Has##FieldName##s() const { \
+ return mHas##FieldName##s; \
+ } \
+ const Prefable<const SpecT>* FieldName##s() const { \
+ return static_cast<const Prefable<const SpecT>*> \
+ (FieldName##sTrio()->mPrefables); \
+ } \
+ const jsid* FieldName##Ids() const { \
+ return FieldName##sTrio()->mIds; \
+ } \
+ const SpecT* FieldName##Specs() const { \
+ return static_cast<const SpecT*>(FieldName##sTrio()->mSpecs); \
+ }
+
+ DO(JSFunctionSpec, StaticMethod)
+ DO(JSPropertySpec, StaticAttribute)
+ DO(JSFunctionSpec, Method)
+ DO(JSPropertySpec, Attribute)
+ DO(JSFunctionSpec, UnforgeableMethod)
+ DO(JSPropertySpec, UnforgeableAttribute)
+ DO(ConstantSpec, Constant)
+
+#undef DO
+
+ const Trio trios[N];
+};
+
+// Ensure the struct has the expected size. The 8 is for the
+// iteratorAliasMethodIndex plus the bitfields; the rest is for trios[].
+static_assert(sizeof(NativePropertiesN<1>) == 8 + 3*sizeof(void*), "1 size");
+static_assert(sizeof(NativePropertiesN<2>) == 8 + 6*sizeof(void*), "2 size");
+static_assert(sizeof(NativePropertiesN<3>) == 8 + 9*sizeof(void*), "3 size");
+static_assert(sizeof(NativePropertiesN<4>) == 8 + 12*sizeof(void*), "4 size");
+static_assert(sizeof(NativePropertiesN<5>) == 8 + 15*sizeof(void*), "5 size");
+static_assert(sizeof(NativePropertiesN<6>) == 8 + 18*sizeof(void*), "6 size");
+static_assert(sizeof(NativePropertiesN<7>) == 8 + 21*sizeof(void*), "7 size");
+
+// The "base" type.
+typedef NativePropertiesN<7> NativeProperties;
+
+struct NativePropertiesHolder
+{
+ const NativeProperties* regular;
+ const NativeProperties* chromeOnly;
+};
+
+// Helper structure for Xrays for DOM binding objects. The same instance is used
+// for instances, interface objects and interface prototype objects of a
+// specific interface.
+struct NativePropertyHooks
+{
+ // The hook to call for resolving indexed or named properties. May be null if
+ // there can't be any.
+ ResolveOwnProperty mResolveOwnProperty;
+ // The hook to call for enumerating indexed or named properties. May be null
+ // if there can't be any.
+ EnumerateOwnProperties mEnumerateOwnProperties;
+ // The hook to call to delete a named property. May be null if there are no
+ // named properties or no named property deleter. On success (true return)
+ // the "found" argument will be set to true if there was in fact such a named
+ // property and false otherwise. If it's set to false, the caller is expected
+ // to proceed with whatever deletion behavior it would have if there were no
+ // named properties involved at all (i.e. if the hook were null). If it's set
+ // to true, it will indicate via opresult whether the delete actually
+ // succeeded.
+ DeleteNamedProperty mDeleteNamedProperty;
+
+ // The property arrays for this interface.
+ NativePropertiesHolder mNativeProperties;
+
+ // This will be set to the ID of the interface prototype object for the
+ // interface, if it has one. If it doesn't have one it will be set to
+ // prototypes::id::_ID_Count.
+ prototypes::ID mPrototypeID;
+
+ // This will be set to the ID of the interface object for the interface, if it
+ // has one. If it doesn't have one it will be set to
+ // constructors::id::_ID_Count.
+ constructors::ID mConstructorID;
+
+ // The NativePropertyHooks instance for the parent interface (for
+ // ShimInterfaceInfo).
+ const NativePropertyHooks* mProtoHooks;
+
+ // The JSClass to use for expandos on our Xrays. Can be null, in which case
+ // Xrays will use a default class of their choice.
+ const JSClass* mXrayExpandoClass;
+};
+
+enum DOMObjectType : uint8_t {
+ eInstance,
+ eGlobalInstance,
+ eInterface,
+ eInterfacePrototype,
+ eGlobalInterfacePrototype,
+ eNamedPropertiesObject
+};
+
+inline
+bool
+IsInstance(DOMObjectType type)
+{
+ return type == eInstance || type == eGlobalInstance;
+}
+
+inline
+bool
+IsInterfacePrototype(DOMObjectType type)
+{
+ return type == eInterfacePrototype || type == eGlobalInterfacePrototype;
+}
+
+typedef JSObject* (*AssociatedGlobalGetter)(JSContext* aCx,
+ JS::Handle<JSObject*> aObj);
+
+typedef JSObject* (*ProtoGetter)(JSContext* aCx);
+
+/**
+ * Returns a handle to the relevant WebIDL prototype object for the current
+ * compartment global (which may be a handle to null on out of memory). Once
+ * allocated, the prototype object is guaranteed to exist as long as the global
+ * does, since the global traces its array of WebIDL prototypes and
+ * constructors.
+ */
+typedef JS::Handle<JSObject*> (*ProtoHandleGetter)(JSContext* aCx);
+
+// 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.
+ const js::Class mBase;
+
+ // A list of interfaces that this object implements, in order of decreasing
+ // derivedness.
+ const prototypes::ID mInterfaceChain[MAX_PROTOTYPE_CHAIN_LENGTH];
+
+ // 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;
+
+ // A callback to find the associated global for our C++ object. Note that
+ // this is used in cases when that global is _changing_, so it will not match
+ // the global of the JSObject* passed in to this function!
+ AssociatedGlobalGetter mGetAssociatedGlobal;
+ ProtoHandleGetter mGetProto;
+
+ // This stores the CC participant for the native, null if this class does not
+ // implement cycle collection or if it inherits from nsISupports (we can get
+ // the CC participant by QI'ing in that case).
+ nsCycleCollectionParticipant* mParticipant;
+
+ static const DOMJSClass* FromJSClass(const JSClass* base) {
+ MOZ_ASSERT(base->flags & JSCLASS_IS_DOMJSCLASS);
+ return reinterpret_cast<const DOMJSClass*>(base);
+ }
+
+ static const DOMJSClass* FromJSClass(const js::Class* base) {
+ return FromJSClass(Jsvalify(base));
+ }
+
+ const JSClass* ToJSClass() const { return Jsvalify(&mBase); }
+};
+
+// Special JSClass for DOM interface and interface prototype objects.
+struct DOMIfaceAndProtoJSClass
+{
+ // It would be nice to just inherit from js::Class, but that precludes pure
+ // compile-time initialization of the form
+ // |DOMJSInterfaceAndPrototypeClass = {...};|, since C++ only allows brace
+ // initialization for aggregate/POD types.
+ const js::Class mBase;
+
+ // Either eInterface, eInterfacePrototype, eGlobalInterfacePrototype or
+ // eNamedPropertiesObject.
+ DOMObjectType mType; // uint8_t
+
+ // Boolean indicating whether this object wants a @@hasInstance property
+ // pointing to InterfaceHasInstance defined on it. Only ever true for the
+ // eInterface case.
+ bool wantsInterfaceHasInstance;
+
+ const prototypes::ID mPrototypeID; // uint16_t
+ const uint32_t mDepth;
+
+ const NativePropertyHooks* mNativeHooks;
+
+ // The value to return for toString() on this interface or interface prototype
+ // object.
+ const char* mToString;
+
+ ProtoGetter mGetParentProto;
+
+ static const DOMIfaceAndProtoJSClass* FromJSClass(const JSClass* base) {
+ MOZ_ASSERT(base->flags & JSCLASS_IS_DOMIFACEANDPROTOJSCLASS);
+ return reinterpret_cast<const DOMIfaceAndProtoJSClass*>(base);
+ }
+ static const DOMIfaceAndProtoJSClass* FromJSClass(const js::Class* base) {
+ return FromJSClass(Jsvalify(base));
+ }
+
+ const JSClass* ToJSClass() const { return Jsvalify(&mBase); }
+};
+
+class ProtoAndIfaceCache;
+
+inline bool
+DOMGlobalHasProtoAndIFaceCache(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 bool
+HasProtoAndIfaceCache(JSObject* global)
+{
+ if (!(js::GetObjectClass(global)->flags & JSCLASS_DOM_GLOBAL)) {
+ return false;
+ }
+ return DOMGlobalHasProtoAndIFaceCache(global);
+}
+
+inline ProtoAndIfaceCache*
+GetProtoAndIfaceCache(JSObject* global)
+{
+ MOZ_ASSERT(js::GetObjectClass(global)->flags & JSCLASS_DOM_GLOBAL);
+ return static_cast<ProtoAndIfaceCache*>(
+ js::GetReservedSlot(global, DOM_PROTOTYPE_SLOT).toPrivate());
+}
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_DOMJSClass_h */
diff --git a/dom/bindings/DOMJSProxyHandler.cpp b/dom/bindings/DOMJSProxyHandler.cpp
new file mode 100644
index 000000000..65e540bc1
--- /dev/null
+++ b/dom/bindings/DOMJSProxyHandler.cpp
@@ -0,0 +1,362 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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/dom/DOMJSProxyHandler.h"
+#include "xpcpublic.h"
+#include "xpcprivate.h"
+#include "XPCWrapper.h"
+#include "WrapperFactory.h"
+#include "nsDOMClassInfo.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)
+{
+ return AtomizeAndPinJSString(cx, s_length_id, "length");
+}
+
+const char DOMProxyHandler::family = 0;
+
+js::DOMProxyShadowsResult
+DOMProxyShadows(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id)
+{
+ JS::Rooted<JSObject*> expando(cx, DOMProxyHandler::GetExpandoObject(proxy));
+ JS::Value v = js::GetProxyExtra(proxy, JSPROXYSLOT_EXPANDO);
+ bool isOverrideBuiltins = !v.isObject() && !v.isUndefined();
+ if (expando) {
+ bool hasOwn;
+ if (!JS_AlreadyHasOwnPropertyById(cx, expando, id, &hasOwn))
+ return js::ShadowCheckFailed;
+
+ if (hasOwn) {
+ return isOverrideBuiltins ?
+ js::ShadowsViaIndirectExpando : js::ShadowsViaDirectExpando;
+ }
+ }
+
+ if (!isOverrideBuiltins) {
+ // Our expando, if any, didn't shadow, so we're not shadowing at all.
+ return js::DoesntShadow;
+ }
+
+ bool hasOwn;
+ if (!GetProxyHandler(proxy)->hasOwn(cx, proxy, id, &hasOwn))
+ return js::ShadowCheckFailed;
+
+ return hasOwn ? js::Shadows : js::DoesntShadowUnique;
+}
+
+// Store the information for the specialized ICs.
+struct SetDOMProxyInformation
+{
+ SetDOMProxyInformation() {
+ js::SetDOMProxyInformation((const void*) &DOMProxyHandler::family,
+ JSPROXYSLOT_EXPANDO, DOMProxyShadows);
+ }
+};
+
+SetDOMProxyInformation gSetDOMProxyInformation;
+
+// static
+void
+DOMProxyHandler::ClearExternalRefsForWrapperRelease(JSObject* obj)
+{
+ MOZ_ASSERT(IsDOMProxy(obj), "expected a DOM proxy object");
+ JS::Value v = js::GetProxyExtra(obj, JSPROXYSLOT_EXPANDO);
+ if (v.isUndefined()) {
+ // No expando.
+ return;
+ }
+
+ // See EnsureExpandoObject for the work we're trying to undo here.
+
+ if (v.isObject()) {
+ // Drop us from the DOM expando hashtable. Don't worry about clearing our
+ // slot reference to the expando; we're about to die anyway.
+ xpc::ObjectScope(obj)->RemoveDOMExpandoObject(obj);
+ return;
+ }
+
+ // Prevent having a dangling pointer to our expando from the
+ // ExpandoAndGeneration.
+ js::ExpandoAndGeneration* expandoAndGeneration =
+ static_cast<js::ExpandoAndGeneration*>(v.toPrivate());
+ expandoAndGeneration->expando = UndefinedValue();
+}
+
+// static
+JSObject*
+DOMProxyHandler::GetAndClearExpandoObject(JSObject* obj)
+{
+ MOZ_ASSERT(IsDOMProxy(obj), "expected a DOM proxy object");
+ JS::Value v = js::GetProxyExtra(obj, JSPROXYSLOT_EXPANDO);
+ if (v.isUndefined()) {
+ return nullptr;
+ }
+
+ if (v.isObject()) {
+ js::SetProxyExtra(obj, JSPROXYSLOT_EXPANDO, UndefinedValue());
+ xpc::ObjectScope(obj)->RemoveDOMExpandoObject(obj);
+ } else {
+ js::ExpandoAndGeneration* expandoAndGeneration =
+ static_cast<js::ExpandoAndGeneration*>(v.toPrivate());
+ v = expandoAndGeneration->expando;
+ if (v.isUndefined()) {
+ return nullptr;
+ }
+ // We have to expose v to active JS here. The reason for that is that we
+ // might be in the middle of a GC right now. If our proxy hasn't been
+ // traced yet, when it _does_ get traced it won't trace the expando, since
+ // we're breaking that link. But the Rooted we're presumably being placed
+ // into is also not going to trace us, because Rooted marking is done at
+ // the very beginning of the GC. In that situation, we need to manually
+ // mark the expando as live here. JS::ExposeValueToActiveJS will do just
+ // that for us.
+ //
+ // We don't need to do this in the non-expandoAndGeneration case, because
+ // in that case our value is stored in a slot and slots will already mark
+ // the old thing live when the value in the slot changes.
+ JS::ExposeValueToActiveJS(v);
+ expandoAndGeneration->expando = UndefinedValue();
+ }
+
+
+ return &v.toObject();
+}
+
+// static
+JSObject*
+DOMProxyHandler::EnsureExpandoObject(JSContext* cx, JS::Handle<JSObject*> obj)
+{
+ NS_ASSERTION(IsDOMProxy(obj), "expected a DOM proxy object");
+ JS::Value v = js::GetProxyExtra(obj, JSPROXYSLOT_EXPANDO);
+ if (v.isObject()) {
+ return &v.toObject();
+ }
+
+ js::ExpandoAndGeneration* expandoAndGeneration;
+ if (!v.isUndefined()) {
+ expandoAndGeneration = static_cast<js::ExpandoAndGeneration*>(v.toPrivate());
+ if (expandoAndGeneration->expando.isObject()) {
+ return &expandoAndGeneration->expando.toObject();
+ }
+ } else {
+ expandoAndGeneration = nullptr;
+ }
+
+ JS::Rooted<JSObject*> expando(cx,
+ JS_NewObjectWithGivenProto(cx, nullptr, nullptr));
+ if (!expando) {
+ return nullptr;
+ }
+
+ nsISupports* native = UnwrapDOMObject<nsISupports>(obj);
+ nsWrapperCache* cache;
+ CallQueryInterface(native, &cache);
+ if (expandoAndGeneration) {
+ cache->PreserveWrapper(native);
+ expandoAndGeneration->expando.setObject(*expando);
+
+ return expando;
+ }
+
+ if (!xpc::ObjectScope(obj)->RegisterDOMExpandoObject(obj)) {
+ return nullptr;
+ }
+
+ cache->SetPreservingWrapper(true);
+ js::SetProxyExtra(obj, JSPROXYSLOT_EXPANDO, ObjectValue(*expando));
+
+ return expando;
+}
+
+bool
+DOMProxyHandler::preventExtensions(JSContext* cx, JS::Handle<JSObject*> proxy,
+ JS::ObjectOpResult& result) const
+{
+ // always extensible per WebIDL
+ return result.failCantPreventExtensions();
+}
+
+bool
+DOMProxyHandler::isExtensible(JSContext *cx, JS::Handle<JSObject*> proxy, bool *extensible) const
+{
+ *extensible = true;
+ return true;
+}
+
+bool
+BaseDOMProxyHandler::getOwnPropertyDescriptor(JSContext* cx,
+ JS::Handle<JSObject*> proxy,
+ JS::Handle<jsid> id,
+ MutableHandle<PropertyDescriptor> desc) const
+{
+ return getOwnPropDescriptor(cx, proxy, id, /* ignoreNamedProps = */ false,
+ desc);
+}
+
+bool
+DOMProxyHandler::defineProperty(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
+ Handle<PropertyDescriptor> desc,
+ JS::ObjectOpResult &result, bool *defined) const
+{
+ if (desc.hasGetterObject() && desc.setter() == JS_StrictPropertyStub) {
+ return result.failGetterOnly();
+ }
+
+ if (xpc::WrapperFactory::IsXrayWrapper(proxy)) {
+ return result.succeed();
+ }
+
+ JS::Rooted<JSObject*> expando(cx, EnsureExpandoObject(cx, proxy));
+ if (!expando) {
+ return false;
+ }
+
+ if (!JS_DefinePropertyById(cx, expando, id, desc, result)) {
+ return false;
+ }
+ *defined = true;
+ return true;
+}
+
+bool
+DOMProxyHandler::set(JSContext *cx, Handle<JSObject*> proxy, Handle<jsid> id,
+ Handle<JS::Value> v, Handle<JS::Value> receiver,
+ ObjectOpResult &result) const
+{
+ MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
+ "Should not have a XrayWrapper here");
+ bool done;
+ if (!setCustom(cx, proxy, id, v, &done)) {
+ return false;
+ }
+ if (done) {
+ return result.succeed();
+ }
+
+ // Make sure to ignore our named properties when checking for own
+ // property descriptors for a set.
+ JS::Rooted<PropertyDescriptor> ownDesc(cx);
+ if (!getOwnPropDescriptor(cx, proxy, id, /* ignoreNamedProps = */ true,
+ &ownDesc)) {
+ return false;
+ }
+ return js::SetPropertyIgnoringNamedGetter(cx, proxy, id, v, receiver, ownDesc, result);
+}
+
+bool
+DOMProxyHandler::delete_(JSContext* cx, JS::Handle<JSObject*> proxy,
+ JS::Handle<jsid> id, JS::ObjectOpResult &result) const
+{
+ JS::Rooted<JSObject*> expando(cx);
+ if (!xpc::WrapperFactory::IsXrayWrapper(proxy) && (expando = GetExpandoObject(proxy))) {
+ return JS_DeletePropertyById(cx, expando, id, result);
+ }
+
+ return result.succeed();
+}
+
+bool
+BaseDOMProxyHandler::watch(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
+ JS::Handle<JSObject*> callable) const
+{
+ return js::WatchGuts(cx, proxy, id, callable);
+}
+
+bool
+BaseDOMProxyHandler::unwatch(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id) const
+{
+ return js::UnwatchGuts(cx, proxy, id);
+}
+
+bool
+BaseDOMProxyHandler::ownPropertyKeys(JSContext* cx,
+ JS::Handle<JSObject*> proxy,
+ JS::AutoIdVector& props) const
+{
+ return ownPropNames(cx, proxy, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, props);
+}
+
+bool
+BaseDOMProxyHandler::getPrototypeIfOrdinary(JSContext* cx, JS::Handle<JSObject*> proxy,
+ bool* isOrdinary,
+ JS::MutableHandle<JSObject*> proto) const
+{
+ *isOrdinary = true;
+ proto.set(GetStaticPrototype(proxy));
+ return true;
+}
+
+bool
+BaseDOMProxyHandler::getOwnEnumerablePropertyKeys(JSContext* cx,
+ JS::Handle<JSObject*> proxy,
+ JS::AutoIdVector& props) const
+{
+ return ownPropNames(cx, proxy, JSITER_OWNONLY, props);
+}
+
+bool
+DOMProxyHandler::setCustom(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
+ JS::Handle<JS::Value> v, bool *done) const
+{
+ *done = false;
+ return true;
+}
+
+//static
+JSObject *
+DOMProxyHandler::GetExpandoObject(JSObject *obj)
+{
+ MOZ_ASSERT(IsDOMProxy(obj), "expected a DOM proxy object");
+ JS::Value v = js::GetProxyExtra(obj, JSPROXYSLOT_EXPANDO);
+ if (v.isObject()) {
+ return &v.toObject();
+ }
+
+ if (v.isUndefined()) {
+ return nullptr;
+ }
+
+ js::ExpandoAndGeneration* expandoAndGeneration =
+ static_cast<js::ExpandoAndGeneration*>(v.toPrivate());
+ v = expandoAndGeneration->expando;
+ return v.isUndefined() ? nullptr : &v.toObject();
+}
+
+void
+ShadowingDOMProxyHandler::trace(JSTracer* trc, JSObject* proxy) const
+{
+ DOMProxyHandler::trace(trc, proxy);
+
+ MOZ_ASSERT(IsDOMProxy(proxy), "expected a DOM proxy object");
+ JS::Value v = js::GetProxyExtra(proxy, JSPROXYSLOT_EXPANDO);
+ MOZ_ASSERT(!v.isObject(), "Should not have expando object directly!");
+
+ if (v.isUndefined()) {
+ // This can happen if we GC while creating our object, before we get a
+ // chance to set up its JSPROXYSLOT_EXPANDO slot.
+ return;
+ }
+
+ js::ExpandoAndGeneration* expandoAndGeneration =
+ static_cast<js::ExpandoAndGeneration*>(v.toPrivate());
+ JS::TraceEdge(trc, &expandoAndGeneration->expando,
+ "Shadowing DOM proxy expando");
+}
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/bindings/DOMJSProxyHandler.h b/dom/bindings/DOMJSProxyHandler.h
new file mode 100644
index 000000000..1781649cc
--- /dev/null
+++ b/dom/bindings/DOMJSProxyHandler.h
@@ -0,0 +1,268 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "mozilla/Attributes.h"
+#include "mozilla/Likely.h"
+
+#include "jsapi.h"
+#include "js/Proxy.h"
+#include "nsString.h"
+
+#define DOM_PROXY_OBJECT_SLOT js::PROXY_PRIVATE_SLOT
+
+namespace mozilla {
+namespace dom {
+
+enum {
+ /**
+ * DOM proxies have an extra slot for the expando object at index
+ * JSPROXYSLOT_EXPANDO.
+ *
+ * The expando object is a plain JSObject whose properties correspond to
+ * "expandos" (custom properties set by the script author).
+ *
+ * The exact value stored in the JSPROXYSLOT_EXPANDO slot depends on whether
+ * the interface is annotated with the [OverrideBuiltins] extended attribute.
+ *
+ * If it is, the proxy is initialized with a PrivateValue, which contains a
+ * pointer to a js::ExpandoAndGeneration object; this contains a pointer to
+ * the actual expando object as well as the "generation" of the object. The
+ * proxy handler will trace the expando object stored in the
+ * js::ExpandoAndGeneration while the proxy itself is alive.
+ *
+ * If it is not, the proxy is initialized with an UndefinedValue. In
+ * EnsureExpandoObject, it is set to an ObjectValue that points to the
+ * expando object directly. (It is set back to an UndefinedValue only when
+ * the object is about to die.)
+ */
+ JSPROXYSLOT_EXPANDO = 0
+};
+
+template<typename T> struct Prefable;
+
+class BaseDOMProxyHandler : public js::BaseProxyHandler
+{
+public:
+ explicit constexpr BaseDOMProxyHandler(const void* aProxyFamily, bool aHasPrototype = false)
+ : js::BaseProxyHandler(aProxyFamily, aHasPrototype)
+ {}
+
+ // Implementations of methods that can be implemented in terms of
+ // other lower-level methods.
+ bool getOwnPropertyDescriptor(JSContext* cx, JS::Handle<JSObject*> proxy,
+ JS::Handle<jsid> id,
+ JS::MutableHandle<JS::PropertyDescriptor> desc) const override;
+ virtual bool ownPropertyKeys(JSContext* cx, JS::Handle<JSObject*> proxy,
+ JS::AutoIdVector &props) const override;
+
+ virtual bool getPrototypeIfOrdinary(JSContext* cx, JS::Handle<JSObject*> proxy,
+ bool* isOrdinary,
+ JS::MutableHandle<JSObject*> proto) const override;
+
+ // We override getOwnEnumerablePropertyKeys() and implement it directly
+ // instead of using the default implementation, which would call
+ // ownPropertyKeys and then filter out the non-enumerable ones. This avoids
+ // unnecessary work during enumeration.
+ virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, JS::Handle<JSObject*> proxy,
+ JS::AutoIdVector &props) const override;
+
+ bool watch(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
+ JS::Handle<JSObject*> callable) const override;
+ bool unwatch(JSContext* cx, JS::Handle<JSObject*> proxy,
+ JS::Handle<jsid> id) const override;
+
+protected:
+ // Hook for subclasses to implement shared ownPropertyKeys()/keys()
+ // functionality. The "flags" argument is either JSITER_OWNONLY (for keys())
+ // or JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS (for
+ // ownPropertyKeys()).
+ virtual bool ownPropNames(JSContext* cx, JS::Handle<JSObject*> proxy,
+ unsigned flags,
+ JS::AutoIdVector& props) const = 0;
+
+ // Hook for subclasses to allow set() to ignore named props while other things
+ // that look at property descriptors see them. This is intentionally not
+ // named getOwnPropertyDescriptor to avoid subclasses that override it hiding
+ // our public getOwnPropertyDescriptor.
+ virtual bool getOwnPropDescriptor(JSContext* cx,
+ JS::Handle<JSObject*> proxy,
+ JS::Handle<jsid> id,
+ bool ignoreNamedProps,
+ JS::MutableHandle<JS::PropertyDescriptor> desc) const = 0;
+};
+
+class DOMProxyHandler : public BaseDOMProxyHandler
+{
+public:
+ constexpr DOMProxyHandler()
+ : BaseDOMProxyHandler(&family)
+ {}
+
+ bool defineProperty(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
+ JS::Handle<JS::PropertyDescriptor> desc,
+ JS::ObjectOpResult &result) const override
+ {
+ bool unused;
+ return defineProperty(cx, proxy, id, desc, result, &unused);
+ }
+ virtual bool defineProperty(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
+ JS::Handle<JS::PropertyDescriptor> desc,
+ JS::ObjectOpResult &result, bool *defined) const;
+ bool delete_(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
+ JS::ObjectOpResult &result) const override;
+ bool preventExtensions(JSContext* cx, JS::Handle<JSObject*> proxy,
+ JS::ObjectOpResult& result) const override;
+ bool isExtensible(JSContext *cx, JS::Handle<JSObject*> proxy, bool *extensible)
+ const override;
+ bool set(JSContext *cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
+ JS::Handle<JS::Value> v, JS::Handle<JS::Value> receiver, JS::ObjectOpResult &result)
+ const override;
+
+ /*
+ * If assigning to proxy[id] hits a named setter with OverrideBuiltins or
+ * an indexed setter, call it and set *done to true on success. Otherwise, set
+ * *done to false.
+ */
+ virtual bool setCustom(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
+ JS::Handle<JS::Value> v, bool *done) const;
+
+ /*
+ * Get the expando object for the given DOM proxy.
+ */
+ static JSObject* GetExpandoObject(JSObject* obj);
+
+ /*
+ * Clear the "external references" to this object. If you are not
+ * nsWrapperCAche::ReleaseWrapper, you do NOT want to be calling this method.
+ *
+ * XXXbz if we nixed the DOM expando hash and just had a finalizer that
+ * cleared out the value in the ExpandoAndGeneration in the shadowing case,
+ * could we just get rid of this function altogether?
+ */
+ static void ClearExternalRefsForWrapperRelease(JSObject* obj);
+
+ /*
+ * Clear the expando object for the given DOM proxy and return it. This
+ * function will ensure that the returned object is exposed to active JS if
+ * the given object is exposed.
+ *
+ * GetAndClearExpandoObject does not DROP or clear the preserving wrapper
+ * flag.
+ */
+ static JSObject* GetAndClearExpandoObject(JSObject* obj);
+
+ /*
+ * Ensure that the given proxy (obj) has an expando object, and return it.
+ * Returns null on failure.
+ */
+ static JSObject* EnsureExpandoObject(JSContext* cx,
+ JS::Handle<JSObject*> obj);
+
+ static const char family;
+};
+
+// Class used by shadowing handlers (the ones that have [OverrideBuiltins].
+// This handles tracing the expando in ExpandoAndGeneration.
+class ShadowingDOMProxyHandler : public DOMProxyHandler
+{
+ virtual void trace(JSTracer* trc, JSObject* proxy) const override;
+};
+
+inline bool IsDOMProxy(JSObject *obj)
+{
+ const js::Class* clasp = js::GetObjectClass(obj);
+ return clasp->isProxy() &&
+ js::GetProxyHandler(obj)->family() == &DOMProxyHandler::family;
+}
+
+inline const DOMProxyHandler*
+GetDOMProxyHandler(JSObject* obj)
+{
+ MOZ_ASSERT(IsDOMProxy(obj));
+ return static_cast<const DOMProxyHandler*>(js::GetProxyHandler(obj));
+}
+
+extern jsid s_length_id;
+
+// A return value of UINT32_MAX indicates "not an array index". Note, in
+// particular, that UINT32_MAX itself is not a valid array index in general.
+inline uint32_t
+GetArrayIndexFromId(JSContext* cx, JS::Handle<jsid> id)
+{
+ // Much like js::IdIsIndex, except with a fast path for "length" and another
+ // fast path for starting with a lowercase ascii char. Is that second one
+ // really needed? I guess it is because StringIsArrayIndex is out of line...
+ if (MOZ_LIKELY(JSID_IS_INT(id))) {
+ return JSID_TO_INT(id);
+ }
+ if (MOZ_LIKELY(id == s_length_id)) {
+ return UINT32_MAX;
+ }
+ if (MOZ_UNLIKELY(!JSID_IS_ATOM(id))) {
+ return UINT32_MAX;
+ }
+
+ JSLinearString* str = js::AtomToLinearString(JSID_TO_ATOM(id));
+ char16_t s;
+ {
+ JS::AutoCheckCannotGC nogc;
+ if (js::LinearStringHasLatin1Chars(str)) {
+ s = *js::GetLatin1LinearStringChars(nogc, str);
+ } else {
+ s = *js::GetTwoByteLinearStringChars(nogc, str);
+ }
+ }
+ if (MOZ_LIKELY((unsigned)s >= 'a' && (unsigned)s <= 'z'))
+ return UINT32_MAX;
+
+ uint32_t i;
+ return js::StringIsArrayIndex(str, &i) ? i : UINT32_MAX;
+}
+
+inline bool
+IsArrayIndex(uint32_t index)
+{
+ return index < UINT32_MAX;
+}
+
+inline void
+FillPropertyDescriptor(JS::MutableHandle<JS::PropertyDescriptor> desc,
+ JSObject* obj, bool readonly, bool enumerable = true)
+{
+ desc.object().set(obj);
+ desc.setAttributes((readonly ? JSPROP_READONLY : 0) |
+ (enumerable ? JSPROP_ENUMERATE : 0));
+ desc.setGetter(nullptr);
+ desc.setSetter(nullptr);
+}
+
+inline void
+FillPropertyDescriptor(JS::MutableHandle<JS::PropertyDescriptor> desc,
+ JSObject* obj, const JS::Value& v,
+ bool readonly, bool enumerable = true)
+{
+ desc.value().set(v);
+ FillPropertyDescriptor(desc, obj, readonly, enumerable);
+}
+
+inline void
+FillPropertyDescriptor(JS::MutableHandle<JS::PropertyDescriptor> desc,
+ JSObject* obj, unsigned attributes, const JS::Value& v)
+{
+ desc.object().set(obj);
+ desc.value().set(v);
+ desc.setAttributes(attributes);
+ desc.setGetter(nullptr);
+ desc.setSetter(nullptr);
+}
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_DOMProxyHandler_h */
diff --git a/dom/bindings/DOMString.h b/dom/bindings/DOMString.h
new file mode 100644
index 000000000..72c8445ec
--- /dev/null
+++ b/dom/bindings/DOMString.h
@@ -0,0 +1,245 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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_DOMString_h
+#define mozilla_dom_DOMString_h
+
+#include "nsStringGlue.h"
+#include "nsStringBuffer.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Maybe.h"
+#include "nsDOMString.h"
+#include "nsIAtom.h"
+
+namespace mozilla {
+namespace dom {
+
+/**
+ * A class for representing string return values. This can be either passed to
+ * callees that have an nsString or nsAString out param or passed to a callee
+ * that actually knows about this class and can work with it. Such a callee may
+ * call SetStringBuffer or SetEphemeralStringBuffer or SetOwnedString or
+ * SetOwnedAtom on this object. It's only OK to call
+ * SetStringBuffer/SetOwnedString/SetOwnedAtom if the caller of the method in
+ * question plans to keep holding a strong ref to the stringbuffer involved,
+ * whether it's a raw nsStringBuffer, or stored inside the string or atom being
+ * passed. In the string/atom cases that means the caller must own the string
+ * or atom, and not mutate it (in the string case) for the lifetime of the
+ * DOMString.
+ *
+ * The proper way to store a value in this class is to either to do nothing
+ * (which leaves this as an empty string), to call
+ * SetStringBuffer/SetEphemeralStringBuffer with a non-null stringbuffer, to
+ * call SetOwnedString, to call SetOwnedAtom, to call SetNull(), or to call
+ * AsAString() and set the value in the resulting nsString. These options are
+ * mutually exclusive! Don't do more than one of them.
+ *
+ * The proper way to extract a value is to check IsNull(). If not null, then
+ * check HasStringBuffer(). If that's true, check for a zero length, and if the
+ * length is nonzero call StringBuffer(). If the length is zero this is the
+ * empty string. If HasStringBuffer() returns false, call AsAString() and get
+ * the value from that.
+ */
+class MOZ_STACK_CLASS DOMString {
+public:
+ DOMString()
+ : mStringBuffer(nullptr)
+ , mLength(0)
+ , mIsNull(false)
+ , mStringBufferOwned(false)
+ {}
+ ~DOMString()
+ {
+ MOZ_ASSERT(!mString || !mStringBuffer,
+ "Shouldn't have both present!");
+ if (mStringBufferOwned) {
+ MOZ_ASSERT(mStringBuffer);
+ mStringBuffer->Release();
+ }
+ }
+
+ operator nsString&()
+ {
+ return AsAString();
+ }
+
+ // It doesn't make any sense to convert a DOMString to a const nsString or
+ // nsAString reference; this class is meant for outparams only.
+ operator const nsString&() = delete;
+ operator const nsAString&() = delete;
+
+ nsString& AsAString()
+ {
+ MOZ_ASSERT(!mStringBuffer, "We already have a stringbuffer?");
+ MOZ_ASSERT(!mIsNull, "We're already set as null");
+ if (!mString) {
+ mString.emplace();
+ }
+ return *mString;
+ }
+
+ bool HasStringBuffer() const
+ {
+ MOZ_ASSERT(!mString || !mStringBuffer,
+ "Shouldn't have both present!");
+ MOZ_ASSERT(!mIsNull, "Caller should have checked IsNull() first");
+ return !mString;
+ }
+
+ // Get the stringbuffer. This can only be called if HasStringBuffer()
+ // returned true and StringBufferLength() is nonzero. If that's true, it will
+ // never return null. Note that constructing a string from this
+ // nsStringBuffer with length given by StringBufferLength() might give you
+ // something that is not null-terminated.
+ nsStringBuffer* StringBuffer() const
+ {
+ MOZ_ASSERT(!mIsNull, "Caller should have checked IsNull() first");
+ MOZ_ASSERT(HasStringBuffer(),
+ "Don't ask for the stringbuffer if we don't have it");
+ MOZ_ASSERT(StringBufferLength() != 0, "Why are you asking for this?");
+ MOZ_ASSERT(mStringBuffer,
+ "If our length is nonzero, we better have a stringbuffer.");
+ return mStringBuffer;
+ }
+
+ // Get the length of the stringbuffer. Can only be called if
+ // HasStringBuffer().
+ uint32_t StringBufferLength() const
+ {
+ MOZ_ASSERT(HasStringBuffer(), "Don't call this if there is no stringbuffer");
+ return mLength;
+ }
+
+ // Tell the DOMString to relinquish ownership of its nsStringBuffer to the
+ // caller. Can only be called if HasStringBuffer().
+ void RelinquishBufferOwnership()
+ {
+ MOZ_ASSERT(HasStringBuffer(), "Don't call this if there is no stringbuffer");
+ if (mStringBufferOwned) {
+ // Just hand that ref over.
+ mStringBufferOwned = false;
+ } else {
+ // Caller should end up holding a ref.
+ mStringBuffer->AddRef();
+ }
+ }
+
+ // Initialize the DOMString to a (nsStringBuffer, length) pair. The length
+ // does NOT have to be the full length of the (null-terminated) string in the
+ // nsStringBuffer.
+ void SetStringBuffer(nsStringBuffer* aStringBuffer, uint32_t aLength)
+ {
+ MOZ_ASSERT(mString.isNothing(), "We already have a string?");
+ MOZ_ASSERT(!mIsNull, "We're already set as null");
+ MOZ_ASSERT(!mStringBuffer, "Setting stringbuffer twice?");
+ MOZ_ASSERT(aStringBuffer, "Why are we getting null?");
+ mStringBuffer = aStringBuffer;
+ mLength = aLength;
+ }
+
+ // Like SetStringBuffer, but holds a reference to the nsStringBuffer.
+ void SetEphemeralStringBuffer(nsStringBuffer* aStringBuffer, uint32_t aLength)
+ {
+ // We rely on SetStringBuffer to ensure our state invariants.
+ SetStringBuffer(aStringBuffer, aLength);
+ aStringBuffer->AddRef();
+ mStringBufferOwned = true;
+ }
+
+ void SetOwnedString(const nsAString& aString)
+ {
+ MOZ_ASSERT(mString.isNothing(), "We already have a string?");
+ MOZ_ASSERT(!mIsNull, "We're already set as null");
+ MOZ_ASSERT(!mStringBuffer, "Setting stringbuffer twice?");
+ nsStringBuffer* buf = nsStringBuffer::FromString(aString);
+ if (buf) {
+ SetStringBuffer(buf, aString.Length());
+ } else if (aString.IsVoid()) {
+ SetNull();
+ } else if (!aString.IsEmpty()) {
+ AsAString() = aString;
+ }
+ }
+
+ enum NullHandling
+ {
+ eTreatNullAsNull,
+ eTreatNullAsEmpty,
+ eNullNotExpected
+ };
+
+ void SetOwnedAtom(nsIAtom* aAtom, NullHandling aNullHandling)
+ {
+ MOZ_ASSERT(mString.isNothing(), "We already have a string?");
+ MOZ_ASSERT(!mIsNull, "We're already set as null");
+ MOZ_ASSERT(!mStringBuffer, "Setting stringbuffer twice?");
+ MOZ_ASSERT(aAtom || aNullHandling != eNullNotExpected);
+ if (aNullHandling == eNullNotExpected || aAtom) {
+ SetStringBuffer(aAtom->GetStringBuffer(), aAtom->GetLength());
+ } else if (aNullHandling == eTreatNullAsNull) {
+ SetNull();
+ }
+ }
+
+ void SetNull()
+ {
+ MOZ_ASSERT(!mStringBuffer, "Should have no stringbuffer if null");
+ MOZ_ASSERT(mString.isNothing(), "Should have no string if null");
+ mIsNull = true;
+ }
+
+ bool IsNull() const
+ {
+ MOZ_ASSERT(!mStringBuffer || mString.isNothing(),
+ "How could we have a stringbuffer and a nonempty string?");
+ return mIsNull || (mString && mString->IsVoid());
+ }
+
+ void ToString(nsAString& aString)
+ {
+ if (IsNull()) {
+ SetDOMStringToNull(aString);
+ } else if (HasStringBuffer()) {
+ if (StringBufferLength() == 0) {
+ aString.Truncate();
+ } else {
+ // Don't share the nsStringBuffer with aString if the result would not
+ // be null-terminated.
+ nsStringBuffer* buf = StringBuffer();
+ uint32_t len = StringBufferLength();
+ auto chars = static_cast<char16_t*>(buf->Data());
+ if (chars[len] == '\0') {
+ // Safe to share the buffer.
+ buf->ToString(len, aString);
+ } else {
+ // We need to copy, unfortunately.
+ aString.Assign(chars, len);
+ }
+ }
+ } else {
+ aString = AsAString();
+ }
+ }
+
+private:
+ // We need to be able to act like a string as needed
+ Maybe<nsAutoString> mString;
+
+ // For callees that know we exist, we can be a stringbuffer/length/null-flag
+ // triple.
+ nsStringBuffer* MOZ_UNSAFE_REF("The ways in which this can be safe are "
+ "documented above and enforced through "
+ "assertions") mStringBuffer;
+ uint32_t mLength;
+ bool mIsNull;
+ bool mStringBufferOwned;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_DOMString_h
diff --git a/dom/bindings/Date.cpp b/dom/bindings/Date.cpp
new file mode 100644
index 000000000..8c8d50b33
--- /dev/null
+++ b/dom/bindings/Date.cpp
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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/dom/Date.h"
+
+#include "jsapi.h" // for JS_ObjectIsDate
+#include "jsfriendapi.h" // for DateGetMsecSinceEpoch
+#include "js/Date.h" // for JS::NewDateObject, JS::ClippedTime, JS::TimeClip
+#include "js/RootingAPI.h" // for Rooted, MutableHandle
+#include "js/Value.h" // for Value
+#include "mozilla/FloatingPoint.h" // for IsNaN, UnspecifiedNaN
+
+namespace mozilla {
+namespace dom {
+
+bool
+Date::SetTimeStamp(JSContext* aCx, JSObject* aObject)
+{
+ JS::Rooted<JSObject*> obj(aCx, aObject);
+
+ double msecs;
+ if (!js::DateGetMsecSinceEpoch(aCx, obj, &msecs)) {
+ return false;
+ }
+
+ JS::ClippedTime time = JS::TimeClip(msecs);
+ MOZ_ASSERT(NumbersAreIdentical(msecs, time.toDouble()));
+
+ mMsecSinceEpoch = time;
+ return true;
+}
+
+bool
+Date::ToDateObject(JSContext* aCx, JS::MutableHandle<JS::Value> aRval) const
+{
+ JSObject* obj = JS::NewDateObject(aCx, mMsecSinceEpoch);
+ if (!obj) {
+ return false;
+ }
+
+ aRval.setObject(*obj);
+ return true;
+}
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/bindings/Date.h b/dom/bindings/Date.h
new file mode 100644
index 000000000..66c893e4d
--- /dev/null
+++ b/dom/bindings/Date.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* Representation for dates. */
+
+#ifndef mozilla_dom_Date_h
+#define mozilla_dom_Date_h
+
+#include "js/Date.h"
+#include "js/TypeDecls.h"
+
+namespace mozilla {
+namespace dom {
+
+class Date
+{
+public:
+ Date() {}
+ explicit Date(JS::ClippedTime aMilliseconds)
+ : mMsecSinceEpoch(aMilliseconds)
+ {}
+
+ bool IsUndefined() const
+ {
+ return !mMsecSinceEpoch.isValid();
+ }
+
+ JS::ClippedTime TimeStamp() const
+ {
+ return mMsecSinceEpoch;
+ }
+
+ // Returns an integer in the range [-8.64e15, +8.64e15] (-0 excluded), *or*
+ // returns NaN. DO NOT ASSUME THIS IS FINITE!
+ double ToDouble() const
+ {
+ return mMsecSinceEpoch.toDouble();
+ }
+
+ void SetTimeStamp(JS::ClippedTime aMilliseconds)
+ {
+ mMsecSinceEpoch = aMilliseconds;
+ }
+
+ // Can return false if CheckedUnwrap fails. This will NOT throw;
+ // callers should do it as needed.
+ bool SetTimeStamp(JSContext* aCx, JSObject* aObject);
+
+ bool ToDateObject(JSContext* aCx, JS::MutableHandle<JS::Value> aRval) const;
+
+private:
+ JS::ClippedTime mMsecSinceEpoch;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_Date_h
diff --git a/dom/bindings/ErrorIPCUtils.h b/dom/bindings/ErrorIPCUtils.h
new file mode 100644
index 000000000..0e60d8174
--- /dev/null
+++ b/dom/bindings/ErrorIPCUtils.h
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "ipc/IPCMessageUtils.h"
+#include "mozilla/ErrorResult.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Move.h"
+
+#ifndef IPC_ErrorIPCUtils_h
+#define IPC_ErrorIPCUtils_h
+
+namespace IPC {
+
+template<>
+struct ParamTraits<mozilla::dom::ErrNum> :
+ public ContiguousEnumSerializer<mozilla::dom::ErrNum,
+ mozilla::dom::ErrNum(0),
+ mozilla::dom::ErrNum(mozilla::dom::Err_Limit)> {};
+
+template<>
+struct ParamTraits<mozilla::ErrorResult>
+{
+ typedef mozilla::ErrorResult paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ // It should be the case that mMightHaveUnreportedJSException can only be
+ // true when we're expecting a JS exception. We cannot send such messages
+ // over the IPC channel since there is no sane way of transferring the JS
+ // value over to the other side. Callers should never do that.
+ MOZ_ASSERT_IF(aParam.IsJSException(), aParam.mMightHaveUnreportedJSException);
+ if (aParam.IsJSException()
+#ifdef DEBUG
+ || aParam.mMightHaveUnreportedJSException
+#endif
+ ) {
+ MOZ_CRASH("Cannot encode an ErrorResult representing a Javascript exception");
+ }
+
+ WriteParam(aMsg, aParam.mResult);
+ WriteParam(aMsg, aParam.IsErrorWithMessage());
+ WriteParam(aMsg, aParam.IsDOMException());
+ if (aParam.IsErrorWithMessage()) {
+ aParam.SerializeMessage(aMsg);
+ } else if (aParam.IsDOMException()) {
+ aParam.SerializeDOMExceptionInfo(aMsg);
+ }
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ paramType readValue;
+ if (!ReadParam(aMsg, aIter, &readValue.mResult)) {
+ return false;
+ }
+ bool hasMessage = false;
+ if (!ReadParam(aMsg, aIter, &hasMessage)) {
+ return false;
+ }
+ bool hasDOMExceptionInfo = false;
+ if (!ReadParam(aMsg, aIter, &hasDOMExceptionInfo)) {
+ return false;
+ }
+ if (hasMessage && hasDOMExceptionInfo) {
+ // Shouldn't have both!
+ return false;
+ }
+ if (hasMessage && !readValue.DeserializeMessage(aMsg, aIter)) {
+ return false;
+ } else if (hasDOMExceptionInfo &&
+ !readValue.DeserializeDOMExceptionInfo(aMsg, aIter)) {
+ return false;
+ }
+ *aResult = Move(readValue);
+ return true;
+ }
+};
+
+} // namespace IPC
+
+#endif
diff --git a/dom/bindings/ErrorResult.h b/dom/bindings/ErrorResult.h
new file mode 100644
index 000000000..c45e7ea3b
--- /dev/null
+++ b/dom/bindings/ErrorResult.h
@@ -0,0 +1,587 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 set of structs for tracking exceptions that need to be thrown to JS:
+ * ErrorResult and IgnoredErrorResult.
+ *
+ * Conceptually, these structs represent either success or an exception in the
+ * process of being thrown. This means that a failing ErrorResult _must_ be
+ * handled in one of the following ways before coming off the stack:
+ *
+ * 1) Suppressed via SuppressException().
+ * 2) Converted to a pure nsresult return value via StealNSResult().
+ * 3) Converted to an actual pending exception on a JSContext via
+ * MaybeSetPendingException.
+ * 4) Converted to an exception JS::Value (probably to then reject a Promise
+ * with) via dom::ToJSValue.
+ *
+ * An IgnoredErrorResult will automatically do the first of those four things.
+ */
+
+#ifndef mozilla_ErrorResult_h
+#define mozilla_ErrorResult_h
+
+#include <stdarg.h>
+
+#include "js/GCAnnotations.h"
+#include "js/Value.h"
+#include "nscore.h"
+#include "nsStringGlue.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Move.h"
+#include "nsTArray.h"
+#include "nsISupportsImpl.h"
+
+namespace IPC {
+class Message;
+template <typename> struct ParamTraits;
+} // namespace IPC
+class PickleIterator;
+
+namespace mozilla {
+
+namespace dom {
+
+enum ErrNum {
+#define MSG_DEF(_name, _argc, _exn, _str) \
+ _name,
+#include "mozilla/dom/Errors.msg"
+#undef MSG_DEF
+ Err_Limit
+};
+
+// Debug-only compile-time table of the number of arguments of each error, for use in static_assert.
+#if defined(DEBUG) && (defined(__clang__) || defined(__GNUC__))
+uint16_t constexpr ErrorFormatNumArgs[] = {
+#define MSG_DEF(_name, _argc, _exn, _str) \
+ _argc,
+#include "mozilla/dom/Errors.msg"
+#undef MSG_DEF
+};
+#endif
+
+uint16_t
+GetErrorArgCount(const ErrNum aErrorNumber);
+
+namespace binding_detail {
+void ThrowErrorMessage(JSContext* aCx, const unsigned aErrorNumber, ...);
+} // namespace binding_detail
+
+template<typename... Ts>
+inline bool
+ThrowErrorMessage(JSContext* aCx, const ErrNum aErrorNumber, Ts&&... aArgs)
+{
+ binding_detail::ThrowErrorMessage(aCx, static_cast<const unsigned>(aErrorNumber),
+ mozilla::Forward<Ts>(aArgs)...);
+ return false;
+}
+
+struct StringArrayAppender
+{
+ static void Append(nsTArray<nsString>& aArgs, uint16_t aCount)
+ {
+ MOZ_RELEASE_ASSERT(aCount == 0, "Must give at least as many string arguments as are required by the ErrNum.");
+ }
+
+ template<typename... Ts>
+ static void Append(nsTArray<nsString>& aArgs, uint16_t aCount, const nsAString& aFirst, Ts&&... aOtherArgs)
+ {
+ if (aCount == 0) {
+ MOZ_ASSERT(false, "There should not be more string arguments provided than are required by the ErrNum.");
+ return;
+ }
+ aArgs.AppendElement(aFirst);
+ Append(aArgs, aCount - 1, Forward<Ts>(aOtherArgs)...);
+ }
+};
+
+} // namespace dom
+
+class ErrorResult;
+
+namespace binding_danger {
+
+/**
+ * Templated implementation class for various ErrorResult-like things. The
+ * instantiations differ only in terms of their cleanup policies (used in the
+ * destructor), which they can specify via the template argument. Note that
+ * this means it's safe to reinterpret_cast between the instantiations unless
+ * you plan to invoke the destructor through such a cast pointer.
+ *
+ * A cleanup policy consists of two booleans: whether to assert that we've been
+ * reported or suppressed, and whether to then go ahead and suppress the
+ * exception.
+ */
+template<typename CleanupPolicy>
+class TErrorResult {
+public:
+ TErrorResult()
+ : mResult(NS_OK)
+#ifdef DEBUG
+ , mMightHaveUnreportedJSException(false)
+ , mUnionState(HasNothing)
+#endif
+ {
+ }
+
+ ~TErrorResult() {
+ AssertInOwningThread();
+
+ if (CleanupPolicy::assertHandled) {
+ // Consumers should have called one of MaybeSetPendingException
+ // (possibly via ToJSValue), StealNSResult, and SuppressException
+ AssertReportedOrSuppressed();
+ }
+
+ if (CleanupPolicy::suppress) {
+ SuppressException();
+ }
+
+ // And now assert that we're in a good final state.
+ AssertReportedOrSuppressed();
+ }
+
+ TErrorResult(TErrorResult&& aRHS)
+ // Initialize mResult and whatever else we need to default-initialize, so
+ // the ClearUnionData call in our operator= will do the right thing
+ // (nothing).
+ : TErrorResult()
+ {
+ *this = Move(aRHS);
+ }
+ TErrorResult& operator=(TErrorResult&& aRHS);
+
+ explicit TErrorResult(nsresult aRv)
+ : TErrorResult()
+ {
+ AssignErrorCode(aRv);
+ }
+
+ operator ErrorResult&();
+
+ void Throw(nsresult rv) {
+ MOZ_ASSERT(NS_FAILED(rv), "Please don't try throwing success");
+ AssignErrorCode(rv);
+ }
+
+ // Duplicate our current state on the given TErrorResult object. Any
+ // existing errors or messages on the target will be suppressed before
+ // cloning. Our own error state remains unchanged.
+ void CloneTo(TErrorResult& aRv) const;
+
+ // Use SuppressException when you want to suppress any exception that might be
+ // on the TErrorResult. After this call, the TErrorResult will be back a "no
+ // exception thrown" state.
+ void SuppressException();
+
+ // Use StealNSResult() when you want to safely convert the TErrorResult to
+ // an nsresult that you will then return to a caller. This will
+ // SuppressException(), since there will no longer be a way to report it.
+ nsresult StealNSResult() {
+ nsresult rv = ErrorCode();
+ SuppressException();
+ // Don't propagate out our internal error codes that have special meaning.
+ if (rv == NS_ERROR_TYPE_ERR ||
+ rv == NS_ERROR_RANGE_ERR ||
+ rv == NS_ERROR_DOM_JS_EXCEPTION ||
+ rv == NS_ERROR_DOM_DOMEXCEPTION) {
+ // What about NS_ERROR_DOM_EXCEPTION_ON_JSCONTEXT? I guess that can be
+ // legitimately passed on through....
+ // What to pick here?
+ return NS_ERROR_DOM_INVALID_STATE_ERR;
+ }
+
+ return rv;
+ }
+
+ // Use MaybeSetPendingException to convert a TErrorResult to a pending
+ // exception on the given JSContext. This is the normal "throw an exception"
+ // codepath.
+ //
+ // The return value is false if the TErrorResult represents success, true
+ // otherwise. This does mean that in JSAPI method implementations you can't
+ // just use this as |return rv.MaybeSetPendingException(cx)| (though you could
+ // |return !rv.MaybeSetPendingException(cx)|), but in practice pretty much any
+ // consumer would want to do some more work on the success codepath. So
+ // instead the way you use this is:
+ //
+ // if (rv.MaybeSetPendingException(cx)) {
+ // bail out here
+ // }
+ // go on to do something useful
+ //
+ // The success path is inline, since it should be the common case and we don't
+ // want to pay the price of a function call in some of the consumers of this
+ // method in the common case.
+ //
+ // Note that a true return value does NOT mean there is now a pending
+ // exception on aCx, due to uncatchable exceptions. It should still be
+ // considered equivalent to a JSAPI failure in terms of what callers should do
+ // after true is returned.
+ //
+ // After this call, the TErrorResult will no longer return true from Failed(),
+ // since the exception will have moved to the JSContext.
+ bool MaybeSetPendingException(JSContext* cx)
+ {
+ WouldReportJSException();
+ if (!Failed()) {
+ return false;
+ }
+
+ SetPendingException(cx);
+ return true;
+ }
+
+ // Use StealExceptionFromJSContext to convert a pending exception on a
+ // JSContext to a TErrorResult. This function must be called only when a
+ // JSAPI operation failed. It assumes that lack of pending exception on the
+ // JSContext means an uncatchable exception was thrown.
+ //
+ // Codepaths that might call this method must call MightThrowJSException even
+ // if the relevant JSAPI calls do not fail.
+ //
+ // When this function returns, JS_IsExceptionPending(cx) will definitely be
+ // false.
+ void StealExceptionFromJSContext(JSContext* cx);
+
+ template<dom::ErrNum errorNumber, typename... Ts>
+ void ThrowTypeError(Ts&&... messageArgs)
+ {
+ ThrowErrorWithMessage<errorNumber>(NS_ERROR_TYPE_ERR,
+ Forward<Ts>(messageArgs)...);
+ }
+
+ template<dom::ErrNum errorNumber, typename... Ts>
+ void ThrowRangeError(Ts&&... messageArgs)
+ {
+ ThrowErrorWithMessage<errorNumber>(NS_ERROR_RANGE_ERR,
+ Forward<Ts>(messageArgs)...);
+ }
+
+ bool IsErrorWithMessage() const { return ErrorCode() == NS_ERROR_TYPE_ERR || ErrorCode() == NS_ERROR_RANGE_ERR; }
+
+ // Facilities for throwing a preexisting JS exception value via this
+ // TErrorResult. The contract is that any code which might end up calling
+ // ThrowJSException() or StealExceptionFromJSContext() must call
+ // MightThrowJSException() even if no exception is being thrown. Code that
+ // conditionally calls ToJSValue on this TErrorResult only if Failed() must
+ // first call WouldReportJSException even if this TErrorResult has not failed.
+ //
+ // The exn argument to ThrowJSException can be in any compartment. It does
+ // not have to be in the compartment of cx. If someone later uses it, they
+ // will wrap it into whatever compartment they're working in, as needed.
+ void ThrowJSException(JSContext* cx, JS::Handle<JS::Value> exn);
+ bool IsJSException() const { return ErrorCode() == NS_ERROR_DOM_JS_EXCEPTION; }
+
+ // Facilities for throwing a DOMException. If an empty message string is
+ // passed to ThrowDOMException, the default message string for the given
+ // nsresult will be used. The passed-in string must be UTF-8. The nsresult
+ // passed in must be one we create DOMExceptions for; otherwise you may get an
+ // XPConnect Exception.
+ void ThrowDOMException(nsresult rv, const nsACString& message = EmptyCString());
+ bool IsDOMException() const { return ErrorCode() == NS_ERROR_DOM_DOMEXCEPTION; }
+
+ // Flag on the TErrorResult that whatever needs throwing has been
+ // thrown on the JSContext already and we should not mess with it.
+ // If nothing was thrown, this becomes an uncatchable exception.
+ void NoteJSContextException(JSContext* aCx);
+
+ // Check whether the TErrorResult says to just throw whatever is on
+ // the JSContext already.
+ bool IsJSContextException() {
+ return ErrorCode() == NS_ERROR_DOM_EXCEPTION_ON_JSCONTEXT;
+ }
+
+ // Support for uncatchable exceptions.
+ void ThrowUncatchableException() {
+ Throw(NS_ERROR_UNCATCHABLE_EXCEPTION);
+ }
+ bool IsUncatchableException() const {
+ return ErrorCode() == NS_ERROR_UNCATCHABLE_EXCEPTION;
+ }
+
+ void MOZ_ALWAYS_INLINE MightThrowJSException()
+ {
+#ifdef DEBUG
+ mMightHaveUnreportedJSException = true;
+#endif
+ }
+ void MOZ_ALWAYS_INLINE WouldReportJSException()
+ {
+#ifdef DEBUG
+ mMightHaveUnreportedJSException = false;
+#endif
+ }
+
+ // 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) {
+ AssignErrorCode(rv);
+ }
+
+ bool Failed() const {
+ return NS_FAILED(mResult);
+ }
+
+ bool ErrorCodeIs(nsresult rv) const {
+ return mResult == rv;
+ }
+
+ // For use in logging ONLY.
+ uint32_t ErrorCodeAsInt() const {
+ return static_cast<uint32_t>(ErrorCode());
+ }
+
+protected:
+ nsresult ErrorCode() const {
+ return mResult;
+ }
+
+private:
+#ifdef DEBUG
+ enum UnionState {
+ HasMessage,
+ HasDOMExceptionInfo,
+ HasJSException,
+ HasNothing
+ };
+#endif // DEBUG
+
+ friend struct IPC::ParamTraits<TErrorResult>;
+ friend struct IPC::ParamTraits<ErrorResult>;
+ void SerializeMessage(IPC::Message* aMsg) const;
+ bool DeserializeMessage(const IPC::Message* aMsg, PickleIterator* aIter);
+
+ void SerializeDOMExceptionInfo(IPC::Message* aMsg) const;
+ bool DeserializeDOMExceptionInfo(const IPC::Message* aMsg, PickleIterator* aIter);
+
+ // Helper method that creates a new Message for this TErrorResult,
+ // and returns the arguments array from that Message.
+ nsTArray<nsString>& CreateErrorMessageHelper(const dom::ErrNum errorNumber, nsresult errorType);
+
+ template<dom::ErrNum errorNumber, typename... Ts>
+ void ThrowErrorWithMessage(nsresult errorType, Ts&&... messageArgs)
+ {
+#if defined(DEBUG) && (defined(__clang__) || defined(__GNUC__))
+ static_assert(dom::ErrorFormatNumArgs[errorNumber] == sizeof...(messageArgs),
+ "Pass in the right number of arguments");
+#endif
+
+ ClearUnionData();
+
+ nsTArray<nsString>& messageArgsArray = CreateErrorMessageHelper(errorNumber, errorType);
+ uint16_t argCount = dom::GetErrorArgCount(errorNumber);
+ dom::StringArrayAppender::Append(messageArgsArray, argCount,
+ Forward<Ts>(messageArgs)...);
+#ifdef DEBUG
+ mUnionState = HasMessage;
+#endif // DEBUG
+ }
+
+ MOZ_ALWAYS_INLINE void AssertInOwningThread() const {
+#ifdef DEBUG
+ NS_ASSERT_OWNINGTHREAD(TErrorResult);
+#endif
+ }
+
+ void AssignErrorCode(nsresult aRv) {
+ MOZ_ASSERT(aRv != NS_ERROR_TYPE_ERR, "Use ThrowTypeError()");
+ MOZ_ASSERT(aRv != NS_ERROR_RANGE_ERR, "Use ThrowRangeError()");
+ MOZ_ASSERT(!IsErrorWithMessage(), "Don't overwrite errors with message");
+ MOZ_ASSERT(aRv != NS_ERROR_DOM_JS_EXCEPTION, "Use ThrowJSException()");
+ MOZ_ASSERT(!IsJSException(), "Don't overwrite JS exceptions");
+ MOZ_ASSERT(aRv != NS_ERROR_DOM_DOMEXCEPTION, "Use ThrowDOMException()");
+ MOZ_ASSERT(!IsDOMException(), "Don't overwrite DOM exceptions");
+ MOZ_ASSERT(aRv != NS_ERROR_XPC_NOT_ENOUGH_ARGS, "May need to bring back ThrowNotEnoughArgsError");
+ MOZ_ASSERT(aRv != NS_ERROR_DOM_EXCEPTION_ON_JSCONTEXT,
+ "Use NoteJSContextException");
+ // Don't trust people anyway, though.
+ if (aRv == NS_ERROR_TYPE_ERR ||
+ aRv == NS_ERROR_RANGE_ERR ||
+ aRv == NS_ERROR_DOM_JS_EXCEPTION ||
+ aRv == NS_ERROR_DOM_DOMEXCEPTION) {
+ mResult = NS_ERROR_UNEXPECTED;
+ } else {
+ mResult = aRv;
+ }
+ }
+
+ void ClearMessage();
+ void ClearDOMExceptionInfo();
+
+ // ClearUnionData will try to clear the data in our
+ // mMessage/mJSException/mDOMExceptionInfo union. After this the union may be
+ // in an uninitialized state (e.g. mMessage or mDOMExceptionInfo may be
+ // pointing to deleted memory) and the caller must either reinitialize it or
+ // change mResult to something that will not involve us touching the union
+ // anymore.
+ void ClearUnionData();
+
+ // Implementation of MaybeSetPendingException for the case when we're a
+ // failure result.
+ void SetPendingException(JSContext* cx);
+
+ // Methods for setting various specific kinds of pending exceptions.
+ void SetPendingExceptionWithMessage(JSContext* cx);
+ void SetPendingJSException(JSContext* cx);
+ void SetPendingDOMException(JSContext* cx);
+ void SetPendingGenericErrorException(JSContext* cx);
+
+ MOZ_ALWAYS_INLINE void AssertReportedOrSuppressed()
+ {
+ MOZ_ASSERT(!Failed());
+ MOZ_ASSERT(!mMightHaveUnreportedJSException);
+ MOZ_ASSERT(mUnionState == HasNothing);
+ }
+
+ // Special values of mResult:
+ // NS_ERROR_TYPE_ERR -- ThrowTypeError() called on us.
+ // NS_ERROR_RANGE_ERR -- ThrowRangeError() called on us.
+ // NS_ERROR_DOM_JS_EXCEPTION -- ThrowJSException() called on us.
+ // NS_ERROR_UNCATCHABLE_EXCEPTION -- ThrowUncatchableException called on us.
+ // NS_ERROR_DOM_DOMEXCEPTION -- ThrowDOMException() called on us.
+ nsresult mResult;
+
+ struct Message;
+ struct DOMExceptionInfo;
+ // mMessage is set by ThrowErrorWithMessage and reported (and deallocated) by
+ // SetPendingExceptionWithMessage.
+ // mJSException is set (and rooted) by ThrowJSException and reported
+ // (and unrooted) by SetPendingJSException.
+ // mDOMExceptionInfo is set by ThrowDOMException and reported
+ // (and deallocated) by SetPendingDOMException.
+ union {
+ Message* mMessage; // valid when IsErrorWithMessage()
+ JS::Value mJSException; // valid when IsJSException()
+ DOMExceptionInfo* mDOMExceptionInfo; // valid when IsDOMException()
+ };
+
+#ifdef DEBUG
+ // Used to keep track of codepaths that might throw JS exceptions,
+ // for assertion purposes.
+ bool mMightHaveUnreportedJSException;
+
+ // Used to keep track of what's stored in our union right now. Note
+ // that this may be set to HasNothing even if our mResult suggests
+ // we should have something, if we have already cleaned up the
+ // something.
+ UnionState mUnionState;
+
+ // The thread that created this TErrorResult
+ NS_DECL_OWNINGTHREAD;
+#endif
+
+ // Not to be implemented, to make sure people always pass this by
+ // reference, not by value.
+ TErrorResult(const TErrorResult&) = delete;
+ void operator=(const TErrorResult&) = delete;
+};
+
+struct JustAssertCleanupPolicy {
+ static const bool assertHandled = true;
+ static const bool suppress = false;
+};
+
+struct AssertAndSuppressCleanupPolicy {
+ static const bool assertHandled = true;
+ static const bool suppress = true;
+};
+
+struct JustSuppressCleanupPolicy {
+ static const bool assertHandled = false;
+ static const bool suppress = true;
+};
+
+} // namespace binding_danger
+
+// A class people should normally use on the stack when they plan to actually
+// do something with the exception.
+class ErrorResult :
+ public binding_danger::TErrorResult<binding_danger::AssertAndSuppressCleanupPolicy>
+{
+ typedef binding_danger::TErrorResult<binding_danger::AssertAndSuppressCleanupPolicy> BaseErrorResult;
+
+public:
+ ErrorResult()
+ : BaseErrorResult()
+ {}
+
+ ErrorResult(ErrorResult&& aRHS)
+ : BaseErrorResult(Move(aRHS))
+ {}
+
+ explicit ErrorResult(nsresult aRv)
+ : BaseErrorResult(aRv)
+ {}
+
+ void operator=(nsresult rv)
+ {
+ BaseErrorResult::operator=(rv);
+ }
+
+ ErrorResult& operator=(ErrorResult&& aRHS)
+ {
+ BaseErrorResult::operator=(Move(aRHS));
+ return *this;
+ }
+
+private:
+ // Not to be implemented, to make sure people always pass this by
+ // reference, not by value.
+ ErrorResult(const ErrorResult&) = delete;
+ void operator=(const ErrorResult&) = delete;
+};
+
+template<typename CleanupPolicy>
+binding_danger::TErrorResult<CleanupPolicy>::operator ErrorResult&()
+{
+ return *static_cast<ErrorResult*>(
+ reinterpret_cast<TErrorResult<AssertAndSuppressCleanupPolicy>*>(this));
+}
+
+// A class for use when an ErrorResult should just automatically be ignored.
+// This doesn't inherit from ErrorResult so we don't make two separate calls to
+// SuppressException.
+class IgnoredErrorResult :
+ public binding_danger::TErrorResult<binding_danger::JustSuppressCleanupPolicy>
+{
+};
+
+/******************************************************************************
+ ** Macros for checking results
+ ******************************************************************************/
+
+#define ENSURE_SUCCESS(res, ret) \
+ do { \
+ if (res.Failed()) { \
+ nsCString msg; \
+ msg.AppendPrintf("ENSURE_SUCCESS(%s, %s) failed with " \
+ "result 0x%X", #res, #ret, res.ErrorCodeAsInt()); \
+ NS_WARNING(msg.get()); \
+ return ret; \
+ } \
+ } while(0)
+
+#define ENSURE_SUCCESS_VOID(res) \
+ do { \
+ if (res.Failed()) { \
+ nsCString msg; \
+ msg.AppendPrintf("ENSURE_SUCCESS_VOID(%s) failed with " \
+ "result 0x%X", #res, res.ErrorCodeAsInt()); \
+ NS_WARNING(msg.get()); \
+ return; \
+ } \
+ } while(0)
+
+} // namespace mozilla
+
+#endif /* mozilla_ErrorResult_h */
diff --git a/dom/bindings/Errors.msg b/dom/bindings/Errors.msg
new file mode 100644
index 000000000..142ccfdd6
--- /dev/null
+++ b/dom/bindings/Errors.msg
@@ -0,0 +1,107 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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>, <JS_EXN_TYPE>, <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.
+ *
+ * <JS_EXN_TYPE> is a JSExnType which specifies which kind of error the JS
+ * engine should throw.
+ *
+ * <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, 3, JSEXN_TYPEERR, "{0} '{1}' is not a valid value for enumeration {2}.")
+MSG_DEF(MSG_MISSING_ARGUMENTS, 1, JSEXN_TYPEERR, "Not enough arguments to {0}.")
+MSG_DEF(MSG_NOT_OBJECT, 1, JSEXN_TYPEERR, "{0} is not an object.")
+MSG_DEF(MSG_NOT_CALLABLE, 1, JSEXN_TYPEERR, "{0} is not callable.")
+MSG_DEF(MSG_NOT_CONSTRUCTOR, 1, JSEXN_TYPEERR, "{0} is not a constructor.")
+MSG_DEF(MSG_DOES_NOT_IMPLEMENT_INTERFACE, 2, JSEXN_TYPEERR, "{0} does not implement interface {1}.")
+MSG_DEF(MSG_METHOD_THIS_DOES_NOT_IMPLEMENT_INTERFACE, 2, JSEXN_TYPEERR, "'{0}' called on an object that does not implement interface {1}.")
+MSG_DEF(MSG_METHOD_THIS_UNWRAPPING_DENIED, 1, JSEXN_TYPEERR, "Permission to call '{0}' denied.")
+MSG_DEF(MSG_THIS_DOES_NOT_IMPLEMENT_INTERFACE, 1, JSEXN_TYPEERR, "\"this\" object does not implement interface {0}.")
+MSG_DEF(MSG_NOT_IN_UNION, 2, JSEXN_TYPEERR, "{0} could not be converted to any of: {1}.")
+MSG_DEF(MSG_ILLEGAL_CONSTRUCTOR, 0, JSEXN_TYPEERR, "Illegal constructor.")
+MSG_DEF(MSG_CONSTRUCTOR_WITHOUT_NEW, 1, JSEXN_TYPEERR, "Constructor {0} requires 'new'")
+MSG_DEF(MSG_ENFORCE_RANGE_NON_FINITE, 1, JSEXN_TYPEERR, "Non-finite value is out of range for {0}.")
+MSG_DEF(MSG_ENFORCE_RANGE_OUT_OF_RANGE, 1, JSEXN_TYPEERR, "Value is out of range for {0}.")
+MSG_DEF(MSG_NOT_SEQUENCE, 1, JSEXN_TYPEERR, "{0} can't be converted to a sequence.")
+MSG_DEF(MSG_NOT_DICTIONARY, 1, JSEXN_TYPEERR, "{0} can't be converted to a dictionary.")
+MSG_DEF(MSG_OVERLOAD_RESOLUTION_FAILED, 3, JSEXN_TYPEERR, "Argument {0} is not valid for any of the {1}-argument overloads of {2}.")
+MSG_DEF(MSG_GLOBAL_NOT_NATIVE, 0, JSEXN_TYPEERR, "Global is not a native object.")
+MSG_DEF(MSG_ENCODING_NOT_SUPPORTED, 1, JSEXN_RANGEERR, "The given encoding '{0}' is not supported.")
+MSG_DEF(MSG_DOM_ENCODING_NOT_UTF, 0, JSEXN_RANGEERR, "The encoding must be utf-8, utf-16, or utf-16be.")
+MSG_DEF(MSG_DOM_DECODING_FAILED, 0, JSEXN_TYPEERR, "Decoding failed.")
+MSG_DEF(MSG_NOT_FINITE, 1, JSEXN_TYPEERR, "{0} is not a finite floating-point value.")
+MSG_DEF(MSG_INVALID_VERSION, 0, JSEXN_TYPEERR, "0 (Zero) is not a valid database version.")
+MSG_DEF(MSG_INVALID_BYTESTRING, 2, JSEXN_TYPEERR, "Cannot convert string to ByteString because the character"
+ " at index {0} has value {1} which is greater than 255.")
+MSG_DEF(MSG_NOT_DATE, 1, JSEXN_TYPEERR, "{0} is not a date.")
+MSG_DEF(MSG_INVALID_ADVANCE_COUNT, 0, JSEXN_TYPEERR, "0 (Zero) is not a valid advance count.")
+MSG_DEF(MSG_DEFINEPROPERTY_ON_GSP, 0, JSEXN_TYPEERR, "Not allowed to define a property on the named properties object.")
+MSG_DEF(MSG_INVALID_URL, 1, JSEXN_TYPEERR, "{0} is not a valid URL.")
+MSG_DEF(MSG_URL_HAS_CREDENTIALS, 1, JSEXN_TYPEERR, "{0} is an url with embedded credentials.")
+MSG_DEF(MSG_METADATA_NOT_CONFIGURED, 0, JSEXN_TYPEERR, "Either size or lastModified should be true.")
+MSG_DEF(MSG_INVALID_READ_SIZE, 0, JSEXN_TYPEERR, "0 (Zero) is not a valid read size.")
+MSG_DEF(MSG_HEADERS_IMMUTABLE, 0, JSEXN_TYPEERR, "Headers are immutable and cannot be modified.")
+MSG_DEF(MSG_INVALID_HEADER_NAME, 1, JSEXN_TYPEERR, "{0} is an invalid header name.")
+MSG_DEF(MSG_INVALID_HEADER_VALUE, 1, JSEXN_TYPEERR, "{0} is an invalid header value.")
+MSG_DEF(MSG_INVALID_HEADER_SEQUENCE, 0, JSEXN_TYPEERR, "Headers require name/value tuples when being initialized by a sequence.")
+MSG_DEF(MSG_PERMISSION_DENIED_TO_PASS_ARG, 1, JSEXN_TYPEERR, "Permission denied to pass cross-origin object as {0}.")
+MSG_DEF(MSG_MISSING_REQUIRED_DICTIONARY_MEMBER, 1, JSEXN_TYPEERR, "Missing required {0}.")
+MSG_DEF(MSG_REQUEST_INTEGRITY_METADATA_NOT_EMPTY, 0, JSEXN_TYPEERR, "Request integrity metadata should be an empty string when in no-cors mode.")
+MSG_DEF(MSG_INVALID_REQUEST_METHOD, 1, JSEXN_TYPEERR, "Invalid request method {0}.")
+MSG_DEF(MSG_INVALID_REQUEST_MODE, 1, JSEXN_TYPEERR, "Invalid request mode {0}.")
+MSG_DEF(MSG_INVALID_REFERRER_URL, 1, JSEXN_TYPEERR, "Invalid referrer URL {0}.")
+MSG_DEF(MSG_CROSS_ORIGIN_REFERRER_URL, 2, JSEXN_TYPEERR, "Referrer URL {0} cannot be cross-origin to the entry settings object ({1}).")
+MSG_DEF(MSG_FETCH_BODY_CONSUMED_ERROR, 0, JSEXN_TYPEERR, "Body has already been consumed.")
+MSG_DEF(MSG_RESPONSE_INVALID_STATUSTEXT_ERROR, 0, JSEXN_TYPEERR, "Response statusText may not contain newline or carriage return.")
+MSG_DEF(MSG_FETCH_FAILED, 0, JSEXN_TYPEERR, "NetworkError when attempting to fetch resource.")
+MSG_DEF(MSG_NO_BODY_ALLOWED_FOR_GET_AND_HEAD, 0, JSEXN_TYPEERR, "HEAD or GET Request cannot have a body.")
+MSG_DEF(MSG_RESPONSE_NULL_STATUS_WITH_BODY, 0, JSEXN_TYPEERR, "Response body is given with a null body status.")
+MSG_DEF(MSG_DEFINE_NON_CONFIGURABLE_PROP_ON_WINDOW, 0, JSEXN_TYPEERR, "Not allowed to define a non-configurable property on the WindowProxy object")
+MSG_DEF(MSG_INVALID_ZOOMANDPAN_VALUE_ERROR, 0, JSEXN_RANGEERR, "Invalid zoom and pan value.")
+MSG_DEF(MSG_INVALID_TRANSFORM_ANGLE_ERROR, 0, JSEXN_RANGEERR, "Invalid transform angle.")
+MSG_DEF(MSG_INVALID_RESPONSE_STATUSCODE_ERROR, 0, JSEXN_RANGEERR, "Invalid response status code.")
+MSG_DEF(MSG_INVALID_REDIRECT_STATUSCODE_ERROR, 0, JSEXN_RANGEERR, "Invalid redirect status code.")
+MSG_DEF(MSG_INVALID_URL_SCHEME, 2, JSEXN_TYPEERR, "{0} URL {1} must be either http:// or https://.")
+MSG_DEF(MSG_RESPONSE_URL_IS_NULL, 0, JSEXN_TYPEERR, "Cannot set Response.finalURL when Response.url is null.")
+MSG_DEF(MSG_RESPONSE_HAS_VARY_STAR, 0, JSEXN_TYPEERR, "Invalid Response object with a 'Vary: *' header.")
+MSG_DEF(MSG_BAD_FORMDATA, 0, JSEXN_TYPEERR, "Could not parse content as FormData.")
+MSG_DEF(MSG_NO_ACTIVE_WORKER, 1, JSEXN_TYPEERR, "No active worker for scope {0}.")
+MSG_DEF(MSG_NOTIFICATION_PERMISSION_DENIED, 0, JSEXN_TYPEERR, "Permission to show Notification denied.")
+MSG_DEF(MSG_NOTIFICATION_NO_CONSTRUCTOR_IN_SERVICEWORKER, 0, JSEXN_TYPEERR, "Notification constructor cannot be used in ServiceWorkerGlobalScope. Use registration.showNotification() instead.")
+MSG_DEF(MSG_INVALID_SCOPE, 2, JSEXN_TYPEERR, "Invalid scope trying to resolve {0} with base URL {1}.")
+MSG_DEF(MSG_INVALID_KEYFRAME_OFFSETS, 0, JSEXN_TYPEERR, "Keyframes with specified offsets must be in order and all be in the range [0, 1].")
+MSG_DEF(MSG_ILLEGAL_PROMISE_CONSTRUCTOR, 0, JSEXN_TYPEERR, "Non-constructor value passed to NewPromiseCapability.")
+MSG_DEF(MSG_PROMISE_CAPABILITY_HAS_SOMETHING_ALREADY, 0, JSEXN_TYPEERR, "GetCapabilitiesExecutor function already invoked with non-undefined values.")
+MSG_DEF(MSG_PROMISE_RESOLVE_FUNCTION_NOT_CALLABLE, 0, JSEXN_TYPEERR, "A Promise subclass passed a non-callable value as the resolve function.")
+MSG_DEF(MSG_PROMISE_REJECT_FUNCTION_NOT_CALLABLE, 0, JSEXN_TYPEERR, "A Promise subclass passed a non-callable value as the reject function.")
+MSG_DEF(MSG_PROMISE_ARG_NOT_ITERABLE, 1, JSEXN_TYPEERR, "{0} is not iterable")
+MSG_DEF(MSG_IS_NOT_PROMISE, 1, JSEXN_TYPEERR, "{0} is not a Promise")
+MSG_DEF(MSG_SW_INSTALL_ERROR, 2, JSEXN_TYPEERR, "ServiceWorker script at {0} for scope {1} encountered an error during installation.")
+MSG_DEF(MSG_SW_SCRIPT_THREW, 2, JSEXN_TYPEERR, "ServiceWorker script at {0} for scope {1} threw an exception during script evaluation.")
+MSG_DEF(MSG_TYPEDARRAY_IS_SHARED, 1, JSEXN_TYPEERR, "{0} can't be a typed array on SharedArrayBuffer")
+MSG_DEF(MSG_CACHE_ADD_FAILED_RESPONSE, 3, JSEXN_TYPEERR, "Cache got {0} response with bad status {1} while trying to add request {2}")
+MSG_DEF(MSG_SW_UPDATE_BAD_REGISTRATION, 2, JSEXN_TYPEERR, "Failed to update the ServiceWorker for scope {0} because the registration has been {1} since the update was scheduled.")
+MSG_DEF(MSG_INVALID_DURATION_ERROR, 1, JSEXN_TYPEERR, "Invalid duration '{0}'.")
+MSG_DEF(MSG_INVALID_EASING_ERROR, 1, JSEXN_TYPEERR, "Invalid easing '{0}'.")
+MSG_DEF(MSG_INVALID_SPACING_MODE_ERROR, 1, JSEXN_TYPEERR, "Invalid spacing '{0}'.")
+MSG_DEF(MSG_USELESS_SETTIMEOUT, 1, JSEXN_TYPEERR, "Useless {0} call (missing quotes around argument?)")
+MSG_DEF(MSG_TOKENLIST_NO_SUPPORTED_TOKENS, 2, JSEXN_TYPEERR, "{0} attribute of <{1}> does not define any supported tokens")
+MSG_DEF(MSG_CACHE_STREAM_CLOSED, 0, JSEXN_TYPEERR, "Response body is a cache file stream that has already been closed.")
+MSG_DEF(MSG_TIME_VALUE_OUT_OF_RANGE, 1, JSEXN_TYPEERR, "{0} is outside the supported range for time values.")
+MSG_DEF(MSG_ONLY_IF_CACHED_WITHOUT_SAME_ORIGIN, 1, JSEXN_TYPEERR, "Request mode '{0}' was used, but request cache mode 'only-if-cached' can only be used with request mode 'same-origin'.")
+MSG_DEF(MSG_THRESHOLD_RANGE_ERROR, 0, JSEXN_RANGEERR, "Threshold values must all be in the range [0, 1].")
+MSG_DEF(MSG_CACHE_OPEN_FAILED, 0, JSEXN_TYPEERR, "CacheStorage.open() failed to access the storage system.")
diff --git a/dom/bindings/Exceptions.cpp b/dom/bindings/Exceptions.cpp
new file mode 100644
index 000000000..a3f807688
--- /dev/null
+++ b/dom/bindings/Exceptions.cpp
@@ -0,0 +1,709 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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/dom/Exceptions.h"
+
+#include "js/GCAPI.h"
+#include "js/TypeDecls.h"
+#include "jsapi.h"
+#include "jsprf.h"
+#include "mozilla/CycleCollectedJSContext.h"
+#include "mozilla/dom/BindingUtils.h"
+#include "mozilla/dom/DOMException.h"
+#include "mozilla/dom/ScriptSettings.h"
+#include "nsIProgrammingLanguage.h"
+#include "nsPIDOMWindow.h"
+#include "nsServiceManagerUtils.h"
+#include "nsThreadUtils.h"
+#include "XPCWrapper.h"
+#include "WorkerPrivate.h"
+#include "nsContentUtils.h"
+
+namespace mozilla {
+namespace dom {
+
+// Throw the given exception value if it's safe. If it's not safe, then
+// synthesize and throw a new exception value for NS_ERROR_UNEXPECTED. The
+// incoming value must be in the compartment of aCx. This function guarantees
+// that an exception is pending on aCx when it returns.
+static void
+ThrowExceptionValueIfSafe(JSContext* aCx, JS::Handle<JS::Value> exnVal,
+ nsIException* aOriginalException)
+{
+ MOZ_ASSERT(aOriginalException);
+
+ if (!exnVal.isObject()) {
+ JS_SetPendingException(aCx, exnVal);
+ return;
+ }
+
+ JS::Rooted<JSObject*> exnObj(aCx, &exnVal.toObject());
+ MOZ_ASSERT(js::IsObjectInContextCompartment(exnObj, aCx),
+ "exnObj needs to be in the right compartment for the "
+ "CheckedUnwrap thing to make sense");
+
+ if (js::CheckedUnwrap(exnObj)) {
+ // This is an object we're allowed to work with, so just go ahead and throw
+ // it.
+ JS_SetPendingException(aCx, exnVal);
+ return;
+ }
+
+ // We could probably Throw(aCx, NS_ERROR_UNEXPECTED) here, and it would do the
+ // right thing due to there not being an existing exception on the runtime at
+ // this point, but it's clearer to explicitly do the thing we want done. This
+ // is also why we don't just call ThrowExceptionObject on the Exception we
+ // create: it would do the right thing, but that fact is not obvious.
+ RefPtr<Exception> syntheticException =
+ CreateException(aCx, NS_ERROR_UNEXPECTED);
+ JS::Rooted<JS::Value> syntheticVal(aCx);
+ if (!GetOrCreateDOMReflector(aCx, syntheticException, &syntheticVal)) {
+ return;
+ }
+ MOZ_ASSERT(syntheticVal.isObject() &&
+ !js::IsWrapper(&syntheticVal.toObject()),
+ "Must have a reflector here, not a wrapper");
+ JS_SetPendingException(aCx, syntheticVal);
+}
+
+void
+ThrowExceptionObject(JSContext* aCx, nsIException* aException)
+{
+ // See if we really have an Exception.
+ nsCOMPtr<Exception> exception = do_QueryInterface(aException);
+ if (exception) {
+ ThrowExceptionObject(aCx, exception);
+ return;
+ }
+
+ // We only have an nsIException (probably an XPCWrappedJS). Fall back on old
+ // wrapping.
+ MOZ_ASSERT(NS_IsMainThread());
+
+ JS::Rooted<JS::Value> val(aCx);
+ if (!WrapObject(aCx, aException, &NS_GET_IID(nsIException), &val)) {
+ return;
+ }
+
+ ThrowExceptionValueIfSafe(aCx, val, aException);
+}
+
+void
+ThrowExceptionObject(JSContext* aCx, Exception* aException)
+{
+ JS::Rooted<JS::Value> thrown(aCx);
+
+ // If we stored the original thrown JS value in the exception
+ // (see XPCConvert::ConstructException) and we are in a web context
+ // (i.e., not chrome), rethrow the original value. This only applies to JS
+ // implemented components so we only need to check for this on the main
+ // thread.
+ if (NS_IsMainThread() && !nsContentUtils::IsCallerChrome() &&
+ aException->StealJSVal(thrown.address())) {
+ // Now check for the case when thrown is a number which matches
+ // aException->GetResult(). This would indicate that what actually got
+ // thrown was an nsresult value. In that situation, we should go back
+ // through dom::Throw with that nsresult value, because it will make sure to
+ // create the right sort of Exception or DOMException, with the right
+ // global.
+ if (thrown.isNumber()) {
+ nsresult exceptionResult;
+ if (NS_SUCCEEDED(aException->GetResult(&exceptionResult)) &&
+ double(exceptionResult) == thrown.toNumber()) {
+ Throw(aCx, exceptionResult);
+ return;
+ }
+ }
+ if (!JS_WrapValue(aCx, &thrown)) {
+ return;
+ }
+ ThrowExceptionValueIfSafe(aCx, thrown, aException);
+ return;
+ }
+
+ if (!GetOrCreateDOMReflector(aCx, aException, &thrown)) {
+ return;
+ }
+
+ ThrowExceptionValueIfSafe(aCx, thrown, aException);
+}
+
+bool
+Throw(JSContext* aCx, nsresult aRv, const nsACString& aMessage)
+{
+ if (aRv == NS_ERROR_UNCATCHABLE_EXCEPTION) {
+ // Nuke any existing exception on aCx, to make sure we're uncatchable.
+ JS_ClearPendingException(aCx);
+ return false;
+ }
+
+ if (JS_IsExceptionPending(aCx)) {
+ // Don't clobber the existing exception.
+ return false;
+ }
+
+ CycleCollectedJSContext* context = CycleCollectedJSContext::Get();
+ nsCOMPtr<nsIException> existingException = context->GetPendingException();
+ // Make sure to clear the pending exception now. Either we're going to reuse
+ // it (and we already grabbed it), or we plan to throw something else and this
+ // pending exception is no longer relevant.
+ context->SetPendingException(nullptr);
+
+ // Ignore the pending exception if we have a non-default message passed in.
+ if (aMessage.IsEmpty() && existingException) {
+ nsresult nr;
+ if (NS_SUCCEEDED(existingException->GetResult(&nr)) &&
+ aRv == nr) {
+ // Reuse the existing exception.
+ ThrowExceptionObject(aCx, existingException);
+ return false;
+ }
+ }
+
+ RefPtr<Exception> finalException = CreateException(aCx, aRv, aMessage);
+ MOZ_ASSERT(finalException);
+
+ ThrowExceptionObject(aCx, finalException);
+ return false;
+}
+
+void
+ThrowAndReport(nsPIDOMWindowInner* aWindow, nsresult aRv)
+{
+ MOZ_ASSERT(aRv != NS_ERROR_UNCATCHABLE_EXCEPTION,
+ "Doesn't make sense to report uncatchable exceptions!");
+ AutoJSAPI jsapi;
+ if (NS_WARN_IF(!jsapi.Init(aWindow))) {
+ return;
+ }
+
+ Throw(jsapi.cx(), aRv);
+}
+
+already_AddRefed<Exception>
+CreateException(JSContext* aCx, nsresult aRv, const nsACString& aMessage)
+{
+ // Do we use DOM exceptions for this error code?
+ switch (NS_ERROR_GET_MODULE(aRv)) {
+ case NS_ERROR_MODULE_DOM:
+ case NS_ERROR_MODULE_SVG:
+ case NS_ERROR_MODULE_DOM_XPATH:
+ case NS_ERROR_MODULE_DOM_INDEXEDDB:
+ case NS_ERROR_MODULE_DOM_FILEHANDLE:
+ case NS_ERROR_MODULE_DOM_ANIM:
+ case NS_ERROR_MODULE_DOM_PUSH:
+ case NS_ERROR_MODULE_DOM_MEDIA:
+ if (aMessage.IsEmpty()) {
+ return DOMException::Create(aRv);
+ }
+ return DOMException::Create(aRv, aMessage);
+ default:
+ break;
+ }
+
+ // If not, use the default.
+ RefPtr<Exception> exception =
+ new Exception(aMessage, aRv, EmptyCString(), nullptr, nullptr);
+ return exception.forget();
+}
+
+already_AddRefed<nsIStackFrame>
+GetCurrentJSStack(int32_t aMaxDepth)
+{
+ // is there a current context available?
+ JSContext* cx = nsContentUtils::GetCurrentJSContextForThread();
+
+ if (!cx || !js::GetContextCompartment(cx)) {
+ return nullptr;
+ }
+
+ static const unsigned MAX_FRAMES = 100;
+ if (aMaxDepth < 0) {
+ aMaxDepth = MAX_FRAMES;
+ }
+
+ JS::StackCapture captureMode = aMaxDepth == 0
+ ? JS::StackCapture(JS::AllFrames())
+ : JS::StackCapture(JS::MaxFrames(aMaxDepth));
+
+ return dom::exceptions::CreateStack(cx, mozilla::Move(captureMode));
+}
+
+namespace exceptions {
+
+class JSStackFrame : public nsIStackFrame
+{
+public:
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(JSStackFrame)
+ NS_DECL_NSISTACKFRAME
+
+ // aStack must not be null.
+ explicit JSStackFrame(JS::Handle<JSObject*> aStack);
+
+protected:
+ int32_t GetLineno(JSContext* aCx);
+
+ int32_t GetColNo(JSContext* aCx);
+
+private:
+ virtual ~JSStackFrame();
+
+ JS::Heap<JSObject*> mStack;
+ nsString mFormattedStack;
+
+ nsCOMPtr<nsIStackFrame> mCaller;
+ nsCOMPtr<nsIStackFrame> mAsyncCaller;
+ nsString mFilename;
+ nsString mFunname;
+ nsString mAsyncCause;
+ int32_t mLineno;
+ int32_t mColNo;
+
+ bool mFilenameInitialized;
+ bool mFunnameInitialized;
+ bool mLinenoInitialized;
+ bool mColNoInitialized;
+ bool mAsyncCauseInitialized;
+ bool mAsyncCallerInitialized;
+ bool mCallerInitialized;
+ bool mFormattedStackInitialized;
+};
+
+JSStackFrame::JSStackFrame(JS::Handle<JSObject*> aStack)
+ : mStack(aStack)
+ , mLineno(0)
+ , mColNo(0)
+ , mFilenameInitialized(false)
+ , mFunnameInitialized(false)
+ , mLinenoInitialized(false)
+ , mColNoInitialized(false)
+ , mAsyncCauseInitialized(false)
+ , mAsyncCallerInitialized(false)
+ , mCallerInitialized(false)
+ , mFormattedStackInitialized(false)
+{
+ MOZ_ASSERT(mStack);
+
+ mozilla::HoldJSObjects(this);
+}
+
+JSStackFrame::~JSStackFrame()
+{
+ mozilla::DropJSObjects(this);
+}
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(JSStackFrame)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(JSStackFrame)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mCaller)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mAsyncCaller)
+ tmp->mStack = nullptr;
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(JSStackFrame)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCaller)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAsyncCaller)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(JSStackFrame)
+ NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mStack)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(JSStackFrame)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(JSStackFrame)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(JSStackFrame)
+ NS_INTERFACE_MAP_ENTRY(nsIStackFrame)
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+NS_IMETHODIMP JSStackFrame::GetLanguage(uint32_t* aLanguage)
+{
+ *aLanguage = nsIProgrammingLanguage::JAVASCRIPT;
+ return NS_OK;
+}
+
+NS_IMETHODIMP JSStackFrame::GetLanguageName(nsACString& aLanguageName)
+{
+ aLanguageName.AssignLiteral("JavaScript");
+ return NS_OK;
+}
+
+// Helper method to get the value of a stack property, if it's not already
+// cached. This will make sure we skip the cache if the access is happening
+// over Xrays.
+//
+// @argument aStack the stack we're working with; must be non-null.
+// @argument aPropGetter the getter function to call.
+// @argument aIsCached whether we've cached this property's value before.
+//
+// @argument [out] aCanCache whether the value can get cached.
+// @argument [out] aUseCachedValue if true, just use the cached value.
+// @argument [out] aValue the value we got from the stack.
+template<typename ReturnType, typename GetterOutParamType>
+static void
+GetValueIfNotCached(JSContext* aCx, const JS::Heap<JSObject*>& aStack,
+ JS::SavedFrameResult (*aPropGetter)(JSContext*,
+ JS::Handle<JSObject*>,
+ GetterOutParamType,
+ JS::SavedFrameSelfHosted),
+ bool aIsCached, bool* aCanCache, bool* aUseCachedValue,
+ ReturnType aValue)
+{
+ MOZ_ASSERT(aStack);
+
+ JS::Rooted<JSObject*> stack(aCx, aStack);
+ // Allow caching if aCx and stack are same-compartment. Otherwise take the
+ // slow path.
+ *aCanCache = js::GetContextCompartment(aCx) == js::GetObjectCompartment(stack);
+ if (*aCanCache && aIsCached) {
+ *aUseCachedValue = true;
+ return;
+ }
+
+ *aUseCachedValue = false;
+
+ aPropGetter(aCx, stack, aValue, JS::SavedFrameSelfHosted::Exclude);
+}
+
+NS_IMETHODIMP JSStackFrame::GetFilename(JSContext* aCx, nsAString& aFilename)
+{
+ if (!mStack) {
+ aFilename.Truncate();
+ return NS_OK;
+ }
+
+ JS::Rooted<JSString*> filename(aCx);
+ bool canCache = false, useCachedValue = false;
+ GetValueIfNotCached(aCx, mStack, JS::GetSavedFrameSource,
+ mFilenameInitialized,
+ &canCache, &useCachedValue, &filename);
+ if (useCachedValue) {
+ aFilename = mFilename;
+ return NS_OK;
+ }
+
+ nsAutoJSString str;
+ if (!str.init(aCx, filename)) {
+ JS_ClearPendingException(aCx);
+ aFilename.Truncate();
+ return NS_OK;
+ }
+ aFilename = str;
+
+ if (canCache) {
+ mFilename = str;
+ mFilenameInitialized = true;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP JSStackFrame::GetName(JSContext* aCx, nsAString& aFunction)
+{
+ if (!mStack) {
+ aFunction.Truncate();
+ return NS_OK;
+ }
+
+ JS::Rooted<JSString*> name(aCx);
+ bool canCache = false, useCachedValue = false;
+ GetValueIfNotCached(aCx, mStack, JS::GetSavedFrameFunctionDisplayName,
+ mFunnameInitialized, &canCache, &useCachedValue,
+ &name);
+
+ if (useCachedValue) {
+ aFunction = mFunname;
+ return NS_OK;
+ }
+
+ if (name) {
+ nsAutoJSString str;
+ if (!str.init(aCx, name)) {
+ JS_ClearPendingException(aCx);
+ aFunction.Truncate();
+ return NS_OK;
+ }
+ aFunction = str;
+ } else {
+ aFunction.SetIsVoid(true);
+ }
+
+ if (canCache) {
+ mFunname = aFunction;
+ mFunnameInitialized = true;
+ }
+
+ return NS_OK;
+}
+
+int32_t
+JSStackFrame::GetLineno(JSContext* aCx)
+{
+ if (!mStack) {
+ return 0;
+ }
+
+ uint32_t line;
+ bool canCache = false, useCachedValue = false;
+ GetValueIfNotCached(aCx, mStack, JS::GetSavedFrameLine, mLinenoInitialized,
+ &canCache, &useCachedValue, &line);
+
+ if (useCachedValue) {
+ return mLineno;
+ }
+
+ if (canCache) {
+ mLineno = line;
+ mLinenoInitialized = true;
+ }
+
+ return line;
+}
+
+NS_IMETHODIMP JSStackFrame::GetLineNumber(JSContext* aCx, int32_t* aLineNumber)
+{
+ *aLineNumber = GetLineno(aCx);
+ return NS_OK;
+}
+
+int32_t
+JSStackFrame::GetColNo(JSContext* aCx)
+{
+ if (!mStack) {
+ return 0;
+ }
+
+ uint32_t col;
+ bool canCache = false, useCachedValue = false;
+ GetValueIfNotCached(aCx, mStack, JS::GetSavedFrameColumn, mColNoInitialized,
+ &canCache, &useCachedValue, &col);
+
+ if (useCachedValue) {
+ return mColNo;
+ }
+
+ if (canCache) {
+ mColNo = col;
+ mColNoInitialized = true;
+ }
+
+ return col;
+}
+
+NS_IMETHODIMP JSStackFrame::GetColumnNumber(JSContext* aCx,
+ int32_t* aColumnNumber)
+{
+ *aColumnNumber = GetColNo(aCx);
+ return NS_OK;
+}
+
+NS_IMETHODIMP JSStackFrame::GetSourceLine(nsACString& aSourceLine)
+{
+ aSourceLine.Truncate();
+ return NS_OK;
+}
+
+NS_IMETHODIMP JSStackFrame::GetAsyncCause(JSContext* aCx,
+ nsAString& aAsyncCause)
+{
+ if (!mStack) {
+ aAsyncCause.Truncate();
+ return NS_OK;
+ }
+
+ JS::Rooted<JSString*> asyncCause(aCx);
+ bool canCache = false, useCachedValue = false;
+ GetValueIfNotCached(aCx, mStack, JS::GetSavedFrameAsyncCause,
+ mAsyncCauseInitialized, &canCache, &useCachedValue,
+ &asyncCause);
+
+ if (useCachedValue) {
+ aAsyncCause = mAsyncCause;
+ return NS_OK;
+ }
+
+ if (asyncCause) {
+ nsAutoJSString str;
+ if (!str.init(aCx, asyncCause)) {
+ JS_ClearPendingException(aCx);
+ aAsyncCause.Truncate();
+ return NS_OK;
+ }
+ aAsyncCause = str;
+ } else {
+ aAsyncCause.SetIsVoid(true);
+ }
+
+ if (canCache) {
+ mAsyncCause = aAsyncCause;
+ mAsyncCauseInitialized = true;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP JSStackFrame::GetAsyncCaller(JSContext* aCx,
+ nsIStackFrame** aAsyncCaller)
+{
+ if (!mStack) {
+ *aAsyncCaller = nullptr;
+ return NS_OK;
+ }
+
+ JS::Rooted<JSObject*> asyncCallerObj(aCx);
+ bool canCache = false, useCachedValue = false;
+ GetValueIfNotCached(aCx, mStack, JS::GetSavedFrameAsyncParent,
+ mAsyncCallerInitialized, &canCache, &useCachedValue,
+ &asyncCallerObj);
+
+ if (useCachedValue) {
+ NS_IF_ADDREF(*aAsyncCaller = mAsyncCaller);
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIStackFrame> asyncCaller =
+ asyncCallerObj ? new JSStackFrame(asyncCallerObj) : nullptr;
+ asyncCaller.forget(aAsyncCaller);
+
+ if (canCache) {
+ mAsyncCaller = *aAsyncCaller;
+ mAsyncCallerInitialized = true;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP JSStackFrame::GetCaller(JSContext* aCx, nsIStackFrame** aCaller)
+{
+ if (!mStack) {
+ *aCaller = nullptr;
+ return NS_OK;
+ }
+
+ JS::Rooted<JSObject*> callerObj(aCx);
+ bool canCache = false, useCachedValue = false;
+ GetValueIfNotCached(aCx, mStack, JS::GetSavedFrameParent, mCallerInitialized,
+ &canCache, &useCachedValue, &callerObj);
+
+ if (useCachedValue) {
+ NS_IF_ADDREF(*aCaller = mCaller);
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIStackFrame> caller =
+ callerObj ? new JSStackFrame(callerObj) : nullptr;
+ caller.forget(aCaller);
+
+ if (canCache) {
+ mCaller = *aCaller;
+ mCallerInitialized = true;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP JSStackFrame::GetFormattedStack(JSContext* aCx, nsAString& aStack)
+{
+ if (!mStack) {
+ aStack.Truncate();
+ return NS_OK;
+ }
+
+ // Sadly we can't use GetValueIfNotCached here, because our getter
+ // returns bool, not JS::SavedFrameResult. Maybe it's possible to
+ // make the templates more complicated to deal, but in the meantime
+ // let's just inline GetValueIfNotCached here.
+
+ // Allow caching if aCx and stack are same-compartment. Otherwise take the
+ // slow path.
+ bool canCache =
+ js::GetContextCompartment(aCx) == js::GetObjectCompartment(mStack);
+ if (canCache && mFormattedStackInitialized) {
+ aStack = mFormattedStack;
+ return NS_OK;
+ }
+
+ JS::Rooted<JSObject*> stack(aCx, mStack);
+
+ JS::Rooted<JSString*> formattedStack(aCx);
+ if (!JS::BuildStackString(aCx, stack, &formattedStack)) {
+ JS_ClearPendingException(aCx);
+ aStack.Truncate();
+ return NS_OK;
+ }
+
+ nsAutoJSString str;
+ if (!str.init(aCx, formattedStack)) {
+ JS_ClearPendingException(aCx);
+ aStack.Truncate();
+ return NS_OK;
+ }
+
+ aStack = str;
+
+ if (canCache) {
+ mFormattedStack = str;
+ mFormattedStackInitialized = true;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP JSStackFrame::GetNativeSavedFrame(JS::MutableHandle<JS::Value> aSavedFrame)
+{
+ aSavedFrame.setObjectOrNull(mStack);
+ return NS_OK;
+}
+
+NS_IMETHODIMP JSStackFrame::ToString(JSContext* aCx, nsACString& _retval)
+{
+ _retval.Truncate();
+
+ nsString filename;
+ nsresult rv = GetFilename(aCx, filename);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (filename.IsEmpty()) {
+ filename.AssignLiteral("<unknown filename>");
+ }
+
+ nsString funname;
+ rv = GetName(aCx, funname);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (funname.IsEmpty()) {
+ funname.AssignLiteral("<TOP_LEVEL>");
+ }
+
+ int32_t lineno = GetLineno(aCx);
+
+ static const char format[] = "JS frame :: %s :: %s :: line %d";
+ _retval.AppendPrintf(format,
+ NS_ConvertUTF16toUTF8(filename).get(),
+ NS_ConvertUTF16toUTF8(funname).get(),
+ lineno);
+ return NS_OK;
+}
+
+already_AddRefed<nsIStackFrame>
+CreateStack(JSContext* aCx, JS::StackCapture&& aCaptureMode)
+{
+ JS::Rooted<JSObject*> stack(aCx);
+ if (!JS::CaptureCurrentStack(aCx, &stack, mozilla::Move(aCaptureMode))) {
+ return nullptr;
+ }
+
+ if (!stack) {
+ return nullptr;
+ }
+
+ nsCOMPtr<nsIStackFrame> frame = new JSStackFrame(stack);
+ return frame.forget();
+}
+
+} // namespace exceptions
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/bindings/Exceptions.h b/dom/bindings/Exceptions.h
new file mode 100644
index 000000000..521c550f5
--- /dev/null
+++ b/dom/bindings/Exceptions.h
@@ -0,0 +1,69 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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_Exceptions_h__
+#define mozilla_dom_Exceptions_h__
+
+// DOM exception throwing machinery (for both main thread and workers).
+
+#include <stdint.h>
+#include "jspubtd.h"
+#include "nsIException.h"
+#include "nsStringGlue.h"
+#include "jsapi.h"
+
+class nsIStackFrame;
+class nsPIDOMWindowInner;
+template <class T>
+struct already_AddRefed;
+
+namespace mozilla {
+namespace dom {
+
+class Exception;
+
+// If we're throwing a DOMException and message is empty, the default
+// message for the nsresult in question will be used.
+bool
+Throw(JSContext* cx, nsresult rv, const nsACString& message = EmptyCString());
+
+// Create, throw and report an exception to a given window.
+void
+ThrowAndReport(nsPIDOMWindowInner* aWindow, nsresult aRv);
+
+// Both signatures of ThrowExceptionObject guarantee that an exception is set on
+// aCx before they return.
+void
+ThrowExceptionObject(JSContext* aCx, Exception* aException);
+void
+ThrowExceptionObject(JSContext* aCx, nsIException* aException);
+
+// Create an exception object for the given nsresult and message but don't set
+// it pending on aCx. If we're throwing a DOMException and aMessage is empty,
+// the default message for the nsresult in question will be used.
+//
+// This never returns null.
+already_AddRefed<Exception>
+CreateException(JSContext* aCx, nsresult aRv,
+ const nsACString& aMessage = EmptyCString());
+
+// aMaxDepth can be used to define a maximal depth for the stack trace. If the
+// value is -1, a default maximal depth will be selected. Will return null if
+// there is no JS stack right now.
+already_AddRefed<nsIStackFrame>
+GetCurrentJSStack(int32_t aMaxDepth = -1);
+
+// Internal stuff not intended to be widely used.
+namespace exceptions {
+
+already_AddRefed<nsIStackFrame>
+CreateStack(JSContext* aCx, JS::StackCapture&& aCaptureMode);
+
+} // namespace exceptions
+} // namespace dom
+} // namespace mozilla
+
+#endif
diff --git a/dom/bindings/FakeString.h b/dom/bindings/FakeString.h
new file mode 100644
index 000000000..bd92a1bca
--- /dev/null
+++ b/dom/bindings/FakeString.h
@@ -0,0 +1,160 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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_FakeString_h__
+#define mozilla_dom_FakeString_h__
+
+#include "nsString.h"
+#include "nsStringBuffer.h"
+#include "mozilla/RefPtr.h"
+
+namespace mozilla {
+namespace dom {
+namespace binding_detail {
+// A struct that has the same layout as an nsString but much faster
+// constructor and destructor behavior. FakeString uses inline storage
+// for small strings and a nsStringBuffer for longer strings.
+struct FakeString {
+ FakeString() :
+ mFlags(nsString::F_TERMINATED)
+ {
+ }
+
+ ~FakeString() {
+ if (mFlags & nsString::F_SHARED) {
+ nsStringBuffer::FromData(mData)->Release();
+ }
+ }
+
+ void Rebind(const nsString::char_type* aData, nsString::size_type aLength) {
+ MOZ_ASSERT(mFlags == nsString::F_TERMINATED);
+ mData = const_cast<nsString::char_type*>(aData);
+ mLength = aLength;
+ }
+
+ // Share aString's string buffer, if it has one; otherwise, make this string
+ // depend upon aString's data. aString should outlive this instance of
+ // FakeString.
+ void ShareOrDependUpon(const nsAString& aString) {
+ RefPtr<nsStringBuffer> sharedBuffer = nsStringBuffer::FromString(aString);
+ if (!sharedBuffer) {
+ Rebind(aString.Data(), aString.Length());
+ } else {
+ AssignFromStringBuffer(sharedBuffer.forget());
+ mLength = aString.Length();
+ }
+ }
+
+ void Truncate() {
+ MOZ_ASSERT(mFlags == nsString::F_TERMINATED);
+ mData = nsString::char_traits::sEmptyBuffer;
+ mLength = 0;
+ }
+
+ void SetIsVoid(bool aValue) {
+ MOZ_ASSERT(aValue,
+ "We don't support SetIsVoid(false) on FakeString!");
+ Truncate();
+ mFlags |= nsString::F_VOIDED;
+ }
+
+ const nsString::char_type* Data() const
+ {
+ return mData;
+ }
+
+ nsString::char_type* BeginWriting()
+ {
+ return mData;
+ }
+
+ nsString::size_type Length() const
+ {
+ return mLength;
+ }
+
+ // Reserve space to write aLength chars, not including null-terminator.
+ bool SetLength(nsString::size_type aLength, mozilla::fallible_t const&) {
+ // Use mInlineStorage for small strings.
+ if (aLength < sInlineCapacity) {
+ SetData(mInlineStorage);
+ } else {
+ RefPtr<nsStringBuffer> buf = nsStringBuffer::Alloc((aLength + 1) * sizeof(nsString::char_type));
+ if (MOZ_UNLIKELY(!buf)) {
+ return false;
+ }
+
+ AssignFromStringBuffer(buf.forget());
+ }
+ mLength = aLength;
+ mData[mLength] = char16_t(0);
+ return true;
+ }
+
+ // If this ever changes, change the corresponding code in the
+ // Optional<nsAString> specialization as well.
+ const nsAString* ToAStringPtr() const {
+ return reinterpret_cast<const nsString*>(this);
+ }
+
+operator const nsAString& () const {
+ return *reinterpret_cast<const nsString*>(this);
+ }
+
+private:
+ nsAString* ToAStringPtr() {
+ return reinterpret_cast<nsString*>(this);
+ }
+
+ nsString::char_type* mData;
+ nsString::size_type mLength;
+ uint32_t mFlags;
+
+ static const size_t sInlineCapacity = 64;
+ nsString::char_type mInlineStorage[sInlineCapacity];
+
+ FakeString(const FakeString& other) = delete;
+ void operator=(const FakeString& other) = delete;
+
+ void SetData(nsString::char_type* aData) {
+ MOZ_ASSERT(mFlags == nsString::F_TERMINATED);
+ mData = const_cast<nsString::char_type*>(aData);
+ }
+ void AssignFromStringBuffer(already_AddRefed<nsStringBuffer> aBuffer) {
+ SetData(static_cast<nsString::char_type*>(aBuffer.take()->Data()));
+ mFlags = nsString::F_SHARED | nsString::F_TERMINATED;
+ }
+
+ friend class NonNull<nsAString>;
+
+ // A class to use for our static asserts to ensure our object layout
+ // matches that of nsString.
+ class StringAsserter;
+ friend class StringAsserter;
+
+ class StringAsserter : public nsString {
+ public:
+ static void StaticAsserts() {
+ static_assert(offsetof(FakeString, mInlineStorage) ==
+ sizeof(nsString),
+ "FakeString should include all nsString members");
+ static_assert(offsetof(FakeString, mData) ==
+ offsetof(StringAsserter, mData),
+ "Offset of mData should match");
+ static_assert(offsetof(FakeString, mLength) ==
+ offsetof(StringAsserter, mLength),
+ "Offset of mLength should match");
+ static_assert(offsetof(FakeString, mFlags) ==
+ offsetof(StringAsserter, mFlags),
+ "Offset of mFlags should match");
+ }
+ };
+};
+} // namespace binding_detail
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_FakeString_h__ */ \ No newline at end of file
diff --git a/dom/bindings/GenerateCSS2PropertiesWebIDL.py b/dom/bindings/GenerateCSS2PropertiesWebIDL.py
new file mode 100644
index 000000000..58ec60c29
--- /dev/null
+++ b/dom/bindings/GenerateCSS2PropertiesWebIDL.py
@@ -0,0 +1,84 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.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
+import argparse
+import subprocess
+import buildconfig
+from mozbuild import shellutil
+
+# Generates a line of WebIDL with the given spelling of the property name
+# (whether camelCase, _underscorePrefixed, etc.) and the given array of
+# extended attributes.
+def generateLine(propName, extendedAttrs):
+ return " [%s] attribute DOMString %s;\n" % (", ".join(extendedAttrs),
+ propName)
+def generate(output, idlFilename, preprocessorHeader):
+ cpp = list(buildconfig.substs['CPP'])
+ cpp += shellutil.split(buildconfig.substs['ACDEFINES'])
+ cpp.append(preprocessorHeader)
+ preprocessed = subprocess.check_output(cpp)
+
+ propList = eval(preprocessed)
+ props = ""
+ for [name, prop, id, flags, pref, proptype] in propList:
+ if "CSS_PROPERTY_INTERNAL" in flags:
+ continue
+ # Unfortunately, even some of the getters here are fallible
+ # (e.g. on nsComputedDOMStyle).
+ extendedAttrs = ["Throws", "TreatNullAs=EmptyString"]
+ if pref is not "":
+ extendedAttrs.append('Pref="%s"' % pref)
+
+ # webkit properties get a capitalized "WebkitFoo" accessor (added here)
+ # as well as a camelcase "webkitFoo" accessor (added next).
+ if (prop.startswith("Webkit")):
+ props += generateLine(prop, extendedAttrs)
+
+ # Generate a line with camelCase spelling of property-name (or capitalized,
+ # for Moz-prefixed properties):
+ if not prop.startswith("Moz"):
+ prop = prop[0].lower() + prop[1:]
+ props += generateLine(prop, extendedAttrs)
+
+ # Per spec, what's actually supposed to happen here is that we're supposed
+ # to have properties for:
+ #
+ # 1) Each supported CSS property name, camelCased.
+ # 2) Each supported name that contains or starts with dashes,
+ # without any changes to the name.
+ # 3) cssFloat
+ #
+ # Note that "float" will cause a property called "float" to exist due to (1)
+ # in that list.
+ #
+ # In practice, cssFloat is the only case in which "name" doesn't contain
+ # "-" but also doesn't match "prop". So the above generatePropLine() call
+ # covered (3) and all of (1) except "float". If we now output attributes
+ # for all the cases where "name" doesn't match "prop", that will cover
+ # "float" and (2).
+ if prop != name:
+ extendedAttrs.append('BinaryName="%s"' % prop)
+ # Throw in a '_' before the attribute name, because some of these
+ # property names collide with IDL reserved words.
+ props += generateLine("_" + name, extendedAttrs)
+
+
+ idlFile = open(idlFilename, "r")
+ idlTemplate = idlFile.read()
+ idlFile.close()
+
+ output.write("/* THIS IS AN AUTOGENERATED FILE. DO NOT EDIT */\n\n" +
+ string.Template(idlTemplate).substitute({"props": props}) + '\n')
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument('idlFilename', help='IDL property file template')
+ parser.add_argument('preprocessorHeader', help='Header file to pass through the preprocessor')
+ args = parser.parse_args()
+ generate(sys.stdout, args.idlFilename, args.preprocessorHeader)
+
+if __name__ == '__main__':
+ main()
diff --git a/dom/bindings/IterableIterator.cpp b/dom/bindings/IterableIterator.cpp
new file mode 100644
index 000000000..041319638
--- /dev/null
+++ b/dom/bindings/IterableIterator.cpp
@@ -0,0 +1,35 @@
+/* -*- 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 "mozilla/dom/IterableIterator.h"
+
+namespace mozilla {
+namespace dom {
+
+// Due to IterableIterator being a templated class, we implement the necessary
+// CC bits in a superclass that IterableIterator then inherits from. This allows
+// us to put the macros outside of the header. The base class has pure virtual
+// functions for Traverse/Unlink that the templated subclasses will override.
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(IterableIteratorBase)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(IterableIteratorBase)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(IterableIteratorBase)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IterableIteratorBase)
+ tmp->TraverseHelper(cb);
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IterableIteratorBase)
+ tmp->UnlinkHelper();
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IterableIteratorBase)
+NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+}
+}
diff --git a/dom/bindings/IterableIterator.h b/dom/bindings/IterableIterator.h
new file mode 100644
index 000000000..1a56cec33
--- /dev/null
+++ b/dom/bindings/IterableIterator.h
@@ -0,0 +1,204 @@
+/* -*- 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/. */
+
+/**
+ * The IterableIterator class is used for WebIDL interfaces that have a
+ * iterable<> member defined with two types (so a pair iterator). It handles
+ * the ES6 Iterator-like functions that are generated for the iterable
+ * interface.
+ *
+ * For iterable interfaces with a pair iterator, the implementation class will
+ * need to implement these two functions:
+ *
+ * - size_t GetIterableLength()
+ * - Returns the number of elements available to iterate over
+ * - [type] GetValueAtIndex(size_t index)
+ * - Returns the value at the requested index.
+ * - [type] GetKeyAtIndex(size_t index)
+ * - Returns the key at the requested index
+ *
+ * Examples of iterable interface implementations can be found in the bindings
+ * test directory.
+ */
+
+#ifndef mozilla_dom_IterableIterator_h
+#define mozilla_dom_IterableIterator_h
+
+#include "nsISupports.h"
+#include "nsWrapperCache.h"
+#include "nsPIDOMWindow.h"
+#include "nsCOMPtr.h"
+#include "mozilla/dom/ToJSValue.h"
+#include "jswrapper.h"
+#include "mozilla/dom/IterableIteratorBinding.h"
+
+namespace mozilla {
+namespace dom {
+
+class IterableIteratorBase : public nsISupports
+{
+public:
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_CLASS(IterableIteratorBase)
+ typedef enum {
+ Keys = 0,
+ Values,
+ Entries
+ } IterableIteratorType;
+
+ IterableIteratorBase() {}
+
+protected:
+ virtual ~IterableIteratorBase() {}
+ virtual void UnlinkHelper() = 0;
+ virtual void TraverseHelper(nsCycleCollectionTraversalCallback& cb) = 0;
+};
+
+template <typename T>
+class IterableIterator final : public IterableIteratorBase
+{
+public:
+ typedef bool (*WrapFunc)(JSContext* aCx,
+ IterableIterator<T>* aObject,
+ JS::Handle<JSObject*> aGivenProto,
+ JS::MutableHandle<JSObject*> aReflector);
+
+ explicit IterableIterator(T* aIterableObj,
+ IterableIteratorType aIteratorType,
+ WrapFunc aWrapFunc)
+ : mIterableObj(aIterableObj)
+ , mIteratorType(aIteratorType)
+ , mWrapFunc(aWrapFunc)
+ , mIndex(0)
+ {
+ MOZ_ASSERT(mIterableObj);
+ MOZ_ASSERT(mWrapFunc);
+ }
+
+ void
+ Next(JSContext* aCx, JS::MutableHandle<JSObject*> aResult, ErrorResult& aRv)
+ {
+ JS::Rooted<JS::Value> value(aCx, JS::UndefinedValue());
+ if (mIndex >= this->mIterableObj->GetIterableLength()) {
+ DictReturn(aCx, aResult, true, value, aRv);
+ return;
+ }
+ switch (mIteratorType) {
+ case IterableIteratorType::Keys:
+ {
+ if (!ToJSValue(aCx, this->mIterableObj->GetKeyAtIndex(mIndex), &value)) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return;
+ }
+ DictReturn(aCx, aResult, false, value, aRv);
+ break;
+ }
+ case IterableIteratorType::Values:
+ {
+ if (!ToJSValue(aCx, this->mIterableObj->GetValueAtIndex(mIndex), &value)) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return;
+ }
+ DictReturn(aCx, aResult, false, value, aRv);
+ break;
+ }
+ case IterableIteratorType::Entries:
+ {
+ JS::Rooted<JS::Value> key(aCx);
+ if (!ToJSValue(aCx, this->mIterableObj->GetKeyAtIndex(mIndex), &key)) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return;
+ }
+ if (!ToJSValue(aCx, this->mIterableObj->GetValueAtIndex(mIndex), &value)) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return;
+ }
+ KeyAndValueReturn(aCx, key, value, aResult, aRv);
+ break;
+ }
+ default:
+ MOZ_CRASH("Invalid iterator type!");
+ }
+ ++mIndex;
+ }
+
+ bool
+ WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto, JS::MutableHandle<JSObject*> aObj)
+ {
+ return (*mWrapFunc)(aCx, this, aGivenProto, aObj);
+ }
+
+protected:
+ static void
+ DictReturn(JSContext* aCx, JS::MutableHandle<JSObject*> aResult,
+ bool aDone, JS::Handle<JS::Value> aValue, ErrorResult& aRv)
+ {
+ RootedDictionary<IterableKeyOrValueResult> dict(aCx);
+ dict.mDone = aDone;
+ dict.mValue = aValue;
+ JS::Rooted<JS::Value> dictValue(aCx);
+ if (!ToJSValue(aCx, dict, &dictValue)) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return;
+ }
+ aResult.set(&dictValue.toObject());
+ }
+
+ static void
+ KeyAndValueReturn(JSContext* aCx, JS::Handle<JS::Value> aKey,
+ JS::Handle<JS::Value> aValue,
+ JS::MutableHandle<JSObject*> aResult, ErrorResult& aRv)
+ {
+ RootedDictionary<IterableKeyAndValueResult> dict(aCx);
+ dict.mDone = false;
+ // Dictionary values are a Sequence, which is a FallibleTArray, so we need
+ // to check returns when appending.
+ if (!dict.mValue.AppendElement(aKey, mozilla::fallible)) {
+ aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
+ return;
+ }
+ if (!dict.mValue.AppendElement(aValue, mozilla::fallible)) {
+ aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
+ return;
+ }
+ JS::Rooted<JS::Value> dictValue(aCx);
+ if (!ToJSValue(aCx, dict, &dictValue)) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return;
+ }
+ aResult.set(&dictValue.toObject());
+ }
+
+protected:
+ virtual ~IterableIterator() {}
+
+ // Since we're templated on a binding, we need to possibly CC it, but can't do
+ // that through macros. So it happens here.
+ virtual void UnlinkHelper() final
+ {
+ mIterableObj = nullptr;
+ }
+
+ virtual void TraverseHelper(nsCycleCollectionTraversalCallback& cb) override
+ {
+ IterableIterator<T>* tmp = this;
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIterableObj);
+ }
+
+ // Binding Implementation object that we're iterating over.
+ RefPtr<T> mIterableObj;
+ // Tells whether this is a key, value, or entries iterator.
+ IterableIteratorType mIteratorType;
+ // Function pointer to binding-type-specific Wrap() call for this iterator.
+ WrapFunc mWrapFunc;
+ // Current index of iteration.
+ uint32_t mIndex;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_IterableIterator_h
diff --git a/dom/bindings/JSSlots.h b/dom/bindings/JSSlots.h
new file mode 100644
index 000000000..5e19957d6
--- /dev/null
+++ b/dom/bindings/JSSlots.h
@@ -0,0 +1,34 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * This file defines various reserved slot indices used by JavaScript
+ * reflections of DOM objects.
+ */
+#ifndef mozilla_dom_DOMSlots_h
+#define mozilla_dom_DOMSlots_h
+
+// We use slot 0 for holding the raw object. This is safe for both
+// globals and non-globals.
+// 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_OBJECT_SLOT 0
+
+// The total number of slots non-proxy DOM objects use by default.
+// Specific objects may have more for storing cached values.
+#define DOM_INSTANCE_RESERVED_SLOTS 1
+
+// Interface objects store a number of reserved slots equal to
+// DOM_INTERFACE_SLOTS_BASE + number of named constructors.
+#define DOM_INTERFACE_SLOTS_BASE 0
+
+// Interface prototype objects store a number of reserved slots equal to
+// DOM_INTERFACE_PROTO_SLOTS_BASE or DOM_INTERFACE_PROTO_SLOTS_BASE + 1 if a
+// slot for the unforgeable holder is needed.
+#define DOM_INTERFACE_PROTO_SLOTS_BASE 0
+
+#endif /* mozilla_dom_DOMSlots_h */
diff --git a/dom/bindings/Makefile.in b/dom/bindings/Makefile.in
new file mode 100644
index 000000000..95f267397
--- /dev/null
+++ b/dom/bindings/Makefile.in
@@ -0,0 +1,70 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+webidl_base := $(topsrcdir)/dom/webidl
+
+ifdef COMPILE_ENVIRONMENT
+
+# Generated by moz.build
+include webidlsrcs.mk
+
+# These come from webidlsrcs.mk.
+# TODO Write directly into backend.mk (bug 1281618)
+CPPSRCS += $(globalgen_sources) $(unified_binding_cpp_files)
+
+include $(topsrcdir)/config/rules.mk
+
+# TODO This list should be emitted to a .pp file via
+# GenerateCSS2PropertiesWebIDL.py (bug 1281614)
+css2properties_dependencies = \
+ $(topsrcdir)/layout/style/nsCSSPropList.h \
+ $(topsrcdir)/layout/style/nsCSSPropAliasList.h \
+ $(webidl_base)/CSS2Properties.webidl.in \
+ $(topsrcdir)/layout/style/PythonCSSProps.h \
+ $(srcdir)/GenerateCSS2PropertiesWebIDL.py \
+ $(GLOBAL_DEPS) \
+ $(NULL)
+
+CSS2Properties.webidl: $(css2properties_dependencies)
+
+# Most of the logic for dependencies lives inside Python so it can be
+# used by multiple build backends. We simply have rules to generate
+# and include the .pp file.
+#
+# The generated .pp file contains all the important dependencies such as
+# changes to .webidl or .py files should result in code generation being
+# performed. But we do pull in file-lists.jon to catch file additions.
+codegen_dependencies := \
+ file-lists.json \
+ $(nonstatic_webidl_files) \
+ $(GLOBAL_DEPS) \
+ $(NULL)
+
+include codegen.pp
+
+codegen.pp: $(codegen_dependencies)
+ $(call py_action,webidl,$(srcdir))
+ @$(TOUCH) $@
+
+.PHONY: compiletests
+compiletests:
+ $(call SUBMAKE,libs,test)
+
+endif
+
+GARBAGE += \
+ codegen.pp \
+ codegen.json \
+ parser.out \
+ WebIDLGrammar.pkl \
+ $(wildcard *.h) \
+ $(wildcard *Binding.cpp) \
+ $(wildcard *Event.cpp) \
+ $(wildcard *-event.cpp) \
+ $(wildcard *.webidl) \
+ $(NULL)
+
+DIST_GARBAGE += \
+ file-lists.json \
+ $(NULL)
diff --git a/dom/bindings/MozMap.h b/dom/bindings/MozMap.h
new file mode 100644
index 000000000..1e920c098
--- /dev/null
+++ b/dom/bindings/MozMap.h
@@ -0,0 +1,121 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Class for representing MozMap arguments. This is an nsTHashtable
+ * under the hood, but we don't want to leak that implementation
+ * detail.
+ */
+
+#ifndef mozilla_dom_MozMap_h
+#define mozilla_dom_MozMap_h
+
+#include "nsTHashtable.h"
+#include "nsHashKeys.h"
+#include "nsStringGlue.h"
+#include "nsTArray.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Move.h"
+
+namespace mozilla {
+namespace dom {
+
+namespace binding_detail {
+template<typename DataType>
+class MozMapEntry : public nsStringHashKey
+{
+public:
+ explicit MozMapEntry(const nsAString* aKeyTypePointer)
+ : nsStringHashKey(aKeyTypePointer)
+ {
+ }
+
+ // Move constructor so we can do MozMaps of MozMaps.
+ MozMapEntry(MozMapEntry<DataType>&& aOther)
+ : nsStringHashKey(aOther),
+ mData(Move(aOther.mData))
+ {
+ }
+
+ DataType mData;
+};
+
+} // namespace binding_detail
+
+template<typename DataType>
+class MozMap : protected nsTHashtable<binding_detail::MozMapEntry<DataType>>
+{
+public:
+ typedef typename binding_detail::MozMapEntry<DataType> EntryType;
+ typedef nsTHashtable<EntryType> Base;
+ typedef MozMap<DataType> SelfType;
+
+ MozMap()
+ {
+ }
+
+ // Move constructor so we can do MozMap of MozMap.
+ MozMap(SelfType&& aOther) :
+ Base(Move(aOther))
+ {
+ }
+
+ // The return value is only safe to use until an AddEntry call.
+ const DataType& Get(const nsAString& aKey) const
+ {
+ const EntryType* ent = this->GetEntry(aKey);
+ MOZ_ASSERT(ent, "Why are you using a key we didn't claim to have?");
+ return ent->mData;
+ }
+
+ DataType& Get(const nsAString& aKey)
+ {
+ EntryType* ent = this->GetEntry(aKey);
+ MOZ_ASSERT(ent, "Why are you using a key we didn't claim to have?");
+ return ent->mData;
+ }
+
+ // The return value is only safe to use until an AddEntry call.
+ const DataType* GetIfExists(const nsAString& aKey) const
+ {
+ const EntryType* ent = this->GetEntry(aKey);
+ if (!ent) {
+ return nullptr;
+ }
+ return &ent->mData;
+ }
+
+ void GetKeys(nsTArray<nsString>& aKeys) const {
+ for (auto iter = this->ConstIter(); !iter.Done(); iter.Next()) {
+ aKeys.AppendElement(iter.Get()->GetKey());
+ }
+ }
+
+ // XXXbz we expose this generic enumerator for tracing. Otherwise we'd end up
+ // with a dependency on BindingUtils.h here for the SequenceTracer bits.
+ typedef void (* Enumerator)(DataType* aValue, void* aClosure);
+ void EnumerateValues(Enumerator aEnumerator, void *aClosure)
+ {
+ for (auto iter = this->Iter(); !iter.Done(); iter.Next()) {
+ aEnumerator(&iter.Get()->mData, aClosure);
+ }
+ }
+
+ MOZ_MUST_USE
+ DataType* AddEntry(const nsAString& aKey)
+ {
+ EntryType* ent = this->PutEntry(aKey, fallible);
+ if (!ent) {
+ return nullptr;
+ }
+ return &ent->mData;
+ }
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_MozMap_h
diff --git a/dom/bindings/NonRefcountedDOMObject.h b/dom/bindings/NonRefcountedDOMObject.h
new file mode 100644
index 000000000..2aef9ce4a
--- /dev/null
+++ b/dom/bindings/NonRefcountedDOMObject.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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_NonRefcountedDOMObject_h__
+#define mozilla_dom_NonRefcountedDOMObject_h__
+
+#include "nsISupportsImpl.h"
+
+namespace mozilla {
+namespace dom {
+
+// Natives for DOM classes that aren't refcounted need to inherit from this
+// class.
+// If you're seeing objects of this class leak then natives for one of the DOM
+// classes inheriting from it is leaking. If the native for that class has
+// MOZ_COUNT_CTOR/DTOR in its constructor/destructor then it should show up in
+// the leak log too.
+class NonRefcountedDOMObject
+{
+protected:
+ NonRefcountedDOMObject()
+ {
+ MOZ_COUNT_CTOR(NonRefcountedDOMObject);
+ }
+ ~NonRefcountedDOMObject()
+ {
+ MOZ_COUNT_DTOR(NonRefcountedDOMObject);
+ }
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_NonRefcountedDOMObject_h__ */
diff --git a/dom/bindings/Nullable.h b/dom/bindings/Nullable.h
new file mode 100644
index 000000000..8d7d46905
--- /dev/null
+++ b/dom/bindings/Nullable.h
@@ -0,0 +1,140 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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"
+#include "nsTArrayForwardDeclare.h"
+#include "mozilla/Move.h"
+#include "mozilla/Maybe.h"
+
+class nsCycleCollectionTraversalCallback;
+
+namespace mozilla {
+namespace dom {
+
+// Support for nullable types
+template <typename T>
+struct Nullable
+{
+private:
+ Maybe<T> mValue;
+
+public:
+ Nullable()
+ : mValue()
+ {}
+
+ MOZ_IMPLICIT Nullable(const decltype(nullptr)&)
+ : mValue()
+ {}
+
+ explicit Nullable(const T& aValue)
+ : mValue()
+ {
+ mValue.emplace(aValue);
+ }
+
+ MOZ_IMPLICIT Nullable(T&& aValue)
+ : mValue()
+ {
+ mValue.emplace(mozilla::Move(aValue));
+ }
+
+ Nullable(Nullable<T>&& aOther)
+ : mValue(mozilla::Move(aOther.mValue))
+ {}
+
+ Nullable(const Nullable<T>& aOther)
+ : mValue(aOther.mValue)
+ {}
+
+ void operator=(const Nullable<T>& aOther)
+ {
+ mValue = aOther.mValue;
+ }
+
+ void SetValue(const T& aArgs)
+ {
+ mValue.reset();
+ mValue.emplace(aArgs);
+ }
+
+ void SetValue(T&& aArgs)
+ {
+ mValue.reset();
+ mValue.emplace(mozilla::Move(aArgs));
+ }
+
+ // 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() {
+ if (mValue.isNothing()) {
+ mValue.emplace();
+ }
+ return mValue.ref();
+ }
+
+ void SetNull() {
+ mValue.reset();
+ }
+
+ const T& Value() const {
+ return mValue.ref();
+ }
+
+ T& Value() {
+ return mValue.ref();
+ }
+
+ bool IsNull() const {
+ return mValue.isNothing();
+ }
+
+ bool Equals(const Nullable<T>& aOtherNullable) const
+ {
+ return mValue == aOtherNullable.mValue;
+ }
+
+ bool operator==(const Nullable<T>& aOtherNullable) const
+ {
+ return Equals(aOtherNullable);
+ }
+
+ bool operator!=(const Nullable<T>& aOtherNullable) const
+ {
+ return !Equals(aOtherNullable);
+ }
+};
+
+
+template<typename T>
+void
+ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
+ Nullable<T>& aNullable,
+ const char* aName,
+ uint32_t aFlags = 0)
+{
+ if (!aNullable.IsNull()) {
+ ImplCycleCollectionTraverse(aCallback, aNullable.Value(), aName, aFlags);
+ }
+}
+
+template<typename T>
+void
+ImplCycleCollectionUnlink(Nullable<T>& aNullable)
+{
+ if (!aNullable.IsNull()) {
+ ImplCycleCollectionUnlink(aNullable.Value());
+ }
+}
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_Nullable_h */
diff --git a/dom/bindings/PrimitiveConversions.h b/dom/bindings/PrimitiveConversions.h
new file mode 100644
index 000000000..3da58a877
--- /dev/null
+++ b/dom/bindings/PrimitiveConversions.h
@@ -0,0 +1,359 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 <stdint.h>
+
+#include "jsapi.h"
+#include "js/Conversions.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/ErrorResult.h"
+#include "mozilla/FloatingPoint.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::Handle<JS::Value> v,
+ jstype* retval) {
+ MOZ_CRASH("This should never be instantiated!");
+ }
+};
+
+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::Handle<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::Handle<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::Handle<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::Handle<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)
+{
+ static_assert(std::numeric_limits<T>::is_integer,
+ "This can only be applied to integers!");
+
+ if (!mozilla::IsFinite(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)
+{
+ static_assert(std::numeric_limits<T>::is_integer,
+ "This can only be applied to integers!");
+
+ if (mozilla::IsNaN(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(mozilla::IsFinite(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 = static_cast<T>(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 bool jstype;
+ typedef bool intermediateType;
+ static inline bool converter(JSContext* /* unused */, JS::Handle<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::Handle<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::Handle<JS::Value> v, T* retval)
+{
+ typename PrimitiveConversionTraits<T, B>::jstype t;
+ if (!PrimitiveConversionTraits<T, B>::converter(cx, v, &t))
+ return false;
+
+ *retval = static_cast<T>(
+ static_cast<typename PrimitiveConversionTraits<T, B>::intermediateType>(t));
+ return true;
+}
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_PrimitiveConversions_h */
diff --git a/dom/bindings/RootedDictionary.h b/dom/bindings/RootedDictionary.h
new file mode 100644
index 000000000..cf4adaaef
--- /dev/null
+++ b/dom/bindings/RootedDictionary.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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_RootedDictionary_h__
+#define mozilla_dom_RootedDictionary_h__
+
+#include "mozilla/GuardObjects.h"
+#include "mozilla/dom/Nullable.h"
+#include "jsapi.h"
+
+namespace mozilla {
+namespace dom {
+
+template<typename T>
+class MOZ_RAII RootedDictionary final : public T,
+ private JS::CustomAutoRooter
+{
+public:
+ template <typename CX>
+ explicit RootedDictionary(const CX& cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) :
+ T(),
+ JS::CustomAutoRooter(cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT)
+ {
+ }
+
+ virtual void trace(JSTracer *trc) override
+ {
+ this->TraceDictionary(trc);
+ }
+};
+
+template<typename T>
+class MOZ_RAII NullableRootedDictionary final : public Nullable<T>,
+ private JS::CustomAutoRooter
+{
+public:
+ template <typename CX>
+ explicit NullableRootedDictionary(const CX& cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) :
+ Nullable<T>(),
+ JS::CustomAutoRooter(cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT)
+ {
+ }
+
+ virtual void trace(JSTracer *trc) override
+ {
+ if (!this->IsNull()) {
+ this->Value().TraceDictionary(trc);
+ }
+ }
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_RootedDictionary_h__ */
diff --git a/dom/bindings/RootedOwningNonNull.h b/dom/bindings/RootedOwningNonNull.h
new file mode 100644
index 000000000..890afd74f
--- /dev/null
+++ b/dom/bindings/RootedOwningNonNull.h
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * An implementation of Rooted for OwningNonNull<T>. This works by assuming
+ * that T has a Trace() method defined on it which will trace whatever things
+ * inside the T instance need tracing.
+ *
+ * This implementation has one serious drawback: operator= doesn't work right
+ * because it's declared on Rooted directly and expects the type Rooted is
+ * templated over.
+ */
+
+#ifndef mozilla_RootedOwningNonNull_h__
+#define mozilla_RootedOwningNonNull_h__
+
+#include "mozilla/OwningNonNull.h"
+#include "js/GCPolicyAPI.h"
+#include "js/RootingAPI.h"
+
+namespace JS {
+template<typename T>
+struct GCPolicy<mozilla::OwningNonNull<T>>
+{
+ typedef mozilla::OwningNonNull<T> SmartPtrType;
+
+ static SmartPtrType initial()
+ {
+ return SmartPtrType();
+ }
+
+ static void trace(JSTracer* trc, SmartPtrType* tp,
+ const char* name)
+ {
+ // We have to be very careful here. Normally, OwningNonNull can't be null.
+ // But binding code can end up in a situation where it sets up a
+ // Rooted<OwningNonNull> and then before it gets a chance to assign to it
+ // (e.g. from the constructor of the thing being assigned) a GC happens. So
+ // we can land here when *tp stores a null pointer because it's not
+ // initialized.
+ //
+ // So we need to check for that before jumping.
+ if ((*tp).isInitialized()) {
+ (*tp)->Trace(trc);
+ }
+ }
+};
+} // namespace JS
+
+namespace js {
+template<typename T>
+struct RootedBase<mozilla::OwningNonNull<T>>
+{
+ typedef mozilla::OwningNonNull<T> SmartPtrType;
+
+ operator SmartPtrType& () const
+ {
+ auto& self = *static_cast<const JS::Rooted<SmartPtrType>*>(this);
+ return self.get();
+ }
+
+ operator T& () const
+ {
+ auto& self = *static_cast<const JS::Rooted<SmartPtrType>*>(this);
+ return self.get();
+ }
+};
+} // namespace js
+
+#endif /* mozilla_RootedOwningNonNull_h__ */
diff --git a/dom/bindings/RootedRefPtr.h b/dom/bindings/RootedRefPtr.h
new file mode 100644
index 000000000..e27361b24
--- /dev/null
+++ b/dom/bindings/RootedRefPtr.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * An implementation of Rooted for RefPtr<T>. This works by assuming that T has
+ * a Trace() method defined on it which will trace whatever things inside the T
+ * instance need tracing.
+ *
+ * This implementation has one serious drawback: operator= doesn't work right
+ * because it's declared on Rooted directly and expects the type Rooted is
+ * templated over.
+ */
+
+#ifndef mozilla_RootedRefPtr_h__
+#define mozilla_RootedRefPtr_h__
+
+#include "mozilla/RefPtr.h"
+#include "js/GCPolicyAPI.h"
+#include "js/RootingAPI.h"
+
+namespace JS {
+template<typename T>
+struct GCPolicy<RefPtr<T>>
+{
+ static RefPtr<T> initial() {
+ return RefPtr<T>();
+ }
+
+ static void trace(JSTracer* trc, RefPtr<T>* tp, const char* name)
+ {
+ if (*tp) {
+ (*tp)->Trace(trc);
+ }
+ }
+};
+} // namespace JS
+
+namespace js {
+template<typename T>
+struct RootedBase<RefPtr<T>>
+{
+ operator RefPtr<T>& () const
+ {
+ auto& self = *static_cast<const JS::Rooted<RefPtr<T>>*>(this);
+ return self.get();
+ }
+
+ operator T*() const
+ {
+ auto& self = *static_cast<const JS::Rooted<RefPtr<T>>*>(this);
+ return self.get();
+ }
+};
+} // namespace js
+
+#endif /* mozilla_RootedRefPtr_h__ */
diff --git a/dom/bindings/SimpleGlobalObject.cpp b/dom/bindings/SimpleGlobalObject.cpp
new file mode 100644
index 000000000..6ac397019
--- /dev/null
+++ b/dom/bindings/SimpleGlobalObject.cpp
@@ -0,0 +1,176 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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/dom/SimpleGlobalObject.h"
+
+#include "jsapi.h"
+#include "js/Class.h"
+
+#include "nsJSPrincipals.h"
+#include "nsNullPrincipal.h"
+#include "nsThreadUtils.h"
+#include "nsContentUtils.h"
+
+#include "xpcprivate.h"
+
+#include "mozilla/dom/ScriptSettings.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(SimpleGlobalObject)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(SimpleGlobalObject)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
+ tmp->UnlinkHostObjectURIs();
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(SimpleGlobalObject)
+
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
+ tmp->TraverseHostObjectURIs(cb);
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(SimpleGlobalObject)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(SimpleGlobalObject)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(SimpleGlobalObject)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SimpleGlobalObject)
+ NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+ NS_INTERFACE_MAP_ENTRY(nsIGlobalObject)
+NS_INTERFACE_MAP_END
+
+static void
+SimpleGlobal_finalize(js::FreeOp *fop, JSObject *obj)
+{
+ SimpleGlobalObject* globalObject =
+ static_cast<SimpleGlobalObject*>(JS_GetPrivate(obj));
+ NS_RELEASE(globalObject);
+}
+
+static void
+SimpleGlobal_moved(JSObject *obj, const JSObject *old)
+{
+ SimpleGlobalObject* globalObject =
+ static_cast<SimpleGlobalObject*>(JS_GetPrivate(obj));
+ globalObject->UpdateWrapper(obj, old);
+}
+
+static const js::ClassOps SimpleGlobalClassOps = {
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ JS_EnumerateStandardClasses,
+ JS_ResolveStandardClass,
+ JS_MayResolveStandardClass,
+ SimpleGlobal_finalize,
+ nullptr,
+ nullptr,
+ nullptr,
+ JS_GlobalObjectTraceHook,
+};
+
+static const js::ClassExtension SimpleGlobalClassExtension = {
+ nullptr,
+ SimpleGlobal_moved
+};
+
+const js::Class SimpleGlobalClass = {
+ "",
+ JSCLASS_GLOBAL_FLAGS |
+ JSCLASS_HAS_PRIVATE |
+ JSCLASS_PRIVATE_IS_NSISUPPORTS |
+ JSCLASS_FOREGROUND_FINALIZE,
+ &SimpleGlobalClassOps,
+ JS_NULL_CLASS_SPEC,
+ &SimpleGlobalClassExtension,
+ JS_NULL_OBJECT_OPS
+};
+
+// static
+JSObject*
+SimpleGlobalObject::Create(GlobalType globalType, JS::Handle<JS::Value> proto)
+{
+ // We can't root our return value with our AutoJSAPI because the rooting
+ // analysis thinks ~AutoJSAPI can GC. So we need to root in a scope outside
+ // the lifetime of the AutoJSAPI.
+ JS::Rooted<JSObject*> global(RootingCx());
+
+ { // Scope to ensure the AutoJSAPI destructor runs before we end up returning
+ AutoJSAPI jsapi;
+ jsapi.Init();
+ JSContext* cx = jsapi.cx();
+
+ JS::CompartmentOptions options;
+ options.creationOptions().setInvisibleToDebugger(true);
+
+ if (NS_IsMainThread()) {
+ nsCOMPtr<nsIPrincipal> principal = nsNullPrincipal::Create();
+ options.creationOptions().setTrace(xpc::TraceXPCGlobal);
+ global = xpc::CreateGlobalObject(cx, js::Jsvalify(&SimpleGlobalClass),
+ nsJSPrincipals::get(principal),
+ options);
+ } else {
+ global = JS_NewGlobalObject(cx, js::Jsvalify(&SimpleGlobalClass),
+ nullptr,
+ JS::DontFireOnNewGlobalHook, options);
+ }
+
+ if (!global) {
+ jsapi.ClearException();
+ return nullptr;
+ }
+
+ JSAutoCompartment ac(cx, global);
+
+ // It's important to create the nsIGlobalObject for our new global before we
+ // start trying to wrap things like the prototype into its compartment,
+ // because the wrap operation relies on the global having its
+ // nsIGlobalObject already.
+ RefPtr<SimpleGlobalObject> globalObject =
+ new SimpleGlobalObject(global, globalType);
+
+ // Pass on ownership of globalObject to |global|.
+ JS_SetPrivate(global, globalObject.forget().take());
+
+ if (proto.isObjectOrNull()) {
+ JS::Rooted<JSObject*> protoObj(cx, proto.toObjectOrNull());
+ if (!JS_WrapObject(cx, &protoObj)) {
+ jsapi.ClearException();
+ return nullptr;
+ }
+
+ if (!JS_SplicePrototype(cx, global, protoObj)) {
+ jsapi.ClearException();
+ return nullptr;
+ }
+ } else if (!proto.isUndefined()) {
+ // Bogus proto.
+ return nullptr;
+ }
+
+ JS_FireOnNewGlobalObject(cx, global);
+ }
+
+ return global;
+}
+
+// static
+SimpleGlobalObject::GlobalType
+SimpleGlobalObject::SimpleGlobalType(JSObject* obj)
+{
+ if (js::GetObjectClass(obj) != &SimpleGlobalClass) {
+ return SimpleGlobalObject::GlobalType::NotSimpleGlobal;
+ }
+
+ SimpleGlobalObject* globalObject =
+ static_cast<SimpleGlobalObject*>(JS_GetPrivate(obj));
+ return globalObject->Type();
+}
+
+} // namespace mozilla
+} // namespace dom
diff --git a/dom/bindings/SimpleGlobalObject.h b/dom/bindings/SimpleGlobalObject.h
new file mode 100644
index 000000000..9781fbfaa
--- /dev/null
+++ b/dom/bindings/SimpleGlobalObject.h
@@ -0,0 +1,99 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 simplere nsIGlobalObject implementation that can be used to set up a new
+ * global without anything interesting in it other than the JS builtins. This
+ * is safe to use on both mainthread and worker threads.
+ */
+
+#ifndef mozilla_dom_SimpleGlobalObject_h__
+#define mozilla_dom_SimpleGlobalObject_h__
+
+#include "nsIGlobalObject.h"
+#include "nsWrapperCache.h"
+#include "js/TypeDecls.h"
+#include "nsISupportsImpl.h"
+#include "nsCycleCollectionParticipant.h"
+
+namespace mozilla {
+namespace dom {
+
+class SimpleGlobalObject : public nsIGlobalObject,
+ public nsWrapperCache
+{
+public:
+ enum class GlobalType {
+ BindingDetail, // Should only be used by DOM bindings code.
+ WorkerDebuggerSandbox,
+ NotSimpleGlobal // Sentinel to be used by BasicGlobalType.
+ };
+
+ // Create a new JS global object that can be used to do some work. This
+ // global will NOT have any DOM APIs exposed in it, will not be visible to the
+ // debugger, and will not have a useful concept of principals, so don't try to
+ // use it with any DOM objects. Apart from that, running code with
+ // side-effects is safe in this global. Importantly, when you are first
+ // handed this global it's guaranteed to have pristine built-ins. The
+ // corresponding nsIGlobalObject* for this global object will be a
+ // SimpleGlobalObject of the type provided; JS_GetPrivate on the returned
+ // JSObject* will return the SimpleGlobalObject*.
+ //
+ // If the provided prototype value is undefined, it is ignored. If it's an
+ // object or null, it's set as the prototype of the created global. If it's
+ // anything else, this function returns null.
+ //
+ // Note that creating new globals is not cheap and should not be done
+ // gratuitously. Please think carefully before you use this function.
+ static JSObject* Create(GlobalType globalType,
+ JS::Handle<JS::Value> proto =
+ JS::UndefinedHandleValue);
+
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(SimpleGlobalObject,
+ nsIGlobalObject)
+
+ // Gets the GlobalType of this SimpleGlobalObject.
+ GlobalType Type() const
+ {
+ return mType;
+ }
+
+ // Gets the GlobalType of the SimpleGlobalObject for the given JSObject*, if
+ // the given JSObject* is the global corresponding to a SimpleGlobalObject.
+ // Oherwise, returns GlobalType::NotSimpleGlobal.
+ static GlobalType SimpleGlobalType(JSObject* obj);
+
+ virtual JSObject *GetGlobalJSObject() override
+ {
+ return GetWrapper();
+ }
+
+ virtual JSObject* WrapObject(JSContext* cx,
+ JS::Handle<JSObject*> aGivenProto) override
+ {
+ MOZ_CRASH("SimpleGlobalObject doesn't use DOM bindings!");
+ }
+
+private:
+ SimpleGlobalObject(JSObject *global, GlobalType type)
+ : mType(type)
+ {
+ SetWrapper(global);
+ }
+
+ virtual ~SimpleGlobalObject()
+ {
+ ClearWrapper();
+ }
+
+ const GlobalType mType;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_SimpleGlobalObject_h__ */
diff --git a/dom/bindings/StructuredClone.cpp b/dom/bindings/StructuredClone.cpp
new file mode 100644
index 000000000..71b4f5c74
--- /dev/null
+++ b/dom/bindings/StructuredClone.cpp
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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/dom/StructuredClone.h"
+
+#include "js/StructuredClone.h"
+#include "mozilla/dom/ImageData.h"
+#include "mozilla/dom/StructuredCloneTags.h"
+
+namespace mozilla {
+namespace dom {
+
+JSObject*
+ReadStructuredCloneImageData(JSContext* aCx, JSStructuredCloneReader* aReader)
+{
+ // Read the information out of the stream.
+ uint32_t width, height;
+ JS::Rooted<JS::Value> dataArray(aCx);
+ if (!JS_ReadUint32Pair(aReader, &width, &height) ||
+ !JS_ReadTypedArray(aReader, &dataArray)) {
+ return nullptr;
+ }
+ MOZ_ASSERT(dataArray.isObject());
+
+ // Protect the result from a moving GC in ~nsRefPtr.
+ JS::Rooted<JSObject*> result(aCx);
+ {
+ // Construct the ImageData.
+ RefPtr<ImageData> imageData = new ImageData(width, height,
+ dataArray.toObject());
+ // Wrap it in a JS::Value.
+ if (!imageData->WrapObject(aCx, nullptr, &result)) {
+ return nullptr;
+ }
+ }
+ return result;
+}
+
+bool
+WriteStructuredCloneImageData(JSContext* aCx, JSStructuredCloneWriter* aWriter,
+ ImageData* aImageData)
+{
+ uint32_t width = aImageData->Width();
+ uint32_t height = aImageData->Height();
+ JS::Rooted<JSObject*> dataArray(aCx, aImageData->GetDataObject());
+
+ JSAutoCompartment ac(aCx, dataArray);
+ JS::Rooted<JS::Value> arrayValue(aCx, JS::ObjectValue(*dataArray));
+ return JS_WriteUint32Pair(aWriter, SCTAG_DOM_IMAGEDATA, 0) &&
+ JS_WriteUint32Pair(aWriter, width, height) &&
+ JS_WriteTypedArray(aWriter, arrayValue);
+}
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/bindings/StructuredClone.h b/dom/bindings/StructuredClone.h
new file mode 100644
index 000000000..bfd7700c6
--- /dev/null
+++ b/dom/bindings/StructuredClone.h
@@ -0,0 +1,25 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+class JSObject;
+struct JSContext;
+struct JSStructuredCloneReader;
+struct JSStructuredCloneWriter;
+
+namespace mozilla {
+namespace dom {
+
+class ImageData;
+
+JSObject*
+ReadStructuredCloneImageData(JSContext* aCx, JSStructuredCloneReader* aReader);
+
+bool
+WriteStructuredCloneImageData(JSContext* aCx, JSStructuredCloneWriter* aWriter,
+ ImageData* aImageData);
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/bindings/ToJSValue.cpp b/dom/bindings/ToJSValue.cpp
new file mode 100644
index 000000000..d84428fb3
--- /dev/null
+++ b/dom/bindings/ToJSValue.cpp
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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/dom/ToJSValue.h"
+#include "mozilla/dom/DOMException.h"
+#include "mozilla/dom/Exceptions.h"
+#ifdef SPIDERMONKEY_PROMISE
+#include "mozilla/dom/Promise.h"
+#endif // SPIDERMONKEY_PROMISE
+#include "nsAString.h"
+#include "nsContentUtils.h"
+#include "nsStringBuffer.h"
+#include "xpcpublic.h"
+
+namespace mozilla {
+namespace dom {
+
+bool
+ToJSValue(JSContext* aCx, const nsAString& aArgument,
+ JS::MutableHandle<JS::Value> aValue)
+{
+ // Make sure we're called in a compartment
+ MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
+
+// XXXkhuey I'd love to use xpc::NonVoidStringToJsval here, but it requires
+ // a non-const nsAString for silly reasons.
+ nsStringBuffer* sharedBuffer;
+ if (!XPCStringConvert::ReadableToJSVal(aCx, aArgument, &sharedBuffer,
+ aValue)) {
+ return false;
+ }
+
+ if (sharedBuffer) {
+ NS_ADDREF(sharedBuffer);
+ }
+
+ return true;
+}
+
+
+bool
+ToJSValue(JSContext* aCx,
+ nsresult aArgument,
+ JS::MutableHandle<JS::Value> aValue)
+{
+ RefPtr<Exception> exception = CreateException(aCx, aArgument);
+ return ToJSValue(aCx, exception, aValue);
+}
+
+bool
+ToJSValue(JSContext* aCx,
+ ErrorResult& aArgument,
+ JS::MutableHandle<JS::Value> aValue)
+{
+ MOZ_ASSERT(aArgument.Failed());
+ MOZ_ASSERT(!aArgument.IsUncatchableException(),
+ "Doesn't make sense to convert uncatchable exception to a JS value!");
+ DebugOnly<bool> throwResult = aArgument.MaybeSetPendingException(aCx);
+ MOZ_ASSERT(throwResult);
+ DebugOnly<bool> getPendingResult = JS_GetPendingException(aCx, aValue);
+ MOZ_ASSERT(getPendingResult);
+ JS_ClearPendingException(aCx);
+ return true;
+}
+
+#ifdef SPIDERMONKEY_PROMISE
+bool
+ToJSValue(JSContext* aCx, Promise& aArgument,
+ JS::MutableHandle<JS::Value> aValue)
+{
+ aValue.setObject(*aArgument.PromiseObj());
+ return true;
+}
+#endif // SPIDERMONKEY_PROMISE
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/bindings/ToJSValue.h b/dom/bindings/ToJSValue.h
new file mode 100644
index 000000000..2021c0b4c
--- /dev/null
+++ b/dom/bindings/ToJSValue.h
@@ -0,0 +1,377 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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_ToJSValue_h
+#define mozilla_dom_ToJSValue_h
+
+#include "mozilla/TypeTraits.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/dom/BindingUtils.h"
+#include "mozilla/dom/TypedArray.h"
+#include "jsapi.h"
+#include "nsISupports.h"
+#include "nsTArray.h"
+#include "nsWrapperCache.h"
+
+namespace mozilla {
+namespace dom {
+
+class Promise;
+
+// If ToJSValue returns false, it must set an exception on the
+// JSContext.
+
+// Accept strings.
+MOZ_MUST_USE bool
+ToJSValue(JSContext* aCx,
+ const nsAString& aArgument,
+ JS::MutableHandle<JS::Value> aValue);
+
+// Accept booleans. But be careful here: if we just have a function that takes
+// a boolean argument, then any pointer that doesn't match one of our other
+// signatures/templates will get treated as a boolean, which is clearly not
+// desirable. So make this a template that only gets used if the argument type
+// is actually boolean
+template<typename T>
+MOZ_MUST_USE
+typename EnableIf<IsSame<T, bool>::value, bool>::Type
+ToJSValue(JSContext* aCx,
+ T aArgument,
+ JS::MutableHandle<JS::Value> aValue)
+{
+ // Make sure we're called in a compartment
+ MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
+
+ aValue.setBoolean(aArgument);
+ return true;
+}
+
+// Accept integer types
+inline bool
+ToJSValue(JSContext* aCx,
+ int32_t aArgument,
+ JS::MutableHandle<JS::Value> aValue)
+{
+ // Make sure we're called in a compartment
+ MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
+
+ aValue.setInt32(aArgument);
+ return true;
+}
+
+inline bool
+ToJSValue(JSContext* aCx,
+ uint32_t aArgument,
+ JS::MutableHandle<JS::Value> aValue)
+{
+ // Make sure we're called in a compartment
+ MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
+
+ aValue.setNumber(aArgument);
+ return true;
+}
+
+inline bool
+ToJSValue(JSContext* aCx,
+ int64_t aArgument,
+ JS::MutableHandle<JS::Value> aValue)
+{
+ // Make sure we're called in a compartment
+ MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
+
+ aValue.setNumber(double(aArgument));
+ return true;
+}
+
+inline bool
+ToJSValue(JSContext* aCx,
+ uint64_t aArgument,
+ JS::MutableHandle<JS::Value> aValue)
+{
+ // Make sure we're called in a compartment
+ MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
+
+ aValue.setNumber(double(aArgument));
+ return true;
+}
+
+// accept floating point types
+inline bool
+ToJSValue(JSContext* aCx,
+ float aArgument,
+ JS::MutableHandle<JS::Value> aValue)
+{
+ // Make sure we're called in a compartment
+ MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
+
+ aValue.setNumber(aArgument);
+ return true;
+}
+
+inline bool
+ToJSValue(JSContext* aCx,
+ double aArgument,
+ JS::MutableHandle<JS::Value> aValue)
+{
+ // Make sure we're called in a compartment
+ MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
+
+ aValue.setNumber(aArgument);
+ return true;
+}
+
+// Accept CallbackObjects
+MOZ_MUST_USE inline bool
+ToJSValue(JSContext* aCx,
+ CallbackObject& aArgument,
+ JS::MutableHandle<JS::Value> aValue)
+{
+ // Make sure we're called in a compartment
+ MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
+
+ aValue.setObject(*aArgument.Callback());
+
+ return MaybeWrapValue(aCx, aValue);
+}
+
+// Accept objects that inherit from nsWrapperCache (e.g. most
+// DOM objects).
+template <class T>
+MOZ_MUST_USE
+typename EnableIf<IsBaseOf<nsWrapperCache, T>::value, bool>::Type
+ToJSValue(JSContext* aCx,
+ T& aArgument,
+ JS::MutableHandle<JS::Value> aValue)
+{
+ // Make sure we're called in a compartment
+ MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
+ // Make sure non-webidl objects don't sneak in here
+ MOZ_ASSERT(aArgument.IsDOMBinding());
+
+ return GetOrCreateDOMReflector(aCx, aArgument, aValue);
+}
+
+// Accept typed arrays built from appropriate nsTArray values
+template<typename T>
+MOZ_MUST_USE
+typename EnableIf<IsBaseOf<AllTypedArraysBase, T>::value, bool>::Type
+ToJSValue(JSContext* aCx,
+ const TypedArrayCreator<T>& aArgument,
+ JS::MutableHandle<JS::Value> aValue)
+{
+ // Make sure we're called in a compartment
+ MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
+
+ JSObject* obj = aArgument.Create(aCx);
+ if (!obj) {
+ return false;
+ }
+ aValue.setObject(*obj);
+ return true;
+}
+
+// Accept objects that inherit from nsISupports but not nsWrapperCache (e.g.
+// DOM File).
+template <class T>
+MOZ_MUST_USE
+typename EnableIf<!IsBaseOf<nsWrapperCache, T>::value &&
+ !IsBaseOf<CallbackObject, T>::value &&
+ IsBaseOf<nsISupports, T>::value, bool>::Type
+ToJSValue(JSContext* aCx,
+ T& aArgument,
+ JS::MutableHandle<JS::Value> aValue)
+{
+ // Make sure we're called in a compartment
+ MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
+
+ qsObjectHelper helper(ToSupports(&aArgument), nullptr);
+ JS::Rooted<JSObject*> scope(aCx, JS::CurrentGlobalOrNull(aCx));
+ return XPCOMObjectToJsval(aCx, scope, helper, nullptr, true, aValue);
+}
+
+// Accept nsRefPtr/nsCOMPtr
+template <typename T>
+MOZ_MUST_USE bool
+ToJSValue(JSContext* aCx,
+ const nsCOMPtr<T>& aArgument,
+ JS::MutableHandle<JS::Value> aValue)
+{
+ return ToJSValue(aCx, *aArgument.get(), aValue);
+}
+
+template <typename T>
+MOZ_MUST_USE bool
+ToJSValue(JSContext* aCx,
+ const RefPtr<T>& aArgument,
+ JS::MutableHandle<JS::Value> aValue)
+{
+ return ToJSValue(aCx, *aArgument.get(), aValue);
+}
+
+template <typename T>
+MOZ_MUST_USE bool
+ToJSValue(JSContext* aCx,
+ const NonNull<T>& aArgument,
+ JS::MutableHandle<JS::Value> aValue)
+{
+ return ToJSValue(aCx, *aArgument.get(), aValue);
+}
+
+// Accept WebIDL dictionaries
+template <class T>
+MOZ_MUST_USE
+typename EnableIf<IsBaseOf<DictionaryBase, T>::value, bool>::Type
+ToJSValue(JSContext* aCx,
+ const T& aArgument,
+ JS::MutableHandle<JS::Value> aValue)
+{
+ return aArgument.ToObjectInternal(aCx, aValue);
+}
+
+// Accept existing JS values (which may not be same-compartment with us
+MOZ_MUST_USE inline bool
+ToJSValue(JSContext* aCx, JS::Handle<JS::Value> aArgument,
+ JS::MutableHandle<JS::Value> aValue)
+{
+ aValue.set(aArgument);
+ return MaybeWrapValue(aCx, aValue);
+}
+
+// Accept existing JS values on the Heap (which may not be same-compartment with us
+MOZ_MUST_USE inline bool
+ToJSValue(JSContext* aCx, const JS::Heap<JS::Value>& aArgument,
+ JS::MutableHandle<JS::Value> aValue)
+{
+ aValue.set(aArgument);
+ return MaybeWrapValue(aCx, aValue);
+}
+
+// Accept existing rooted JS values (which may not be same-compartment with us
+MOZ_MUST_USE inline bool
+ToJSValue(JSContext* aCx, const JS::Rooted<JS::Value>& aArgument,
+ JS::MutableHandle<JS::Value> aValue)
+{
+ aValue.set(aArgument);
+ return MaybeWrapValue(aCx, aValue);
+}
+
+// Accept existing rooted JS objects (which may not be same-compartment with
+// us).
+MOZ_MUST_USE inline bool
+ToJSValue(JSContext* aCx, const JS::Rooted<JSObject*>& aArgument,
+ JS::MutableHandle<JS::Value> aValue)
+{
+ aValue.setObjectOrNull(aArgument);
+ return MaybeWrapObjectOrNullValue(aCx, aValue);
+}
+
+// Accept nsresult, for use in rejections, and create an XPCOM
+// exception object representing that nsresult.
+MOZ_MUST_USE bool
+ToJSValue(JSContext* aCx,
+ nsresult aArgument,
+ JS::MutableHandle<JS::Value> aValue);
+
+// Accept ErrorResult, for use in rejections, and create an exception
+// representing the failure. Note, the ErrorResult must indicate a failure
+// with aArgument.Failure() returning true.
+MOZ_MUST_USE bool
+ToJSValue(JSContext* aCx,
+ ErrorResult& aArgument,
+ JS::MutableHandle<JS::Value> aValue);
+
+// Accept owning WebIDL unions.
+template <typename T>
+MOZ_MUST_USE
+typename EnableIf<IsBaseOf<AllOwningUnionBase, T>::value, bool>::Type
+ToJSValue(JSContext* aCx,
+ const T& aArgument,
+ JS::MutableHandle<JS::Value> aValue)
+{
+ JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
+ return aArgument.ToJSVal(aCx, global, aValue);
+}
+
+// Accept pointers to other things we accept
+template <typename T>
+MOZ_MUST_USE
+typename EnableIf<IsPointer<T>::value, bool>::Type
+ToJSValue(JSContext* aCx,
+ T aArgument,
+ JS::MutableHandle<JS::Value> aValue)
+{
+ return ToJSValue(aCx, *aArgument, aValue);
+}
+
+#ifdef SPIDERMONKEY_PROMISE
+// Accept Promise objects, which need special handling.
+MOZ_MUST_USE bool
+ToJSValue(JSContext* aCx,
+ Promise& aArgument,
+ JS::MutableHandle<JS::Value> aValue);
+#endif // SPIDERMONKEY_PROMISE
+
+// Accept arrays of other things we accept
+template <typename T>
+MOZ_MUST_USE bool
+ToJSValue(JSContext* aCx,
+ T* aArguments,
+ size_t aLength,
+ JS::MutableHandle<JS::Value> aValue)
+{
+ // Make sure we're called in a compartment
+ MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
+
+ JS::AutoValueVector v(aCx);
+ if (!v.resize(aLength)) {
+ return false;
+ }
+ for (size_t i = 0; i < aLength; ++i) {
+ if (!ToJSValue(aCx, aArguments[i], v[i])) {
+ return false;
+ }
+ }
+ JSObject* arrayObj = JS_NewArrayObject(aCx, v);
+ if (!arrayObj) {
+ return false;
+ }
+ aValue.setObject(*arrayObj);
+ return true;
+}
+
+template <typename T>
+MOZ_MUST_USE bool
+ToJSValue(JSContext* aCx,
+ const nsTArray<T>& aArgument,
+ JS::MutableHandle<JS::Value> aValue)
+{
+ return ToJSValue(aCx, aArgument.Elements(),
+ aArgument.Length(), aValue);
+}
+
+template <typename T>
+MOZ_MUST_USE bool
+ToJSValue(JSContext* aCx,
+ const FallibleTArray<T>& aArgument,
+ JS::MutableHandle<JS::Value> aValue)
+{
+ return ToJSValue(aCx, aArgument.Elements(),
+ aArgument.Length(), aValue);
+}
+
+template <typename T, int N>
+MOZ_MUST_USE bool
+ToJSValue(JSContext* aCx,
+ const T(&aArgument)[N],
+ JS::MutableHandle<JS::Value> aValue)
+{
+ return ToJSValue(aCx, aArgument, N, aValue);
+}
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_ToJSValue_h */
diff --git a/dom/bindings/TypedArray.h b/dom/bindings/TypedArray.h
new file mode 100644
index 000000000..a86abcd9d
--- /dev/null
+++ b/dom/bindings/TypedArray.h
@@ -0,0 +1,441 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "jsapi.h"
+#include "jsfriendapi.h"
+#include "js/RootingAPI.h"
+#include "js/TracingAPI.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Move.h"
+#include "mozilla/dom/BindingDeclarations.h"
+#include "nsWrapperCache.h"
+
+namespace mozilla {
+namespace dom {
+
+/*
+ * Class that just handles the JSObject storage and tracing for typed arrays
+ */
+struct TypedArrayObjectStorage : AllTypedArraysBase {
+protected:
+ JSObject* mTypedObj;
+ JSObject* mWrappedObj;
+
+ TypedArrayObjectStorage()
+ : mTypedObj(nullptr),
+ mWrappedObj(nullptr)
+ {
+ }
+
+ TypedArrayObjectStorage(TypedArrayObjectStorage&& aOther)
+ : mTypedObj(aOther.mTypedObj),
+ mWrappedObj(aOther.mWrappedObj)
+ {
+ aOther.mTypedObj = nullptr;
+ aOther.mWrappedObj = nullptr;
+ }
+
+public:
+ inline void TraceSelf(JSTracer* trc)
+ {
+ JS::UnsafeTraceRoot(trc, &mTypedObj, "TypedArray.mTypedObj");
+ JS::UnsafeTraceRoot(trc, &mWrappedObj, "TypedArray.mWrappedObj");
+ }
+
+private:
+ TypedArrayObjectStorage(const TypedArrayObjectStorage&) = delete;
+};
+
+/*
+ * 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* UnwrapArray(JSObject*),
+ void GetLengthAndDataAndSharedness(JSObject*, uint32_t*, bool*, T**)>
+struct TypedArray_base : public TypedArrayObjectStorage {
+ typedef T element_type;
+
+ TypedArray_base()
+ : mData(nullptr),
+ mLength(0),
+ mShared(false),
+ mComputed(false)
+ {
+ }
+
+ TypedArray_base(TypedArray_base&& aOther)
+ : TypedArrayObjectStorage(Move(aOther)),
+ mData(aOther.mData),
+ mLength(aOther.mLength),
+ mShared(aOther.mShared),
+ mComputed(aOther.mComputed)
+ {
+ aOther.mData = nullptr;
+ aOther.mLength = 0;
+ aOther.mShared = false;
+ aOther.mComputed = false;
+ }
+
+private:
+ mutable T* mData;
+ mutable uint32_t mLength;
+ mutable bool mShared;
+ mutable bool mComputed;
+
+public:
+ inline bool Init(JSObject* obj)
+ {
+ MOZ_ASSERT(!inited());
+ mTypedObj = mWrappedObj = UnwrapArray(obj);
+ return inited();
+ }
+
+ inline bool inited() const {
+ return !!mTypedObj;
+ }
+
+ // About shared memory:
+ //
+ // Any DOM TypedArray as well as any DOM ArrayBufferView that does
+ // not represent a JS DataView can map the memory of either a JS
+ // ArrayBuffer or a JS SharedArrayBuffer. (DataView cannot view
+ // shared memory.) If the TypedArray maps a SharedArrayBuffer the
+ // Length() and Data() accessors on the DOM view will return zero
+ // and nullptr; to get the actual length and data, call the
+ // LengthAllowShared() and DataAllowShared() accessors instead.
+ //
+ // Two methods are available for determining if a DOM view maps
+ // shared memory. The IsShared() method is cheap and can be called
+ // if the view has been computed; the JS_GetTypedArraySharedness()
+ // method is slightly more expensive and can be called on the Obj()
+ // value if the view may not have been computed and if the value is
+ // known to represent a JS TypedArray.
+ //
+ // (Just use JS_IsSharedArrayBuffer() to test if any object is of
+ // that type.)
+ //
+ // Code that elects to allow views that map shared memory to be used
+ // -- ie, code that "opts in to shared memory" -- should generally
+ // not access the raw data buffer with standard C++ mechanisms as
+ // that creates the possibility of C++ data races, which is
+ // undefined behavior. The JS engine will eventually export (bug
+ // 1225033) a suite of methods that avoid undefined behavior.
+ //
+ // Callers of Obj() that do not opt in to shared memory can produce
+ // better diagnostics by checking whether the JSObject in fact maps
+ // shared memory and throwing an error if it does. However, it is
+ // safe to use the value of Obj() without such checks.
+ //
+ // The DOM TypedArray abstraction prevents the underlying buffer object
+ // from being accessed directly, but JS_GetArrayBufferViewBuffer(Obj())
+ // will obtain the buffer object. Code that calls that function must
+ // not assume the returned buffer is an ArrayBuffer. That is guarded
+ // against by an out parameter on that call that communicates the
+ // sharedness of the buffer.
+ //
+ // Finally, note that the buffer memory of a SharedArrayBuffer is
+ // not detachable.
+
+ inline bool IsShared() const {
+ MOZ_ASSERT(mComputed);
+ return mShared;
+ }
+
+ inline T *Data() const {
+ MOZ_ASSERT(mComputed);
+ if (mShared)
+ return nullptr;
+ return mData;
+ }
+
+ inline T *DataAllowShared() const {
+ MOZ_ASSERT(mComputed);
+ return mData;
+ }
+
+ inline uint32_t Length() const {
+ MOZ_ASSERT(mComputed);
+ if (mShared)
+ return 0;
+ return mLength;
+ }
+
+ inline uint32_t LengthAllowShared() const {
+ MOZ_ASSERT(mComputed);
+ return mLength;
+ }
+
+ inline JSObject *Obj() const {
+ MOZ_ASSERT(inited());
+ return mWrappedObj;
+ }
+
+ inline bool WrapIntoNewCompartment(JSContext* cx)
+ {
+ return JS_WrapObject(cx,
+ JS::MutableHandle<JSObject*>::fromMarkedLocation(&mWrappedObj));
+ }
+
+ inline void ComputeLengthAndData() const
+ {
+ MOZ_ASSERT(inited());
+ MOZ_ASSERT(!mComputed);
+ GetLengthAndDataAndSharedness(mTypedObj, &mLength, &mShared, &mData);
+ mComputed = true;
+ }
+
+private:
+ TypedArray_base(const TypedArray_base&) = delete;
+};
+
+template<typename T,
+ JSObject* UnwrapArray(JSObject*),
+ T* GetData(JSObject*, bool* isShared, const JS::AutoCheckCannotGC&),
+ void GetLengthAndDataAndSharedness(JSObject*, uint32_t*, bool*, T**),
+ JSObject* CreateNew(JSContext*, uint32_t)>
+struct TypedArray
+ : public TypedArray_base<T, UnwrapArray, GetLengthAndDataAndSharedness>
+{
+private:
+ typedef TypedArray_base<T, UnwrapArray, GetLengthAndDataAndSharedness> Base;
+
+public:
+ TypedArray()
+ : Base()
+ {}
+
+ TypedArray(TypedArray&& aOther)
+ : Base(Move(aOther))
+ {
+ }
+
+ static inline JSObject*
+ Create(JSContext* cx, nsWrapperCache* creator, uint32_t length,
+ const T* data = nullptr) {
+ JS::Rooted<JSObject*> creatorWrapper(cx);
+ Maybe<JSAutoCompartment> ac;
+ if (creator && (creatorWrapper = creator->GetWrapperPreserveColor())) {
+ ac.emplace(cx, creatorWrapper);
+ }
+
+ return CreateCommon(cx, length, data);
+ }
+
+ static inline JSObject*
+ Create(JSContext* cx, uint32_t length, const T* data = nullptr) {
+ return CreateCommon(cx, length, data);
+ }
+
+private:
+ static inline JSObject*
+ CreateCommon(JSContext* cx, uint32_t length, const T* data) {
+ JSObject* obj = CreateNew(cx, length);
+ if (!obj) {
+ return nullptr;
+ }
+ if (data) {
+ JS::AutoCheckCannotGC nogc;
+ bool isShared;
+ T* buf = static_cast<T*>(GetData(obj, &isShared, nogc));
+ // Data will not be shared, until a construction protocol exists
+ // for constructing shared data.
+ MOZ_ASSERT(!isShared);
+ memcpy(buf, data, length*sizeof(T));
+ }
+ return obj;
+ }
+
+ TypedArray(const TypedArray&) = delete;
+};
+
+template<JSObject* UnwrapArray(JSObject*),
+ void GetLengthAndDataAndSharedness(JSObject*, uint32_t*, bool*,
+ uint8_t**),
+ js::Scalar::Type GetViewType(JSObject*)>
+struct ArrayBufferView_base
+ : public TypedArray_base<uint8_t, UnwrapArray, GetLengthAndDataAndSharedness>
+{
+private:
+ typedef TypedArray_base<uint8_t, UnwrapArray, GetLengthAndDataAndSharedness>
+ Base;
+
+public:
+ ArrayBufferView_base()
+ : Base()
+ {
+ }
+
+ ArrayBufferView_base(ArrayBufferView_base&& aOther)
+ : Base(Move(aOther)),
+ mType(aOther.mType)
+ {
+ aOther.mType = js::Scalar::MaxTypedArrayViewType;
+ }
+
+private:
+ js::Scalar::Type mType;
+
+public:
+ inline bool Init(JSObject* obj)
+ {
+ if (!Base::Init(obj)) {
+ return false;
+ }
+
+ mType = GetViewType(this->Obj());
+ return true;
+ }
+
+ inline js::Scalar::Type Type() const
+ {
+ MOZ_ASSERT(this->inited());
+ return mType;
+ }
+};
+
+typedef TypedArray<int8_t, js::UnwrapInt8Array, JS_GetInt8ArrayData,
+ js::GetInt8ArrayLengthAndData, JS_NewInt8Array>
+ Int8Array;
+typedef TypedArray<uint8_t, js::UnwrapUint8Array, JS_GetUint8ArrayData,
+ js::GetUint8ArrayLengthAndData, JS_NewUint8Array>
+ Uint8Array;
+typedef TypedArray<uint8_t, js::UnwrapUint8ClampedArray, JS_GetUint8ClampedArrayData,
+ js::GetUint8ClampedArrayLengthAndData, JS_NewUint8ClampedArray>
+ Uint8ClampedArray;
+typedef TypedArray<int16_t, js::UnwrapInt16Array, JS_GetInt16ArrayData,
+ js::GetInt16ArrayLengthAndData, JS_NewInt16Array>
+ Int16Array;
+typedef TypedArray<uint16_t, js::UnwrapUint16Array, JS_GetUint16ArrayData,
+ js::GetUint16ArrayLengthAndData, JS_NewUint16Array>
+ Uint16Array;
+typedef TypedArray<int32_t, js::UnwrapInt32Array, JS_GetInt32ArrayData,
+ js::GetInt32ArrayLengthAndData, JS_NewInt32Array>
+ Int32Array;
+typedef TypedArray<uint32_t, js::UnwrapUint32Array, JS_GetUint32ArrayData,
+ js::GetUint32ArrayLengthAndData, JS_NewUint32Array>
+ Uint32Array;
+typedef TypedArray<float, js::UnwrapFloat32Array, JS_GetFloat32ArrayData,
+ js::GetFloat32ArrayLengthAndData, JS_NewFloat32Array>
+ Float32Array;
+typedef TypedArray<double, js::UnwrapFloat64Array, JS_GetFloat64ArrayData,
+ js::GetFloat64ArrayLengthAndData, JS_NewFloat64Array>
+ Float64Array;
+typedef ArrayBufferView_base<js::UnwrapArrayBufferView,
+ js::GetArrayBufferViewLengthAndData,
+ JS_GetArrayBufferViewType>
+ ArrayBufferView;
+typedef TypedArray<uint8_t, js::UnwrapArrayBuffer, JS_GetArrayBufferData,
+ js::GetArrayBufferLengthAndData, JS_NewArrayBuffer>
+ ArrayBuffer;
+
+typedef TypedArray<uint8_t, js::UnwrapSharedArrayBuffer, JS_GetSharedArrayBufferData,
+ js::GetSharedArrayBufferLengthAndData, JS_NewSharedArrayBuffer>
+ SharedArrayBuffer;
+
+// A class for converting an nsTArray to a TypedArray
+// Note: A TypedArrayCreator must not outlive the nsTArray it was created from.
+// So this is best used to pass from things that understand nsTArray to
+// things that understand TypedArray, as with Promise::ArgumentToJSValue.
+template<typename TypedArrayType>
+class TypedArrayCreator
+{
+ typedef nsTArray<typename TypedArrayType::element_type> ArrayType;
+
+ public:
+ explicit TypedArrayCreator(const ArrayType& aArray)
+ : mArray(aArray)
+ {}
+
+ JSObject* Create(JSContext* aCx) const
+ {
+ return TypedArrayType::Create(aCx, mArray.Length(), mArray.Elements());
+ }
+
+ private:
+ const ArrayType& mArray;
+};
+
+// A class for rooting an existing TypedArray struct
+template<typename ArrayType>
+class MOZ_RAII TypedArrayRooter : private JS::CustomAutoRooter
+{
+public:
+ template <typename CX>
+ TypedArrayRooter(const CX& cx,
+ ArrayType* aArray MOZ_GUARD_OBJECT_NOTIFIER_PARAM) :
+ JS::CustomAutoRooter(cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT),
+ mArray(aArray)
+ {
+ }
+
+ virtual void trace(JSTracer* trc) override
+ {
+ mArray->TraceSelf(trc);
+ }
+
+private:
+ TypedArrayObjectStorage* const mArray;
+};
+
+// And a specialization for dealing with nullable typed arrays
+template<typename Inner> struct Nullable;
+template<typename ArrayType>
+class MOZ_RAII TypedArrayRooter<Nullable<ArrayType> > :
+ private JS::CustomAutoRooter
+{
+public:
+ template <typename CX>
+ TypedArrayRooter(const CX& cx,
+ Nullable<ArrayType>* aArray MOZ_GUARD_OBJECT_NOTIFIER_PARAM) :
+ JS::CustomAutoRooter(cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT),
+ mArray(aArray)
+ {
+ }
+
+ virtual void trace(JSTracer* trc) override
+ {
+ if (!mArray->IsNull()) {
+ mArray->Value().TraceSelf(trc);
+ }
+ }
+
+private:
+ Nullable<ArrayType>* const mArray;
+};
+
+// Class for easily setting up a rooted typed array object on the stack
+template<typename ArrayType>
+class MOZ_RAII RootedTypedArray final : public ArrayType,
+ private TypedArrayRooter<ArrayType>
+{
+public:
+ template <typename CX>
+ explicit RootedTypedArray(const CX& cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) :
+ ArrayType(),
+ TypedArrayRooter<ArrayType>(cx, this
+ MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT)
+ {
+ }
+
+ template <typename CX>
+ RootedTypedArray(const CX& cx, JSObject* obj MOZ_GUARD_OBJECT_NOTIFIER_PARAM) :
+ ArrayType(obj),
+ TypedArrayRooter<ArrayType>(cx, this
+ MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT)
+ {
+ }
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_TypedArray_h */
diff --git a/dom/bindings/UnionMember.h b/dom/bindings/UnionMember.h
new file mode 100644
index 000000000..3842c6eb0
--- /dev/null
+++ b/dom/bindings/UnionMember.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 class for holding the members of a union. */
+
+#ifndef mozilla_dom_UnionMember_h
+#define mozilla_dom_UnionMember_h
+
+#include "mozilla/Alignment.h"
+
+namespace mozilla {
+namespace dom {
+
+// The union type has an enum to keep track of which of its UnionMembers has
+// been constructed.
+template<class T>
+class UnionMember
+{
+ AlignedStorage2<T> mStorage;
+
+public:
+ T& SetValue()
+ {
+ new (mStorage.addr()) T();
+ return *mStorage.addr();
+ }
+ template <typename T1>
+ T& SetValue(const T1& aValue)
+ {
+ new (mStorage.addr()) T(aValue);
+ return *mStorage.addr();
+ }
+ template<typename T1, typename T2>
+ T& SetValue(const T1& aValue1, const T2& aValue2)
+ {
+ new (mStorage.addr()) T(aValue1, aValue2);
+ return *mStorage.addr();
+ }
+ T& Value()
+ {
+ return *mStorage.addr();
+ }
+ const T& Value() const
+ {
+ return *mStorage.addr();
+ }
+ void Destroy()
+ {
+ mStorage.addr()->~T();
+ }
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_UnionMember_h
diff --git a/dom/bindings/WebIDLGlobalNameHash.cpp b/dom/bindings/WebIDLGlobalNameHash.cpp
new file mode 100644
index 000000000..7477b52e7
--- /dev/null
+++ b/dom/bindings/WebIDLGlobalNameHash.cpp
@@ -0,0 +1,324 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "WebIDLGlobalNameHash.h"
+#include "js/GCAPI.h"
+#include "mozilla/HashFunctions.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/dom/DOMJSProxyHandler.h"
+#include "mozilla/dom/RegisterBindings.h"
+#include "nsIMemoryReporter.h"
+#include "nsTHashtable.h"
+
+namespace mozilla {
+namespace dom {
+
+struct MOZ_STACK_CLASS WebIDLNameTableKey
+{
+ explicit WebIDLNameTableKey(JSFlatString* aJSString)
+ : mLength(js::GetFlatStringLength(aJSString))
+ {
+ mNogc.emplace();
+ JSLinearString* jsString = js::FlatStringToLinearString(aJSString);
+ if (js::LinearStringHasLatin1Chars(jsString)) {
+ mLatin1String = reinterpret_cast<const char*>(
+ js::GetLatin1LinearStringChars(*mNogc, jsString));
+ mTwoBytesString = nullptr;
+ mHash = mLatin1String ? HashString(mLatin1String, mLength) : 0;
+ } else {
+ mLatin1String = nullptr;
+ mTwoBytesString = js::GetTwoByteLinearStringChars(*mNogc, jsString);
+ mHash = mTwoBytesString ? HashString(mTwoBytesString, mLength) : 0;
+ }
+ }
+ explicit WebIDLNameTableKey(const char* aString, size_t aLength)
+ : mLatin1String(aString),
+ mTwoBytesString(nullptr),
+ mLength(aLength),
+ mHash(HashString(aString, aLength))
+ {
+ MOZ_ASSERT(aString[aLength] == '\0');
+ }
+
+ Maybe<JS::AutoCheckCannotGC> mNogc;
+ const char* mLatin1String;
+ const char16_t* mTwoBytesString;
+ size_t mLength;
+ uint32_t mHash;
+};
+
+struct WebIDLNameTableEntry : public PLDHashEntryHdr
+{
+ typedef const WebIDLNameTableKey& KeyType;
+ typedef const WebIDLNameTableKey* KeyTypePointer;
+
+ explicit WebIDLNameTableEntry(KeyTypePointer aKey)
+ : mNameOffset(0),
+ mNameLength(0),
+ mDefine(nullptr),
+ mEnabled(nullptr)
+ {}
+ WebIDLNameTableEntry(WebIDLNameTableEntry&& aEntry)
+ : mNameOffset(aEntry.mNameOffset),
+ mNameLength(aEntry.mNameLength),
+ mDefine(aEntry.mDefine),
+ mEnabled(aEntry.mEnabled)
+ {}
+ ~WebIDLNameTableEntry()
+ {}
+
+ bool KeyEquals(KeyTypePointer aKey) const
+ {
+ if (mNameLength != aKey->mLength) {
+ return false;
+ }
+
+ const char* name = WebIDLGlobalNameHash::sNames + mNameOffset;
+
+ if (aKey->mLatin1String) {
+ return PodEqual(aKey->mLatin1String, name, aKey->mLength);
+ }
+
+ return nsCharTraits<char16_t>::compareASCII(aKey->mTwoBytesString, name,
+ aKey->mLength) == 0;
+ }
+
+ static KeyTypePointer KeyToPointer(KeyType aKey)
+ {
+ return &aKey;
+ }
+
+ static PLDHashNumber HashKey(KeyTypePointer aKey)
+ {
+ return aKey->mHash;
+ }
+
+ enum { ALLOW_MEMMOVE = true };
+
+ uint16_t mNameOffset;
+ uint16_t mNameLength;
+ WebIDLGlobalNameHash::DefineGlobalName mDefine;
+ // May be null if enabled unconditionally
+ WebIDLGlobalNameHash::ConstructorEnabled* mEnabled;
+};
+
+static nsTHashtable<WebIDLNameTableEntry>* sWebIDLGlobalNames;
+
+class WebIDLGlobalNamesHashReporter final : public nsIMemoryReporter
+{
+ MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
+
+ ~WebIDLGlobalNamesHashReporter() {}
+
+public:
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize) override
+ {
+ int64_t amount =
+ sWebIDLGlobalNames ?
+ sWebIDLGlobalNames->ShallowSizeOfIncludingThis(MallocSizeOf) : 0;
+
+ MOZ_COLLECT_REPORT(
+ "explicit/dom/webidl-globalnames", KIND_HEAP, UNITS_BYTES, amount,
+ "Memory used by the hash table for WebIDL's global names.");
+
+ return NS_OK;
+ }
+};
+
+NS_IMPL_ISUPPORTS(WebIDLGlobalNamesHashReporter, nsIMemoryReporter)
+
+/* static */
+void
+WebIDLGlobalNameHash::Init()
+{
+ sWebIDLGlobalNames = new nsTHashtable<WebIDLNameTableEntry>(sCount);
+ RegisterWebIDLGlobalNames();
+
+ RegisterStrongMemoryReporter(new WebIDLGlobalNamesHashReporter());
+}
+
+/* static */
+void
+WebIDLGlobalNameHash::Shutdown()
+{
+ delete sWebIDLGlobalNames;
+}
+
+/* static */
+void
+WebIDLGlobalNameHash::Register(uint16_t aNameOffset, uint16_t aNameLength,
+ DefineGlobalName aDefine,
+ ConstructorEnabled* aEnabled)
+{
+ const char* name = sNames + aNameOffset;
+ WebIDLNameTableKey key(name, aNameLength);
+ WebIDLNameTableEntry* entry = sWebIDLGlobalNames->PutEntry(key);
+ entry->mNameOffset = aNameOffset;
+ entry->mNameLength = aNameLength;
+ entry->mDefine = aDefine;
+ entry->mEnabled = aEnabled;
+}
+
+/* static */
+void
+WebIDLGlobalNameHash::Remove(const char* aName, uint32_t aLength)
+{
+ WebIDLNameTableKey key(aName, aLength);
+ sWebIDLGlobalNames->RemoveEntry(key);
+}
+
+/* static */
+bool
+WebIDLGlobalNameHash::DefineIfEnabled(JSContext* aCx,
+ JS::Handle<JSObject*> aObj,
+ JS::Handle<jsid> aId,
+ JS::MutableHandle<JS::PropertyDescriptor> aDesc,
+ bool* aFound)
+{
+ MOZ_ASSERT(JSID_IS_STRING(aId), "Check for string id before calling this!");
+
+ const WebIDLNameTableEntry* entry;
+ {
+ WebIDLNameTableKey key(JSID_TO_FLAT_STRING(aId));
+ // Rooting analysis thinks nsTHashtable<...>::GetEntry may GC because it
+ // ends up calling through PLDHashTableOps' matchEntry function pointer, but
+ // we know WebIDLNameTableEntry::KeyEquals can't cause a GC.
+ JS::AutoSuppressGCAnalysis suppress;
+ entry = sWebIDLGlobalNames->GetEntry(key);
+ }
+
+ if (!entry) {
+ *aFound = false;
+ return true;
+ }
+
+ *aFound = true;
+
+ ConstructorEnabled* checkEnabledForScope = entry->mEnabled;
+ // We do the enabled check on the current compartment of aCx, but for the
+ // actual object we pass in the underlying object in the Xray case. That
+ // way the callee can decide whether to allow access based on the caller
+ // or the window being touched.
+ JS::Rooted<JSObject*> global(aCx,
+ js::CheckedUnwrap(aObj, /* stopAtWindowProxy = */ false));
+ if (!global) {
+ return Throw(aCx, NS_ERROR_DOM_SECURITY_ERR);
+ }
+
+ {
+ // It's safe to pass "&global" here, because we've already unwrapped it, but
+ // for general sanity better to not have debug code even having the
+ // appearance of mutating things that opt code uses.
+#ifdef DEBUG
+ JS::Rooted<JSObject*> temp(aCx, global);
+ DebugOnly<nsGlobalWindow*> win;
+ MOZ_ASSERT(NS_SUCCEEDED(UNWRAP_OBJECT(Window, &temp, win)));
+#endif
+ }
+
+ if (checkEnabledForScope && !checkEnabledForScope(aCx, global)) {
+ return true;
+ }
+
+ // The DOM constructor resolve machinery interacts with Xrays in tricky
+ // ways, and there are some asymmetries that are important to understand.
+ //
+ // In the regular (non-Xray) case, we only want to resolve constructors
+ // once (so that if they're deleted, they don't reappear). We do this by
+ // stashing the constructor in a slot on the global, such that we can see
+ // during resolve whether we've created it already. This is rather
+ // memory-intensive, so we don't try to maintain these semantics when
+ // manipulating a global over Xray (so the properties just re-resolve if
+ // they've been deleted).
+ //
+ // Unfortunately, there's a bit of an impedance-mismatch between the Xray
+ // and non-Xray machinery. The Xray machinery wants an API that returns a
+ // JS::PropertyDescriptor, so that the resolve hook doesn't have to get
+ // snared up with trying to define a property on the Xray holder. At the
+ // same time, the DefineInterface callbacks are set up to define things
+ // directly on the global. And re-jiggering them to return property
+ // descriptors is tricky, because some DefineInterface callbacks define
+ // multiple things (like the Image() alias for HTMLImageElement).
+ //
+ // So the setup is as-follows:
+ //
+ // * The resolve function takes a JS::PropertyDescriptor, but in the
+ // non-Xray case, callees may define things directly on the global, and
+ // set the value on the property descriptor to |undefined| to indicate
+ // that there's nothing more for the caller to do. We assert against
+ // this behavior in the Xray case.
+ //
+ // * We make sure that we do a non-Xray resolve first, so that all the
+ // slots are set up. In the Xray case, this means unwrapping and doing
+ // a non-Xray resolve before doing the Xray resolve.
+ //
+ // This all could use some grand refactoring, but for now we just limp
+ // along.
+ if (xpc::WrapperFactory::IsXrayWrapper(aObj)) {
+ JS::Rooted<JSObject*> interfaceObject(aCx);
+ {
+ JSAutoCompartment ac(aCx, global);
+ interfaceObject = entry->mDefine(aCx, global, aId, false);
+ }
+ if (NS_WARN_IF(!interfaceObject)) {
+ return Throw(aCx, NS_ERROR_FAILURE);
+ }
+ if (!JS_WrapObject(aCx, &interfaceObject)) {
+ return Throw(aCx, NS_ERROR_FAILURE);
+ }
+
+ FillPropertyDescriptor(aDesc, aObj, 0, JS::ObjectValue(*interfaceObject));
+ return true;
+ }
+
+ JS::Rooted<JSObject*> interfaceObject(aCx,
+ entry->mDefine(aCx, aObj, aId, true));
+ if (NS_WARN_IF(!interfaceObject)) {
+ return Throw(aCx, NS_ERROR_FAILURE);
+ }
+
+ // We've already defined the property. We indicate this to the caller
+ // by filling a property descriptor with JS::UndefinedValue() as the
+ // value. We still have to fill in a property descriptor, though, so
+ // that the caller knows the property is in fact on this object. It
+ // doesn't matter what we pass for the "readonly" argument here.
+ FillPropertyDescriptor(aDesc, aObj, JS::UndefinedValue(), false);
+
+ return true;
+}
+
+/* static */
+bool
+WebIDLGlobalNameHash::MayResolve(jsid aId)
+{
+ WebIDLNameTableKey key(JSID_TO_FLAT_STRING(aId));
+ // Rooting analysis thinks nsTHashtable<...>::Contains may GC because it ends
+ // up calling through PLDHashTableOps' matchEntry function pointer, but we
+ // know WebIDLNameTableEntry::KeyEquals can't cause a GC.
+ JS::AutoSuppressGCAnalysis suppress;
+ return sWebIDLGlobalNames->Contains(key);
+}
+
+/* static */
+void
+WebIDLGlobalNameHash::GetNames(JSContext* aCx, JS::Handle<JSObject*> aObj,
+ nsTArray<nsString>& aNames)
+{
+ for (auto iter = sWebIDLGlobalNames->Iter(); !iter.Done(); iter.Next()) {
+ const WebIDLNameTableEntry* entry = iter.Get();
+ if (!entry->mEnabled || entry->mEnabled(aCx, aObj)) {
+ AppendASCIItoUTF16(nsDependentCString(sNames + entry->mNameOffset,
+ entry->mNameLength),
+ *aNames.AppendElement());
+ }
+ }
+}
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/bindings/WebIDLGlobalNameHash.h b/dom/bindings/WebIDLGlobalNameHash.h
new file mode 100644
index 000000000..bbe015395
--- /dev/null
+++ b/dom/bindings/WebIDLGlobalNameHash.h
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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_WebIDLGlobalNameHash_h__
+#define mozilla_dom_WebIDLGlobalNameHash_h__
+
+#include "js/RootingAPI.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace dom {
+
+struct WebIDLNameTableEntry;
+
+class WebIDLGlobalNameHash
+{
+public:
+ static void Init();
+ static void Shutdown();
+
+ typedef JSObject*
+ (*DefineGlobalName)(JSContext* cx, JS::Handle<JSObject*> global,
+ JS::Handle<jsid> id, bool defineOnGlobal);
+
+ // Check whether a constructor should be enabled for the given object.
+ // Note that the object should NOT be an Xray, since Xrays will end up
+ // defining constructors on the underlying object.
+ // This is a typedef for the function type itself, not the function
+ // pointer, so it's more obvious that pointers to a ConstructorEnabled
+ // can be null.
+ typedef bool
+ (ConstructorEnabled)(JSContext* cx, JS::Handle<JSObject*> obj);
+
+ static void Register(uint16_t aNameOffset, uint16_t aNameLength,
+ DefineGlobalName aDefine, ConstructorEnabled* aEnabled);
+
+ static void Remove(const char* aName, uint32_t aLength);
+
+ // Returns false if something failed. aFound is set to true if the name is in
+ // the hash, whether it's enabled or not.
+ static bool DefineIfEnabled(JSContext* aCx, JS::Handle<JSObject*> aObj,
+ JS::Handle<jsid> aId,
+ JS::MutableHandle<JS::PropertyDescriptor> aDesc,
+ bool* aFound);
+
+ static bool MayResolve(jsid aId);
+
+ static void GetNames(JSContext* aCx, JS::Handle<JSObject*> aObj,
+ nsTArray<nsString>& aNames);
+
+private:
+ friend struct WebIDLNameTableEntry;
+
+ // The total number of names that we will add to the hash.
+ // The value of sCount is generated by Codegen.py in RegisterBindings.cpp.
+ static const uint32_t sCount;
+
+ // The names that will be registered in the hash, concatenated as one big
+ // string with \0 as a separator between names.
+ // The value of sNames is generated by Codegen.py in RegisterBindings.cpp.
+ static const char sNames[];
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_WebIDLGlobalNameHash_h__
diff --git a/dom/bindings/XrayExpandoClass.h b/dom/bindings/XrayExpandoClass.h
new file mode 100644
index 000000000..af5c1e9ba
--- /dev/null
+++ b/dom/bindings/XrayExpandoClass.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * This file declares a macro for defining Xray expando classes and declares the
+ * default Xray expando class. The actual definition of that default class
+ * lives elsewhere.
+ */
+#ifndef mozilla_dom_XrayExpandoClass_h
+#define mozilla_dom_XrayExpandoClass_h
+
+/*
+ * maybeStatic_ Should be either `static` or nothing (because some Xray expando
+ * classes are not static).
+ *
+ * name_ should be the name of the variable.
+ *
+ * extraSlots_ should be how many extra slots to give the class, in addition to
+ * the ones Xray expandos want.
+ */
+#define DEFINE_XRAY_EXPANDO_CLASS(maybeStatic_, name_, extraSlots_) \
+ maybeStatic_ const JSClass name_ = { \
+ "XrayExpandoObject", \
+ JSCLASS_HAS_RESERVED_SLOTS(xpc::JSSLOT_EXPANDO_COUNT + \
+ (extraSlots_)) | \
+ JSCLASS_FOREGROUND_FINALIZE, \
+ &xpc::XrayExpandoObjectClassOps \
+ }
+
+namespace mozilla {
+namespace dom {
+
+extern const JSClass DefaultXrayExpandoObjectClass;
+
+} // namespace mozilla
+} // namespace dom
+
+#endif /* mozilla_dom_XrayExpandoClass_h */
diff --git a/dom/bindings/crashtests/1010658-1.html b/dom/bindings/crashtests/1010658-1.html
new file mode 100644
index 000000000..af46069c7
--- /dev/null
+++ b/dom/bindings/crashtests/1010658-1.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+function boom()
+{
+ window.__proto__ = null;
+ for (var i = 0; i < 10000; ++i) {
+ self.document;
+ }
+}
+
+</script></head>
+
+<body onload="boom();"></body>
+</html>
diff --git a/dom/bindings/crashtests/1010658-2.html b/dom/bindings/crashtests/1010658-2.html
new file mode 100644
index 000000000..4b80242ec
--- /dev/null
+++ b/dom/bindings/crashtests/1010658-2.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+function boom()
+{
+ window.__proto__ = function(){};
+ for (var i = 0; i < 10000; ++i) {
+ self.document;
+ }
+}
+
+</script></head>
+
+<body onload="boom();"></body>
+</html>
diff --git a/dom/bindings/crashtests/769464.html b/dom/bindings/crashtests/769464.html
new file mode 100644
index 000000000..84d6dbc08
--- /dev/null
+++ b/dom/bindings/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/dom/bindings/crashtests/822340-1.html b/dom/bindings/crashtests/822340-1.html
new file mode 100644
index 000000000..4c8f6ae46
--- /dev/null
+++ b/dom/bindings/crashtests/822340-1.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<script>
+ var xhr = new XMLHttpRequest;
+ function f() {
+ var x = xhr.getResponseHeader;
+ x("abc");
+ }
+ for (var i = 0; i < 20000; ++i) {
+ try { f(); } catch (e) {}
+ }
+</script>
diff --git a/dom/bindings/crashtests/822340-2.html b/dom/bindings/crashtests/822340-2.html
new file mode 100644
index 000000000..e938c91aa
--- /dev/null
+++ b/dom/bindings/crashtests/822340-2.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<script>
+ var l = document.getElementsByTagName("*");
+ var count = 20000;
+ for (var i = 0; i < count; ++i) {
+ l.item(0);
+ }
+</script>
diff --git a/dom/bindings/crashtests/832899.html b/dom/bindings/crashtests/832899.html
new file mode 100644
index 000000000..c565ad00f
--- /dev/null
+++ b/dom/bindings/crashtests/832899.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<script>
+ var ev = document.createEvent("Events");
+ EventTarget.prototype.dispatchEvent.call(navigator.connection, ev);
+</script>
diff --git a/dom/bindings/crashtests/860551.html b/dom/bindings/crashtests/860551.html
new file mode 100644
index 000000000..5008e5739
--- /dev/null
+++ b/dom/bindings/crashtests/860551.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<script>
+SVGZoomAndPan instanceof SVGZoomAndPan
+</script>
diff --git a/dom/bindings/crashtests/860591.html b/dom/bindings/crashtests/860591.html
new file mode 100644
index 000000000..565a729c4
--- /dev/null
+++ b/dom/bindings/crashtests/860591.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+ var timeEvent = document.createEvent('TimeEvent');
+ var mutationEvent = document.createEvent('MutationEvents');
+ mutationEvent.__proto__ = timeEvent;
+ mutationEvent.target;
+
+ var mouseScrollEvent = document.createEvent("MouseScrollEvents");
+ var mouseEvent = document.createEvent("MouseEvents");
+ mouseEvent.__proto__ = mouseScrollEvent;
+ mouseEvent.relatedTarget;
+
+ var uiEvent = document.createEvent("UIEvents");
+ uiEvent.__proto__ = mouseScrollEvent;
+ uiEvent.rangeParent;
+</script>
+</head>
+</html>
diff --git a/dom/bindings/crashtests/862092.html b/dom/bindings/crashtests/862092.html
new file mode 100644
index 000000000..1b31775a9
--- /dev/null
+++ b/dom/bindings/crashtests/862092.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<script>
+
+function boom()
+{
+ var frameDoc = document.getElementById("f").contentDocument;
+ frameDoc.adoptNode(document.createElement("select"));
+}
+
+</script>
+</head>
+
+<body onload="boom();">
+<iframe id="f"></iframe>
+</body>
+</html>
diff --git a/dom/bindings/crashtests/862610.html b/dom/bindings/crashtests/862610.html
new file mode 100644
index 000000000..768871ad9
--- /dev/null
+++ b/dom/bindings/crashtests/862610.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<script>
+ HTMLElement.prototype.__proto__ = new Proxy({}, {});
+ try {
+ window.Image;
+ } finally {
+ // Restore our prototype so the test harnesses can deal with us
+ // We can't just assign to __proto__ because it lives on our proto chain
+ // and we messed that up.
+ var desc = Object.getOwnPropertyDescriptor(Object.prototype, "__proto__");
+ desc.set.call(HTMLElement.prototype, Element.prototype);
+ }
+</script>
+</head>
+
+<body></body>
+</html>
diff --git a/dom/bindings/crashtests/869038.html b/dom/bindings/crashtests/869038.html
new file mode 100644
index 000000000..dedb4dd4d
--- /dev/null
+++ b/dom/bindings/crashtests/869038.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<script>
+
+function boom()
+{
+ var frame = document.createElementNS("http://www.w3.org/1999/xhtml", "iframe");
+ document.body.appendChild(frame);
+ var frameDoc = frame.contentDocument;
+ frameDoc.contentEditable = "true";
+ document.body.removeChild(frame);
+ SpecialPowers.gc();
+ frameDoc.focus();
+}
+
+</script>
+</head>
+
+<body onload="boom();"></body>
+</html>
diff --git a/dom/bindings/crashtests/949940.html b/dom/bindings/crashtests/949940.html
new file mode 100644
index 000000000..7f20085fe
--- /dev/null
+++ b/dom/bindings/crashtests/949940.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<script>
+function boom()
+{
+ var frameWin = document.getElementById("f").contentWindow;
+ Object.create(frameWin).self;
+}
+</script>
+</head>
+<body onload="boom()">
+<iframe id="f" src="data:text/html,3"></iframe>
+</body>
+</html>
diff --git a/dom/bindings/crashtests/crashtests.list b/dom/bindings/crashtests/crashtests.list
new file mode 100644
index 000000000..3b517dd88
--- /dev/null
+++ b/dom/bindings/crashtests/crashtests.list
@@ -0,0 +1,12 @@
+skip load 769464.html # bug 823822 - assert often leaks into other tests
+load 822340-1.html
+load 822340-2.html
+load 832899.html
+load 860551.html
+load 860591.html
+load 862092.html
+load 862610.html
+load 869038.html
+load 949940.html
+load 1010658-1.html
+load 1010658-2.html
diff --git a/dom/bindings/docs/index.rst b/dom/bindings/docs/index.rst
new file mode 100644
index 000000000..f6fedbd16
--- /dev/null
+++ b/dom/bindings/docs/index.rst
@@ -0,0 +1,123 @@
+.. _webidl:
+
+======
+WebIDL
+======
+
+WebIDL describes interfaces web browsers are supposed to implement.
+
+The interaction between WebIDL and the build system is somewhat complex.
+This document will attempt to explain how it all works.
+
+Overview
+========
+
+``.webidl`` files throughout the tree define interfaces the browser
+implements. Since Gecko/Firefox is implemented in C++, there is a
+mechanism to convert these interfaces and associated metadata to
+C++ code. That's where the build system comes into play.
+
+All the code for interacting with ``.webidl`` files lives under
+``dom/bindings``. There is code in the build system to deal with
+WebIDLs explicitly.
+
+WebIDL source file flavors
+==========================
+
+Not all ``.webidl`` files are created equal! There are several flavors,
+each represented by a separate symbol from :ref:`mozbuild_symbols`.
+
+WEBIDL_FILES
+ Refers to regular/static ``.webidl`` files. Most WebIDL interfaces
+ are defined this way.
+
+GENERATED_EVENTS_WEBIDL_FILES
+ In addition to generating a binding, these ``.webidl`` files also
+ generate a source file implementing the event object in C++
+
+PREPROCESSED_WEBIDL_FILES
+ The ``.webidl`` files are generated by preprocessing an input file.
+ They otherwise behave like *WEBIDL_FILES*.
+
+TEST_WEBIDL_FILES
+ Like *WEBIDL_FILES* but the interfaces are for testing only and
+ aren't shipped with the browser.
+
+PREPROCESSED_TEST_WEBIDL_FILES
+ Like *TEST_WEBIDL_FILES* except the ``.webidl`` is obtained via
+ preprocessing, much like *PREPROCESSED_WEBIDL_FILES*.
+
+GENERATED_WEBIDL_FILES
+ The ``.webidl`` for these is obtained through an *external*
+ mechanism. Typically there are custom build rules for producing these
+ files.
+
+Producing C++ code
+==================
+
+The most complicated part about WebIDLs is the process by which
+``.webidl`` files are converted into C++.
+
+This process is handled by code in the :py:mod:`mozwebidlcodegen`
+package. :py:class:`mozwebidlcodegen.WebIDLCodegenManager` is
+specifically where you want to look for how code generation is
+performed. This includes complex dependency management.
+
+Requirements
+============
+
+This section aims to document the build and developer workflow requirements
+for WebIDL.
+
+Parser unit tests
+ There are parser tests provided by ``dom/bindings/parser/runtests.py``
+ that should run as part of ``make check``. There must be a mechanism
+ to run the tests in *human* mode so they output friendly error
+ messages.
+
+ The current mechanism for this is ``mach webidl-parser-test``.
+
+Mochitests
+ There are various mochitests under ``dom/bindings/test``. They should
+ be runnable through the standard mechanisms.
+
+Working with test interfaces
+ ``TestExampleGenBinding.cpp`` calls into methods from the
+ ``TestExampleInterface``, ``TestExampleProxyInterface``,
+ and ``TestExampleWorkerInterface`` interfaces.
+ These interfaces need to be generated as part of the build. These
+ interfaces should not be exported or packaged.
+
+ There is a ``compiletests`` make target in ``dom/bindings`` that
+ isn't part of the build that facilitates turnkey code generation
+ and test file compilation.
+
+Minimal rebuilds
+ Reprocessing every output for every change is expensive. So we don't
+ inconvenience people changing ``.webidl`` files, the build system
+ should only perform a minimal rebuild when sources change.
+
+ This logic is mostly all handled in
+ :py:class:`mozwebidlcodegen.WebIDLCodegenManager`. The unit tests for
+ that Python code should adequately test typical rebuild scenarios.
+
+ Bug 940469 tracks making the existing implementation better.
+
+Explicit method for performing codegen
+ There needs to be an explicit method for invoking code generation.
+ It needs to cover regular and test files.
+
+ This is implemented via ``make export`` in ``dom/bindings``.
+
+No-op binding generation should be fast
+ So developers touching ``.webidl`` files are not inconvenienced,
+ no-op binding generation should be fast. Watch out for the build system
+ processing large dependency files it doesn't need in order to perform
+ code generation.
+
+Ability to generate example files
+ *Any* interface can have example ``.h``/``.cpp`` files generated.
+ There must be a mechanism to facilitate this.
+
+ This is currently facilitated through ``mach webidl-example``. e.g.
+ ``mach webidl-example HTMLStyleElement``.
diff --git a/dom/bindings/mach_commands.py b/dom/bindings/mach_commands.py
new file mode 100644
index 000000000..eec68c907
--- /dev/null
+++ b/dom/bindings/mach_commands.py
@@ -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/.
+
+from __future__ import absolute_import, unicode_literals
+
+import os
+import sys
+
+from mach.decorators import (
+ CommandArgument,
+ CommandProvider,
+ Command,
+)
+
+from mozbuild.base import MachCommandBase
+
+def get_test_parser():
+ import runtests
+ return runtests.get_parser
+
+@CommandProvider
+class WebIDLProvider(MachCommandBase):
+ @Command('webidl-example', category='misc',
+ description='Generate example files for a WebIDL interface.')
+ @CommandArgument('interface', nargs='+',
+ help='Interface(s) whose examples to generate.')
+ def webidl_example(self, interface):
+ from mozwebidlcodegen import BuildSystemWebIDL
+
+ manager = self._spawn(BuildSystemWebIDL).manager
+ for i in interface:
+ manager.generate_example_files(i)
+
+ @Command('webidl-parser-test', category='testing', parser=get_test_parser,
+ description='Run WebIDL tests (Interface Browser parser).')
+ def webidl_test(self, **kwargs):
+ sys.path.insert(0, os.path.join(self.topsrcdir, 'other-licenses',
+ 'ply'))
+
+ # Make sure we drop our cached grammar bits in the objdir, not
+ # wherever we happen to be running from.
+ os.chdir(self.topobjdir)
+
+ if kwargs["verbose"] is None:
+ kwargs["verbose"] = False
+
+ # Now we're going to create the cached grammar file in the
+ # objdir. But we're going to try loading it as a python
+ # module, so we need to make sure the objdir is in our search
+ # path.
+ sys.path.insert(0, self.topobjdir)
+
+ import runtests
+ return runtests.run_tests(kwargs["tests"], verbose=kwargs["verbose"])
diff --git a/dom/bindings/moz.build b/dom/bindings/moz.build
new file mode 100644
index 000000000..fadaac69b
--- /dev/null
+++ b/dom/bindings/moz.build
@@ -0,0 +1,161 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+TEST_DIRS += ['test']
+
+EXPORTS.ipc += [
+ 'ErrorIPCUtils.h',
+]
+
+EXPORTS.mozilla += [
+ 'ErrorResult.h',
+ 'RootedOwningNonNull.h',
+ 'RootedRefPtr.h',
+]
+
+EXPORTS.mozilla.dom += [
+ 'AtomList.h',
+ 'BindingDeclarations.h',
+ 'BindingUtils.h',
+ 'CallbackFunction.h',
+ 'CallbackInterface.h',
+ 'CallbackObject.h',
+ 'Date.h',
+ 'DOMJSClass.h',
+ 'DOMJSProxyHandler.h',
+ 'DOMString.h',
+ 'Errors.msg',
+ 'Exceptions.h',
+ 'FakeString.h',
+ 'IterableIterator.h',
+ 'JSSlots.h',
+ 'MozMap.h',
+ 'NonRefcountedDOMObject.h',
+ 'Nullable.h',
+ 'PrimitiveConversions.h',
+ 'RootedDictionary.h',
+ 'SimpleGlobalObject.h',
+ 'StructuredClone.h',
+ 'ToJSValue.h',
+ 'TypedArray.h',
+ 'UnionMember.h',
+ 'WebIDLGlobalNameHash.h',
+ 'XrayExpandoClass.h',
+]
+
+# Generated bindings reference *Binding.h, not mozilla/dom/*Binding.h. And,
+# since we generate exported bindings directly to $(DIST)/include, we need
+# to add that path to the search list.
+#
+# Ideally, binding generation uses the prefixed header file names.
+# Bug 932082 tracks.
+LOCAL_INCLUDES += [
+ '!/dist/include/mozilla/dom',
+]
+
+LOCAL_INCLUDES += [
+ '/dom/base',
+ '/dom/battery',
+ '/dom/canvas',
+ '/dom/geolocation',
+ '/dom/html',
+ '/dom/indexedDB',
+ '/dom/media/webaudio',
+ '/dom/media/webspeech/recognition',
+ '/dom/svg',
+ '/dom/workers',
+ '/dom/xbl',
+ '/dom/xml',
+ '/dom/xslt/base',
+ '/dom/xslt/xpath',
+ '/dom/xul',
+ '/js/xpconnect/src',
+ '/js/xpconnect/wrappers',
+ '/layout/generic',
+ '/layout/style',
+ '/layout/xul/tree',
+ '/media/mtransport',
+ '/media/webrtc/',
+ '/media/webrtc/signaling/src/common/time_profiling',
+ '/media/webrtc/signaling/src/peerconnection',
+]
+
+UNIFIED_SOURCES += [
+ 'BindingUtils.cpp',
+ 'CallbackInterface.cpp',
+ 'CallbackObject.cpp',
+ 'Date.cpp',
+ 'DOMJSProxyHandler.cpp',
+ 'Exceptions.cpp',
+ 'IterableIterator.cpp',
+ 'SimpleGlobalObject.cpp',
+ 'ToJSValue.cpp',
+ 'WebIDLGlobalNameHash.cpp',
+]
+
+SOURCES += [
+ 'StructuredClone.cpp',
+]
+
+# Tests for maplike and setlike require bindings to be built, which means they
+# must be included in libxul. This breaks the "no test classes are exported"
+# rule stated in the test/ directory, but it's the only way this will work.
+# Test classes are only built in debug mode, and all tests requiring use of
+# them are only run in debug mode.
+if CONFIG['MOZ_DEBUG']:
+ EXPORTS.mozilla.dom += [
+ "test/TestFunctions.h",
+ "test/TestInterfaceIterableDouble.h",
+ "test/TestInterfaceIterableDoubleUnion.h",
+ "test/TestInterfaceIterableSingle.h",
+ "test/TestInterfaceMaplike.h",
+ "test/TestInterfaceMaplikeObject.h",
+ "test/TestInterfaceSetlike.h",
+ "test/TestInterfaceSetlikeNode.h"
+ ]
+ UNIFIED_SOURCES += [
+ "test/TestFunctions.cpp",
+ "test/TestInterfaceIterableDouble.cpp",
+ "test/TestInterfaceIterableDoubleUnion.cpp",
+ "test/TestInterfaceIterableSingle.cpp",
+ "test/TestInterfaceMaplike.cpp",
+ "test/TestInterfaceMaplikeObject.cpp",
+ "test/TestInterfaceSetlike.cpp",
+ "test/TestInterfaceSetlikeNode.cpp",
+ ]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+if CONFIG['MOZ_AUDIO_CHANNEL_MANAGER']:
+ LOCAL_INCLUDES += [
+ '/dom/system/gonk',
+ ]
+
+FINAL_LIBRARY = 'xul'
+
+SPHINX_TREES['webidl'] = 'docs'
+SPHINX_PYTHON_PACKAGE_DIRS += ['mozwebidlcodegen']
+
+if CONFIG['MOZ_BUILD_APP'] in ['browser', 'mobile/android', 'xulrunner']:
+ # This is needed for Window.webidl
+ DEFINES['HAVE_SIDEBAR'] = True
+
+
+PYTHON_UNIT_TESTS += [
+ 'mozwebidlcodegen/test/test_mozwebidlcodegen.py',
+]
+
+if CONFIG['GNU_CXX']:
+ CXXFLAGS += ['-Wno-error=shadow']
+
+if CONFIG['COMPILE_ENVIRONMENT']:
+ GENERATED_FILES += ['CSS2Properties.webidl']
+ css_props = GENERATED_FILES['CSS2Properties.webidl']
+ css_props.script = 'GenerateCSS2PropertiesWebIDL.py:generate'
+ css_props.inputs = [
+ '/dom/webidl/CSS2Properties.webidl.in',
+ '/layout/style/PythonCSSProps.h',
+ ]
diff --git a/dom/bindings/mozwebidlcodegen/__init__.py b/dom/bindings/mozwebidlcodegen/__init__.py
new file mode 100644
index 000000000..b6e351e52
--- /dev/null
+++ b/dom/bindings/mozwebidlcodegen/__init__.py
@@ -0,0 +1,583 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+# This module contains code for managing WebIDL files and bindings for
+# the build system.
+
+from __future__ import unicode_literals
+
+import errno
+import hashlib
+import json
+import logging
+import os
+
+from copy import deepcopy
+
+from mach.mixin.logging import LoggingMixin
+
+from mozbuild.base import MozbuildObject
+from mozbuild.makeutil import Makefile
+from mozbuild.pythonutil import iter_modules_in_path
+from mozbuild.util import FileAvoidWrite
+
+import mozpack.path as mozpath
+
+# There are various imports in this file in functions to avoid adding
+# dependencies to config.status. See bug 949875.
+
+
+class BuildResult(object):
+ """Represents the result of processing WebIDL files.
+
+ This holds a summary of output file generation during code generation.
+ """
+
+ def __init__(self):
+ # The .webidl files that had their outputs regenerated.
+ self.inputs = set()
+
+ # The output files that were created.
+ self.created = set()
+
+ # The output files that changed.
+ self.updated = set()
+
+ # The output files that didn't change.
+ self.unchanged = set()
+
+
+class WebIDLCodegenManagerState(dict):
+ """Holds state for the WebIDL code generation manager.
+
+ State is currently just an extended dict. The internal implementation of
+ state should be considered a black box to everyone except
+ WebIDLCodegenManager. But we'll still document it.
+
+ Fields:
+
+ version
+ The integer version of the format. This is to detect incompatible
+ changes between state. It should be bumped whenever the format
+ changes or semantics change.
+
+ webidls
+ A dictionary holding information about every known WebIDL input.
+ Keys are the basenames of input WebIDL files. Values are dicts of
+ metadata. Keys in those dicts are:
+
+ * filename - The full path to the input filename.
+ * inputs - A set of full paths to other webidl files this webidl
+ depends on.
+ * outputs - Set of full output paths that are created/derived from
+ this file.
+ * sha1 - The hexidecimal SHA-1 of the input filename from the last
+ processing time.
+
+ global_inputs
+ A dictionary defining files that influence all processing. Keys
+ are full filenames. Values are hexidecimal SHA-1 from the last
+ processing time.
+ """
+
+ VERSION = 1
+
+ def __init__(self, fh=None):
+ self['version'] = self.VERSION
+ self['webidls'] = {}
+ self['global_depends'] = {}
+
+ if not fh:
+ return
+
+ state = json.load(fh)
+ if state['version'] != self.VERSION:
+ raise Exception('Unknown state version: %s' % state['version'])
+
+ self['version'] = state['version']
+ self['global_depends'] = state['global_depends']
+
+ for k, v in state['webidls'].items():
+ self['webidls'][k] = v
+
+ # Sets are converted to lists for serialization because JSON
+ # doesn't support sets.
+ self['webidls'][k]['inputs'] = set(v['inputs'])
+ self['webidls'][k]['outputs'] = set(v['outputs'])
+
+ def dump(self, fh):
+ """Dump serialized state to a file handle."""
+ normalized = deepcopy(self)
+
+ for k, v in self['webidls'].items():
+ # Convert sets to lists because JSON doesn't support sets.
+ normalized['webidls'][k]['outputs'] = sorted(v['outputs'])
+ normalized['webidls'][k]['inputs'] = sorted(v['inputs'])
+
+ json.dump(normalized, fh, sort_keys=True)
+
+
+class WebIDLCodegenManager(LoggingMixin):
+ """Manages all code generation around WebIDL.
+
+ To facilitate testing, this object is meant to be generic and reusable.
+ Paths, etc should be parameters and not hardcoded.
+ """
+
+ # Global parser derived declaration files.
+ GLOBAL_DECLARE_FILES = {
+ 'GeneratedAtomList.h',
+ 'GeneratedEventList.h',
+ 'PrototypeList.h',
+ 'RegisterBindings.h',
+ 'RegisterWorkerBindings.h',
+ 'RegisterWorkerDebuggerBindings.h',
+ 'RegisterWorkletBindings.h',
+ 'ResolveSystemBinding.h',
+ 'UnionConversions.h',
+ 'UnionTypes.h',
+ }
+
+ # Global parser derived definition files.
+ GLOBAL_DEFINE_FILES = {
+ 'RegisterBindings.cpp',
+ 'RegisterWorkerBindings.cpp',
+ 'RegisterWorkerDebuggerBindings.cpp',
+ 'RegisterWorkletBindings.cpp',
+ 'ResolveSystemBinding.cpp',
+ 'UnionTypes.cpp',
+ 'PrototypeList.cpp',
+ }
+
+ def __init__(self, config_path, inputs, exported_header_dir,
+ codegen_dir, state_path, cache_dir=None, make_deps_path=None,
+ make_deps_target=None):
+ """Create an instance that manages WebIDLs in the build system.
+
+ config_path refers to a WebIDL config file (e.g. Bindings.conf).
+ inputs is a 4-tuple describing the input .webidl files and how to
+ process them. Members are:
+ (set(.webidl files), set(basenames of exported files),
+ set(basenames of generated events files),
+ set(example interface names))
+
+ exported_header_dir and codegen_dir are directories where generated
+ files will be written to.
+ state_path is the path to a file that will receive JSON state from our
+ actions.
+ make_deps_path is the path to a make dependency file that we can
+ optionally write.
+ make_deps_target is the target that receives the make dependencies. It
+ must be defined if using make_deps_path.
+ """
+ self.populate_logger()
+
+ input_paths, exported_stems, generated_events_stems, example_interfaces = inputs
+
+ self._config_path = config_path
+ self._input_paths = set(input_paths)
+ self._exported_stems = set(exported_stems)
+ self._generated_events_stems = set(generated_events_stems)
+ self._generated_events_stems_as_array = generated_events_stems
+ self._example_interfaces = set(example_interfaces)
+ self._exported_header_dir = exported_header_dir
+ self._codegen_dir = codegen_dir
+ self._state_path = state_path
+ self._cache_dir = cache_dir
+ self._make_deps_path = make_deps_path
+ self._make_deps_target = make_deps_target
+
+ if ((make_deps_path and not make_deps_target) or
+ (not make_deps_path and make_deps_target)):
+ raise Exception('Must define both make_deps_path and make_deps_target '
+ 'if one is defined.')
+
+ self._parser_results = None
+ self._config = None
+ self._state = WebIDLCodegenManagerState()
+
+ if os.path.exists(state_path):
+ with open(state_path, 'rb') as fh:
+ try:
+ self._state = WebIDLCodegenManagerState(fh=fh)
+ except Exception as e:
+ self.log(logging.WARN, 'webidl_bad_state', {'msg': str(e)},
+ 'Bad WebIDL state: {msg}')
+
+ @property
+ def config(self):
+ if not self._config:
+ self._parse_webidl()
+
+ return self._config
+
+ def generate_build_files(self):
+ """Generate files required for the build.
+
+ This function is in charge of generating all the .h/.cpp files derived
+ from input .webidl files. Please note that there are build actions
+ required to produce .webidl files and these build actions are
+ explicitly not captured here: this function assumes all .webidl files
+ are present and up to date.
+
+ This routine is called as part of the build to ensure files that need
+ to exist are present and up to date. This routine may not be called if
+ the build dependencies (generated as a result of calling this the first
+ time) say everything is up to date.
+
+ Because reprocessing outputs for every .webidl on every invocation
+ is expensive, we only regenerate the minimal set of files on every
+ invocation. The rules for deciding what needs done are roughly as
+ follows:
+
+ 1. If any .webidl changes, reparse all .webidl files and regenerate
+ the global derived files. Only regenerate output files (.h/.cpp)
+ impacted by the modified .webidl files.
+ 2. If an non-.webidl dependency (Python files, config file) changes,
+ assume everything is out of date and regenerate the world. This
+ is because changes in those could globally impact every output
+ file.
+ 3. If an output file is missing, ensure it is present by performing
+ necessary regeneration.
+ """
+ # Despite #1 above, we assume the build system is smart enough to not
+ # invoke us if nothing has changed. Therefore, any invocation means
+ # something has changed. And, if anything has changed, we need to
+ # parse the WebIDL.
+ self._parse_webidl()
+
+ result = BuildResult()
+
+ # If we parse, we always update globals - they are cheap and it is
+ # easier that way.
+ created, updated, unchanged = self._write_global_derived()
+ result.created |= created
+ result.updated |= updated
+ result.unchanged |= unchanged
+
+ # If any of the extra dependencies changed, regenerate the world.
+ global_changed, global_hashes = self._global_dependencies_changed()
+ if global_changed:
+ # Make a copy because we may modify.
+ changed_inputs = set(self._input_paths)
+ else:
+ changed_inputs = self._compute_changed_inputs()
+
+ self._state['global_depends'] = global_hashes
+
+ # Generate bindings from .webidl files.
+ for filename in sorted(changed_inputs):
+ basename = mozpath.basename(filename)
+ result.inputs.add(filename)
+ written, deps = self._generate_build_files_for_webidl(filename)
+ result.created |= written[0]
+ result.updated |= written[1]
+ result.unchanged |= written[2]
+
+ self._state['webidls'][basename] = dict(
+ filename=filename,
+ outputs=written[0] | written[1] | written[2],
+ inputs=set(deps),
+ sha1=self._input_hashes[filename],
+ )
+
+ # Process some special interfaces required for testing.
+ for interface in self._example_interfaces:
+ written = self.generate_example_files(interface)
+ result.created |= written[0]
+ result.updated |= written[1]
+ result.unchanged |= written[2]
+
+ # Generate a make dependency file.
+ if self._make_deps_path:
+ mk = Makefile()
+ codegen_rule = mk.create_rule([self._make_deps_target])
+ codegen_rule.add_dependencies(global_hashes.keys())
+ codegen_rule.add_dependencies(self._input_paths)
+
+ with FileAvoidWrite(self._make_deps_path) as fh:
+ mk.dump(fh)
+
+ self._save_state()
+
+ return result
+
+ def generate_example_files(self, interface):
+ """Generates example files for a given interface."""
+ from Codegen import CGExampleRoot
+
+ root = CGExampleRoot(self.config, interface)
+
+ example_paths = self._example_paths(interface)
+ for path in example_paths:
+ print "Generating %s" % path
+
+ return self._maybe_write_codegen(root, *example_paths)
+
+ def _parse_webidl(self):
+ import WebIDL
+ from Configuration import Configuration
+
+ self.log(logging.INFO, 'webidl_parse',
+ {'count': len(self._input_paths)},
+ 'Parsing {count} WebIDL files.')
+
+ hashes = {}
+ parser = WebIDL.Parser(self._cache_dir)
+
+ for path in sorted(self._input_paths):
+ with open(path, 'rb') as fh:
+ data = fh.read()
+ hashes[path] = hashlib.sha1(data).hexdigest()
+ parser.parse(data, path)
+
+ self._parser_results = parser.finish()
+ self._config = Configuration(self._config_path, self._parser_results,
+ self._generated_events_stems_as_array)
+ self._input_hashes = hashes
+
+ def _write_global_derived(self):
+ from Codegen import GlobalGenRoots
+
+ things = [('declare', f) for f in self.GLOBAL_DECLARE_FILES]
+ things.extend(('define', f) for f in self.GLOBAL_DEFINE_FILES)
+
+ result = (set(), set(), set())
+
+ for what, filename in things:
+ stem = mozpath.splitext(filename)[0]
+ root = getattr(GlobalGenRoots, stem)(self._config)
+
+ if what == 'declare':
+ code = root.declare()
+ output_root = self._exported_header_dir
+ elif what == 'define':
+ code = root.define()
+ output_root = self._codegen_dir
+ else:
+ raise Exception('Unknown global gen type: %s' % what)
+
+ output_path = mozpath.join(output_root, filename)
+ self._maybe_write_file(output_path, code, result)
+
+ return result
+
+ def _compute_changed_inputs(self):
+ """Compute the set of input files that need to be regenerated."""
+ changed_inputs = set()
+ expected_outputs = self.expected_build_output_files()
+
+ # Look for missing output files.
+ if any(not os.path.exists(f) for f in expected_outputs):
+ # FUTURE Bug 940469 Only regenerate minimum set.
+ changed_inputs |= self._input_paths
+
+ # That's it for examining output files. We /could/ examine SHA-1's of
+ # output files from a previous run to detect modifications. But that's
+ # a lot of extra work and most build systems don't do that anyway.
+
+ # Now we move on to the input files.
+ old_hashes = {v['filename']: v['sha1']
+ for v in self._state['webidls'].values()}
+
+ old_filenames = set(old_hashes.keys())
+ new_filenames = self._input_paths
+
+ # If an old file has disappeared or a new file has arrived, mark
+ # it.
+ changed_inputs |= old_filenames ^ new_filenames
+
+ # For the files in common between runs, compare content. If the file
+ # has changed, mark it. We don't need to perform mtime comparisons
+ # because content is a stronger validator.
+ for filename in old_filenames & new_filenames:
+ if old_hashes[filename] != self._input_hashes[filename]:
+ changed_inputs.add(filename)
+
+ # We've now populated the base set of inputs that have changed.
+
+ # Inherit dependencies from previous run. The full set of dependencies
+ # is associated with each record, so we don't need to perform any fancy
+ # graph traversal.
+ for v in self._state['webidls'].values():
+ if any(dep for dep in v['inputs'] if dep in changed_inputs):
+ changed_inputs.add(v['filename'])
+
+ # Only use paths that are known to our current state.
+ # This filters out files that were deleted or changed type (e.g. from
+ # static to preprocessed).
+ return changed_inputs & self._input_paths
+
+ def _binding_info(self, p):
+ """Compute binding metadata for an input path.
+
+ Returns a tuple of:
+
+ (stem, binding_stem, is_event, output_files)
+
+ output_files is itself a tuple. The first two items are the binding
+ header and C++ paths, respectively. The 2nd pair are the event header
+ and C++ paths or None if this isn't an event binding.
+ """
+ basename = mozpath.basename(p)
+ stem = mozpath.splitext(basename)[0]
+ binding_stem = '%sBinding' % stem
+
+ if stem in self._exported_stems:
+ header_dir = self._exported_header_dir
+ else:
+ header_dir = self._codegen_dir
+
+ is_event = stem in self._generated_events_stems
+
+ files = (
+ mozpath.join(header_dir, '%s.h' % binding_stem),
+ mozpath.join(self._codegen_dir, '%s.cpp' % binding_stem),
+ mozpath.join(header_dir, '%s.h' % stem) if is_event else None,
+ mozpath.join(self._codegen_dir, '%s.cpp' % stem) if is_event else None,
+ )
+
+ return stem, binding_stem, is_event, header_dir, files
+
+ def _example_paths(self, interface):
+ return (
+ mozpath.join(self._codegen_dir, '%s-example.h' % interface),
+ mozpath.join(self._codegen_dir, '%s-example.cpp' % interface))
+
+ def expected_build_output_files(self):
+ """Obtain the set of files generate_build_files() should write."""
+ paths = set()
+
+ # Account for global generation.
+ for p in self.GLOBAL_DECLARE_FILES:
+ paths.add(mozpath.join(self._exported_header_dir, p))
+ for p in self.GLOBAL_DEFINE_FILES:
+ paths.add(mozpath.join(self._codegen_dir, p))
+
+ for p in self._input_paths:
+ stem, binding_stem, is_event, header_dir, files = self._binding_info(p)
+ paths |= {f for f in files if f}
+
+ for interface in self._example_interfaces:
+ for p in self._example_paths(interface):
+ paths.add(p)
+
+ return paths
+
+ def _generate_build_files_for_webidl(self, filename):
+ from Codegen import (
+ CGBindingRoot,
+ CGEventRoot,
+ )
+
+ self.log(logging.INFO, 'webidl_generate_build_for_input',
+ {'filename': filename},
+ 'Generating WebIDL files derived from {filename}')
+
+ stem, binding_stem, is_event, header_dir, files = self._binding_info(filename)
+ root = CGBindingRoot(self._config, binding_stem, filename)
+
+ result = self._maybe_write_codegen(root, files[0], files[1])
+
+ if is_event:
+ generated_event = CGEventRoot(self._config, stem)
+ result = self._maybe_write_codegen(generated_event, files[2],
+ files[3], result)
+
+ return result, root.deps()
+
+ def _global_dependencies_changed(self):
+ """Determine whether the global dependencies have changed."""
+ current_files = set(iter_modules_in_path(mozpath.dirname(__file__)))
+
+ # We need to catch other .py files from /dom/bindings. We assume these
+ # are in the same directory as the config file.
+ current_files |= set(iter_modules_in_path(mozpath.dirname(self._config_path)))
+
+ current_files.add(self._config_path)
+
+ current_hashes = {}
+ for f in current_files:
+ # This will fail if the file doesn't exist. If a current global
+ # dependency doesn't exist, something else is wrong.
+ with open(f, 'rb') as fh:
+ current_hashes[f] = hashlib.sha1(fh.read()).hexdigest()
+
+ # The set of files has changed.
+ if current_files ^ set(self._state['global_depends'].keys()):
+ return True, current_hashes
+
+ # Compare hashes.
+ for f, sha1 in current_hashes.items():
+ if sha1 != self._state['global_depends'][f]:
+ return True, current_hashes
+
+ return False, current_hashes
+
+ def _save_state(self):
+ with open(self._state_path, 'wb') as fh:
+ self._state.dump(fh)
+
+ def _maybe_write_codegen(self, obj, declare_path, define_path, result=None):
+ assert declare_path and define_path
+ if not result:
+ result = (set(), set(), set())
+
+ self._maybe_write_file(declare_path, obj.declare(), result)
+ self._maybe_write_file(define_path, obj.define(), result)
+
+ return result
+
+ def _maybe_write_file(self, path, content, result):
+ fh = FileAvoidWrite(path)
+ fh.write(content)
+ existed, updated = fh.close()
+
+ if not existed:
+ result[0].add(path)
+ elif updated:
+ result[1].add(path)
+ else:
+ result[2].add(path)
+
+
+def create_build_system_manager(topsrcdir, topobjdir, dist_dir):
+ """Create a WebIDLCodegenManager for use by the build system."""
+ src_dir = os.path.join(topsrcdir, 'dom', 'bindings')
+ obj_dir = os.path.join(topobjdir, 'dom', 'bindings')
+
+ with open(os.path.join(obj_dir, 'file-lists.json'), 'rb') as fh:
+ files = json.load(fh)
+
+ inputs = (files['webidls'], files['exported_stems'],
+ files['generated_events_stems'], files['example_interfaces'])
+
+ cache_dir = os.path.join(obj_dir, '_cache')
+ try:
+ os.makedirs(cache_dir)
+ except OSError as e:
+ if e.errno != errno.EEXIST:
+ raise
+
+ return WebIDLCodegenManager(
+ os.path.join(src_dir, 'Bindings.conf'),
+ inputs,
+ os.path.join(dist_dir, 'include', 'mozilla', 'dom'),
+ obj_dir,
+ os.path.join(obj_dir, 'codegen.json'),
+ cache_dir=cache_dir,
+ # The make rules include a codegen.pp file containing dependencies.
+ make_deps_path=os.path.join(obj_dir, 'codegen.pp'),
+ make_deps_target='codegen.pp',
+ )
+
+
+class BuildSystemWebIDL(MozbuildObject):
+ @property
+ def manager(self):
+ if not hasattr(self, '_webidl_manager'):
+ self._webidl_manager = create_build_system_manager(
+ self.topsrcdir, self.topobjdir, self.distdir)
+
+ return self._webidl_manager
diff --git a/dom/bindings/mozwebidlcodegen/test/Child.webidl b/dom/bindings/mozwebidlcodegen/test/Child.webidl
new file mode 100644
index 000000000..f7d0a76c9
--- /dev/null
+++ b/dom/bindings/mozwebidlcodegen/test/Child.webidl
@@ -0,0 +1,3 @@
+interface Child : Parent {
+ void ChildBaz();
+};
diff --git a/dom/bindings/mozwebidlcodegen/test/ExampleBinding.webidl b/dom/bindings/mozwebidlcodegen/test/ExampleBinding.webidl
new file mode 100644
index 000000000..34794993f
--- /dev/null
+++ b/dom/bindings/mozwebidlcodegen/test/ExampleBinding.webidl
@@ -0,0 +1,3 @@
+/* These interfaces are hard-coded and need to be defined. */
+interface TestExampleInterface {};
+interface TestExampleProxyInterface {};
diff --git a/dom/bindings/mozwebidlcodegen/test/Parent.webidl b/dom/bindings/mozwebidlcodegen/test/Parent.webidl
new file mode 100644
index 000000000..423f364ae
--- /dev/null
+++ b/dom/bindings/mozwebidlcodegen/test/Parent.webidl
@@ -0,0 +1,3 @@
+interface Parent {
+ void MethodFoo();
+};
diff --git a/dom/bindings/mozwebidlcodegen/test/TestEvent.webidl b/dom/bindings/mozwebidlcodegen/test/TestEvent.webidl
new file mode 100644
index 000000000..db0856291
--- /dev/null
+++ b/dom/bindings/mozwebidlcodegen/test/TestEvent.webidl
@@ -0,0 +1,13 @@
+interface EventTarget {
+ void addEventListener();
+};
+
+interface Event {};
+
+callback EventHandlerNonNull = any (Event event);
+typedef EventHandlerNonNull? EventHandler;
+
+[NoInterfaceObject]
+interface TestEvent : EventTarget {
+ attribute EventHandler onfoo;
+};
diff --git a/dom/bindings/mozwebidlcodegen/test/test_mozwebidlcodegen.py b/dom/bindings/mozwebidlcodegen/test/test_mozwebidlcodegen.py
new file mode 100644
index 000000000..ce3315e88
--- /dev/null
+++ b/dom/bindings/mozwebidlcodegen/test/test_mozwebidlcodegen.py
@@ -0,0 +1,298 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.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 __future__ import unicode_literals
+
+import imp
+import json
+import os
+import shutil
+import sys
+import tempfile
+import unittest
+
+import mozpack.path as mozpath
+
+from mozwebidlcodegen import (
+ WebIDLCodegenManager,
+ WebIDLCodegenManagerState,
+)
+
+from mozfile import NamedTemporaryFile
+
+from mozunit import (
+ MockedOpen,
+ main,
+)
+
+
+OUR_DIR = mozpath.abspath(mozpath.dirname(__file__))
+TOPSRCDIR = mozpath.normpath(mozpath.join(OUR_DIR, '..', '..', '..', '..'))
+
+
+class TestWebIDLCodegenManager(unittest.TestCase):
+ TEST_STEMS = {
+ 'Child',
+ 'Parent',
+ 'ExampleBinding',
+ 'TestEvent',
+ }
+
+ @property
+ def _static_input_paths(self):
+ s = {mozpath.join(OUR_DIR, p) for p in os.listdir(OUR_DIR)
+ if p.endswith('.webidl')}
+
+ return s
+
+ @property
+ def _config_path(self):
+ config = mozpath.join(TOPSRCDIR, 'dom', 'bindings', 'Bindings.conf')
+ self.assertTrue(os.path.exists(config))
+
+ return config
+
+ def _get_manager_args(self):
+ tmp = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, tmp)
+
+ cache_dir = mozpath.join(tmp, 'cache')
+ os.mkdir(cache_dir)
+
+ ip = self._static_input_paths
+
+ inputs = (
+ ip,
+ {mozpath.splitext(mozpath.basename(p))[0] for p in ip},
+ set(),
+ set(),
+ )
+
+ return dict(
+ config_path=self._config_path,
+ inputs=inputs,
+ exported_header_dir=mozpath.join(tmp, 'exports'),
+ codegen_dir=mozpath.join(tmp, 'codegen'),
+ state_path=mozpath.join(tmp, 'state.json'),
+ make_deps_path=mozpath.join(tmp, 'codegen.pp'),
+ make_deps_target='codegen.pp',
+ cache_dir=cache_dir,
+ )
+
+ def _get_manager(self):
+ return WebIDLCodegenManager(**self._get_manager_args())
+
+ def test_unknown_state_version(self):
+ """Loading a state file with a too new version resets state."""
+ args = self._get_manager_args()
+
+ p = args['state_path']
+
+ with open(p, 'wb') as fh:
+ json.dump({
+ 'version': WebIDLCodegenManagerState.VERSION + 1,
+ 'foobar': '1',
+ }, fh)
+
+ manager = WebIDLCodegenManager(**args)
+
+ self.assertEqual(manager._state['version'],
+ WebIDLCodegenManagerState.VERSION)
+ self.assertNotIn('foobar', manager._state)
+
+ def test_generate_build_files(self):
+ """generate_build_files() does the right thing from empty."""
+ manager = self._get_manager()
+ result = manager.generate_build_files()
+ self.assertEqual(len(result.inputs), 4)
+
+ output = manager.expected_build_output_files()
+ self.assertEqual(result.created, output)
+ self.assertEqual(len(result.updated), 0)
+ self.assertEqual(len(result.unchanged), 0)
+
+ for f in output:
+ self.assertTrue(os.path.isfile(f))
+
+ for f in manager.GLOBAL_DECLARE_FILES:
+ self.assertIn(mozpath.join(manager._exported_header_dir, f), output)
+
+ for f in manager.GLOBAL_DEFINE_FILES:
+ self.assertIn(mozpath.join(manager._codegen_dir, f), output)
+
+ for s in self.TEST_STEMS:
+ self.assertTrue(os.path.isfile(mozpath.join(
+ manager._exported_header_dir, '%sBinding.h' % s)))
+ self.assertTrue(os.path.isfile(mozpath.join(
+ manager._codegen_dir, '%sBinding.cpp' % s)))
+
+ self.assertTrue(os.path.isfile(manager._state_path))
+
+ with open(manager._state_path, 'rb') as fh:
+ state = json.load(fh)
+ self.assertEqual(state['version'], 1)
+ self.assertIn('webidls', state)
+
+ child = state['webidls']['Child.webidl']
+ self.assertEqual(len(child['inputs']), 2)
+ self.assertEqual(len(child['outputs']), 2)
+ self.assertEqual(child['sha1'], 'c41527cad3bc161fa6e7909e48fa11f9eca0468b')
+
+ def test_generate_build_files_load_state(self):
+ """State should be equivalent when instantiating a new instance."""
+ args = self._get_manager_args()
+ m1 = WebIDLCodegenManager(**args)
+ self.assertEqual(len(m1._state['webidls']), 0)
+ m1.generate_build_files()
+
+ m2 = WebIDLCodegenManager(**args)
+ self.assertGreater(len(m2._state['webidls']), 2)
+ self.assertEqual(m1._state, m2._state)
+
+ def test_no_change_no_writes(self):
+ """If nothing changes, no files should be updated."""
+ args = self._get_manager_args()
+ m1 = WebIDLCodegenManager(**args)
+ m1.generate_build_files()
+
+ m2 = WebIDLCodegenManager(**args)
+ result = m2.generate_build_files()
+
+ self.assertEqual(len(result.inputs), 0)
+ self.assertEqual(len(result.created), 0)
+ self.assertEqual(len(result.updated), 0)
+
+ def test_output_file_regenerated(self):
+ """If an output file disappears, it is regenerated."""
+ args = self._get_manager_args()
+ m1 = WebIDLCodegenManager(**args)
+ m1.generate_build_files()
+
+ rm_count = 0
+ for p in m1._state['webidls']['Child.webidl']['outputs']:
+ rm_count += 1
+ os.unlink(p)
+
+ for p in m1.GLOBAL_DECLARE_FILES:
+ rm_count += 1
+ os.unlink(mozpath.join(m1._exported_header_dir, p))
+
+ m2 = WebIDLCodegenManager(**args)
+ result = m2.generate_build_files()
+ self.assertEqual(len(result.created), rm_count)
+
+ def test_only_rebuild_self(self):
+ """If an input file changes, only rebuild that one file."""
+ args = self._get_manager_args()
+ m1 = WebIDLCodegenManager(**args)
+ m1.generate_build_files()
+
+ child_path = None
+ for p in m1._input_paths:
+ if p.endswith('Child.webidl'):
+ child_path = p
+ break
+
+ self.assertIsNotNone(child_path)
+ child_content = open(child_path, 'rb').read()
+
+ with MockedOpen({child_path: child_content + '\n/* */'}):
+ m2 = WebIDLCodegenManager(**args)
+ result = m2.generate_build_files()
+ self.assertEqual(result.inputs, set([child_path]))
+ self.assertEqual(len(result.updated), 0)
+ self.assertEqual(len(result.created), 0)
+
+ def test_rebuild_dependencies(self):
+ """Ensure an input file used by others results in others rebuilding."""
+ args = self._get_manager_args()
+ m1 = WebIDLCodegenManager(**args)
+ m1.generate_build_files()
+
+ parent_path = None
+ child_path = None
+ for p in m1._input_paths:
+ if p.endswith('Parent.webidl'):
+ parent_path = p
+ elif p.endswith('Child.webidl'):
+ child_path = p
+
+ self.assertIsNotNone(parent_path)
+ parent_content = open(parent_path, 'rb').read()
+
+ with MockedOpen({parent_path: parent_content + '\n/* */'}):
+ m2 = WebIDLCodegenManager(**args)
+ result = m2.generate_build_files()
+ self.assertEqual(result.inputs, {child_path, parent_path})
+ self.assertEqual(len(result.updated), 0)
+ self.assertEqual(len(result.created), 0)
+
+ def test_python_change_regenerate_everything(self):
+ """If a Python file changes, we should attempt to rebuild everything."""
+
+ # We don't want to mutate files in the source directory because we want
+ # to be able to build from a read-only filesystem. So, we install a
+ # dummy module and rewrite the metadata to say it comes from the source
+ # directory.
+ #
+ # Hacking imp to accept a MockedFile doesn't appear possible. So for
+ # the first iteration we read from a temp file. The second iteration
+ # doesn't need to import, so we are fine with a mocked file.
+ fake_path = mozpath.join(OUR_DIR, 'fakemodule.py')
+ with NamedTemporaryFile('wt') as fh:
+ fh.write('# Original content')
+ fh.flush()
+ mod = imp.load_source('mozwebidlcodegen.fakemodule', fh.name)
+ mod.__file__ = fake_path
+
+ args = self._get_manager_args()
+ m1 = WebIDLCodegenManager(**args)
+ with MockedOpen({fake_path: '# Original content'}):
+ try:
+ result = m1.generate_build_files()
+ l = len(result.inputs)
+
+ with open(fake_path, 'wt') as fh:
+ fh.write('# Modified content')
+
+ m2 = WebIDLCodegenManager(**args)
+ result = m2.generate_build_files()
+ self.assertEqual(len(result.inputs), l)
+
+ result = m2.generate_build_files()
+ self.assertEqual(len(result.inputs), 0)
+ finally:
+ del sys.modules['mozwebidlcodegen.fakemodule']
+
+ def test_copy_input(self):
+ """Ensure a copied .webidl file is handled properly."""
+
+ # This test simulates changing the type of a WebIDL from static to
+ # preprocessed. In that scenario, the original file still exists but
+ # it should no longer be consulted during codegen.
+
+ args = self._get_manager_args()
+ m1 = WebIDLCodegenManager(**args)
+ m1.generate_build_files()
+
+ old_path = None
+ for p in args['inputs'][0]:
+ if p.endswith('Parent.webidl'):
+ old_path = p
+ break
+ self.assertIsNotNone(old_path)
+
+ new_path = mozpath.join(args['cache_dir'], 'Parent.webidl')
+ shutil.copy2(old_path, new_path)
+
+ args['inputs'][0].remove(old_path)
+ args['inputs'][0].add(new_path)
+
+ m2 = WebIDLCodegenManager(**args)
+ result = m2.generate_build_files()
+ self.assertEqual(len(result.updated), 0)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/dom/bindings/parser/README b/dom/bindings/parser/README
new file mode 100644
index 000000000..94b64b884
--- /dev/null
+++ b/dom/bindings/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/dom/bindings/parser/UPSTREAM b/dom/bindings/parser/UPSTREAM
new file mode 100644
index 000000000..7ac589937
--- /dev/null
+++ b/dom/bindings/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/dom/bindings/parser/WebIDL.py b/dom/bindings/parser/WebIDL.py
new file mode 100644
index 000000000..f668d7d62
--- /dev/null
+++ b/dom/bindings/parser/WebIDL.py
@@ -0,0 +1,6874 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.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
+import string
+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 isNamespace(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 isTypedef(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 is 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.update(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 (isinstance(originalObject, IDLMethod) and
+ isinstance(newObject, IDLMethod)):
+ 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 == "__noSuchMethod__":
+ raise WebIDLError("__noSuchMethod__ is deprecated", [location])
+
+ if name[:2] == "__" and name != "__content" 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 IDLExposureMixins():
+ def __init__(self, location):
+ # _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()
+ self._location = location
+ self._globalScope = None
+
+ def finish(self, scope):
+ assert scope.parentScope is None
+ self._globalScope = scope
+
+ # 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)
+
+ def isExposedInWindow(self):
+ return 'Window' in self.exposureSet
+
+ def isExposedOnMainThread(self):
+ return (self.isExposedInWindow() or
+ self.isExposedInSystemGlobals())
+
+ def isExposedInAnyWorker(self):
+ return len(self.getWorkerExposureSet()) > 0
+
+ def isExposedInWorkerDebugger(self):
+ return len(self.getWorkerDebuggerExposureSet()) > 0
+
+ def isExposedInAnyWorklet(self):
+ return len(self.getWorkletExposureSet()) > 0
+
+ def isExposedInSystemGlobals(self):
+ return 'BackstagePass' in self.exposureSet
+
+ def isExposedInSomeButNotAllWorkers(self):
+ """
+ Returns true if the Exposed extended attribute for this interface
+ exposes it in some worker globals but not others. The return value does
+ not depend on whether the interface is exposed in Window or System
+ globals.
+ """
+ if not self.isExposedInAnyWorker():
+ return False
+ workerScopes = self.parentScope.globalNameMapping["Worker"]
+ return len(workerScopes.difference(self.exposureSet)) > 0
+
+ def getWorkerExposureSet(self):
+ workerScopes = self._globalScope.globalNameMapping["Worker"]
+ return workerScopes.intersection(self.exposureSet)
+
+ def getWorkletExposureSet(self):
+ workletScopes = self._globalScope.globalNameMapping["Worklet"]
+ return workletScopes.intersection(self.exposureSet)
+
+ def getWorkerDebuggerExposureSet(self):
+ workerDebuggerScopes = self._globalScope.globalNameMapping["WorkerDebugger"]
+ return workerDebuggerScopes.intersection(self.exposureSet)
+
+
+class IDLExternalInterface(IDLObjectWithIdentifier, IDLExposureMixins):
+ def __init__(self, location, parentScope, identifier):
+ assert isinstance(identifier, IDLUnresolvedIdentifier)
+ assert isinstance(parentScope, IDLScope)
+ self.parent = None
+ IDLObjectWithIdentifier.__init__(self, location, parentScope, identifier)
+ IDLExposureMixins.__init__(self, location)
+ IDLObjectWithIdentifier.resolve(self, parentScope)
+
+ def finish(self, scope):
+ IDLExposureMixins.finish(self, scope)
+ pass
+
+ def validate(self):
+ pass
+
+ def isIteratorInterface(self):
+ return False
+
+ 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 isProbablyShortLivingObject(self):
+ return False
+
+ def isNavigatorProperty(self):
+ return False
+
+ def _getDependentObjects(self):
+ return set()
+
+
+class IDLPartialInterfaceOrNamespace(IDLObject):
+ def __init__(self, location, name, members, nonPartialInterfaceOrNamespace):
+ 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._haveSecureContextExtendedAttribute = False
+ self._nonPartialInterfaceOrNamespace = nonPartialInterfaceOrNamespace
+ self._finished = False
+ nonPartialInterfaceOrNamespace.addPartialInterface(self)
+
+ def addExtendedAttributes(self, attrs):
+ for attr in attrs:
+ identifier = attr.identifier()
+
+ if identifier in ["Constructor", "NamedConstructor"]:
+ self.propagatedExtendedAttrs.append(attr)
+ elif identifier == "SecureContext":
+ self._haveSecureContextExtendedAttribute = True
+ # This gets propagated to all our members.
+ for member in self.members:
+ if member.getExtendedAttribute("SecureContext"):
+ raise WebIDLError("[SecureContext] specified on both a "
+ "partial interface member and on the "
+ "partial interface itself",
+ [member.location, attr.location])
+ member.addExtendedAttributes([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
+ if (not self._haveSecureContextExtendedAttribute and
+ self._nonPartialInterfaceOrNamespace.getExtendedAttribute("SecureContext")):
+ # This gets propagated to all our members.
+ for member in self.members:
+ if member.getExtendedAttribute("SecureContext"):
+ raise WebIDLError("[SecureContext] specified on both a "
+ "partial interface member and on the "
+ "non-partial interface",
+ [member.location,
+ self._nonPartialInterfaceOrNamespace.location])
+ member.addExtendedAttributes(
+ [IDLExtendedAttribute(self._nonPartialInterfaceOrNamespace.location,
+ ("SecureContext",))])
+ # Need to make sure our non-partial interface or namespace gets
+ # finished so it can report cases when we only have partial
+ # interfaces/namespaces.
+ self._nonPartialInterfaceOrNamespace.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 IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
+ 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.maplikeOrSetlikeOrIterable = None
+ 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
+ # If this is an iterator interface, we need to know what iterable
+ # interface we're iterating for in order to get its nativeType.
+ self.iterableInterface = None
+
+ IDLObjectWithScope.__init__(self, location, parentScope, name)
+ IDLExposureMixins.__init__(self, location)
+
+ if isKnownNonPartial:
+ self.setNonPartial(location, parent, members)
+
+ def ctor(self):
+ identifier = IDLUnresolvedIdentifier(self.location, "constructor",
+ allowForbidden=True)
+ try:
+ return self._lookupIdentifier(identifier)
+ except:
+ return None
+
+ def isIterable(self):
+ return (self.maplikeOrSetlikeOrIterable and
+ self.maplikeOrSetlikeOrIterable.isIterable())
+
+ def isIteratorInterface(self):
+ return self.iterableInterface is not 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])
+
+ IDLExposureMixins.finish(self, scope)
+
+ # 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)
+
+ # Generate maplike/setlike interface members. Since generated members
+ # need to be treated like regular interface members, do this before
+ # things like exposure setting.
+ for member in self.members:
+ if member.isMaplikeOrSetlikeOrIterable():
+ # Check that we only have one interface declaration (currently
+ # there can only be one maplike/setlike declaration per
+ # interface)
+ if self.maplikeOrSetlikeOrIterable:
+ raise WebIDLError("%s declaration used on "
+ "interface that already has %s "
+ "declaration" %
+ (member.maplikeOrSetlikeOrIterableType,
+ self.maplikeOrSetlikeOrIterable.maplikeOrSetlikeOrIterableType),
+ [self.maplikeOrSetlikeOrIterable.location,
+ member.location])
+ self.maplikeOrSetlikeOrIterable = member
+ # If we've got a maplike or setlike declaration, we'll be building all of
+ # our required methods in Codegen. Generate members now.
+ self.maplikeOrSetlikeOrIterable.expand(self.members, self.isJSImplemented())
+
+ # 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.isNamespace():
+ assert not self.parent
+ for m in self.members:
+ if m.isAttr() or m.isMethod():
+ if m.isStatic():
+ raise WebIDLError("Don't mark things explicitly static "
+ "in namespaces",
+ [self.location, m.location])
+ # Just mark all our methods/attributes as static. The other
+ # option is to duplicate the relevant InterfaceMembers
+ # production bits but modified to produce static stuff to
+ # start with, but that sounds annoying.
+ m.forceStatic()
+
+ 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])
+
+ # Interfaces which have interface objects can't inherit
+ # from [NoInterfaceObject] interfaces.
+ if (self.parent.getExtendedAttribute("NoInterfaceObject") and
+ not self.getExtendedAttribute("NoInterfaceObject")):
+ raise WebIDLError("Interface %s does not have "
+ "[NoInterfaceObject] but inherits from "
+ "interface %s which does" %
+ (self.identifier.name,
+ self.parent.identifier.name),
+ [self.location, self.parent.location])
+
+ # Interfaces that are not [SecureContext] can't inherit
+ # from [SecureContext] interfaces.
+ if (self.parent.getExtendedAttribute("SecureContext") and
+ not self.getExtendedAttribute("SecureContext")):
+ raise WebIDLError("Interface %s does not have "
+ "[SecureContext] but inherits from "
+ "interface %s which does" %
+ (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:
+ assert len(ctor._exposureGlobalNames) == 0
+ ctor._exposureGlobalNames.update(self._exposureGlobalNames)
+ ctor.finish(scope)
+
+ for ctor in self.namedConstructors:
+ assert len(ctor._exposureGlobalNames) == 0
+ ctor._exposureGlobalNames.update(self._exposureGlobalNames)
+ 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])
+
+ # If we have a maplike or setlike, and the consequential interface
+ # also does, throw an error.
+ if iface.maplikeOrSetlikeOrIterable and self.maplikeOrSetlikeOrIterable:
+ raise WebIDLError("Maplike/setlike/iterable interface %s cannot have "
+ "maplike/setlike/iterable interface %s as a "
+ "consequential interface" %
+ (self.identifier.name,
+ iface.identifier.name),
+ [self.maplikeOrSetlikeOrIterable.location,
+ iface.maplikeOrSetlikeOrIterable.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)
+ if (ancestor.maplikeOrSetlikeOrIterable is not None and
+ self.maplikeOrSetlikeOrIterable is not None):
+ raise WebIDLError("Cannot have maplike/setlike on %s that "
+ "inherits %s, which is already "
+ "maplike/setlike" %
+ (self.identifier.name,
+ ancestor.identifier.name),
+ [self.maplikeOrSetlikeOrIterable.location,
+ ancestor.maplikeOrSetlikeOrIterable.location])
+ 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. Also, maplike/setlike declarations get a
+ # slot to hold their backing object.
+ for member in self.members:
+ if ((member.isAttr() and
+ (member.getExtendedAttribute("StoreInSlot") or
+ member.getExtendedAttribute("Cached"))) or
+ member.isMaplikeOrSetlike()):
+ if member.slotIndices is None:
+ member.slotIndices = dict()
+ member.slotIndices[self.identifier.name] = 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)
+
+ # At this point, we have all of our members. If the current interface
+ # uses maplike/setlike, check for collisions anywhere in the current
+ # interface or higher in the inheritance chain.
+ if self.maplikeOrSetlikeOrIterable:
+ testInterface = self
+ isAncestor = False
+ while testInterface:
+ self.maplikeOrSetlikeOrIterable.checkCollisions(testInterface.members,
+ isAncestor)
+ isAncestor = True
+ testInterface = testInterface.parent
+
+ # 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. Also note that in practice we disallow
+ # indexed deleters, but it simplifies some other code to
+ # treat deleter analogously to getter/setter/creator by
+ # prefixing it with "named".
+ 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.getExtendedAttribute("LegacyUnenumerableNamedProperties"):
+ # Check that we have a named getter.
+ if "named getters" not in specialMembersSeen:
+ raise WebIDLError(
+ "Interface with [LegacyUnenumerableNamedProperties] does "
+ "not have a named getter",
+ [self.location])
+ ancestor = self.parent
+ while ancestor:
+ if ancestor.getExtendedAttribute("LegacyUnenumerableNamedProperties"):
+ raise WebIDLError(
+ "Interface with [LegacyUnenumerableNamedProperties] "
+ "inherits from another interface with "
+ "[LegacyUnenumerableNamedProperties]",
+ [self.location, ancestor.location])
+ ancestor = ancestor.parent
+
+ 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, because 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():
+ locations = ([self.location] +
+ list(i.location for i in
+ self.interfacesBasedOnSelf if i.parent == self))
+ raise WebIDLError("%s is an unforgeable ancestor interface" %
+ self.identifier.name,
+ locations)
+
+ indexedGetter = None
+ hasLengthAttribute = False
+ for member in self.members:
+ member.validate()
+
+ if self.isCallback() and member.getExtendedAttribute("Replaceable"):
+ raise WebIDLError("[Replaceable] used on an attribute on "
+ "interface %s which is a callback interface" %
+ self.identifier.name,
+ [self.location, member.location])
+
+ # Check that PutForwards refers to another attribute and that no
+ # cycles exist in forwarded assignments. Also check for a
+ # integer-typed "length" attribute.
+ if member.isAttr():
+ if (member.identifier.name == "length" and
+ member.type.isInteger()):
+ hasLengthAttribute = True
+
+ 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")
+
+ # Check that the name of an [Alias] doesn't conflict with an
+ # interface member and whether we support indexed properties.
+ if member.isMethod():
+ if member.isGetter() and member.isIndexed():
+ indexedGetter = member
+
+ for alias in member.aliases:
+ if self.isOnGlobalProtoChain():
+ raise WebIDLError("[Alias] must not be used on a "
+ "[Global] interface operation",
+ [member.location])
+ if (member.getExtendedAttribute("Exposed") or
+ member.getExtendedAttribute("ChromeOnly") or
+ member.getExtendedAttribute("Pref") or
+ member.getExtendedAttribute("Func") or
+ member.getExtendedAttribute("SecureContext")):
+ raise WebIDLError("[Alias] must not be used on a "
+ "conditionally exposed operation",
+ [member.location])
+ if member.isStatic():
+ raise WebIDLError("[Alias] must not be used on a "
+ "static operation",
+ [member.location])
+ if member.isIdentifierLess():
+ raise WebIDLError("[Alias] must not be used on an "
+ "identifierless operation",
+ [member.location])
+ if member.isUnforgeable():
+ raise WebIDLError("[Alias] must not be used on an "
+ "[Unforgeable] operation",
+ [member.location])
+ for m in self.members:
+ if m.identifier.name == alias:
+ raise WebIDLError("[Alias=%s] has same name as "
+ "interface member" % alias,
+ [member.location, m.location])
+ if m.isMethod() and m != member and alias in m.aliases:
+ raise WebIDLError("duplicate [Alias=%s] definitions" %
+ alias,
+ [member.location, m.location])
+
+ if (self.getExtendedAttribute("Pref") and
+ self._exposureGlobalNames != set([self.parentScope.primaryGlobalName])):
+ raise WebIDLError("[Pref] used on an interface that is not %s-only" %
+ self.parentScope.primaryGlobalName,
+ [self.location])
+
+ # Conditional exposure makes no sense for interfaces with no
+ # interface object, unless they're navigator properties.
+ # And SecureContext makes sense for interfaces with no interface object,
+ # since it is also propagated to interface members.
+ if (self.isExposedConditionally(exclusions=["SecureContext"]) and
+ not self.hasInterfaceObject() and
+ not self.isNavigatorProperty()):
+ raise WebIDLError("Interface with no interface object is "
+ "exposed conditionally",
+ [self.location])
+
+ # Value iterators are only allowed on interfaces with indexed getters,
+ # and pair iterators are only allowed on interfaces without indexed
+ # getters.
+ if self.isIterable():
+ iterableDecl = self.maplikeOrSetlikeOrIterable
+ if iterableDecl.isValueIterator():
+ if not indexedGetter:
+ raise WebIDLError("Interface with value iterator does not "
+ "support indexed properties",
+ [self.location])
+
+ if iterableDecl.valueType != indexedGetter.signatures()[0][0]:
+ raise WebIDLError("Iterable type does not match indexed "
+ "getter type",
+ [iterableDecl.location,
+ indexedGetter.location])
+
+ if not hasLengthAttribute:
+ raise WebIDLError('Interface with value iterator does not '
+ 'have an integer-typed "length" attribute',
+ [self.location])
+ else:
+ assert iterableDecl.isPairIterator()
+ if indexedGetter:
+ raise WebIDLError("Interface with pair iterator supports "
+ "indexed properties",
+ [self.location, iterableDecl.location,
+ indexedGetter.location])
+
+ 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 not self.isNamespace()
+ and self.getUserData('hasConcreteDescendant', False))
+
+ 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 %s" %
+ ("interface" if self.isInterface()
+ else "namespace"),
+ [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 isProbablyShortLivingObject(self):
+ current = self
+ while current:
+ if current.getExtendedAttribute("ProbablyShortLivingObject"):
+ return True
+ current = current.parent
+ return False
+
+ def isNavigatorProperty(self):
+ naviProp = self.getExtendedAttribute("NavigatorProperty")
+ if not naviProp:
+ return False
+ assert len(naviProp) == 1
+ assert isinstance(naviProp, list)
+ assert len(naviProp[0]) != 0
+ return True
+
+ 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
+ conditionExtendedAttributes = self._extendedAttrDict.viewkeys() & IDLInterfaceOrNamespace.conditionExtendedAttributes
+ attr = IDLAttribute(self.location,
+ IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"), naviProp[0]),
+ IDLUnresolvedType(self.location, IDLUnresolvedIdentifier(self.location, self.identifier.name)),
+ True,
+ extendedAttrDict={ a: self._extendedAttrDict[a] for a in conditionExtendedAttributes },
+ navigatorObjectGetter=True)
+ attr._exposureGlobalNames = self._exposureGlobalNames
+ # We're abusing Constant a little bit here, because we need Cached. The
+ # getter will create a new object every time, but we're never going to
+ # clear the cached value.
+ extendedAttrs = [ IDLExtendedAttribute(self.location, ("Throws", )),
+ IDLExtendedAttribute(self.location, ("Cached", )),
+ IDLExtendedAttribute(self.location, ("Constant", )) ]
+ attr.addExtendedAttributes(extendedAttrs)
+ return attr
+
+ def hasChildInterfaces(self):
+ return self._hasChildInterfaces
+
+ def isOnGlobalProtoChain(self):
+ return self._isOnGlobalProtoChain
+
+ def _getDependentObjects(self):
+ deps = set(self.members)
+ deps.update(self.implementedInterfaces)
+ if self.parent:
+ deps.add(self.parent)
+ return deps
+
+ def hasMembersInSlots(self):
+ return self._ownMembersInSlots != 0
+
+ conditionExtendedAttributes = [ "Pref", "ChromeOnly", "Func", "AvailableIn",
+ "SecureContext",
+ "CheckAnyPermissions",
+ "CheckAllPermissions" ]
+ def isExposedConditionally(self, exclusions=[]):
+ return any(((not a in exclusions) and self.getExtendedAttribute(a)) for a in self.conditionExtendedAttributes)
+
+class IDLInterface(IDLInterfaceOrNamespace):
+ def __init__(self, location, parentScope, name, parent, members,
+ isKnownNonPartial):
+ IDLInterfaceOrNamespace.__init__(self, location, parentScope, name,
+ parent, members, isKnownNonPartial)
+
+ def __str__(self):
+ return "Interface '%s'" % self.identifier.name
+
+ def isInterface(self):
+ return True
+
+ 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 []
+
+ if self.identifier.name == "Promise":
+ promiseType = BuiltinTypes[IDLBuiltinType.Types.any]
+ else:
+ promiseType = None
+ retType = IDLWrapperType(self.location, self, promiseType)
+
+ 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 newMethod not 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 == "SecureContext":
+ if not attr.noArguments():
+ raise WebIDLError("[%s] must take no arguments" % identifier,
+ [attr.location])
+ # This gets propagated to all our members.
+ for member in self.members:
+ if member.getExtendedAttribute("SecureContext"):
+ raise WebIDLError("[SecureContext] specified on both "
+ "an interface member and on the "
+ "interface itself",
+ [member.location, attr.location])
+ member.addExtendedAttributes([attr])
+ elif (identifier == "NeedResolve" or
+ identifier == "OverrideBuiltins" or
+ identifier == "ChromeOnly" or
+ identifier == "Unforgeable" or
+ identifier == "UnsafeInPrerendering" or
+ identifier == "LegacyEventInit" or
+ identifier == "ProbablyShortLivingObject" or
+ identifier == "LegacyUnenumerableNamedProperties" or
+ identifier == "NonOrdinaryGetPrototypeOf"):
+ # 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 == "Func" or
+ identifier == "Deprecated"):
+ # 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
+
+
+class IDLNamespace(IDLInterfaceOrNamespace):
+ def __init__(self, location, parentScope, name, members, isKnownNonPartial):
+ IDLInterfaceOrNamespace.__init__(self, location, parentScope, name,
+ None, members, isKnownNonPartial)
+
+ def __str__(self):
+ return "Namespace '%s'" % self.identifier.name
+
+ def isNamespace(self):
+ return True
+
+ def addExtendedAttributes(self, attrs):
+ # The set of things namespaces support is small enough it's simpler
+ # to factor out into a separate method than it is to sprinkle
+ # isNamespace() checks all through
+ # IDLInterfaceOrNamespace.addExtendedAttributes.
+ for attr in attrs:
+ identifier = attr.identifier()
+
+ if identifier == "Exposed":
+ convertExposedAttrToGlobalNameSet(attr,
+ self._exposureGlobalNames)
+ elif identifier == "ClassString":
+ # Takes a string value to override the default "Object" if
+ # desired.
+ if not attr.hasValue():
+ raise WebIDLError("[%s] must have a value" % identifier,
+ [attr.location])
+ elif identifier == "ProtoObjectHack":
+ if not attr.noArguments():
+ raise WebIDLError("[%s] must not have arguments" % identifier,
+ [attr.location])
+ else:
+ raise WebIDLError("Unknown extended attribute %s on namespace" %
+ identifier,
+ [attr.location])
+
+ attrlist = attr.listValue()
+ self._extendedAttrDict[identifier] = attrlist if len(attrlist) else True
+
+
+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 canBeEmpty(self):
+ """
+ Returns true if this dictionary can be empty (that is, it has no
+ required members and neither do any of its ancestors).
+ """
+ return (all(member.optional for member in self.members) and
+ (not self.parent or self.parent.canBeEmpty()))
+
+ 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.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',
+ 'usvstring',
+ 'object',
+ 'date',
+ 'void',
+ # Funny stuff
+ 'interface',
+ 'dictionary',
+ 'enum',
+ 'callback',
+ 'union',
+ 'sequence',
+ 'mozmap'
+ )
+
+ 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 isUSVString(self):
+ return False
+
+ def isVoid(self):
+ return self.name == "Void"
+
+ def isSequence(self):
+ return False
+
+ def isMozMap(self):
+ return False
+
+ def isArrayBuffer(self):
+ return False
+
+ def isArrayBufferView(self):
+ return False
+
+ def isSharedArrayBuffer(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.isSharedArrayBuffer() 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.callback._treatNonCallableAsNull
+
+ def treatNonObjectAsNull(self):
+ assert self.tag() == IDLType.Tags.callback
+ return self.nullable() and self.inner.callback._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():
+ print obj
+ assert not obj.isType()
+ if obj.isTypedef():
+ assert self.name.name == obj.identifier.name
+ typedefType = IDLTypedefType(self.location, obj.innerType,
+ obj.identifier)
+ assert not typedefType.isComplete()
+ return typedefType.complete(scope)
+ elif obj.isCallback() and not obj.isInterface():
+ assert self.name.name == obj.identifier.name
+ return IDLCallbackType(self.location, 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 IDLParameterizedType(IDLType):
+ def __init__(self, location, name, innerType):
+ IDLType.__init__(self, location, name)
+ self.builtin = False
+ self.inner = innerType
+
+ def includesRestrictedFloat(self):
+ return self.inner.includesRestrictedFloat()
+
+ def resolveType(self, parentScope):
+ assert isinstance(parentScope, IDLScope)
+ self.inner.resolveType(parentScope)
+
+ def isComplete(self):
+ return self.inner.isComplete()
+
+ def unroll(self):
+ return self.inner.unroll()
+
+ def _getDependentObjects(self):
+ return self.inner._getDependentObjects()
+
+
+class IDLNullableType(IDLParameterizedType):
+ def __init__(self, location, innerType):
+ assert not innerType.isVoid()
+ assert not innerType == BuiltinTypes[IDLBuiltinType.Types.any]
+
+ name = innerType.name
+ if innerType.isComplete():
+ name += "OrNull"
+ IDLParameterizedType.__init__(self, location, name, innerType)
+
+ 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 isUSVString(self):
+ return self.inner.isUSVString()
+
+ def isFloat(self):
+ return self.inner.isFloat()
+
+ def isUnrestricted(self):
+ return self.inner.isUnrestricted()
+
+ 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 isArrayBuffer(self):
+ return self.inner.isArrayBuffer()
+
+ def isArrayBufferView(self):
+ return self.inner.isArrayBufferView()
+
+ def isSharedArrayBuffer(self):
+ return self.inner.isSharedArrayBuffer()
+
+ def isTypedArray(self):
+ return self.inner.isTypedArray()
+
+ def isDictionary(self):
+ return self.inner.isDictionary()
+
+ def isInterface(self):
+ return self.inner.isInterface()
+
+ def isPromise(self):
+ return self.inner.isPromise()
+
+ 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 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 + "OrNull"
+ return self
+
+ 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)
+
+
+class IDLSequenceType(IDLParameterizedType):
+ def __init__(self, location, parameterType):
+ assert not parameterType.isVoid()
+
+ IDLParameterizedType.__init__(self, location, parameterType.name, parameterType)
+ # Need to set self.name up front if our inner type is already complete,
+ # since in that case our .complete() won't be called.
+ if self.inner.isComplete():
+ self.name = self.inner.name + "Sequence"
+
+ 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 isUSVString(self):
+ return False
+
+ def isVoid(self):
+ return False
+
+ def isSequence(self):
+ return True
+
+ def isDictionary(self):
+ return False
+
+ def isInterface(self):
+ return False
+
+ def isEnum(self):
+ return False
+
+ def isSerializable(self):
+ return self.inner.isSerializable()
+
+ def tag(self):
+ return IDLType.Tags.sequence
+
+ def complete(self, scope):
+ self.inner = self.inner.complete(scope)
+ self.name = self.inner.name + "Sequence"
+ return self
+
+ def isDistinguishableFrom(self, other):
+ if other.isPromise():
+ return False
+ 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.isInterface() or
+ other.isDictionary() or
+ other.isCallback() or other.isMozMap())
+
+
+class IDLMozMapType(IDLParameterizedType):
+ def __init__(self, location, parameterType):
+ assert not parameterType.isVoid()
+
+ IDLParameterizedType.__init__(self, location, parameterType.name, parameterType)
+ # Need to set self.name up front if our inner type is already complete,
+ # since in that case our .complete() won't be called.
+ if self.inner.isComplete():
+ self.name = self.inner.name + "MozMap"
+
+ 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 tag(self):
+ return IDLType.Tags.mozmap
+
+ def complete(self, scope):
+ self.inner = self.inner.complete(scope)
+ self.name = self.inner.name + "MozMap"
+ 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.isPromise():
+ return False
+ 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)
+
+
+class IDLUnionType(IDLType):
+ def __init__(self, location, memberTypes):
+ IDLType.__init__(self, location, "")
+ self.memberTypes = memberTypes
+ self.hasNullableType = False
+ self._dictionaryType = None
+ self.flatMemberTypes = None
+ self.builtin = False
+
+ def __eq__(self, other):
+ return isinstance(other, IDLUnionType) and self.memberTypes == other.memberTypes
+
+ def __hash__(self):
+ assert self.isComplete()
+ return self.name.__hash__()
+
+ 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)
+ 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",
+ [self._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._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 hasDictionaryType(self):
+ return self._dictionaryType is not None
+
+ def hasPossiblyEmptyDictionaryType(self):
+ return (self._dictionaryType is not None and
+ self._dictionaryType.inner.canBeEmpty())
+
+ def _getDependentObjects(self):
+ return set(self.memberTypes)
+
+
+class IDLTypedefType(IDLType):
+ def __init__(self, location, innerType, name):
+ IDLType.__init__(self, location, name)
+ self.inner = innerType
+ self.builtin = False
+
+ def __eq__(self, other):
+ return isinstance(other, IDLTypedefType) and self.inner == other.inner
+
+ def __str__(self):
+ return self.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 isUSVString(self):
+ return self.inner.isUSVString()
+
+ def isVoid(self):
+ return self.inner.isVoid()
+
+ def isSequence(self):
+ return self.inner.isSequence()
+
+ def isMozMap(self):
+ return self.inner.isMozMap()
+
+ def isDictionary(self):
+ return self.inner.isDictionary()
+
+ def isArrayBuffer(self):
+ return self.inner.isArrayBuffer()
+
+ def isArrayBufferView(self):
+ return self.inner.isArrayBufferView()
+
+ def isSharedArrayBuffer(self):
+ return self.inner.isSharedArrayBuffer()
+
+ 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
+
+ # 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 IDLTypedef(IDLObjectWithIdentifier):
+ def __init__(self, location, parentScope, innerType, name):
+ identifier = IDLUnresolvedIdentifier(location, name)
+ IDLObjectWithIdentifier.__init__(self, location, parentScope, identifier)
+ self.innerType = innerType
+
+ def __str__(self):
+ return "Typedef %s %s" % (self.identifier.name, self.innerType)
+
+ def finish(self, parentScope):
+ if not self.innerType.isComplete():
+ self.innerType = self.innerType.complete(parentScope)
+
+ def validate(self):
+ pass
+
+ def isTypedef(self):
+ return True
+
+ def addExtendedAttributes(self, attrs):
+ assert len(attrs) == 0
+
+ def _getDependentObjects(self):
+ return self.innerType._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 isUSVString(self):
+ return False
+
+ def isVoid(self):
+ return False
+
+ def isSequence(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 promiseInnerType(self):
+ assert self.isPromise()
+ return self._promiseInnerType
+
+ 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 self.isPromise():
+ return False
+ if other.isPromise():
+ return False
+ 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.isDate())
+ if self.isDictionary() and other.nullable():
+ return False
+ if (other.isPrimitive() or other.isString() or other.isEnum() or
+ other.isDate() or other.isSequence()):
+ 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.isMozMap()):
+ 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 (self.isPromise() 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.
+ #
+ # On the other hand, if our type is a dictionary, we should
+ # depend on it, because the member types of a dictionary
+ # affect whether a method taking the dictionary as an argument
+ # takes a JSContext* argument or not.
+ if self.isDictionary():
+ return set([self.inner])
+ 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',
+ 'usvstring',
+ 'object',
+ 'date',
+ 'void',
+ # Funny stuff
+ 'ArrayBuffer',
+ 'ArrayBufferView',
+ 'SharedArrayBuffer',
+ '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.usvstring: IDLType.Tags.usvstring,
+ 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.SharedArrayBuffer: 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.usvstring)
+
+ def isByteString(self):
+ return self._typeTag == IDLBuiltinType.Types.bytestring
+
+ def isDOMString(self):
+ return self._typeTag == IDLBuiltinType.Types.domstring
+
+ def isUSVString(self):
+ return self._typeTag == IDLBuiltinType.Types.usvstring
+
+ 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 isSharedArrayBuffer(self):
+ return self._typeTag == IDLBuiltinType.Types.SharedArrayBuffer
+
+ 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.isSharedArrayBuffer() 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.isString() 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.isPromise():
+ return False
+ 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.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.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.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())
+ 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.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
+ (self.isSharedArrayBuffer() and not other.isSharedArrayBuffer()) 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.usvstring:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "USVString",
+ IDLBuiltinType.Types.usvstring),
+ 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.SharedArrayBuffer:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "SharedArrayBuffer",
+ IDLBuiltinType.Types.SharedArrayBuffer),
+ 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 NoCoercionFoundError(WebIDLError):
+ """
+ A class we use to indicate generic coercion failures because none of the
+ types worked out in IDLValue.coerceToType.
+ """
+
+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 Exception as e:
+ # Make sure to propagate out WebIDLErrors that are not the
+ # generic "hey, we could not coerce to this type at all"
+ # exception, because those are specific "coercion failed for
+ # reason X" exceptions. Note that we want to swallow
+ # non-WebIDLErrors here, because those can just happen if
+ # "type" is not something that can have a default value at
+ # all.
+ if (isinstance(e, WebIDLError) and
+ not isinstance(e, NoCoercionFoundError)):
+ raise e
+
+ # 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:
+ return IDLValue(self.location, type, 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 IDLValue(self.location, type, self.value)
+ elif self.type.isString() and type.isUSVString():
+ # Allow USVStrings to use default value just like
+ # DOMString. No coercion is required in this case as Codegen.py
+ # treats USVString just like DOMString, but with an
+ # extra normalization step.
+ assert self.type.isDOMString()
+ return self
+ elif self.type.isString() and type.isByteString():
+ # Allow ByteStrings to use a default value like DOMString.
+ # No coercion is required as Codegen.py will handle the
+ # extra steps. We want to make sure that our string contains
+ # only valid characters, so we check that here.
+ valid_ascii_lit = " " + string.ascii_letters + string.digits + string.punctuation
+ for idx, c in enumerate(self.value):
+ if c not in valid_ascii_lit:
+ raise WebIDLError("Coercing this string literal %s to a ByteString is not supported yet. "
+ "Coercion failed due to an unsupported byte %d at index %d."
+ % (self.value.__repr__(), ord(c), idx), [location])
+
+ return IDLValue(self.location, type, self.value)
+
+ raise NoCoercionFoundError("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, IDLExposureMixins):
+
+ Tags = enum(
+ 'Const',
+ 'Attr',
+ 'Method',
+ 'MaplikeOrSetlike',
+ 'Iterable'
+ )
+
+ Special = enum(
+ 'Static',
+ 'Stringifier'
+ )
+
+ AffectsValues = ("Nothing", "Everything")
+ DependsOnValues = ("Nothing", "DOMState", "DeviceState", "Everything")
+
+ def __init__(self, location, identifier, tag, extendedAttrDict=None):
+ IDLObjectWithIdentifier.__init__(self, location, None, identifier)
+ IDLExposureMixins.__init__(self, location)
+ self.tag = tag
+ if extendedAttrDict is None:
+ self._extendedAttrDict = {}
+ else:
+ self._extendedAttrDict = extendedAttrDict
+
+ 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 isMaplikeOrSetlikeOrIterable(self):
+ return (self.tag == IDLInterfaceMember.Tags.MaplikeOrSetlike or
+ self.tag == IDLInterfaceMember.Tags.Iterable)
+
+ def isMaplikeOrSetlike(self):
+ return self.tag == IDLInterfaceMember.Tags.MaplikeOrSetlike
+
+ 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):
+ # We better be exposed _somewhere_.
+ if (len(self._exposureGlobalNames) == 0):
+ print self.identifier.name
+ assert len(self._exposureGlobalNames) != 0
+ IDLExposureMixins.finish(self, scope)
+
+ def validate(self):
+ if (self.getExtendedAttribute("Pref") and
+ self.exposureSet != set([self._globalScope.primaryGlobalName])):
+ raise WebIDLError("[Pref] used on an interface member that is not "
+ "%s-only" % self._globalScope.primaryGlobalName,
+ [self.location])
+
+ if self.isAttr() or self.isMethod():
+ if self.affects == "Everything" and self.dependsOn != "Everything":
+ raise WebIDLError("Interface member is flagged as affecting "
+ "everything but not depending on everything. "
+ "That seems rather unlikely.",
+ [self.location])
+
+ if self.getExtendedAttribute("NewObject"):
+ if self.dependsOn == "Nothing" or self.dependsOn == "DOMState":
+ raise WebIDLError("A [NewObject] method is not idempotent, "
+ "so it has to depend on something other than DOM state.",
+ [self.location])
+ if (self.getExtendedAttribute("Cached") or
+ self.getExtendedAttribute("StoreInSlot")):
+ raise WebIDLError("A [NewObject] attribute shouldnt be "
+ "[Cached] or [StoreInSlot], since the point "
+ "of those is to keep returning the same "
+ "thing across multiple calls, which is not "
+ "what [NewObject] does.",
+ [self.location])
+
+ def _setDependsOn(self, dependsOn):
+ if self.dependsOn != "Everything":
+ raise WebIDLError("Trying to specify multiple different DependsOn, "
+ "Pure, or Constant extended attributes for "
+ "attribute", [self.location])
+ if dependsOn not in IDLInterfaceMember.DependsOnValues:
+ raise WebIDLError("Invalid [DependsOn=%s] on attribute" % dependsOn,
+ [self.location])
+ self.dependsOn = dependsOn
+
+ def _setAffects(self, affects):
+ if self.affects != "Everything":
+ raise WebIDLError("Trying to specify multiple different Affects, "
+ "Pure, or Constant extended attributes for "
+ "attribute", [self.location])
+ if affects not in IDLInterfaceMember.AffectsValues:
+ raise WebIDLError("Invalid [Affects=%s] on attribute" % dependsOn,
+ [self.location])
+ self.affects = affects
+
+ def _addAlias(self, alias):
+ if alias in self.aliases:
+ raise WebIDLError("Duplicate [Alias=%s] on attribute" % alias,
+ [self.location])
+ self.aliases.append(alias)
+
+
+class IDLMaplikeOrSetlikeOrIterableBase(IDLInterfaceMember):
+
+ def __init__(self, location, identifier, ifaceType, keyType, valueType, ifaceKind):
+ IDLInterfaceMember.__init__(self, location, identifier, ifaceKind)
+ if keyType is not None:
+ assert isinstance(keyType, IDLType)
+ else:
+ assert valueType is not None
+ assert ifaceType in ['maplike', 'setlike', 'iterable']
+ if valueType is not None:
+ assert isinstance(valueType, IDLType)
+ self.keyType = keyType
+ self.valueType = valueType
+ self.maplikeOrSetlikeOrIterableType = ifaceType
+ self.disallowedMemberNames = []
+ self.disallowedNonMethodNames = []
+
+ def isMaplike(self):
+ return self.maplikeOrSetlikeOrIterableType == "maplike"
+
+ def isSetlike(self):
+ return self.maplikeOrSetlikeOrIterableType == "setlike"
+
+ def isIterable(self):
+ return self.maplikeOrSetlikeOrIterableType == "iterable"
+
+ def hasKeyType(self):
+ return self.keyType is not None
+
+ def hasValueType(self):
+ return self.valueType is not None
+
+ def checkCollisions(self, members, isAncestor):
+ for member in members:
+ # Check that there are no disallowed members
+ if (member.identifier.name in self.disallowedMemberNames and
+ not ((member.isMethod() and member.isMaplikeOrSetlikeOrIterableMethod()) or
+ (member.isAttr() and member.isMaplikeOrSetlikeAttr()))):
+ raise WebIDLError("Member '%s' conflicts "
+ "with reserved %s name." %
+ (member.identifier.name,
+ self.maplikeOrSetlikeOrIterableType),
+ [self.location, member.location])
+ # Check that there are no disallowed non-method members.
+ # Ancestor members are always disallowed here; own members
+ # are disallowed only if they're non-methods.
+ if ((isAncestor or member.isAttr() or member.isConst()) and
+ member.identifier.name in self.disallowedNonMethodNames):
+ raise WebIDLError("Member '%s' conflicts "
+ "with reserved %s method." %
+ (member.identifier.name,
+ self.maplikeOrSetlikeOrIterableType),
+ [self.location, member.location])
+
+ def addMethod(self, name, members, allowExistingOperations, returnType, args=[],
+ chromeOnly=False, isPure=False, affectsNothing=False, newObject=False,
+ isIteratorAlias=False):
+ """
+ Create an IDLMethod based on the parameters passed in.
+
+ - members is the member list to add this function to, since this is
+ called during the member expansion portion of interface object
+ building.
+
+ - chromeOnly is only True for read-only js implemented classes, to
+ implement underscore prefixed convenience functions which would
+ otherwise not be available, unlike the case of C++ bindings.
+
+ - isPure is only True for idempotent functions, so it is not valid for
+ things like keys, values, etc. that return a new object every time.
+
+ - affectsNothing means that nothing changes due to this method, which
+ affects JIT optimization behavior
+
+ - newObject means the method creates and returns a new object.
+
+ """
+ # Only add name to lists for collision checks if it's not chrome
+ # only.
+ if chromeOnly:
+ name = "__" + name
+ else:
+ if not allowExistingOperations:
+ self.disallowedMemberNames.append(name)
+ else:
+ self.disallowedNonMethodNames.append(name)
+ # If allowExistingOperations is True, and another operation exists
+ # with the same name as the one we're trying to add, don't add the
+ # maplike/setlike operation. However, if the operation is static,
+ # then fail by way of creating the function, which will cause a
+ # naming conflict, per the spec.
+ if allowExistingOperations:
+ for m in members:
+ if m.identifier.name == name and m.isMethod() and not m.isStatic():
+ return
+ method = IDLMethod(self.location,
+ IDLUnresolvedIdentifier(self.location, name, allowDoubleUnderscore=chromeOnly),
+ returnType, args, maplikeOrSetlikeOrIterable=self)
+ # We need to be able to throw from declaration methods
+ method.addExtendedAttributes(
+ [IDLExtendedAttribute(self.location, ("Throws",))])
+ if chromeOnly:
+ method.addExtendedAttributes(
+ [IDLExtendedAttribute(self.location, ("ChromeOnly",))])
+ if isPure:
+ method.addExtendedAttributes(
+ [IDLExtendedAttribute(self.location, ("Pure",))])
+ # Following attributes are used for keys/values/entries. Can't mark
+ # them pure, since they return a new object each time they are run.
+ if affectsNothing:
+ method.addExtendedAttributes(
+ [IDLExtendedAttribute(self.location, ("DependsOn", "Everything")),
+ IDLExtendedAttribute(self.location, ("Affects", "Nothing"))])
+ if newObject:
+ method.addExtendedAttributes(
+ [IDLExtendedAttribute(self.location, ("NewObject",))])
+ if isIteratorAlias:
+ method.addExtendedAttributes(
+ [IDLExtendedAttribute(self.location, ("Alias", "@@iterator"))])
+ members.append(method)
+
+ def resolve(self, parentScope):
+ if self.keyType:
+ self.keyType.resolveType(parentScope)
+ if self.valueType:
+ self.valueType.resolveType(parentScope)
+
+ def finish(self, scope):
+ IDLInterfaceMember.finish(self, scope)
+ if self.keyType and not self.keyType.isComplete():
+ t = self.keyType.complete(scope)
+
+ assert not isinstance(t, IDLUnresolvedType)
+ assert not isinstance(t, IDLTypedefType)
+ assert not isinstance(t.name, IDLUnresolvedIdentifier)
+ self.keyType = t
+ if self.valueType and not self.valueType.isComplete():
+ t = self.valueType.complete(scope)
+
+ assert not isinstance(t, IDLUnresolvedType)
+ assert not isinstance(t, IDLTypedefType)
+ assert not isinstance(t.name, IDLUnresolvedIdentifier)
+ self.valueType = t
+
+ def validate(self):
+ IDLInterfaceMember.validate(self)
+
+ def handleExtendedAttribute(self, attr):
+ IDLInterfaceMember.handleExtendedAttribute(self, attr)
+
+ def _getDependentObjects(self):
+ deps = set()
+ if self.keyType:
+ deps.add(self.keyType)
+ if self.valueType:
+ deps.add(self.valueType)
+ return deps
+
+ def getForEachArguments(self):
+ return [IDLArgument(self.location,
+ IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"),
+ "callback"),
+ BuiltinTypes[IDLBuiltinType.Types.object]),
+ IDLArgument(self.location,
+ IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"),
+ "thisArg"),
+ BuiltinTypes[IDLBuiltinType.Types.any],
+ optional=True)]
+
+# Iterable adds ES6 iterator style functions and traits
+# (keys/values/entries/@@iterator) to an interface.
+class IDLIterable(IDLMaplikeOrSetlikeOrIterableBase):
+
+ def __init__(self, location, identifier, keyType, valueType=None, scope=None):
+ IDLMaplikeOrSetlikeOrIterableBase.__init__(self, location, identifier,
+ "iterable", keyType, valueType,
+ IDLInterfaceMember.Tags.Iterable)
+ self.iteratorType = None
+
+ def __str__(self):
+ return "declared iterable with key '%s' and value '%s'" % (self.keyType, self.valueType)
+
+ def expand(self, members, isJSImplemented):
+ """
+ In order to take advantage of all of the method machinery in Codegen,
+ we generate our functions as if they were part of the interface
+ specification during parsing.
+ """
+ # We only need to add entries/keys/values here if we're a pair iterator.
+ # Value iterators just copy these from %ArrayPrototype% instead.
+ if not self.isPairIterator():
+ return
+
+ # object entries()
+ self.addMethod("entries", members, False, self.iteratorType,
+ affectsNothing=True, newObject=True,
+ isIteratorAlias=True)
+ # object keys()
+ self.addMethod("keys", members, False, self.iteratorType,
+ affectsNothing=True, newObject=True)
+ # object values()
+ self.addMethod("values", members, False, self.iteratorType,
+ affectsNothing=True, newObject=True)
+
+ # void forEach(callback(valueType, keyType), optional any thisArg)
+ self.addMethod("forEach", members, False,
+ BuiltinTypes[IDLBuiltinType.Types.void],
+ self.getForEachArguments())
+
+ def isValueIterator(self):
+ return not self.isPairIterator()
+
+ def isPairIterator(self):
+ return self.hasKeyType()
+
+# MaplikeOrSetlike adds ES6 map-or-set-like traits to an interface.
+class IDLMaplikeOrSetlike(IDLMaplikeOrSetlikeOrIterableBase):
+
+ def __init__(self, location, identifier, maplikeOrSetlikeType,
+ readonly, keyType, valueType):
+ IDLMaplikeOrSetlikeOrIterableBase.__init__(self, location, identifier, maplikeOrSetlikeType,
+ keyType, valueType, IDLInterfaceMember.Tags.MaplikeOrSetlike)
+ self.readonly = readonly
+ self.slotIndices = None
+
+ # When generating JSAPI access code, we need to know the backing object
+ # type prefix to create the correct function. Generate here for reuse.
+ if self.isMaplike():
+ self.prefix = 'Map'
+ elif self.isSetlike():
+ self.prefix = 'Set'
+
+ def __str__(self):
+ return "declared '%s' with key '%s'" % (self.maplikeOrSetlikeOrIterableType, self.keyType)
+
+ def expand(self, members, isJSImplemented):
+ """
+ In order to take advantage of all of the method machinery in Codegen,
+ we generate our functions as if they were part of the interface
+ specification during parsing.
+ """
+ # Both maplike and setlike have a size attribute
+ members.append(IDLAttribute(self.location,
+ IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"), "size"),
+ BuiltinTypes[IDLBuiltinType.Types.unsigned_long],
+ True,
+ maplikeOrSetlike=self))
+ self.reserved_ro_names = ["size"]
+ self.disallowedMemberNames.append("size")
+
+ # object entries()
+ self.addMethod("entries", members, False, BuiltinTypes[IDLBuiltinType.Types.object],
+ affectsNothing=True, isIteratorAlias=self.isMaplike())
+ # object keys()
+ self.addMethod("keys", members, False, BuiltinTypes[IDLBuiltinType.Types.object],
+ affectsNothing=True)
+ # object values()
+ self.addMethod("values", members, False, BuiltinTypes[IDLBuiltinType.Types.object],
+ affectsNothing=True, isIteratorAlias=self.isSetlike())
+
+ # void forEach(callback(valueType, keyType), thisVal)
+ self.addMethod("forEach", members, False, BuiltinTypes[IDLBuiltinType.Types.void],
+ self.getForEachArguments())
+
+ def getKeyArg():
+ return IDLArgument(self.location,
+ IDLUnresolvedIdentifier(self.location, "key"),
+ self.keyType)
+
+ # boolean has(keyType key)
+ self.addMethod("has", members, False, BuiltinTypes[IDLBuiltinType.Types.boolean],
+ [getKeyArg()], isPure=True)
+
+ if not self.readonly:
+ # void clear()
+ self.addMethod("clear", members, True, BuiltinTypes[IDLBuiltinType.Types.void],
+ [])
+ # boolean delete(keyType key)
+ self.addMethod("delete", members, True,
+ BuiltinTypes[IDLBuiltinType.Types.boolean], [getKeyArg()])
+
+ # Always generate underscored functions (e.g. __add, __clear) for js
+ # implemented interfaces as convenience functions.
+ if isJSImplemented:
+ # void clear()
+ self.addMethod("clear", members, True, BuiltinTypes[IDLBuiltinType.Types.void],
+ [], chromeOnly=True)
+ # boolean delete(keyType key)
+ self.addMethod("delete", members, True,
+ BuiltinTypes[IDLBuiltinType.Types.boolean], [getKeyArg()],
+ chromeOnly=True)
+
+ if self.isSetlike():
+ if not self.readonly:
+ # Add returns the set object it just added to.
+ # object add(keyType key)
+
+ self.addMethod("add", members, True,
+ BuiltinTypes[IDLBuiltinType.Types.object], [getKeyArg()])
+ if isJSImplemented:
+ self.addMethod("add", members, True,
+ BuiltinTypes[IDLBuiltinType.Types.object], [getKeyArg()],
+ chromeOnly=True)
+ return
+
+ # If we get this far, we're a maplike declaration.
+
+ # valueType get(keyType key)
+ #
+ # Note that instead of the value type, we're using any here. The
+ # validity checks should happen as things are inserted into the map,
+ # and using any as the return type makes code generation much simpler.
+ #
+ # TODO: Bug 1155340 may change this to use specific type to provide
+ # more info to JIT.
+ self.addMethod("get", members, False, BuiltinTypes[IDLBuiltinType.Types.any],
+ [getKeyArg()], isPure=True)
+
+ def getValueArg():
+ return IDLArgument(self.location,
+ IDLUnresolvedIdentifier(self.location, "value"),
+ self.valueType)
+
+ if not self.readonly:
+ self.addMethod("set", members, True, BuiltinTypes[IDLBuiltinType.Types.object],
+ [getKeyArg(), getValueArg()])
+ if isJSImplemented:
+ self.addMethod("set", members, True, BuiltinTypes[IDLBuiltinType.Types.object],
+ [getKeyArg(), getValueArg()], chromeOnly=True)
+
+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 == "SecureContext"):
+ # 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, maplikeOrSetlike=None,
+ extendedAttrDict=None, navigatorObjectGetter=False):
+ IDLInterfaceMember.__init__(self, location, identifier,
+ IDLInterfaceMember.Tags.Attr,
+ extendedAttrDict=extendedAttrDict)
+
+ 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.slotIndices = None
+ assert maplikeOrSetlike is None or isinstance(maplikeOrSetlike, IDLMaplikeOrSetlike)
+ self.maplikeOrSetlike = maplikeOrSetlike
+ self.dependsOn = "Everything"
+ self.affects = "Everything"
+ self.navigatorObjectGetter = navigatorObjectGetter
+
+ 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 forceStatic(self):
+ self._static = True
+
+ 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):
+ def typeContainsChromeOnlyDictionaryMember(type):
+ if (type.nullable() or
+ type.isSequence() or
+ type.isMozMap()):
+ return typeContainsChromeOnlyDictionaryMember(type.inner)
+
+ if type.isUnion():
+ for memberType in type.flatMemberTypes:
+ (contains, location) = typeContainsChromeOnlyDictionaryMember(memberType)
+ if contains:
+ return (True, location)
+
+ if type.isDictionary():
+ dictionary = type.inner
+ while dictionary:
+ (contains, location) = dictionaryContainsChromeOnlyMember(dictionary)
+ if contains:
+ return (True, location)
+ dictionary = dictionary.parent
+
+ return (False, None)
+
+ def dictionaryContainsChromeOnlyMember(dictionary):
+ for member in dictionary.members:
+ if member.getExtendedAttribute("ChromeOnly"):
+ return (True, member.location)
+ (contains, location) = typeContainsChromeOnlyDictionaryMember(member.type)
+ if contains:
+ return (True, location)
+ return (False, None)
+
+ IDLInterfaceMember.validate(self)
+
+ if (self.getExtendedAttribute("Cached") or
+ self.getExtendedAttribute("StoreInSlot")):
+ if not self.affects == "Nothing":
+ raise WebIDLError("Cached attributes and attributes stored in "
+ "slots must be Constant or Pure or "
+ "Affects=Nothing, since the getter won't always "
+ "be called.",
+ [self.location])
+ (contains, location) = typeContainsChromeOnlyDictionaryMember(self.type)
+ if contains:
+ raise WebIDLError("[Cached] and [StoreInSlot] must not be used "
+ "on an attribute whose type contains a "
+ "[ChromeOnly] dictionary member",
+ [self.location, 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 [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 not attr.noArguments():
+ raise WebIDLError("[Replaceable] must take no arguments",
+ [attr.location])
+ if not self.readonly:
+ raise WebIDLError("[Replaceable] is only allowed on readonly "
+ "attributes", [attr.location, self.location])
+ if self.isStatic():
+ raise WebIDLError("[Replaceable] is only allowed on non-static "
+ "attributes", [attr.location, self.location])
+ 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 == "LenientSetter":
+ if not attr.noArguments():
+ raise WebIDLError("[LenientSetter] must take no arguments",
+ [attr.location])
+ if not self.readonly:
+ raise WebIDLError("[LenientSetter] is only allowed on readonly "
+ "attributes", [attr.location, self.location])
+ if self.isStatic():
+ raise WebIDLError("[LenientSetter] is only allowed on non-static "
+ "attributes", [attr.location, self.location])
+ if self.getExtendedAttribute("PutForwards") is not None:
+ raise WebIDLError("[LenientSetter] and [PutForwards] can't both "
+ "appear on the same attribute",
+ [attr.location, self.location])
+ if self.getExtendedAttribute("Replaceable") is not None:
+ raise WebIDLError("[LenientSetter] 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 == "Pure":
+ if not attr.noArguments():
+ raise WebIDLError("[Pure] must take no arguments",
+ [attr.location])
+ self._setDependsOn("DOMState")
+ self._setAffects("Nothing")
+ elif identifier == "Constant" or identifier == "SameObject":
+ if not attr.noArguments():
+ raise WebIDLError("[%s] must take no arguments" % identifier,
+ [attr.location])
+ self._setDependsOn("Nothing")
+ self._setAffects("Nothing")
+ elif identifier == "Affects":
+ if not attr.hasValue():
+ raise WebIDLError("[Affects] takes an identifier",
+ [attr.location])
+ self._setAffects(attr.value())
+ elif identifier == "DependsOn":
+ if not attr.hasValue():
+ raise WebIDLError("[DependsOn] takes an identifier",
+ [attr.location])
+ if (attr.value() != "Everything" and attr.value() != "DOMState" and
+ not self.readonly):
+ raise WebIDLError("[DependsOn=%s] only allowed on "
+ "readonly attributes" % attr.value(),
+ [attr.location, self.location])
+ self._setDependsOn(attr.value())
+ elif identifier == "UseCounter":
+ if self.stringifier:
+ raise WebIDLError("[UseCounter] must not be used on a "
+ "stringifier attribute",
+ [attr.location, self.location])
+ elif identifier == "Unscopable":
+ if not attr.noArguments():
+ raise WebIDLError("[Unscopable] must take no arguments",
+ [attr.location])
+ if self.isStatic():
+ raise WebIDLError("[Unscopable] is only allowed on non-static "
+ "attributes and operations",
+ [attr.location, self.location])
+ elif (identifier == "Pref" or
+ identifier == "Deprecated" or
+ identifier == "SetterThrows" or
+ identifier == "Throws" or
+ identifier == "GetterThrows" or
+ identifier == "ChromeOnly" or
+ identifier == "Func" or
+ identifier == "SecureContext" or
+ identifier == "Frozen" or
+ identifier == "NewObject" or
+ identifier == "UnsafeInPrerendering" or
+ identifier == "NeedsSubjectPrincipal" or
+ identifier == "NeedsCallerType" or
+ identifier == "ReturnValueNeedsContainsHack" or
+ identifier == "BinaryName"):
+ # 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 isMaplikeOrSetlikeAttr(self):
+ """
+ True if this attribute was generated from an interface with
+ maplike/setlike (e.g. this is the size attribute for
+ maplike/setlike)
+ """
+ return self.maplikeOrSetlike is not None
+
+ 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
+ self._extendedAttrDict = {}
+
+ assert not variadic or optional
+ assert not variadic or not defaultValue
+
+ 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
+ elif (self.dictionaryMember and
+ (identifier == "ChromeOnly" or identifier == "Func")):
+ if not self.optional:
+ raise WebIDLError("[%s] must not be used on a required "
+ "dictionary member" % identifier,
+ [attribute.location])
+ else:
+ raise WebIDLError("Unhandled extended attribute on %s" %
+ ("a dictionary member" if self.dictionaryMember else
+ "an argument"),
+ [attribute.location])
+ attrlist = attribute.listValue()
+ self._extendedAttrDict[identifier] = attrlist if len(attrlist) else True
+
+ def getExtendedAttribute(self, name):
+ return self._extendedAttrDict.get(name, None)
+
+ 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 and not self.variadic):
+ # Default optional non-variadic 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
+
+ def canHaveMissingValue(self):
+ return self.optional and not self.defaultValue
+
+
+class IDLCallback(IDLObjectWithScope):
+ def __init__(self, location, parentScope, identifier, returnType, arguments):
+ assert isinstance(returnType, IDLType)
+
+ 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 isCallback(self):
+ return True
+
+ def signatures(self):
+ return [(self._returnType, self._arguments)]
+
+ 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 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 IDLCallbackType(IDLType):
+ def __init__(self, location, callback):
+ IDLType.__init__(self, location, callback.identifier.name)
+ self.callback = callback
+
+ def isCallback(self):
+ return True
+
+ def tag(self):
+ return IDLType.Tags.callback
+
+ def isDistinguishableFrom(self, other):
+ if other.isPromise():
+ return False
+ 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() or
+ other.isSequence())
+
+ def _getDependentObjects(self):
+ return self.callback._getDependentObjects()
+
+
+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
+ )
+
+ 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,
+ maplikeOrSetlikeOrIterable=None):
+ # 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
+ assert maplikeOrSetlikeOrIterable is None or isinstance(maplikeOrSetlikeOrIterable, IDLMaplikeOrSetlikeOrIterableBase)
+ self.maplikeOrSetlikeOrIterable = maplikeOrSetlikeOrIterable
+ self._specialType = specialType
+ self._unforgeable = False
+ self.dependsOn = "Everything"
+ self.affects = "Everything"
+ self.aliases = []
+
+ 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 forceStatic(self):
+ self._static = True
+
+ 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 isMaplikeOrSetlikeOrIterableMethod(self):
+ """
+ True if this method was generated as part of a
+ maplike/setlike/etc interface (e.g. has/get methods)
+ """
+ return self.maplikeOrSetlikeOrIterable is not None
+
+ def isSpecial(self):
+ return (self.isGetter() or
+ self.isSetter() or
+ self.isCreator() or
+ self.isDeleter() or
+ self.isLegacycaller() or
+ self.isStringifier() or
+ self.isJsonifier())
+
+ def hasOverloads(self):
+ return self._hasOverloads
+
+ def isIdentifierLess(self):
+ """
+ True if the method name started with __, and if the method is not a
+ maplike/setlike method. Interfaces with maplike/setlike will generate
+ methods starting with __ for chrome only backing object access in JS
+ implemented interfaces, so while these functions use what is considered
+ an non-identifier name, they actually DO have an identifier.
+ """
+ return (self.identifier.name[:2] == "__" and
+ not self.isMaplikeOrSetlikeOrIterableMethod())
+
+ 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)
+
+ for overload in self._overloads:
+ 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
+
+ for argument in overload.arguments:
+ if not argument.isComplete():
+ argument.complete(scope)
+ assert argument.type.isComplete()
+
+ # 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])
+
+ overloadWithPromiseReturnType = None
+ overloadWithoutPromiseReturnType = None
+ for overload in self._overloads:
+ returnType = overload.returnType
+ if not returnType.unroll().isExposedInAllOf(self.exposureSet):
+ raise WebIDLError("Overload returns a type that is not exposed "
+ "everywhere where the method is exposed",
+ [overload.location])
+
+ variadicArgument = None
+
+ arguments = overload.arguments
+ for (idx, argument) in enumerate(arguments):
+ assert argument.type.isComplete()
+
+ if ((argument.type.isDictionary() and
+ argument.type.inner.canBeEmpty())or
+ (argument.type.isUnion() and
+ argument.type.unroll().hasPossiblyEmptyDictionaryType())):
+ # Optional dictionaries and unions containing optional
+ # 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
+
+ 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])
+
+ if self.getExtendedAttribute("StaticClassOverride") and not \
+ (self.identifier.scope.isJSImplemented() and self.isStatic()):
+ raise WebIDLError("StaticClassOverride can be applied to static"
+ " methods on JS-implemented classes only.",
+ [self.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 == "LenientSetter":
+ raise WebIDLError("Only attributes support [LenientSetter]",
+ [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 == "CrossOriginCallable" or
+ identifier == "WebGLHandlesContextLoss"):
+ # Known no-argument attributes.
+ if not attr.noArguments():
+ raise WebIDLError("[%s] must take no arguments" % identifier,
+ [attr.location])
+ elif identifier == "Pure":
+ if not attr.noArguments():
+ raise WebIDLError("[Pure] must take no arguments",
+ [attr.location])
+ self._setDependsOn("DOMState")
+ self._setAffects("Nothing")
+ elif identifier == "Affects":
+ if not attr.hasValue():
+ raise WebIDLError("[Affects] takes an identifier",
+ [attr.location])
+ self._setAffects(attr.value())
+ elif identifier == "DependsOn":
+ if not attr.hasValue():
+ raise WebIDLError("[DependsOn] takes an identifier",
+ [attr.location])
+ self._setDependsOn(attr.value())
+ elif identifier == "Alias":
+ if not attr.hasValue():
+ raise WebIDLError("[Alias] takes an identifier or string",
+ [attr.location])
+ self._addAlias(attr.value())
+ elif identifier == "UseCounter":
+ if self.isSpecial():
+ raise WebIDLError("[UseCounter] must not be used on a special "
+ "operation",
+ [attr.location, self.location])
+ elif identifier == "Unscopable":
+ if not attr.noArguments():
+ raise WebIDLError("[Unscopable] must take no arguments",
+ [attr.location])
+ if self.isStatic():
+ raise WebIDLError("[Unscopable] is only allowed on non-static "
+ "attributes and operations",
+ [attr.location, self.location])
+ elif (identifier == "Throws" or
+ identifier == "NewObject" or
+ identifier == "ChromeOnly" or
+ identifier == "UnsafeInPrerendering" or
+ identifier == "Pref" or
+ identifier == "Deprecated" or
+ identifier == "Func" or
+ identifier == "SecureContext" or
+ identifier == "BinaryName" or
+ identifier == "NeedsSubjectPrincipal" or
+ identifier == "NeedsCallerType" or
+ identifier == "StaticClassOverride"):
+ # 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.update(overload._getDependentObjects())
+ return deps
+
+
+class IDLImplementsStatement(IDLObject):
+ def __init__(self, location, implementor, implementee):
+ IDLObject.__init__(self, location)
+ self.implementor = implementor
+ self.implementee = implementee
+ self._finished = False
+
+ def finish(self, scope):
+ if self._finished:
+ return
+ 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)
+ self.implementor = implementor
+ self.implementee = 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",
+ "USVString": "USVSTRING",
+ "any": "ANY",
+ "boolean": "BOOLEAN",
+ "byte": "BYTE",
+ "double": "DOUBLE",
+ "float": "FLOAT",
+ "long": "LONG",
+ "object": "OBJECT",
+ "octet": "OCTET",
+ "Promise": "PROMISE",
+ "required": "REQUIRED",
+ "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",
+ "SharedArrayBuffer": "SHAREDARRAYBUFFER",
+ "or": "OR",
+ "maplike": "MAPLIKE",
+ "setlike": "SETLIKE",
+ "iterable": "ITERABLE",
+ "namespace": "NAMESPACE"
+ }
+
+ 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 %r defined, but not used" or \
+ 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 %r 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
+ | Namespace
+ | Partial
+ | 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 handleNonPartialObject(self, location, identifier, constructor,
+ constructorArgs, nonPartialArgs):
+ """
+ This handles non-partial objects (interfaces and namespaces) by
+ checking for an existing partial object, and promoting it to
+ non-partial as needed. The return value is the non-partial object.
+
+ constructorArgs are all the args for the constructor except the last
+ one: isKnownNonPartial.
+
+ nonPartialArgs are the args for the setNonPartial call.
+ """
+ # The name of the class starts with "IDL", so strip that off.
+ # Also, starts with a capital letter after that, so nix that
+ # as well.
+ prettyname = constructor.__name__[3:].lower()
+
+ try:
+ existingObj = self.globalScope()._lookupIdentifier(identifier)
+ if existingObj:
+ if not isinstance(existingObj, constructor):
+ raise WebIDLError("%s has the same name as "
+ "non-%s object" %
+ (prettyname.capitalize(), prettyname),
+ [location, existingObj.location])
+ existingObj.setNonPartial(*nonPartialArgs)
+ return existingObj
+ except Exception, ex:
+ if isinstance(ex, WebIDLError):
+ raise ex
+ pass
+
+ # True for isKnownNonPartial
+ return constructor(*(constructorArgs + [True]))
+
+ 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]
+
+ p[0] = self.handleNonPartialObject(
+ location, identifier, IDLInterface,
+ [location, self.globalScope(), identifier, parent, members],
+ [location, parent, members])
+
+ 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_Namespace(self, p):
+ """
+ Namespace : NAMESPACE IDENTIFIER LBRACE InterfaceMembers RBRACE SEMICOLON
+ """
+ location = self.getLocation(p, 1)
+ identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
+ members = p[4]
+
+ p[0] = self.handleNonPartialObject(
+ location, identifier, IDLNamespace,
+ [location, self.globalScope(), identifier, members],
+ [location, None, members])
+
+ def p_Partial(self, p):
+ """
+ Partial : PARTIAL PartialDefinition
+ """
+ p[0] = p[2]
+
+ def p_PartialDefinition(self, p):
+ """
+ PartialDefinition : PartialInterface
+ | PartialNamespace
+ """
+ p[0] = p[1]
+
+ def handlePartialObject(self, location, identifier, nonPartialConstructor,
+ nonPartialConstructorArgs,
+ partialConstructorArgs):
+ """
+ This handles partial objects (interfaces and namespaces) by checking for
+ an existing non-partial object, and adding ourselves to it as needed.
+ The return value is our partial object. For now we just use
+ IDLPartialInterfaceOrNamespace for partial objects.
+
+ nonPartialConstructorArgs are all the args for the non-partial
+ constructor except the last two: members and isKnownNonPartial.
+
+ partialConstructorArgs are the arguments for the
+ IDLPartialInterfaceOrNamespace constructor, except the last one (the
+ non-partial object).
+ """
+ # The name of the class starts with "IDL", so strip that off.
+ # Also, starts with a capital letter after that, so nix that
+ # as well.
+ prettyname = nonPartialConstructor.__name__[3:].lower()
+
+ nonPartialObject = None
+ try:
+ nonPartialObject = self.globalScope()._lookupIdentifier(identifier)
+ if nonPartialObject:
+ if not isinstance(nonPartialObject, nonPartialConstructor):
+ raise WebIDLError("Partial %s has the same name as "
+ "non-%s object" %
+ (prettyname, prettyname),
+ [location, nonPartialObject.location])
+ except Exception, ex:
+ if isinstance(ex, WebIDLError):
+ raise ex
+ pass
+
+ if not nonPartialObject:
+ nonPartialObject = nonPartialConstructor(
+ # No members, False for isKnownNonPartial
+ *(nonPartialConstructorArgs + [[], False]))
+ partialInterface = IDLPartialInterfaceOrNamespace(
+ *(partialConstructorArgs + [nonPartialObject]))
+ return partialInterface
+
+ def p_PartialInterface(self, p):
+ """
+ PartialInterface : INTERFACE IDENTIFIER LBRACE InterfaceMembers RBRACE SEMICOLON
+ """
+ location = self.getLocation(p, 1)
+ identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
+ members = p[4]
+
+ p[0] = self.handlePartialObject(
+ location, identifier, IDLInterface,
+ [location, self.globalScope(), identifier, None],
+ [location, identifier, members])
+
+ def p_PartialNamespace(self, p):
+ """
+ PartialNamespace : NAMESPACE IDENTIFIER LBRACE InterfaceMembers RBRACE SEMICOLON
+ """
+ location = self.getLocation(p, 1)
+ identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
+ members = p[4]
+
+ p[0] = self.handlePartialObject(
+ location, identifier, IDLNamespace,
+ [location, self.globalScope(), identifier],
+ [location, identifier, members])
+
+ 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
+ | AttributeOrOperationOrMaplikeOrSetlikeOrIterable
+ """
+ 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 : Required Type IDENTIFIER Default SEMICOLON
+ """
+ # These quack a lot like optional arguments, so just treat them that way.
+ t = p[2]
+ assert isinstance(t, IDLType)
+ identifier = IDLUnresolvedIdentifier(self.getLocation(p, 3), p[3])
+ defaultValue = p[4]
+ optional = not p[1]
+
+ if not optional and defaultValue:
+ raise WebIDLError("Required dictionary members can't have a default value.",
+ [self.getLocation(p, 4)])
+
+ p[0] = IDLArgument(self.getLocation(p, 3), identifier, t,
+ optional=optional,
+ 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] = IDLCallback(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 = IDLTypedef(self.getLocation(p, 1), self.globalScope(),
+ p[2], p[3])
+ 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 is 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_AttributeOrOperationOrMaplikeOrSetlikeOrIterable(self, p):
+ """
+ AttributeOrOperationOrMaplikeOrSetlikeOrIterable : Attribute
+ | Maplike
+ | Setlike
+ | Iterable
+ | Operation
+ """
+ p[0] = p[1]
+
+ def p_Iterable(self, p):
+ """
+ Iterable : ITERABLE LT Type GT SEMICOLON
+ | ITERABLE LT Type COMMA Type GT SEMICOLON
+ """
+ location = self.getLocation(p, 2)
+ identifier = IDLUnresolvedIdentifier(location, "__iterable",
+ allowDoubleUnderscore=True)
+ if (len(p) > 6):
+ keyType = p[3]
+ valueType = p[5]
+ else:
+ keyType = None
+ valueType = p[3]
+
+ p[0] = IDLIterable(location, identifier, keyType, valueType, self.globalScope())
+
+ def p_Setlike(self, p):
+ """
+ Setlike : ReadOnly SETLIKE LT Type GT SEMICOLON
+ """
+ readonly = p[1]
+ maplikeOrSetlikeType = p[2]
+ location = self.getLocation(p, 2)
+ identifier = IDLUnresolvedIdentifier(location, "__setlike",
+ allowDoubleUnderscore=True)
+ keyType = p[4]
+ valueType = keyType
+ p[0] = IDLMaplikeOrSetlike(location, identifier, maplikeOrSetlikeType,
+ readonly, keyType, valueType)
+
+ def p_Maplike(self, p):
+ """
+ Maplike : ReadOnly MAPLIKE LT Type COMMA Type GT SEMICOLON
+ """
+ readonly = p[1]
+ maplikeOrSetlikeType = p[2]
+ location = self.getLocation(p, 2)
+ identifier = IDLUnresolvedIdentifier(location, "__maplike",
+ allowDoubleUnderscore=True)
+ keyType = p[4]
+ valueType = p[6]
+ p[0] = IDLMaplikeOrSetlike(location, identifier, maplikeOrSetlikeType,
+ readonly, keyType, valueType)
+
+ 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_AttributeInherited(self, p):
+ """
+ Attribute : INHERIT AttributeRest
+ """
+ (location, identifier, type, readonly) = p[2]
+ p[0] = IDLAttribute(location, identifier, type, readonly, inherit=True)
+
+ def p_Attribute(self, p):
+ """
+ Attribute : AttributeRest
+ """
+ (location, identifier, type, readonly) = p[1]
+ p[0] = IDLAttribute(location, identifier, type, readonly, inherit=False)
+
+ def p_AttributeRest(self, p):
+ """
+ AttributeRest : ReadOnly ATTRIBUTE Type AttributeName 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_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
+ if deleter:
+ raise WebIDLError("There is no such thing as an indexed deleter.",
+ [self.getLocation(p, 1)])
+ 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
+ | ITERABLE
+ | LEGACYCALLER
+ | MAPLIKE
+ | PARTIAL
+ | REQUIRED
+ | SERIALIZER
+ | SETLIKE
+ | SETTER
+ | STATIC
+ | STRINGIFIER
+ | JSONIFIER
+ | TYPEDEF
+ | UNRESTRICTED
+ | NAMESPACE
+ """
+ p[0] = p[1]
+
+ def p_AttributeName(self, p):
+ """
+ AttributeName : IDENTIFIER
+ | REQUIRED
+ """
+ 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_Required(self, p):
+ """
+ Required : REQUIRED
+ """
+ p[0] = True
+
+ def p_RequiredEmpty(self, p):
+ """
+ Required :
+ """
+ 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
+ | USVSTRING
+ | 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 Null
+ """
+ p[0] = self.handleNullable(p[1], p[2])
+
+ def p_SingleTypeNonAnyType(self, p):
+ """
+ SingleType : NonAnyType
+ """
+ p[0] = p[1]
+
+ def p_SingleTypeAnyType(self, p):
+ """
+ SingleType : ANY
+ """
+ p[0] = BuiltinTypes[IDLBuiltinType.Types.any]
+
+ 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_UnionMemberType(self, p):
+ """
+ UnionMemberType : UnionType Null
+ """
+ p[0] = self.handleNullable(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 Null
+ | ARRAYBUFFER Null
+ | SHAREDARRAYBUFFER Null
+ | OBJECT Null
+ """
+ if p[1] == "object":
+ type = BuiltinTypes[IDLBuiltinType.Types.object]
+ elif p[1] == "ArrayBuffer":
+ type = BuiltinTypes[IDLBuiltinType.Types.ArrayBuffer]
+ elif p[1] == "SharedArrayBuffer":
+ type = BuiltinTypes[IDLBuiltinType.Types.SharedArrayBuffer]
+ else:
+ type = BuiltinTypes[p[1]]
+
+ p[0] = self.handleNullable(type, p[2])
+
+ def p_NonAnyTypeSequenceType(self, p):
+ """
+ NonAnyType : SEQUENCE LT Type GT Null
+ """
+ innerType = p[3]
+ type = IDLSequenceType(self.getLocation(p, 1), innerType)
+ p[0] = self.handleNullable(type, p[5])
+
+ # 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])
+ p[0] = self.handleNullable(type, p[5])
+
+ def p_NonAnyTypeMozMapType(self, p):
+ """
+ NonAnyType : MOZMAP LT Type GT Null
+ """
+ innerType = p[3]
+ type = IDLMozMapType(self.getLocation(p, 1), innerType)
+ p[0] = self.handleNullable(type, p[5])
+
+ def p_NonAnyTypeScopedName(self, p):
+ """
+ NonAnyType : ScopedName Null
+ """
+ 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])
+ assert not obj.isType()
+ if obj.isTypedef():
+ type = IDLTypedefType(self.getLocation(p, 1), obj.innerType,
+ obj.identifier.name)
+ elif obj.isCallback() and not obj.isInterface():
+ type = IDLCallbackType(self.getLocation(p, 1), obj)
+ else:
+ type = IDLWrapperType(self.getLocation(p, 1), p[1])
+ p[0] = self.handleNullable(type, p[2])
+ return
+ except:
+ pass
+
+ type = IDLUnresolvedType(self.getLocation(p, 1), p[1])
+ p[0] = self.handleNullable(type, p[2])
+
+ def p_NonAnyTypeDate(self, p):
+ """
+ NonAnyType : DATE Null
+ """
+ p[0] = self.handleNullable(BuiltinTypes[IDLBuiltinType.Types.date],
+ p[2])
+
+ def p_ConstType(self, p):
+ """
+ ConstType : PrimitiveOrStringType Null
+ """
+ type = BuiltinTypes[p[1]]
+ p[0] = self.handleNullable(type, p[2])
+
+ def p_ConstTypeIdentifier(self, p):
+ """
+ ConstType : IDENTIFIER Null
+ """
+ identifier = IDLUnresolvedIdentifier(self.getLocation(p, 1), p[1])
+
+ type = IDLUnresolvedType(self.getLocation(p, 1), identifier)
+ p[0] = self.handleNullable(type, p[2])
+
+ 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_PrimitiveOrStringTypeUSVString(self, p):
+ """
+ PrimitiveOrStringType : USVSTRING
+ """
+ p[0] = IDLBuiltinType.Types.usvstring
+
+ def p_UnsignedIntegerTypeUnsigned(self, p):
+ """
+ UnsignedIntegerType : UNSIGNED IntegerType
+ """
+ # Adding one to a given signed integer type gets you the unsigned type:
+ p[0] = p[2] + 1
+
+ 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_Null(self, p):
+ """
+ Null : QUESTIONMARK
+ |
+ """
+ if len(p) > 1:
+ p[0] = self.getLocation(p, 1)
+ else:
+ p[0] = None
+
+ 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()
+ try:
+ 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'
+ )
+ finally:
+ 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")
+ # And we add the special-cased "System" global name, which
+ # doesn't have any corresponding interfaces.
+ self._globalScope.globalNames.add("System")
+ self._globalScope.globalNameMapping["System"].add("BackstagePass")
+ 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 = IDLTypedef(BuiltinLocation("<builtin type>"), scope, builtin, name)
+
+ @ staticmethod
+ def handleNullable(type, questionMarkLocation):
+ if questionMarkLocation is not None:
+ type = IDLNullableType(questionMarkLocation, 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):
+ # If we have interfaces that are iterable, create their
+ # iterator interfaces and add them to the productions array.
+ interfaceStatements = []
+ for p in self._productions:
+ if isinstance(p, IDLInterface):
+ interfaceStatements.append(p)
+ if p.identifier.name == "Navigator":
+ navigatorInterface = p
+
+ iterableIteratorIface = None
+ for iface in interfaceStatements:
+ navigatorProperty = iface.getNavigatorProperty()
+ if navigatorProperty:
+ # We're generating a partial interface to add a readonly
+ # property to the Navigator interface for every interface
+ # annotated with NavigatorProperty.
+ partialInterface = IDLPartialInterfaceOrNamespace(
+ iface.location,
+ IDLUnresolvedIdentifier(iface.location, "Navigator"),
+ [ navigatorProperty ],
+ navigatorInterface)
+ self._productions.append(partialInterface)
+
+ iterable = None
+ # We haven't run finish() on the interface yet, so we don't know
+ # whether our interface is maplike/setlike/iterable or not. This
+ # means we have to loop through the members to see if we have an
+ # iterable member.
+ for m in iface.members:
+ if isinstance(m, IDLIterable):
+ iterable = m
+ break
+ if iterable and iterable.isPairIterator():
+ def simpleExtendedAttr(str):
+ return IDLExtendedAttribute(iface.location, (str, ))
+ nextMethod = IDLMethod(
+ iface.location,
+ IDLUnresolvedIdentifier(iface.location, "next"),
+ BuiltinTypes[IDLBuiltinType.Types.object], [])
+ nextMethod.addExtendedAttributes([simpleExtendedAttr("Throws")])
+ itr_ident = IDLUnresolvedIdentifier(iface.location,
+ iface.identifier.name + "Iterator")
+ itr_iface = IDLInterface(iface.location, self.globalScope(),
+ itr_ident, None, [nextMethod],
+ isKnownNonPartial=True)
+ itr_iface.addExtendedAttributes([simpleExtendedAttr("NoInterfaceObject")])
+ # Make sure the exposure set for the iterator interface is the
+ # same as the exposure set for the iterable interface, because
+ # we're going to generate methods on the iterable that return
+ # instances of the iterator.
+ itr_iface._exposureGlobalNames = set(iface._exposureGlobalNames)
+ # Always append generated iterable interfaces after the
+ # interface they're a member of, otherwise nativeType generation
+ # won't work correctly.
+ itr_iface.iterableInterface = iface
+ self._productions.append(itr_iface)
+ iterable.iteratorType = IDLWrapperType(iface.location, itr_iface)
+
+ # Then, 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;
+ typedef (ArrayBufferView or ArrayBuffer) BufferSource;
+ """
+
+
+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/dom/bindings/parser/runtests.py b/dom/bindings/parser/runtests.py
new file mode 100644
index 000000000..33bb59f8a
--- /dev/null
+++ b/dom/bindings/parser/runtests.py
@@ -0,0 +1,108 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.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 argparse
+import traceback
+import WebIDL
+
+class TestHarness(object):
+ def __init__(self, test, verbose):
+ self.test = test
+ self.verbose = verbose
+ self.printed_intro = False
+ self.passed = 0
+ self.failures = []
+
+ 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):
+ self.passed += 1
+ if self.verbose:
+ print "TEST-PASS | %s" % msg
+
+ def test_fail(self, msg):
+ self.maybe_print_intro()
+ self.failures.append(msg)
+ 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 + " | Got %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)
+
+ all_passed = 0
+ failed_tests = []
+
+ 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:
+ harness.test_fail("Unhandled exception in test %s: %s" %
+ (testpath, ex))
+ traceback.print_exc()
+ finally:
+ harness.finish()
+ all_passed += harness.passed
+ if harness.failures:
+ failed_tests.append((test, harness.failures))
+
+ if verbose or failed_tests:
+ print
+ print 'Result summary:'
+ print 'Successful: %d' % all_passed
+ print 'Unexpected: %d' % \
+ sum(len(failures) for _, failures in failed_tests)
+ for test, failures in failed_tests:
+ print '%s:' % test
+ for failure in failures:
+ print 'TEST-UNEXPECTED-FAIL | %s' % failure
+
+def get_parser():
+ usage = """%(prog)s [OPTIONS] [TESTS]
+ Where TESTS are relative to the tests directory."""
+ parser = argparse.ArgumentParser(usage=usage)
+ parser.add_argument('-q', '--quiet', action='store_false', dest='verbose',
+ help="Don't print passing tests.", default=None)
+ parser.add_argument('-v', '--verbose', action='store_true', dest='verbose',
+ help="Run tests in verbose mode.")
+ parser.add_argument('tests', nargs="*", help="Tests to run")
+ return parser
+
+if __name__ == '__main__':
+ parser = get_parser()
+ args = parser.parse_args()
+ if args.verbose is None:
+ args.verbose = True
+ run_tests(args.tests, verbose=args.verbose)
diff --git a/dom/bindings/parser/tests/test_any_null.py b/dom/bindings/parser/tests/test_any_null.py
new file mode 100644
index 000000000..e3b690bf6
--- /dev/null
+++ b/dom/bindings/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/dom/bindings/parser/tests/test_argument_identifier_conflicts.py b/dom/bindings/parser/tests/test_argument_identifier_conflicts.py
new file mode 100644
index 000000000..eb1f6d3c9
--- /dev/null
+++ b/dom/bindings/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/dom/bindings/parser/tests/test_argument_novoid.py b/dom/bindings/parser/tests/test_argument_novoid.py
new file mode 100644
index 000000000..ef8c2229a
--- /dev/null
+++ b/dom/bindings/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/dom/bindings/parser/tests/test_arraybuffer.py b/dom/bindings/parser/tests/test_arraybuffer.py
new file mode 100644
index 000000000..4a96c0ff5
--- /dev/null
+++ b/dom/bindings/parser/tests/test_arraybuffer.py
@@ -0,0 +1,81 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface TestArrayBuffer {
+ attribute ArrayBuffer bufferAttr;
+ void bufferMethod(ArrayBuffer arg1, ArrayBuffer? arg2, sequence<ArrayBuffer> arg3);
+
+ attribute ArrayBufferView viewAttr;
+ void viewMethod(ArrayBufferView arg1, ArrayBufferView? arg2, sequence<ArrayBufferView> arg3);
+
+ attribute Int8Array int8ArrayAttr;
+ void int8ArrayMethod(Int8Array arg1, Int8Array? arg2, sequence<Int8Array> arg3);
+
+ attribute Uint8Array uint8ArrayAttr;
+ void uint8ArrayMethod(Uint8Array arg1, Uint8Array? arg2, sequence<Uint8Array> arg3);
+
+ attribute Uint8ClampedArray uint8ClampedArrayAttr;
+ void uint8ClampedArrayMethod(Uint8ClampedArray arg1, Uint8ClampedArray? arg2, sequence<Uint8ClampedArray> arg3);
+
+ attribute Int16Array int16ArrayAttr;
+ void int16ArrayMethod(Int16Array arg1, Int16Array? arg2, sequence<Int16Array> arg3);
+
+ attribute Uint16Array uint16ArrayAttr;
+ void uint16ArrayMethod(Uint16Array arg1, Uint16Array? arg2, sequence<Uint16Array> arg3);
+
+ attribute Int32Array int32ArrayAttr;
+ void int32ArrayMethod(Int32Array arg1, Int32Array? arg2, sequence<Int32Array> arg3);
+
+ attribute Uint32Array uint32ArrayAttr;
+ void uint32ArrayMethod(Uint32Array arg1, Uint32Array? arg2, sequence<Uint32Array> arg3);
+
+ attribute Float32Array float32ArrayAttr;
+ void float32ArrayMethod(Float32Array arg1, Float32Array? arg2, sequence<Float32Array> arg3);
+
+ attribute Float64Array float64ArrayAttr;
+ void float64ArrayMethod(Float64Array arg1, Float64Array? arg2, sequence<Float64Array> arg3);
+ };
+ """)
+
+ 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), 3, "Expect 3 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 + "Sequence", "Expect an ArrayBuffer type")
+ harness.ok(arguments[2].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/dom/bindings/parser/tests/test_attr.py b/dom/bindings/parser/tests/test_attr.py
new file mode 100644
index 000000000..ad7aabc19
--- /dev/null
+++ b/dom/bindings/parser/tests/test_attr.py
@@ -0,0 +1,177 @@
+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;
+ };
+ """)
+
+ 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), 2, "Should be two 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)
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A {
+ [SetterThrows] readonly attribute boolean foo;
+ };
+ """)
+ results = parser.finish()
+ except Exception, x:
+ threw = True
+ harness.ok(threw, "Should not allow [SetterThrows] on readonly attributes")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A {
+ [Throw] readonly attribute boolean foo;
+ };
+ """)
+ results = parser.finish()
+ except Exception, x:
+ threw = True
+ harness.ok(threw, "Should spell [Throws] correctly")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A {
+ [SameObject] readonly attribute boolean foo;
+ };
+ """)
+ results = parser.finish()
+ except Exception, x:
+ threw = True
+ harness.ok(threw, "Should not allow [SameObject] on attributes not of interface type")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A {
+ [SameObject] readonly attribute A foo;
+ };
+ """)
+ results = parser.finish()
+ except Exception, x:
+ threw = True
+ harness.ok(not threw, "Should allow [SameObject] on attributes of interface type")
diff --git a/dom/bindings/parser/tests/test_attr_sequence_type.py b/dom/bindings/parser/tests/test_attr_sequence_type.py
new file mode 100644
index 000000000..fb1b97812
--- /dev/null
+++ b/dom/bindings/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/dom/bindings/parser/tests/test_builtin_filename.py b/dom/bindings/parser/tests/test_builtin_filename.py
new file mode 100644
index 000000000..631e52eba
--- /dev/null
+++ b/dom/bindings/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/dom/bindings/parser/tests/test_builtins.py b/dom/bindings/parser/tests/test_builtins.py
new file mode 100644
index 000000000..f8563fc2d
--- /dev/null
+++ b/dom/bindings/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/dom/bindings/parser/tests/test_bytestring.py b/dom/bindings/parser/tests/test_bytestring.py
new file mode 100644
index 000000000..fa83e9e2d
--- /dev/null
+++ b/dom/bindings/parser/tests/test_bytestring.py
@@ -0,0 +1,99 @@
+# -*- coding: UTF-8 -*-
+
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface TestByteString {
+ attribute ByteString bs;
+ attribute DOMString ds;
+ };
+ """)
+
+ results = parser.finish();
+
+ harness.ok(True, "TestByteString 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(), "::TestByteString", "Interface has the right QName")
+ harness.check(iface.identifier.name, "TestByteString", "Interface has the right name")
+ harness.check(iface.parent, None, "Interface has no parent")
+
+ members = iface.members
+ harness.check(len(members), 2, "Should be two productions")
+
+ attr = members[0]
+ harness.ok(isinstance(attr, WebIDL.IDLAttribute), "Should be an IDLAttribute")
+ harness.check(attr.identifier.QName(), "::TestByteString::bs", "Attr has correct QName")
+ harness.check(attr.identifier.name, "bs", "Attr has correct name")
+ harness.check(str(attr.type), "ByteString", "Attr type is the correct name")
+ harness.ok(attr.type.isByteString(), "Should be ByteString type")
+ harness.ok(attr.type.isString(), "Should be String collective type")
+ harness.ok(not attr.type.isDOMString(), "Should be not be DOMString type")
+
+ # now check we haven't broken DOMStrings in the process.
+ attr = members[1]
+ harness.ok(isinstance(attr, WebIDL.IDLAttribute), "Should be an IDLAttribute")
+ harness.check(attr.identifier.QName(), "::TestByteString::ds", "Attr has correct QName")
+ harness.check(attr.identifier.name, "ds", "Attr has correct name")
+ harness.check(str(attr.type), "String", "Attr type is the correct name")
+ harness.ok(attr.type.isDOMString(), "Should be DOMString type")
+ harness.ok(attr.type.isString(), "Should be String collective type")
+ harness.ok(not attr.type.isByteString(), "Should be not be ByteString type")
+
+ # Cannot represent constant ByteString in IDL.
+ threw = False
+ try:
+ parser.parse("""
+ interface ConstByteString {
+ const ByteString foo = "hello"
+ };
+ """)
+ except WebIDL.WebIDLError:
+ threw = True
+ harness.ok(threw, "Should have thrown a WebIDL error for ByteString default in interface")
+
+ # Can have optional ByteStrings with default values
+ try:
+ parser.parse("""
+ interface OptionalByteString {
+ void passByteString(optional ByteString arg = "hello");
+ };
+ """)
+ results2 = parser.finish();
+ except WebIDL.WebIDLError as e:
+ harness.ok(False,
+ "Should not have thrown a WebIDL error for ByteString "
+ "default in dictionary. " + str(e))
+
+ # Can have a default ByteString value in a dictionary
+ try:
+ parser.parse("""
+ dictionary OptionalByteStringDict {
+ ByteString item = "some string";
+ };
+ """)
+ results3 = parser.finish();
+ except WebIDL.WebIDLError as e:
+ harness.ok(False,
+ "Should not have thrown a WebIDL error for ByteString "
+ "default in dictionary. " + str(e))
+
+ # Don't allow control characters in ByteString literals
+ threw = False
+ try:
+ parser.parse("""
+ dictionary OptionalByteStringDict2 {
+ ByteString item = "\x03";
+ };
+ """)
+ results4 = parser.finish()
+ except WebIDL.WebIDLError as e:
+ threw = True
+
+ harness.ok(threw,
+ "Should have thrown a WebIDL error for invalid ByteString "
+ "default in dictionary")
diff --git a/dom/bindings/parser/tests/test_callback.py b/dom/bindings/parser/tests/test_callback.py
new file mode 100644
index 000000000..4dfda1c3c
--- /dev/null
+++ b/dom/bindings/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 two productions.")
+ 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/dom/bindings/parser/tests/test_callback_interface.py b/dom/bindings/parser/tests/test_callback_interface.py
new file mode 100644
index 000000000..e4789dae1
--- /dev/null
+++ b/dom/bindings/parser/tests/test_callback_interface.py
@@ -0,0 +1,94 @@
+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")
+
+ parser = parser.reset()
+ parser.parse("""
+ callback interface TestCallbackInterface1 {
+ void foo();
+ };
+ callback interface TestCallbackInterface2 {
+ void foo(DOMString arg);
+ void foo(TestCallbackInterface1 arg);
+ };
+ callback interface TestCallbackInterface3 {
+ void foo(DOMString arg);
+ void foo(TestCallbackInterface1 arg);
+ static void bar();
+ };
+ callback interface TestCallbackInterface4 {
+ void foo(DOMString arg);
+ void foo(TestCallbackInterface1 arg);
+ static void bar();
+ const long baz = 5;
+ };
+ callback interface TestCallbackInterface5 {
+ static attribute boolean bool;
+ void foo();
+ };
+ callback interface TestCallbackInterface6 {
+ void foo(DOMString arg);
+ void foo(TestCallbackInterface1 arg);
+ void bar();
+ };
+ callback interface TestCallbackInterface7 {
+ static attribute boolean bool;
+ };
+ callback interface TestCallbackInterface8 {
+ attribute boolean bool;
+ };
+ callback interface TestCallbackInterface9 : TestCallbackInterface1 {
+ void foo();
+ };
+ callback interface TestCallbackInterface10 : TestCallbackInterface1 {
+ void bar();
+ };
+ """)
+ results = parser.finish()
+ for (i, iface) in enumerate(results):
+ harness.check(iface.isSingleOperationInterface(), i < 4,
+ "Interface %s should be a single operation interface" %
+ iface.identifier.name)
diff --git a/dom/bindings/parser/tests/test_conditional_dictionary_member.py b/dom/bindings/parser/tests/test_conditional_dictionary_member.py
new file mode 100644
index 000000000..433b7e501
--- /dev/null
+++ b/dom/bindings/parser/tests/test_conditional_dictionary_member.py
@@ -0,0 +1,110 @@
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ dictionary Dict {
+ any foo;
+ [ChromeOnly] any bar;
+ };
+ """)
+ results = parser.finish()
+ harness.check(len(results), 1, "Should have a dictionary")
+ members = results[0].members;
+ harness.check(len(members), 2, "Should have two members")
+ # Note that members are ordered lexicographically, so "bar" comes
+ # before "foo".
+ harness.ok(members[0].getExtendedAttribute("ChromeOnly"),
+ "First member is not ChromeOnly")
+ harness.ok(not members[1].getExtendedAttribute("ChromeOnly"),
+ "Second member is ChromeOnly")
+
+ parser = parser.reset()
+ parser.parse("""
+ dictionary Dict {
+ any foo;
+ any bar;
+ };
+
+ interface Iface {
+ [Constant, Cached] readonly attribute Dict dict;
+ };
+ """)
+ results = parser.finish()
+ harness.check(len(results), 2, "Should have a dictionary and an interface")
+
+ parser = parser.reset()
+ exception = None
+ try:
+ parser.parse("""
+ dictionary Dict {
+ any foo;
+ [ChromeOnly] any bar;
+ };
+
+ interface Iface {
+ [Constant, Cached] readonly attribute Dict dict;
+ };
+ """)
+ results = parser.finish()
+ except Exception, exception:
+ pass
+
+ harness.ok(exception, "Should have thrown.")
+ harness.check(exception.message,
+ "[Cached] and [StoreInSlot] must not be used on an attribute "
+ "whose type contains a [ChromeOnly] dictionary member",
+ "Should have thrown the right exception")
+
+ parser = parser.reset()
+ exception = None
+ try:
+ parser.parse("""
+ dictionary ParentDict {
+ [ChromeOnly] any bar;
+ };
+
+ dictionary Dict : ParentDict {
+ any foo;
+ };
+
+ interface Iface {
+ [Constant, Cached] readonly attribute Dict dict;
+ };
+ """)
+ results = parser.finish()
+ except Exception, exception:
+ pass
+
+ harness.ok(exception, "Should have thrown (2).")
+ harness.check(exception.message,
+ "[Cached] and [StoreInSlot] must not be used on an attribute "
+ "whose type contains a [ChromeOnly] dictionary member",
+ "Should have thrown the right exception (2)")
+
+ parser = parser.reset()
+ exception = None
+ try:
+ parser.parse("""
+ dictionary GrandParentDict {
+ [ChromeOnly] any baz;
+ };
+
+ dictionary ParentDict : GrandParentDict {
+ any bar;
+ };
+
+ dictionary Dict : ParentDict {
+ any foo;
+ };
+
+ interface Iface {
+ [Constant, Cached] readonly attribute Dict dict;
+ };
+ """)
+ results = parser.finish()
+ except Exception, exception:
+ pass
+
+ harness.ok(exception, "Should have thrown (3).")
+ harness.check(exception.message,
+ "[Cached] and [StoreInSlot] must not be used on an attribute "
+ "whose type contains a [ChromeOnly] dictionary member",
+ "Should have thrown the right exception (3)")
diff --git a/dom/bindings/parser/tests/test_const.py b/dom/bindings/parser/tests/test_const.py
new file mode 100644
index 000000000..80b6fb0e9
--- /dev/null
+++ b/dom/bindings/parser/tests/test_const.py
@@ -0,0 +1,80 @@
+import WebIDL
+
+expected = [
+ ("::TestConsts::zero", "zero", "Byte", 0),
+ ("::TestConsts::b", "b", "Byte", -1),
+ ("::TestConsts::o", "o", "Octet", 2),
+ ("::TestConsts::s", "s", "Short", -3),
+ ("::TestConsts::us", "us", "UnsignedShort", 4),
+ ("::TestConsts::l", "l", "Long", -5),
+ ("::TestConsts::ul", "ul", "UnsignedLong", 6),
+ ("::TestConsts::ull", "ull", "UnsignedLongLong", 7),
+ ("::TestConsts::ll", "ll", "LongLong", -8),
+ ("::TestConsts::t", "t", "Boolean", True),
+ ("::TestConsts::f", "f", "Boolean", False),
+ ("::TestConsts::n", "n", "BooleanOrNull", None),
+ ("::TestConsts::nt", "nt", "BooleanOrNull", True),
+ ("::TestConsts::nf", "nf", "BooleanOrNull", False),
+ ("::TestConsts::fl", "fl", "Float", 0.2),
+ ("::TestConsts::db", "db", "Double", 0.2),
+ ("::TestConsts::ufl", "ufl", "UnrestrictedFloat", 0.2),
+ ("::TestConsts::udb", "udb", "UnrestrictedDouble", 0.2),
+ ("::TestConsts::fli", "fli", "Float", 2),
+ ("::TestConsts::dbi", "dbi", "Double", 2),
+ ("::TestConsts::ufli", "ufli", "UnrestrictedFloat", 2),
+ ("::TestConsts::udbi", "udbi", "UnrestrictedDouble", 2),
+]
+
+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;
+ const float fl = 0.2;
+ const double db = 0.2;
+ const unrestricted float ufl = 0.2;
+ const unrestricted double udb = 0.2;
+ const float fli = 2;
+ const double dbi = 2;
+ const unrestricted float ufli = 2;
+ const unrestricted double udbi = 2;
+ };
+ """)
+
+ 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), len(expected), "Expect %s members" % len(expected))
+
+ for (const, (QName, name, type, value)) in zip(iface.members, expected):
+ 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.")
+
diff --git a/dom/bindings/parser/tests/test_constructor.py b/dom/bindings/parser/tests/test_constructor.py
new file mode 100644
index 000000000..348204c7d
--- /dev/null
+++ b/dom/bindings/parser/tests/test_constructor.py
@@ -0,0 +1,109 @@
+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=True, getter=False, setter=False, creator=False,
+ deleter=False, legacycaller=False, stringifier=False,
+ chromeOnly=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(method.getExtendedAttribute("ChromeOnly") is not None, chromeOnly, "Method has the correct value for ChromeOnly")
+ 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 three productions")
+ harness.ok(isinstance(results[0], WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ harness.ok(isinstance(results[1], WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ harness.ok(isinstance(results[2], 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)])])
+
+ parser = parser.reset()
+ parser.parse("""
+ [ChromeConstructor()]
+ interface TestChromeConstructor {
+ };
+ """)
+ results = parser.finish()
+ harness.check(len(results), 1, "Should be one production")
+ harness.ok(isinstance(results[0], WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+
+ checkMethod(results[0].ctor(), "::TestChromeConstructor::constructor",
+ "constructor", [("TestChromeConstructor (Wrapper)", [])],
+ chromeOnly=True)
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ [Constructor(),
+ ChromeConstructor(DOMString a)]
+ interface TestChromeConstructor {
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Can't have both a Constructor and a ChromeConstructor")
diff --git a/dom/bindings/parser/tests/test_constructor_no_interface_object.py b/dom/bindings/parser/tests/test_constructor_no_interface_object.py
new file mode 100644
index 000000000..2b09ae71e
--- /dev/null
+++ b/dom/bindings/parser/tests/test_constructor_no_interface_object.py
@@ -0,0 +1,36 @@
+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.")
+
+ parser = parser.reset()
+
+ parser.parse("""
+ [NoInterfaceObject, NamedConstructor=FooBar]
+ interface TestNamedConstructorNoInterfaceObject {
+ };
+ """)
diff --git a/dom/bindings/parser/tests/test_date.py b/dom/bindings/parser/tests/test_date.py
new file mode 100644
index 000000000..2bdfc95e1
--- /dev/null
+++ b/dom/bindings/parser/tests/test_date.py
@@ -0,0 +1,15 @@
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface WithDates {
+ attribute Date foo;
+ void bar(Date arg);
+ void baz(sequence<Date> arg);
+ };
+ """)
+
+ results = parser.finish()
+ harness.ok(results[0].members[0].type.isDate(), "Should have Date")
+ harness.ok(results[0].members[1].signatures()[0][1][0].type.isDate(),
+ "Should have Date argument")
+ harness.ok(not results[0].members[2].signatures()[0][1][0].type.isDate(),
+ "Should have non-Date argument")
diff --git a/dom/bindings/parser/tests/test_deduplicate.py b/dom/bindings/parser/tests/test_deduplicate.py
new file mode 100644
index 000000000..6249d36fb
--- /dev/null
+++ b/dom/bindings/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/dom/bindings/parser/tests/test_dictionary.py b/dom/bindings/parser/tests/test_dictionary.py
new file mode 100644
index 000000000..2c0fa6123
--- /dev/null
+++ b/dom/bindings/parser/tests/test_dictionary.py
@@ -0,0 +1,555 @@
+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 {
+ };
+ 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 or DOMString) arg);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Trailing union arg containing a dictionary 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()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary A {
+ };
+ interface X {
+ void doFoo(A arg1, optional long arg2, long arg3);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(not threw,
+ "Dictionary arg followed by non-optional arg doesn't have to be optional")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary A {
+ };
+ interface X {
+ void doFoo((A or DOMString) arg1, optional long arg2);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Union arg containing dictionary 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(optional (A or long)? arg1);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Dictionary arg must not be in a nullable union")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary A {
+ };
+ interface X {
+ void doFoo(optional (A or long?) arg1);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Dictionary must not be in a union with a nullable type")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary A {
+ };
+ interface X {
+ void doFoo(optional (long? or A) arg1);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "A nullable type must not be in a union with a dictionary")
+
+ parser = parser.reset()
+ parser.parse("""
+ dictionary A {
+ };
+ interface X {
+ A? doFoo();
+ };
+ """)
+ results = parser.finish()
+ harness.ok(True, "Dictionary return value can be nullable")
+
+ parser = parser.reset()
+ parser.parse("""
+ dictionary A {
+ };
+ interface X {
+ void doFoo(optional A arg);
+ };
+ """)
+ results = parser.finish()
+ harness.ok(True, "Dictionary arg should actually parse")
+
+ parser = parser.reset()
+ parser.parse("""
+ dictionary A {
+ };
+ interface X {
+ void doFoo(optional (A or DOMString) arg);
+ };
+ """)
+ results = parser.finish()
+ harness.ok(True, "Union arg containing a dictionary should actually parse")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary Foo {
+ Foo foo;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Member type must not be its Dictionary.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary Foo3 : Foo {
+ short d;
+ };
+
+ dictionary Foo2 : Foo3 {
+ boolean c;
+ };
+
+ dictionary Foo1 : Foo2 {
+ long a;
+ };
+
+ dictionary Foo {
+ Foo1 b;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Member type must not be a Dictionary that "
+ "inherits from its Dictionary.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary Foo {
+ (Foo or DOMString)[]? b;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Member type must not be a Nullable type "
+ "whose inner type includes its Dictionary.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary Foo {
+ (DOMString or Foo) b;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Member type must not be a Union type, one of "
+ "whose member types includes its Dictionary.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary Foo {
+ sequence<sequence<sequence<Foo>>> c;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Member type must not be a Sequence type "
+ "whose element type includes its Dictionary.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary Foo {
+ (DOMString or Foo)[] d;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Member type must not be an Array type "
+ "whose element type includes its Dictionary.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary Foo {
+ Foo1 b;
+ };
+
+ dictionary Foo3 {
+ Foo d;
+ };
+
+ dictionary Foo2 : Foo3 {
+ short c;
+ };
+
+ dictionary Foo1 : Foo2 {
+ long a;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Member type must not be a Dictionary, one of whose "
+ "members or inherited members has a type that includes "
+ "its Dictionary.")
+
+ parser = parser.reset();
+ threw = False
+ try:
+ parser.parse("""
+ dictionary Foo {
+ };
+
+ dictionary Bar {
+ Foo? d;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Member type must not be a nullable dictionary")
+
+ parser = parser.reset();
+ parser.parse("""
+ dictionary Foo {
+ unrestricted float urFloat = 0;
+ unrestricted float urFloat2 = 1.1;
+ unrestricted float urFloat3 = -1.1;
+ unrestricted float? urFloat4 = null;
+ unrestricted float infUrFloat = Infinity;
+ unrestricted float negativeInfUrFloat = -Infinity;
+ unrestricted float nanUrFloat = NaN;
+
+ unrestricted double urDouble = 0;
+ unrestricted double urDouble2 = 1.1;
+ unrestricted double urDouble3 = -1.1;
+ unrestricted double? urDouble4 = null;
+ unrestricted double infUrDouble = Infinity;
+ unrestricted double negativeInfUrDouble = -Infinity;
+ unrestricted double nanUrDouble = NaN;
+ };
+ """)
+ results = parser.finish()
+ harness.ok(True, "Parsing default values for unrestricted types succeeded.")
+
+ parser = parser.reset();
+ threw = False
+ try:
+ parser.parse("""
+ dictionary Foo {
+ double f = Infinity;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Only unrestricted values can be initialized to Infinity")
+
+ parser = parser.reset();
+ threw = False
+ try:
+ parser.parse("""
+ dictionary Foo {
+ double f = -Infinity;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Only unrestricted values can be initialized to -Infinity")
+
+ parser = parser.reset();
+ threw = False
+ try:
+ parser.parse("""
+ dictionary Foo {
+ double f = NaN;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Only unrestricted values can be initialized to NaN")
+
+ parser = parser.reset();
+ threw = False
+ try:
+ parser.parse("""
+ dictionary Foo {
+ float f = Infinity;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Only unrestricted values can be initialized to Infinity")
+
+
+ parser = parser.reset();
+ threw = False
+ try:
+ parser.parse("""
+ dictionary Foo {
+ float f = -Infinity;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Only unrestricted values can be initialized to -Infinity")
+
+ parser = parser.reset();
+ threw = False
+ try:
+ parser.parse("""
+ dictionary Foo {
+ float f = NaN;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Only unrestricted values can be initialized to NaN")
diff --git a/dom/bindings/parser/tests/test_distinguishability.py b/dom/bindings/parser/tests/test_distinguishability.py
new file mode 100644
index 000000000..d7780c1ff
--- /dev/null
+++ b/dom/bindings/parser/tests/test_distinguishability.py
@@ -0,0 +1,293 @@
+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")
+
+ # Now let's test our whole distinguishability table
+ argTypes = [ "long", "short", "long?", "short?", "boolean",
+ "boolean?", "DOMString", "ByteString", "Enum", "Enum2",
+ "Interface", "Interface?",
+ "AncestorInterface", "UnrelatedInterface",
+ "ImplementedInterface", "CallbackInterface",
+ "CallbackInterface?", "CallbackInterface2",
+ "object", "Callback", "Callback2", "optional Dict",
+ "optional Dict2", "sequence<long>", "sequence<short>",
+ "MozMap<object>", "MozMap<Dict>", "MozMap<long>",
+ "Date", "Date?", "any",
+ "Promise<any>", "Promise<any>?",
+ "USVString", "ArrayBuffer", "ArrayBufferView", "SharedArrayBuffer",
+ "Uint8Array", "Uint16Array" ]
+ # When we can parse Date and RegExp, we need to add them here.
+
+ # Try to categorize things a bit to keep list lengths down
+ def allBut(list1, list2):
+ return [a for a in list1 if a not in list2 and
+ (a != "any" and a != "Promise<any>" and a != "Promise<any>?")]
+ numerics = [ "long", "short", "long?", "short?" ]
+ booleans = [ "boolean", "boolean?" ]
+ primitives = numerics + booleans
+ nonNumerics = allBut(argTypes, numerics)
+ nonBooleans = allBut(argTypes, booleans)
+ strings = [ "DOMString", "ByteString", "Enum", "Enum2", "USVString" ]
+ nonStrings = allBut(argTypes, strings)
+ nonObjects = primitives + strings
+ objects = allBut(argTypes, nonObjects )
+ bufferSourceTypes = ["ArrayBuffer", "ArrayBufferView", "Uint8Array", "Uint16Array"]
+ sharedBufferSourceTypes = ["SharedArrayBuffer"]
+ interfaces = [ "Interface", "Interface?", "AncestorInterface",
+ "UnrelatedInterface", "ImplementedInterface" ] + bufferSourceTypes + sharedBufferSourceTypes
+ nullables = ["long?", "short?", "boolean?", "Interface?",
+ "CallbackInterface?", "optional Dict", "optional Dict2",
+ "Date?", "any", "Promise<any>?"]
+ dates = [ "Date", "Date?" ]
+ sequences = [ "sequence<long>", "sequence<short>" ]
+ nonUserObjects = nonObjects + interfaces + dates + sequences
+ otherObjects = allBut(argTypes, nonUserObjects + ["object"])
+ notRelatedInterfaces = (nonObjects + ["UnrelatedInterface"] +
+ otherObjects + dates + sequences + bufferSourceTypes + sharedBufferSourceTypes)
+ mozMaps = [ "MozMap<object>", "MozMap<Dict>", "MozMap<long>" ]
+
+ # Build a representation of the distinguishability table as a dict
+ # of dicts, holding True values where needed, holes elsewhere.
+ data = dict();
+ for type in argTypes:
+ data[type] = dict()
+ def setDistinguishable(type, types):
+ for other in types:
+ data[type][other] = True
+
+ setDistinguishable("long", nonNumerics)
+ setDistinguishable("short", nonNumerics)
+ setDistinguishable("long?", allBut(nonNumerics, nullables))
+ setDistinguishable("short?", allBut(nonNumerics, nullables))
+ setDistinguishable("boolean", nonBooleans)
+ setDistinguishable("boolean?", allBut(nonBooleans, nullables))
+ setDistinguishable("DOMString", nonStrings)
+ setDistinguishable("ByteString", nonStrings)
+ setDistinguishable("USVString", nonStrings)
+ setDistinguishable("Enum", nonStrings)
+ setDistinguishable("Enum2", nonStrings)
+ setDistinguishable("Interface", notRelatedInterfaces)
+ setDistinguishable("Interface?", allBut(notRelatedInterfaces, nullables))
+ setDistinguishable("AncestorInterface", notRelatedInterfaces)
+ setDistinguishable("UnrelatedInterface",
+ allBut(argTypes, ["object", "UnrelatedInterface"]))
+ setDistinguishable("ImplementedInterface", notRelatedInterfaces)
+ setDistinguishable("CallbackInterface", nonUserObjects)
+ setDistinguishable("CallbackInterface?", allBut(nonUserObjects, nullables))
+ setDistinguishable("CallbackInterface2", nonUserObjects)
+ setDistinguishable("object", nonObjects)
+ setDistinguishable("Callback", nonUserObjects)
+ setDistinguishable("Callback2", nonUserObjects)
+ setDistinguishable("optional Dict", allBut(nonUserObjects, nullables))
+ setDistinguishable("optional Dict2", allBut(nonUserObjects, nullables))
+ setDistinguishable("sequence<long>",
+ allBut(argTypes, sequences + ["object"]))
+ setDistinguishable("sequence<short>",
+ allBut(argTypes, sequences + ["object"]))
+ setDistinguishable("MozMap<object>", nonUserObjects)
+ setDistinguishable("MozMap<Dict>", nonUserObjects)
+ setDistinguishable("MozMap<long>", nonUserObjects)
+ setDistinguishable("Date", allBut(argTypes, dates + ["object"]))
+ setDistinguishable("Date?", allBut(argTypes, dates + nullables + ["object"]))
+ setDistinguishable("any", [])
+ setDistinguishable("Promise<any>", [])
+ setDistinguishable("Promise<any>?", [])
+ setDistinguishable("ArrayBuffer", allBut(argTypes, ["ArrayBuffer", "object"]))
+ setDistinguishable("ArrayBufferView", allBut(argTypes, ["ArrayBufferView", "Uint8Array", "Uint16Array", "object"]))
+ setDistinguishable("Uint8Array", allBut(argTypes, ["ArrayBufferView", "Uint8Array", "object"]))
+ setDistinguishable("Uint16Array", allBut(argTypes, ["ArrayBufferView", "Uint16Array", "object"]))
+ setDistinguishable("SharedArrayBuffer", allBut(argTypes, ["SharedArrayBuffer", "object"]))
+
+ def areDistinguishable(type1, type2):
+ return data[type1].get(type2, False)
+
+ def checkDistinguishability(parser, type1, type2):
+ idlTemplate = """
+ enum Enum { "a", "b" };
+ enum Enum2 { "c", "d" };
+ interface Interface : AncestorInterface {};
+ interface AncestorInterface {};
+ interface UnrelatedInterface {};
+ interface ImplementedInterface {};
+ Interface implements ImplementedInterface;
+ callback interface CallbackInterface {};
+ callback interface CallbackInterface2 {};
+ callback Callback = any();
+ callback Callback2 = long(short arg);
+ dictionary Dict {};
+ dictionary Dict2 {};
+ interface _Promise {};
+ interface TestInterface {%s
+ };
+ """
+ methodTemplate = """
+ void myMethod(%s arg);"""
+ methods = (methodTemplate % type1) + (methodTemplate % type2)
+ idl = idlTemplate % methods
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(idl)
+ results = parser.finish()
+ except:
+ threw = True
+
+ if areDistinguishable(type1, type2):
+ harness.ok(not threw,
+ "Should not throw for '%s' and '%s' because they are distinguishable" % (type1, type2))
+ else:
+ harness.ok(threw,
+ "Should throw for '%s' and '%s' because they are not distinguishable" % (type1, type2))
+
+ # Enumerate over everything in both orders, since order matters in
+ # terms of our implementation of distinguishability checks
+ for type1 in argTypes:
+ for type2 in argTypes:
+ checkDistinguishability(parser, type1, type2)
diff --git a/dom/bindings/parser/tests/test_double_null.py b/dom/bindings/parser/tests/test_double_null.py
new file mode 100644
index 000000000..700c7eade
--- /dev/null
+++ b/dom/bindings/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/dom/bindings/parser/tests/test_duplicate_qualifiers.py b/dom/bindings/parser/tests/test_duplicate_qualifiers.py
new file mode 100644
index 000000000..799f2e0e0
--- /dev/null
+++ b/dom/bindings/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/dom/bindings/parser/tests/test_empty_enum.py b/dom/bindings/parser/tests/test_empty_enum.py
new file mode 100644
index 000000000..ee0079f06
--- /dev/null
+++ b/dom/bindings/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/dom/bindings/parser/tests/test_empty_sequence_default_value.py b/dom/bindings/parser/tests/test_empty_sequence_default_value.py
new file mode 100644
index 000000000..350ae72f0
--- /dev/null
+++ b/dom/bindings/parser/tests/test_empty_sequence_default_value.py
@@ -0,0 +1,45 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface X {
+ const sequence<long> foo = [];
+ };
+ """)
+
+ results = parser.finish()
+ except Exception,x:
+ threw = True
+
+ harness.ok(threw, "Constant cannot have [] as a default value")
+
+ parser = parser.reset()
+
+ parser.parse("""
+ interface X {
+ void foo(optional sequence<long> arg = []);
+ };
+ """)
+ results = parser.finish();
+
+ harness.ok(isinstance(
+ results[0].members[0].signatures()[0][1][0].defaultValue,
+ WebIDL.IDLEmptySequenceValue),
+ "Should have IDLEmptySequenceValue as default value of argument")
+
+ parser = parser.reset()
+
+ parser.parse("""
+ dictionary X {
+ sequence<long> foo = [];
+ };
+ """)
+ results = parser.finish();
+
+ harness.ok(isinstance(results[0].members[0].defaultValue,
+ WebIDL.IDLEmptySequenceValue),
+ "Should have IDLEmptySequenceValue as default value of "
+ "dictionary member")
+
diff --git a/dom/bindings/parser/tests/test_enum.py b/dom/bindings/parser/tests/test_enum.py
new file mode 100644
index 000000000..862289391
--- /dev/null
+++ b/dom/bindings/parser/tests/test_enum.py
@@ -0,0 +1,93 @@
+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")
+
+ # Now reset our parser
+ parser = parser.reset()
+ parser.parse("""
+ enum Enum {
+ "a",
+ "b",
+ "c",
+ };
+ """)
+ results = parser.finish()
+ harness.check(len(results), 1, "Should allow trailing comma in enum")
diff --git a/dom/bindings/parser/tests/test_enum_duplicate_values.py b/dom/bindings/parser/tests/test_enum_duplicate_values.py
new file mode 100644
index 000000000..51205d209
--- /dev/null
+++ b/dom/bindings/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/dom/bindings/parser/tests/test_error_colno.py b/dom/bindings/parser/tests/test_error_colno.py
new file mode 100644
index 000000000..ca0674aec
--- /dev/null
+++ b/dom/bindings/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/dom/bindings/parser/tests/test_error_lineno.py b/dom/bindings/parser/tests/test_error_lineno.py
new file mode 100644
index 000000000..f11222e7a
--- /dev/null
+++ b/dom/bindings/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/dom/bindings/parser/tests/test_exposed_extended_attribute.py b/dom/bindings/parser/tests/test_exposed_extended_attribute.py
new file mode 100644
index 000000000..48957098b
--- /dev/null
+++ b/dom/bindings/parser/tests/test_exposed_extended_attribute.py
@@ -0,0 +1,222 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ [PrimaryGlobal] interface Foo {};
+ [Global=(Bar1,Bar2)] interface Bar {};
+ [Global=Baz2] interface Baz {};
+
+ [Exposed=(Foo,Bar1)]
+ interface Iface {
+ void method1();
+
+ [Exposed=Bar1]
+ readonly attribute any attr;
+ };
+
+ [Exposed=Foo]
+ partial interface Iface {
+ void method2();
+ };
+ """)
+
+ results = parser.finish()
+
+ harness.check(len(results), 5, "Should know about five things");
+ iface = results[3]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface),
+ "Should have an interface here");
+ members = iface.members
+ harness.check(len(members), 3, "Should have three members")
+
+ harness.ok(members[0].exposureSet == set(["Foo", "Bar"]),
+ "method1 should have the right exposure set")
+ harness.ok(members[0]._exposureGlobalNames == set(["Foo", "Bar1"]),
+ "method1 should have the right exposure global names")
+
+ harness.ok(members[1].exposureSet == set(["Bar"]),
+ "attr should have the right exposure set")
+ harness.ok(members[1]._exposureGlobalNames == set(["Bar1"]),
+ "attr should have the right exposure global names")
+
+ harness.ok(members[2].exposureSet == set(["Foo"]),
+ "method2 should have the right exposure set")
+ harness.ok(members[2]._exposureGlobalNames == set(["Foo"]),
+ "method2 should have the right exposure global names")
+
+ harness.ok(iface.exposureSet == set(["Foo", "Bar"]),
+ "Iface should have the right exposure set")
+ harness.ok(iface._exposureGlobalNames == set(["Foo", "Bar1"]),
+ "Iface should have the right exposure global names")
+
+ parser = parser.reset()
+ parser.parse("""
+ [PrimaryGlobal] interface Foo {};
+ [Global=(Bar1,Bar2)] interface Bar {};
+ [Global=Baz2] interface Baz {};
+
+ interface Iface2 {
+ void method3();
+ };
+ """)
+ results = parser.finish()
+
+ harness.check(len(results), 4, "Should know about four things");
+ iface = results[3]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface),
+ "Should have an interface here");
+ members = iface.members
+ harness.check(len(members), 1, "Should have one member")
+
+ harness.ok(members[0].exposureSet == set(["Foo"]),
+ "method3 should have the right exposure set")
+ harness.ok(members[0]._exposureGlobalNames == set(["Foo"]),
+ "method3 should have the right exposure global names")
+
+ harness.ok(iface.exposureSet == set(["Foo"]),
+ "Iface2 should have the right exposure set")
+ harness.ok(iface._exposureGlobalNames == set(["Foo"]),
+ "Iface2 should have the right exposure global names")
+
+ parser = parser.reset()
+ parser.parse("""
+ [PrimaryGlobal] interface Foo {};
+ [Global=(Bar1,Bar2)] interface Bar {};
+ [Global=Baz2] interface Baz {};
+
+ [Exposed=Foo]
+ interface Iface3 {
+ void method4();
+ };
+
+ [Exposed=(Foo,Bar1)]
+ interface Mixin {
+ void method5();
+ };
+
+ Iface3 implements Mixin;
+ """)
+ results = parser.finish()
+ harness.check(len(results), 6, "Should know about six things");
+ iface = results[3]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface),
+ "Should have an interface here");
+ members = iface.members
+ harness.check(len(members), 2, "Should have two members")
+
+ harness.ok(members[0].exposureSet == set(["Foo"]),
+ "method4 should have the right exposure set")
+ harness.ok(members[0]._exposureGlobalNames == set(["Foo"]),
+ "method4 should have the right exposure global names")
+
+ harness.ok(members[1].exposureSet == set(["Foo", "Bar"]),
+ "method5 should have the right exposure set")
+ harness.ok(members[1]._exposureGlobalNames == set(["Foo", "Bar1"]),
+ "method5 should have the right exposure global names")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ [Exposed=Foo]
+ interface Bar {
+ };
+ """)
+
+ results = parser.finish()
+ except Exception,x:
+ threw = True
+
+ harness.ok(threw, "Should have thrown on invalid Exposed value on interface.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface Bar {
+ [Exposed=Foo]
+ readonly attribute bool attr;
+ };
+ """)
+
+ results = parser.finish()
+ except Exception,x:
+ threw = True
+
+ harness.ok(threw, "Should have thrown on invalid Exposed value on attribute.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface Bar {
+ [Exposed=Foo]
+ void operation();
+ };
+ """)
+
+ results = parser.finish()
+ except Exception,x:
+ threw = True
+
+ harness.ok(threw, "Should have thrown on invalid Exposed value on operation.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface Bar {
+ [Exposed=Foo]
+ const long constant = 5;
+ };
+ """)
+
+ results = parser.finish()
+ except Exception,x:
+ threw = True
+
+ harness.ok(threw, "Should have thrown on invalid Exposed value on constant.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ [Global] interface Foo {};
+ [Global] interface Bar {};
+
+ [Exposed=Foo]
+ interface Baz {
+ [Exposed=Bar]
+ void method();
+ };
+ """)
+
+ results = parser.finish()
+ except Exception,x:
+ threw = True
+
+ harness.ok(threw, "Should have thrown on member exposed where its interface is not.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ [Global] interface Foo {};
+ [Global] interface Bar {};
+
+ [Exposed=Foo]
+ interface Baz {
+ void method();
+ };
+
+ [Exposed=Bar]
+ interface Mixin {};
+
+ Baz implements Mixin;
+ """)
+
+ results = parser.finish()
+ except Exception,x:
+ threw = True
+
+ harness.ok(threw, "Should have thrown on LHS of implements being exposed where RHS is not.")
diff --git a/dom/bindings/parser/tests/test_extended_attributes.py b/dom/bindings/parser/tests/test_extended_attributes.py
new file mode 100644
index 000000000..85a70d98f
--- /dev/null
+++ b/dom/bindings/parser/tests/test_extended_attributes.py
@@ -0,0 +1,107 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ [NoInterfaceObject]
+ interface TestExtendedAttr {
+ [Unforgeable] readonly attribute byte b;
+ };
+ """)
+
+ results = parser.finish()
+
+ parser = parser.reset()
+ parser.parse("""
+ [Pref="foo.bar",Pref=flop]
+ interface TestExtendedAttr {
+ [Pref="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/dom/bindings/parser/tests/test_float_types.py b/dom/bindings/parser/tests/test_float_types.py
new file mode 100644
index 000000000..718f09c11
--- /dev/null
+++ b/dom/bindings/parser/tests/test_float_types.py
@@ -0,0 +1,125 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ typedef float myFloat;
+ typedef unrestricted float myUnrestrictedFloat;
+ interface FloatTypes {
+ attribute float f;
+ attribute unrestricted float uf;
+ attribute double d;
+ attribute unrestricted double ud;
+ [LenientFloat]
+ attribute float lf;
+ [LenientFloat]
+ attribute double ld;
+
+ void m1(float arg1, double arg2, float? arg3, double? arg4,
+ myFloat arg5, unrestricted float arg6,
+ unrestricted double arg7, unrestricted float? arg8,
+ unrestricted double? arg9, myUnrestrictedFloat arg10);
+ [LenientFloat]
+ void m2(float arg1, double arg2, float? arg3, double? arg4,
+ myFloat arg5, unrestricted float arg6,
+ unrestricted double arg7, unrestricted float? arg8,
+ unrestricted double? arg9, myUnrestrictedFloat arg10);
+ [LenientFloat]
+ void m3(float arg);
+ [LenientFloat]
+ void m4(double arg);
+ [LenientFloat]
+ void m5((float or FloatTypes) arg);
+ [LenientFloat]
+ void m6(sequence<float> arg);
+ };
+ """)
+
+ results = parser.finish()
+
+ harness.check(len(results), 3, "Should be two typedefs and one interface.")
+ iface = results[2]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ types = [a.type for a in iface.members if a.isAttr()]
+ harness.ok(types[0].isFloat(), "'float' is a float")
+ harness.ok(not types[0].isUnrestricted(), "'float' is not unrestricted")
+ harness.ok(types[1].isFloat(), "'unrestricted float' is a float")
+ harness.ok(types[1].isUnrestricted(), "'unrestricted float' is unrestricted")
+ harness.ok(types[2].isFloat(), "'double' is a float")
+ harness.ok(not types[2].isUnrestricted(), "'double' is not unrestricted")
+ harness.ok(types[3].isFloat(), "'unrestricted double' is a float")
+ harness.ok(types[3].isUnrestricted(), "'unrestricted double' is unrestricted")
+
+ method = iface.members[6]
+ harness.ok(isinstance(method, WebIDL.IDLMethod), "Should be an IDLMethod")
+ argtypes = [a.type for a in method.signatures()[0][1]]
+ for (idx, type) in enumerate(argtypes):
+ harness.ok(type.isFloat(), "Type %d should be float" % idx)
+ harness.check(type.isUnrestricted(), idx >= 5,
+ "Type %d should %sbe unrestricted" % (
+ idx, "" if idx >= 4 else "not "))
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface FloatTypes {
+ [LenientFloat]
+ long m(float arg);
+ };
+ """)
+ except Exception, x:
+ threw = True
+ harness.ok(threw, "[LenientFloat] only allowed on void methods")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface FloatTypes {
+ [LenientFloat]
+ void m(unrestricted float arg);
+ };
+ """)
+ except Exception, x:
+ threw = True
+ harness.ok(threw, "[LenientFloat] only allowed on methods with unrestricted float args")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface FloatTypes {
+ [LenientFloat]
+ void m(sequence<unrestricted float> arg);
+ };
+ """)
+ except Exception, x:
+ threw = True
+ harness.ok(threw, "[LenientFloat] only allowed on methods with unrestricted float args (2)")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface FloatTypes {
+ [LenientFloat]
+ void m((unrestricted float or FloatTypes) arg);
+ };
+ """)
+ except Exception, x:
+ threw = True
+ harness.ok(threw, "[LenientFloat] only allowed on methods with unrestricted float args (3)")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface FloatTypes {
+ [LenientFloat]
+ readonly attribute float foo;
+ };
+ """)
+ except Exception, x:
+ threw = True
+ harness.ok(threw, "[LenientFloat] only allowed on writable attributes")
diff --git a/dom/bindings/parser/tests/test_forward_decl.py b/dom/bindings/parser/tests/test_forward_decl.py
new file mode 100644
index 000000000..cac24c832
--- /dev/null
+++ b/dom/bindings/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/dom/bindings/parser/tests/test_global_extended_attr.py b/dom/bindings/parser/tests/test_global_extended_attr.py
new file mode 100644
index 000000000..c752cecd2
--- /dev/null
+++ b/dom/bindings/parser/tests/test_global_extended_attr.py
@@ -0,0 +1,122 @@
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ [Global]
+ interface Foo : Bar {
+ getter any(DOMString name);
+ };
+ interface Bar {};
+ """)
+
+ results = parser.finish()
+
+ harness.ok(results[0].isOnGlobalProtoChain(),
+ "[Global] interface should be on global's proto chain")
+ harness.ok(results[1].isOnGlobalProtoChain(),
+ "[Global] interface should be on global's proto chain")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ [Global]
+ interface Foo {
+ getter any(DOMString name);
+ setter void(DOMString name, any arg);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Should have thrown for [Global] used on an interface with a "
+ "named setter")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ [Global]
+ interface Foo {
+ getter any(DOMString name);
+ creator void(DOMString name, any arg);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Should have thrown for [Global] used on an interface with a "
+ "named creator")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ [Global]
+ interface Foo {
+ getter any(DOMString name);
+ deleter void(DOMString name);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Should have thrown for [Global] used on an interface with a "
+ "named deleter")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ [Global, OverrideBuiltins]
+ interface Foo {
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Should have thrown for [Global] used on an interface with a "
+ "[OverrideBuiltins]")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ [Global]
+ interface Foo : Bar {
+ };
+ [OverrideBuiltins]
+ interface Bar {
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Should have thrown for [Global] used on an interface with an "
+ "[OverrideBuiltins] ancestor")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ [Global]
+ interface Foo {
+ };
+ interface Bar : Foo {
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Should have thrown for [Global] used on an interface with a "
+ "descendant")
diff --git a/dom/bindings/parser/tests/test_identifier_conflict.py b/dom/bindings/parser/tests/test_identifier_conflict.py
new file mode 100644
index 000000000..b510a30c0
--- /dev/null
+++ b/dom/bindings/parser/tests/test_identifier_conflict.py
@@ -0,0 +1,39 @@
+# Import the WebIDL module, so we can do isinstance checks and whatnot
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ try:
+ parser.parse("""
+ enum Foo { "a" };
+ interface Foo;
+ """)
+ results = parser.finish()
+ harness.ok(False, "Should fail to parse")
+ except Exception, e:
+ harness.ok("Name collision" in e.message,
+ "Should have name collision for interface")
+
+ parser = parser.reset()
+ try:
+ parser.parse("""
+ dictionary Foo { long x; };
+ enum Foo { "a" };
+ """)
+ results = parser.finish()
+ harness.ok(False, "Should fail to parse")
+ except Exception, e:
+ harness.ok("Name collision" in e.message,
+ "Should have name collision for dictionary")
+
+ parser = parser.reset()
+ try:
+ parser.parse("""
+ enum Foo { "a" };
+ enum Foo { "b" };
+ """)
+ results = parser.finish()
+ harness.ok(False, "Should fail to parse")
+ except Exception, e:
+ harness.ok("Multiple unresolvable definitions" in e.message,
+ "Should have name collision for dictionary")
+
diff --git a/dom/bindings/parser/tests/test_implements.py b/dom/bindings/parser/tests/test_implements.py
new file mode 100644
index 000000000..04c47d92a
--- /dev/null
+++ b/dom/bindings/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/dom/bindings/parser/tests/test_incomplete_parent.py b/dom/bindings/parser/tests/test_incomplete_parent.py
new file mode 100644
index 000000000..1f520a28e
--- /dev/null
+++ b/dom/bindings/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/dom/bindings/parser/tests/test_incomplete_types.py b/dom/bindings/parser/tests/test_incomplete_types.py
new file mode 100644
index 000000000..fdc396040
--- /dev/null
+++ b/dom/bindings/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/dom/bindings/parser/tests/test_interface.py b/dom/bindings/parser/tests/test_interface.py
new file mode 100644
index 000000000..e8ed67b54
--- /dev/null
+++ b/dom/bindings/parser/tests/test_interface.py
@@ -0,0 +1,405 @@
+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")
+
+ parser = parser.reset()
+ parser.parse("""
+ [Constructor(long arg)]
+ interface A {
+ readonly attribute boolean x;
+ void foo();
+ };
+ [Constructor]
+ partial interface A {
+ readonly attribute boolean y;
+ void foo(long arg);
+ };
+ """);
+ results = parser.finish();
+ harness.check(len(results), 2,
+ "Should have two results with partial interface")
+ iface = results[0]
+ harness.check(len(iface.members), 3,
+ "Should have three members with partial interface")
+ harness.check(iface.members[0].identifier.name, "x",
+ "First member should be x with partial interface")
+ harness.check(iface.members[1].identifier.name, "foo",
+ "Second member should be foo with partial interface")
+ harness.check(len(iface.members[1].signatures()), 2,
+ "Should have two foo signatures with partial interface")
+ harness.check(iface.members[2].identifier.name, "y",
+ "Third member should be y with partial interface")
+ harness.check(len(iface.ctor().signatures()), 2,
+ "Should have two constructors with partial interface")
+
+ parser = parser.reset()
+ parser.parse("""
+ [Constructor]
+ partial interface A {
+ readonly attribute boolean y;
+ void foo(long arg);
+ };
+ [Constructor(long arg)]
+ interface A {
+ readonly attribute boolean x;
+ void foo();
+ };
+ """);
+ results = parser.finish();
+ harness.check(len(results), 2,
+ "Should have two results with reversed partial interface")
+ iface = results[1]
+ harness.check(len(iface.members), 3,
+ "Should have three members with reversed partial interface")
+ harness.check(iface.members[0].identifier.name, "x",
+ "First member should be x with reversed partial interface")
+ harness.check(iface.members[1].identifier.name, "foo",
+ "Second member should be foo with reversed partial interface")
+ harness.check(len(iface.members[1].signatures()), 2,
+ "Should have two foo signatures with reversed partial interface")
+ harness.check(iface.members[2].identifier.name, "y",
+ "Third member should be y with reversed partial interface")
+ harness.check(len(iface.ctor().signatures()), 2,
+ "Should have two constructors with reversed partial interface")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A {
+ readonly attribute boolean x;
+ };
+ interface A {
+ readonly attribute boolean y;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow two non-partial interfaces with the same name")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ partial interface A {
+ readonly attribute boolean x;
+ };
+ partial interface A {
+ readonly attribute boolean y;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Must have a non-partial interface for a given name")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary A {
+ boolean x;
+ };
+ partial interface A {
+ readonly attribute boolean y;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow a name collision between partial interface "
+ "and other object")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary A {
+ boolean x;
+ };
+ interface A {
+ readonly attribute boolean y;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow a name collision between interface "
+ "and other object")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary A {
+ boolean x;
+ };
+ interface A;
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow a name collision between external interface "
+ "and other object")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A {
+ readonly attribute boolean x;
+ };
+ interface A;
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow a name collision between external interface "
+ "and interface")
+
+ parser = parser.reset()
+ parser.parse("""
+ interface A;
+ interface A;
+ """)
+ results = parser.finish()
+ harness.ok(len(results) == 1 and
+ isinstance(results[0], WebIDL.IDLExternalInterface),
+ "Should allow name collisions between external interface "
+ "declarations")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ [SomeRandomAnnotation]
+ interface A {
+ readonly attribute boolean y;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow unknown extended attributes on interfaces")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface B {};
+ [ArrayClass]
+ interface A : B {
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow [ArrayClass] on interfaces with parents")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ [ArrayClass]
+ interface A {
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(not threw,
+ "Should allow [ArrayClass] on interfaces without parents")
diff --git a/dom/bindings/parser/tests/test_interface_const_identifier_conflicts.py b/dom/bindings/parser/tests/test_interface_const_identifier_conflicts.py
new file mode 100644
index 000000000..db944e7aa
--- /dev/null
+++ b/dom/bindings/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/dom/bindings/parser/tests/test_interface_identifier_conflicts_across_members.py b/dom/bindings/parser/tests/test_interface_identifier_conflicts_across_members.py
new file mode 100644
index 000000000..1a73fb917
--- /dev/null
+++ b/dom/bindings/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/dom/bindings/parser/tests/test_interface_maplikesetlikeiterable.py b/dom/bindings/parser/tests/test_interface_maplikesetlikeiterable.py
new file mode 100644
index 000000000..ee5d870c2
--- /dev/null
+++ b/dom/bindings/parser/tests/test_interface_maplikesetlikeiterable.py
@@ -0,0 +1,691 @@
+import WebIDL
+import traceback
+def WebIDLTest(parser, harness):
+
+ def shouldPass(prefix, iface, expectedMembers, numProductions=1):
+ p = parser.reset()
+ p.parse(iface)
+ results = p.finish()
+ harness.check(len(results), numProductions,
+ "%s - Should have production count %d" % (prefix, numProductions))
+ harness.ok(isinstance(results[0], WebIDL.IDLInterface),
+ "%s - Should be an IDLInterface" % (prefix))
+ # Make a copy, since we plan to modify it
+ expectedMembers = list(expectedMembers)
+ for m in results[0].members:
+ name = m.identifier.name
+ if (name, type(m)) in expectedMembers:
+ harness.ok(True, "%s - %s - Should be a %s" % (prefix, name,
+ type(m)))
+ expectedMembers.remove((name, type(m)))
+ else:
+ harness.ok(False, "%s - %s - Unknown symbol of type %s" %
+ (prefix, name, type(m)))
+ # A bit of a hoop because we can't generate the error string if we pass
+ if len(expectedMembers) == 0:
+ harness.ok(True, "Found all the members")
+ else:
+ harness.ok(False,
+ "Expected member not found: %s of type %s" %
+ (expectedMembers[0][0], expectedMembers[0][1]))
+ return results
+
+ def shouldFail(prefix, iface):
+ try:
+ p = parser.reset()
+ p.parse(iface)
+ p.finish()
+ harness.ok(False,
+ prefix + " - Interface passed when should've failed")
+ except WebIDL.WebIDLError, e:
+ harness.ok(True,
+ prefix + " - Interface failed as expected")
+ except Exception, e:
+ harness.ok(False,
+ prefix + " - Interface failed but not as a WebIDLError exception: %s" % e)
+
+ iterableMembers = [(x, WebIDL.IDLMethod) for x in ["entries", "keys",
+ "values", "forEach"]]
+ setROMembers = ([(x, WebIDL.IDLMethod) for x in ["has"]] +
+ [("__setlike", WebIDL.IDLMaplikeOrSetlike)] +
+ iterableMembers)
+ setROMembers.extend([("size", WebIDL.IDLAttribute)])
+ setRWMembers = ([(x, WebIDL.IDLMethod) for x in ["add",
+ "clear",
+ "delete"]] +
+ setROMembers)
+ setROChromeMembers = ([(x, WebIDL.IDLMethod) for x in ["__add",
+ "__clear",
+ "__delete"]] +
+ setROMembers)
+ setRWChromeMembers = ([(x, WebIDL.IDLMethod) for x in ["__add",
+ "__clear",
+ "__delete"]] +
+ setRWMembers)
+ mapROMembers = ([(x, WebIDL.IDLMethod) for x in ["get", "has"]] +
+ [("__maplike", WebIDL.IDLMaplikeOrSetlike)] +
+ iterableMembers)
+ mapROMembers.extend([("size", WebIDL.IDLAttribute)])
+ mapRWMembers = ([(x, WebIDL.IDLMethod) for x in ["set",
+ "clear",
+ "delete"]] + mapROMembers)
+ mapRWChromeMembers = ([(x, WebIDL.IDLMethod) for x in ["__set",
+ "__clear",
+ "__delete"]] +
+ mapRWMembers)
+
+ # OK, now that we've used iterableMembers to set up the above, append
+ # __iterable to it for the iterable<> case.
+ iterableMembers.append(("__iterable", WebIDL.IDLIterable))
+
+ valueIterableMembers = [("__iterable", WebIDL.IDLIterable)]
+ valueIterableMembers.append(("__indexedgetter", WebIDL.IDLMethod))
+ valueIterableMembers.append(("length", WebIDL.IDLAttribute))
+
+ disallowedIterableNames = ["keys", "entries", "values"]
+ disallowedMemberNames = ["forEach", "has", "size"] + disallowedIterableNames
+ mapDisallowedMemberNames = ["get"] + disallowedMemberNames
+ disallowedNonMethodNames = ["clear", "delete"]
+ mapDisallowedNonMethodNames = ["set"] + disallowedNonMethodNames
+ setDisallowedNonMethodNames = ["add"] + disallowedNonMethodNames
+ unrelatedMembers = [("unrelatedAttribute", WebIDL.IDLAttribute),
+ ("unrelatedMethod", WebIDL.IDLMethod)]
+
+ #
+ # Simple Usage Tests
+ #
+
+ shouldPass("Iterable (key only)",
+ """
+ interface Foo1 {
+ iterable<long>;
+ readonly attribute unsigned long length;
+ getter long(unsigned long index);
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """, valueIterableMembers + unrelatedMembers)
+
+ shouldPass("Iterable (key only) inheriting from parent",
+ """
+ interface Foo1 : Foo2 {
+ iterable<long>;
+ readonly attribute unsigned long length;
+ getter long(unsigned long index);
+ };
+ interface Foo2 {
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """, valueIterableMembers, numProductions=2)
+
+ shouldPass("Iterable (key and value)",
+ """
+ interface Foo1 {
+ iterable<long, long>;
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """, iterableMembers + unrelatedMembers,
+ # numProductions == 2 because of the generated iterator iface,
+ numProductions=2)
+
+ shouldPass("Iterable (key and value) inheriting from parent",
+ """
+ interface Foo1 : Foo2 {
+ iterable<long, long>;
+ };
+ interface Foo2 {
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """, iterableMembers,
+ # numProductions == 3 because of the generated iterator iface,
+ numProductions=3)
+
+ shouldPass("Maplike (readwrite)",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """, mapRWMembers + unrelatedMembers)
+
+ shouldPass("Maplike (readwrite) inheriting from parent",
+ """
+ interface Foo1 : Foo2 {
+ maplike<long, long>;
+ };
+ interface Foo2 {
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """, mapRWMembers, numProductions=2)
+
+ shouldPass("Maplike (readwrite)",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """, mapRWMembers + unrelatedMembers)
+
+ shouldPass("Maplike (readwrite) inheriting from parent",
+ """
+ interface Foo1 : Foo2 {
+ maplike<long, long>;
+ };
+ interface Foo2 {
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """, mapRWMembers, numProductions=2)
+
+ shouldPass("Maplike (readonly)",
+ """
+ interface Foo1 {
+ readonly maplike<long, long>;
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """, mapROMembers + unrelatedMembers)
+
+ shouldPass("Maplike (readonly) inheriting from parent",
+ """
+ interface Foo1 : Foo2 {
+ readonly maplike<long, long>;
+ };
+ interface Foo2 {
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """, mapROMembers, numProductions=2)
+
+ shouldPass("Setlike (readwrite)",
+ """
+ interface Foo1 {
+ setlike<long>;
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """, setRWMembers + unrelatedMembers)
+
+ shouldPass("Setlike (readwrite) inheriting from parent",
+ """
+ interface Foo1 : Foo2 {
+ setlike<long>;
+ };
+ interface Foo2 {
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """, setRWMembers, numProductions=2)
+
+ shouldPass("Setlike (readonly)",
+ """
+ interface Foo1 {
+ readonly setlike<long>;
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """, setROMembers + unrelatedMembers)
+
+ shouldPass("Setlike (readonly) inheriting from parent",
+ """
+ interface Foo1 : Foo2 {
+ readonly setlike<long>;
+ };
+ interface Foo2 {
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """, setROMembers, numProductions=2)
+
+ shouldPass("Inheritance of maplike/setlike",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ };
+ interface Foo2 : Foo1 {
+ };
+ """, mapRWMembers, numProductions=2)
+
+ shouldPass("Implements with maplike/setlike",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ };
+ interface Foo2 {
+ };
+ Foo2 implements Foo1;
+ """, mapRWMembers, numProductions=3)
+
+ shouldPass("JS Implemented maplike interface",
+ """
+ [JSImplementation="@mozilla.org/dom/test-interface-js-maplike;1",
+ Constructor()]
+ interface Foo1 {
+ setlike<long>;
+ };
+ """, setRWChromeMembers)
+
+ shouldPass("JS Implemented maplike interface",
+ """
+ [JSImplementation="@mozilla.org/dom/test-interface-js-maplike;1",
+ Constructor()]
+ interface Foo1 {
+ maplike<long, long>;
+ };
+ """, mapRWChromeMembers)
+
+ #
+ # Multiple maplike/setlike tests
+ #
+
+ shouldFail("Two maplike/setlikes on same interface",
+ """
+ interface Foo1 {
+ setlike<long>;
+ maplike<long, long>;
+ };
+ """)
+
+ shouldFail("Two iterable/setlikes on same interface",
+ """
+ interface Foo1 {
+ iterable<long>;
+ maplike<long, long>;
+ };
+ """)
+
+ shouldFail("Two iterables on same interface",
+ """
+ interface Foo1 {
+ iterable<long>;
+ iterable<long, long>;
+ };
+ """)
+
+ shouldFail("Two maplike/setlikes in partials",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ };
+ partial interface Foo1 {
+ setlike<long>;
+ };
+ """)
+
+ shouldFail("Conflicting maplike/setlikes across inheritance",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ };
+ interface Foo2 : Foo1 {
+ setlike<long>;
+ };
+ """)
+
+ shouldFail("Conflicting maplike/iterable across inheritance",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ };
+ interface Foo2 : Foo1 {
+ iterable<long>;
+ };
+ """)
+
+ shouldFail("Conflicting maplike/setlikes across multistep inheritance",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ };
+ interface Foo2 : Foo1 {
+ };
+ interface Foo3 : Foo2 {
+ setlike<long>;
+ };
+ """)
+
+ shouldFail("Consequential interface with conflicting maplike/setlike",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ };
+ interface Foo2 {
+ setlike<long>;
+ };
+ Foo2 implements Foo1;
+ """)
+
+ shouldFail("Consequential interfaces with conflicting maplike/setlike",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ };
+ interface Foo2 {
+ setlike<long>;
+ };
+ interface Foo3 {
+ };
+ Foo3 implements Foo1;
+ Foo3 implements Foo2;
+ """)
+
+ #
+ # Member name collision tests
+ #
+
+ def testConflictingMembers(likeMember, conflictName, expectedMembers, methodPasses):
+ """
+ Tests for maplike/setlike member generation against conflicting member
+ names. If methodPasses is True, this means we expect the interface to
+ pass in the case of method shadowing, and expectedMembers should be the
+ list of interface members to check against on the passing interface.
+
+ """
+ if methodPasses:
+ shouldPass("Conflicting method: %s and %s" % (likeMember, conflictName),
+ """
+ interface Foo1 {
+ %s;
+ [Throws]
+ void %s(long test1, double test2, double test3);
+ };
+ """ % (likeMember, conflictName), expectedMembers)
+ else:
+ shouldFail("Conflicting method: %s and %s" % (likeMember, conflictName),
+ """
+ interface Foo1 {
+ %s;
+ [Throws]
+ void %s(long test1, double test2, double test3);
+ };
+ """ % (likeMember, conflictName))
+ # Inherited conflicting methods should ALWAYS fail
+ shouldFail("Conflicting inherited method: %s and %s" % (likeMember, conflictName),
+ """
+ interface Foo1 {
+ void %s(long test1, double test2, double test3);
+ };
+ interface Foo2 : Foo1 {
+ %s;
+ };
+ """ % (conflictName, likeMember))
+ shouldFail("Conflicting static method: %s and %s" % (likeMember, conflictName),
+ """
+ interface Foo1 {
+ %s;
+ static void %s(long test1, double test2, double test3);
+ };
+ """ % (likeMember, conflictName))
+ shouldFail("Conflicting attribute: %s and %s" % (likeMember, conflictName),
+ """
+ interface Foo1 {
+ %s
+ attribute double %s;
+ };
+ """ % (likeMember, conflictName))
+ shouldFail("Conflicting const: %s and %s" % (likeMember, conflictName),
+ """
+ interface Foo1 {
+ %s;
+ const double %s = 0;
+ };
+ """ % (likeMember, conflictName))
+ shouldFail("Conflicting static attribute: %s and %s" % (likeMember, conflictName),
+ """
+ interface Foo1 {
+ %s;
+ static attribute long %s;
+ };
+ """ % (likeMember, conflictName))
+
+ for member in disallowedIterableNames:
+ testConflictingMembers("iterable<long, long>", member, iterableMembers, False)
+ for member in mapDisallowedMemberNames:
+ testConflictingMembers("maplike<long, long>", member, mapRWMembers, False)
+ for member in disallowedMemberNames:
+ testConflictingMembers("setlike<long>", member, setRWMembers, False)
+ for member in mapDisallowedNonMethodNames:
+ testConflictingMembers("maplike<long, long>", member, mapRWMembers, True)
+ for member in setDisallowedNonMethodNames:
+ testConflictingMembers("setlike<long>", member, setRWMembers, True)
+
+ shouldPass("Inheritance of maplike/setlike with child member collision",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ };
+ interface Foo2 : Foo1 {
+ void entries();
+ };
+ """, mapRWMembers, numProductions=2)
+
+ shouldPass("Inheritance of multi-level maplike/setlike with child member collision",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ };
+ interface Foo2 : Foo1 {
+ };
+ interface Foo3 : Foo2 {
+ void entries();
+ };
+ """, mapRWMembers, numProductions=3)
+
+ shouldFail("Interface with consequential maplike/setlike interface member collision",
+ """
+ interface Foo1 {
+ void entries();
+ };
+ interface Foo2 {
+ maplike<long, long>;
+ };
+ Foo1 implements Foo2;
+ """)
+
+ shouldFail("Maplike interface with consequential interface member collision",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ };
+ interface Foo2 {
+ void entries();
+ };
+ Foo1 implements Foo2;
+ """)
+
+ shouldPass("Consequential Maplike interface with inherited interface member collision",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ };
+ interface Foo2 {
+ void entries();
+ };
+ interface Foo3 : Foo2 {
+ };
+ Foo3 implements Foo1;
+ """, mapRWMembers, numProductions=4)
+
+ shouldPass("Inherited Maplike interface with consequential interface member collision",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ };
+ interface Foo2 {
+ void entries();
+ };
+ interface Foo3 : Foo1 {
+ };
+ Foo3 implements Foo2;
+ """, mapRWMembers, numProductions=4)
+
+ shouldFail("Inheritance of name collision with child maplike/setlike",
+ """
+ interface Foo1 {
+ void entries();
+ };
+ interface Foo2 : Foo1 {
+ maplike<long, long>;
+ };
+ """)
+
+ shouldFail("Inheritance of multi-level name collision with child maplike/setlike",
+ """
+ interface Foo1 {
+ void entries();
+ };
+ interface Foo2 : Foo1 {
+ };
+ interface Foo3 : Foo2 {
+ maplike<long, long>;
+ };
+ """)
+
+ shouldPass("Inheritance of attribute collision with parent maplike/setlike",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ };
+ interface Foo2 : Foo1 {
+ attribute double size;
+ };
+ """, mapRWMembers, numProductions=2)
+
+ shouldPass("Inheritance of multi-level attribute collision with parent maplike/setlike",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ };
+ interface Foo2 : Foo1 {
+ };
+ interface Foo3 : Foo2 {
+ attribute double size;
+ };
+ """, mapRWMembers, numProductions=3)
+
+ shouldFail("Inheritance of attribute collision with child maplike/setlike",
+ """
+ interface Foo1 {
+ attribute double size;
+ };
+ interface Foo2 : Foo1 {
+ maplike<long, long>;
+ };
+ """)
+
+ shouldFail("Inheritance of multi-level attribute collision with child maplike/setlike",
+ """
+ interface Foo1 {
+ attribute double size;
+ };
+ interface Foo2 : Foo1 {
+ };
+ interface Foo3 : Foo2 {
+ maplike<long, long>;
+ };
+ """)
+
+ shouldFail("Inheritance of attribute/rw function collision with child maplike/setlike",
+ """
+ interface Foo1 {
+ attribute double set;
+ };
+ interface Foo2 : Foo1 {
+ maplike<long, long>;
+ };
+ """)
+
+ shouldFail("Inheritance of const/rw function collision with child maplike/setlike",
+ """
+ interface Foo1 {
+ const double set = 0;
+ };
+ interface Foo2 : Foo1 {
+ maplike<long, long>;
+ };
+ """)
+
+ shouldPass("Inheritance of rw function with same name in child maplike/setlike",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ };
+ interface Foo2 : Foo1 {
+ void clear();
+ };
+ """, mapRWMembers, numProductions=2)
+
+ shouldFail("Inheritance of unforgeable attribute collision with child maplike/setlike",
+ """
+ interface Foo1 {
+ [Unforgeable]
+ attribute double size;
+ };
+ interface Foo2 : Foo1 {
+ maplike<long, long>;
+ };
+ """)
+
+ shouldFail("Inheritance of multi-level unforgeable attribute collision with child maplike/setlike",
+ """
+ interface Foo1 {
+ [Unforgeable]
+ attribute double size;
+ };
+ interface Foo2 : Foo1 {
+ };
+ interface Foo3 : Foo2 {
+ maplike<long, long>;
+ };
+ """)
+
+ shouldPass("Implemented interface with readonly allowable overrides",
+ """
+ interface Foo1 {
+ readonly setlike<long>;
+ readonly attribute boolean clear;
+ };
+ """, setROMembers + [("clear", WebIDL.IDLAttribute)])
+
+ shouldPass("JS Implemented read-only interface with readonly allowable overrides",
+ """
+ [JSImplementation="@mozilla.org/dom/test-interface-js-maplike;1",
+ Constructor()]
+ interface Foo1 {
+ readonly setlike<long>;
+ readonly attribute boolean clear;
+ };
+ """, setROChromeMembers + [("clear", WebIDL.IDLAttribute)])
+
+ shouldFail("JS Implemented read-write interface with non-readwrite allowable overrides",
+ """
+ [JSImplementation="@mozilla.org/dom/test-interface-js-maplike;1",
+ Constructor()]
+ interface Foo1 {
+ setlike<long>;
+ readonly attribute boolean clear;
+ };
+ """)
+
+ r = shouldPass("Check proper override of clear/delete/set",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ long clear(long a, long b, double c, double d);
+ long set(long a, long b, double c, double d);
+ long delete(long a, long b, double c, double d);
+ };
+ """, mapRWMembers)
+
+ for m in r[0].members:
+ if m.identifier.name in ["clear", "set", "delete"]:
+ harness.ok(m.isMethod(), "%s should be a method" % m.identifier.name)
+ harness.check(m.maxArgCount, 4, "%s should have 4 arguments" % m.identifier.name)
+ harness.ok(not m.isMaplikeOrSetlikeOrIterableMethod(),
+ "%s should not be a maplike/setlike function" % m.identifier.name)
diff --git a/dom/bindings/parser/tests/test_lenientSetter.py b/dom/bindings/parser/tests/test_lenientSetter.py
new file mode 100644
index 000000000..78a9ffe9e
--- /dev/null
+++ b/dom/bindings/parser/tests/test_lenientSetter.py
@@ -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/.
+
+def should_throw(parser, harness, message, code):
+ parser = parser.reset();
+ threw = False
+ try:
+ parser.parse(code)
+ parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown: %s" % message)
+
+
+def WebIDLTest(parser, harness):
+ # The [LenientSetter] extended attribute MUST take no arguments.
+ should_throw(parser, harness, "no arguments", """
+ interface I {
+ [LenientSetter=X] readonly attribute long A;
+ };
+ """)
+
+ # An attribute with the [LenientSetter] extended attribute MUST NOT
+ # also be declared with the [PutForwards] extended attribute.
+ should_throw(parser, harness, "PutForwards", """
+ interface I {
+ [PutForwards=B, LenientSetter] readonly attribute J A;
+ };
+ interface J {
+ attribute long B;
+ };
+ """)
+
+ # An attribute with the [LenientSetter] extended attribute MUST NOT
+ # also be declared with the [Replaceable] extended attribute.
+ should_throw(parser, harness, "Replaceable", """
+ interface I {
+ [Replaceable, LenientSetter] readonly attribute J A;
+ };
+ """)
+
+ # The [LenientSetter] extended attribute MUST NOT be used on an
+ # attribute that is not read only.
+ should_throw(parser, harness, "writable attribute", """
+ interface I {
+ [LenientSetter] attribute long A;
+ };
+ """)
+
+ # The [LenientSetter] extended attribute MUST NOT be used on a
+ # static attribute.
+ should_throw(parser, harness, "static attribute", """
+ interface I {
+ [LenientSetter] static readonly attribute long A;
+ };
+ """)
diff --git a/dom/bindings/parser/tests/test_method.py b/dom/bindings/parser/tests/test_method.py
new file mode 100644
index 000000000..cf7f1b40d
--- /dev/null
+++ b/dom/bindings/parser/tests/test_method.py
@@ -0,0 +1,178 @@
+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);
+ 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), 12, "Expect 12 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::getObject",
+ "getObject", [("Object", [])])
+ checkMethod(methods[9], "::TestMethods::setObject",
+ "setObject",
+ [("Void",
+ [("::TestMethods::setObject::arg1", "arg1", "Object", False, False)])])
+ checkMethod(methods[10], "::TestMethods::setAny",
+ "setAny",
+ [("Void",
+ [("::TestMethods::setAny::arg1", "arg1", "Any", False, False)])])
+ checkMethod(methods[11], "::TestMethods::doFloats",
+ "doFloats",
+ [("Float",
+ [("::TestMethods::doFloats::arg1", "arg1", "Float", False, False)])])
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A {
+ void foo(optional float bar = 1);
+ };
+ """)
+ results = parser.finish()
+ except Exception, x:
+ threw = True
+ harness.ok(not threw, "Should allow integer to float type corecion")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A {
+ [GetterThrows] void foo();
+ };
+ """)
+ results = parser.finish()
+ except Exception, x:
+ threw = True
+ harness.ok(threw, "Should not allow [GetterThrows] on methods")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A {
+ [SetterThrows] void foo();
+ };
+ """)
+ results = parser.finish()
+ except Exception, x:
+ threw = True
+ harness.ok(threw, "Should not allow [SetterThrows] on methods")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A {
+ [Throw] void foo();
+ };
+ """)
+ results = parser.finish()
+ except Exception, x:
+ threw = True
+ harness.ok(threw, "Should spell [Throws] correctly on methods")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A {
+ void __noSuchMethod__();
+ };
+ """)
+ results = parser.finish()
+ except Exception, x:
+ threw = True
+ harness.ok(threw, "Should not allow __noSuchMethod__ methods")
diff --git a/dom/bindings/parser/tests/test_mozmap.py b/dom/bindings/parser/tests/test_mozmap.py
new file mode 100644
index 000000000..1a36fdd62
--- /dev/null
+++ b/dom/bindings/parser/tests/test_mozmap.py
@@ -0,0 +1,39 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ dictionary Dict {};
+ interface MozMapArg {
+ void foo(MozMap<Dict> arg);
+ };
+ """)
+
+ results = parser.finish()
+
+ harness.check(len(results), 2, "Should know about two things");
+ harness.ok(isinstance(results[1], WebIDL.IDLInterface),
+ "Should have an interface here");
+ members = results[1].members
+ harness.check(len(members), 1, "Should have one member")
+ harness.ok(members[0].isMethod(), "Should have method")
+ signature = members[0].signatures()[0]
+ args = signature[1]
+ harness.check(len(args), 1, "Should have one arg")
+ harness.ok(args[0].type.isMozMap(), "Should have a MozMap type here")
+ harness.ok(args[0].type.inner.isDictionary(),
+ "Should have a dictionary inner type")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface MozMapVoidArg {
+ void foo(MozMap<void> arg);
+ };
+ """)
+
+ results = parser.finish()
+ except Exception,x:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/dom/bindings/parser/tests/test_namespace.py b/dom/bindings/parser/tests/test_namespace.py
new file mode 100644
index 000000000..74533a177
--- /dev/null
+++ b/dom/bindings/parser/tests/test_namespace.py
@@ -0,0 +1,223 @@
+def WebIDLTest(parser, harness):
+ parser.parse(
+ """
+ namespace MyNamespace {
+ attribute any foo;
+ any bar();
+ };
+ """)
+
+ results = parser.finish()
+ harness.check(len(results), 1, "Should have a thing.")
+ harness.ok(results[0].isNamespace(), "Our thing should be a namespace");
+ harness.check(len(results[0].members), 2,
+ "Should have two things in our namespace")
+ harness.ok(results[0].members[0].isAttr(), "First member is attribute")
+ harness.ok(results[0].members[0].isStatic(), "Attribute should be static")
+ harness.ok(results[0].members[1].isMethod(), "Second member is method")
+ harness.ok(results[0].members[1].isStatic(), "Operation should be static")
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ namespace MyNamespace {
+ attribute any foo;
+ };
+ partial namespace MyNamespace {
+ any bar();
+ };
+ """)
+
+ results = parser.finish()
+ harness.check(len(results), 2, "Should have things.")
+ harness.ok(results[0].isNamespace(), "Our thing should be a namespace");
+ harness.check(len(results[0].members), 2,
+ "Should have two things in our namespace")
+ harness.ok(results[0].members[0].isAttr(), "First member is attribute")
+ harness.ok(results[0].members[0].isStatic(), "Attribute should be static");
+ harness.ok(results[0].members[1].isMethod(), "Second member is method")
+ harness.ok(results[0].members[1].isStatic(), "Operation should be static");
+
+ parser = parser.reset()
+ parser.parse(
+ """
+ partial namespace MyNamespace {
+ any bar();
+ };
+ namespace MyNamespace {
+ attribute any foo;
+ };
+ """)
+
+ results = parser.finish()
+ harness.check(len(results), 2, "Should have things.")
+ harness.ok(results[1].isNamespace(), "Our thing should be a namespace");
+ harness.check(len(results[1].members), 2,
+ "Should have two things in our namespace")
+ harness.ok(results[1].members[0].isAttr(), "First member is attribute")
+ harness.ok(results[1].members[0].isStatic(), "Attribute should be static");
+ harness.ok(results[1].members[1].isMethod(), "Second member is method")
+ harness.ok(results[1].members[1].isStatic(), "Operation should be static");
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ namespace MyNamespace {
+ static attribute any foo;
+ };
+ """)
+
+ results = parser.finish()
+ except Exception, x:
+ threw = True
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ namespace MyNamespace {
+ static any bar();
+ };
+ """)
+
+ results = parser.finish()
+ except Exception, x:
+ threw = True
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ namespace MyNamespace {
+ any bar();
+ };
+
+ interface MyNamespace {
+ any baz();
+ };
+ """)
+
+ results = parser.finish()
+ except Exception, x:
+ threw = True
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface MyNamespace {
+ any baz();
+ };
+
+ namespace MyNamespace {
+ any bar();
+ };
+ """)
+
+ results = parser.finish()
+ except Exception, x:
+ threw = True
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ namespace MyNamespace {
+ any baz();
+ };
+
+ namespace MyNamespace {
+ any bar();
+ };
+ """)
+
+ results = parser.finish()
+ except Exception, x:
+ threw = True
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ partial namespace MyNamespace {
+ any baz();
+ };
+
+ interface MyNamespace {
+ any bar();
+ };
+ """)
+
+ results = parser.finish()
+ except Exception, x:
+ threw = True
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ namespace MyNamespace {
+ any bar();
+ };
+
+ partial interface MyNamespace {
+ any baz();
+ };
+ """)
+
+ results = parser.finish()
+ except Exception, x:
+ threw = True
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ partial interface MyNamespace {
+ any baz();
+ };
+
+ namespace MyNamespace {
+ any bar();
+ };
+ """)
+
+ results = parser.finish()
+ except Exception, x:
+ threw = True
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface MyNamespace {
+ any bar();
+ };
+
+ partial namespace MyNamespace {
+ any baz();
+ };
+ """)
+
+ results = parser.finish()
+ except Exception, x:
+ threw = True
+ harness.ok(threw, "Should have thrown.")
diff --git a/dom/bindings/parser/tests/test_newobject.py b/dom/bindings/parser/tests/test_newobject.py
new file mode 100644
index 000000000..26785c6a2
--- /dev/null
+++ b/dom/bindings/parser/tests/test_newobject.py
@@ -0,0 +1,70 @@
+# Import the WebIDL module, so we can do isinstance checks and whatnot
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ # Basic functionality
+ parser.parse(
+ """
+ interface Iface {
+ [NewObject] readonly attribute Iface attr;
+ [NewObject] Iface method();
+ };
+ """)
+ results = parser.finish()
+ harness.ok(results, "Should not have thrown on basic [NewObject] usage")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Iface {
+ [Pure, NewObject] readonly attribute Iface attr;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw, "[NewObject] attributes must depend on something")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Iface {
+ [Pure, NewObject] Iface method();
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw, "[NewObject] methods must depend on something")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Iface {
+ [Cached, NewObject, Affects=Nothing] readonly attribute Iface attr;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw, "[NewObject] attributes must not be [Cached]")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Iface {
+ [StoreInSlot, NewObject, Affects=Nothing] readonly attribute Iface attr;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw, "[NewObject] attributes must not be [StoreInSlot]")
diff --git a/dom/bindings/parser/tests/test_nullable_equivalency.py b/dom/bindings/parser/tests/test_nullable_equivalency.py
new file mode 100644
index 000000000..2b48b615d
--- /dev/null
+++ b/dom/bindings/parser/tests/test_nullable_equivalency.py
@@ -0,0 +1,115 @@
+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;
+ };
+
+ interface TestNullableEquivalency8 {
+ attribute float a;
+ attribute float? b;
+ };
+
+ interface TestNullableEquivalency9 {
+ attribute double a;
+ attribute double? b;
+ };
+
+ interface TestNullableEquivalency10 {
+ attribute object a;
+ attribute object? 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', 'getDeps', 'name'] 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/dom/bindings/parser/tests/test_nullable_void.py b/dom/bindings/parser/tests/test_nullable_void.py
new file mode 100644
index 000000000..961ff825e
--- /dev/null
+++ b/dom/bindings/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/dom/bindings/parser/tests/test_optional_constraints.py b/dom/bindings/parser/tests/test_optional_constraints.py
new file mode 100644
index 000000000..6217465ce
--- /dev/null
+++ b/dom/bindings/parser/tests/test_optional_constraints.py
@@ -0,0 +1,30 @@
+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(not threw,
+ "Should not have thrown on non-optional argument following "
+ "optional argument.")
+
+ parser = parser.reset()
+ parser.parse("""
+ interface OptionalConstraints2 {
+ void foo(optional byte arg1 = 1, optional byte arg2 = 2,
+ optional byte arg3, optional byte arg4 = 4,
+ optional byte arg5, optional byte arg6 = 9);
+ };
+ """)
+ results = parser.finish()
+ args = results[0].members[0].signatures()[0][1]
+ harness.check(len(args), 6, "Should have 6 arguments")
+ harness.check(args[5].defaultValue.value, 9,
+ "Should have correct default value")
diff --git a/dom/bindings/parser/tests/test_overload.py b/dom/bindings/parser/tests/test_overload.py
new file mode 100644
index 000000000..3c680ad52
--- /dev/null
+++ b/dom/bindings/parser/tests/test_overload.py
@@ -0,0 +1,60 @@
+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);
+ void withVariadics(long... numbers);
+ void withVariadics(TestOverloads iface);
+ void withVariadics(long num, TestOverloads iface);
+ void optionalTest();
+ void optionalTest(optional long num1, long num2);
+ };
+ """)
+
+ 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), 4, "Expect %s members" % 4)
+
+ 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")
+
+ member = iface.members[3]
+ harness.check(len(member.overloadsForArgCount(0)), 1,
+ "Only one overload for no args")
+ harness.check(len(member.overloadsForArgCount(1)), 0,
+ "No overloads for one arg")
+ harness.check(len(member.overloadsForArgCount(2)), 1,
+ "Only one overload for two args")
diff --git a/dom/bindings/parser/tests/test_promise.py b/dom/bindings/parser/tests/test_promise.py
new file mode 100644
index 000000000..55bc07680
--- /dev/null
+++ b/dom/bindings/parser/tests/test_promise.py
@@ -0,0 +1,63 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface _Promise {};
+ interface A {
+ legacycaller Promise<any> foo();
+ };
+ """)
+ results = parser.finish()
+
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow Promise return values for legacycaller.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface _Promise {};
+ interface A {
+ Promise<any> foo();
+ long foo(long arg);
+ };
+ """)
+ results = parser.finish();
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow overloads which have both Promise and "
+ "non-Promise return types.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface _Promise {};
+ interface A {
+ long foo(long arg);
+ Promise<any> foo();
+ };
+ """)
+ results = parser.finish();
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow overloads which have both Promise and "
+ "non-Promise return types.")
+
+ parser = parser.reset()
+ parser.parse("""
+ interface _Promise {};
+ interface A {
+ Promise<any> foo();
+ Promise<any> foo(long arg);
+ };
+ """)
+ results = parser.finish();
+
+ harness.ok(True,
+ "Should allow overloads which only have Promise and return "
+ "types.")
diff --git a/dom/bindings/parser/tests/test_prototype_ident.py b/dom/bindings/parser/tests/test_prototype_ident.py
new file mode 100644
index 000000000..d3932b54f
--- /dev/null
+++ b/dom/bindings/parser/tests/test_prototype_ident.py
@@ -0,0 +1,80 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface TestIface {
+ static attribute boolean prototype;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "The identifier of a static attribute must not be 'prototype'")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface TestIface {
+ static boolean prototype();
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "The identifier of a static operation must not be 'prototype'")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface TestIface {
+ const boolean prototype = true;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "The identifier of a constant must not be 'prototype'")
+
+ # Make sure that we can parse non-static attributes with 'prototype' as identifier.
+ parser = parser.reset()
+ parser.parse("""
+ interface TestIface {
+ attribute boolean prototype;
+ };
+ """)
+ results = parser.finish()
+
+ testIface = results[0];
+ harness.check(testIface.members[0].isStatic(), False, "Attribute should not be static")
+ harness.check(testIface.members[0].identifier.name, "prototype", "Attribute identifier should be 'prototype'")
+
+ # Make sure that we can parse non-static operations with 'prototype' as identifier.
+ parser = parser.reset()
+ parser.parse("""
+ interface TestIface {
+ boolean prototype();
+ };
+ """)
+ results = parser.finish()
+
+ testIface = results[0];
+ harness.check(testIface.members[0].isStatic(), False, "Operation should not be static")
+ harness.check(testIface.members[0].identifier.name, "prototype", "Operation identifier should be 'prototype'")
+
+ # Make sure that we can parse dictionary members with 'prototype' as identifier.
+ parser = parser.reset()
+ parser.parse("""
+ dictionary TestDict {
+ boolean prototype;
+ };
+ """)
+ results = parser.finish()
+
+ testDict = results[0];
+ harness.check(testDict.members[0].identifier.name, "prototype", "Dictionary member should be 'prototype'")
+
diff --git a/dom/bindings/parser/tests/test_putForwards.py b/dom/bindings/parser/tests/test_putForwards.py
new file mode 100644
index 000000000..86a1bf115
--- /dev/null
+++ b/dom/bindings/parser/tests/test_putForwards.py
@@ -0,0 +1,107 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface I {
+ [PutForwards=B] readonly attribute long A;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset();
+ threw = False
+ try:
+ parser.parse("""
+ interface I {
+ [PutForwards=B] readonly attribute J A;
+ };
+ interface J {
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset();
+ threw = False
+ try:
+ parser.parse("""
+ interface I {
+ [PutForwards=B] attribute J A;
+ };
+ interface J {
+ attribute long B;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset();
+ threw = False
+ try:
+ parser.parse("""
+ interface I {
+ [PutForwards=B] static readonly attribute J A;
+ };
+ interface J {
+ attribute long B;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset();
+ threw = False
+ try:
+ parser.parse("""
+ callback interface I {
+ [PutForwards=B] readonly attribute J A;
+ };
+ interface J {
+ attribute long B;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset();
+ threw = False
+ try:
+ parser.parse("""
+ interface I {
+ [PutForwards=C] readonly attribute J A;
+ [PutForwards=C] readonly attribute J B;
+ };
+ interface J {
+ [PutForwards=D] readonly attribute K C;
+ };
+ interface K {
+ [PutForwards=A] readonly attribute I D;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/dom/bindings/parser/tests/test_replaceable.py b/dom/bindings/parser/tests/test_replaceable.py
new file mode 100644
index 000000000..93ee42ed9
--- /dev/null
+++ b/dom/bindings/parser/tests/test_replaceable.py
@@ -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/.
+
+def should_throw(parser, harness, message, code):
+ parser = parser.reset();
+ threw = False
+ try:
+ parser.parse(code)
+ parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown: %s" % message)
+
+
+def WebIDLTest(parser, harness):
+ # The [Replaceable] extended attribute MUST take no arguments.
+ should_throw(parser, harness, "no arguments", """
+ interface I {
+ [Replaceable=X] readonly attribute long A;
+ };
+ """)
+
+ # An attribute with the [Replaceable] extended attribute MUST NOT also be
+ # declared with the [PutForwards] extended attribute.
+ should_throw(parser, harness, "PutForwards", """
+ interface I {
+ [PutForwards=B, Replaceable] readonly attribute J A;
+ };
+ interface J {
+ attribute long B;
+ };
+ """)
+
+ # The [Replaceable] extended attribute MUST NOT be used on an attribute
+ # that is not read only.
+ should_throw(parser, harness, "writable attribute", """
+ interface I {
+ [Replaceable] attribute long A;
+ };
+ """)
+
+ # The [Replaceable] extended attribute MUST NOT be used on a static
+ # attribute.
+ should_throw(parser, harness, "static attribute", """
+ interface I {
+ [Replaceable] static readonly attribute long A;
+ };
+ """)
+
+ # The [Replaceable] extended attribute MUST NOT be used on an attribute
+ # declared on a callback interface.
+ should_throw(parser, harness, "callback interface", """
+ callback interface I {
+ [Replaceable] readonly attribute long A;
+ };
+ """)
diff --git a/dom/bindings/parser/tests/test_sanity.py b/dom/bindings/parser/tests/test_sanity.py
new file mode 100644
index 000000000..d3184c007
--- /dev/null
+++ b/dom/bindings/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/dom/bindings/parser/tests/test_securecontext_extended_attribute.py b/dom/bindings/parser/tests/test_securecontext_extended_attribute.py
new file mode 100644
index 000000000..084f19fa7
--- /dev/null
+++ b/dom/bindings/parser/tests/test_securecontext_extended_attribute.py
@@ -0,0 +1,332 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ [SecureContext]
+ interface TestSecureContextOnInterface {
+ const octet TEST_CONSTANT = 0;
+ readonly attribute byte testAttribute;
+ void testMethod(byte foo);
+ };
+ partial interface TestSecureContextOnInterface {
+ const octet TEST_CONSTANT_2 = 0;
+ readonly attribute byte testAttribute2;
+ void testMethod2(byte foo);
+ };
+ """)
+ results = parser.finish()
+ harness.check(len(results[0].members), 6, "TestSecureContextOnInterface should have six members")
+ harness.ok(results[0].getExtendedAttribute("SecureContext"),
+ "Interface should have [SecureContext] extended attribute")
+ harness.ok(results[0].members[0].getExtendedAttribute("SecureContext"),
+ "[SecureContext] should propagate from interface to constant members")
+ harness.ok(results[0].members[1].getExtendedAttribute("SecureContext"),
+ "[SecureContext] should propagate from interface to attribute members")
+ harness.ok(results[0].members[2].getExtendedAttribute("SecureContext"),
+ "[SecureContext] should propagate from interface to method members")
+ harness.ok(results[0].members[3].getExtendedAttribute("SecureContext"),
+ "[SecureContext] should propagate from interface to constant members from partial interface")
+ harness.ok(results[0].members[4].getExtendedAttribute("SecureContext"),
+ "[SecureContext] should propagate from interface to attribute members from partial interface")
+ harness.ok(results[0].members[5].getExtendedAttribute("SecureContext"),
+ "[SecureContext] should propagate from interface to method members from partial interface")
+
+ # Same thing, but with the partial interface specified first:
+ parser = parser.reset()
+ parser.parse("""
+ partial interface TestSecureContextOnInterfaceAfterPartialInterface {
+ const octet TEST_CONSTANT_2 = 0;
+ readonly attribute byte testAttribute2;
+ void testMethod2(byte foo);
+ };
+ [SecureContext]
+ interface TestSecureContextOnInterfaceAfterPartialInterface {
+ const octet TEST_CONSTANT = 0;
+ readonly attribute byte testAttribute;
+ void testMethod(byte foo);
+ };
+ """)
+ results = parser.finish()
+ harness.check(len(results[1].members), 6, "TestSecureContextOnInterfaceAfterPartialInterface should have six members")
+ harness.ok(results[1].getExtendedAttribute("SecureContext"),
+ "Interface should have [SecureContext] extended attribute")
+ harness.ok(results[1].members[0].getExtendedAttribute("SecureContext"),
+ "[SecureContext] should propagate from interface to constant members")
+ harness.ok(results[1].members[1].getExtendedAttribute("SecureContext"),
+ "[SecureContext] should propagate from interface to attribute members")
+ harness.ok(results[1].members[2].getExtendedAttribute("SecureContext"),
+ "[SecureContext] should propagate from interface to method members")
+ harness.ok(results[1].members[3].getExtendedAttribute("SecureContext"),
+ "[SecureContext] should propagate from interface to constant members from partial interface")
+ harness.ok(results[1].members[4].getExtendedAttribute("SecureContext"),
+ "[SecureContext] should propagate from interface to attribute members from partial interface")
+ harness.ok(results[1].members[5].getExtendedAttribute("SecureContext"),
+ "[SecureContext] should propagate from interface to method members from partial interface")
+
+ parser = parser.reset()
+ parser.parse("""
+ interface TestSecureContextOnPartialInterface {
+ const octet TEST_CONSTANT = 0;
+ readonly attribute byte testAttribute;
+ void testMethod(byte foo);
+ };
+ [SecureContext]
+ partial interface TestSecureContextOnPartialInterface {
+ const octet TEST_CONSTANT_2 = 0;
+ readonly attribute byte testAttribute2;
+ void testMethod2(byte foo);
+ };
+ """)
+ results = parser.finish()
+ harness.check(len(results[0].members), 6, "TestSecureContextOnPartialInterface should have six members")
+ harness.ok(results[0].getExtendedAttribute("SecureContext") is None,
+ "[SecureContext] should not propagate from a partial interface to the interface")
+ harness.ok(results[0].members[0].getExtendedAttribute("SecureContext") is None,
+ "[SecureContext] should not propagate from a partial interface to the interface's constant members")
+ harness.ok(results[0].members[1].getExtendedAttribute("SecureContext") is None,
+ "[SecureContext] should not propagate from a partial interface to the interface's attribute members")
+ harness.ok(results[0].members[2].getExtendedAttribute("SecureContext") is None,
+ "[SecureContext] should not propagate from a partial interface to the interface's method members")
+ harness.ok(results[0].members[3].getExtendedAttribute("SecureContext"),
+ "Constant members from [SecureContext] partial interface should be [SecureContext]")
+ harness.ok(results[0].members[4].getExtendedAttribute("SecureContext"),
+ "Attribute members from [SecureContext] partial interface should be [SecureContext]")
+ harness.ok(results[0].members[5].getExtendedAttribute("SecureContext"),
+ "Method members from [SecureContext] partial interface should be [SecureContext]")
+
+ parser = parser.reset()
+ parser.parse("""
+ interface TestSecureContextOnInterfaceMembers {
+ const octet TEST_NON_SECURE_CONSTANT_1 = 0;
+ [SecureContext]
+ const octet TEST_SECURE_CONSTANT = 1;
+ const octet TEST_NON_SECURE_CONSTANT_2 = 2;
+ readonly attribute byte testNonSecureAttribute1;
+ [SecureContext]
+ readonly attribute byte testSecureAttribute;
+ readonly attribute byte testNonSecureAttribute2;
+ void testNonSecureMethod1(byte foo);
+ [SecureContext]
+ void testSecureMethod(byte foo);
+ void testNonSecureMethod2(byte foo);
+ };
+ """)
+ results = parser.finish()
+ harness.check(len(results[0].members), 9, "TestSecureContextOnInterfaceMembers should have nine members")
+ harness.ok(results[0].getExtendedAttribute("SecureContext") is None,
+ "[SecureContext] on members should not propagate up to the interface")
+ harness.ok(results[0].members[0].getExtendedAttribute("SecureContext") is None,
+ "Constant should not have [SecureContext] extended attribute")
+ harness.ok(results[0].members[1].getExtendedAttribute("SecureContext"),
+ "Constant should have [SecureContext] extended attribute")
+ harness.ok(results[0].members[2].getExtendedAttribute("SecureContext") is None,
+ "Constant should not have [SecureContext] extended attribute")
+ harness.ok(results[0].members[3].getExtendedAttribute("SecureContext") is None,
+ "Attribute should not have [SecureContext] extended attribute")
+ harness.ok(results[0].members[4].getExtendedAttribute("SecureContext"),
+ "Attribute should have [SecureContext] extended attribute")
+ harness.ok(results[0].members[5].getExtendedAttribute("SecureContext") is None,
+ "Attribute should not have [SecureContext] extended attribute")
+ harness.ok(results[0].members[6].getExtendedAttribute("SecureContext") is None,
+ "Method should not have [SecureContext] extended attribute")
+ harness.ok(results[0].members[7].getExtendedAttribute("SecureContext"),
+ "Method should have [SecureContext] extended attribute")
+ harness.ok(results[0].members[8].getExtendedAttribute("SecureContext") is None,
+ "Method should not have [SecureContext] extended attribute")
+
+ parser = parser.reset()
+ parser.parse("""
+ interface TestSecureContextOnPartialInterfaceMembers {
+ };
+ partial interface TestSecureContextOnPartialInterfaceMembers {
+ const octet TEST_NON_SECURE_CONSTANT_1 = 0;
+ [SecureContext]
+ const octet TEST_SECURE_CONSTANT = 1;
+ const octet TEST_NON_SECURE_CONSTANT_2 = 2;
+ readonly attribute byte testNonSecureAttribute1;
+ [SecureContext]
+ readonly attribute byte testSecureAttribute;
+ readonly attribute byte testNonSecureAttribute2;
+ void testNonSecureMethod1(byte foo);
+ [SecureContext]
+ void testSecureMethod(byte foo);
+ void testNonSecureMethod2(byte foo);
+ };
+ """)
+ results = parser.finish()
+ harness.check(len(results[0].members), 9, "TestSecureContextOnPartialInterfaceMembers should have nine members")
+ harness.ok(results[0].members[0].getExtendedAttribute("SecureContext") is None,
+ "Constant from partial interface should not have [SecureContext] extended attribute")
+ harness.ok(results[0].members[1].getExtendedAttribute("SecureContext"),
+ "Constant from partial interface should have [SecureContext] extended attribute")
+ harness.ok(results[0].members[2].getExtendedAttribute("SecureContext") is None,
+ "Constant from partial interface should not have [SecureContext] extended attribute")
+ harness.ok(results[0].members[3].getExtendedAttribute("SecureContext") is None,
+ "Attribute from partial interface should not have [SecureContext] extended attribute")
+ harness.ok(results[0].members[4].getExtendedAttribute("SecureContext"),
+ "Attribute from partial interface should have [SecureContext] extended attribute")
+ harness.ok(results[0].members[5].getExtendedAttribute("SecureContext") is None,
+ "Attribute from partial interface should not have [SecureContext] extended attribute")
+ harness.ok(results[0].members[6].getExtendedAttribute("SecureContext") is None,
+ "Method from partial interface should not have [SecureContext] extended attribute")
+ harness.ok(results[0].members[7].getExtendedAttribute("SecureContext"),
+ "Method from partial interface should have [SecureContext] extended attribute")
+ harness.ok(results[0].members[8].getExtendedAttribute("SecureContext") is None,
+ "Method from partial interface should not have [SecureContext] extended attribute")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ [SecureContext=something]
+ interface TestSecureContextTakesNoValue1 {
+ const octet TEST_SECURE_CONSTANT = 0;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw, "[SecureContext] must take no arguments (testing on interface)")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface TestSecureContextForOverloads1 {
+ [SecureContext]
+ void testSecureMethod(byte foo);
+ };
+ partial interface TestSecureContextForOverloads1 {
+ void testSecureMethod(byte foo, byte bar);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw, "If [SecureContext] appears on an overloaded operation, then it MUST appear on all overloads")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface TestSecureContextForOverloads2 {
+ [SecureContext]
+ void testSecureMethod(byte foo);
+ };
+ partial interface TestSecureContextForOverloads2 {
+ [SecureContext]
+ void testSecureMethod(byte foo, byte bar);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(not threw, "[SecureContext] can appear on an overloaded operation if it appears on all overloads")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ [SecureContext]
+ interface TestSecureContextOnInterfaceAndMember {
+ [SecureContext]
+ void testSecureMethod(byte foo);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw, "[SecureContext] must not appear on an interface and interface member")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface TestSecureContextOnPartialInterfaceAndMember {
+ };
+ [SecureContext]
+ partial interface TestSecureContextOnPartialInterfaceAndMember {
+ [SecureContext]
+ void testSecureMethod(byte foo);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw, "[SecureContext] must not appear on a partial interface and one of the partial interface's member's")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ [SecureContext]
+ interface TestSecureContextOnInterfaceAndPartialInterfaceMember {
+ };
+ partial interface TestSecureContextOnInterfaceAndPartialInterfaceMember {
+ [SecureContext]
+ void testSecureMethod(byte foo);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw, "[SecureContext] must not appear on an interface and one of its partial interface's member's")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ [SecureContext]
+ interface TestSecureContextOnInheritedInterface {
+ };
+ interface TestSecureContextNotOnInheritingInterface : TestSecureContextOnInheritedInterface {
+ void testSecureMethod(byte foo);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw, "[SecureContext] must appear on interfaces that inherit from another [SecureContext] interface")
+
+ # Test 'implements'. The behavior tested here may have to change depending
+ # on the resolution of https://github.com/heycam/webidl/issues/118
+ parser = parser.reset()
+ parser.parse("""
+ [SecureContext]
+ interface TestSecureContextInterfaceThatImplementsNonSecureContextInterface {
+ const octet TEST_CONSTANT = 0;
+ };
+ interface TestNonSecureContextInterface {
+ const octet TEST_CONSTANT_2 = 0;
+ readonly attribute byte testAttribute2;
+ void testMethod2(byte foo);
+ };
+ TestSecureContextInterfaceThatImplementsNonSecureContextInterface implements TestNonSecureContextInterface;
+ """)
+ results = parser.finish()
+ harness.check(len(results[0].members), 4, "TestSecureContextInterfaceThatImplementsNonSecureContextInterface should have two members")
+ harness.ok(results[0].getExtendedAttribute("SecureContext"),
+ "Interface should have [SecureContext] extended attribute")
+ harness.ok(results[0].members[0].getExtendedAttribute("SecureContext"),
+ "[SecureContext] should propagate from interface to constant members even when other members are copied from a non-[SecureContext] interface")
+ harness.ok(results[0].members[1].getExtendedAttribute("SecureContext") is None,
+ "Constants copied from non-[SecureContext] interface should not be [SecureContext]")
+ harness.ok(results[0].members[2].getExtendedAttribute("SecureContext") is None,
+ "Attributes copied from non-[SecureContext] interface should not be [SecureContext]")
+ harness.ok(results[0].members[3].getExtendedAttribute("SecureContext") is None,
+ "Methods copied from non-[SecureContext] interface should not be [SecureContext]")
+
+ # Test SecureContext and NoInterfaceObject
+ parser = parser.reset()
+ parser.parse("""
+ [NoInterfaceObject, SecureContext]
+ interface TestSecureContextNoInterfaceObject {
+ void testSecureMethod(byte foo);
+ };
+ """)
+ results = parser.finish()
+ harness.check(len(results[0].members), 1, "TestSecureContextNoInterfaceObject should have only one member")
+ harness.ok(results[0].getExtendedAttribute("SecureContext"),
+ "Interface should have [SecureContext] extended attribute")
+ harness.ok(results[0].members[0].getExtendedAttribute("SecureContext"),
+ "Interface member should have [SecureContext] extended attribute")
diff --git a/dom/bindings/parser/tests/test_special_method_signature_mismatch.py b/dom/bindings/parser/tests/test_special_method_signature_mismatch.py
new file mode 100644
index 000000000..5ea1743d3
--- /dev/null
+++ b/dom/bindings/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/dom/bindings/parser/tests/test_special_methods.py b/dom/bindings/parser/tests/test_special_methods.py
new file mode 100644
index 000000000..5b45c3399
--- /dev/null
+++ b/dom/bindings/parser/tests/test_special_methods.py
@@ -0,0 +1,85 @@
+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);
+ getter boolean (DOMString name);
+ setter boolean (DOMString name, boolean value);
+ creator boolean (DOMString name, boolean value);
+ deleter boolean (DOMString name);
+ };
+
+ interface SpecialMethodsCombination {
+ 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), 7, "Expect 7 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::__namedgetter", "__namedgetter",
+ getter=True)
+ checkMethod(iface.members[4], "::SpecialMethods::__namedsetter", "__namedsetter",
+ setter=True)
+ checkMethod(iface.members[5], "::SpecialMethods::__namedcreator", "__namedcreator",
+ creator=True)
+ checkMethod(iface.members[6], "::SpecialMethods::__nameddeleter", "__nameddeleter",
+ deleter=True)
+
+ iface = results[1]
+ harness.check(len(iface.members), 3, "Expect 3 members")
+
+ checkMethod(iface.members[0], "::SpecialMethodsCombination::__indexedsettercreator",
+ "__indexedsettercreator", setter=True, creator=True)
+ checkMethod(iface.members[1], "::SpecialMethodsCombination::__namedgetterdeleter",
+ "__namedgetterdeleter", getter=True, deleter=True)
+ checkMethod(iface.members[2], "::SpecialMethodsCombination::__namedsettercreator",
+ "__namedsettercreator", setter=True, creator=True)
+
+ parser = parser.reset();
+
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface IndexedDeleter {
+ deleter void(unsigned long index);
+ };
+ """)
+ parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "There are no indexed deleters")
+
+
diff --git a/dom/bindings/parser/tests/test_special_methods_uniqueness.py b/dom/bindings/parser/tests/test_special_methods_uniqueness.py
new file mode 100644
index 000000000..42e2c5bb7
--- /dev/null
+++ b/dom/bindings/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/dom/bindings/parser/tests/test_stringifier.py b/dom/bindings/parser/tests/test_stringifier.py
new file mode 100644
index 000000000..14c2c5226
--- /dev/null
+++ b/dom/bindings/parser/tests/test_stringifier.py
@@ -0,0 +1,46 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface TestStringifier {
+ stringifier;
+ };
+ """)
+
+ results = parser.finish()
+
+ harness.ok(isinstance(results[0].members[0], WebIDL.IDLMethod),
+ "Stringifer should be method")
+
+ parser = parser.reset()
+
+ threw = False
+ try:
+ parser.parse("""
+ interface TestStringifier {
+ stringifier;
+ stringifier;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow two 'stringifier;'")
+
+ parser = parser.reset()
+
+ threw = False
+ try:
+ parser.parse("""
+ interface TestStringifier {
+ stringifier;
+ stringifier DOMString foo();
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow a 'stringifier;' and a 'stringifier()'")
+
diff --git a/dom/bindings/parser/tests/test_treatNonCallableAsNull.py b/dom/bindings/parser/tests/test_treatNonCallableAsNull.py
new file mode 100644
index 000000000..7a0bde8a6
--- /dev/null
+++ b/dom/bindings/parser/tests/test_treatNonCallableAsNull.py
@@ -0,0 +1,71 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ [TreatNonCallableAsNull] callback Function = any(any... arguments);
+
+ interface TestTreatNonCallableAsNull1 {
+ 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.")
+
+ parser = parser.reset()
+
+ threw = False
+ try:
+ parser.parse("""
+ [TreatNonCallableAsNull, TreatNonObjectAsNull]
+ callback Function = any(any... arguments);
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/dom/bindings/parser/tests/test_typedef.py b/dom/bindings/parser/tests/test_typedef.py
new file mode 100644
index 000000000..8921985c5
--- /dev/null
+++ b/dom/bindings/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, "LongOrNull",
+ "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/dom/bindings/parser/tests/test_unenumerable_own_properties.py b/dom/bindings/parser/tests/test_unenumerable_own_properties.py
new file mode 100644
index 000000000..d017d5ce0
--- /dev/null
+++ b/dom/bindings/parser/tests/test_unenumerable_own_properties.py
@@ -0,0 +1,64 @@
+def WebIDLTest(parser, harness):
+
+ parser.parse(
+ """
+ interface Foo {};
+ [LegacyUnenumerableNamedProperties]
+ interface Bar : Foo {
+ getter long(DOMString name);
+ };
+ interface Baz : Bar {
+ getter long(DOMString name);
+ };
+ """);
+ results = parser.finish();
+ harness.check(len(results), 3, "Should have three interfaces")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ [LegacyUnenumerableNamedProperties]
+ interface NoNamedGetter {
+ };
+ """)
+
+ results = parser.finish()
+ except Exception, x:
+ threw = True
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ [LegacyUnenumerableNamedProperties=Foo]
+ interface ShouldNotHaveArg {
+ getter long(DOMString name);
+ };
+ """)
+
+ results = parser.finish()
+ except Exception, x:
+ threw = True
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ [LegacyUnenumerableNamedProperties]
+ interface Foo {
+ getter long(DOMString name);
+ };
+ interface Bar : Foo {};
+ [LegacyUnenumerableNamedProperties]
+ interface Baz : Bar {
+ getter long(DOMString name);
+ };
+ """)
+
+ results = parser.finish()
+ except Exception, x:
+ threw = True
+ harness.ok(threw, "Should have thrown.")
diff --git a/dom/bindings/parser/tests/test_unforgeable.py b/dom/bindings/parser/tests/test_unforgeable.py
new file mode 100644
index 000000000..3787e8c6a
--- /dev/null
+++ b/dom/bindings/parser/tests/test_unforgeable.py
@@ -0,0 +1,253 @@
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface Child : Parent {
+ };
+ interface Parent {
+ [Unforgeable] readonly attribute long foo;
+ };
+ """)
+
+ results = parser.finish()
+ harness.check(len(results), 2,
+ "Should be able to inherit from an interface with "
+ "[Unforgeable] properties.")
+
+ parser = parser.reset();
+ parser.parse("""
+ interface Child : Parent {
+ const short foo = 10;
+ };
+ interface Parent {
+ [Unforgeable] readonly attribute long foo;
+ };
+ """)
+
+ results = parser.finish()
+ harness.check(len(results), 2,
+ "Should be able to inherit from an interface with "
+ "[Unforgeable] properties even if we have a constant with "
+ "the same name.")
+
+ parser = parser.reset();
+ parser.parse("""
+ interface Child : Parent {
+ static attribute short foo;
+ };
+ interface Parent {
+ [Unforgeable] readonly attribute long foo;
+ };
+ """)
+
+ results = parser.finish()
+ harness.check(len(results), 2,
+ "Should be able to inherit from an interface with "
+ "[Unforgeable] properties even if we have a static attribute "
+ "with the same name.")
+
+ parser = parser.reset();
+ parser.parse("""
+ interface Child : Parent {
+ static void foo();
+ };
+ interface Parent {
+ [Unforgeable] readonly attribute long foo;
+ };
+ """)
+
+ results = parser.finish()
+ harness.check(len(results), 2,
+ "Should be able to inherit from an interface with "
+ "[Unforgeable] properties even if we have a static operation "
+ "with the same name.")
+
+ parser = parser.reset();
+ threw = False
+ try:
+ parser.parse("""
+ interface Child : Parent {
+ void foo();
+ };
+ interface Parent {
+ [Unforgeable] readonly attribute long foo;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should have thrown when shadowing unforgeable attribute on "
+ "parent with operation.")
+
+ parser = parser.reset();
+ threw = False
+ try:
+ parser.parse("""
+ interface Child : Parent {
+ void foo();
+ };
+ interface Parent {
+ [Unforgeable] void foo();
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should have thrown when shadowing unforgeable operation on "
+ "parent with operation.")
+
+ parser = parser.reset();
+ threw = False
+ try:
+ parser.parse("""
+ interface Child : Parent {
+ attribute short foo;
+ };
+ interface Parent {
+ [Unforgeable] readonly attribute long foo;
+ };
+ """)
+
+ results = parser.finish()
+ except Exception,x:
+ threw = True
+ harness.ok(threw,
+ "Should have thrown when shadowing unforgeable attribute on "
+ "parent with attribute.")
+
+ parser = parser.reset();
+ threw = False
+ try:
+ parser.parse("""
+ interface Child : Parent {
+ attribute short foo;
+ };
+ interface Parent {
+ [Unforgeable] void foo();
+ };
+ """)
+
+ results = parser.finish()
+ except Exception,x:
+ threw = True
+ harness.ok(threw,
+ "Should have thrown when shadowing unforgeable operation on "
+ "parent with attribute.")
+
+ parser = parser.reset();
+ parser.parse("""
+ interface Child : Parent {
+ };
+ interface Parent {};
+ interface Consequential {
+ [Unforgeable] readonly attribute long foo;
+ };
+ Parent implements Consequential;
+ """)
+
+ results = parser.finish()
+ harness.check(len(results), 4,
+ "Should be able to inherit from an interface with a "
+ "consequential interface with [Unforgeable] properties.")
+
+ parser = parser.reset();
+ threw = False
+ try:
+ parser.parse("""
+ interface Child : Parent {
+ void foo();
+ };
+ interface Parent {};
+ interface Consequential {
+ [Unforgeable] readonly attribute long foo;
+ };
+ Parent implements Consequential;
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Should have thrown when shadowing unforgeable attribute "
+ "of parent's consequential interface.")
+
+ parser = parser.reset();
+ threw = False
+ try:
+ parser.parse("""
+ interface Child : Parent {
+ };
+ interface Parent : GrandParent {};
+ interface GrandParent {};
+ interface Consequential {
+ [Unforgeable] readonly attribute long foo;
+ };
+ GrandParent implements Consequential;
+ interface ChildConsequential {
+ void foo();
+ };
+ Child implements ChildConsequential;
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Should have thrown when our consequential interface shadows unforgeable attribute "
+ "of ancestor's consequential interface.")
+
+ parser = parser.reset();
+ threw = False
+ try:
+ parser.parse("""
+ interface Child : Parent {
+ };
+ interface Parent : GrandParent {};
+ interface GrandParent {};
+ interface Consequential {
+ [Unforgeable] void foo();
+ };
+ GrandParent implements Consequential;
+ interface ChildConsequential {
+ void foo();
+ };
+ Child implements ChildConsequential;
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Should have thrown when our consequential interface shadows unforgeable operation "
+ "of ancestor's consequential interface.")
+
+ parser = parser.reset();
+ parser.parse("""
+ interface iface {
+ [Unforgeable] attribute long foo;
+ };
+ """)
+
+ results = parser.finish()
+ harness.check(len(results), 1,
+ "Should allow writable [Unforgeable] attribute.")
+
+ parser = parser.reset();
+ threw = False
+ try:
+ parser.parse("""
+ interface iface {
+ [Unforgeable] static readonly attribute long foo;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown for static [Unforgeable] attribute.")
diff --git a/dom/bindings/parser/tests/test_union.py b/dom/bindings/parser/tests/test_union.py
new file mode 100644
index 000000000..9c4f2a56a
--- /dev/null
+++ b/dom/bindings/parser/tests/test_union.py
@@ -0,0 +1,168 @@
+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",
+ "ByteString",
+ "USVString",
+ #"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 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/dom/bindings/parser/tests/test_union_any.py b/dom/bindings/parser/tests/test_union_any.py
new file mode 100644
index 000000000..e34cadab4
--- /dev/null
+++ b/dom/bindings/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/dom/bindings/parser/tests/test_union_nullable.py b/dom/bindings/parser/tests/test_union_nullable.py
new file mode 100644
index 000000000..08430a94a
--- /dev/null
+++ b/dom/bindings/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/dom/bindings/parser/tests/test_usvstring.py b/dom/bindings/parser/tests/test_usvstring.py
new file mode 100644
index 000000000..3a1369abd
--- /dev/null
+++ b/dom/bindings/parser/tests/test_usvstring.py
@@ -0,0 +1,36 @@
+# -*- coding: UTF-8 -*-
+
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface TestUSVString {
+ attribute USVString svs;
+ };
+ """)
+
+ results = parser.finish();
+
+ 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(), "::TestUSVString",
+ "Interface has the right QName")
+ harness.check(iface.identifier.name, "TestUSVString",
+ "Interface has the right name")
+ harness.check(iface.parent, None, "Interface has no parent")
+
+ members = iface.members
+ harness.check(len(members), 1, "Should be one member")
+
+ attr = members[0]
+ harness.ok(isinstance(attr, WebIDL.IDLAttribute), "Should be an IDLAttribute")
+ harness.check(attr.identifier.QName(), "::TestUSVString::svs",
+ "Attr has correct QName")
+ harness.check(attr.identifier.name, "svs", "Attr has correct name")
+ harness.check(str(attr.type), "USVString",
+ "Attr type is the correct name")
+ harness.ok(attr.type.isUSVString(), "Should be USVString type")
+ harness.ok(attr.type.isString(), "Should be String collective type")
+ harness.ok(not attr.type.isDOMString(), "Should be not be DOMString type")
diff --git a/dom/bindings/parser/tests/test_variadic_callback.py b/dom/bindings/parser/tests/test_variadic_callback.py
new file mode 100644
index 000000000..d9a78db20
--- /dev/null
+++ b/dom/bindings/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/dom/bindings/parser/tests/test_variadic_constraints.py b/dom/bindings/parser/tests/test_variadic_constraints.py
new file mode 100644
index 000000000..7448e40d5
--- /dev/null
+++ b/dom/bindings/parser/tests/test_variadic_constraints.py
@@ -0,0 +1,63 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface VariadicConstraints1 {
+ void foo(byte... arg1, byte arg2);
+ };
+ """)
+ results = parser.finish()
+
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Should have thrown on variadic argument followed by required "
+ "argument.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface VariadicConstraints2 {
+ void foo(byte... arg1, optional byte arg2);
+ };
+ """)
+ results = parser.finish();
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Should have thrown on variadic argument followed by optional "
+ "argument.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface VariadicConstraints3 {
+ void foo(optional byte... arg1);
+ };
+ """)
+ results = parser.finish()
+
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Should have thrown on variadic argument explicitly flagged as "
+ "optional.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface VariadicConstraints4 {
+ void foo(byte... arg1 = 0);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown on variadic argument with default value.")
diff --git a/dom/bindings/test/Makefile.in b/dom/bindings/test/Makefile.in
new file mode 100644
index 000000000..844a51c27
--- /dev/null
+++ b/dom/bindings/test/Makefile.in
@@ -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/.
+
+ifdef COMPILE_ENVIRONMENT
+
+include ../webidlsrcs.mk
+
+# $(test_sources) comes from webidlsrcs.mk.
+# TODO Update this variable in backend.mk.
+CPPSRCS += $(addprefix ../,$(test_sources))
+
+# 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
+
+endif
+
+check::
+ PYTHONDONTWRITEBYTECODE=1 $(PYTHON) $(topsrcdir)/config/pythonpath.py \
+ $(PLY_INCLUDE) $(srcdir)/../parser/runtests.py
diff --git a/dom/bindings/test/TestBindingHeader.h b/dom/bindings/test/TestBindingHeader.h
new file mode 100644
index 000000000..ca5aafdc5
--- /dev/null
+++ b/dom/bindings/test/TestBindingHeader.h
@@ -0,0 +1,1431 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "mozilla/dom/BindingUtils.h"
+#include "mozilla/dom/Date.h"
+#include "mozilla/dom/MozMap.h"
+#include "mozilla/dom/TypedArray.h"
+#include "mozilla/ErrorResult.h"
+#include "nsCOMPtr.h"
+#include "nsGenericHTMLElement.h"
+#include "nsWrapperCache.h"
+
+// Forward declare this before we include TestCodeGenBinding.h, because that header relies on including
+// this one for it, for ParentDict. Hopefully it won't begin to rely on it in more fundamental ways.
+namespace mozilla {
+namespace dom {
+class TestExternalInterface;
+class Promise;
+} // namespace dom
+} // namespace mozilla
+
+// We don't export TestCodeGenBinding.h, but it's right in our parent dir.
+#include "../TestCodeGenBinding.h"
+
+extern bool TestFuncControlledMember(JSContext*, JSObject*);
+
+namespace mozilla {
+namespace dom {
+
+// IID for nsRenamedInterface
+#define NS_RENAMED_INTERFACE_IID \
+{ 0xd4b19ef3, 0xe68b, 0x4e3f, \
+ { 0x94, 0xbc, 0xc9, 0xde, 0x3a, 0x69, 0xb0, 0xe8 } }
+
+class nsRenamedInterface : public nsISupports,
+ public nsWrapperCache
+{
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_RENAMED_INTERFACE_IID)
+ NS_DECL_ISUPPORTS
+
+ // We need a GetParentObject to make binding codegen happy
+ virtual nsISupports* GetParentObject();
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsRenamedInterface, NS_RENAMED_INTERFACE_IID)
+
+// 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();
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(IndirectlyImplementedInterface, NS_INDIRECTLY_IMPLEMENTED_INTERFACE_IID)
+
+// 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
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(TestExternalInterface, NS_TEST_EXTERNAL_INTERFACE_IID)
+
+class TestNonWrapperCacheInterface : public nsISupports
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ bool WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto, JS::MutableHandle<JSObject*> aReflector);
+};
+
+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(const GlobalObject&, ErrorResult&);
+ static
+ already_AddRefed<TestInterface>
+ Constructor(const GlobalObject&, const nsAString&, ErrorResult&);
+ static
+ already_AddRefed<TestInterface>
+ Constructor(const GlobalObject&, uint32_t, const Nullable<bool>&,
+ ErrorResult&);
+ static
+ already_AddRefed<TestInterface>
+ Constructor(const GlobalObject&, TestInterface*, ErrorResult&);
+ static
+ already_AddRefed<TestInterface>
+ Constructor(const GlobalObject&, uint32_t, IndirectlyImplementedInterface&, ErrorResult&);
+
+ static
+ already_AddRefed<TestInterface>
+ Constructor(const GlobalObject&, Date&, ErrorResult&);
+ static
+ already_AddRefed<TestInterface>
+ Constructor(const GlobalObject&, const ArrayBuffer&, ErrorResult&);
+ static
+ already_AddRefed<TestInterface>
+ Constructor(const GlobalObject&, const Uint8Array&, ErrorResult&);
+ /* static
+ already_AddRefed<TestInterface>
+ Constructor(const GlobalObject&, uint32_t, uint32_t,
+ const TestInterfaceOrOnlyForUseInConstructor&, ErrorResult&);
+ */
+
+ static
+ already_AddRefed<TestInterface> Test(const GlobalObject&, ErrorResult&);
+ static
+ already_AddRefed<TestInterface> Test(const GlobalObject&, const nsAString&,
+ ErrorResult&);
+ static
+ already_AddRefed<TestInterface> Test(const GlobalObject&, const nsACString&,
+ ErrorResult&);
+
+ static
+ already_AddRefed<TestInterface> Test2(const GlobalObject&,
+ const DictForConstructor&,
+ JS::Handle<JS::Value>,
+ JS::Handle<JSObject*>,
+ JS::Handle<JSObject*>,
+ const Sequence<Dict>&,
+ JS::Handle<JS::Value>,
+ const Optional<JS::Handle<JSObject*> >&,
+ const Optional<JS::Handle<JSObject*> >&,
+ ErrorResult&);
+
+ static
+ already_AddRefed<TestInterface> Test3(const GlobalObject&,
+ const LongOrAnyMozMap&,
+ 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 PassOptionalByteBeforeRequired(const Optional<int8_t>&, int8_t);
+ void PassOptionalByteWithDefault(int8_t);
+ void PassOptionalByteWithDefaultBeforeRequired(int8_t, int8_t);
+ void PassNullableByte(const Nullable<int8_t>&);
+ void PassOptionalNullableByte(const Optional< Nullable<int8_t> >&);
+ void PassVariadicByte(const Sequence<int8_t>&);
+ int8_t CachedByte();
+ int8_t CachedConstantByte();
+ int8_t CachedWritableByte();
+ void SetCachedWritableByte(int8_t);
+ int8_t SideEffectFreeByte();
+ int8_t SetSideEffectFreeByte(int8_t);
+ int8_t DomDependentByte();
+ int8_t SetDomDependentByte(int8_t);
+ int8_t ConstantByte();
+ int8_t DeviceStateDependentByte();
+ int8_t ReturnByteSideEffectFree();
+ int8_t ReturnDOMDependentByte();
+ int8_t ReturnConstantByte();
+ int8_t ReturnDeviceStateDependentByte();
+
+ void UnsafePrerenderMethod();
+ int32_t UnsafePrerenderWritable();
+ void SetUnsafePrerenderWritable(int32_t);
+ int32_t UnsafePrerenderReadonly();
+ 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);
+
+ float WritableFloat() const;
+ void SetWritableFloat(float);
+ float WritableUnrestrictedFloat() const;
+ void SetWritableUnrestrictedFloat(float);
+ Nullable<float> GetWritableNullableFloat() const;
+ void SetWritableNullableFloat(Nullable<float>);
+ Nullable<float> GetWritableNullableUnrestrictedFloat() const;
+ void SetWritableNullableUnrestrictedFloat(Nullable<float>);
+ double WritableDouble() const;
+ void SetWritableDouble(double);
+ double WritableUnrestrictedDouble() const;
+ void SetWritableUnrestrictedDouble(double);
+ Nullable<double> GetWritableNullableDouble() const;
+ void SetWritableNullableDouble(Nullable<double>);
+ Nullable<double> GetWritableNullableUnrestrictedDouble() const;
+ void SetWritableNullableUnrestrictedDouble(Nullable<double>);
+ void PassFloat(float, float, Nullable<float>, Nullable<float>,
+ double, double, Nullable<double>, Nullable<double>,
+ const Sequence<float>&, const Sequence<float>&,
+ const Sequence<Nullable<float> >&,
+ const Sequence<Nullable<float> >&,
+ const Sequence<double>&, const Sequence<double>&,
+ const Sequence<Nullable<double> >&,
+ const Sequence<Nullable<double> >&);
+ void PassLenientFloat(float, float, Nullable<float>, Nullable<float>,
+ double, double, Nullable<double>, Nullable<double>,
+ const Sequence<float>&, const Sequence<float>&,
+ const Sequence<Nullable<float> >&,
+ const Sequence<Nullable<float> >&,
+ const Sequence<double>&, const Sequence<double>&,
+ const Sequence<Nullable<double> >&,
+ const Sequence<Nullable<double> >&);
+ float LenientFloatAttr() const;
+ void SetLenientFloatAttr(float);
+ double LenientDoubleAttr() const;
+ void SetLenientDoubleAttr(double);
+
+ void PassUnrestricted(float arg1,
+ float arg2,
+ float arg3,
+ float arg4,
+ double arg5,
+ double arg6,
+ double arg7,
+ double arg8);
+
+ // Interface types
+ already_AddRefed<TestInterface> ReceiveSelf();
+ already_AddRefed<TestInterface> ReceiveNullableSelf();
+ TestInterface* ReceiveWeakSelf();
+ TestInterface* ReceiveWeakNullableSelf();
+ void PassSelf(TestInterface&);
+ void PassNullableSelf(TestInterface*);
+ already_AddRefed<TestInterface> NonNullSelf();
+ void SetNonNullSelf(TestInterface&);
+ already_AddRefed<TestInterface> GetNullableSelf();
+ already_AddRefed<TestInterface> CachedSelf();
+ 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<RefPtr<TestNonWrapperCacheInterface> >&);
+ void ReceiveNullableNonWrapperCacheInterfaceSequence(nsTArray<RefPtr<TestNonWrapperCacheInterface> >&);
+ void ReceiveNonWrapperCacheInterfaceNullableSequence(Nullable<nsTArray<RefPtr<TestNonWrapperCacheInterface> > >&);
+ void ReceiveNullableNonWrapperCacheInterfaceNullableSequence(Nullable<nsTArray<RefPtr<TestNonWrapperCacheInterface> > >&);
+
+ already_AddRefed<IndirectlyImplementedInterface> ReceiveOther();
+ already_AddRefed<IndirectlyImplementedInterface> ReceiveNullableOther();
+ IndirectlyImplementedInterface* ReceiveWeakOther();
+ IndirectlyImplementedInterface* ReceiveWeakNullableOther();
+ void PassOther(IndirectlyImplementedInterface&);
+ void PassNullableOther(IndirectlyImplementedInterface*);
+ already_AddRefed<IndirectlyImplementedInterface> NonNullOther();
+ void SetNonNullOther(IndirectlyImplementedInterface&);
+ already_AddRefed<IndirectlyImplementedInterface> GetNullableOther();
+ void SetNullableOther(IndirectlyImplementedInterface*);
+ void PassOptionalOther(const Optional<IndirectlyImplementedInterface*>&);
+ void PassOptionalNonNullOther(const Optional<NonNull<IndirectlyImplementedInterface> >&);
+ void PassOptionalOtherWithDefault(IndirectlyImplementedInterface*);
+
+ already_AddRefed<TestExternalInterface> ReceiveExternal();
+ already_AddRefed<TestExternalInterface> ReceiveNullableExternal();
+ TestExternalInterface* ReceiveWeakExternal();
+ TestExternalInterface* ReceiveWeakNullableExternal();
+ void PassExternal(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 PassNullableCallbackInterface(TestCallbackInterface*);
+ already_AddRefed<TestCallbackInterface> NonNullCallbackInterface();
+ void SetNonNullCallbackInterface(TestCallbackInterface&);
+ already_AddRefed<TestCallbackInterface> GetNullableCallbackInterface();
+ void SetNullableCallbackInterface(TestCallbackInterface*);
+ void PassOptionalCallbackInterface(const Optional<RefPtr<TestCallbackInterface> >&);
+ void PassOptionalNonNullCallbackInterface(const Optional<OwningNonNull<TestCallbackInterface> >&);
+ void PassOptionalCallbackInterfaceWithDefault(TestCallbackInterface*);
+
+ already_AddRefed<IndirectlyImplementedInterface> ReceiveConsequentialInterface();
+ void PassConsequentialInterface(IndirectlyImplementedInterface&);
+
+ // Sequence types
+ void GetReadonlySequence(nsTArray<int32_t>&);
+ void GetReadonlySequenceOfDictionaries(JSContext*, nsTArray<Dict>&);
+ void GetReadonlyNullableSequenceOfDictionaries(JSContext*, Nullable<nsTArray<Dict> >&);
+ void GetReadonlyFrozenSequence(JSContext*, nsTArray<Dict>&);
+ void GetReadonlyFrozenNullableSequence(JSContext*, Nullable<nsTArray<Dict>>&);
+ 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< RefPtr<TestInterface> > &);
+ void ReceiveCallbackObjectSequence(nsTArray< RefPtr<TestCallbackInterface> > &);
+ void ReceiveNullableCastableObjectSequence(nsTArray< RefPtr<TestInterface> > &);
+ void ReceiveNullableCallbackObjectSequence(nsTArray< RefPtr<TestCallbackInterface> > &);
+ void ReceiveCastableObjectNullableSequence(Nullable< nsTArray< RefPtr<TestInterface> > >&);
+ void ReceiveNullableCastableObjectNullableSequence(Nullable< nsTArray< RefPtr<TestInterface> > >&);
+ void ReceiveWeakCastableObjectSequence(nsTArray<RefPtr<TestInterface>> &);
+ void ReceiveWeakNullableCastableObjectSequence(nsTArray<RefPtr<TestInterface>> &);
+ void ReceiveWeakCastableObjectNullableSequence(Nullable< nsTArray<RefPtr<TestInterface>> >&);
+ void ReceiveWeakNullableCastableObjectNullableSequence(Nullable< nsTArray<RefPtr<TestInterface>> >&);
+ void PassCastableObjectSequence(const Sequence< OwningNonNull<TestInterface> >&);
+ void PassNullableCastableObjectSequence(const Sequence< RefPtr<TestInterface> > &);
+ void PassCastableObjectNullableSequence(const Nullable< Sequence< OwningNonNull<TestInterface> > >&);
+ void PassNullableCastableObjectNullableSequence(const Nullable< Sequence< RefPtr<TestInterface> > >&);
+ void PassOptionalSequence(const Optional<Sequence<int32_t> >&);
+ void PassOptionalSequenceWithDefaultValue(const Sequence<int32_t> &);
+ void PassOptionalNullableSequence(const Optional<Nullable<Sequence<int32_t> > >&);
+ void PassOptionalNullableSequenceWithDefaultValue(const Nullable< Sequence<int32_t> >&);
+ void PassOptionalNullableSequenceWithDefaultValue2(const Nullable< Sequence<int32_t> >&);
+ void PassOptionalObjectSequence(const Optional<Sequence<OwningNonNull<TestInterface> > >&);
+ void PassExternalInterfaceSequence(const Sequence<RefPtr<TestExternalInterface> >&);
+ void PassNullableExternalInterfaceSequence(const Sequence<RefPtr<TestExternalInterface> >&);
+
+ void ReceiveStringSequence(nsTArray<nsString>&);
+ void PassStringSequence(const Sequence<nsString>&);
+
+ void ReceiveByteStringSequence(nsTArray<nsCString>&);
+ void PassByteStringSequence(const Sequence<nsCString>&);
+
+ void ReceiveAnySequence(JSContext*, nsTArray<JS::Value>&);
+ void ReceiveNullableAnySequence(JSContext*, Nullable<nsTArray<JS::Value> >&);
+ void ReceiveAnySequenceSequence(JSContext*, nsTArray<nsTArray<JS::Value> >&);
+
+ void ReceiveObjectSequence(JSContext*, nsTArray<JSObject*>&);
+ void ReceiveNullableObjectSequence(JSContext*, nsTArray<JSObject*>&);
+
+ void PassSequenceOfSequences(const Sequence< Sequence<int32_t> >&);
+ void PassSequenceOfSequencesOfSequences(const Sequence<Sequence<Sequence<int32_t>>>&);
+ void ReceiveSequenceOfSequences(nsTArray< nsTArray<int32_t> >&);
+ void ReceiveSequenceOfSequencesOfSequences(nsTArray<nsTArray<nsTArray<int32_t>>>&);
+
+ // MozMap types
+ void PassMozMap(const MozMap<int32_t> &);
+ void PassNullableMozMap(const Nullable< MozMap<int32_t> >&);
+ void PassMozMapOfNullableInts(const MozMap<Nullable<int32_t> >&);
+ void PassOptionalMozMapOfNullableInts(const Optional<MozMap<Nullable<int32_t> > > &);
+ void PassOptionalNullableMozMapOfNullableInts(const Optional<Nullable<MozMap<Nullable<int32_t> > > > &);
+ void PassCastableObjectMozMap(const MozMap< OwningNonNull<TestInterface> >&);
+ void PassNullableCastableObjectMozMap(const MozMap< RefPtr<TestInterface> > &);
+ void PassCastableObjectNullableMozMap(const Nullable< MozMap< OwningNonNull<TestInterface> > >&);
+ void PassNullableCastableObjectNullableMozMap(const Nullable< MozMap< RefPtr<TestInterface> > >&);
+ void PassOptionalMozMap(const Optional<MozMap<int32_t> >&);
+ void PassOptionalNullableMozMap(const Optional<Nullable<MozMap<int32_t> > >&);
+ void PassOptionalNullableMozMapWithDefaultValue(const Nullable< MozMap<int32_t> >&);
+ void PassOptionalObjectMozMap(const Optional<MozMap<OwningNonNull<TestInterface> > >&);
+ void PassExternalInterfaceMozMap(const MozMap<RefPtr<TestExternalInterface> >&);
+ void PassNullableExternalInterfaceMozMap(const MozMap<RefPtr<TestExternalInterface> >&);
+ void PassStringMozMap(const MozMap<nsString>&);
+ void PassByteStringMozMap(const MozMap<nsCString>&);
+ void PassMozMapOfMozMaps(const MozMap< MozMap<int32_t> >&);
+ void ReceiveMozMap(MozMap<int32_t>&);
+ void ReceiveNullableMozMap(Nullable<MozMap<int32_t>>&);
+ void ReceiveMozMapOfNullableInts(MozMap<Nullable<int32_t>>&);
+ void ReceiveNullableMozMapOfNullableInts(Nullable<MozMap<Nullable<int32_t>>>&);
+ void ReceiveMozMapOfMozMaps(MozMap<MozMap<int32_t>>&);
+ void ReceiveAnyMozMap(JSContext*, MozMap<JS::Value>&);
+
+ // Typed array types
+ void PassArrayBuffer(const ArrayBuffer&);
+ void PassNullableArrayBuffer(const Nullable<ArrayBuffer>&);
+ void PassOptionalArrayBuffer(const Optional<ArrayBuffer>&);
+ void PassOptionalNullableArrayBuffer(const Optional<Nullable<ArrayBuffer> >&);
+ void PassOptionalNullableArrayBufferWithDefaultValue(const Nullable<ArrayBuffer>&);
+ void PassArrayBufferView(const ArrayBufferView&);
+ void PassInt8Array(const Int8Array&);
+ void PassInt16Array(const Int16Array&);
+ void PassInt32Array(const Int32Array&);
+ void PassUint8Array(const Uint8Array&);
+ void PassUint16Array(const Uint16Array&);
+ void PassUint32Array(const Uint32Array&);
+ void PassUint8ClampedArray(const Uint8ClampedArray&);
+ void PassFloat32Array(const Float32Array&);
+ void PassFloat64Array(const Float64Array&);
+ void PassSequenceOfArrayBuffers(const Sequence<ArrayBuffer>&);
+ void PassSequenceOfNullableArrayBuffers(const Sequence<Nullable<ArrayBuffer> >&);
+ void PassMozMapOfArrayBuffers(const MozMap<ArrayBuffer>&);
+ void PassMozMapOfNullableArrayBuffers(const MozMap<Nullable<ArrayBuffer> >&);
+ void PassVariadicTypedArray(const Sequence<Float32Array>&);
+ void PassVariadicNullableTypedArray(const Sequence<Nullable<Float32Array> >&);
+ void ReceiveUint8Array(JSContext*, JS::MutableHandle<JSObject*>);
+ void SetUint8ArrayAttr(const Uint8Array&);
+ void GetUint8ArrayAttr(JSContext*, JS::MutableHandle<JSObject*>);
+
+ // DOMString 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&);
+ void PassVariadicString(const Sequence<nsString>&);
+ void ReceiveString(DOMString&);
+
+ // ByteString types
+ void PassByteString(const nsCString&);
+ void PassNullableByteString(const nsCString&);
+ void PassOptionalByteString(const Optional<nsCString>&);
+ void PassOptionalByteStringWithDefaultValue(const nsCString&);
+ void PassOptionalNullableByteString(const Optional<nsCString>&);
+ void PassOptionalNullableByteStringWithDefaultValue(const nsCString&);
+ void PassVariadicByteString(const Sequence<nsCString>&);
+ void PassOptionalUnionByteString(const Optional<ByteStringOrLong>&);
+ void PassOptionalUnionByteStringWithDefaultValue(const ByteStringOrLong&);
+
+ // USVString types
+ void PassUSVS(const nsAString&);
+ void PassNullableUSVS(const nsAString&);
+ void PassOptionalUSVS(const Optional<nsAString>&);
+ void PassOptionalUSVSWithDefaultValue(const nsAString&);
+ void PassOptionalNullableUSVS(const Optional<nsAString>&);
+ void PassOptionalNullableUSVSWithDefaultValue(const nsAString&);
+ void PassVariadicUSVS(const Sequence<nsString>&);
+ void ReceiveUSVS(DOMString&);
+
+ // Enumerated types
+ void PassEnum(TestEnum);
+ void PassNullableEnum(const Nullable<TestEnum>&);
+ void PassOptionalEnum(const Optional<TestEnum>&);
+ void PassEnumWithDefault(TestEnum);
+ void PassOptionalNullableEnum(const Optional<Nullable<TestEnum> >&);
+ void PassOptionalNullableEnumWithDefaultValue(const Nullable<TestEnum>&);
+ void PassOptionalNullableEnumWithDefaultValue2(const Nullable<TestEnum>&);
+ TestEnum ReceiveEnum();
+ Nullable<TestEnum> ReceiveNullableEnum();
+ TestEnum EnumAttribute();
+ TestEnum ReadonlyEnumAttribute();
+ void SetEnumAttribute(TestEnum);
+
+ // Callback types
+ void PassCallback(TestCallback&);
+ void PassNullableCallback(TestCallback*);
+ void PassOptionalCallback(const Optional<OwningNonNull<TestCallback> >&);
+ void PassOptionalNullableCallback(const Optional<RefPtr<TestCallback> >&);
+ void PassOptionalNullableCallbackWithDefaultValue(TestCallback*);
+ already_AddRefed<TestCallback> ReceiveCallback();
+ already_AddRefed<TestCallback> ReceiveNullableCallback();
+ void PassNullableTreatAsNullCallback(TestTreatAsNullCallback*);
+ void PassOptionalNullableTreatAsNullCallback(const Optional<RefPtr<TestTreatAsNullCallback> >&);
+ void PassOptionalNullableTreatAsNullCallbackWithDefaultValue(TestTreatAsNullCallback*);
+ void SetTreatAsNullCallback(TestTreatAsNullCallback&);
+ already_AddRefed<TestTreatAsNullCallback> TreatAsNullCallback();
+ void SetNullableTreatAsNullCallback(TestTreatAsNullCallback*);
+ already_AddRefed<TestTreatAsNullCallback> GetNullableTreatAsNullCallback();
+
+ void ForceCallbackGeneration(TestIntegerReturn&,
+ TestNullableIntegerReturn&,
+ TestBooleanReturn&,
+ TestFloatReturn&,
+ TestStringReturn&,
+ TestEnumReturn&,
+ TestInterfaceReturn&,
+ TestNullableInterfaceReturn&,
+ TestExternalInterfaceReturn&,
+ TestNullableExternalInterfaceReturn&,
+ TestCallbackInterfaceReturn&,
+ TestNullableCallbackInterfaceReturn&,
+ TestCallbackReturn&,
+ TestNullableCallbackReturn&,
+ TestObjectReturn&,
+ TestNullableObjectReturn&,
+ TestTypedArrayReturn&,
+ TestNullableTypedArrayReturn&,
+ TestSequenceReturn&,
+ TestNullableSequenceReturn&,
+ TestIntegerArguments&,
+ TestInterfaceArguments&,
+ TestStringEnumArguments&,
+ TestObjectArguments&,
+ TestOptionalArguments&);
+
+ // Any types
+ void PassAny(JSContext*, JS::Handle<JS::Value>);
+ void PassVariadicAny(JSContext*, const Sequence<JS::Value>&);
+ void PassOptionalAny(JSContext*, JS::Handle<JS::Value>);
+ void PassAnyDefaultNull(JSContext*, JS::Handle<JS::Value>);
+ void PassSequenceOfAny(JSContext*, const Sequence<JS::Value>&);
+ void PassNullableSequenceOfAny(JSContext*, const Nullable<Sequence<JS::Value> >&);
+ void PassOptionalSequenceOfAny(JSContext*, const Optional<Sequence<JS::Value> >&);
+ void PassOptionalNullableSequenceOfAny(JSContext*, const Optional<Nullable<Sequence<JS::Value> > >&);
+ void PassOptionalSequenceOfAnyWithDefaultValue(JSContext*, const Nullable<Sequence<JS::Value> >&);
+ void PassSequenceOfSequenceOfAny(JSContext*, const Sequence<Sequence<JS::Value> >&);
+ void PassSequenceOfNullableSequenceOfAny(JSContext*, const Sequence<Nullable<Sequence<JS::Value> > >&);
+ void PassNullableSequenceOfNullableSequenceOfAny(JSContext*, const Nullable<Sequence<Nullable<Sequence<JS::Value> > > >&);
+ void PassOptionalNullableSequenceOfNullableSequenceOfAny(JSContext*, const Optional<Nullable<Sequence<Nullable<Sequence<JS::Value> > > > >&);
+ void PassMozMapOfAny(JSContext*, const MozMap<JS::Value>&);
+ void PassNullableMozMapOfAny(JSContext*, const Nullable<MozMap<JS::Value> >&);
+ void PassOptionalMozMapOfAny(JSContext*, const Optional<MozMap<JS::Value> >&);
+ void PassOptionalNullableMozMapOfAny(JSContext*, const Optional<Nullable<MozMap<JS::Value> > >&);
+ void PassOptionalMozMapOfAnyWithDefaultValue(JSContext*, const Nullable<MozMap<JS::Value> >&);
+ void PassMozMapOfMozMapOfAny(JSContext*, const MozMap<MozMap<JS::Value> >&);
+ void PassMozMapOfNullableMozMapOfAny(JSContext*, const MozMap<Nullable<MozMap<JS::Value> > >&);
+ void PassNullableMozMapOfNullableMozMapOfAny(JSContext*, const Nullable<MozMap<Nullable<MozMap<JS::Value> > > >&);
+ void PassOptionalNullableMozMapOfNullableMozMapOfAny(JSContext*, const Optional<Nullable<MozMap<Nullable<MozMap<JS::Value>>>>>&);
+ void PassOptionalNullableMozMapOfNullableSequenceOfAny(JSContext*, const Optional<Nullable<MozMap<Nullable<Sequence<JS::Value>>>>>&);
+ void PassOptionalNullableSequenceOfNullableMozMapOfAny(JSContext*, const Optional<Nullable<Sequence<Nullable<MozMap<JS::Value>>>>>&);
+ void ReceiveAny(JSContext*, JS::MutableHandle<JS::Value>);
+
+ // object types
+ void PassObject(JSContext*, JS::Handle<JSObject*>);
+ void PassVariadicObject(JSContext*, const Sequence<JSObject*>&);
+ void PassNullableObject(JSContext*, JS::Handle<JSObject*>);
+ void PassVariadicNullableObject(JSContext*, const Sequence<JSObject*>&);
+ void PassOptionalObject(JSContext*, const Optional<JS::Handle<JSObject*> >&);
+ void PassOptionalNullableObject(JSContext*, const Optional<JS::Handle<JSObject*> >&);
+ void PassOptionalNullableObjectWithDefaultValue(JSContext*, JS::Handle<JSObject*>);
+ void PassSequenceOfObject(JSContext*, const Sequence<JSObject*>&);
+ void PassSequenceOfNullableObject(JSContext*, const Sequence<JSObject*>&);
+ void PassNullableSequenceOfObject(JSContext*, const Nullable<Sequence<JSObject*> >&);
+ void PassOptionalNullableSequenceOfNullableSequenceOfObject(JSContext*, const Optional<Nullable<Sequence<Nullable<Sequence<JSObject*> > > > >&);
+ void PassOptionalNullableSequenceOfNullableSequenceOfNullableObject(JSContext*, const Optional<Nullable<Sequence<Nullable<Sequence<JSObject*> > > > >&);
+ void PassMozMapOfObject(JSContext*, const MozMap<JSObject*>&);
+ void ReceiveObject(JSContext*, JS::MutableHandle<JSObject*>);
+ void ReceiveNullableObject(JSContext*, JS::MutableHandle<JSObject*>);
+
+ // Union types
+ void PassUnion(JSContext*, const ObjectOrLong& arg);
+ void PassUnionWithNullable(JSContext* cx, const ObjectOrNullOrLong& arg)
+ {
+ OwningObjectOrLong returnValue;
+ if (arg.IsNull()) {
+ } else if (arg.IsObject()) {
+ JS::Rooted<JSObject*> obj(cx, arg.GetAsObject());
+ JS_GetClass(obj);
+ returnValue.SetAsObject() = obj;
+ } else {
+ int32_t i = arg.GetAsLong();
+ i += 1;
+ returnValue.SetAsLong() = i;
+ }
+ }
+#ifdef DEBUG
+ void PassUnion2(const LongOrBoolean& arg);
+ void PassUnion3(JSContext*, const ObjectOrLongOrBoolean& arg);
+ void PassUnion4(const NodeOrLongOrBoolean& arg);
+ void PassUnion5(JSContext*, const ObjectOrBoolean& arg);
+ void PassUnion6(JSContext*, const ObjectOrString& arg);
+ void PassUnion7(JSContext*, const ObjectOrStringOrLong& arg);
+ void PassUnion8(JSContext*, const ObjectOrStringOrBoolean& arg);
+ void PassUnion9(JSContext*, const ObjectOrStringOrLongOrBoolean& arg);
+ void PassUnion10(const EventInitOrLong& arg);
+ void PassUnion11(JSContext*, const CustomEventInitOrLong& arg);
+ void PassUnion12(const EventInitOrLong& arg);
+ void PassUnion13(JSContext*, const ObjectOrLongOrNull& arg);
+ void PassUnion14(JSContext*, const ObjectOrLongOrNull& arg);
+ void PassUnion15(const LongSequenceOrLong&);
+ void PassUnion16(const Optional<LongSequenceOrLong>&);
+ void PassUnion17(const LongSequenceOrNullOrLong&);
+ void PassUnion18(JSContext*, const ObjectSequenceOrLong&);
+ void PassUnion19(JSContext*, const Optional<ObjectSequenceOrLong>&);
+ void PassUnion20(JSContext*, const ObjectSequenceOrLong&);
+ void PassUnion21(const LongMozMapOrLong&);
+ void PassUnion22(JSContext*, const ObjectMozMapOrLong&);
+ void PassUnion23(const ImageDataSequenceOrLong&);
+ void PassUnion24(const ImageDataOrNullSequenceOrLong&);
+ void PassUnion25(const ImageDataSequenceSequenceOrLong&);
+ void PassUnion26(const ImageDataOrNullSequenceSequenceOrLong&);
+ void PassUnion27(const StringSequenceOrEventInit&);
+ void PassUnion28(const EventInitOrStringSequence&);
+ void PassUnionWithCallback(const EventHandlerNonNullOrNullOrLong& arg);
+ void PassUnionWithByteString(const ByteStringOrLong&);
+ void PassUnionWithMozMap(const StringMozMapOrString&);
+ void PassUnionWithMozMapAndSequence(const StringMozMapOrStringSequence&);
+ void PassUnionWithSequenceAndMozMap(const StringSequenceOrStringMozMap&);
+ void PassUnionWithUSVS(const USVStringOrLong&);
+#endif
+ 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 SupportedTypeOrObject&);
+ //void PassUnionWithCallback(JSContext*, const TestCallbackOrLong&);
+ void PassUnionWithObject(JSContext*, const ObjectOrLong&);
+
+ void PassUnionWithDefaultValue1(const DoubleOrString& arg);
+ void PassUnionWithDefaultValue2(const DoubleOrString& arg);
+ void PassUnionWithDefaultValue3(const DoubleOrString& arg);
+ void PassUnionWithDefaultValue4(const FloatOrString& arg);
+ void PassUnionWithDefaultValue5(const FloatOrString& arg);
+ void PassUnionWithDefaultValue6(const FloatOrString& arg);
+ void PassUnionWithDefaultValue7(const UnrestrictedDoubleOrString& arg);
+ void PassUnionWithDefaultValue8(const UnrestrictedDoubleOrString& arg);
+ void PassUnionWithDefaultValue9(const UnrestrictedDoubleOrString& arg);
+ void PassUnionWithDefaultValue10(const UnrestrictedDoubleOrString& arg);
+ void PassUnionWithDefaultValue11(const UnrestrictedFloatOrString& arg);
+ void PassUnionWithDefaultValue12(const UnrestrictedFloatOrString& arg);
+ void PassUnionWithDefaultValue13(const UnrestrictedFloatOrString& arg);
+ void PassUnionWithDefaultValue14(const DoubleOrByteString& arg);
+ void PassUnionWithDefaultValue15(const DoubleOrByteString& arg);
+ void PassUnionWithDefaultValue16(const DoubleOrByteString& arg);
+ void PassUnionWithDefaultValue17(const DoubleOrSupportedType& arg);
+ void PassUnionWithDefaultValue18(const DoubleOrSupportedType& arg);
+ void PassUnionWithDefaultValue19(const DoubleOrSupportedType& arg);
+
+ void PassNullableUnionWithDefaultValue1(const Nullable<DoubleOrString>& arg);
+ void PassNullableUnionWithDefaultValue2(const Nullable<DoubleOrString>& arg);
+ void PassNullableUnionWithDefaultValue3(const Nullable<DoubleOrString>& arg);
+ void PassNullableUnionWithDefaultValue4(const Nullable<FloatOrString>& arg);
+ void PassNullableUnionWithDefaultValue5(const Nullable<FloatOrString>& arg);
+ void PassNullableUnionWithDefaultValue6(const Nullable<FloatOrString>& arg);
+ void PassNullableUnionWithDefaultValue7(const Nullable<UnrestrictedDoubleOrString>& arg);
+ void PassNullableUnionWithDefaultValue8(const Nullable<UnrestrictedDoubleOrString>& arg);
+ void PassNullableUnionWithDefaultValue9(const Nullable<UnrestrictedDoubleOrString>& arg);
+ void PassNullableUnionWithDefaultValue10(const Nullable<UnrestrictedFloatOrString>& arg);
+ void PassNullableUnionWithDefaultValue11(const Nullable<UnrestrictedFloatOrString>& arg);
+ void PassNullableUnionWithDefaultValue12(const Nullable<UnrestrictedFloatOrString>& arg);
+ void PassNullableUnionWithDefaultValue13(const Nullable<DoubleOrByteString>& arg);
+ void PassNullableUnionWithDefaultValue14(const Nullable<DoubleOrByteString>& arg);
+ void PassNullableUnionWithDefaultValue15(const Nullable<DoubleOrByteString>& arg);
+ void PassNullableUnionWithDefaultValue16(const Nullable<DoubleOrByteString>& arg);
+ void PassNullableUnionWithDefaultValue17(const Nullable<DoubleOrSupportedType>& arg);
+ void PassNullableUnionWithDefaultValue18(const Nullable<DoubleOrSupportedType>& arg);
+ void PassNullableUnionWithDefaultValue19(const Nullable<DoubleOrSupportedType>& arg);
+ void PassNullableUnionWithDefaultValue20(const Nullable<DoubleOrSupportedType>& arg);
+
+ void PassSequenceOfUnions(const Sequence<OwningCanvasPatternOrCanvasGradient>&);
+ void PassSequenceOfUnions2(JSContext*, const Sequence<OwningObjectOrLong>&);
+ void PassVariadicUnion(const Sequence<OwningCanvasPatternOrCanvasGradient>&);
+
+ void PassSequenceOfNullableUnions(const Sequence<Nullable<OwningCanvasPatternOrCanvasGradient>>&);
+ void PassVariadicNullableUnion(const Sequence<Nullable<OwningCanvasPatternOrCanvasGradient>>&);
+ void PassMozMapOfUnions(const MozMap<OwningCanvasPatternOrCanvasGradient>&);
+ void PassMozMapOfUnions2(JSContext*, const MozMap<OwningObjectOrLong>&);
+
+ void ReceiveUnion(OwningCanvasPatternOrCanvasGradient&);
+ void ReceiveUnion2(JSContext*, OwningObjectOrLong&);
+ void ReceiveUnionContainingNull(OwningCanvasPatternOrNullOrCanvasGradient&);
+ void ReceiveNullableUnion(Nullable<OwningCanvasPatternOrCanvasGradient>&);
+ void ReceiveNullableUnion2(JSContext*, Nullable<OwningObjectOrLong>&);
+ void GetWritableUnion(OwningCanvasPatternOrCanvasGradient&);
+ void SetWritableUnion(const CanvasPatternOrCanvasGradient&);
+ void GetWritableUnionContainingNull(OwningCanvasPatternOrNullOrCanvasGradient&);
+ void SetWritableUnionContainingNull(const CanvasPatternOrNullOrCanvasGradient&);
+ void GetWritableNullableUnion(Nullable<OwningCanvasPatternOrCanvasGradient>&);
+ void SetWritableNullableUnion(const Nullable<CanvasPatternOrCanvasGradient>&);
+
+ // Date types
+ void PassDate(Date);
+ void PassNullableDate(const Nullable<Date>&);
+ void PassOptionalDate(const Optional<Date>&);
+ void PassOptionalNullableDate(const Optional<Nullable<Date> >&);
+ void PassOptionalNullableDateWithDefaultValue(const Nullable<Date>&);
+ void PassDateSequence(const Sequence<Date>&);
+ void PassDateMozMap(const MozMap<Date>&);
+ void PassNullableDateSequence(const Sequence<Nullable<Date> >&);
+ Date ReceiveDate();
+ Nullable<Date> ReceiveNullableDate();
+
+ // Promise types
+ void PassPromise(Promise&);
+ void PassNullablePromise(Promise*);
+ void PassOptionalPromise(const Optional<OwningNonNull<Promise>>&);
+ void PassOptionalNullablePromise(const Optional<RefPtr<Promise>>&);
+ void PassOptionalNullablePromiseWithDefaultValue(Promise*);
+ void PassPromiseSequence(const Sequence<OwningNonNull<Promise>>&);
+ void PassPromiseMozMap(const MozMap<RefPtr<Promise>>&);
+ void PassNullablePromiseSequence(const Sequence<RefPtr<Promise>> &);
+ Promise* ReceivePromise();
+ already_AddRefed<Promise> ReceiveAddrefedPromise();
+
+ // binaryNames tests
+ void MethodRenamedTo();
+ void OtherMethodRenamedTo();
+ void MethodRenamedTo(int8_t);
+ int8_t AttributeGetterRenamedTo();
+ int8_t AttributeRenamedTo();
+ void SetAttributeRenamedTo(int8_t);
+ int8_t OtherAttributeRenamedTo();
+ void SetOtherAttributeRenamedTo(int8_t);
+
+ // Dictionary tests
+ void PassDictionary(JSContext*, const Dict&);
+ void PassDictionary2(JSContext*, const Dict&);
+ void GetReadonlyDictionary(JSContext*, Dict&);
+ void GetReadonlyNullableDictionary(JSContext*, Nullable<Dict>&);
+ void GetWritableDictionary(JSContext*, Dict&);
+ void SetWritableDictionary(JSContext*, const Dict&);
+ void GetReadonlyFrozenDictionary(JSContext*, Dict&);
+ void GetReadonlyFrozenNullableDictionary(JSContext*, Nullable<Dict>&);
+ void GetWritableFrozenDictionary(JSContext*, Dict&);
+ void SetWritableFrozenDictionary(JSContext*, const Dict&);
+ void ReceiveDictionary(JSContext*, Dict&);
+ void ReceiveNullableDictionary(JSContext*, Nullable<Dict>&);
+ void PassOtherDictionary(const GrandparentDict&);
+ void PassSequenceOfDictionaries(JSContext*, const Sequence<Dict>&);
+ void PassMozMapOfDictionaries(const MozMap<GrandparentDict>&);
+ void PassDictionaryOrLong(JSContext*, const Dict&);
+ void PassDictionaryOrLong(int32_t);
+ void PassDictContainingDict(JSContext*, const DictContainingDict&);
+ void PassDictContainingSequence(JSContext*, const DictContainingSequence&);
+ void ReceiveDictContainingSequence(JSContext*, DictContainingSequence&);
+ void PassVariadicDictionary(JSContext*, const Sequence<Dict>&);
+
+ // Typedefs
+ void ExerciseTypedefInterfaces1(TestInterface&);
+ already_AddRefed<TestInterface> ExerciseTypedefInterfaces2(TestInterface*);
+ void ExerciseTypedefInterfaces3(TestInterface&);
+
+ // Deprecated methods and attributes
+ int8_t DeprecatedAttribute();
+ int8_t SetDeprecatedAttribute(int8_t);
+ int8_t DeprecatedMethod();
+ int8_t DeprecatedMethodWithContext(JSContext*, const JS::Value&);
+
+ // Static methods and attributes
+ static void StaticMethod(const GlobalObject&, bool);
+ static void StaticMethodWithContext(const GlobalObject&, const JS::Value&);
+ static bool StaticAttribute(const GlobalObject&);
+ static void SetStaticAttribute(const GlobalObject&, bool);
+ static void Assert(const GlobalObject&, bool);
+
+ // Deprecated static methods and attributes
+ static int8_t StaticDeprecatedAttribute(const GlobalObject&);
+ static int8_t SetStaticDeprecatedAttribute(const GlobalObject&, int8_t);
+ static int8_t StaticDeprecatedMethod(const GlobalObject&);
+ static int8_t StaticDeprecatedMethodWithContext(const GlobalObject&, const JS::Value&);
+
+ // Overload resolution tests
+ bool Overload1(TestInterface&);
+ TestInterface* Overload1(const nsAString&, TestInterface&);
+ void Overload2(TestInterface&);
+ void Overload2(JSContext*, const Dict&);
+ void Overload2(bool);
+ void Overload2(const nsAString&);
+ void Overload2(Date);
+ void Overload3(TestInterface&);
+ void Overload3(const TestCallback&);
+ void Overload3(bool);
+ void Overload4(TestInterface&);
+ void Overload4(TestCallbackInterface&);
+ void Overload4(const nsAString&);
+ void Overload5(int32_t);
+ void Overload5(TestEnum);
+ void Overload6(int32_t);
+ void Overload6(bool);
+ void Overload7(int32_t);
+ void Overload7(bool);
+ void Overload7(const nsCString&);
+ void Overload8(int32_t);
+ void Overload8(TestInterface&);
+ void Overload9(const Nullable<int32_t>&);
+ void Overload9(const nsAString&);
+ void Overload10(const Nullable<int32_t>&);
+ void Overload10(JSContext*, JS::Handle<JSObject*>);
+ void Overload11(int32_t);
+ void Overload11(const nsAString&);
+ void Overload12(int32_t);
+ void Overload12(const Nullable<bool>&);
+ void Overload13(const Nullable<int32_t>&);
+ void Overload13(bool);
+ void Overload14(const Optional<int32_t>&);
+ void Overload14(TestInterface&);
+ void Overload15(int32_t);
+ void Overload15(const Optional<NonNull<TestInterface> >&);
+ void Overload16(int32_t);
+ void Overload16(const Optional<TestInterface*>&);
+ void Overload17(const Sequence<int32_t>&);
+ void Overload17(const MozMap<int32_t>&);
+ void Overload18(const MozMap<nsString>&);
+ void Overload18(const Sequence<nsString>&);
+ void Overload19(const Sequence<int32_t>&);
+ void Overload19(JSContext*, const Dict&);
+ void Overload20(JSContext*, const Dict&);
+ void Overload20(const Sequence<int32_t>&);
+
+ // Variadic handling
+ void PassVariadicThirdArg(const nsAString&, int32_t,
+ const Sequence<OwningNonNull<TestInterface> >&);
+
+ // Conditionally exposed methods/attributes
+ bool Prefable1();
+ bool Prefable2();
+ bool Prefable3();
+ bool Prefable4();
+ bool Prefable5();
+ bool Prefable6();
+ bool Prefable7();
+ bool Prefable8();
+ bool Prefable9();
+ void Prefable10();
+ void Prefable11();
+ bool Prefable12();
+ void Prefable13();
+ bool Prefable14();
+ bool Prefable15();
+ bool Prefable16();
+ void Prefable17();
+ void Prefable18();
+ void Prefable19();
+ void Prefable20();
+ void Prefable21();
+ void Prefable22();
+ void Prefable23();
+ void Prefable24();
+
+ // Conditionally exposed methods/attributes involving [SecureContext]
+ bool ConditionalOnSecureContext1();
+ bool ConditionalOnSecureContext2();
+ bool ConditionalOnSecureContext3();
+ bool ConditionalOnSecureContext4();
+ void ConditionalOnSecureContext5();
+ void ConditionalOnSecureContext6();
+ void ConditionalOnSecureContext7();
+ void ConditionalOnSecureContext8();
+
+ // Miscellania
+ int32_t AttrWithLenientThis();
+ void SetAttrWithLenientThis(int32_t);
+ uint32_t UnforgeableAttr();
+ uint32_t UnforgeableAttr2();
+ uint32_t UnforgeableMethod();
+ uint32_t UnforgeableMethod2();
+ void Stringify(nsString&);
+ void PassRenamedInterface(nsRenamedInterface&);
+ TestInterface* PutForwardsAttr();
+ TestInterface* PutForwardsAttr2();
+ TestInterface* PutForwardsAttr3();
+ void GetJsonifierShouldSkipThis(JSContext*, JS::MutableHandle<JS::Value>);
+ void SetJsonifierShouldSkipThis(JSContext*, JS::Rooted<JS::Value>&);
+ TestParentInterface* JsonifierShouldSkipThis2();
+ void SetJsonifierShouldSkipThis2(TestParentInterface&);
+ TestCallbackInterface* JsonifierShouldSkipThis3();
+ void SetJsonifierShouldSkipThis3(TestCallbackInterface&);
+ void ThrowingMethod(ErrorResult& aRv);
+ bool GetThrowingAttr(ErrorResult& aRv) const;
+ void SetThrowingAttr(bool arg, ErrorResult& aRv);
+ bool GetThrowingGetterAttr(ErrorResult& aRv) const;
+ void SetThrowingGetterAttr(bool arg);
+ bool ThrowingSetterAttr() const;
+ void SetThrowingSetterAttr(bool arg, ErrorResult& aRv);
+ void NeedsSubjectPrincipalMethod(nsIPrincipal&);
+ bool NeedsSubjectPrincipalAttr(nsIPrincipal&);
+ void SetNeedsSubjectPrincipalAttr(bool, nsIPrincipal&);
+ void NeedsCallerTypeMethod(CallerType);
+ bool NeedsCallerTypeAttr(CallerType);
+ void SetNeedsCallerTypeAttr(bool, CallerType);
+ int16_t LegacyCall(const JS::Value&, uint32_t, TestInterface&);
+ void PassArgsWithDefaults(JSContext*, const Optional<int32_t>&,
+ TestInterface*, const Dict&, double,
+ const Optional<float>&);
+
+ void SetDashed_attribute(int8_t);
+ int8_t Dashed_attribute();
+ void Dashed_method();
+
+ // 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);
+ void SetEnforcedByte(int8_t);
+ int8_t EnforcedByte();
+ void SetClampedByte(int8_t);
+ int8_t ClampedByte();
+
+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) = delete;
+ template<typename T>
+ void SetWritableByte(T) = delete;
+ template<typename T>
+ void PassByte(T) = delete;
+ void PassNullableByte(Nullable<int8_t>&) = delete;
+ template<typename T>
+ void PassOptionalByte(const Optional<T>&) = delete;
+ template<typename T>
+ void PassOptionalByteWithDefault(T) = delete;
+ void PassVariadicByte(Sequence<int8_t>&) = delete;
+
+ void SetReadonlyShort(int16_t) = delete;
+ template<typename T>
+ void SetWritableShort(T) = delete;
+ template<typename T>
+ void PassShort(T) = delete;
+ template<typename T>
+ void PassOptionalShort(const Optional<T>&) = delete;
+ template<typename T>
+ void PassOptionalShortWithDefault(T) = delete;
+
+ void SetReadonlyLong(int32_t) = delete;
+ template<typename T>
+ void SetWritableLong(T) = delete;
+ template<typename T>
+ void PassLong(T) = delete;
+ template<typename T>
+ void PassOptionalLong(const Optional<T>&) = delete;
+ template<typename T>
+ void PassOptionalLongWithDefault(T) = delete;
+
+ void SetReadonlyLongLong(int64_t) = delete;
+ template<typename T>
+ void SetWritableLongLong(T) = delete;
+ template<typename T>
+ void PassLongLong(T) = delete;
+ template<typename T>
+ void PassOptionalLongLong(const Optional<T>&) = delete;
+ template<typename T>
+ void PassOptionalLongLongWithDefault(T) = delete;
+
+ void SetReadonlyOctet(uint8_t) = delete;
+ template<typename T>
+ void SetWritableOctet(T) = delete;
+ template<typename T>
+ void PassOctet(T) = delete;
+ template<typename T>
+ void PassOptionalOctet(const Optional<T>&) = delete;
+ template<typename T>
+ void PassOptionalOctetWithDefault(T) = delete;
+
+ void SetReadonlyUnsignedShort(uint16_t) = delete;
+ template<typename T>
+ void SetWritableUnsignedShort(T) = delete;
+ template<typename T>
+ void PassUnsignedShort(T) = delete;
+ template<typename T>
+ void PassOptionalUnsignedShort(const Optional<T>&) = delete;
+ template<typename T>
+ void PassOptionalUnsignedShortWithDefault(T) = delete;
+
+ void SetReadonlyUnsignedLong(uint32_t) = delete;
+ template<typename T>
+ void SetWritableUnsignedLong(T) = delete;
+ template<typename T>
+ void PassUnsignedLong(T) = delete;
+ template<typename T>
+ void PassOptionalUnsignedLong(const Optional<T>&) = delete;
+ template<typename T>
+ void PassOptionalUnsignedLongWithDefault(T) = delete;
+
+ void SetReadonlyUnsignedLongLong(uint64_t) = delete;
+ template<typename T>
+ void SetWritableUnsignedLongLong(T) = delete;
+ template<typename T>
+ void PassUnsignedLongLong(T) = delete;
+ template<typename T>
+ void PassOptionalUnsignedLongLong(const Optional<T>&) = delete;
+ template<typename T>
+ void PassOptionalUnsignedLongLongWithDefault(T) = delete;
+
+ // Enforce that only const things are passed for sequences
+ void PassSequence(Sequence<int32_t> &) = delete;
+ void PassNullableSequence(Nullable< Sequence<int32_t> >&) = delete;
+ void PassOptionalNullableSequenceWithDefaultValue(Nullable< Sequence<int32_t> >&) = delete;
+ void PassSequenceOfAny(JSContext*, Sequence<JS::Value>&) = delete;
+ void PassNullableSequenceOfAny(JSContext*, Nullable<Sequence<JS::Value> >&) = delete;
+ void PassOptionalSequenceOfAny(JSContext*, Optional<Sequence<JS::Value> >&) = delete;
+ void PassOptionalNullableSequenceOfAny(JSContext*, Optional<Nullable<Sequence<JS::Value> > >&) = delete;
+ void PassOptionalSequenceOfAnyWithDefaultValue(JSContext*, Nullable<Sequence<JS::Value> >&) = delete;
+ void PassSequenceOfSequenceOfAny(JSContext*, Sequence<Sequence<JS::Value> >&) = delete;
+ void PassSequenceOfNullableSequenceOfAny(JSContext*, Sequence<Nullable<Sequence<JS::Value> > >&) = delete;
+ void PassNullableSequenceOfNullableSequenceOfAny(JSContext*, Nullable<Sequence<Nullable<Sequence<JS::Value> > > >&) = delete;
+ void PassOptionalNullableSequenceOfNullableSequenceOfAny(JSContext*, Optional<Nullable<Sequence<Nullable<Sequence<JS::Value> > > > >&) = delete;
+ void PassSequenceOfObject(JSContext*, Sequence<JSObject*>&) = delete;
+ void PassSequenceOfNullableObject(JSContext*, Sequence<JSObject*>&) = delete;
+ void PassOptionalNullableSequenceOfNullableSequenceOfObject(JSContext*, Optional<Nullable<Sequence<Nullable<Sequence<JSObject*> > > > >&) = delete;
+ void PassOptionalNullableSequenceOfNullableSequenceOfNullableObject(JSContext*, Optional<Nullable<Sequence<Nullable<Sequence<JSObject*> > > > >&) = delete;
+
+ // Enforce that only const things are passed for optional
+ void PassOptionalByte(Optional<int8_t>&) = delete;
+ void PassOptionalNullableByte(Optional<Nullable<int8_t> >&) = delete;
+ void PassOptionalShort(Optional<int16_t>&) = delete;
+ void PassOptionalLong(Optional<int32_t>&) = delete;
+ void PassOptionalLongLong(Optional<int64_t>&) = delete;
+ void PassOptionalOctet(Optional<uint8_t>&) = delete;
+ void PassOptionalUnsignedShort(Optional<uint16_t>&) = delete;
+ void PassOptionalUnsignedLong(Optional<uint32_t>&) = delete;
+ void PassOptionalUnsignedLongLong(Optional<uint64_t>&) = delete;
+ void PassOptionalSelf(Optional<TestInterface*> &) = delete;
+ void PassOptionalNonNullSelf(Optional<NonNull<TestInterface> >&) = delete;
+ void PassOptionalOther(Optional<IndirectlyImplementedInterface*>&);
+ void PassOptionalNonNullOther(Optional<NonNull<IndirectlyImplementedInterface> >&);
+ void PassOptionalExternal(Optional<TestExternalInterface*>&) = delete;
+ void PassOptionalNonNullExternal(Optional<TestExternalInterface*>&) = delete;
+ void PassOptionalSequence(Optional<Sequence<int32_t> >&) = delete;
+ void PassOptionalNullableSequence(Optional<Nullable<Sequence<int32_t> > >&) = delete;
+ void PassOptionalObjectSequence(Optional<Sequence<OwningNonNull<TestInterface> > >&) = delete;
+ void PassOptionalArrayBuffer(Optional<ArrayBuffer>&) = delete;
+ void PassOptionalNullableArrayBuffer(Optional<ArrayBuffer*>&) = delete;
+ void PassOptionalEnum(Optional<TestEnum>&) = delete;
+ void PassOptionalCallback(JSContext*, Optional<OwningNonNull<TestCallback> >&) = delete;
+ void PassOptionalNullableCallback(JSContext*, Optional<RefPtr<TestCallback> >&) = delete;
+ void PassOptionalAny(Optional<JS::Handle<JS::Value> >&) = delete;
+
+ // And test that string stuff is always const
+ void PassString(nsAString&) = delete;
+ void PassNullableString(nsAString&) = delete;
+ void PassOptionalString(Optional<nsAString>&) = delete;
+ void PassOptionalStringWithDefaultValue(nsAString&) = delete;
+ void PassOptionalNullableString(Optional<nsAString>&) = delete;
+ void PassOptionalNullableStringWithDefaultValue(nsAString&) = delete;
+ void PassVariadicString(Sequence<nsString>&) = delete;
+
+ // cstrings should be const as well
+ void PassByteString(nsCString&) = delete;
+ void PassNullableByteString(nsCString&) = delete;
+ void PassOptionalByteString(Optional<nsCString>&) = delete;
+ void PassOptionalByteStringWithDefaultValue(nsCString&) = delete;
+ void PassOptionalNullableByteString(Optional<nsCString>&) = delete;
+ void PassOptionalNullableByteStringWithDefaultValue(nsCString&) = delete;
+ void PassVariadicByteString(Sequence<nsCString>&) = delete;
+
+ // Make sure dictionary arguments are always const
+ void PassDictionary(JSContext*, Dict&) = delete;
+ void PassOtherDictionary(GrandparentDict&) = delete;
+ void PassSequenceOfDictionaries(JSContext*, Sequence<Dict>&) = delete;
+ void PassDictionaryOrLong(JSContext*, Dict&) = delete;
+ void PassDictContainingDict(JSContext*, DictContainingDict&) = delete;
+ void PassDictContainingSequence(DictContainingSequence&) = delete;
+
+ // Make sure various nullable things are always const
+ void PassNullableEnum(Nullable<TestEnum>&) = delete;
+
+ // Make sure unions are always const
+ void PassUnion(JSContext*, ObjectOrLong& arg) = delete;
+ void PassUnionWithNullable(JSContext*, ObjectOrNullOrLong& arg) = delete;
+ void PassNullableUnion(JSContext*, Nullable<ObjectOrLong>&) = delete;
+ void PassOptionalUnion(JSContext*, Optional<ObjectOrLong>&) = delete;
+ void PassOptionalNullableUnion(JSContext*, Optional<Nullable<ObjectOrLong> >&) = delete;
+ void PassOptionalNullableUnionWithDefaultValue(JSContext*, Nullable<ObjectOrLong>&) = delete;
+
+ // Make sure various date stuff is const as needed
+ void PassNullableDate(Nullable<Date>&) = delete;
+ void PassOptionalDate(Optional<Date>&) = delete;
+ void PassOptionalNullableDate(Optional<Nullable<Date> >&) = delete;
+ void PassOptionalNullableDateWithDefaultValue(Nullable<Date>&) = delete;
+ void PassDateSequence(Sequence<Date>&) = delete;
+ void PassNullableDateSequence(Sequence<Nullable<Date> >&) = delete;
+
+ // Make sure variadics are const as needed
+ void PassVariadicAny(JSContext*, Sequence<JS::Value>&) = delete;
+ void PassVariadicObject(JSContext*, Sequence<JSObject*>&) = delete;
+ void PassVariadicNullableObject(JSContext*, Sequence<JSObject*>&) = delete;
+
+ // Ensure NonNull does not leak in
+ void PassSelf(NonNull<TestInterface>&) = delete;
+ void PassSelf(OwningNonNull<TestInterface>&) = delete;
+ void PassSelf(const NonNull<TestInterface>&) = delete;
+ void PassSelf(const OwningNonNull<TestInterface>&) = delete;
+ void PassOther(NonNull<IndirectlyImplementedInterface>&) = delete;
+ void PassOther(const NonNull<IndirectlyImplementedInterface>&) = delete;
+ void PassOther(OwningNonNull<IndirectlyImplementedInterface>&) = delete;
+ void PassOther(const OwningNonNull<IndirectlyImplementedInterface>&) = delete;
+ void PassCallbackInterface(OwningNonNull<TestCallbackInterface>&) = delete;
+ void PassCallbackInterface(const OwningNonNull<TestCallbackInterface>&) = delete;
+ void PassCallbackInterface(NonNull<TestCallbackInterface>&) = delete;
+ void PassCallbackInterface(const NonNull<TestCallbackInterface>&) = delete;
+ void PassCallback(OwningNonNull<TestCallback>&) = delete;
+ void PassCallback(const OwningNonNull<TestCallback>&) = delete;
+ void PassCallback(NonNull<TestCallback>&) = delete;
+ void PassCallback(const NonNull<TestCallback>&) = delete;
+ void PassString(const NonNull<nsAString>&) = delete;
+ void PassString(NonNull<nsAString>&) = delete;
+ void PassString(const OwningNonNull<nsAString>&) = delete;
+ void PassString(OwningNonNull<nsAString>&) = 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&) = delete;
+ uint32_t Item(uint32_t&);
+ uint32_t Item(uint32_t, bool&) = delete;
+ uint32_t Length();
+ void LegacyCall(JS::Handle<JS::Value>);
+};
+
+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&);
+ void GetSupportedNames(nsTArray<nsString>&);
+};
+
+class TestIndexedGetterAndSetterAndNamedGetterInterface : 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&);
+ void GetSupportedNames(nsTArray<nsString>&);
+ int32_t IndexedGetter(uint32_t, bool&);
+ void IndexedSetter(uint32_t, int32_t);
+ uint32_t Length();
+};
+
+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();
+ void GetSupportedNames(nsTArray<nsString>&);
+};
+
+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 IndexedGetter(uint32_t, bool&, nsString&);
+ uint32_t Length();
+ 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&);
+ TestIndexedSetterInterface* NamedGetter(const nsAString&, bool&);
+ void GetSupportedNames(nsTArray<nsString>&);
+};
+
+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&);
+ TestIndexedSetterInterface* IndexedGetter(uint32_t, bool&);
+ uint32_t Length();
+ void NamedSetter(const nsAString&, TestIndexedSetterInterface&);
+ TestIndexedSetterInterface* NamedGetter(const nsAString&, bool&);
+ void SetNamedItem(const nsAString&, TestIndexedSetterInterface&);
+ void GetSupportedNames(nsTArray<nsString>&);
+};
+
+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&) = delete;
+ void NamedSetter(const nsAString&, const nsAString&);
+ void Stringify(nsAString&);
+ uint32_t Length();
+ void GetSupportedNames(nsTArray<nsString>&);
+};
+
+class TestCppKeywordNamedMethodsInterface : public nsISupports,
+ public nsWrapperCache
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ // We need a GetParentObject to make binding codegen happy
+ virtual nsISupports* GetParentObject();
+
+ bool Continue();
+ bool Delete();
+ int32_t Volatile();
+};
+
+class TestNamedDeleterInterface : public nsISupports,
+ public nsWrapperCache
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ // We need a GetParentObject to make binding codegen happy
+ virtual nsISupports* GetParentObject();
+
+ void NamedDeleter(const nsAString&, bool&);
+ long NamedGetter(const nsAString&, bool&);
+ void GetSupportedNames(nsTArray<nsString>&);
+};
+
+class TestNamedDeleterWithRetvalInterface : public nsISupports,
+ public nsWrapperCache
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ // We need a GetParentObject to make binding codegen happy
+ virtual nsISupports* GetParentObject();
+
+ bool NamedDeleter(const nsAString&, bool&);
+ bool NamedDeleter(const nsAString&) = delete;
+ long NamedGetter(const nsAString&, bool&);
+ bool DelNamedItem(const nsAString&);
+ bool DelNamedItem(const nsAString&, bool&) = delete;
+ void GetSupportedNames(nsTArray<nsString>&);
+};
+
+class TestParentInterface : public nsISupports,
+ public nsWrapperCache
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ // We need a GetParentObject to make binding codegen happy
+ virtual nsISupports* GetParentObject();
+};
+
+class TestChildInterface : public TestParentInterface
+{
+};
+
+class TestDeprecatedInterface : public nsISupports, public nsWrapperCache
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ static
+ already_AddRefed<TestDeprecatedInterface>
+ Constructor(const GlobalObject&, ErrorResult&);
+
+ static void AlsoDeprecated(const GlobalObject&);
+
+ virtual nsISupports* GetParentObject();
+};
+
+class TestInterfaceWithPromiseConstructorArg : public nsISupports, public nsWrapperCache
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ static
+ already_AddRefed<TestInterfaceWithPromiseConstructorArg>
+ Constructor(const GlobalObject&, Promise&, ErrorResult&);
+
+ virtual nsISupports* GetParentObject();
+};
+
+class TestSecureContextInterface : public nsISupports, public nsWrapperCache
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ static
+ already_AddRefed<TestSecureContextInterface>
+ Constructor(const GlobalObject&, ErrorResult&);
+
+ static void AlsoSecureContext(const GlobalObject&);
+
+ virtual nsISupports* GetParentObject();
+};
+
+class TestNamespace {
+public:
+ static bool Foo(const GlobalObject&);
+ static int32_t Bar(const GlobalObject&);
+ static void Baz(const GlobalObject&);
+};
+
+class TestRenamedNamespace {
+};
+
+class TestProtoObjectHackedNamespace {
+};
+
+class TestWorkerExposedInterface : public nsISupports,
+ public nsWrapperCache
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ // We need a GetParentObject to make binding codegen happy
+ nsISupports* GetParentObject();
+
+ void NeedsSubjectPrincipalMethod(Maybe<nsIPrincipal*>);
+ bool NeedsSubjectPrincipalAttr(Maybe<nsIPrincipal*>);
+ void SetNeedsSubjectPrincipalAttr(bool, Maybe<nsIPrincipal*>);
+ void NeedsCallerTypeMethod(CallerType);
+ bool NeedsCallerTypeAttr(CallerType);
+ void SetNeedsCallerTypeAttr(bool, CallerType);
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* TestBindingHeader_h */
diff --git a/dom/bindings/test/TestCImplementedInterface.h b/dom/bindings/test/TestCImplementedInterface.h
new file mode 100644
index 000000000..64b5c9954
--- /dev/null
+++ b/dom/bindings/test/TestCImplementedInterface.h
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 TestCImplementedInterface_h
+#define TestCImplementedInterface_h
+
+#include "../TestJSImplGenBinding.h"
+
+namespace mozilla {
+namespace dom {
+
+class TestCImplementedInterface : public TestJSImplInterface
+{
+public:
+ TestCImplementedInterface(JS::Handle<JSObject*> aJSImpl,
+ nsIGlobalObject* aParent)
+ : TestJSImplInterface(aJSImpl, aParent)
+ {}
+};
+
+class TestCImplementedInterface2 : public nsISupports,
+ public nsWrapperCache
+{
+public:
+ explicit TestCImplementedInterface2(nsIGlobalObject* aParent)
+ {}
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(TestCImplementedInterface2)
+
+ // We need a GetParentObject to make binding codegen happy
+ nsISupports* GetParentObject();
+};
+
+
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // TestCImplementedInterface_h
diff --git a/dom/bindings/test/TestCodeGen.webidl b/dom/bindings/test/TestCodeGen.webidl
new file mode 100644
index 000000000..4fb9be270
--- /dev/null
+++ b/dom/bindings/test/TestCodeGen.webidl
@@ -0,0 +1,1264 @@
+/* -*- 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;
+typedef CustomEventInit TestDictionaryTypedef;
+
+interface TestExternalInterface;
+
+[Pref="xyz"]
+interface TestRenamedInterface {
+};
+
+callback interface TestCallbackInterface {
+ readonly attribute long foo;
+ attribute DOMString bar;
+ void doSomething();
+ long doSomethingElse(DOMString arg, TestInterface otherArg);
+ void doSequenceLongArg(sequence<long> arg);
+ void doSequenceStringArg(sequence<DOMString> arg);
+ void doMozMapLongArg(MozMap<long> arg);
+ sequence<long> getSequenceOfLong();
+ sequence<TestInterface> getSequenceOfInterfaces();
+ sequence<TestInterface>? getNullableSequenceOfInterfaces();
+ sequence<TestInterface?> getSequenceOfNullableInterfaces();
+ sequence<TestInterface?>? getNullableSequenceOfNullableInterfaces();
+ sequence<TestCallbackInterface> getSequenceOfCallbackInterfaces();
+ sequence<TestCallbackInterface>? getNullableSequenceOfCallbackInterfaces();
+ sequence<TestCallbackInterface?> getSequenceOfNullableCallbackInterfaces();
+ sequence<TestCallbackInterface?>? getNullableSequenceOfNullableCallbackInterfaces();
+ MozMap<long> getMozMapOfLong();
+ Dict? getDictionary();
+ 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);
+ void passSequenceOfArrayBuffers(sequence<ArrayBuffer> arg);
+ void passSequenceOfNullableArrayBuffers(sequence<ArrayBuffer?> arg);
+ void passVariadicTypedArray(Float32Array... arg);
+ void passVariadicNullableTypedArray(Float32Array?... arg);
+ Uint8Array receiveUint8Array();
+ attribute Uint8Array uint8ArrayAttr;
+ Promise<void> receivePromise();
+};
+
+callback interface TestSingleOperationCallbackInterface {
+ TestInterface doSomething(short arg, sequence<double> anotherArg);
+};
+
+enum TestEnum {
+ "1",
+ "a",
+ "b"
+};
+
+callback TestCallback = void();
+[TreatNonCallableAsNull] callback TestTreatAsNullCallback = void();
+
+// Callback return value tests
+callback TestIntegerReturn = long();
+callback TestNullableIntegerReturn = long?();
+callback TestBooleanReturn = boolean();
+callback TestFloatReturn = float();
+callback TestStringReturn = DOMString(long arg);
+callback TestEnumReturn = TestEnum();
+callback TestInterfaceReturn = TestInterface();
+callback TestNullableInterfaceReturn = TestInterface?();
+callback TestExternalInterfaceReturn = TestExternalInterface();
+callback TestNullableExternalInterfaceReturn = TestExternalInterface?();
+callback TestCallbackInterfaceReturn = TestCallbackInterface();
+callback TestNullableCallbackInterfaceReturn = TestCallbackInterface?();
+callback TestCallbackReturn = TestCallback();
+callback TestNullableCallbackReturn = TestCallback?();
+callback TestObjectReturn = object();
+callback TestNullableObjectReturn = object?();
+callback TestTypedArrayReturn = ArrayBuffer();
+callback TestNullableTypedArrayReturn = ArrayBuffer?();
+callback TestSequenceReturn = sequence<boolean>();
+callback TestNullableSequenceReturn = sequence<boolean>?();
+// Callback argument tests
+callback TestIntegerArguments = sequence<long>(long arg1, long? arg2,
+ sequence<long> arg3,
+ sequence<long?>? arg4);
+callback TestInterfaceArguments = void(TestInterface arg1, TestInterface? arg2,
+ TestExternalInterface arg3,
+ TestExternalInterface? arg4,
+ TestCallbackInterface arg5,
+ TestCallbackInterface? arg6,
+ sequence<TestInterface> arg7,
+ sequence<TestInterface?>? arg8,
+ sequence<TestExternalInterface> arg9,
+ sequence<TestExternalInterface?>? arg10,
+ sequence<TestCallbackInterface> arg11,
+ sequence<TestCallbackInterface?>? arg12);
+callback TestStringEnumArguments = void(DOMString myString, DOMString? nullString,
+ TestEnum myEnum);
+callback TestObjectArguments = void(object anObj, object? anotherObj,
+ ArrayBuffer buf, ArrayBuffer? buf2);
+callback TestOptionalArguments = void(optional DOMString aString,
+ optional object something,
+ optional sequence<TestInterface> aSeq,
+ optional TestInterface? anInterface,
+ optional TestInterface anotherInterface,
+ optional long aLong);
+// If you add a new test callback, add it to the forceCallbackGeneration
+// method on TestInterface so it actually gets tested.
+
+TestInterface implements ImplementedInterface;
+
+// This interface is only for use in the constructor below
+interface OnlyForUseInConstructor {
+};
+
+[Constructor,
+ Constructor(DOMString str),
+ Constructor(unsigned long num, boolean? boolArg),
+ Constructor(TestInterface? iface),
+ Constructor(long arg1, IndirectlyImplementedInterface iface),
+ Constructor(Date arg1),
+ Constructor(ArrayBuffer arrayBuf),
+ Constructor(Uint8Array typedArr),
+ // Constructor(long arg1, long arg2, (TestInterface or OnlyForUseInConstructor) arg3),
+ NamedConstructor=Test,
+ NamedConstructor=Test(DOMString str),
+ NamedConstructor=Test2(DictForConstructor dict, any any1, object obj1,
+ object? obj2, sequence<Dict> seq, optional any any2,
+ optional object obj3, optional object? obj4),
+ NamedConstructor=Test3((long or MozMap<any>) arg1)
+ ]
+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 passOptionalByteBeforeRequired(optional byte arg1, byte arg2);
+ void passOptionalByteWithDefault(optional byte arg = 0);
+ void passOptionalByteWithDefaultBeforeRequired(optional byte arg1 = 0, byte arg2);
+ void passNullableByte(byte? arg);
+ void passOptionalNullableByte(optional byte? arg);
+ void passVariadicByte(byte... arg);
+ [StoreInSlot, Pure]
+ readonly attribute byte cachedByte;
+ [StoreInSlot, Constant]
+ readonly attribute byte cachedConstantByte;
+ [StoreInSlot, Pure]
+ attribute byte cachedWritableByte;
+ [Affects=Nothing]
+ attribute byte sideEffectFreeByte;
+ [Affects=Nothing, DependsOn=DOMState]
+ attribute byte domDependentByte;
+ [Affects=Nothing, DependsOn=Nothing]
+ readonly attribute byte constantByte;
+ [DependsOn=DeviceState, Affects=Nothing]
+ readonly attribute byte deviceStateDependentByte;
+ [Affects=Nothing]
+ byte returnByteSideEffectFree();
+ [Affects=Nothing, DependsOn=DOMState]
+ byte returnDOMDependentByte();
+ [Affects=Nothing, DependsOn=Nothing]
+ byte returnConstantByte();
+ [DependsOn=DeviceState, Affects=Nothing]
+ byte returnDeviceStateDependentByte();
+
+ [UnsafeInPrerendering]
+ void unsafePrerenderMethod();
+ [UnsafeInPrerendering]
+ attribute long unsafePrerenderWritable;
+ [UnsafeInPrerendering]
+ readonly attribute long unsafePrerenderReadonly;
+ 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);
+
+ attribute float writableFloat;
+ attribute unrestricted float writableUnrestrictedFloat;
+ attribute float? writableNullableFloat;
+ attribute unrestricted float? writableNullableUnrestrictedFloat;
+ attribute double writableDouble;
+ attribute unrestricted double writableUnrestrictedDouble;
+ attribute double? writableNullableDouble;
+ attribute unrestricted double? writableNullableUnrestrictedDouble;
+ void passFloat(float arg1, unrestricted float arg2,
+ float? arg3, unrestricted float? arg4,
+ double arg5, unrestricted double arg6,
+ double? arg7, unrestricted double? arg8,
+ sequence<float> arg9, sequence<unrestricted float> arg10,
+ sequence<float?> arg11, sequence<unrestricted float?> arg12,
+ sequence<double> arg13, sequence<unrestricted double> arg14,
+ sequence<double?> arg15, sequence<unrestricted double?> arg16);
+ [LenientFloat]
+ void passLenientFloat(float arg1, unrestricted float arg2,
+ float? arg3, unrestricted float? arg4,
+ double arg5, unrestricted double arg6,
+ double? arg7, unrestricted double? arg8,
+ sequence<float> arg9,
+ sequence<unrestricted float> arg10,
+ sequence<float?> arg11,
+ sequence<unrestricted float?> arg12,
+ sequence<double> arg13,
+ sequence<unrestricted double> arg14,
+ sequence<double?> arg15,
+ sequence<unrestricted double?> arg16);
+ [LenientFloat]
+ attribute float lenientFloatAttr;
+ [LenientFloat]
+ attribute double lenientDoubleAttr;
+
+ void passUnrestricted(optional unrestricted float arg1 = 0,
+ optional unrestricted float arg2 = Infinity,
+ optional unrestricted float arg3 = -Infinity,
+ optional unrestricted float arg4 = NaN,
+ optional unrestricted double arg5 = 0,
+ optional unrestricted double arg6 = Infinity,
+ optional unrestricted double arg7 = -Infinity,
+ optional unrestricted double arg8 = NaN);
+
+ // Castable interface types
+ // XXXbz add tests for throwing versions of all the castable interface stuff
+ TestInterface receiveSelf();
+ TestInterface? receiveNullableSelf();
+ TestInterface receiveWeakSelf();
+ TestInterface? receiveWeakNullableSelf();
+ void passSelf(TestInterface arg);
+ void passNullableSelf(TestInterface? arg);
+ attribute TestInterface nonNullSelf;
+ attribute TestInterface? nullableSelf;
+ [Cached, Pure]
+ readonly attribute TestInterface cachedSelf;
+ // Optional arguments
+ void passOptionalSelf(optional TestInterface? arg);
+ void passOptionalNonNullSelf(optional TestInterface arg);
+ void passOptionalSelfWithDefault(optional TestInterface? arg = null);
+
+ // Non-wrapper-cache interface types
+ [NewObject]
+ TestNonWrapperCacheInterface receiveNonWrapperCacheInterface();
+ [NewObject]
+ TestNonWrapperCacheInterface? receiveNullableNonWrapperCacheInterface();
+ [NewObject]
+ sequence<TestNonWrapperCacheInterface> receiveNonWrapperCacheInterfaceSequence();
+ [NewObject]
+ sequence<TestNonWrapperCacheInterface?> receiveNullableNonWrapperCacheInterfaceSequence();
+ [NewObject]
+ sequence<TestNonWrapperCacheInterface>? receiveNonWrapperCacheInterfaceNullableSequence();
+ [NewObject]
+ sequence<TestNonWrapperCacheInterface?>? receiveNullableNonWrapperCacheInterfaceNullableSequence();
+
+ // Non-castable interface types
+ IndirectlyImplementedInterface receiveOther();
+ IndirectlyImplementedInterface? receiveNullableOther();
+ IndirectlyImplementedInterface receiveWeakOther();
+ IndirectlyImplementedInterface? receiveWeakNullableOther();
+ void passOther(IndirectlyImplementedInterface arg);
+ void passNullableOther(IndirectlyImplementedInterface? arg);
+ attribute IndirectlyImplementedInterface nonNullOther;
+ attribute IndirectlyImplementedInterface? nullableOther;
+ // Optional arguments
+ void passOptionalOther(optional IndirectlyImplementedInterface? arg);
+ void passOptionalNonNullOther(optional IndirectlyImplementedInterface arg);
+ void passOptionalOtherWithDefault(optional IndirectlyImplementedInterface? arg = null);
+
+ // External interface types
+ TestExternalInterface receiveExternal();
+ TestExternalInterface? receiveNullableExternal();
+ TestExternalInterface receiveWeakExternal();
+ TestExternalInterface? receiveWeakNullableExternal();
+ void passExternal(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();
+ void passCallbackInterface(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
+ [Cached, Pure]
+ readonly attribute sequence<long> readonlySequence;
+ [Cached, Pure]
+ readonly attribute sequence<Dict> readonlySequenceOfDictionaries;
+ [Cached, Pure]
+ readonly attribute sequence<Dict>? readonlyNullableSequenceOfDictionaries;
+ [Cached, Pure, Frozen]
+ readonly attribute sequence<Dict> readonlyFrozenSequence;
+ [Cached, Pure, Frozen]
+ readonly attribute sequence<Dict>? readonlyFrozenNullableSequence;
+ 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<TestCallbackInterface> receiveCallbackObjectSequence();
+ sequence<TestInterface?> receiveNullableCastableObjectSequence();
+ sequence<TestCallbackInterface?> receiveNullableCallbackObjectSequence();
+ 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 passOptionalSequenceWithDefaultValue(optional sequence<long> arg = []);
+ void passOptionalNullableSequence(optional sequence<long>? arg);
+ void passOptionalNullableSequenceWithDefaultValue(optional sequence<long>? arg = null);
+ void passOptionalNullableSequenceWithDefaultValue2(optional sequence<long>? arg = []);
+ void passOptionalObjectSequence(optional sequence<TestInterface> arg);
+ void passExternalInterfaceSequence(sequence<TestExternalInterface> arg);
+ void passNullableExternalInterfaceSequence(sequence<TestExternalInterface?> arg);
+
+ sequence<DOMString> receiveStringSequence();
+ void passStringSequence(sequence<DOMString> arg);
+
+ sequence<ByteString> receiveByteStringSequence();
+ void passByteStringSequence(sequence<ByteString> arg);
+
+ sequence<any> receiveAnySequence();
+ sequence<any>? receiveNullableAnySequence();
+ sequence<sequence<any>> receiveAnySequenceSequence();
+
+ sequence<object> receiveObjectSequence();
+ sequence<object?> receiveNullableObjectSequence();
+
+ void passSequenceOfSequences(sequence<sequence<long>> arg);
+ void passSequenceOfSequencesOfSequences(sequence<sequence<sequence<long>>> arg);
+ sequence<sequence<long>> receiveSequenceOfSequences();
+ sequence<sequence<sequence<long>>> receiveSequenceOfSequencesOfSequences();
+
+ // MozMap types
+ void passMozMap(MozMap<long> arg);
+ void passNullableMozMap(MozMap<long>? arg);
+ void passMozMapOfNullableInts(MozMap<long?> arg);
+ void passOptionalMozMapOfNullableInts(optional MozMap<long?> arg);
+ void passOptionalNullableMozMapOfNullableInts(optional MozMap<long?>? arg);
+ void passCastableObjectMozMap(MozMap<TestInterface> arg);
+ void passNullableCastableObjectMozMap(MozMap<TestInterface?> arg);
+ void passCastableObjectNullableMozMap(MozMap<TestInterface>? arg);
+ void passNullableCastableObjectNullableMozMap(MozMap<TestInterface?>? arg);
+ void passOptionalMozMap(optional MozMap<long> arg);
+ void passOptionalNullableMozMap(optional MozMap<long>? arg);
+ void passOptionalNullableMozMapWithDefaultValue(optional MozMap<long>? arg = null);
+ void passOptionalObjectMozMap(optional MozMap<TestInterface> arg);
+ void passExternalInterfaceMozMap(MozMap<TestExternalInterface> arg);
+ void passNullableExternalInterfaceMozMap(MozMap<TestExternalInterface?> arg);
+ void passStringMozMap(MozMap<DOMString> arg);
+ void passByteStringMozMap(MozMap<ByteString> arg);
+ void passMozMapOfMozMaps(MozMap<MozMap<long>> arg);
+ MozMap<long> receiveMozMap();
+ MozMap<long>? receiveNullableMozMap();
+ MozMap<long?> receiveMozMapOfNullableInts();
+ MozMap<long?>? receiveNullableMozMapOfNullableInts();
+ MozMap<MozMap<long>> receiveMozMapOfMozMaps();
+ MozMap<any> receiveAnyMozMap();
+
+ // 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);
+ void passSequenceOfArrayBuffers(sequence<ArrayBuffer> arg);
+ void passSequenceOfNullableArrayBuffers(sequence<ArrayBuffer?> arg);
+ void passMozMapOfArrayBuffers(MozMap<ArrayBuffer> arg);
+ void passMozMapOfNullableArrayBuffers(MozMap<ArrayBuffer?> arg);
+ void passVariadicTypedArray(Float32Array... arg);
+ void passVariadicNullableTypedArray(Float32Array?... arg);
+ Uint8Array receiveUint8Array();
+ attribute Uint8Array uint8ArrayAttr;
+
+ // DOMString 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);
+ void passVariadicString(DOMString... arg);
+ DOMString receiveString();
+
+ // ByteString types
+ void passByteString(ByteString arg);
+ void passNullableByteString(ByteString? arg);
+ void passOptionalByteString(optional ByteString arg);
+ void passOptionalByteStringWithDefaultValue(optional ByteString arg = "abc");
+ void passOptionalNullableByteString(optional ByteString? arg);
+ void passOptionalNullableByteStringWithDefaultValue(optional ByteString? arg = null);
+ void passVariadicByteString(ByteString... arg);
+ void passOptionalUnionByteString(optional (ByteString or long) arg);
+ void passOptionalUnionByteStringWithDefaultValue(optional (ByteString or long) arg = "abc");
+
+ // USVString types
+ void passUSVS(USVString arg);
+ void passNullableUSVS(USVString? arg);
+ void passOptionalUSVS(optional USVString arg);
+ void passOptionalUSVSWithDefaultValue(optional USVString arg = "abc");
+ void passOptionalNullableUSVS(optional USVString? arg);
+ void passOptionalNullableUSVSWithDefaultValue(optional USVString? arg = null);
+ void passVariadicUSVS(USVString... arg);
+ USVString receiveUSVS();
+
+ // Enumerated types
+ void passEnum(TestEnum arg);
+ 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);
+ void passOptionalNullableEnumWithDefaultValue2(optional TestEnum? arg = "a");
+ TestEnum receiveEnum();
+ TestEnum? receiveNullableEnum();
+ 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();
+ void passNullableTreatAsNullCallback(TestTreatAsNullCallback? arg);
+ void passOptionalNullableTreatAsNullCallback(optional TestTreatAsNullCallback? arg);
+ void passOptionalNullableTreatAsNullCallbackWithDefaultValue(optional TestTreatAsNullCallback? arg = null);
+ attribute TestTreatAsNullCallback treatAsNullCallback;
+ attribute TestTreatAsNullCallback? nullableTreatAsNullCallback;
+
+ // Force code generation of the various test callbacks we have.
+ void forceCallbackGeneration(TestIntegerReturn arg1,
+ TestNullableIntegerReturn arg2,
+ TestBooleanReturn arg3,
+ TestFloatReturn arg4,
+ TestStringReturn arg5,
+ TestEnumReturn arg6,
+ TestInterfaceReturn arg7,
+ TestNullableInterfaceReturn arg8,
+ TestExternalInterfaceReturn arg9,
+ TestNullableExternalInterfaceReturn arg10,
+ TestCallbackInterfaceReturn arg11,
+ TestNullableCallbackInterfaceReturn arg12,
+ TestCallbackReturn arg13,
+ TestNullableCallbackReturn arg14,
+ TestObjectReturn arg15,
+ TestNullableObjectReturn arg16,
+ TestTypedArrayReturn arg17,
+ TestNullableTypedArrayReturn arg18,
+ TestSequenceReturn arg19,
+ TestNullableSequenceReturn arg20,
+ TestIntegerArguments arg21,
+ TestInterfaceArguments arg22,
+ TestStringEnumArguments arg23,
+ TestObjectArguments arg24,
+ TestOptionalArguments arg25);
+
+ // Any types
+ void passAny(any arg);
+ void passVariadicAny(any... arg);
+ void passOptionalAny(optional any arg);
+ void passAnyDefaultNull(optional any arg = null);
+ void passSequenceOfAny(sequence<any> arg);
+ void passNullableSequenceOfAny(sequence<any>? arg);
+ void passOptionalSequenceOfAny(optional sequence<any> arg);
+ void passOptionalNullableSequenceOfAny(optional sequence<any>? arg);
+ void passOptionalSequenceOfAnyWithDefaultValue(optional sequence<any>? arg = null);
+ void passSequenceOfSequenceOfAny(sequence<sequence<any>> arg);
+ void passSequenceOfNullableSequenceOfAny(sequence<sequence<any>?> arg);
+ void passNullableSequenceOfNullableSequenceOfAny(sequence<sequence<any>?>? arg);
+ void passOptionalNullableSequenceOfNullableSequenceOfAny(optional sequence<sequence<any>?>? arg);
+ void passMozMapOfAny(MozMap<any> arg);
+ void passNullableMozMapOfAny(MozMap<any>? arg);
+ void passOptionalMozMapOfAny(optional MozMap<any> arg);
+ void passOptionalNullableMozMapOfAny(optional MozMap<any>? arg);
+ void passOptionalMozMapOfAnyWithDefaultValue(optional MozMap<any>? arg = null);
+ void passMozMapOfMozMapOfAny(MozMap<MozMap<any>> arg);
+ void passMozMapOfNullableMozMapOfAny(MozMap<MozMap<any>?> arg);
+ void passNullableMozMapOfNullableMozMapOfAny(MozMap<MozMap<any>?>? arg);
+ void passOptionalNullableMozMapOfNullableMozMapOfAny(optional MozMap<MozMap<any>?>? arg);
+ void passOptionalNullableMozMapOfNullableSequenceOfAny(optional MozMap<sequence<any>?>? arg);
+ void passOptionalNullableSequenceOfNullableMozMapOfAny(optional sequence<MozMap<any>?>? arg);
+ any receiveAny();
+
+ // object types
+ void passObject(object arg);
+ void passVariadicObject(object... arg);
+ void passNullableObject(object? arg);
+ void passVariadicNullableObject(object... arg);
+ void passOptionalObject(optional object arg);
+ void passOptionalNullableObject(optional object? arg);
+ void passOptionalNullableObjectWithDefaultValue(optional object? arg = null);
+ void passSequenceOfObject(sequence<object> arg);
+ void passSequenceOfNullableObject(sequence<object?> arg);
+ void passNullableSequenceOfObject(sequence<object>? arg);
+ void passOptionalNullableSequenceOfNullableSequenceOfObject(optional sequence<sequence<object>?>? arg);
+ void passOptionalNullableSequenceOfNullableSequenceOfNullableObject(optional sequence<sequence<object?>?>? arg);
+ void passMozMapOfObject(MozMap<object> arg);
+ object receiveObject();
+ object? receiveNullableObject();
+
+ // Union types
+ void passUnion((object or long) arg);
+ // Some union tests are debug-only to avoid creating all those
+ // unused union types in opt builds.
+#ifdef DEBUG
+ void passUnion2((long or boolean) arg);
+ void passUnion3((object or long or boolean) arg);
+ void passUnion4((Node or long or boolean) arg);
+ void passUnion5((object or boolean) arg);
+ void passUnion6((object or DOMString) arg);
+ void passUnion7((object or DOMString or long) arg);
+ void passUnion8((object or DOMString or boolean) arg);
+ void passUnion9((object or DOMString or long or boolean) arg);
+ void passUnion10(optional (EventInit or long) arg);
+ void passUnion11(optional (CustomEventInit or long) arg);
+ void passUnion12(optional (EventInit or long) arg = 5);
+ void passUnion13(optional (object or long?) arg = null);
+ void passUnion14(optional (object or long?) arg = 5);
+ void passUnion15((sequence<long> or long) arg);
+ void passUnion16(optional (sequence<long> or long) arg);
+ void passUnion17(optional (sequence<long>? or long) arg = 5);
+ void passUnion18((sequence<object> or long) arg);
+ void passUnion19(optional (sequence<object> or long) arg);
+ void passUnion20(optional (sequence<object> or long) arg = []);
+ void passUnion21((MozMap<long> or long) arg);
+ void passUnion22((MozMap<object> or long) arg);
+ void passUnion23((sequence<ImageData> or long) arg);
+ void passUnion24((sequence<ImageData?> or long) arg);
+ void passUnion25((sequence<sequence<ImageData>> or long) arg);
+ void passUnion26((sequence<sequence<ImageData?>> or long) arg);
+ void passUnion27(optional (sequence<DOMString> or EventInit) arg);
+ void passUnion28(optional (EventInit or sequence<DOMString>) arg);
+ void passUnionWithCallback((EventHandler or long) arg);
+ void passUnionWithByteString((ByteString or long) arg);
+ void passUnionWithMozMap((MozMap<DOMString> or DOMString) arg);
+ void passUnionWithMozMapAndSequence((MozMap<DOMString> or sequence<DOMString>) arg);
+ void passUnionWithSequenceAndMozMap((sequence<DOMString> or MozMap<DOMString>) arg);
+ void passUnionWithUSVS((USVString or long) arg);
+#endif
+ 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);
+ // Using an enum in a union. Note that we use some enum not declared in our
+ // binding file, because UnionTypes.h will need to include the binding header
+ // for this enum. Pick an enum from an interface that won't drag in too much
+ // stuff.
+ void passUnionWithEnum((SupportedType or object) arg);
+
+ // Trying to use a callback in a union won't include the test
+ // headers, unfortunately, so won't compile.
+ //void passUnionWithCallback((TestCallback or long) arg);
+ void passUnionWithObject((object or long) arg);
+ //void passUnionWithDict((Dict or long) arg);
+
+ void passUnionWithDefaultValue1(optional (double or DOMString) arg = "");
+ void passUnionWithDefaultValue2(optional (double or DOMString) arg = 1);
+ void passUnionWithDefaultValue3(optional (double or DOMString) arg = 1.5);
+ void passUnionWithDefaultValue4(optional (float or DOMString) arg = "");
+ void passUnionWithDefaultValue5(optional (float or DOMString) arg = 1);
+ void passUnionWithDefaultValue6(optional (float or DOMString) arg = 1.5);
+ void passUnionWithDefaultValue7(optional (unrestricted double or DOMString) arg = "");
+ void passUnionWithDefaultValue8(optional (unrestricted double or DOMString) arg = 1);
+ void passUnionWithDefaultValue9(optional (unrestricted double or DOMString) arg = 1.5);
+ void passUnionWithDefaultValue10(optional (unrestricted double or DOMString) arg = Infinity);
+ void passUnionWithDefaultValue11(optional (unrestricted float or DOMString) arg = "");
+ void passUnionWithDefaultValue12(optional (unrestricted float or DOMString) arg = 1);
+ void passUnionWithDefaultValue13(optional (unrestricted float or DOMString) arg = Infinity);
+ void passUnionWithDefaultValue14(optional (double or ByteString) arg = "");
+ void passUnionWithDefaultValue15(optional (double or ByteString) arg = 1);
+ void passUnionWithDefaultValue16(optional (double or ByteString) arg = 1.5);
+ void passUnionWithDefaultValue17(optional (double or SupportedType) arg = "text/html");
+ void passUnionWithDefaultValue18(optional (double or SupportedType) arg = 1);
+ void passUnionWithDefaultValue19(optional (double or SupportedType) arg = 1.5);
+
+ void passNullableUnionWithDefaultValue1(optional (double or DOMString)? arg = "");
+ void passNullableUnionWithDefaultValue2(optional (double or DOMString)? arg = 1);
+ void passNullableUnionWithDefaultValue3(optional (double or DOMString)? arg = null);
+ void passNullableUnionWithDefaultValue4(optional (float or DOMString)? arg = "");
+ void passNullableUnionWithDefaultValue5(optional (float or DOMString)? arg = 1);
+ void passNullableUnionWithDefaultValue6(optional (float or DOMString)? arg = null);
+ void passNullableUnionWithDefaultValue7(optional (unrestricted double or DOMString)? arg = "");
+ void passNullableUnionWithDefaultValue8(optional (unrestricted double or DOMString)? arg = 1);
+ void passNullableUnionWithDefaultValue9(optional (unrestricted double or DOMString)? arg = null);
+ void passNullableUnionWithDefaultValue10(optional (unrestricted float or DOMString)? arg = "");
+ void passNullableUnionWithDefaultValue11(optional (unrestricted float or DOMString)? arg = 1);
+ void passNullableUnionWithDefaultValue12(optional (unrestricted float or DOMString)? arg = null);
+ void passNullableUnionWithDefaultValue13(optional (double or ByteString)? arg = "");
+ void passNullableUnionWithDefaultValue14(optional (double or ByteString)? arg = 1);
+ void passNullableUnionWithDefaultValue15(optional (double or ByteString)? arg = 1.5);
+ void passNullableUnionWithDefaultValue16(optional (double or ByteString)? arg = null);
+ void passNullableUnionWithDefaultValue17(optional (double or SupportedType)? arg = "text/html");
+ void passNullableUnionWithDefaultValue18(optional (double or SupportedType)? arg = 1);
+ void passNullableUnionWithDefaultValue19(optional (double or SupportedType)? arg = 1.5);
+ void passNullableUnionWithDefaultValue20(optional (double or SupportedType)? arg = null);
+
+ void passSequenceOfUnions(sequence<(CanvasPattern or CanvasGradient)> arg);
+ void passSequenceOfUnions2(sequence<(object or long)> arg);
+ void passVariadicUnion((CanvasPattern or CanvasGradient)... arg);
+
+ void passSequenceOfNullableUnions(sequence<(CanvasPattern or CanvasGradient)?> arg);
+ void passVariadicNullableUnion((CanvasPattern or CanvasGradient)?... arg);
+ void passMozMapOfUnions(MozMap<(CanvasPattern or CanvasGradient)> arg);
+ // XXXbz no move constructor on some unions
+ // void passMozMapOfUnions2(MozMap<(object or long)> arg);
+
+ (CanvasPattern or CanvasGradient) receiveUnion();
+ (object or long) receiveUnion2();
+ (CanvasPattern? or CanvasGradient) receiveUnionContainingNull();
+ (CanvasPattern or CanvasGradient)? receiveNullableUnion();
+ (object or long)? receiveNullableUnion2();
+
+ attribute (CanvasPattern or CanvasGradient) writableUnion;
+ attribute (CanvasPattern? or CanvasGradient) writableUnionContainingNull;
+ attribute (CanvasPattern or CanvasGradient)? writableNullableUnion;
+
+ // Date types
+ void passDate(Date arg);
+ void passNullableDate(Date? arg);
+ void passOptionalDate(optional Date arg);
+ void passOptionalNullableDate(optional Date? arg);
+ void passOptionalNullableDateWithDefaultValue(optional Date? arg = null);
+ void passDateSequence(sequence<Date> arg);
+ void passNullableDateSequence(sequence<Date?> arg);
+ void passDateMozMap(MozMap<Date> arg);
+ Date receiveDate();
+ Date? receiveNullableDate();
+
+ // Promise types
+ void passPromise(Promise<any> arg);
+ void passNullablePromise(Promise<any>? arg);
+ void passOptionalPromise(optional Promise<any> arg);
+ void passOptionalNullablePromise(optional Promise<any>? arg);
+ void passOptionalNullablePromiseWithDefaultValue(optional Promise<any>? arg = null);
+ void passPromiseSequence(sequence<Promise<any>> arg);
+ void passNullablePromiseSequence(sequence<Promise<any>?> arg);
+ Promise<any> receivePromise();
+ Promise<any> receiveAddrefedPromise();
+
+ // binaryNames tests
+ void methodRenamedFrom();
+ [BinaryName="otherMethodRenamedTo"]
+ void otherMethodRenamedFrom();
+ void methodRenamedFrom(byte argument);
+ readonly attribute byte attributeGetterRenamedFrom;
+ attribute byte attributeRenamedFrom;
+ [BinaryName="otherAttributeRenamedTo"]
+ attribute byte otherAttributeRenamedFrom;
+
+ void passDictionary(optional Dict x);
+ void passDictionary2(Dict x);
+ [Cached, Pure]
+ readonly attribute Dict readonlyDictionary;
+ [Cached, Pure]
+ readonly attribute Dict? readonlyNullableDictionary;
+ [Cached, Pure]
+ attribute Dict writableDictionary;
+ [Cached, Pure, Frozen]
+ readonly attribute Dict readonlyFrozenDictionary;
+ [Cached, Pure, Frozen]
+ readonly attribute Dict? readonlyFrozenNullableDictionary;
+ [Cached, Pure, Frozen]
+ attribute Dict writableFrozenDictionary;
+ Dict receiveDictionary();
+ Dict? receiveNullableDictionary();
+ void passOtherDictionary(optional GrandparentDict x);
+ void passSequenceOfDictionaries(sequence<Dict> x);
+ void passMozMapOfDictionaries(MozMap<GrandparentDict> x);
+ // No support for nullable dictionaries inside a sequence (nor should there be)
+ // void passSequenceOfNullableDictionaries(sequence<Dict?> x);
+ void passDictionaryOrLong(optional Dict x);
+ void passDictionaryOrLong(long x);
+
+ void passDictContainingDict(optional DictContainingDict arg);
+ void passDictContainingSequence(optional DictContainingSequence arg);
+ DictContainingSequence receiveDictContainingSequence();
+ void passVariadicDictionary(Dict... arg);
+
+ // EnforceRange/Clamp tests
+ void dontEnforceRangeOrClamp(byte arg);
+ void doEnforceRange([EnforceRange] byte arg);
+ void doClamp([Clamp] byte arg);
+ [EnforceRange] attribute byte enforcedByte;
+ [Clamp] attribute byte clampedByte;
+
+ // Typedefs
+ const myLong myLongConstant = 5;
+ void exerciseTypedefInterfaces1(AnotherNameForTestInterface arg);
+ AnotherNameForTestInterface exerciseTypedefInterfaces2(NullableTestInterface arg);
+ void exerciseTypedefInterfaces3(YetAnotherNameForTestInterface arg);
+
+ // Deprecated methods and attributes
+ [Deprecated="GetAttributeNode"]
+ attribute byte deprecatedAttribute;
+ [Deprecated="GetAttributeNode"]
+ byte deprecatedMethod();
+ [Deprecated="GetAttributeNode"]
+ byte deprecatedMethodWithContext(any arg);
+
+ // Static methods and attributes
+ static attribute boolean staticAttribute;
+ static void staticMethod(boolean arg);
+ static void staticMethodWithContext(any arg);
+
+ // Testing static method with a reserved C++ keyword as the name
+ static void assert(boolean arg);
+
+ // Deprecated static methods and attributes
+ [Deprecated="GetAttributeNode"]
+ static attribute byte staticDeprecatedAttribute;
+ [Deprecated="GetAttributeNode"]
+ static void staticDeprecatedMethod();
+ [Deprecated="GetAttributeNode"]
+ static void staticDeprecatedMethodWithContext(any arg);
+
+ // Overload resolution tests
+ //void overload1(DOMString... strs);
+ boolean overload1(TestInterface arg);
+ TestInterface overload1(DOMString strs, TestInterface arg);
+ void overload2(TestInterface arg);
+ void overload2(optional Dict arg);
+ void overload2(boolean arg);
+ void overload2(DOMString arg);
+ void overload2(Date arg);
+ void overload3(TestInterface arg);
+ void overload3(TestCallback arg);
+ void overload3(boolean arg);
+ void overload4(TestInterface arg);
+ void overload4(TestCallbackInterface arg);
+ void overload4(DOMString arg);
+ void overload5(long arg);
+ void overload5(TestEnum arg);
+ void overload6(long arg);
+ void overload6(boolean arg);
+ void overload7(long arg);
+ void overload7(boolean arg);
+ void overload7(ByteString arg);
+ void overload8(long arg);
+ void overload8(TestInterface arg);
+ void overload9(long? arg);
+ void overload9(DOMString arg);
+ void overload10(long? arg);
+ void overload10(object arg);
+ void overload11(long arg);
+ void overload11(DOMString? arg);
+ void overload12(long arg);
+ void overload12(boolean? arg);
+ void overload13(long? arg);
+ void overload13(boolean arg);
+ void overload14(optional long arg);
+ void overload14(TestInterface arg);
+ void overload15(long arg);
+ void overload15(optional TestInterface arg);
+ void overload16(long arg);
+ void overload16(optional TestInterface? arg);
+ void overload17(sequence<long> arg);
+ void overload17(MozMap<long> arg);
+ void overload18(MozMap<DOMString> arg);
+ void overload18(sequence<DOMString> arg);
+ void overload19(sequence<long> arg);
+ void overload19(optional Dict arg);
+ void overload20(optional Dict arg);
+ void overload20(sequence<long> arg);
+
+ // Variadic handling
+ void passVariadicThirdArg(DOMString arg1, long arg2, TestInterface... arg3);
+
+ // Conditionally exposed methods/attributes
+ [Pref="abc.def"]
+ readonly attribute boolean prefable1;
+ [Pref="abc.def"]
+ readonly attribute boolean prefable2;
+ [Pref="ghi.jkl"]
+ readonly attribute boolean prefable3;
+ [Pref="ghi.jkl"]
+ readonly attribute boolean prefable4;
+ [Pref="abc.def"]
+ readonly attribute boolean prefable5;
+ [Pref="abc.def", Func="nsGenericHTMLElement::TouchEventsEnabled"]
+ readonly attribute boolean prefable6;
+ [Pref="abc.def", Func="nsGenericHTMLElement::TouchEventsEnabled"]
+ readonly attribute boolean prefable7;
+ [Pref="ghi.jkl", Func="nsGenericHTMLElement::TouchEventsEnabled"]
+ readonly attribute boolean prefable8;
+ [Pref="abc.def", Func="nsGenericHTMLElement::TouchEventsEnabled"]
+ readonly attribute boolean prefable9;
+ [Pref="abc.def"]
+ void prefable10();
+ [Pref="abc.def", Func="nsGenericHTMLElement::TouchEventsEnabled"]
+ void prefable11();
+ [Pref="abc.def", Func="TestFuncControlledMember"]
+ readonly attribute boolean prefable12;
+ [Pref="abc.def", Func="nsGenericHTMLElement::TouchEventsEnabled"]
+ void prefable13();
+ [Pref="abc.def", Func="TestFuncControlledMember"]
+ readonly attribute boolean prefable14;
+ [Func="TestFuncControlledMember"]
+ readonly attribute boolean prefable15;
+ [Func="TestFuncControlledMember"]
+ readonly attribute boolean prefable16;
+ [Pref="abc.def", Func="TestFuncControlledMember"]
+ void prefable17();
+ [Func="TestFuncControlledMember"]
+ void prefable18();
+ [Func="TestFuncControlledMember"]
+ void prefable19();
+ [Pref="abc.def", Func="TestFuncControlledMember", ChromeOnly]
+ void prefable20();
+
+ // Conditionally exposed methods/attributes involving [SecureContext]
+ [SecureContext]
+ readonly attribute boolean conditionalOnSecureContext1;
+ [SecureContext, Pref="abc.def"]
+ readonly attribute boolean conditionalOnSecureContext2;
+ [SecureContext, Pref="abc.def", Func="nsGenericHTMLElement::TouchEventsEnabled"]
+ readonly attribute boolean conditionalOnSecureContext3;
+ [SecureContext, Pref="abc.def", Func="TestFuncControlledMember"]
+ readonly attribute boolean conditionalOnSecureContext4;
+ [SecureContext]
+ void conditionalOnSecureContext5();
+ [SecureContext, Pref="abc.def"]
+ void conditionalOnSecureContext6();
+ [SecureContext, Pref="abc.def", Func="nsGenericHTMLElement::TouchEventsEnabled"]
+ void conditionalOnSecureContext7();
+ [SecureContext, Pref="abc.def", Func="TestFuncControlledMember"]
+ void conditionalOnSecureContext8();
+
+ // Miscellania
+ [LenientThis] attribute long attrWithLenientThis;
+ [Unforgeable] readonly attribute long unforgeableAttr;
+ [Unforgeable, ChromeOnly] readonly attribute long unforgeableAttr2;
+ [Unforgeable] long unforgeableMethod();
+ [Unforgeable, ChromeOnly] long unforgeableMethod2();
+ stringifier;
+ void passRenamedInterface(TestRenamedInterface arg);
+ [PutForwards=writableByte] readonly attribute TestInterface putForwardsAttr;
+ [PutForwards=writableByte, LenientThis] readonly attribute TestInterface putForwardsAttr2;
+ [PutForwards=writableByte, ChromeOnly] readonly attribute TestInterface putForwardsAttr3;
+ [Throws] void throwingMethod();
+ [Throws] attribute boolean throwingAttr;
+ [GetterThrows] attribute boolean throwingGetterAttr;
+ [SetterThrows] attribute boolean throwingSetterAttr;
+ [NeedsSubjectPrincipal] void needsSubjectPrincipalMethod();
+ [NeedsSubjectPrincipal] attribute boolean needsSubjectPrincipalAttr;
+ [NeedsCallerType] void needsCallerTypeMethod();
+ [NeedsCallerType] attribute boolean needsCallerTypeAttr;
+ legacycaller short(unsigned long arg1, TestInterface arg2);
+ void passArgsWithDefaults(optional long arg1,
+ optional TestInterface? arg2 = null,
+ optional Dict arg3, optional double arg4 = 5.0,
+ optional float arg5);
+
+ attribute any jsonifierShouldSkipThis;
+ attribute TestParentInterface jsonifierShouldSkipThis2;
+ attribute TestCallbackInterface jsonifierShouldSkipThis3;
+ jsonifier;
+
+ attribute byte dashed-attribute;
+ void dashed-method();
+
+ // If you add things here, add them to TestExampleGen and TestJSImplGen as well
+};
+
+interface TestParentInterface {
+};
+
+interface TestChildInterface : TestParentInterface {
+};
+
+interface TestNonWrapperCacheInterface {
+};
+
+[NoInterfaceObject]
+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;
+};
+
+[NoInterfaceObject]
+interface ImplementedInterface : ImplementedInterfaceParent {
+ void implementedMethod();
+ attribute boolean implementedProperty;
+
+ const long implementedConstant = 5;
+};
+
+[NoInterfaceObject]
+interface DiamondImplements {
+ readonly attribute long diamondImplementedProperty;
+};
+[NoInterfaceObject]
+interface DiamondBranch1A {
+};
+[NoInterfaceObject]
+interface DiamondBranch1B {
+};
+[NoInterfaceObject]
+interface DiamondBranch2A : DiamondImplements {
+};
+[NoInterfaceObject]
+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;
+ [EnforceRange] unsigned long enforcedUnsignedLong;
+ [Clamp] unsigned long clampedUnsignedLong;
+ DOMString str;
+ DOMString empty = "";
+ TestEnum otherEnum = "b";
+ DOMString otherStr = "def";
+ DOMString? yetAnotherStr = null;
+ DOMString template;
+ ByteString byteStr;
+ ByteString emptyByteStr = "";
+ ByteString otherByteStr = "def";
+ object someObj;
+ boolean prototype;
+ object? anotherObj = null;
+ TestCallback? someCallback = null;
+ any someAny;
+ any anotherAny = null;
+
+ unrestricted float urFloat = 0;
+ unrestricted float urFloat2 = 1.1;
+ unrestricted float urFloat3 = -1.1;
+ unrestricted float? urFloat4 = null;
+ unrestricted float infUrFloat = Infinity;
+ unrestricted float negativeInfUrFloat = -Infinity;
+ unrestricted float nanUrFloat = NaN;
+
+ unrestricted double urDouble = 0;
+ unrestricted double urDouble2 = 1.1;
+ unrestricted double urDouble3 = -1.1;
+ unrestricted double? urDouble4 = null;
+ unrestricted double infUrDouble = Infinity;
+ unrestricted double negativeInfUrDouble = -Infinity;
+ unrestricted double nanUrDouble = NaN;
+
+ (float or DOMString) floatOrString = "str";
+ (float or DOMString)? nullableFloatOrString = "str";
+ (object or long) objectOrLong;
+#ifdef DEBUG
+ (EventInit or long) eventInitOrLong;
+ (EventInit or long)? nullableEventInitOrLong;
+ (HTMLElement or long)? nullableHTMLElementOrLong;
+ // CustomEventInit is useful to test because it needs rooting.
+ (CustomEventInit or long) eventInitOrLong2;
+ (CustomEventInit or long)? nullableEventInitOrLong2;
+ (EventInit or long) eventInitOrLongWithDefaultValue = null;
+ (CustomEventInit or long) eventInitOrLongWithDefaultValue2 = null;
+ (EventInit or long) eventInitOrLongWithDefaultValue3 = 5;
+ (CustomEventInit or long) eventInitOrLongWithDefaultValue4 = 5;
+ (EventInit or long)? nullableEventInitOrLongWithDefaultValue = null;
+ (CustomEventInit or long)? nullableEventInitOrLongWithDefaultValue2 = null;
+ (EventInit or long)? nullableEventInitOrLongWithDefaultValue3 = 5;
+ (CustomEventInit or long)? nullableEventInitOrLongWithDefaultValue4 = 5;
+ (sequence<object> or long) objectSequenceOrLong;
+ (sequence<object> or long) objectSequenceOrLongWithDefaultValue1 = 1;
+ (sequence<object> or long) objectSequenceOrLongWithDefaultValue2 = [];
+ (sequence<object> or long)? nullableObjectSequenceOrLong;
+ (sequence<object> or long)? nullableObjectSequenceOrLongWithDefaultValue1 = 1;
+ (sequence<object> or long)? nullableObjectSequenceOrLongWithDefaultValue2 = [];
+#endif
+
+ ArrayBuffer arrayBuffer;
+ ArrayBuffer? nullableArrayBuffer;
+ Uint8Array uint8Array;
+ Float64Array? float64Array = null;
+
+ sequence<long> seq1;
+ sequence<long> seq2 = [];
+ sequence<long>? seq3;
+ sequence<long>? seq4 = null;
+ sequence<long>? seq5 = [];
+
+ long dashed-name;
+
+ required long requiredLong;
+ required object requiredObject;
+
+ CustomEventInit customEventInit;
+ TestDictionaryTypedef dictionaryTypedef;
+
+ Promise<void> promise;
+ sequence<Promise<void>> promiseSequence;
+};
+
+dictionary ParentDict : GrandparentDict {
+ long c = 5;
+ TestInterface someInterface;
+ TestInterface? someNullableInterface = null;
+ TestExternalInterface someExternalInterface;
+ any parentAny;
+};
+
+dictionary DictContainingDict {
+ Dict memberDict;
+};
+
+dictionary DictContainingSequence {
+ sequence<long> ourSequence;
+ sequence<TestInterface> ourSequence2;
+ sequence<any> ourSequence3;
+ sequence<object> ourSequence4;
+ sequence<object?> ourSequence5;
+ sequence<object>? ourSequence6;
+ sequence<object?>? ourSequence7;
+ sequence<object>? ourSequence8 = null;
+ sequence<object?>? ourSequence9 = null;
+ sequence<(float or DOMString)> ourSequence10;
+};
+
+dictionary DictForConstructor {
+ Dict dict;
+ DictContainingDict dict2;
+ sequence<Dict> seq1;
+ sequence<sequence<Dict>>? seq2;
+ sequence<sequence<Dict>?> seq3;
+ sequence<any> seq4;
+ sequence<any> seq5;
+ sequence<DictContainingSequence> seq6;
+ object obj1;
+ object? obj2;
+ any any1 = null;
+};
+
+dictionary DictWithConditionalMembers {
+ [ChromeOnly]
+ long chromeOnlyMember;
+ [Func="TestFuncControlledMember"]
+ long funcControlledMember;
+ [ChromeOnly, Func="nsGenericHTMLElement::TouchEventsEnabled"]
+ long chromeOnlyFuncControlledMember;
+};
+
+interface TestIndexedGetterInterface {
+ getter long item(unsigned long idx);
+ readonly attribute unsigned long length;
+ legacycaller void();
+};
+
+interface TestNamedGetterInterface {
+ getter DOMString (DOMString name);
+};
+
+interface TestIndexedGetterAndSetterAndNamedGetterInterface {
+ getter DOMString (DOMString myName);
+ getter long (unsigned long index);
+ setter creator void (unsigned long index, long arg);
+};
+
+interface TestIndexedAndNamedGetterInterface {
+ getter long (unsigned long index);
+ getter DOMString namedItem(DOMString name);
+ readonly attribute unsigned long length;
+};
+
+interface TestIndexedSetterInterface {
+ setter creator void setItem(unsigned long idx, DOMString item);
+ getter DOMString (unsigned long idx);
+};
+
+interface TestNamedSetterInterface {
+ setter creator void (DOMString myName, TestIndexedSetterInterface item);
+ getter TestIndexedSetterInterface (DOMString name);
+};
+
+interface TestIndexedAndNamedSetterInterface {
+ setter creator void (unsigned long index, TestIndexedSetterInterface item);
+ getter TestIndexedSetterInterface (unsigned long index);
+ setter creator void setNamedItem(DOMString name, TestIndexedSetterInterface item);
+ getter TestIndexedSetterInterface (DOMString name);
+};
+
+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);
+ stringifier DOMString ();
+ readonly attribute unsigned long length;
+};
+
+interface TestNamedDeleterInterface {
+ deleter void (DOMString name);
+ getter long (DOMString name);
+};
+
+interface TestNamedDeleterWithRetvalInterface {
+ deleter boolean delNamedItem(DOMString name);
+ getter long (DOMString name);
+};
+
+interface TestCppKeywordNamedMethodsInterface {
+ boolean continue();
+ boolean delete();
+ long volatile();
+};
+
+[Deprecated="GetAttributeNode", Constructor()]
+interface TestDeprecatedInterface {
+ static void alsoDeprecated();
+};
+
+
+[Constructor(Promise<void> promise)]
+interface TestInterfaceWithPromiseConstructorArg {
+};
+
+namespace TestNamespace {
+ readonly attribute boolean foo;
+ long bar();
+};
+
+partial namespace TestNamespace {
+ void baz();
+};
+
+[ClassString="RenamedNamespaceClassName"]
+namespace TestRenamedNamespace {
+};
+
+[ProtoObjectHack]
+namespace TestProtoObjectHackedNamespace {
+};
+
+[SecureContext]
+interface TestSecureContextInterface {
+ static void alsoSecureContext();
+};
+
+[Exposed=(Window,Worker)]
+interface TestWorkerExposedInterface {
+ [NeedsSubjectPrincipal] void needsSubjectPrincipalMethod();
+ [NeedsSubjectPrincipal] attribute boolean needsSubjectPrincipalAttr;
+ [NeedsCallerType] void needsCallerTypeMethod();
+ [NeedsCallerType] attribute boolean needsCallerTypeAttr;
+};
diff --git a/dom/bindings/test/TestDictionary.webidl b/dom/bindings/test/TestDictionary.webidl
new file mode 100644
index 000000000..3dd91bd65
--- /dev/null
+++ b/dom/bindings/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/dom/bindings/test/TestExampleGen.webidl b/dom/bindings/test/TestExampleGen.webidl
new file mode 100644
index 000000000..ea6387a84
--- /dev/null
+++ b/dom/bindings/test/TestExampleGen.webidl
@@ -0,0 +1,811 @@
+/* -*- 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/.
+ */
+[Constructor,
+ Constructor(DOMString str),
+ Constructor(unsigned long num, boolean? boolArg),
+ Constructor(TestInterface? iface),
+ Constructor(long arg1, IndirectlyImplementedInterface iface),
+ Constructor(Date arg1),
+ Constructor(ArrayBuffer arrayBuf),
+ Constructor(Uint8Array typedArr),
+ // Constructor(long arg1, long arg2, (TestInterface or OnlyForUseInConstructor) arg3),
+ NamedConstructor=Example,
+ NamedConstructor=Example(DOMString str),
+ NamedConstructor=Example2(DictForConstructor dict, any any1, object obj1,
+ object? obj2, sequence<Dict> seq, optional any any2,
+ optional object obj3, optional object? obj4),
+ NamedConstructor=Example2((long or MozMap<any>) arg1)
+ ]
+interface TestExampleInterface {
+ // 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 passOptionalByteBeforeRequired(optional byte arg1, byte arg2);
+ void passOptionalByteWithDefault(optional byte arg = 0);
+ void passOptionalByteWithDefaultBeforeRequired(optional byte arg1 = 0, byte arg2);
+ void passNullableByte(byte? arg);
+ void passOptionalNullableByte(optional byte? arg);
+ void passVariadicByte(byte... arg);
+ [Cached, Pure]
+ readonly attribute byte cachedByte;
+ [StoreInSlot, Constant]
+ readonly attribute byte cachedConstantByte;
+ [Cached, Pure]
+ attribute byte cachedWritableByte;
+ [Affects=Nothing]
+ attribute byte sideEffectFreeByte;
+ [Affects=Nothing, DependsOn=DOMState]
+ attribute byte domDependentByte;
+ [Affects=Nothing, DependsOn=Nothing]
+ readonly attribute byte constantByte;
+ [DependsOn=DeviceState, Affects=Nothing]
+ readonly attribute byte deviceStateDependentByte;
+ [Affects=Nothing]
+ byte returnByteSideEffectFree();
+ [Affects=Nothing, DependsOn=DOMState]
+ byte returnDOMDependentByte();
+ [Affects=Nothing, DependsOn=Nothing]
+ byte returnConstantByte();
+ [DependsOn=DeviceState, Affects=Nothing]
+ byte returnDeviceStateDependentByte();
+
+ 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);
+
+ attribute float writableFloat;
+ attribute unrestricted float writableUnrestrictedFloat;
+ attribute float? writableNullableFloat;
+ attribute unrestricted float? writableNullableUnrestrictedFloat;
+ attribute double writableDouble;
+ attribute unrestricted double writableUnrestrictedDouble;
+ attribute double? writableNullableDouble;
+ attribute unrestricted double? writableNullableUnrestrictedDouble;
+ void passFloat(float arg1, unrestricted float arg2,
+ float? arg3, unrestricted float? arg4,
+ double arg5, unrestricted double arg6,
+ double? arg7, unrestricted double? arg8,
+ sequence<float> arg9, sequence<unrestricted float> arg10,
+ sequence<float?> arg11, sequence<unrestricted float?> arg12,
+ sequence<double> arg13, sequence<unrestricted double> arg14,
+ sequence<double?> arg15, sequence<unrestricted double?> arg16);
+ [LenientFloat]
+ void passLenientFloat(float arg1, unrestricted float arg2,
+ float? arg3, unrestricted float? arg4,
+ double arg5, unrestricted double arg6,
+ double? arg7, unrestricted double? arg8,
+ sequence<float> arg9,
+ sequence<unrestricted float> arg10,
+ sequence<float?> arg11,
+ sequence<unrestricted float?> arg12,
+ sequence<double> arg13,
+ sequence<unrestricted double> arg14,
+ sequence<double?> arg15,
+ sequence<unrestricted double?> arg16);
+ [LenientFloat]
+ attribute float lenientFloatAttr;
+ [LenientFloat]
+ attribute double lenientDoubleAttr;
+
+ // Castable interface types
+ // XXXbz add tests for throwing versions of all the castable interface stuff
+ TestInterface receiveSelf();
+ TestInterface? receiveNullableSelf();
+ TestInterface receiveWeakSelf();
+ TestInterface? receiveWeakNullableSelf();
+ void passSelf(TestInterface arg);
+ void passNullableSelf(TestInterface? arg);
+ attribute TestInterface nonNullSelf;
+ attribute TestInterface? nullableSelf;
+ [Cached, Pure]
+ readonly attribute TestInterface cachedSelf;
+ // Optional arguments
+ void passOptionalSelf(optional TestInterface? arg);
+ void passOptionalNonNullSelf(optional TestInterface arg);
+ void passOptionalSelfWithDefault(optional TestInterface? arg = null);
+
+ // Non-wrapper-cache interface types
+ [NewObject]
+ TestNonWrapperCacheInterface receiveNonWrapperCacheInterface();
+ [NewObject]
+ TestNonWrapperCacheInterface? receiveNullableNonWrapperCacheInterface();
+ [NewObject]
+ sequence<TestNonWrapperCacheInterface> receiveNonWrapperCacheInterfaceSequence();
+ [NewObject]
+ sequence<TestNonWrapperCacheInterface?> receiveNullableNonWrapperCacheInterfaceSequence();
+ [NewObject]
+ sequence<TestNonWrapperCacheInterface>? receiveNonWrapperCacheInterfaceNullableSequence();
+ [NewObject]
+ sequence<TestNonWrapperCacheInterface?>? receiveNullableNonWrapperCacheInterfaceNullableSequence();
+
+ // Non-castable interface types
+ IndirectlyImplementedInterface receiveOther();
+ IndirectlyImplementedInterface? receiveNullableOther();
+ IndirectlyImplementedInterface receiveWeakOther();
+ IndirectlyImplementedInterface? receiveWeakNullableOther();
+ void passOther(IndirectlyImplementedInterface arg);
+ void passNullableOther(IndirectlyImplementedInterface? arg);
+ attribute IndirectlyImplementedInterface nonNullOther;
+ attribute IndirectlyImplementedInterface? nullableOther;
+ // Optional arguments
+ void passOptionalOther(optional IndirectlyImplementedInterface? arg);
+ void passOptionalNonNullOther(optional IndirectlyImplementedInterface arg);
+ void passOptionalOtherWithDefault(optional IndirectlyImplementedInterface? arg = null);
+
+ // External interface types
+ TestExternalInterface receiveExternal();
+ TestExternalInterface? receiveNullableExternal();
+ TestExternalInterface receiveWeakExternal();
+ TestExternalInterface? receiveWeakNullableExternal();
+ void passExternal(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();
+ void passCallbackInterface(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
+ [Cached, Pure]
+ readonly attribute sequence<long> readonlySequence;
+ [Cached, Pure]
+ readonly attribute sequence<Dict> readonlySequenceOfDictionaries;
+ [Cached, Pure]
+ readonly attribute sequence<Dict>? readonlyNullableSequenceOfDictionaries;
+ [Cached, Pure, Frozen]
+ readonly attribute sequence<long> readonlyFrozenSequence;
+ [Cached, Pure, Frozen]
+ readonly attribute sequence<long>? readonlyFrozenNullableSequence;
+ 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<TestCallbackInterface> receiveCallbackObjectSequence();
+ sequence<TestInterface?> receiveNullableCastableObjectSequence();
+ sequence<TestCallbackInterface?> receiveNullableCallbackObjectSequence();
+ 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 passOptionalSequenceWithDefaultValue(optional sequence<long> arg = []);
+ void passOptionalNullableSequence(optional sequence<long>? arg);
+ void passOptionalNullableSequenceWithDefaultValue(optional sequence<long>? arg = null);
+ void passOptionalNullableSequenceWithDefaultValue2(optional sequence<long>? arg = []);
+ void passOptionalObjectSequence(optional sequence<TestInterface> arg);
+ void passExternalInterfaceSequence(sequence<TestExternalInterface> arg);
+ void passNullableExternalInterfaceSequence(sequence<TestExternalInterface?> arg);
+
+ sequence<DOMString> receiveStringSequence();
+ void passStringSequence(sequence<DOMString> arg);
+
+ sequence<ByteString> receiveByteStringSequence();
+ void passByteStringSequence(sequence<ByteString> arg);
+
+ sequence<any> receiveAnySequence();
+ sequence<any>? receiveNullableAnySequence();
+ //XXXbz No support for sequence of sequence return values yet.
+ //sequence<sequence<any>> receiveAnySequenceSequence();
+
+ sequence<object> receiveObjectSequence();
+ sequence<object?> receiveNullableObjectSequence();
+
+ void passSequenceOfSequences(sequence<sequence<long>> arg);
+ void passSequenceOfSequencesOfSequences(sequence<sequence<sequence<long>>> arg);
+ //XXXbz No support for sequence of sequence return values yet.
+ //sequence<sequence<long>> receiveSequenceOfSequences();
+
+ // MozMap types
+ void passMozMap(MozMap<long> arg);
+ void passNullableMozMap(MozMap<long>? arg);
+ void passMozMapOfNullableInts(MozMap<long?> arg);
+ void passOptionalMozMapOfNullableInts(optional MozMap<long?> arg);
+ void passOptionalNullableMozMapOfNullableInts(optional MozMap<long?>? arg);
+ void passCastableObjectMozMap(MozMap<TestInterface> arg);
+ void passNullableCastableObjectMozMap(MozMap<TestInterface?> arg);
+ void passCastableObjectNullableMozMap(MozMap<TestInterface>? arg);
+ void passNullableCastableObjectNullableMozMap(MozMap<TestInterface?>? arg);
+ void passOptionalMozMap(optional MozMap<long> arg);
+ void passOptionalNullableMozMap(optional MozMap<long>? arg);
+ void passOptionalNullableMozMapWithDefaultValue(optional MozMap<long>? arg = null);
+ void passOptionalObjectMozMap(optional MozMap<TestInterface> arg);
+ void passExternalInterfaceMozMap(MozMap<TestExternalInterface> arg);
+ void passNullableExternalInterfaceMozMap(MozMap<TestExternalInterface?> arg);
+ void passStringMozMap(MozMap<DOMString> arg);
+ void passByteStringMozMap(MozMap<ByteString> arg);
+ void passMozMapOfMozMaps(MozMap<MozMap<long>> arg);
+ MozMap<long> receiveMozMap();
+ MozMap<long>? receiveNullableMozMap();
+ MozMap<long?> receiveMozMapOfNullableInts();
+ MozMap<long?>? receiveNullableMozMapOfNullableInts();
+ //XXXbz No support for MozMap of MozMaps return values yet.
+ //MozMap<MozMap<long>> receiveMozMapOfMozMaps();
+ MozMap<any> receiveAnyMozMap();
+
+ // 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);
+ void passSequenceOfArrayBuffers(sequence<ArrayBuffer> arg);
+ void passSequenceOfNullableArrayBuffers(sequence<ArrayBuffer?> arg);
+ void passMozMapOfArrayBuffers(MozMap<ArrayBuffer> arg);
+ void passMozMapOfNullableArrayBuffers(MozMap<ArrayBuffer?> arg);
+ void passVariadicTypedArray(Float32Array... arg);
+ void passVariadicNullableTypedArray(Float32Array?... arg);
+ Uint8Array receiveUint8Array();
+ attribute Uint8Array uint8ArrayAttr;
+
+ // DOMString 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);
+ void passVariadicString(DOMString... arg);
+
+ // ByteString types
+ void passByteString(ByteString arg);
+ void passNullableByteString(ByteString? arg);
+ void passOptionalByteString(optional ByteString arg);
+ void passOptionalByteStringWithDefaultValue(optional ByteString arg = "abc");
+ void passOptionalNullableByteString(optional ByteString? arg);
+ void passOptionalNullableByteStringWithDefaultValue(optional ByteString? arg = null);
+ void passVariadicByteString(ByteString... arg);
+ void passUnionByteString((ByteString or long) arg);
+ void passOptionalUnionByteString(optional (ByteString or long) arg);
+ void passOptionalUnionByteStringWithDefaultValue(optional (ByteString or long) arg = "abc");
+
+ // USVString types
+ void passSVS(USVString arg);
+ void passNullableSVS(USVString? arg);
+ void passOptionalSVS(optional USVString arg);
+ void passOptionalSVSWithDefaultValue(optional USVString arg = "abc");
+ void passOptionalNullableSVS(optional USVString? arg);
+ void passOptionalNullableSVSWithDefaultValue(optional USVString? arg = null);
+ void passVariadicSVS(USVString... arg);
+ USVString receiveSVS();
+
+ // Enumerated types
+ void passEnum(TestEnum arg);
+ 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);
+ void passOptionalNullableEnumWithDefaultValue2(optional TestEnum? arg = "a");
+ TestEnum receiveEnum();
+ TestEnum? receiveNullableEnum();
+ 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();
+ void passNullableTreatAsNullCallback(TestTreatAsNullCallback? arg);
+ void passOptionalNullableTreatAsNullCallback(optional TestTreatAsNullCallback? arg);
+ void passOptionalNullableTreatAsNullCallbackWithDefaultValue(optional TestTreatAsNullCallback? arg = null);
+
+ // Any types
+ void passAny(any arg);
+ void passVariadicAny(any... arg);
+ void passOptionalAny(optional any arg);
+ void passAnyDefaultNull(optional any arg = null);
+ void passSequenceOfAny(sequence<any> arg);
+ void passNullableSequenceOfAny(sequence<any>? arg);
+ void passOptionalSequenceOfAny(optional sequence<any> arg);
+ void passOptionalNullableSequenceOfAny(optional sequence<any>? arg);
+ void passOptionalSequenceOfAnyWithDefaultValue(optional sequence<any>? arg = null);
+ void passSequenceOfSequenceOfAny(sequence<sequence<any>> arg);
+ void passSequenceOfNullableSequenceOfAny(sequence<sequence<any>?> arg);
+ void passNullableSequenceOfNullableSequenceOfAny(sequence<sequence<any>?>? arg);
+ void passOptionalNullableSequenceOfNullableSequenceOfAny(optional sequence<sequence<any>?>? arg);
+ void passMozMapOfAny(MozMap<any> arg);
+ void passNullableMozMapOfAny(MozMap<any>? arg);
+ void passOptionalMozMapOfAny(optional MozMap<any> arg);
+ void passOptionalNullableMozMapOfAny(optional MozMap<any>? arg);
+ void passOptionalMozMapOfAnyWithDefaultValue(optional MozMap<any>? arg = null);
+ void passMozMapOfMozMapOfAny(MozMap<MozMap<any>> arg);
+ void passMozMapOfNullableMozMapOfAny(MozMap<MozMap<any>?> arg);
+ void passNullableMozMapOfNullableMozMapOfAny(MozMap<MozMap<any>?>? arg);
+ void passOptionalNullableMozMapOfNullableMozMapOfAny(optional MozMap<MozMap<any>?>? arg);
+ void passOptionalNullableMozMapOfNullableSequenceOfAny(optional MozMap<sequence<any>?>? arg);
+ void passOptionalNullableSequenceOfNullableMozMapOfAny(optional sequence<MozMap<any>?>? arg);
+ any receiveAny();
+
+ // object types
+ void passObject(object arg);
+ void passVariadicObject(object... arg);
+ void passNullableObject(object? arg);
+ void passVariadicNullableObject(object... arg);
+ void passOptionalObject(optional object arg);
+ void passOptionalNullableObject(optional object? arg);
+ void passOptionalNullableObjectWithDefaultValue(optional object? arg = null);
+ void passSequenceOfObject(sequence<object> arg);
+ void passSequenceOfNullableObject(sequence<object?> arg);
+ void passNullableSequenceOfObject(sequence<object>? arg);
+ void passOptionalNullableSequenceOfNullableSequenceOfObject(optional sequence<sequence<object>?>? arg);
+ void passOptionalNullableSequenceOfNullableSequenceOfNullableObject(optional sequence<sequence<object?>?>? arg);
+ void passMozMapOfObject(MozMap<object> arg);
+ object receiveObject();
+ object? receiveNullableObject();
+
+ // Union types
+ void passUnion((object or long) arg);
+ // Some union tests are debug-only to avoid creating all those
+ // unused union types in opt builds.
+#ifdef DEBUG
+ void passUnion2((long or boolean) arg);
+ void passUnion3((object or long or boolean) arg);
+ void passUnion4((Node or long or boolean) arg);
+ void passUnion5((object or boolean) arg);
+ void passUnion6((object or DOMString) arg);
+ void passUnion7((object or DOMString or long) arg);
+ void passUnion8((object or DOMString or boolean) arg);
+ void passUnion9((object or DOMString or long or boolean) arg);
+ void passUnion10(optional (EventInit or long) arg);
+ void passUnion11(optional (CustomEventInit or long) arg);
+ void passUnion12(optional (EventInit or long) arg = 5);
+ void passUnion13(optional (object or long?) arg = null);
+ void passUnion14(optional (object or long?) arg = 5);
+ void passUnion15((sequence<long> or long) arg);
+ void passUnion16(optional (sequence<long> or long) arg);
+ void passUnion17(optional (sequence<long>? or long) arg = 5);
+ void passUnion18((sequence<object> or long) arg);
+ void passUnion19(optional (sequence<object> or long) arg);
+ void passUnion20(optional (sequence<object> or long) arg = []);
+ void passUnion21((MozMap<long> or long) arg);
+ void passUnion22((MozMap<object> or long) arg);
+ void passUnion23((sequence<ImageData> or long) arg);
+ void passUnion24((sequence<ImageData?> or long) arg);
+ void passUnion25((sequence<sequence<ImageData>> or long) arg);
+ void passUnion26((sequence<sequence<ImageData?>> or long) arg);
+ void passUnion27(optional (sequence<DOMString> or EventInit) arg);
+ void passUnion28(optional (EventInit or sequence<DOMString>) arg);
+ void passUnionWithCallback((EventHandler or long) arg);
+ void passUnionWithByteString((ByteString or long) arg);
+ void passUnionWithMozMap((MozMap<DOMString> or DOMString) arg);
+ void passUnionWithMozMapAndSequence((MozMap<DOMString> or sequence<DOMString>) arg);
+ void passUnionWithSequenceAndMozMap((sequence<DOMString> or MozMap<DOMString>) arg);
+ void passUnionWithSVS((USVString or long) arg);
+#endif
+ 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);
+ // Using an enum in a union. Note that we use some enum not declared in our
+ // binding file, because UnionTypes.h will need to include the binding header
+ // for this enum. Pick an enum from an interface that won't drag in too much
+ // stuff.
+ void passUnionWithEnum((SupportedType or object) arg);
+
+ // Trying to use a callback in a union won't include the test
+ // headers, unfortunately, so won't compile.
+ // void passUnionWithCallback((TestCallback or long) arg);
+ void passUnionWithObject((object or long) arg);
+ //void passUnionWithDict((Dict or long) arg);
+
+ void passUnionWithDefaultValue1(optional (double or DOMString) arg = "");
+ void passUnionWithDefaultValue2(optional (double or DOMString) arg = 1);
+ void passUnionWithDefaultValue3(optional (double or DOMString) arg = 1.5);
+ void passUnionWithDefaultValue4(optional (float or DOMString) arg = "");
+ void passUnionWithDefaultValue5(optional (float or DOMString) arg = 1);
+ void passUnionWithDefaultValue6(optional (float or DOMString) arg = 1.5);
+ void passUnionWithDefaultValue7(optional (unrestricted double or DOMString) arg = "");
+ void passUnionWithDefaultValue8(optional (unrestricted double or DOMString) arg = 1);
+ void passUnionWithDefaultValue9(optional (unrestricted double or DOMString) arg = 1.5);
+ void passUnionWithDefaultValue10(optional (unrestricted double or DOMString) arg = Infinity);
+ void passUnionWithDefaultValue11(optional (unrestricted float or DOMString) arg = "");
+ void passUnionWithDefaultValue12(optional (unrestricted float or DOMString) arg = 1);
+ void passUnionWithDefaultValue13(optional (unrestricted float or DOMString) arg = Infinity);
+ void passUnionWithDefaultValue14(optional (double or ByteString) arg = "");
+ void passUnionWithDefaultValue15(optional (double or ByteString) arg = 1);
+ void passUnionWithDefaultValue16(optional (double or ByteString) arg = 1.5);
+ void passUnionWithDefaultValue17(optional (double or SupportedType) arg = "text/html");
+ void passUnionWithDefaultValue18(optional (double or SupportedType) arg = 1);
+ void passUnionWithDefaultValue19(optional (double or SupportedType) arg = 1.5);
+
+ void passNullableUnionWithDefaultValue1(optional (double or DOMString)? arg = "");
+ void passNullableUnionWithDefaultValue2(optional (double or DOMString)? arg = 1);
+ void passNullableUnionWithDefaultValue3(optional (double or DOMString)? arg = null);
+ void passNullableUnionWithDefaultValue4(optional (float or DOMString)? arg = "");
+ void passNullableUnionWithDefaultValue5(optional (float or DOMString)? arg = 1);
+ void passNullableUnionWithDefaultValue6(optional (float or DOMString)? arg = null);
+ void passNullableUnionWithDefaultValue7(optional (unrestricted double or DOMString)? arg = "");
+ void passNullableUnionWithDefaultValue8(optional (unrestricted double or DOMString)? arg = 1);
+ void passNullableUnionWithDefaultValue9(optional (unrestricted double or DOMString)? arg = null);
+ void passNullableUnionWithDefaultValue10(optional (unrestricted float or DOMString)? arg = "");
+ void passNullableUnionWithDefaultValue11(optional (unrestricted float or DOMString)? arg = 1);
+ void passNullableUnionWithDefaultValue12(optional (unrestricted float or DOMString)? arg = null);
+ void passNullableUnionWithDefaultValue13(optional (double or ByteString)? arg = "");
+ void passNullableUnionWithDefaultValue14(optional (double or ByteString)? arg = 1);
+ void passNullableUnionWithDefaultValue15(optional (double or ByteString)? arg = 1.5);
+ void passNullableUnionWithDefaultValue16(optional (double or ByteString)? arg = null);
+ void passNullableUnionWithDefaultValue17(optional (double or SupportedType)? arg = "text/html");
+ void passNullableUnionWithDefaultValue18(optional (double or SupportedType)? arg = 1);
+ void passNullableUnionWithDefaultValue19(optional (double or SupportedType)? arg = 1.5);
+ void passNullableUnionWithDefaultValue20(optional (double or SupportedType)? arg = null);
+
+ void passSequenceOfUnions(sequence<(CanvasPattern or CanvasGradient)> arg);
+ void passSequenceOfUnions2(sequence<(object or long)> arg);
+ void passVariadicUnion((CanvasPattern or CanvasGradient)... arg);
+
+ void passSequenceOfNullableUnions(sequence<(CanvasPattern or CanvasGradient)?> arg);
+ void passVariadicNullableUnion((CanvasPattern or CanvasGradient)?... arg);
+ void passMozMapOfUnions(MozMap<(CanvasPattern or CanvasGradient)> arg);
+ // XXXbz no move constructor on some unions
+ // void passMozMapOfUnions2(MozMap<(object or long)> arg);
+
+ (CanvasPattern or CanvasGradient) receiveUnion();
+ (object or long) receiveUnion2();
+ (CanvasPattern? or CanvasGradient) receiveUnionContainingNull();
+ (CanvasPattern or CanvasGradient)? receiveNullableUnion();
+ (object or long)? receiveNullableUnion2();
+
+ attribute (CanvasPattern or CanvasGradient) writableUnion;
+ attribute (CanvasPattern? or CanvasGradient) writableUnionContainingNull;
+ attribute (CanvasPattern or CanvasGradient)? writableNullableUnion;
+
+ // Date types
+ void passDate(Date arg);
+ void passNullableDate(Date? arg);
+ void passOptionalDate(optional Date arg);
+ void passOptionalNullableDate(optional Date? arg);
+ void passOptionalNullableDateWithDefaultValue(optional Date? arg = null);
+ void passDateSequence(sequence<Date> arg);
+ void passNullableDateSequence(sequence<Date?> arg);
+ void passDateMozMap(MozMap<Date> arg);
+ Date receiveDate();
+ Date? receiveNullableDate();
+
+ // Promise types
+ void passPromise(Promise<any> arg);
+ void passNullablePromise(Promise<any>? arg);
+ void passOptionalPromise(optional Promise<any> arg);
+ void passOptionalNullablePromise(optional Promise<any>? arg);
+ void passOptionalNullablePromiseWithDefaultValue(optional Promise<any>? arg = null);
+ void passPromiseSequence(sequence<Promise<any>> arg);
+ void passNullablePromiseSequence(sequence<Promise<any>?> arg);
+ Promise<any> receivePromise();
+ Promise<any> receiveAddrefedPromise();
+
+ // binaryNames tests
+ void methodRenamedFrom();
+ [BinaryName="otherMethodRenamedTo"]
+ void otherMethodRenamedFrom();
+ void methodRenamedFrom(byte argument);
+ readonly attribute byte attributeGetterRenamedFrom;
+ attribute byte attributeRenamedFrom;
+ [BinaryName="otherAttributeRenamedTo"]
+ attribute byte otherAttributeRenamedFrom;
+
+ void passDictionary(optional Dict x);
+ void passDictionary2(Dict x);
+ [Cached, Pure]
+ readonly attribute Dict readonlyDictionary;
+ [Cached, Pure]
+ readonly attribute Dict? readonlyNullableDictionary;
+ [Cached, Pure]
+ attribute Dict writableDictionary;
+ [Cached, Pure, Frozen]
+ readonly attribute Dict readonlyFrozenDictionary;
+ [Cached, Pure, Frozen]
+ readonly attribute Dict? readonlyFrozenNullableDictionary;
+ [Cached, Pure, Frozen]
+ attribute Dict writableFrozenDictionary;
+ Dict receiveDictionary();
+ Dict? receiveNullableDictionary();
+ void passOtherDictionary(optional GrandparentDict x);
+ void passSequenceOfDictionaries(sequence<Dict> x);
+ void passMozMapOfDictionaries(MozMap<GrandparentDict> x);
+ // No support for nullable dictionaries inside a sequence (nor should there be)
+ // void passSequenceOfNullableDictionaries(sequence<Dict?> x);
+ void passDictionaryOrLong(optional Dict x);
+ void passDictionaryOrLong(long x);
+
+ void passDictContainingDict(optional DictContainingDict arg);
+ void passDictContainingSequence(optional DictContainingSequence arg);
+ DictContainingSequence receiveDictContainingSequence();
+ void passVariadicDictionary(Dict... arg);
+
+ // EnforceRange/Clamp tests
+ void dontEnforceRangeOrClamp(byte arg);
+ void doEnforceRange([EnforceRange] byte arg);
+ void doClamp([Clamp] byte arg);
+ [EnforceRange] attribute byte enforcedByte;
+ [Clamp] attribute byte clampedByte;
+
+ // Typedefs
+ const myLong myLongConstant = 5;
+ void exerciseTypedefInterfaces1(AnotherNameForTestInterface arg);
+ AnotherNameForTestInterface exerciseTypedefInterfaces2(NullableTestInterface arg);
+ void exerciseTypedefInterfaces3(YetAnotherNameForTestInterface arg);
+
+ // Deprecated methods and attributes
+ [Deprecated="GetAttributeNode"]
+ attribute boolean deprecatedAttribute;
+ [Deprecated="GetAttributeNode"]
+ void deprecatedMethod(boolean arg);
+ [Deprecated="GetAttributeNode"]
+ void deprecatedMethodWithContext(any arg);
+
+ // Static methods and attributes
+ static attribute boolean staticAttribute;
+ static void staticMethod(boolean arg);
+ static void staticMethodWithContext(any arg);
+
+ // Deprecated methods and attributes;
+ [Deprecated="GetAttributeNode"]
+ static attribute boolean staticDeprecatedAttribute;
+ [Deprecated="GetAttributeNode"]
+ static void staticDeprecatedMethod(boolean arg);
+ [Deprecated="GetAttributeNode"]
+ static void staticDeprecatedMethodWithContext(any arg);
+
+ // Overload resolution tests
+ //void overload1(DOMString... strs);
+ boolean overload1(TestInterface arg);
+ TestInterface overload1(DOMString strs, TestInterface arg);
+ void overload2(TestInterface arg);
+ void overload2(optional Dict arg);
+ void overload2(boolean arg);
+ void overload2(DOMString arg);
+ void overload2(Date arg);
+ void overload3(TestInterface arg);
+ void overload3(TestCallback arg);
+ void overload3(boolean arg);
+ void overload4(TestInterface arg);
+ void overload4(TestCallbackInterface arg);
+ void overload4(DOMString arg);
+ void overload5(long arg);
+ void overload5(TestEnum arg);
+ void overload6(long arg);
+ void overload6(boolean arg);
+ void overload7(long arg);
+ void overload7(boolean arg);
+ void overload7(ByteString arg);
+ void overload8(long arg);
+ void overload8(TestInterface arg);
+ void overload9(long? arg);
+ void overload9(DOMString arg);
+ void overload10(long? arg);
+ void overload10(object arg);
+ void overload11(long arg);
+ void overload11(DOMString? arg);
+ void overload12(long arg);
+ void overload12(boolean? arg);
+ void overload13(long? arg);
+ void overload13(boolean arg);
+ void overload14(optional long arg);
+ void overload14(TestInterface arg);
+ void overload15(long arg);
+ void overload15(optional TestInterface arg);
+ void overload16(long arg);
+ void overload16(optional TestInterface? arg);
+ void overload17(sequence<long> arg);
+ void overload17(MozMap<long> arg);
+ void overload18(MozMap<DOMString> arg);
+ void overload18(sequence<DOMString> arg);
+ void overload19(sequence<long> arg);
+ void overload19(optional Dict arg);
+ void overload20(optional Dict arg);
+ void overload20(sequence<long> arg);
+
+ // Variadic handling
+ void passVariadicThirdArg(DOMString arg1, long arg2, TestInterface... arg3);
+
+ // Conditionally exposed methods/attributes
+ [Pref="abc.def"]
+ readonly attribute boolean prefable1;
+ [Pref="abc.def"]
+ readonly attribute boolean prefable2;
+ [Pref="ghi.jkl"]
+ readonly attribute boolean prefable3;
+ [Pref="ghi.jkl"]
+ readonly attribute boolean prefable4;
+ [Pref="abc.def"]
+ readonly attribute boolean prefable5;
+ [Pref="abc.def", Func="nsGenericHTMLElement::TouchEventsEnabled"]
+ readonly attribute boolean prefable6;
+ [Pref="abc.def", Func="nsGenericHTMLElement::TouchEventsEnabled"]
+ readonly attribute boolean prefable7;
+ [Pref="ghi.jkl", Func="nsGenericHTMLElement::TouchEventsEnabled"]
+ readonly attribute boolean prefable8;
+ [Pref="abc.def", Func="nsGenericHTMLElement::TouchEventsEnabled"]
+ readonly attribute boolean prefable9;
+ [Pref="abc.def"]
+ void prefable10();
+ [Pref="abc.def", Func="nsGenericHTMLElement::TouchEventsEnabled"]
+ void prefable11();
+ [Pref="abc.def", Func="TestFuncControlledMember"]
+ readonly attribute boolean prefable12;
+ [Pref="abc.def", Func="nsGenericHTMLElement::TouchEventsEnabled"]
+ void prefable13();
+ [Pref="abc.def", Func="TestFuncControlledMember"]
+ readonly attribute boolean prefable14;
+ [Func="TestFuncControlledMember"]
+ readonly attribute boolean prefable15;
+ [Func="TestFuncControlledMember"]
+ readonly attribute boolean prefable16;
+ [Pref="abc.def", Func="TestFuncControlledMember"]
+ void prefable17();
+ [Func="TestFuncControlledMember"]
+ void prefable18();
+ [Func="TestFuncControlledMember"]
+ void prefable19();
+
+ // Conditionally exposed methods/attributes involving [SecureContext]
+ [SecureContext]
+ readonly attribute boolean conditionalOnSecureContext1;
+ [SecureContext, Pref="abc.def"]
+ readonly attribute boolean conditionalOnSecureContext2;
+ [SecureContext, Pref="abc.def", Func="nsGenericHTMLElement::TouchEventsEnabled"]
+ readonly attribute boolean conditionalOnSecureContext3;
+ [SecureContext, Pref="abc.def", Func="TestFuncControlledMember"]
+ readonly attribute boolean conditionalOnSecureContext4;
+ [SecureContext]
+ void conditionalOnSecureContext5();
+ [SecureContext, Pref="abc.def"]
+ void conditionalOnSecureContext6();
+ [SecureContext, Pref="abc.def", Func="nsGenericHTMLElement::TouchEventsEnabled"]
+ void conditionalOnSecureContext7();
+ [SecureContext, Pref="abc.def", Func="TestFuncControlledMember"]
+ void conditionalOnSecureContext8();
+
+ // Miscellania
+ [LenientThis] attribute long attrWithLenientThis;
+ [Unforgeable] readonly attribute long unforgeableAttr;
+ [Unforgeable, ChromeOnly] readonly attribute long unforgeableAttr2;
+ [Unforgeable] long unforgeableMethod();
+ [Unforgeable, ChromeOnly] long unforgeableMethod2();
+ stringifier;
+ void passRenamedInterface(TestRenamedInterface arg);
+ [PutForwards=writableByte] readonly attribute TestExampleInterface putForwardsAttr;
+ [PutForwards=writableByte, LenientThis] readonly attribute TestExampleInterface putForwardsAttr2;
+ [PutForwards=writableByte, ChromeOnly] readonly attribute TestExampleInterface putForwardsAttr3;
+ [Throws] void throwingMethod();
+ [Throws] attribute boolean throwingAttr;
+ [GetterThrows] attribute boolean throwingGetterAttr;
+ [SetterThrows] attribute boolean throwingSetterAttr;
+ [NeedsSubjectPrincipal] void needsSubjectPrincipalMethod();
+ [NeedsSubjectPrincipal] attribute boolean needsSubjectPrincipalAttr;
+ [NeedsCallerType] void needsCallerTypeMethod();
+ [NeedsCallerType] attribute boolean needsCallerTypeAttr;
+ legacycaller short(unsigned long arg1, TestInterface arg2);
+ void passArgsWithDefaults(optional long arg1,
+ optional TestInterface? arg2 = null,
+ optional Dict arg3, optional double arg4 = 5.0,
+ optional float arg5);
+ attribute any jsonifierShouldSkipThis;
+ attribute TestParentInterface jsonifierShouldSkipThis2;
+ attribute TestCallbackInterface jsonifierShouldSkipThis3;
+ jsonifier;
+
+ attribute byte dashed-attribute;
+ void dashed-method();
+
+ // If you add things here, add them to TestCodeGen and TestJSImplGen as well
+};
+
+interface TestExampleProxyInterface {
+ getter long longIndexedGetter(unsigned long ix);
+ setter creator void longIndexedSetter(unsigned long y, long z);
+ stringifier DOMString myStringifier();
+ getter short shortNameGetter(DOMString nom);
+ deleter void (DOMString nomnom);
+ setter creator void shortNamedSetter(DOMString me, short value);
+};
+
+[Exposed=(Window,Worker)]
+interface TestExampleWorkerInterface {
+ [NeedsSubjectPrincipal] void needsSubjectPrincipalMethod();
+ [NeedsSubjectPrincipal] attribute boolean needsSubjectPrincipalAttr;
+ [NeedsCallerType] void needsCallerTypeMethod();
+ [NeedsCallerType] attribute boolean needsCallerTypeAttr;
+};
diff --git a/dom/bindings/test/TestFunctions.cpp b/dom/bindings/test/TestFunctions.cpp
new file mode 100644
index 000000000..f05c92b48
--- /dev/null
+++ b/dom/bindings/test/TestFunctions.cpp
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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/dom/TestFunctions.h"
+#include "mozilla/dom/TestFunctionsBinding.h"
+#include "nsStringBuffer.h"
+
+namespace mozilla {
+namespace dom {
+
+/* static */ TestFunctions*
+TestFunctions::Constructor(GlobalObject& aGlobal, ErrorResult& aRv)
+{
+ return new TestFunctions;
+}
+
+/* static */ void
+TestFunctions::ThrowUncatchableException(GlobalObject& aGlobal,
+ ErrorResult& aRv)
+{
+ aRv.ThrowUncatchableException();
+}
+
+/* static */ Promise*
+TestFunctions::PassThroughPromise(GlobalObject& aGlobal, Promise& aPromise)
+{
+ return &aPromise;
+}
+
+/* static */ already_AddRefed<Promise>
+TestFunctions::PassThroughCallbackPromise(GlobalObject& aGlobal,
+ PromiseReturner& aCallback,
+ ErrorResult& aRv)
+{
+ return aCallback.Call(aRv);
+}
+
+void
+TestFunctions::SetStringData(const nsAString& aString)
+{
+ mStringData = aString;
+}
+
+void
+TestFunctions::GetStringDataAsAString(nsAString& aString)
+{
+ aString = mStringData;
+}
+
+void
+TestFunctions::GetStringDataAsAString(uint32_t aLength, nsAString& aString)
+{
+ MOZ_RELEASE_ASSERT(aLength <= mStringData.Length(),
+ "Bogus test passing in a too-big length");
+ aString.Assign(mStringData.BeginReading(), aLength);
+}
+
+void
+TestFunctions::GetStringDataAsDOMString(const Optional<uint32_t>& aLength,
+ DOMString& aString)
+{
+ uint32_t length;
+ if (aLength.WasPassed()) {
+ length = aLength.Value();
+ MOZ_RELEASE_ASSERT(length <= mStringData.Length(),
+ "Bogus test passing in a too-big length");
+ } else {
+ length = mStringData.Length();
+ }
+
+ nsStringBuffer* buf = nsStringBuffer::FromString(mStringData);
+ if (buf) {
+ aString.SetStringBuffer(buf, length);
+ return;
+ }
+
+ // We better have an empty mStringData; otherwise why did we not have a string
+ // buffer?
+ MOZ_RELEASE_ASSERT(length == 0, "Why no stringbuffer?");
+ // No need to do anything here; aString is already empty.
+}
+
+bool
+TestFunctions::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto,
+ JS::MutableHandle<JSObject*> aWrapper)
+{
+ return TestFunctionsBinding::Wrap(aCx, this, aGivenProto, aWrapper);
+}
+
+}
+}
diff --git a/dom/bindings/test/TestFunctions.h b/dom/bindings/test/TestFunctions.h
new file mode 100644
index 000000000..b35464824
--- /dev/null
+++ b/dom/bindings/test/TestFunctions.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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_TestFunctions_h
+#define mozilla_dom_TestFunctions_h
+
+#include "mozilla/ErrorResult.h"
+#include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/dom/NonRefcountedDOMObject.h"
+#include "nsString.h"
+
+namespace mozilla {
+namespace dom {
+
+class Promise;
+class PromiseReturner;
+
+class TestFunctions : public NonRefcountedDOMObject {
+public:
+ static TestFunctions* Constructor(GlobalObject& aGlobal, ErrorResult& aRv);
+
+ static void
+ ThrowUncatchableException(GlobalObject& aGlobal, ErrorResult& aRv);
+
+ static Promise*
+ PassThroughPromise(GlobalObject& aGlobal, Promise& aPromise);
+
+ static already_AddRefed<Promise>
+ PassThroughCallbackPromise(GlobalObject& aGlobal,
+ PromiseReturner& aCallback,
+ ErrorResult& aRv);
+
+ void SetStringData(const nsAString& aString);
+
+ void GetStringDataAsAString(nsAString& aString);
+ void GetStringDataAsAString(uint32_t aLength, nsAString& aString);
+ void GetStringDataAsDOMString(const Optional<uint32_t>& aLength,
+ DOMString& aString);
+
+ bool WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto,
+ JS::MutableHandle<JSObject*> aWrapper);
+private:
+ nsString mStringData;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_TestFunctions_h
diff --git a/dom/bindings/test/TestInterfaceIterableDouble.cpp b/dom/bindings/test/TestInterfaceIterableDouble.cpp
new file mode 100644
index 000000000..33a4c97d1
--- /dev/null
+++ b/dom/bindings/test/TestInterfaceIterableDouble.cpp
@@ -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/. */
+
+#include "mozilla/dom/TestInterfaceIterableDouble.h"
+#include "mozilla/dom/TestInterfaceJSMaplikeSetlikeIterableBinding.h"
+#include "nsPIDOMWindow.h"
+#include "mozilla/dom/BindingUtils.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(TestInterfaceIterableDouble, mParent)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(TestInterfaceIterableDouble)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(TestInterfaceIterableDouble)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TestInterfaceIterableDouble)
+NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+TestInterfaceIterableDouble::TestInterfaceIterableDouble(nsPIDOMWindowInner* aParent)
+ : mParent(aParent)
+{
+ mValues.AppendElement(std::pair<nsString, nsString>(NS_LITERAL_STRING("a"),
+ NS_LITERAL_STRING("b")));
+ mValues.AppendElement(std::pair<nsString, nsString>(NS_LITERAL_STRING("c"),
+ NS_LITERAL_STRING("d")));
+ mValues.AppendElement(std::pair<nsString, nsString>(NS_LITERAL_STRING("e"),
+ NS_LITERAL_STRING("f")));
+}
+
+//static
+already_AddRefed<TestInterfaceIterableDouble>
+TestInterfaceIterableDouble::Constructor(const GlobalObject& aGlobal,
+ ErrorResult& aRv)
+{
+ nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal.GetAsSupports());
+ if (!window) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return nullptr;
+ }
+
+ RefPtr<TestInterfaceIterableDouble> r = new TestInterfaceIterableDouble(window);
+ return r.forget();
+}
+
+JSObject*
+TestInterfaceIterableDouble::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+ return TestInterfaceIterableDoubleBinding::Wrap(aCx, this, aGivenProto);
+}
+
+nsPIDOMWindowInner*
+TestInterfaceIterableDouble::GetParentObject() const
+{
+ return mParent;
+}
+
+size_t
+TestInterfaceIterableDouble::GetIterableLength()
+{
+ return mValues.Length();
+}
+
+nsAString&
+TestInterfaceIterableDouble::GetKeyAtIndex(uint32_t aIndex)
+{
+ MOZ_ASSERT(aIndex < mValues.Length());
+ return mValues.ElementAt(aIndex).first;
+}
+
+nsAString&
+TestInterfaceIterableDouble::GetValueAtIndex(uint32_t aIndex)
+{
+ MOZ_ASSERT(aIndex < mValues.Length());
+ return mValues.ElementAt(aIndex).second;
+}
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/bindings/test/TestInterfaceIterableDouble.h b/dom/bindings/test/TestInterfaceIterableDouble.h
new file mode 100644
index 000000000..1e9ff7acd
--- /dev/null
+++ b/dom/bindings/test/TestInterfaceIterableDouble.h
@@ -0,0 +1,51 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_TestInterfaceIterableDouble_h
+#define mozilla_dom_TestInterfaceIterableDouble_h
+
+#include "nsWrapperCache.h"
+#include "nsCOMPtr.h"
+
+class nsPIDOMWindowInner;
+
+namespace mozilla {
+
+class ErrorResult;
+
+namespace dom {
+
+class GlobalObject;
+
+// Implementation of test binding for webidl iterable interfaces, using
+// primitives for value type
+class TestInterfaceIterableDouble final : public nsISupports,
+ public nsWrapperCache
+{
+public:
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(TestInterfaceIterableDouble)
+
+ explicit TestInterfaceIterableDouble(nsPIDOMWindowInner* aParent);
+ nsPIDOMWindowInner* GetParentObject() const;
+ virtual JSObject* WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) override;
+ static already_AddRefed<TestInterfaceIterableDouble>
+ Constructor(const GlobalObject& aGlobal, ErrorResult& rv);
+
+ size_t GetIterableLength();
+ nsAString& GetKeyAtIndex(uint32_t aIndex);
+ nsAString& GetValueAtIndex(uint32_t aIndex);
+private:
+ virtual ~TestInterfaceIterableDouble() {}
+ nsCOMPtr<nsPIDOMWindowInner> mParent;
+ nsTArray<std::pair<nsString, nsString>> mValues;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_TestInterfaceIterableDouble_h
diff --git a/dom/bindings/test/TestInterfaceIterableDoubleUnion.cpp b/dom/bindings/test/TestInterfaceIterableDoubleUnion.cpp
new file mode 100644
index 000000000..29151a4c5
--- /dev/null
+++ b/dom/bindings/test/TestInterfaceIterableDoubleUnion.cpp
@@ -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/. */
+
+#include "mozilla/dom/TestInterfaceIterableDoubleUnion.h"
+#include "mozilla/dom/TestInterfaceJSMaplikeSetlikeIterableBinding.h"
+#include "nsPIDOMWindow.h"
+#include "mozilla/dom/BindingUtils.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(TestInterfaceIterableDoubleUnion, mParent)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(TestInterfaceIterableDoubleUnion)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(TestInterfaceIterableDoubleUnion)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TestInterfaceIterableDoubleUnion)
+NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+TestInterfaceIterableDoubleUnion::TestInterfaceIterableDoubleUnion(nsPIDOMWindowInner* aParent)
+ : mParent(aParent)
+{
+ OwningStringOrLong a;
+ a.SetAsLong() = 1;
+ mValues.AppendElement(std::pair<nsString, OwningStringOrLong>(NS_LITERAL_STRING("long"),
+ a));
+ a.SetAsString() = NS_LITERAL_STRING("a");
+ mValues.AppendElement(std::pair<nsString, OwningStringOrLong>(NS_LITERAL_STRING("string"),
+ a));
+}
+
+//static
+already_AddRefed<TestInterfaceIterableDoubleUnion>
+TestInterfaceIterableDoubleUnion::Constructor(const GlobalObject& aGlobal,
+ ErrorResult& aRv)
+{
+ nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal.GetAsSupports());
+ if (!window) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return nullptr;
+ }
+
+ RefPtr<TestInterfaceIterableDoubleUnion> r = new TestInterfaceIterableDoubleUnion(window);
+ return r.forget();
+}
+
+JSObject*
+TestInterfaceIterableDoubleUnion::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+ return TestInterfaceIterableDoubleUnionBinding::Wrap(aCx, this, aGivenProto);
+}
+
+nsPIDOMWindowInner*
+TestInterfaceIterableDoubleUnion::GetParentObject() const
+{
+ return mParent;
+}
+
+size_t
+TestInterfaceIterableDoubleUnion::GetIterableLength()
+{
+ return mValues.Length();
+}
+
+nsAString&
+TestInterfaceIterableDoubleUnion::GetKeyAtIndex(uint32_t aIndex)
+{
+ MOZ_ASSERT(aIndex < mValues.Length());
+ return mValues.ElementAt(aIndex).first;
+}
+
+OwningStringOrLong&
+TestInterfaceIterableDoubleUnion::GetValueAtIndex(uint32_t aIndex)
+{
+ MOZ_ASSERT(aIndex < mValues.Length());
+ return mValues.ElementAt(aIndex).second;
+}
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/bindings/test/TestInterfaceIterableDoubleUnion.h b/dom/bindings/test/TestInterfaceIterableDoubleUnion.h
new file mode 100644
index 000000000..ff6ea2175
--- /dev/null
+++ b/dom/bindings/test/TestInterfaceIterableDoubleUnion.h
@@ -0,0 +1,51 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_TestInterfaceIterableDoubleUnion_h
+#define mozilla_dom_TestInterfaceIterableDoubleUnion_h
+
+#include "nsWrapperCache.h"
+#include "nsCOMPtr.h"
+
+class nsPIDOMWindowInner;
+
+namespace mozilla {
+
+class ErrorResult;
+
+namespace dom {
+
+class GlobalObject;
+
+// Implementation of test binding for webidl iterable interfaces, using
+// primitives for value type
+class TestInterfaceIterableDoubleUnion final : public nsISupports,
+ public nsWrapperCache
+{
+public:
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(TestInterfaceIterableDoubleUnion)
+
+ explicit TestInterfaceIterableDoubleUnion(nsPIDOMWindowInner* aParent);
+ nsPIDOMWindowInner* GetParentObject() const;
+ virtual JSObject* WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) override;
+ static already_AddRefed<TestInterfaceIterableDoubleUnion>
+ Constructor(const GlobalObject& aGlobal, ErrorResult& rv);
+
+ size_t GetIterableLength();
+ nsAString& GetKeyAtIndex(uint32_t aIndex);
+ OwningStringOrLong& GetValueAtIndex(uint32_t aIndex);
+private:
+ virtual ~TestInterfaceIterableDoubleUnion() {}
+ nsCOMPtr<nsPIDOMWindowInner> mParent;
+ nsTArray<std::pair<nsString, OwningStringOrLong>> mValues;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_TestInterfaceIterableDoubleUnion_h
diff --git a/dom/bindings/test/TestInterfaceIterableSingle.cpp b/dom/bindings/test/TestInterfaceIterableSingle.cpp
new file mode 100644
index 000000000..5f8d6c640
--- /dev/null
+++ b/dom/bindings/test/TestInterfaceIterableSingle.cpp
@@ -0,0 +1,77 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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/dom/TestInterfaceIterableSingle.h"
+#include "mozilla/dom/TestInterfaceJSMaplikeSetlikeIterableBinding.h"
+#include "nsPIDOMWindow.h"
+#include "mozilla/dom/BindingUtils.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(TestInterfaceIterableSingle, mParent)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(TestInterfaceIterableSingle)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(TestInterfaceIterableSingle)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TestInterfaceIterableSingle)
+NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+TestInterfaceIterableSingle::TestInterfaceIterableSingle(nsPIDOMWindowInner* aParent)
+ : mParent(aParent)
+{
+ for (int i = 0; i < 3; ++i) {
+ mValues.AppendElement(i);
+ }
+}
+
+//static
+already_AddRefed<TestInterfaceIterableSingle>
+TestInterfaceIterableSingle::Constructor(const GlobalObject& aGlobal,
+ ErrorResult& aRv)
+{
+ nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal.GetAsSupports());
+ if (!window) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return nullptr;
+ }
+
+ RefPtr<TestInterfaceIterableSingle> r = new TestInterfaceIterableSingle(window);
+ return r.forget();
+}
+
+JSObject*
+TestInterfaceIterableSingle::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+ return TestInterfaceIterableSingleBinding::Wrap(aCx, this, aGivenProto);
+}
+
+nsPIDOMWindowInner*
+TestInterfaceIterableSingle::GetParentObject() const
+{
+ return mParent;
+}
+
+uint32_t
+TestInterfaceIterableSingle::Length() const
+{
+ return mValues.Length();
+}
+
+int32_t
+TestInterfaceIterableSingle::IndexedGetter(uint32_t aIndex, bool& aFound) const
+{
+ if (aIndex >= mValues.Length()) {
+ aFound = false;
+ return 0;
+ }
+
+ aFound = true;
+ return mValues[aIndex];
+}
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/bindings/test/TestInterfaceIterableSingle.h b/dom/bindings/test/TestInterfaceIterableSingle.h
new file mode 100644
index 000000000..a071ada8b
--- /dev/null
+++ b/dom/bindings/test/TestInterfaceIterableSingle.h
@@ -0,0 +1,51 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_TestInterfaceIterableSingle_h
+#define mozilla_dom_TestInterfaceIterableSingle_h
+
+#include "nsWrapperCache.h"
+#include "nsCOMPtr.h"
+
+class nsPIDOMWindowInner;
+
+namespace mozilla {
+
+class ErrorResult;
+
+namespace dom {
+
+class GlobalObject;
+
+// Implementation of test binding for webidl iterable interfaces, using
+// primitives for value type
+class TestInterfaceIterableSingle final : public nsISupports,
+ public nsWrapperCache
+{
+public:
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(TestInterfaceIterableSingle)
+
+ explicit TestInterfaceIterableSingle(nsPIDOMWindowInner* aParent);
+ nsPIDOMWindowInner* GetParentObject() const;
+ virtual JSObject* WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) override;
+ static already_AddRefed<TestInterfaceIterableSingle>
+ Constructor(const GlobalObject& aGlobal, ErrorResult& rv);
+
+ uint32_t Length() const;
+ int32_t IndexedGetter(uint32_t aIndex, bool& aFound) const;
+
+private:
+ virtual ~TestInterfaceIterableSingle() {}
+ nsCOMPtr<nsPIDOMWindowInner> mParent;
+ nsTArray<int32_t> mValues;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_TestInterfaceIterableSingle_h
diff --git a/dom/bindings/test/TestInterfaceJS.js b/dom/bindings/test/TestInterfaceJS.js
new file mode 100644
index 000000000..1a5bf8e61
--- /dev/null
+++ b/dom/bindings/test/TestInterfaceJS.js
@@ -0,0 +1,166 @@
+/* -*- Mode: JavaScript; 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/. */
+
+"use strict";
+const Cu = Components.utils;
+const Ci = Components.interfaces;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+function TestInterfaceJS(anyArg, objectArg) {}
+
+TestInterfaceJS.prototype = {
+ classID: Components.ID("{2ac4e026-cf25-47d5-b067-78d553c3cad8}"),
+ contractID: "@mozilla.org/dom/test-interface-js;1",
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
+ Ci.nsIDOMGlobalPropertyInitializer]),
+
+ init: function(win) { this._win = win; },
+
+ __init: function (anyArg, objectArg, dictionaryArg) {
+ this._anyAttr = undefined;
+ this._objectAttr = null;
+ this._anyArg = anyArg;
+ this._objectArg = objectArg;
+ this._dictionaryArg = dictionaryArg;
+ this._cachedAttr = 15;
+ },
+
+ get anyArg() { return this._anyArg; },
+ get objectArg() { return this._objectArg; },
+ get dictionaryArg() { return this._dictionaryArg; },
+ get anyAttr() { return this._anyAttr; },
+ set anyAttr(val) { this._anyAttr = val; },
+ get objectAttr() { return this._objectAttr; },
+ set objectAttr(val) { this._objectAttr = val; },
+ get dictionaryAttr() { return this._dictionaryAttr; },
+ set dictionaryAttr(val) { this._dictionaryAttr = val; },
+ pingPongAny: function(any) { return any; },
+ pingPongObject: function(obj) { return obj; },
+ pingPongObjectOrString: function(objectOrString) { return objectOrString; },
+ pingPongDictionary: function(dict) { return dict; },
+ pingPongDictionaryOrLong: function(dictOrLong) { return dictOrLong.anyMember || dictOrLong; },
+ pingPongMap: function(map) { return JSON.stringify(map); },
+ objectSequenceLength: function(seq) { return seq.length; },
+ anySequenceLength: function(seq) { return seq.length; },
+
+
+ getCallerPrincipal: function() { return Cu.getWebIDLCallerPrincipal().origin; },
+
+ convertSVS: function(svs) { return svs; },
+
+ pingPongUnion: function(x) { return x; },
+ pingPongUnionContainingNull: function(x) { return x; },
+ pingPongNullableUnion: function(x) { return x; },
+ returnBadUnion: function(x) { return 3; },
+
+ get cachedAttr() { return this._cachedAttr; },
+ setCachedAttr: function(n) { this._cachedAttr = n; },
+ clearCachedAttrCache: function () { this.__DOM_IMPL__._clearCachedCachedAttrValue(); },
+
+ testSequenceOverload: function(arg) {},
+ testSequenceUnion: function(arg) {},
+
+ testThrowError: function() {
+ throw new this._win.Error("We are an Error");
+ },
+
+ testThrowDOMException: function() {
+ throw new this._win.DOMException("We are a DOMException",
+ "NotSupportedError");
+ },
+
+ testThrowTypeError: function() {
+ throw new this._win.TypeError("We are a TypeError");
+ },
+
+ testThrowCallbackError: function(callback) {
+ callback();
+ },
+
+ testThrowXraySelfHosted: function() {
+ this._win.Array.indexOf();
+ },
+
+ testThrowSelfHosted: function() {
+ Array.indexOf();
+ },
+
+ testPromiseWithThrowingChromePromiseInit: function() {
+ return new this._win.Promise(function() {
+ noSuchMethodExistsYo1();
+ })
+ },
+
+ testPromiseWithThrowingContentPromiseInit: function(func) {
+ return new this._win.Promise(func);
+ },
+
+ testPromiseWithDOMExceptionThrowingPromiseInit: function() {
+ return new this._win.Promise(() => {
+ throw new this._win.DOMException("We are a second DOMException",
+ "NotFoundError");
+ })
+ },
+
+ testPromiseWithThrowingChromeThenFunction: function() {
+ return this._win.Promise.resolve(5).then(function() {
+ noSuchMethodExistsYo2();
+ });
+ },
+
+ testPromiseWithThrowingContentThenFunction: function(func) {
+ return this._win.Promise.resolve(10).then(func);
+ },
+
+ testPromiseWithDOMExceptionThrowingThenFunction: function() {
+ return this._win.Promise.resolve(5).then(() => {
+ throw new this._win.DOMException("We are a third DOMException",
+ "NetworkError");
+ });
+ },
+
+ testPromiseWithThrowingChromeThenable: function() {
+ var thenable = {
+ then: function() {
+ noSuchMethodExistsYo3()
+ }
+ };
+ return new this._win.Promise(function(resolve) {
+ resolve(thenable)
+ });
+ },
+
+ testPromiseWithThrowingContentThenable: function(thenable) {
+ // Waive Xrays on the thenable, because we're calling resolve() in the
+ // chrome compartment, so that's the compartment the "then" property get
+ // will happen in, and if we leave the Xray in place the function-valued
+ // property won't return the function.
+ return this._win.Promise.resolve(Cu.waiveXrays(thenable));
+ },
+
+ testPromiseWithDOMExceptionThrowingThenable: function() {
+ var thenable = {
+ then: () => {
+ throw new this._win.DOMException("We are a fourth DOMException",
+ "TypeMismatchError");
+ }
+ };
+ return new this._win.Promise(function(resolve) {
+ resolve(thenable)
+ });
+ },
+
+ get onsomething() {
+ return this.__DOM_IMPL__.getEventHandler("onsomething");
+ },
+
+ set onsomething(val) {
+ this.__DOM_IMPL__.setEventHandler("onsomething", val);
+ }
+};
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([TestInterfaceJS])
diff --git a/dom/bindings/test/TestInterfaceJS.manifest b/dom/bindings/test/TestInterfaceJS.manifest
new file mode 100644
index 000000000..161a42156
--- /dev/null
+++ b/dom/bindings/test/TestInterfaceJS.manifest
@@ -0,0 +1,4 @@
+component {2ac4e026-cf25-47d5-b067-78d553c3cad8} TestInterfaceJS.js
+contract @mozilla.org/dom/test-interface-js;1 {2ac4e026-cf25-47d5-b067-78d553c3cad8}
+component {4bc6f6f3-e005-4f0a-b42d-4d1663a9013a} TestInterfaceJSMaplike.js
+contract @mozilla.org/dom/test-interface-js-maplike;1 {4bc6f6f3-e005-4f0a-b42d-4d1663a9013a}
diff --git a/dom/bindings/test/TestInterfaceJSMaplike.js b/dom/bindings/test/TestInterfaceJSMaplike.js
new file mode 100644
index 000000000..b108ef5b6
--- /dev/null
+++ b/dom/bindings/test/TestInterfaceJSMaplike.js
@@ -0,0 +1,38 @@
+/* -*- Mode: JavaScript; 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/. */
+
+"use strict";
+const Cu = Components.utils;
+const Ci = Components.interfaces;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+function TestInterfaceJSMaplike() {}
+
+TestInterfaceJSMaplike.prototype = {
+ classID: Components.ID("{4bc6f6f3-e005-4f0a-b42d-4d1663a9013a}"),
+ contractID: "@mozilla.org/dom/test-interface-js-maplike;1",
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
+ Ci.nsIDOMGlobalPropertyInitializer]),
+
+ init: function(win) { this._win = win; },
+
+ __init: function () {},
+
+ setInternal: function(aKey, aValue) {
+ return this.__DOM_IMPL__.__set(aKey, aValue);
+ },
+
+ deleteInternal: function(aKey) {
+ return this.__DOM_IMPL__.__delete(aKey);
+ },
+
+ clearInternal: function() {
+ return this.__DOM_IMPL__.__clear();
+ }
+};
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([TestInterfaceJSMaplike])
diff --git a/dom/bindings/test/TestInterfaceMaplike.cpp b/dom/bindings/test/TestInterfaceMaplike.cpp
new file mode 100644
index 000000000..4abace83c
--- /dev/null
+++ b/dom/bindings/test/TestInterfaceMaplike.cpp
@@ -0,0 +1,84 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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/dom/TestInterfaceMaplike.h"
+#include "mozilla/dom/TestInterfaceJSMaplikeSetlikeIterableBinding.h"
+#include "nsPIDOMWindow.h"
+#include "mozilla/dom/BindingUtils.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(TestInterfaceMaplike, mParent)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(TestInterfaceMaplike)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(TestInterfaceMaplike)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TestInterfaceMaplike)
+NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+TestInterfaceMaplike::TestInterfaceMaplike(nsPIDOMWindowInner* aParent)
+: mParent(aParent)
+{
+}
+
+//static
+already_AddRefed<TestInterfaceMaplike>
+TestInterfaceMaplike::Constructor(const GlobalObject& aGlobal,
+ ErrorResult& aRv)
+{
+ nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal.GetAsSupports());
+ if (!window) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return nullptr;
+ }
+
+ RefPtr<TestInterfaceMaplike> r = new TestInterfaceMaplike(window);
+ return r.forget();
+}
+
+JSObject*
+TestInterfaceMaplike::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+ return TestInterfaceMaplikeBinding::Wrap(aCx, this, aGivenProto);
+}
+
+nsPIDOMWindowInner*
+TestInterfaceMaplike::GetParentObject() const
+{
+ return mParent;
+}
+
+void
+TestInterfaceMaplike::SetInternal(const nsAString& aKey, int32_t aValue)
+{
+ ErrorResult rv;
+ TestInterfaceMaplikeBinding::MaplikeHelpers::Set(this, aKey, aValue, rv);
+}
+
+void
+TestInterfaceMaplike::ClearInternal()
+{
+ ErrorResult rv;
+ TestInterfaceMaplikeBinding::MaplikeHelpers::Clear(this, rv);
+}
+
+bool
+TestInterfaceMaplike::DeleteInternal(const nsAString& aKey)
+{
+ ErrorResult rv;
+ return TestInterfaceMaplikeBinding::MaplikeHelpers::Delete(this, aKey, rv);
+}
+
+bool
+TestInterfaceMaplike::HasInternal(const nsAString& aKey)
+{
+ ErrorResult rv;
+ return TestInterfaceMaplikeBinding::MaplikeHelpers::Has(this, aKey, rv);
+}
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/bindings/test/TestInterfaceMaplike.h b/dom/bindings/test/TestInterfaceMaplike.h
new file mode 100644
index 000000000..c012a7a21
--- /dev/null
+++ b/dom/bindings/test/TestInterfaceMaplike.h
@@ -0,0 +1,52 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_TestInterfaceMaplike_h
+#define mozilla_dom_TestInterfaceMaplike_h
+
+#include "nsWrapperCache.h"
+#include "nsCOMPtr.h"
+
+class nsPIDOMWindowInner;
+
+namespace mozilla {
+
+class ErrorResult;
+
+namespace dom {
+
+class GlobalObject;
+
+// Implementation of test binding for webidl maplike interfaces, using
+// primitives for key and value types.
+class TestInterfaceMaplike final : public nsISupports,
+ public nsWrapperCache
+{
+public:
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(TestInterfaceMaplike)
+
+ explicit TestInterfaceMaplike(nsPIDOMWindowInner* aParent);
+ nsPIDOMWindowInner* GetParentObject() const;
+ virtual JSObject* WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) override;
+ static already_AddRefed<TestInterfaceMaplike>
+ Constructor(const GlobalObject& aGlobal, ErrorResult& rv);
+
+ // External access for testing internal convenience functions.
+ void SetInternal(const nsAString& aKey, int32_t aValue);
+ void ClearInternal();
+ bool DeleteInternal(const nsAString& aKey);
+ bool HasInternal(const nsAString& aKey);
+private:
+ virtual ~TestInterfaceMaplike() {}
+ nsCOMPtr<nsPIDOMWindowInner> mParent;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_TestInterfaceMaplike_h
diff --git a/dom/bindings/test/TestInterfaceMaplikeObject.cpp b/dom/bindings/test/TestInterfaceMaplikeObject.cpp
new file mode 100644
index 000000000..3dc1ffdc4
--- /dev/null
+++ b/dom/bindings/test/TestInterfaceMaplikeObject.cpp
@@ -0,0 +1,88 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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/dom/TestInterfaceMaplikeObject.h"
+#include "mozilla/dom/TestInterfaceMaplike.h"
+#include "mozilla/dom/TestInterfaceJSMaplikeSetlikeIterableBinding.h"
+#include "nsPIDOMWindow.h"
+#include "mozilla/dom/BindingUtils.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(TestInterfaceMaplikeObject, mParent)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(TestInterfaceMaplikeObject)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(TestInterfaceMaplikeObject)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TestInterfaceMaplikeObject)
+NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+TestInterfaceMaplikeObject::TestInterfaceMaplikeObject(nsPIDOMWindowInner* aParent)
+: mParent(aParent)
+{
+}
+
+//static
+already_AddRefed<TestInterfaceMaplikeObject>
+TestInterfaceMaplikeObject::Constructor(const GlobalObject& aGlobal,
+ ErrorResult& aRv)
+{
+ nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal.GetAsSupports());
+ if (!window) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return nullptr;
+ }
+
+ RefPtr<TestInterfaceMaplikeObject> r =
+ new TestInterfaceMaplikeObject(window);
+ return r.forget();
+}
+
+JSObject*
+TestInterfaceMaplikeObject::WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto)
+{
+ return TestInterfaceMaplikeObjectBinding::Wrap(aCx, this, aGivenProto);
+}
+
+nsPIDOMWindowInner*
+TestInterfaceMaplikeObject::GetParentObject() const
+{
+ return mParent;
+}
+
+void
+TestInterfaceMaplikeObject::SetInternal(const nsAString& aKey)
+{
+ RefPtr<TestInterfaceMaplike> p(new TestInterfaceMaplike(mParent));
+ ErrorResult rv;
+ TestInterfaceMaplikeObjectBinding::MaplikeHelpers::Set(this, aKey, *p, rv);
+}
+
+void
+TestInterfaceMaplikeObject::ClearInternal()
+{
+ ErrorResult rv;
+ TestInterfaceMaplikeObjectBinding::MaplikeHelpers::Clear(this, rv);
+}
+
+bool
+TestInterfaceMaplikeObject::DeleteInternal(const nsAString& aKey)
+{
+ ErrorResult rv;
+ return TestInterfaceMaplikeObjectBinding::MaplikeHelpers::Delete(this, aKey, rv);
+}
+
+bool
+TestInterfaceMaplikeObject::HasInternal(const nsAString& aKey)
+{
+ ErrorResult rv;
+ return TestInterfaceMaplikeObjectBinding::MaplikeHelpers::Has(this, aKey, rv);
+}
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/bindings/test/TestInterfaceMaplikeObject.h b/dom/bindings/test/TestInterfaceMaplikeObject.h
new file mode 100644
index 000000000..af4660c0d
--- /dev/null
+++ b/dom/bindings/test/TestInterfaceMaplikeObject.h
@@ -0,0 +1,52 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_TestInterfaceMaplikeObject_h
+#define mozilla_dom_TestInterfaceMaplikeObject_h
+
+#include "nsWrapperCache.h"
+#include "nsCOMPtr.h"
+
+class nsPIDOMWindowInner;
+
+namespace mozilla {
+
+class ErrorResult;
+
+namespace dom {
+
+class GlobalObject;
+
+// Implementation of test binding for webidl maplike interfaces, using
+// primitives for key types and objects for value types.
+class TestInterfaceMaplikeObject final : public nsISupports,
+ public nsWrapperCache
+{
+public:
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(TestInterfaceMaplikeObject)
+
+ explicit TestInterfaceMaplikeObject(nsPIDOMWindowInner* aParent);
+ nsPIDOMWindowInner* GetParentObject() const;
+ virtual JSObject* WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) override;
+ static already_AddRefed<TestInterfaceMaplikeObject>
+ Constructor(const GlobalObject& aGlobal,ErrorResult& rv);
+
+ // External access for testing internal convenience functions.
+ void SetInternal(const nsAString& aKey);
+ void ClearInternal();
+ bool DeleteInternal(const nsAString& aKey);
+ bool HasInternal(const nsAString& aKey);
+private:
+ virtual ~TestInterfaceMaplikeObject() {}
+ nsCOMPtr<nsPIDOMWindowInner> mParent;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_TestInterfaceMaplikeObject_h
diff --git a/dom/bindings/test/TestInterfaceSetlike.cpp b/dom/bindings/test/TestInterfaceSetlike.cpp
new file mode 100644
index 000000000..c9f556076
--- /dev/null
+++ b/dom/bindings/test/TestInterfaceSetlike.cpp
@@ -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/. */
+
+#include "mozilla/dom/TestInterfaceSetlike.h"
+#include "mozilla/dom/TestInterfaceJSMaplikeSetlikeIterableBinding.h"
+#include "nsPIDOMWindow.h"
+#include "mozilla/dom/BindingUtils.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(TestInterfaceSetlike, mParent)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(TestInterfaceSetlike)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(TestInterfaceSetlike)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TestInterfaceSetlike)
+NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+TestInterfaceSetlike::TestInterfaceSetlike(JSContext* aCx,
+ nsPIDOMWindowInner* aParent)
+: mParent(aParent)
+{
+}
+
+//static
+already_AddRefed<TestInterfaceSetlike>
+TestInterfaceSetlike::Constructor(const GlobalObject& aGlobal,
+ ErrorResult& aRv)
+{
+ nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal.GetAsSupports());
+ if (!window) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return nullptr;
+ }
+
+ RefPtr<TestInterfaceSetlike> r = new TestInterfaceSetlike(nullptr, window);
+ return r.forget();
+}
+
+JSObject*
+TestInterfaceSetlike::WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto)
+{
+ return TestInterfaceSetlikeBinding::Wrap(aCx, this, aGivenProto);
+}
+
+nsPIDOMWindowInner*
+TestInterfaceSetlike::GetParentObject() const
+{
+ return mParent;
+}
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/bindings/test/TestInterfaceSetlike.h b/dom/bindings/test/TestInterfaceSetlike.h
new file mode 100644
index 000000000..c9f464960
--- /dev/null
+++ b/dom/bindings/test/TestInterfaceSetlike.h
@@ -0,0 +1,46 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_TestInterfaceSetlike_h
+#define mozilla_dom_TestInterfaceSetlike_h
+
+#include "nsWrapperCache.h"
+#include "nsCOMPtr.h"
+
+class nsPIDOMWindowInner;
+
+namespace mozilla {
+
+class ErrorResult;
+
+namespace dom {
+
+class GlobalObject;
+
+// Implementation of test binding for webidl setlike interfaces, using
+// primitives for key type.
+class TestInterfaceSetlike final : public nsISupports,
+ public nsWrapperCache
+{
+public:
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(TestInterfaceSetlike)
+ explicit TestInterfaceSetlike(JSContext* aCx,
+ nsPIDOMWindowInner* aParent);
+ nsPIDOMWindowInner* GetParentObject() const;
+ virtual JSObject* WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) override;
+ static already_AddRefed<TestInterfaceSetlike>
+ Constructor(const GlobalObject& aGlobal, ErrorResult& rv);
+private:
+ virtual ~TestInterfaceSetlike() {}
+ nsCOMPtr<nsPIDOMWindowInner> mParent;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_TestInterfaceSetlike_h
diff --git a/dom/bindings/test/TestInterfaceSetlikeNode.cpp b/dom/bindings/test/TestInterfaceSetlikeNode.cpp
new file mode 100644
index 000000000..5499553fa
--- /dev/null
+++ b/dom/bindings/test/TestInterfaceSetlikeNode.cpp
@@ -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/. */
+
+#include "mozilla/dom/TestInterfaceSetlikeNode.h"
+#include "mozilla/dom/TestInterfaceJSMaplikeSetlikeIterableBinding.h"
+#include "nsPIDOMWindow.h"
+#include "mozilla/dom/BindingUtils.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(TestInterfaceSetlikeNode, mParent)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(TestInterfaceSetlikeNode)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(TestInterfaceSetlikeNode)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TestInterfaceSetlikeNode)
+NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+TestInterfaceSetlikeNode::TestInterfaceSetlikeNode(JSContext* aCx,
+ nsPIDOMWindowInner* aParent)
+: mParent(aParent)
+{
+}
+
+//static
+already_AddRefed<TestInterfaceSetlikeNode>
+TestInterfaceSetlikeNode::Constructor(const GlobalObject& aGlobal,
+ ErrorResult& aRv)
+{
+ nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal.GetAsSupports());
+ if (!window) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return nullptr;
+ }
+
+ RefPtr<TestInterfaceSetlikeNode> r = new TestInterfaceSetlikeNode(nullptr, window);
+ return r.forget();
+}
+
+JSObject*
+TestInterfaceSetlikeNode::WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto)
+{
+ return TestInterfaceSetlikeNodeBinding::Wrap(aCx, this, aGivenProto);
+}
+
+nsPIDOMWindowInner*
+TestInterfaceSetlikeNode::GetParentObject() const
+{
+ return mParent;
+}
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/bindings/test/TestInterfaceSetlikeNode.h b/dom/bindings/test/TestInterfaceSetlikeNode.h
new file mode 100644
index 000000000..05b14190e
--- /dev/null
+++ b/dom/bindings/test/TestInterfaceSetlikeNode.h
@@ -0,0 +1,46 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_TestInterfaceSetlikeNode_h
+#define mozilla_dom_TestInterfaceSetlikeNode_h
+
+#include "nsWrapperCache.h"
+#include "nsCOMPtr.h"
+
+class nsPIDOMWindowInner;
+
+namespace mozilla {
+
+class ErrorResult;
+
+namespace dom {
+
+class GlobalObject;
+
+// Implementation of test binding for webidl setlike interfaces, using
+// primitives for key type.
+class TestInterfaceSetlikeNode final : public nsISupports,
+ public nsWrapperCache
+{
+public:
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(TestInterfaceSetlikeNode)
+ explicit TestInterfaceSetlikeNode(JSContext* aCx,
+ nsPIDOMWindowInner* aParent);
+ nsPIDOMWindowInner* GetParentObject() const;
+ virtual JSObject* WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) override;
+ static already_AddRefed<TestInterfaceSetlikeNode>
+ Constructor(const GlobalObject& aGlobal, ErrorResult& rv);
+private:
+ virtual ~TestInterfaceSetlikeNode() {}
+ nsCOMPtr<nsPIDOMWindowInner> mParent;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_TestInterfaceSetlikeNode_h
diff --git a/dom/bindings/test/TestJSImplGen.webidl b/dom/bindings/test/TestJSImplGen.webidl
new file mode 100644
index 000000000..a131dcdfe
--- /dev/null
+++ b/dom/bindings/test/TestJSImplGen.webidl
@@ -0,0 +1,836 @@
+/* -*- 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 TestJSImplInterface AnotherNameForTestJSImplInterface;
+typedef TestJSImplInterface YetAnotherNameForTestJSImplInterface;
+typedef TestJSImplInterface? NullableTestJSImplInterface;
+
+callback MyTestCallback = void();
+
+enum MyTestEnum {
+ "a",
+ "b"
+};
+
+// We don't support multiple constructors (bug 869268) or named constructors
+// for JS-implemented WebIDL.
+[Constructor(DOMString str, unsigned long num, boolean? boolArg,
+ TestInterface? iface, long arg1,
+ DictForConstructor dict, any any1,
+ object obj1,
+ object? obj2, sequence<Dict> seq, optional any any2,
+ optional object obj3,
+ optional object? obj4,
+ Uint8Array typedArr,
+ ArrayBuffer arrayBuf),
+ JSImplementation="@mozilla.org/test-js-impl-interface;1"]
+interface TestJSImplInterface {
+ // 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 passOptionalByteBeforeRequired(optional byte arg1, byte arg2);
+ void passOptionalByteWithDefault(optional byte arg = 0);
+ void passOptionalByteWithDefaultBeforeRequired(optional byte arg1 = 0, byte arg2);
+ void passNullableByte(byte? arg);
+ void passOptionalNullableByte(optional byte? arg);
+ void passVariadicByte(byte... arg);
+ [Cached, Pure]
+ readonly attribute byte cachedByte;
+ [Cached, Constant]
+ readonly attribute byte cachedConstantByte;
+ [Cached, Pure]
+ attribute byte cachedWritableByte;
+ [Affects=Nothing]
+ attribute byte sideEffectFreeByte;
+ [Affects=Nothing, DependsOn=DOMState]
+ attribute byte domDependentByte;
+ [Affects=Nothing, DependsOn=Nothing]
+ readonly attribute byte constantByte;
+ [DependsOn=DeviceState, Affects=Nothing]
+ readonly attribute byte deviceStateDependentByte;
+ [Affects=Nothing]
+ byte returnByteSideEffectFree();
+ [Affects=Nothing, DependsOn=DOMState]
+ byte returnDOMDependentByte();
+ [Affects=Nothing, DependsOn=Nothing]
+ byte returnConstantByte();
+ [DependsOn=DeviceState, Affects=Nothing]
+ byte returnDeviceStateDependentByte();
+
+ 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);
+
+ attribute float writableFloat;
+ attribute unrestricted float writableUnrestrictedFloat;
+ attribute float? writableNullableFloat;
+ attribute unrestricted float? writableNullableUnrestrictedFloat;
+ attribute double writableDouble;
+ attribute unrestricted double writableUnrestrictedDouble;
+ attribute double? writableNullableDouble;
+ attribute unrestricted double? writableNullableUnrestrictedDouble;
+ void passFloat(float arg1, unrestricted float arg2,
+ float? arg3, unrestricted float? arg4,
+ double arg5, unrestricted double arg6,
+ double? arg7, unrestricted double? arg8,
+ sequence<float> arg9, sequence<unrestricted float> arg10,
+ sequence<float?> arg11, sequence<unrestricted float?> arg12,
+ sequence<double> arg13, sequence<unrestricted double> arg14,
+ sequence<double?> arg15, sequence<unrestricted double?> arg16);
+ [LenientFloat]
+ void passLenientFloat(float arg1, unrestricted float arg2,
+ float? arg3, unrestricted float? arg4,
+ double arg5, unrestricted double arg6,
+ double? arg7, unrestricted double? arg8,
+ sequence<float> arg9,
+ sequence<unrestricted float> arg10,
+ sequence<float?> arg11,
+ sequence<unrestricted float?> arg12,
+ sequence<double> arg13,
+ sequence<unrestricted double> arg14,
+ sequence<double?> arg15,
+ sequence<unrestricted double?> arg16);
+ [LenientFloat]
+ attribute float lenientFloatAttr;
+ [LenientFloat]
+ attribute double lenientDoubleAttr;
+
+ // Castable interface types
+ // XXXbz add tests for throwing versions of all the castable interface stuff
+ TestJSImplInterface receiveSelf();
+ TestJSImplInterface? receiveNullableSelf();
+
+ TestJSImplInterface receiveWeakSelf();
+ TestJSImplInterface? receiveWeakNullableSelf();
+
+ // A version to test for casting to TestJSImplInterface&
+ void passSelf(TestJSImplInterface arg);
+ void passNullableSelf(TestJSImplInterface? arg);
+ attribute TestJSImplInterface nonNullSelf;
+ attribute TestJSImplInterface? nullableSelf;
+ [Cached, Pure]
+ readonly attribute TestJSImplInterface cachedSelf;
+ // Optional arguments
+ void passOptionalSelf(optional TestJSImplInterface? arg);
+ void passOptionalNonNullSelf(optional TestJSImplInterface arg);
+ void passOptionalSelfWithDefault(optional TestJSImplInterface? arg = null);
+
+ // Non-wrapper-cache interface types
+ [NewObject]
+ TestNonWrapperCacheInterface receiveNonWrapperCacheInterface();
+ [NewObject]
+ TestNonWrapperCacheInterface? receiveNullableNonWrapperCacheInterface();
+
+ [NewObject]
+ sequence<TestNonWrapperCacheInterface> receiveNonWrapperCacheInterfaceSequence();
+ [NewObject]
+ sequence<TestNonWrapperCacheInterface?> receiveNullableNonWrapperCacheInterfaceSequence();
+ [NewObject]
+ sequence<TestNonWrapperCacheInterface>? receiveNonWrapperCacheInterfaceNullableSequence();
+ [NewObject]
+ sequence<TestNonWrapperCacheInterface?>? receiveNullableNonWrapperCacheInterfaceNullableSequence();
+
+ // Non-castable interface types
+ IndirectlyImplementedInterface receiveOther();
+ IndirectlyImplementedInterface? receiveNullableOther();
+ IndirectlyImplementedInterface receiveWeakOther();
+ IndirectlyImplementedInterface? receiveWeakNullableOther();
+
+ void passOther(IndirectlyImplementedInterface arg);
+ void passNullableOther(IndirectlyImplementedInterface? arg);
+ attribute IndirectlyImplementedInterface nonNullOther;
+ attribute IndirectlyImplementedInterface? nullableOther;
+ // Optional arguments
+ void passOptionalOther(optional IndirectlyImplementedInterface? arg);
+ void passOptionalNonNullOther(optional IndirectlyImplementedInterface arg);
+ void passOptionalOtherWithDefault(optional IndirectlyImplementedInterface? arg = null);
+
+ // External interface types
+ TestExternalInterface receiveExternal();
+ TestExternalInterface? receiveNullableExternal();
+ TestExternalInterface receiveWeakExternal();
+ TestExternalInterface? receiveWeakNullableExternal();
+ void passExternal(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();
+ void passCallbackInterface(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
+ [Cached, Pure]
+ readonly attribute sequence<long> readonlySequence;
+ [Cached, Pure]
+ readonly attribute sequence<Dict> readonlySequenceOfDictionaries;
+ [Cached, Pure]
+ readonly attribute sequence<Dict>? readonlyNullableSequenceOfDictionaries;
+ [Cached, Pure, Frozen]
+ readonly attribute sequence<long> readonlyFrozenSequence;
+ [Cached, Pure, Frozen]
+ readonly attribute sequence<long>? readonlyFrozenNullableSequence;
+ 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<TestJSImplInterface> receiveCastableObjectSequence();
+ sequence<TestCallbackInterface> receiveCallbackObjectSequence();
+ sequence<TestJSImplInterface?> receiveNullableCastableObjectSequence();
+ sequence<TestCallbackInterface?> receiveNullableCallbackObjectSequence();
+ sequence<TestJSImplInterface>? receiveCastableObjectNullableSequence();
+ sequence<TestJSImplInterface?>? receiveNullableCastableObjectNullableSequence();
+ sequence<TestJSImplInterface> receiveWeakCastableObjectSequence();
+ sequence<TestJSImplInterface?> receiveWeakNullableCastableObjectSequence();
+ sequence<TestJSImplInterface>? receiveWeakCastableObjectNullableSequence();
+ sequence<TestJSImplInterface?>? receiveWeakNullableCastableObjectNullableSequence();
+ void passCastableObjectSequence(sequence<TestJSImplInterface> arg);
+ void passNullableCastableObjectSequence(sequence<TestJSImplInterface?> arg);
+ void passCastableObjectNullableSequence(sequence<TestJSImplInterface>? arg);
+ void passNullableCastableObjectNullableSequence(sequence<TestJSImplInterface?>? arg);
+ void passOptionalSequence(optional sequence<long> arg);
+ void passOptionalSequenceWithDefaultValue(optional sequence<long> arg = []);
+ void passOptionalNullableSequence(optional sequence<long>? arg);
+ void passOptionalNullableSequenceWithDefaultValue(optional sequence<long>? arg = null);
+ void passOptionalNullableSequenceWithDefaultValue2(optional sequence<long>? arg = []);
+ void passOptionalObjectSequence(optional sequence<TestJSImplInterface> arg);
+ void passExternalInterfaceSequence(sequence<TestExternalInterface> arg);
+ void passNullableExternalInterfaceSequence(sequence<TestExternalInterface?> arg);
+
+ sequence<DOMString> receiveStringSequence();
+ sequence<ByteString> receiveByteStringSequence();
+ // Callback interface problem. See bug 843261.
+ //void passStringSequence(sequence<DOMString> arg);
+ sequence<any> receiveAnySequence();
+ sequence<any>? receiveNullableAnySequence();
+ //XXXbz No support for sequence of sequence return values yet.
+ //sequence<sequence<any>> receiveAnySequenceSequence();
+
+ sequence<object> receiveObjectSequence();
+ sequence<object?> receiveNullableObjectSequence();
+
+ void passSequenceOfSequences(sequence<sequence<long>> arg);
+ void passSequenceOfSequencesOfSequences(sequence<sequence<sequence<long>>> arg);
+ //XXXbz No support for sequence of sequence return values yet.
+ //sequence<sequence<long>> receiveSequenceOfSequences();
+
+ // MozMap types
+ void passMozMap(MozMap<long> arg);
+ void passNullableMozMap(MozMap<long>? arg);
+ void passMozMapOfNullableInts(MozMap<long?> arg);
+ void passOptionalMozMapOfNullableInts(optional MozMap<long?> arg);
+ void passOptionalNullableMozMapOfNullableInts(optional MozMap<long?>? arg);
+ void passCastableObjectMozMap(MozMap<TestJSImplInterface> arg);
+ void passNullableCastableObjectMozMap(MozMap<TestJSImplInterface?> arg);
+ void passCastableObjectNullableMozMap(MozMap<TestJSImplInterface>? arg);
+ void passNullableCastableObjectNullableMozMap(MozMap<TestJSImplInterface?>? arg);
+ void passOptionalMozMap(optional MozMap<long> arg);
+ void passOptionalNullableMozMap(optional MozMap<long>? arg);
+ void passOptionalNullableMozMapWithDefaultValue(optional MozMap<long>? arg = null);
+ void passOptionalObjectMozMap(optional MozMap<TestJSImplInterface> arg);
+ void passExternalInterfaceMozMap(MozMap<TestExternalInterface> arg);
+ void passNullableExternalInterfaceMozMap(MozMap<TestExternalInterface?> arg);
+ void passStringMozMap(MozMap<DOMString> arg);
+ void passByteStringMozMap(MozMap<ByteString> arg);
+ void passMozMapOfMozMaps(MozMap<MozMap<long>> arg);
+ MozMap<long> receiveMozMap();
+ MozMap<long>? receiveNullableMozMap();
+ MozMap<long?> receiveMozMapOfNullableInts();
+ MozMap<long?>? receiveNullableMozMapOfNullableInts();
+ //XXXbz No support for MozMap of MozMaps return values yet.
+ //MozMap<MozMap<long>> receiveMozMapOfMozMaps();
+ MozMap<any> receiveAnyMozMap();
+
+ // 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);
+ void passSequenceOfArrayBuffers(sequence<ArrayBuffer> arg);
+ void passSequenceOfNullableArrayBuffers(sequence<ArrayBuffer?> arg);
+ void passMozMapOfArrayBuffers(MozMap<ArrayBuffer> arg);
+ void passMozMapOfNullableArrayBuffers(MozMap<ArrayBuffer?> arg);
+ void passVariadicTypedArray(Float32Array... arg);
+ void passVariadicNullableTypedArray(Float32Array?... arg);
+ Uint8Array receiveUint8Array();
+ attribute Uint8Array uint8ArrayAttr;
+
+ // DOMString 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);
+ void passVariadicString(DOMString... arg);
+
+ // ByteString types
+ void passByteString(ByteString arg);
+ void passNullableByteString(ByteString? arg);
+ void passOptionalByteString(optional ByteString arg);
+ void passOptionalByteStringWithDefaultValue(optional ByteString arg = "abc");
+ void passOptionalNullableByteString(optional ByteString? arg);
+ void passOptionalNullableByteStringWithDefaultValue(optional ByteString? arg = null);
+ void passVariadicByteString(ByteString... arg);
+ void passUnionByteString((ByteString or long) arg);
+ void passOptionalUnionByteString(optional (ByteString or long) arg);
+ void passOptionalUnionByteStringWithDefaultValue(optional (ByteString or long) arg = "abc");
+
+ // USVString types
+ void passSVS(USVString arg);
+ void passNullableSVS(USVString? arg);
+ void passOptionalSVS(optional USVString arg);
+ void passOptionalSVSWithDefaultValue(optional USVString arg = "abc");
+ void passOptionalNullableSVS(optional USVString? arg);
+ void passOptionalNullableSVSWithDefaultValue(optional USVString? arg = null);
+ void passVariadicSVS(USVString... arg);
+ USVString receiveSVS();
+
+ // Enumerated types
+ void passEnum(MyTestEnum arg);
+ void passNullableEnum(MyTestEnum? arg);
+ void passOptionalEnum(optional MyTestEnum arg);
+ void passEnumWithDefault(optional MyTestEnum arg = "a");
+ void passOptionalNullableEnum(optional MyTestEnum? arg);
+ void passOptionalNullableEnumWithDefaultValue(optional MyTestEnum? arg = null);
+ void passOptionalNullableEnumWithDefaultValue2(optional MyTestEnum? arg = "a");
+ MyTestEnum receiveEnum();
+ MyTestEnum? receiveNullableEnum();
+ attribute MyTestEnum enumAttribute;
+ readonly attribute MyTestEnum readonlyEnumAttribute;
+
+ // Callback types
+ void passCallback(MyTestCallback arg);
+ void passNullableCallback(MyTestCallback? arg);
+ void passOptionalCallback(optional MyTestCallback arg);
+ void passOptionalNullableCallback(optional MyTestCallback? arg);
+ void passOptionalNullableCallbackWithDefaultValue(optional MyTestCallback? arg = null);
+ MyTestCallback receiveCallback();
+ MyTestCallback? receiveNullableCallback();
+ // Hmm. These two don't work, I think because I need a locally modified version of TestTreatAsNullCallback.
+ //void passNullableTreatAsNullCallback(TestTreatAsNullCallback? arg);
+ //void passOptionalNullableTreatAsNullCallback(optional TestTreatAsNullCallback? arg);
+ void passOptionalNullableTreatAsNullCallbackWithDefaultValue(optional TestTreatAsNullCallback? arg = null);
+
+ // Any types
+ void passAny(any arg);
+ void passVariadicAny(any... arg);
+ void passOptionalAny(optional any arg);
+ void passAnyDefaultNull(optional any arg = null);
+ void passSequenceOfAny(sequence<any> arg);
+ void passNullableSequenceOfAny(sequence<any>? arg);
+ void passOptionalSequenceOfAny(optional sequence<any> arg);
+ void passOptionalNullableSequenceOfAny(optional sequence<any>? arg);
+ void passOptionalSequenceOfAnyWithDefaultValue(optional sequence<any>? arg = null);
+ void passSequenceOfSequenceOfAny(sequence<sequence<any>> arg);
+ void passSequenceOfNullableSequenceOfAny(sequence<sequence<any>?> arg);
+ void passNullableSequenceOfNullableSequenceOfAny(sequence<sequence<any>?>? arg);
+ void passOptionalNullableSequenceOfNullableSequenceOfAny(optional sequence<sequence<any>?>? arg);
+ void passMozMapOfAny(MozMap<any> arg);
+ void passNullableMozMapOfAny(MozMap<any>? arg);
+ void passOptionalMozMapOfAny(optional MozMap<any> arg);
+ void passOptionalNullableMozMapOfAny(optional MozMap<any>? arg);
+ void passOptionalMozMapOfAnyWithDefaultValue(optional MozMap<any>? arg = null);
+ void passMozMapOfMozMapOfAny(MozMap<MozMap<any>> arg);
+ void passMozMapOfNullableMozMapOfAny(MozMap<MozMap<any>?> arg);
+ void passNullableMozMapOfNullableMozMapOfAny(MozMap<MozMap<any>?>? arg);
+ void passOptionalNullableMozMapOfNullableMozMapOfAny(optional MozMap<MozMap<any>?>? arg);
+ void passOptionalNullableMozMapOfNullableSequenceOfAny(optional MozMap<sequence<any>?>? arg);
+ void passOptionalNullableSequenceOfNullableMozMapOfAny(optional sequence<MozMap<any>?>? arg);
+ any receiveAny();
+
+ // object types
+ void passObject(object arg);
+ void passVariadicObject(object... arg);
+ void passNullableObject(object? arg);
+ void passVariadicNullableObject(object... arg);
+ void passOptionalObject(optional object arg);
+ void passOptionalNullableObject(optional object? arg);
+ void passOptionalNullableObjectWithDefaultValue(optional object? arg = null);
+ void passSequenceOfObject(sequence<object> arg);
+ void passSequenceOfNullableObject(sequence<object?> arg);
+ void passNullableSequenceOfObject(sequence<object>? arg);
+ void passOptionalNullableSequenceOfNullableSequenceOfObject(optional sequence<sequence<object>?>? arg);
+ void passOptionalNullableSequenceOfNullableSequenceOfNullableObject(optional sequence<sequence<object?>?>? arg);
+ void passMozMapOfObject(MozMap<object> arg);
+ object receiveObject();
+ object? receiveNullableObject();
+
+ // Union types
+ void passUnion((object or long) arg);
+ // Some union tests are debug-only to avoid creating all those
+ // unused union types in opt builds.
+#ifdef DEBUG
+ void passUnion2((long or boolean) arg);
+ void passUnion3((object or long or boolean) arg);
+ void passUnion4((Node or long or boolean) arg);
+ void passUnion5((object or boolean) arg);
+ void passUnion6((object or DOMString) arg);
+ void passUnion7((object or DOMString or long) arg);
+ void passUnion8((object or DOMString or boolean) arg);
+ void passUnion9((object or DOMString or long or boolean) arg);
+ void passUnion10(optional (EventInit or long) arg);
+ void passUnion11(optional (CustomEventInit or long) arg);
+ void passUnion12(optional (EventInit or long) arg = 5);
+ void passUnion13(optional (object or long?) arg = null);
+ void passUnion14(optional (object or long?) arg = 5);
+ void passUnion15((sequence<long> or long) arg);
+ void passUnion16(optional (sequence<long> or long) arg);
+ void passUnion17(optional (sequence<long>? or long) arg = 5);
+ void passUnion18((sequence<object> or long) arg);
+ void passUnion19(optional (sequence<object> or long) arg);
+ void passUnion20(optional (sequence<object> or long) arg = []);
+ void passUnion21((MozMap<long> or long) arg);
+ void passUnion22((MozMap<object> or long) arg);
+ void passUnion23((sequence<ImageData> or long) arg);
+ void passUnion24((sequence<ImageData?> or long) arg);
+ void passUnion25((sequence<sequence<ImageData>> or long) arg);
+ void passUnion26((sequence<sequence<ImageData?>> or long) arg);
+ void passUnion27(optional (sequence<DOMString> or EventInit) arg);
+ void passUnion28(optional (EventInit or sequence<DOMString>) arg);
+ void passUnionWithCallback((EventHandler or long) arg);
+ void passUnionWithByteString((ByteString or long) arg);
+ void passUnionWithMozMap((MozMap<DOMString> or DOMString) arg);
+ void passUnionWithMozMapAndSequence((MozMap<DOMString> or sequence<DOMString>) arg);
+ void passUnionWithSequenceAndMozMap((sequence<DOMString> or MozMap<DOMString>) arg);
+ void passUnionWithSVS((USVString or long) arg);
+#endif
+ 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((TestJSImplInterface or TestExternalInterface) arg);
+ //void passUnionWithInterfacesAndNullable((TestJSImplInterface? or TestExternalInterface) arg);
+ //void passUnionWithSequence((sequence<object> or long) arg);
+ void passUnionWithArrayBuffer((ArrayBuffer or long) arg);
+ void passUnionWithString((DOMString or object) arg);
+ // Using an enum in a union. Note that we use some enum not declared in our
+ // binding file, because UnionTypes.h will need to include the binding header
+ // for this enum. Pick an enum from an interface that won't drag in too much
+ // stuff.
+ void passUnionWithEnum((SupportedType or object) arg);
+
+ // Trying to use a callback in a union won't include the test
+ // headers, unfortunately, so won't compile.
+ // void passUnionWithCallback((MyTestCallback or long) arg);
+ void passUnionWithObject((object or long) arg);
+ //void passUnionWithDict((Dict or long) arg);
+
+ void passUnionWithDefaultValue1(optional (double or DOMString) arg = "");
+ void passUnionWithDefaultValue2(optional (double or DOMString) arg = 1);
+ void passUnionWithDefaultValue3(optional (double or DOMString) arg = 1.5);
+ void passUnionWithDefaultValue4(optional (float or DOMString) arg = "");
+ void passUnionWithDefaultValue5(optional (float or DOMString) arg = 1);
+ void passUnionWithDefaultValue6(optional (float or DOMString) arg = 1.5);
+ void passUnionWithDefaultValue7(optional (unrestricted double or DOMString) arg = "");
+ void passUnionWithDefaultValue8(optional (unrestricted double or DOMString) arg = 1);
+ void passUnionWithDefaultValue9(optional (unrestricted double or DOMString) arg = 1.5);
+ void passUnionWithDefaultValue10(optional (unrestricted double or DOMString) arg = Infinity);
+ void passUnionWithDefaultValue11(optional (unrestricted float or DOMString) arg = "");
+ void passUnionWithDefaultValue12(optional (unrestricted float or DOMString) arg = 1);
+ void passUnionWithDefaultValue13(optional (unrestricted float or DOMString) arg = Infinity);
+ void passUnionWithDefaultValue14(optional (double or ByteString) arg = "");
+ void passUnionWithDefaultValue15(optional (double or ByteString) arg = 1);
+ void passUnionWithDefaultValue16(optional (double or ByteString) arg = 1.5);
+ void passUnionWithDefaultValue17(optional (double or SupportedType) arg = "text/html");
+ void passUnionWithDefaultValue18(optional (double or SupportedType) arg = 1);
+ void passUnionWithDefaultValue19(optional (double or SupportedType) arg = 1.5);
+
+ void passNullableUnionWithDefaultValue1(optional (double or DOMString)? arg = "");
+ void passNullableUnionWithDefaultValue2(optional (double or DOMString)? arg = 1);
+ void passNullableUnionWithDefaultValue3(optional (double or DOMString)? arg = null);
+ void passNullableUnionWithDefaultValue4(optional (float or DOMString)? arg = "");
+ void passNullableUnionWithDefaultValue5(optional (float or DOMString)? arg = 1);
+ void passNullableUnionWithDefaultValue6(optional (float or DOMString)? arg = null);
+ void passNullableUnionWithDefaultValue7(optional (unrestricted double or DOMString)? arg = "");
+ void passNullableUnionWithDefaultValue8(optional (unrestricted double or DOMString)? arg = 1);
+ void passNullableUnionWithDefaultValue9(optional (unrestricted double or DOMString)? arg = null);
+ void passNullableUnionWithDefaultValue10(optional (unrestricted float or DOMString)? arg = "");
+ void passNullableUnionWithDefaultValue11(optional (unrestricted float or DOMString)? arg = 1);
+ void passNullableUnionWithDefaultValue12(optional (unrestricted float or DOMString)? arg = null);
+ void passNullableUnionWithDefaultValue13(optional (double or ByteString)? arg = "");
+ void passNullableUnionWithDefaultValue14(optional (double or ByteString)? arg = 1);
+ void passNullableUnionWithDefaultValue15(optional (double or ByteString)? arg = 1.5);
+ void passNullableUnionWithDefaultValue16(optional (double or ByteString)? arg = null);
+ void passNullableUnionWithDefaultValue17(optional (double or SupportedType)? arg = "text/html");
+ void passNullableUnionWithDefaultValue18(optional (double or SupportedType)? arg = 1);
+ void passNullableUnionWithDefaultValue19(optional (double or SupportedType)? arg = 1.5);
+ void passNullableUnionWithDefaultValue20(optional (double or SupportedType)? arg = null);
+
+ void passSequenceOfUnions(sequence<(CanvasPattern or CanvasGradient)> arg);
+ void passSequenceOfUnions2(sequence<(object or long)> arg);
+ void passVariadicUnion((CanvasPattern or CanvasGradient)... arg);
+
+ void passSequenceOfNullableUnions(sequence<(CanvasPattern or CanvasGradient)?> arg);
+ void passVariadicNullableUnion((CanvasPattern or CanvasGradient)?... arg);
+ void passMozMapOfUnions(MozMap<(CanvasPattern or CanvasGradient)> arg);
+ // XXXbz no move constructor on some unions
+ // void passMozMapOfUnions2(MozMap<(object or long)> arg);
+
+ (CanvasPattern or CanvasGradient) receiveUnion();
+ (object or long) receiveUnion2();
+ (CanvasPattern? or CanvasGradient) receiveUnionContainingNull();
+ (CanvasPattern or CanvasGradient)? receiveNullableUnion();
+ (object or long)? receiveNullableUnion2();
+
+ attribute (CanvasPattern or CanvasGradient) writableUnion;
+ attribute (CanvasPattern? or CanvasGradient) writableUnionContainingNull;
+ attribute (CanvasPattern or CanvasGradient)? writableNullableUnion;
+
+ // Date types
+ void passDate(Date arg);
+ void passNullableDate(Date? arg);
+ void passOptionalDate(optional Date arg);
+ void passOptionalNullableDate(optional Date? arg);
+ void passOptionalNullableDateWithDefaultValue(optional Date? arg = null);
+ void passDateSequence(sequence<Date> arg);
+ void passNullableDateSequence(sequence<Date?> arg);
+ void passDateMozMap(MozMap<Date> arg);
+ Date receiveDate();
+ Date? receiveNullableDate();
+
+ // Promise types
+ void passPromise(Promise<any> arg);
+ void passNullablePromise(Promise<any>? arg);
+ void passOptionalPromise(optional Promise<any> arg);
+ void passOptionalNullablePromise(optional Promise<any>? arg);
+ void passOptionalNullablePromiseWithDefaultValue(optional Promise<any>? arg = null);
+ void passPromiseSequence(sequence<Promise<any>> arg);
+ void passNullablePromiseSequence(sequence<Promise<any>?> arg);
+ Promise<any> receivePromise();
+ Promise<any> receiveAddrefedPromise();
+
+ // binaryNames tests
+ void methodRenamedFrom();
+ [BinaryName="otherMethodRenamedTo"]
+ void otherMethodRenamedFrom();
+ void methodRenamedFrom(byte argument);
+ readonly attribute byte attributeGetterRenamedFrom;
+ attribute byte attributeRenamedFrom;
+ [BinaryName="otherAttributeRenamedTo"]
+ attribute byte otherAttributeRenamedFrom;
+
+ void passDictionary(optional Dict x);
+ void passDictionary2(Dict x);
+ [Cached, Pure]
+ readonly attribute Dict readonlyDictionary;
+ [Cached, Pure]
+ readonly attribute Dict? readonlyNullableDictionary;
+ [Cached, Pure]
+ attribute Dict writableDictionary;
+ [Cached, Pure, Frozen]
+ readonly attribute Dict readonlyFrozenDictionary;
+ [Cached, Pure, Frozen]
+ readonly attribute Dict? readonlyFrozenNullableDictionary;
+ [Cached, Pure, Frozen]
+ attribute Dict writableFrozenDictionary;
+ Dict receiveDictionary();
+ Dict? receiveNullableDictionary();
+ void passOtherDictionary(optional GrandparentDict x);
+ void passSequenceOfDictionaries(sequence<Dict> x);
+ void passMozMapOfDictionaries(MozMap<GrandparentDict> x);
+ // No support for nullable dictionaries inside a sequence (nor should there be)
+ // void passSequenceOfNullableDictionaries(sequence<Dict?> x);
+ void passDictionaryOrLong(optional Dict x);
+ void passDictionaryOrLong(long x);
+
+ void passDictContainingDict(optional DictContainingDict arg);
+ void passDictContainingSequence(optional DictContainingSequence arg);
+ DictContainingSequence receiveDictContainingSequence();
+ void passVariadicDictionary(Dict... arg);
+
+ // EnforceRange/Clamp tests
+ void dontEnforceRangeOrClamp(byte arg);
+ void doEnforceRange([EnforceRange] byte arg);
+ void doClamp([Clamp] byte arg);
+ [EnforceRange] attribute byte enforcedByte;
+ [Clamp] attribute byte clampedByte;
+
+ // Typedefs
+ const myLong myLongConstant = 5;
+ void exerciseTypedefInterfaces1(AnotherNameForTestJSImplInterface arg);
+ AnotherNameForTestJSImplInterface exerciseTypedefInterfaces2(NullableTestJSImplInterface arg);
+ void exerciseTypedefInterfaces3(YetAnotherNameForTestJSImplInterface arg);
+
+ // Deprecated methods and attributes
+ [Deprecated="GetAttributeNode"]
+ attribute byte deprecatedAttribute;
+ [Deprecated="GetAttributeNode"]
+ byte deprecatedMethod();
+ [Deprecated="GetAttributeNode"]
+ void deprecatedMethodWithContext(any arg);
+
+ // Static methods and attributes
+ // FIXME: Bug 863952 Static things are not supported yet
+ /*
+ static attribute boolean staticAttribute;
+ static void staticMethod(boolean arg);
+ static void staticMethodWithContext(any arg);
+
+ // Deprecated static methods and attributes
+ [Deprecated="GetAttributeNode"]
+ static attribute byte staticDeprecatedAttribute;
+ [Deprecated="GetAttributeNode"]
+ static byte staticDeprecatedMethod();
+ [Deprecated="GetAttributeNode"]
+ static byte staticDeprecatedMethodWithContext();
+ */
+
+ // Overload resolution tests
+ //void overload1(DOMString... strs);
+ boolean overload1(TestJSImplInterface arg);
+ TestJSImplInterface overload1(DOMString strs, TestJSImplInterface arg);
+ void overload2(TestJSImplInterface arg);
+ void overload2(optional Dict arg);
+ void overload2(boolean arg);
+ void overload2(DOMString arg);
+ void overload2(Date arg);
+ void overload3(TestJSImplInterface arg);
+ void overload3(MyTestCallback arg);
+ void overload3(boolean arg);
+ void overload4(TestJSImplInterface arg);
+ void overload4(TestCallbackInterface arg);
+ void overload4(DOMString arg);
+ void overload5(long arg);
+ void overload5(MyTestEnum arg);
+ void overload6(long arg);
+ void overload6(boolean arg);
+ void overload7(long arg);
+ void overload7(boolean arg);
+ void overload7(ByteString arg);
+ void overload8(long arg);
+ void overload8(TestJSImplInterface arg);
+ void overload9(long? arg);
+ void overload9(DOMString arg);
+ void overload10(long? arg);
+ void overload10(object arg);
+ void overload11(long arg);
+ void overload11(DOMString? arg);
+ void overload12(long arg);
+ void overload12(boolean? arg);
+ void overload13(long? arg);
+ void overload13(boolean arg);
+ void overload14(optional long arg);
+ void overload14(TestInterface arg);
+ void overload15(long arg);
+ void overload15(optional TestInterface arg);
+ void overload16(long arg);
+ void overload16(optional TestInterface? arg);
+ void overload17(sequence<long> arg);
+ void overload17(MozMap<long> arg);
+ void overload18(MozMap<DOMString> arg);
+ void overload18(sequence<DOMString> arg);
+ void overload19(sequence<long> arg);
+ void overload19(optional Dict arg);
+ void overload20(optional Dict arg);
+ void overload20(sequence<long> arg);
+
+ // Variadic handling
+ void passVariadicThirdArg(DOMString arg1, long arg2, TestJSImplInterface... arg3);
+
+ // Conditionally exposed methods/attributes
+ [Pref="abc.def"]
+ readonly attribute boolean prefable1;
+ [Pref="abc.def"]
+ readonly attribute boolean prefable2;
+ [Pref="ghi.jkl"]
+ readonly attribute boolean prefable3;
+ [Pref="ghi.jkl"]
+ readonly attribute boolean prefable4;
+ [Pref="abc.def"]
+ readonly attribute boolean prefable5;
+ [Pref="abc.def", Func="nsGenericHTMLElement::TouchEventsEnabled"]
+ readonly attribute boolean prefable6;
+ [Pref="abc.def", Func="nsGenericHTMLElement::TouchEventsEnabled"]
+ readonly attribute boolean prefable7;
+ [Pref="ghi.jkl", Func="nsGenericHTMLElement::TouchEventsEnabled"]
+ readonly attribute boolean prefable8;
+ [Pref="abc.def", Func="nsGenericHTMLElement::TouchEventsEnabled"]
+ readonly attribute boolean prefable9;
+ [Pref="abc.def"]
+ void prefable10();
+ [Pref="abc.def", Func="nsGenericHTMLElement::TouchEventsEnabled"]
+ void prefable11();
+ [Pref="abc.def", Func="TestFuncControlledMember"]
+ readonly attribute boolean prefable12;
+ [Pref="abc.def", Func="nsGenericHTMLElement::TouchEventsEnabled"]
+ void prefable13();
+ [Pref="abc.def", Func="TestFuncControlledMember"]
+ readonly attribute boolean prefable14;
+ [Func="TestFuncControlledMember"]
+ readonly attribute boolean prefable15;
+ [Func="TestFuncControlledMember"]
+ readonly attribute boolean prefable16;
+ [Pref="abc.def", Func="TestFuncControlledMember"]
+ void prefable17();
+ [Func="TestFuncControlledMember"]
+ void prefable18();
+ [Func="TestFuncControlledMember"]
+ void prefable19();
+ [Pref="abc.def", Func="TestFuncControlledMember", ChromeOnly]
+ void prefable20();
+
+ // Conditionally exposed methods/attributes involving [SecureContext]
+ [SecureContext]
+ readonly attribute boolean conditionalOnSecureContext1;
+ [SecureContext, Pref="abc.def"]
+ readonly attribute boolean conditionalOnSecureContext2;
+ [SecureContext, Pref="abc.def", Func="nsGenericHTMLElement::TouchEventsEnabled"]
+ readonly attribute boolean conditionalOnSecureContext3;
+ [SecureContext, Pref="abc.def", Func="TestFuncControlledMember"]
+ readonly attribute boolean conditionalOnSecureContext4;
+ [SecureContext]
+ void conditionalOnSecureContext5();
+ [SecureContext, Pref="abc.def"]
+ void conditionalOnSecureContext6();
+ [SecureContext, Pref="abc.def", Func="nsGenericHTMLElement::TouchEventsEnabled"]
+ void conditionalOnSecureContext7();
+ [SecureContext, Pref="abc.def", Func="TestFuncControlledMember"]
+ void conditionalOnSecureContext8();
+
+ // Miscellania
+ [LenientThis] attribute long attrWithLenientThis;
+ // FIXME: Bug 863954 Unforgeable things get all confused when
+ // non-JS-implemented interfaces inherit from JS-implemented ones or vice
+ // versa.
+ // [Unforgeable] readonly attribute long unforgeableAttr;
+ // [Unforgeable, ChromeOnly] readonly attribute long unforgeableAttr2;
+ // [Unforgeable] long unforgeableMethod();
+ // [Unforgeable, ChromeOnly] long unforgeableMethod2();
+ // FIXME: Bug 863955 No stringifiers yet
+ // stringifier;
+ void passRenamedInterface(TestRenamedInterface arg);
+ [PutForwards=writableByte] readonly attribute TestJSImplInterface putForwardsAttr;
+ [PutForwards=writableByte, LenientThis] readonly attribute TestJSImplInterface putForwardsAttr2;
+ [PutForwards=writableByte, ChromeOnly] readonly attribute TestJSImplInterface putForwardsAttr3;
+ [Throws] void throwingMethod();
+ [Throws] attribute boolean throwingAttr;
+ [GetterThrows] attribute boolean throwingGetterAttr;
+ [SetterThrows] attribute boolean throwingSetterAttr;
+ // NeedsSubjectPrincipal not supported on JS-implemented things for
+ // now, because we always pass in the caller principal anyway.
+ // [NeedsSubjectPrincipal] void needsSubjectPrincipalMethod();
+ // [NeedsSubjectPrincipal] attribute boolean needsSubjectPrincipalAttr;
+ // legacycaller short(unsigned long arg1, TestInterface arg2);
+ void passArgsWithDefaults(optional long arg1,
+ optional TestInterface? arg2 = null,
+ optional Dict arg3, optional double arg4 = 5.0,
+ optional float arg5);
+ attribute any jsonifierShouldSkipThis;
+ attribute TestParentInterface jsonifierShouldSkipThis2;
+ attribute TestCallbackInterface jsonifierShouldSkipThis3;
+ jsonifier;
+
+ attribute byte dashed-attribute;
+ void dashed-method();
+
+ // If you add things here, add them to TestCodeGen as well
+};
+
+[NavigatorProperty="TestNavigator", JSImplementation="@mozilla.org/test;1"]
+interface TestNavigator {
+};
+
+[Constructor, NavigatorProperty="TestNavigatorWithConstructor", JSImplementation="@mozilla.org/test;1"]
+interface TestNavigatorWithConstructor {
+};
+
+interface TestCImplementedInterface : TestJSImplInterface {
+};
+
+interface TestCImplementedInterface2 {
+};
+
+[NoInterfaceObject,
+ JSImplementation="@mozilla.org/test-js-impl-interface;2"]
+interface TestJSImplNoInterfaceObject {
+ [Cached, Pure]
+ readonly attribute byte cachedByte;
+};
diff --git a/dom/bindings/test/TestJSImplInheritanceGen.webidl b/dom/bindings/test/TestJSImplInheritanceGen.webidl
new file mode 100644
index 000000000..e62dbd10b
--- /dev/null
+++ b/dom/bindings/test/TestJSImplInheritanceGen.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/.
+ */
+
+[Constructor, JSImplementation="@mozilla.org/test-js-impl-interface2;1"]
+interface TestJSImplInterface2 : TestCImplementedInterface {
+};
+
+[Constructor, JSImplementation="@mozilla.org/test-js-impl-interface3;1"]
+interface TestJSImplInterface3 : TestCImplementedInterface2 {
+};
+
+// Important: TestJSImplInterface5 needs to come before TestJSImplInterface6 in
+// this file to test what it's trying to test.
+[Constructor, JSImplementation="@mozilla.org/test-js-impl-interface5;1"]
+interface TestJSImplInterface5 : TestJSImplInterface6 {
+};
+
+// Important: TestJSImplInterface6 needs to come after TestJSImplInterface3 in
+// this file to test what it's trying to test.
+[Constructor, JSImplementation="@mozilla.org/test-js-impl-interface6;1"]
+interface TestJSImplInterface6 : TestJSImplInterface3 {
+};
+
+[Constructor, JSImplementation="@mozilla.org/test-js-impl-interface4;1"]
+interface TestJSImplInterface4 : EventTarget {
+};
diff --git a/dom/bindings/test/TestTypedef.webidl b/dom/bindings/test/TestTypedef.webidl
new file mode 100644
index 000000000..7f758c79e
--- /dev/null
+++ b/dom/bindings/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/dom/bindings/test/chrome.ini b/dom/bindings/test/chrome.ini
new file mode 100644
index 000000000..9fdbd7fd3
--- /dev/null
+++ b/dom/bindings/test/chrome.ini
@@ -0,0 +1,22 @@
+[DEFAULT]
+support-files =
+ !/dom/bindings/test/file_bug775543.html
+ !/dom/bindings/test/file_document_location_set_via_xray.html
+ !/dom/bindings/test/file_dom_xrays.html
+ !/dom/bindings/test/file_proxies_via_xray.html
+
+[test_bug775543.html]
+[test_document_location_set_via_xray.html]
+[test_dom_xrays.html]
+[test_proxies_via_xray.html]
+[test_document_location_via_xray_cached.html]
+[test_blacklisted_prerendering_function.xul]
+support-files =
+ file_focuser.html
+ file_fullScreenPropertyAccessor.html
+skip-if = e10s # prerendering doesn't work in e10s yet
+[test_kill_longrunning_prerendered_content.xul]
+skip-if = e10s # prerendering doesn't work in e10s yet
+[test_bug1123516_maplikesetlikechrome.xul]
+skip-if = debug == false
+[test_bug1287912.html]
diff --git a/dom/bindings/test/file_InstanceOf.html b/dom/bindings/test/file_InstanceOf.html
new file mode 100644
index 000000000..487010fa4
--- /dev/null
+++ b/dom/bindings/test/file_InstanceOf.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+<script type="application/javascript">
+function runTest()
+{
+ return [ parent.HTMLElement.prototype instanceof Element,
+ parent.HTMLElement.prototype instanceof parent.Element ];
+}
+</script>
+</body>
+</html>
diff --git a/dom/bindings/test/file_bug775543.html b/dom/bindings/test/file_bug775543.html
new file mode 100644
index 000000000..ee8c14c4d
--- /dev/null
+++ b/dom/bindings/test/file_bug775543.html
@@ -0,0 +1,5 @@
+<body>
+<script>
+worker = new Worker("a");
+</script>
+</body>
diff --git a/dom/bindings/test/file_document_location_set_via_xray.html b/dom/bindings/test/file_document_location_set_via_xray.html
new file mode 100644
index 000000000..323acba66
--- /dev/null
+++ b/dom/bindings/test/file_document_location_set_via_xray.html
@@ -0,0 +1,5 @@
+<body>
+<script>
+document.x = 5;
+</script>
+</body>
diff --git a/dom/bindings/test/file_dom_xrays.html b/dom/bindings/test/file_dom_xrays.html
new file mode 100644
index 000000000..36b3f8a30
--- /dev/null
+++ b/dom/bindings/test/file_dom_xrays.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+ <script>
+ window.expando = 42;
+ window.shadowedIframe = 42;
+ Object.setPrototypeOf(window, Object.create(Window.prototype,
+ {
+ shadowedIframe: { value: 42 },
+ iframe: { value: 42 },
+ document: { value: 42 },
+ addEventListener: { value: 42 },
+ toString: { value: 42 }
+ }));
+ window.documentElement.expando = 42;
+ Object.defineProperty(window.documentElement, "version", { value: 42 });
+ </script>
+ <iframe name="shadowedIframe" id="shadowedIframe"></iframe>
+ <iframe name="iframe" id="iframe"></iframe>
+ <iframe name="document" id="document"></iframe>
+ <iframe name="self" id="self"></iframe>
+ <iframe name="addEventListener" id="addEventListener"></iframe>
+ <iframe name="toString" id="toString"></iframe>
+ <iframe name="item" id="item"></iframe>
+</html>
diff --git a/dom/bindings/test/file_focuser.html b/dom/bindings/test/file_focuser.html
new file mode 100644
index 000000000..0d5240f95
--- /dev/null
+++ b/dom/bindings/test/file_focuser.html
@@ -0,0 +1,24 @@
+<!DOCTYPE HTML>
+<div id="stage"></div>
+<script>
+ function stage(str) {
+ var s = document.getElementById("stage");
+ s.textContent = str;
+ }
+ stage("before");
+ setTimeout(function() {
+ stage("in timeout");
+ });
+ setInterval(function() {
+ stage("in interval");
+ });
+ addEventListener("keydown", function() {
+ stage("keydown");
+ }, false);
+ try {
+ focus();
+ stage("after");
+ } catch(e) {
+ stage("exception raised");
+ }
+</script>
diff --git a/dom/bindings/test/file_fullScreenPropertyAccessor.html b/dom/bindings/test/file_fullScreenPropertyAccessor.html
new file mode 100644
index 000000000..92a37e0ba
--- /dev/null
+++ b/dom/bindings/test/file_fullScreenPropertyAccessor.html
@@ -0,0 +1,24 @@
+<!DOCTYPE HTML>
+<div id="stage"></div>
+<script>
+ function stage(str) {
+ var s = document.getElementById("stage");
+ s.textContent = str;
+ }
+ stage("before");
+ setTimeout(function() {
+ stage("in timeout");
+ });
+ setInterval(function() {
+ stage("in interval");
+ });
+ addEventListener("keydown", function() {
+ stage("keydown");
+ }, false);
+ try {
+ window.fullScreen;
+ stage("after");
+ } catch(e) {
+ stage("exception raised");
+ }
+</script>
diff --git a/dom/bindings/test/file_proxies_via_xray.html b/dom/bindings/test/file_proxies_via_xray.html
new file mode 100644
index 000000000..2e9a31830
--- /dev/null
+++ b/dom/bindings/test/file_proxies_via_xray.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+ <script>
+ document.x = 5
+ </script>
+ <img id="y" name="y"></div>
+ <img id="z" name="z"></div>
+</html>
diff --git a/dom/bindings/test/forOf_iframe.html b/dom/bindings/test/forOf_iframe.html
new file mode 100644
index 000000000..91417aba0
--- /dev/null
+++ b/dom/bindings/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/dom/bindings/test/mochitest.ini b/dom/bindings/test/mochitest.ini
new file mode 100644
index 000000000..2cd322e74
--- /dev/null
+++ b/dom/bindings/test/mochitest.ini
@@ -0,0 +1,79 @@
+[DEFAULT]
+support-files =
+ file_InstanceOf.html
+ file_bug775543.html
+ file_document_location_set_via_xray.html
+ file_dom_xrays.html
+ file_proxies_via_xray.html
+ forOf_iframe.html
+ !/js/xpconnect/tests/mochitest/file_empty.html
+
+[test_async_stacks.html]
+[test_ByteString.html]
+[test_InstanceOf.html]
+[test_bug560072.html]
+[test_bug742191.html]
+[test_bug759621.html]
+[test_bug773326.html]
+[test_bug788369.html]
+[test_bug852846.html]
+[test_bug862092.html]
+[test_bug1036214.html]
+skip-if = debug == false
+[test_bug963382.html]
+skip-if = debug == false
+[test_bug1041646.html]
+[test_bug1123875.html]
+[test_barewordGetsWindow.html]
+[test_callback_across_document_open.html]
+[test_callback_default_thisval.html]
+[test_cloneAndImportNode.html]
+[test_defineProperty.html]
+[test_enums.html]
+[test_exceptionThrowing.html]
+[test_exception_messages.html]
+[test_forOf.html]
+[test_integers.html]
+[test_interfaceName.html]
+[test_interfaceToString.html]
+[test_exceptions_from_jsimplemented.html]
+tags = webrtc
+[test_lenientThis.html]
+[test_lookupGetter.html]
+[test_namedNoIndexed.html]
+[test_named_getter_enumerability.html]
+[test_Object.prototype_props.html]
+[test_queryInterface.html]
+[test_returnUnion.html]
+skip-if = debug == false
+[test_usvstring.html]
+skip-if = debug == false
+[test_sequence_wrapping.html]
+subsuite = gpu
+[test_setWithNamedGetterNoNamedSetter.html]
+[test_throwing_method_noDCE.html]
+[test_treat_non_object_as_null.html]
+[test_traceProtos.html]
+[test_sequence_detection.html]
+skip-if = debug == false
+[test_exception_options_from_jsimplemented.html]
+skip-if = debug == false
+[test_promise_rejections_from_jsimplemented.html]
+skip-if = debug == false
+[test_worker_UnwrapArg.html]
+[test_unforgeablesonexpando.html]
+[test_crossOriginWindowSymbolAccess.html]
+[test_primitive_this.html]
+[test_callback_exceptions.html]
+[test_bug1123516_maplikesetlike.html]
+skip-if = debug == false
+[test_jsimplemented_eventhandler.html]
+skip-if = debug == false
+[test_iterable.html]
+skip-if = debug == false
+[test_oom_reporting.html]
+[test_domProxyArrayLengthGetter.html]
+[test_exceptionSanitization.html]
+skip-if = os == "android"
+[test_stringBindings.html]
+skip-if = debug == false
diff --git a/dom/bindings/test/moz.build b/dom/bindings/test/moz.build
new file mode 100644
index 000000000..7d0cb6c21
--- /dev/null
+++ b/dom/bindings/test/moz.build
@@ -0,0 +1,58 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DEFINES.update({
+ 'IMPL_LIBXUL': True,
+ 'MOZILLA_INTERNAL_API': True,
+})
+
+# Do NOT export this library. We don't actually want our test code
+# being added to libxul or anything.
+
+Library('dombindings_test_s')
+
+EXTRA_COMPONENTS += [
+ 'TestInterfaceJS.js',
+ 'TestInterfaceJS.manifest',
+ 'TestInterfaceJSMaplike.js'
+]
+
+MOCHITEST_MANIFESTS += ['mochitest.ini']
+
+MOCHITEST_CHROME_MANIFESTS += ['chrome.ini']
+
+TEST_WEBIDL_FILES += [
+ 'TestDictionary.webidl',
+ 'TestJSImplInheritanceGen.webidl',
+ 'TestTypedef.webidl',
+]
+
+PREPROCESSED_TEST_WEBIDL_FILES += [
+ 'TestCodeGen.webidl',
+ 'TestExampleGen.webidl',
+ 'TestJSImplGen.webidl',
+]
+
+WEBIDL_EXAMPLE_INTERFACES += [
+ 'TestExampleInterface',
+ 'TestExampleProxyInterface',
+ 'TestExampleWorkerInterface',
+]
+
+# Bug 932082 tracks having bindings use namespaced includes.
+LOCAL_INCLUDES += [
+ '!/dist/include/mozilla/dom',
+]
+
+LOCAL_INCLUDES += [
+ '!..',
+ '/dom/bindings',
+ '/js/xpconnect/src',
+ '/js/xpconnect/wrappers',
+]
+
+if CONFIG['GNU_CXX']:
+ CXXFLAGS += ['-Wno-error=shadow']
diff --git a/dom/bindings/test/test_ByteString.html b/dom/bindings/test/test_ByteString.html
new file mode 100644
index 000000000..c7e632117
--- /dev/null
+++ b/dom/bindings/test/test_ByteString.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=796850
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for ByteString support</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=796850">Mozilla Bug 796850</a>
+<p id="display"></p>
+<pre id="test">
+<script type="application/javascript">
+
+ /** Test for Bug 796850 **/
+ var xhr = new XMLHttpRequest();
+ caught = false;
+ try {
+ xhr.open("\u5427", "about:mozilla", true);
+ }
+ catch (TypeError) {
+ caught = true;
+ }
+ ok(caught, "Character values > 255 not rejected for ByteString");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/bindings/test/test_InstanceOf.html b/dom/bindings/test/test_InstanceOf.html
new file mode 100644
index 000000000..514ec1b2a
--- /dev/null
+++ b/dom/bindings/test/test_InstanceOf.html
@@ -0,0 +1,54 @@
+<!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 **/
+
+SimpleTest.waitForExplicitFinish();
+
+function runTest()
+{
+ ok(document instanceof EventTarget, "document is an event target")
+ ok(new XMLHttpRequest() instanceof XMLHttpRequest, "instanceof should work on XHR");
+ ok(HTMLElement.prototype instanceof Node, "instanceof needs to walk the prototype chain")
+
+ var otherWin = document.getElementById("testFrame").contentWindow;
+
+ ok(otherWin.HTMLElement.prototype instanceof otherWin.Node, "Same-origin instanceof of a interface prototype object should work, even if called cross-origin");
+ ok(!(otherWin.HTMLElement.prototype instanceof Node), "Cross-origin instanceof of a interface prototype object shouldn't work");
+
+ // We need to reset HTMLElement.prototype.__proto__ to the original value
+ // before using anything from the harness, otherwise the harness code breaks
+ // in weird ways.
+ HTMLElement.prototype.__proto__ = otherWin.Element.prototype;
+ var [ shouldSucceed, shouldFail ] = otherWin.runTest();
+ shouldSucceed = shouldSucceed && HTMLElement.prototype instanceof otherWin.Element;
+ shouldFail = shouldFail && HTMLElement.prototype instanceof Element;
+ HTMLElement.prototype.__proto__ = Element.prototype;
+
+ ok(shouldSucceed, "If an interface prototype object is on the protochain then instanceof with the interface object should succeed");
+ ok(!shouldFail, "If an interface prototype object is not on the protochain then instanceof with the interface object should succeed");
+
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+<iframe id="testFrame" src="file_InstanceOf.html" onload="runTest()"></iframe>
+</body>
+</html>
diff --git a/dom/bindings/test/test_Object.prototype_props.html b/dom/bindings/test/test_Object.prototype_props.html
new file mode 100644
index 000000000..03147eb03
--- /dev/null
+++ b/dom/bindings/test/test_Object.prototype_props.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Test for bug 987110</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+test(function() {
+ var props = Object.getOwnPropertyNames(Object.prototype);
+ // If you change this list, make sure it continues to match the list in
+ // Codegen.py's CGDictionary.getMemberDefinition method.
+ var expected = [
+ "constructor", "toSource", "toString", "toLocaleString", "valueOf",
+ "watch", "unwatch", "hasOwnProperty", "isPrototypeOf",
+ "propertyIsEnumerable", "__defineGetter__", "__defineSetter__",
+ "__lookupGetter__", "__lookupSetter__", "__proto__"
+ ];
+ assert_array_equals(props.sort(), expected.sort());
+}, "Own properties of Object.prototype");
+</script>
diff --git a/dom/bindings/test/test_async_stacks.html b/dom/bindings/test/test_async_stacks.html
new file mode 100644
index 000000000..8b655a14d
--- /dev/null
+++ b/dom/bindings/test/test_async_stacks.html
@@ -0,0 +1,108 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1148593
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1148593</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 1148593 **/
+
+ SimpleTest.waitForExplicitFinish();
+
+ var TESTS;
+
+ function nextTest() {
+ var t = TESTS.pop();
+ if (t) {
+ t();
+ } else {
+ SimpleTest.finish();
+ }
+ }
+
+ function checkStack(functionName) {
+ try {
+ noSuchFunction();
+ } catch (e) {
+ ok(e.stack.indexOf(functionName) >= 0, "stack includes " + functionName);
+ }
+ nextTest();
+ }
+
+ function eventListener() {
+ checkStack("registerEventListener");
+ }
+ function registerEventListener(link) {
+ link.onload = eventListener;
+ }
+ function eventTest() {
+ var link = document.createElement("link");
+ link.rel = "stylesheet";
+ link.href = "data:text/css,";
+ registerEventListener(link);
+ document.body.appendChild(link);
+ }
+
+ function xhrListener() {
+ checkStack("xhrTest");
+ }
+ function xhrTest() {
+ var ourFile = location.href;
+ var x = new XMLHttpRequest();
+ x.onload = xhrListener;
+ x.open("get", ourFile, true);
+ x.send();
+ }
+
+ function rafListener() {
+ checkStack("rafTest");
+ }
+ function rafTest() {
+ requestAnimationFrame(rafListener);
+ }
+
+ var intervalId;
+ function intervalHandler() {
+ clearInterval(intervalId);
+ checkStack("intervalTest");
+ }
+ function intervalTest() {
+ intervalId = setInterval(intervalHandler, 5);
+ }
+
+ function postMessageHandler(ev) {
+ ev.stopPropagation();
+ checkStack("postMessageTest");
+ }
+ function postMessageTest() {
+ window.addEventListener("message", postMessageHandler, true);
+ window.postMessage("whatever", "*");
+ }
+
+ function runTests() {
+ TESTS = [postMessageTest, intervalTest, rafTest, xhrTest, eventTest];
+ nextTest();
+ }
+
+ addLoadEvent(function() {
+ SpecialPowers.pushPrefEnv(
+ {"set": [['javascript.options.asyncstack', true]]},
+ runTests);
+ });
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1148593">Mozilla Bug 1148593</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/bindings/test/test_barewordGetsWindow.html b/dom/bindings/test/test_barewordGetsWindow.html
new file mode 100644
index 000000000..e098eea53
--- /dev/null
+++ b/dom/bindings/test/test_barewordGetsWindow.html
@@ -0,0 +1,60 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=936056
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 936056</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 936056 **/
+ SimpleTest.waitForExplicitFinish();
+ window.onload = function() {
+ var desc = Object.getOwnPropertyDescriptor(frames[0], "document");
+ if (!desc || !desc.get) {
+ todo(false, "This test does nothing so far, but will once Window is on WebIDL bindings");
+ SimpleTest.finish();
+ return;
+ }
+ get = desc.get;
+ ok(get, "Couldn't find document getter");
+ Object.defineProperty(frames[0], "foo", { get: get, configurable: true });
+
+ var barewordFunc = frames[0].eval("(function (count) { var doc; for (var i = 0; i < count; ++i) doc = foo; return doc.documentElement; })");
+ var qualifiedFunc = frames[0].eval("(function (count) { var doc; for (var i = 0; i < count; ++i) doc = window.document; return doc.documentElement; })");
+ document.querySelector("iframe").onload = function () {
+ // interp
+ is(barewordFunc(1).textContent, "OLD", "Bareword should see own inner 1");
+ is(qualifiedFunc(1).textContent, "NEW",
+ "Qualified should see current inner 1");
+ // baseline
+ is(barewordFunc(100).textContent, "OLD", "Bareword should see own inner 2");
+ is(qualifiedFunc(100).textContent, "NEW",
+ "Qualified should see current inner 2");
+ // ion
+ is(barewordFunc(10000).textContent, "OLD", "Bareword should see own inner 3");
+ is(qualifiedFunc(10000).textContent, "NEW",
+ "Qualified should see current inner 2");
+ SimpleTest.finish();
+ }
+ frames[0].location = "data:text/plain,NEW";
+ }
+
+
+
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=936056">Mozilla Bug 936056</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<iframe src="data:text/plain,OLD"></iframe>
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/bindings/test/test_blacklisted_prerendering_function.xul b/dom/bindings/test/test_blacklisted_prerendering_function.xul
new file mode 100644
index 000000000..02a76d88d
--- /dev/null
+++ b/dom/bindings/test/test_blacklisted_prerendering_function.xul
@@ -0,0 +1,124 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ onload="runTest();">
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+ SimpleTest.waitForExplicitFinish();
+
+ function Listener(aBrowser, aPrerendered, aCallback) {
+ this.init(aBrowser, aPrerendered, aCallback);
+ }
+
+ Listener.prototype = {
+ init: function(aBrowser, aPrerendered, aCallback) {
+ this.mBrowser = aBrowser;
+ this.mPrerendered = aPrerendered;
+ this.mCallback = aCallback;
+ },
+ QueryInterface: function(aIID) {
+ if (aIID.equals(Components.interfaces.nsIWebProgressListener) ||
+ aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
+ aIID.equals(Components.interfaces.nsISupports))
+ return this;
+ throw Components.results.NS_NOINTERFACE;
+ },
+ onStateChange : function(aWebProgress, aRequest, aStateFlags, aStatus) {
+ if ((aStateFlags & Components.interfaces.nsIWebProgressListener.STATE_STOP) &&
+ (aStateFlags & Components.interfaces.nsIWebProgressListener.STATE_IS_DOCUMENT)) {
+ var doc = this.mBrowser.contentDocument;
+ var stage = doc.getElementById("stage");
+ if (this.mPrerendered) {
+ is(stage.textContent, "before", "The blacklisted call should properly be intercepted in prerendering mode");
+ } else {
+ // In normal mode, we may or may not have run the timeout and/or the interval.
+ switch (stage.textContent) {
+ case "after":
+ case "in timeout":
+ case "in interval":
+ ok(true, "The blacklisted call should work fine in normal mode");
+ break;
+ default:
+ ok(false, "The blacklisted call should work fine in normal mode");
+ break;
+ }
+ }
+ progress.removeProgressListener(progressListener);
+
+ // Set three timeouts to see if the interval triggered
+ var self = this;
+ function checkInterval() {
+ var expected = self.mPrerendered ? "before" : "in interval";
+ var desc = self.mPrerendered ? "No timer should be running" : "Timers should run as normal";
+ is(stage.textContent, expected, desc);
+ // Now, dispatch a key event to the window and see if the keydown handler runs
+ synthesizeKey("a", {}, self.mBrowser.contentWindow);
+ expected = self.mPrerendered ? "before" : "keydown";
+ desc = self.mPrerendered ? "No event handler should be running" : "Event handlers should run as normal";
+ is(stage.textContent, expected, desc);
+ self.mCallback();
+ }
+ setTimeout(function() {
+ setTimeout(function() {
+ setTimeout(function() {
+ checkInterval();
+ }, 0);
+ }, 0);
+ }, 0);
+ }
+ },
+ onProgressChange : function(aWebProgress, aRequest,
+ aCurSelfProgress, aMaxSelfProgress,
+ aCurTotalProgress, aMaxTotalProgress) {},
+ onLocationChange : function(aWebProgress, aRequest, aLocation, aFlags) {},
+ onStatusChange : function(aWebProgress, aRequest, aStatus, aMessage) {},
+ onSecurityChange : function(aWebProgress, aRequest, aState) {},
+ mBrowser: null,
+ mPrerendered: false,
+ mCallback: null
+ };
+
+ var progress, progressListener;
+
+ function runTest() {
+ testStep(false, "file_focuser.html", function() {
+ testStep(true, "file_focuser.html", function() {
+ testStep(false, "file_fullScreenPropertyAccessor.html", function() {
+ testStep(true, "file_fullScreenPropertyAccessor.html", function() {
+ SimpleTest.finish();
+ });
+ });
+ });
+ });
+ }
+
+ function testStep(aPrerendered, aFileName, aCallback) {
+ var browser = document.getElementById(aPrerendered ? "prerendered" : "normal");;
+ progressListener = new Listener(browser, aPrerendered, aCallback);
+ var docShell = browser.docShell;
+ progress = docShell.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+ .getInterface(Components.interfaces.nsIWebProgress);
+ progress.addProgressListener(progressListener,
+ Components.interfaces.nsIWebProgress.NOTIFY_ALL);
+ browser.loadURI("chrome://mochitests/content/chrome/dom/bindings/test/" + aFileName);
+ }
+
+]]>
+</script>
+
+<body id="html_body" xmlns="http://www.w3.org/1999/xhtml">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1069719">Mozilla Bug 1069719</a>
+<p id="display"></p>
+
+<pre id="test">
+</pre>
+</body>
+<browser prerendered="true" id="prerendered"/>
+<browser id="normal"/>
+</window>
diff --git a/dom/bindings/test/test_bug1036214.html b/dom/bindings/test/test_bug1036214.html
new file mode 100644
index 000000000..dd98eb482
--- /dev/null
+++ b/dom/bindings/test/test_bug1036214.html
@@ -0,0 +1,123 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1036214
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1036214</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for subsumes-checking |any| and |object| for js-implemented WebIDL. **/
+ SimpleTest.waitForExplicitFinish();
+ var xoObjects = [];
+ function setup() {
+ xoObjects.push(window[0]);
+ xoObjects.push(window[0].location);
+ xoObjects.push(SpecialPowers.unwrap(SpecialPowers.wrap(window[0]).document));
+ xoObjects.push(SpecialPowers);
+ xoObjects.push(SpecialPowers.wrap);
+ SpecialPowers.pushPrefEnv({set: [['dom.expose_test_interfaces', true]]}, go);
+ }
+
+ function checkThrows(f, msg) {
+ try {
+ f();
+ ok(false, "Should have thrown: " + msg);
+ } catch (e) {
+ ok(true, "Threw correctly: " + msg);
+ ok(/denied|insecure/.test(e), "Threw security exception: " + e);
+ }
+ }
+
+ function go() {
+
+ //
+ // Test the basics of the test interface.
+ //
+
+ var any = { a: 11 };
+ var obj = { b: 22, c: "str" };
+ var obj2 = { foo: "baz" };
+ var myDict = { anyMember: 42, objectMember: { answer: 42 }, objectOrStringMember: { answer: "anobject" },
+ anySequenceMember: [{}, 1, "thirdinsequence"],
+ innerDictionary: { innerObject: { answer: "rabbithole" } } };
+ var t = new TestInterfaceJS(any, obj, myDict);
+ is(Object.getPrototypeOf(t), TestInterfaceJS.prototype, "Prototype setup works correctly");
+ is(t.anyArg, any, "anyArg is correct");
+ is(t.objectArg, obj, "objectArg is correct");
+ is(t.dictionaryArg.anyMember, 42, "dictionaryArg looks correct");
+ is(t.dictionaryArg.objectMember.answer, 42, "dictionaryArg looks correct");
+ t.anyAttr = 2;
+ is(t.anyAttr, 2, "ping-pong any attribute works");
+ t.objAttr = obj2;
+ is(t.objAttr, obj2, "ping-pong object attribute works");
+ t.dictionaryAttr = myDict;
+ is(t.dictionaryAttr.anyMember, 42, "ping-pong dictionary attribute works");
+ is(t.dictionaryAttr.objectMember.answer, 42, "ping-pong dictionary attribute works");
+
+ is(any, t.pingPongAny(any), "ping-pong works with any");
+ is(obj, t.pingPongObject(obj), "ping-pong works with obj");
+ is(obj, t.pingPongObjectOrString(obj), "ping-pong works with obj or string");
+ is("foo", t.pingPongObjectOrString("foo"), "ping-pong works with obj or string");
+ is(t.pingPongDictionary(myDict).anyMember, 42, "ping pong works with dict");
+ is(t.pingPongDictionary(myDict).objectMember.answer, 42, "ping pong works with dict");
+ is(t.pingPongDictionary(myDict).objectOrStringMember.answer, "anobject", "ping pong works with dict");
+ is(t.pingPongDictionary(myDict).anySequenceMember[2], "thirdinsequence", "ping pong works with dict");
+ is(t.pingPongDictionary(myDict).innerDictionary.innerObject.answer, "rabbithole", "ping pong works with layered dicts");
+ is(t.pingPongDictionaryOrLong({anyMember: 42}), 42, "ping pong (dict or long) works with dict");
+ is(t.pingPongDictionaryOrLong(42), 42, "ping pong (dict or long) works with long");
+ ok(/canary/.test(t.pingPongMap({ someVal: 42, someOtherVal: "canary" })), "ping pong works with mozmap");
+ is(t.objectSequenceLength([{}, {}, {}]), 3, "ping pong works with object sequence");
+ is(t.anySequenceLength([42, 'string', {}, undefined]), 4, "ping pong works with any sequence");
+
+ //
+ // Test that we throw in the cross-origin cases.
+ //
+
+ xoObjects.forEach(function(xoObj) {
+ var blank = new TestInterfaceJS();
+ checkThrows(() => new TestInterfaceJS(xoObj, undefined), "any param for constructor");
+ checkThrows(() => new TestInterfaceJS(undefined, xoObj), "obj param for constructor");
+ checkThrows(() => new TestInterfaceJS(undefined, undefined, { anyMember: xoObj }), "any dict param for constructor");
+ checkThrows(() => new TestInterfaceJS(undefined, undefined, { objectMember: xoObj }), "object dict param for constructor");
+ checkThrows(() => new TestInterfaceJS(undefined, undefined, { objectOrStringMember: xoObj }), "union dict param for constructor");
+ checkThrows(() => new TestInterfaceJS(undefined, undefined, { anySequenceMember: [0, xoObj, 'hi' ] }), "sequence dict param for constructor");
+ checkThrows(() => new TestInterfaceJS(undefined, undefined, { innerDictionary: { innerObject: xoObj } }), "inner dict param for constructor");
+ checkThrows(() => t.anyAttr = xoObj, "anyAttr");
+ checkThrows(() => t.objectAttr = xoObj, "objAttr");
+ checkThrows(() => t.dictionaryAttr = { anyMember: xoObj }, "dictionaryAttr any");
+ checkThrows(() => t.dictionaryAttr = { objectMember: xoObj }, "dictionaryAttr object");
+ checkThrows(() => t.pingPongAny(xoObj), "pingpong any");
+ checkThrows(() => t.pingPongObject(xoObj), "pingpong obj");
+ checkThrows(() => t.pingPongObjectOrString(xoObj), "pingpong union");
+ checkThrows(() => t.pingPongDictionary({ anyMember: xoObj }), "dictionary pingpong any");
+ checkThrows(() => t.pingPongDictionary({ objectMember: xoObj }), "dictionary pingpong object");
+ checkThrows(() => t.pingPongDictionary({ anyMember: xoObj, objectMember: xoObj }), "dictionary pingpong both");
+ checkThrows(() => t.pingPongDictionary({ objectOrStringMember: xoObj }), "dictionary pingpong objectorstring");
+ checkThrows(() => t.pingPongDictionaryOrLong({ objectMember: xoObj }), "unionable dictionary");
+ checkThrows(() => t.pingPongDictionaryOrLong({ anyMember: xoObj }), "unionable dictionary");
+ checkThrows(() => t.pingPongMap({ someMember: 42, someOtherMember: {}, crossOriginMember: xoObj }), "mozmap");
+ checkThrows(() => t.objectSequenceLength([{}, {}, xoObj, {}]), "object sequence");
+ checkThrows(() => t.anySequenceLength([42, 'someString', xoObj, {}]), "any sequence");
+ });
+
+
+ SimpleTest.finish();
+ }
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1036214">Mozilla Bug 1036214</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+<iframe id="ifr" onload="setup();" src="http://example.org/tests/js/xpconnect/tests/mochitest/file_empty.html"></iframe>
+</body>
+</html>
diff --git a/dom/bindings/test/test_bug1041646.html b/dom/bindings/test/test_bug1041646.html
new file mode 100644
index 000000000..22baed454
--- /dev/null
+++ b/dom/bindings/test/test_bug1041646.html
@@ -0,0 +1,49 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1041646
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1041646</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 1041646 **/
+ // We need to reject the promise with a DOMException, so make sure we have
+ // something that produces one.
+ function throwException() {
+ document.createTextNode("").appendChild(document);
+ }
+ try {
+ throwException();
+ } catch (e) {
+ ok(e instanceof DOMException, "This test won't test what it should be testing");
+ }
+
+ SimpleTest.waitForExplicitFinish();
+
+ // We want a new DOMException each time here.
+ for (var i = 0; i < 100; ++i) {
+ new Promise(throwException);
+ }
+
+ // Now make sure we wait for all those promises above to reject themselves
+ Promise.resolve(1).then(function() {
+ SpecialPowers.gc(); // This should not assert or crash
+ SimpleTest.finish();
+ });
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1041646">Mozilla Bug 1041646</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/bindings/test/test_bug1123516_maplikesetlike.html b/dom/bindings/test/test_bug1123516_maplikesetlike.html
new file mode 100644
index 000000000..18ede38ac
--- /dev/null
+++ b/dom/bindings/test/test_bug1123516_maplikesetlike.html
@@ -0,0 +1,271 @@
+<!-- Any copyright is dedicated to the Public Domain.
+- http://creativecommons.org/publicdomain/zero/1.0/ -->
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Test Maplike Interface</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ </head>
+ <body>
+ <script class="testbody" type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({set: [['dom.expose_test_interfaces', true]]}, function() {
+
+ base_properties = [["has", "function", 1],
+ ["entries", "function", 0],
+ ["keys", "function", 0],
+ ["values", "function", 0],
+ ["forEach", "function", 1],
+ ["size", "number"]];
+ maplike_properties = base_properties.concat([["set", "function", 2]]);
+ setlike_properties = base_properties;
+ rw_properties = [["clear", "function", 0],
+ ["delete", "function", 1]];
+ setlike_rw_properties = base_properties.concat(rw_properties).concat([["add", "function", 1]]);
+ maplike_rw_properties = maplike_properties.concat(rw_properties).concat([["get", "function",1]]);
+ var testExistence = function testExistence(prefix, obj, properties) {
+ for (var [name, type, args] of properties) {
+ // Properties are somewhere up the proto chain, hasOwnProperty won't work
+ isnot(obj[name], undefined,
+ `${prefix} object has property ${name}`);
+
+ is(typeof obj[name], type,
+ `${prefix} object property ${name} is a ${type}`);
+ // Check function length
+ if (type == "function") {
+ is(obj[name].length, args,
+ `${prefix} object property ${name} is length ${args}`);
+ is(obj[name].name, name,
+ `${prefix} object method name is ${name}`);
+ }
+
+ // Find where property is on proto chain, check for enumerablility there.
+ var owner = obj;
+ while (owner) {
+ var propDesc = Object.getOwnPropertyDescriptor(owner, name);
+ if (propDesc) {
+ ok(!propDesc.enumerable,
+ `${prefix} object property ${name} is not enumerable`);
+ break;
+ }
+ owner = Object.getPrototypeOf(owner);
+ }
+ }
+ }
+
+ var m;
+ var testSet;
+ var testIndex;
+ // Simple map creation and functionality test
+ info("SimpleMap: Testing simple map creation and functionality");
+ m = new TestInterfaceMaplike();
+ ok(m, "SimpleMap: got a TestInterfaceMaplike object");
+ testExistence("SimpleMap: ", m, maplike_rw_properties);
+ is(m.size, 0, "SimpleMap: size should be zero");
+ ok(!m.has("test"), "SimpleMap: maplike has should return false");
+ is(m.get("test"), undefined, "SimpleMap: maplike get should return undefined on bogus lookup");
+ m1 = m.set("test", 1);
+ is(m, m1, "SimpleMap: return from set should be map object");
+ is(m.size, 1, "SimpleMap: size should be 1");
+ ok(m.has("test"), "SimpleMap: maplike has should return true");
+ is(m.get("test"), 1, "SimpleMap: maplike get should return value entered");
+ m2 = m.set("test2", 2);
+ is(m.size, 2, "SimpleMap: size should be 2");
+ testSet = [["test", 1], ["test2", 2]];
+ testIndex = 0;
+ m.forEach(function(v, k, o) {
+ "use strict";
+ is(o, m, "SimpleMap: foreach obj is correct");
+ is(k, testSet[testIndex][0], "SimpleMap: foreach map key: " + k + " = " + testSet[testIndex][0]);
+ is(v, testSet[testIndex][1], "SimpleMap: foreach map value: " + v + " = " + testSet[testIndex][1]);
+ testIndex += 1;
+ });
+ is(testIndex, 2, "SimpleMap: foreach ran correct number of times");
+ ok(m.has("test2"), "SimpleMap: maplike has should return true");
+ is(m.get("test2"), 2, "SimpleMap: maplike get should return value entered");
+ is(m.delete("test2"), true, "SimpleMap: maplike deletion should return boolean");
+ is(m.size, 1, "SimpleMap: size should be 1");
+ iterable = false;
+ for (var e of m) {
+ iterable = true;
+ is(e[0], "test", "SimpleMap: iterable first array element should be key");
+ is(e[1], 1, "SimpleMap: iterable second array element should be value");
+ }
+ is(m[Symbol.iterator].length, 0, "SimpleMap: @@iterator symbol is correct length");
+ is(m[Symbol.iterator].name, "entries", "SimpleMap: @@iterator symbol has correct name");
+ is(m[Symbol.iterator], m.entries, 'SimpleMap: @@iterator is an alias for "entries"');
+ ok(iterable, "SimpleMap: @@iterator symbol resolved correctly");
+ for (var k of m.keys()) {
+ is(k, "test", "SimpleMap: first keys element should be 'test'");
+ }
+ for (var v of m.values()) {
+ is(v, 1, "SimpleMap: first values elements should be 1");
+ }
+ for (var e of m.entries()) {
+ is(e[0], "test", "SimpleMap: entries first array element should be 'test'");
+ is(e[1], 1, "SimpleMap: entries second array element should be 1");
+ }
+ m.clear();
+ is(m.size, 0, "SimpleMap: size should be 0 after clear");
+
+ // Simple set creation and functionality test
+ info("SimpleSet: Testing simple set creation and functionality");
+ m = new TestInterfaceSetlike();
+ ok(m, "SimpleSet: got a TestInterfaceSetlike object");
+ testExistence("SimpleSet: ", m, setlike_rw_properties);
+ is(m.size, 0, "SimpleSet: size should be zero");
+ ok(!m.has("test"), "SimpleSet: maplike has should return false");
+ m1 = m.add("test");
+ is(m, m1, "SimpleSet: return from set should be map object");
+ is(m.size, 1, "SimpleSet: size should be 1");
+ ok(m.has("test"), "SimpleSet: maplike has should return true");
+ m2 = m.add("test2");
+ is(m.size, 2, "SimpleSet: size should be 2");
+ testSet = ["test", "test2"];
+ testIndex = 0;
+ m.forEach(function(v, k, o) {
+ "use strict";
+ is(o, m, "SimpleSet: foreach obj is correct");
+ is(k, testSet[testIndex], "SimpleSet: foreach set key: " + k + " = " + testSet[testIndex]);
+ testIndex += 1;
+ });
+ is(testIndex, 2, "SimpleSet: foreach ran correct number of times");
+ ok(m.has("test2"), "SimpleSet: maplike has should return true");
+ is(m.delete("test2"), true, "SimpleSet: maplike deletion should return true");
+ is(m.size, 1, "SimpleSet: size should be 1");
+ iterable = false;
+ for (var e of m) {
+ iterable = true;
+ is(e, "test", "SimpleSet: iterable first array element should be key");
+ }
+ is(m[Symbol.iterator].length, 0, "SimpleSet: @@iterator symbol is correct length");
+ is(m[Symbol.iterator].name, "values", "SimpleSet: @@iterator symbol has correct name");
+ is(m[Symbol.iterator], m.values, 'SimpleSet: @@iterator is an alias for "values"');
+ ok(iterable, "SimpleSet: @@iterator symbol resolved correctly");
+ for (var k of m.keys()) {
+ is(k, "test", "SimpleSet: first keys element should be 'test'");
+ }
+ for (var v of m.values()) {
+ is(v, "test", "SimpleSet: first values elements should be 'test'");
+ }
+ for (var e of m.entries()) {
+ is(e[0], "test", "SimpleSet: Entries first array element should be 'test'");
+ is(e[1], "test", "SimpleSet: Entries second array element should be 'test'");
+ }
+ m.clear();
+ is(m.size, 0, "SimpleSet: size should be 0 after clear");
+
+ // Map convenience function test
+ info("Testing map convenience functions");
+ m = new TestInterfaceMaplike();
+ ok(m, "MapConvenience: got a TestInterfaceMaplike object");
+ is(m.size, 0, "MapConvenience: size should be zero");
+ ok(!m.hasInternal("test"), "MapConvenience: maplike hasInternal should return false");
+ m.setInternal("test", 1);
+ is(m.size, 1, "MapConvenience: size should be 1");
+ ok(m.hasInternal("test"), "MapConvenience: maplike hasInternal should return true");
+ is(m.get("test"), 1, "MapConvenience: maplike get should return value entered");
+ m2 = m.setInternal("test2", 2);
+ is(m.size, 2, "size should be 2");
+ ok(m.hasInternal("test2"), "MapConvenience: maplike hasInternal should return true");
+ is(m.get("test2"), 2, "MapConvenience: maplike get should return value entered");
+ is(m.deleteInternal("test2"), true, "MapConvenience: maplike deleteInternal should return true");
+ is(m.size, 1, "MapConvenience: size should be 1");
+ m.clearInternal();
+ is(m.size, 0, "MapConvenience: size should be 0 after clearInternal");
+
+ // Map convenience function test using objects and readonly
+
+ info("Testing Map convenience function test using objects and readonly");
+ m = new TestInterfaceMaplikeObject();
+ ok(m, "ReadOnlyMapConvenience: got a TestInterfaceMaplikeObject object");
+ is(m.size, 0, "ReadOnlyMapConvenience: size should be zero");
+ is(m["set"], undefined, "ReadOnlyMapConvenience: readonly map, should be no set function");
+ is(m["clear"], undefined, "ReadOnlyMapConvenience: readonly map, should be no clear function");
+ is(m["delete"], undefined, "ReadOnlyMapConvenience: readonly map, should be no delete function");
+ ok(!m.hasInternal("test"), "ReadOnlyMapConvenience: maplike hasInternal should return false");
+ m.setInternal("test");
+ is(m.size, 1, "size should be 1");
+ ok(m.hasInternal("test"), "ReadOnlyMapConvenience: maplike hasInternal should return true");
+ m2 = m.setInternal("test2");
+ is(m.size, 2, "size should be 2");
+ ok(m.hasInternal("test2"), "ReadOnlyMapConvenience: maplike hasInternal should return true");
+ is(m.deleteInternal("test2"), true, "ReadOnlyMapConvenience: maplike deleteInternal should return true");
+ is(m.size, 1, "ReadOnlyMapConvenience: size should be 1");
+ m.clearInternal();
+ is(m.size, 0, "ReadOnlyMapConvenience: size should be 0 after clearInternal");
+
+ // JS implemented map creation convenience function test
+
+ info("JSMapConvenience: Testing JS implemented map creation convenience functions");
+ m = new TestInterfaceJSMaplike();
+ ok(m, "JSMapConvenience: got a TestInterfaceJSMaplike object");
+ is(m.size, 0, "JSMapConvenience: size should be zero");
+ ok(!m.has("test"), "JSMapConvenience: maplike has should return false");
+ m.setInternal("test", 1);
+ is(m.size, 1, "JSMapConvenience: size should be 1");
+ ok(m.has("test"), "JSMapConvenience: maplike has should return true");
+ is(m.get("test"), 1, "JSMapConvenience: maplike get should return value entered");
+ m2 = m.setInternal("test2", 2);
+ is(m.size, 2, "JSMapConvenience: size should be 2");
+ ok(m.has("test2"), "JSMapConvenience: maplike has should return true");
+ is(m.get("test2"), 2, "JSMapConvenience: maplike get should return value entered");
+ is(m.deleteInternal("test2"), true, "JSMapConvenience: maplike deleteInternal should return true");
+ is(m.size, 1, "JSMapConvenience: size should be 1");
+ for (var k of m.keys()) {
+ is(k, "test", "JSMapConvenience: first keys element should be 'test'");
+ }
+ for (var v of m.values()) {
+ is(v, 1, "JSMapConvenience: first values elements should be 1");
+ }
+ for (var e of m.entries()) {
+ is(e[0], "test", "JSMapConvenience: entries first array element should be 'test'");
+ is(e[1], 1, "JSMapConvenience: entries second array element should be 1");
+ }
+ m.clearInternal();
+ is(m.size, 0, "JSMapConvenience: size should be 0 after clearInternal");
+
+ // Test this override for forEach
+ info("ForEachThisOverride: Testing this override for forEach");
+ m = new TestInterfaceMaplike();
+ m.set("test", 1);
+ m.forEach(function(v, k, o) {
+ "use strict";
+ is(o, m, "ForEachThisOverride: foreach obj is correct");
+ is(this, 5, "ForEachThisOverride: 'this' value should be correct");
+ }, 5);
+
+ // Test defaulting arguments on maplike to undefined
+ info("MapArgsDefault: Testing maplike defaulting arguments to undefined");
+ m = new TestInterfaceMaplike();
+ m.set();
+ is(m.size, 1, "MapArgsDefault: should have 1 entry");
+ m.forEach(function(v, k) {
+ "use strict";
+ is(typeof k, "string", "MapArgsDefault: key is a string");
+ is(k, "undefined", "MapArgsDefault: key is the string undefined");
+ is(v, 0, "MapArgsDefault: value is 0");
+ });
+ is(m.get(), 0, "MapArgsDefault: no argument to get() returns correct value");
+ m.delete();
+ is(m.size, 0, "MapArgsDefault: should have 0 entries");
+
+ // Test defaulting arguments on setlike to undefined
+ info("SetArgsDefault: Testing setlike defaulting arguments to undefined");
+ m = new TestInterfaceSetlike();
+ m.add();
+ is(m.size, 1, "SetArgsDefault: should have 1 entry");
+ m.forEach(function(v, k) {
+ "use strict";
+ is(typeof k, "string", "SetArgsDefault: key is a string");
+ is(k, "undefined", "SetArgsDefault: key is the string undefined");
+ });
+ m.delete();
+ is(m.size, 0, "SetArgsDefault: should have 0 entries");
+
+ SimpleTest.finish();
+ });
+ </script>
+ </body>
+</html>
diff --git a/dom/bindings/test/test_bug1123516_maplikesetlikechrome.xul b/dom/bindings/test/test_bug1123516_maplikesetlikechrome.xul
new file mode 100644
index 000000000..4bc45cddd
--- /dev/null
+++ b/dom/bindings/test/test_bug1123516_maplikesetlikechrome.xul
@@ -0,0 +1,68 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1123516
+-->
+<window title="Mozilla Bug 1123516"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <iframe id="t"></iframe>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1123516"
+ target="_blank">Mozilla Bug 1123516</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+
+ /** Test for Bug 1123516 **/
+ const Cu = Components.utils;
+ function doTest() {
+ var win = $("t").contentWindow;
+ var sandbox = Components.utils.Sandbox(win, { sandboxPrototype: win });
+ is(sandbox._content, undefined, "_content does nothing over Xray");
+ // Test cross-compartment usage of maplike/setlike WebIDL structures.
+ SpecialPowers.pushPrefEnv({set: [['dom.expose_test_interfaces', true]]}, function() {
+ try {
+ var maplike = Components.utils.evalInSandbox("var m = new TestInterfaceMaplike(); m;", sandbox);
+ maplike.set("test2", 2);
+ is(maplike.get("test2"), 2, "Should be able to create and use maplike/setlike across compartments");
+ var test = Components.utils.evalInSandbox("m.get('test2');", sandbox);
+ is(test, 2, "Maplike/setlike should still work in original compartment");
+ is(maplike.size, 1, "Testing size retrieval across compartments");
+ } catch(e) {
+ ok(false, "Shouldn't throw when working with cross-compartment maplike/setlike interfaces " + e)
+ };
+ try {
+ var setlike = Components.utils.evalInSandbox("var m = new TestInterfaceSetlikeNode(); m.add(document.documentElement); m;", sandbox);
+ is(TestInterfaceSetlikeNode.prototype.has.call(setlike, win.document.documentElement), true,
+ "Cross-compartment unwrapping/comparison has works");
+ // TODO: Should throw until iterators are handled by Xrays, Bug 1023984
+ try {
+ var e = TestInterfaceSetlikeNode.prototype.keys.call(setlike);
+ ok(false, "Calling iterators via xrays should fail");
+ } catch(e) {
+ ok(true, "Calling iterators via xrays should fail");
+ }
+
+ setlike.forEach((v,k,t) => { is(v, win.document.documentElement, "Cross-compartment forEach works"); });
+ TestInterfaceSetlikeNode.prototype.forEach.call(setlike,
+ (v,k,t) => { is(v, win.document.documentElement, "Cross-compartment forEach works"); });
+ is(TestInterfaceSetlikeNode.prototype.delete.call(setlike, win.document.documentElement), true,
+ "Cross-compartment unwrapping/comparison delete works");
+ } catch(e) {
+ ok(false, "Shouldn't throw when working with cross-compartment maplike/setlike interfaces " + e)
+ };
+ SimpleTest.finish();
+ });
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addLoadEvent(doTest);
+ ]]>
+ </script>
+</window>
diff --git a/dom/bindings/test/test_bug1123875.html b/dom/bindings/test/test_bug1123875.html
new file mode 100644
index 000000000..5658091c4
--- /dev/null
+++ b/dom/bindings/test/test_bug1123875.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Test for Bug 1123875</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+ test(() => {
+ assert_throws(new TypeError, () => {
+ "use strict";
+ document.childNodes.length = 0;
+ });
+ }, "setting a readonly attribute on a proxy in strict mode should throw a TypeError");
+</script>
diff --git a/dom/bindings/test/test_bug1287912.html b/dom/bindings/test/test_bug1287912.html
new file mode 100644
index 000000000..ae72b2316
--- /dev/null
+++ b/dom/bindings/test/test_bug1287912.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1287912
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1287912</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=1287912">Mozilla Bug 1287912</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<iframe id="t" src="http://example.org/tests/dom/bindings/test/"></iframe>
+</div>
+<pre id="test">
+<script type="application/javascript">
+function test()
+{
+ var win = document.getElementById("t").contentWindow;
+ is(Object.getPrototypeOf(win.Image), win.Function.prototype, "The __proto__ of a named constructor is Function.prototype");
+ is(win.Image.prototype, win.HTMLImageElement.prototype, "The prototype property of a named constructor is the interface prototype object");
+ is(win.HTMLImageElement['foo'], undefined, "Should not have a property named foo on the HTMLImageElement interface object");
+ is(win.Image['foo'], undefined, "Should not have a property named foo on the Image named constructor");
+
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(test);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/bindings/test/test_bug560072.html b/dom/bindings/test/test_bug560072.html
new file mode 100644
index 000000000..82bb1c2c6
--- /dev/null
+++ b/dom/bindings/test/test_bug560072.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=560072
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 560072</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=560072">Mozilla Bug 560072</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 560072 **/
+is(document.body,
+ Object.getOwnPropertyDescriptor(HTMLDocument.prototype, "body").get.call(document),
+ "Should get body out of property descriptor");
+
+is(document.body,
+ Object.getOwnPropertyDescriptor(Object.getPrototypeOf(document), "body").get.call(document),
+ "Should get body out of property descriptor this way too");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/bindings/test/test_bug742191.html b/dom/bindings/test/test_bug742191.html
new file mode 100644
index 000000000..b4b3151d7
--- /dev/null
+++ b/dom/bindings/test/test_bug742191.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=742191
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for invalid argument object</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=742191">Mozilla Bug 742191</a>
+<p id="display"></p>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 742191 **/
+function doTest() {
+ var gotTypeError = false;
+ var ctx = document.createElement("canvas").getContext("2d");
+ try {
+ ctx.drawImage({}, 0, 0);
+ } catch(e) {
+ if (e instanceof TypeError) {
+ gotTypeError = true;
+ }
+ }
+
+ ok(gotTypeError, "passing an invalid argument should cause a type error!");
+}
+doTest();
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/bindings/test/test_bug759621.html b/dom/bindings/test/test_bug759621.html
new file mode 100644
index 000000000..602a0cd7c
--- /dev/null
+++ b/dom/bindings/test/test_bug759621.html
@@ -0,0 +1,29 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=759621
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 759621</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=759621">Mozilla Bug 759621</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 759621 **/
+var l = document.getElementsByTagName("*");
+l.namedItem = "pass";
+is(l.namedItem, "pass", "Should be able to set expando shadowing a proto prop");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/bindings/test/test_bug773326.html b/dom/bindings/test/test_bug773326.html
new file mode 100644
index 000000000..2e3b1ea30
--- /dev/null
+++ b/dom/bindings/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/dom/bindings/test/test_bug775543.html b/dom/bindings/test/test_bug775543.html
new file mode 100644
index 000000000..d8df05f63
--- /dev/null
+++ b/dom/bindings/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/dom/bindings/test/test_bug788369.html b/dom/bindings/test/test_bug788369.html
new file mode 100644
index 000000000..787bd28fe
--- /dev/null
+++ b/dom/bindings/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/dom/bindings/test/test_bug852846.html b/dom/bindings/test/test_bug852846.html
new file mode 100644
index 000000000..0ca2c7dad
--- /dev/null
+++ b/dom/bindings/test/test_bug852846.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=852846
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 852846</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 852846 **/
+ var elem = document.createElement("div");
+ is(elem.style.color, "", "Shouldn't have color set on HTML element")
+ elem.style = "color: green";
+ is(elem.style.color, "green", "Should have color set on HTML element")
+
+ elem = document.createElementNS("http://www.w3.org/2000/svg", "svg");
+ is(elem.style.color, "", "Shouldn't have color set on SVG element")
+ elem.style = "color: green";
+ is(elem.style.color, "green", "Should have color set on SVG element")
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=852846">Mozilla Bug 852846</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/bindings/test/test_bug862092.html b/dom/bindings/test/test_bug862092.html
new file mode 100644
index 000000000..4b0633328
--- /dev/null
+++ b/dom/bindings/test/test_bug862092.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=862092
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 862092</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 862092 **/
+
+ SimpleTest.waitForExplicitFinish();
+ function runTest()
+ {
+ var frameDoc = document.getElementById("f").contentDocument;
+ var a = document.createElement("select");
+ a.expando = "test";
+ a = frameDoc.adoptNode(a)
+ is(a.expando, "test", "adoptNode needs to preserve expandos");
+ SimpleTest.finish();
+ }
+
+ </script>
+</head>
+<body onload="runTest();">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=862092">Mozilla Bug 862092</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<iframe id="f"></iframe>
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/bindings/test/test_bug963382.html b/dom/bindings/test/test_bug963382.html
new file mode 100644
index 000000000..f48d2e8b0
--- /dev/null
+++ b/dom/bindings/test/test_bug963382.html
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=963382
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 963382</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for clearing cache attributes in JS-implemented WebIDL implementations. **/
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({set: [['dom.expose_test_interfaces', true]]}, go);
+
+ function go() {
+ var t = new TestInterfaceJS();
+
+ // Test [Cached] attribute clearing.
+ is(t.cachedAttr, 15, "Initial value of number");
+
+ t.setCachedAttr(3);
+ is(t.cachedAttr, 15, "Setting the number on the inner JS object should not affect cached value");
+
+ t.clearCachedAttrCache();
+ is(t.cachedAttr, 3, "Setting the number on the inner JS object should affect cached value after clearing the cache.");
+
+ SimpleTest.finish();
+ }
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=963382">Mozilla Bug 963382</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/bindings/test/test_callback_across_document_open.html b/dom/bindings/test/test_callback_across_document_open.html
new file mode 100644
index 000000000..2a505cefa
--- /dev/null
+++ b/dom/bindings/test/test_callback_across_document_open.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Test for callback invocation for a callback that comes from a
+ no-longer-current window that still has an active document.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<iframe srcdoc='<script>function f() { parent.callCount++; }</script>'></iframe>
+<script>
+ var callCount = 0;
+ var t = async_test("A test of callback invocation in a no-longer-current window with a still-active document");
+ window.addEventListener("load", t.step_func_done(function() {
+ var d = document.createElement("div");
+ d.addEventListener("xyz", frames[0].f);
+ frames[0].document.open();
+ frames[0].document.write("All gone");
+ frames[0].document.close();
+ d.dispatchEvent(new Event("xyz"));
+ assert_equals(callCount, 1, "Callback should have been called");
+ }));
+</script>
diff --git a/dom/bindings/test/test_callback_default_thisval.html b/dom/bindings/test/test_callback_default_thisval.html
new file mode 100644
index 000000000..d98ed87b2
--- /dev/null
+++ b/dom/bindings/test/test_callback_default_thisval.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=957929
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 957929</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 957929 **/
+ SimpleTest.waitForExplicitFinish();
+
+ function f() {
+ "use strict";
+ is(this, undefined, "Should have undefined this value");
+ SimpleTest.finish();
+ }
+
+ addLoadEvent(function() {
+ requestAnimationFrame(f);
+ });
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=957929">Mozilla Bug 957929</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/bindings/test/test_callback_exceptions.html b/dom/bindings/test/test_callback_exceptions.html
new file mode 100644
index 000000000..a40b0b94f
--- /dev/null
+++ b/dom/bindings/test/test_callback_exceptions.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Test for ...</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+promise_test(function(t) {
+ var iterator = document.createNodeIterator(document, NodeFilter.SHOW_ALL, JSON.parse);
+ return promise_rejects(t, new SyntaxError,
+ Promise.resolve().then(iterator.nextNode.bind(iterator)));
+}, "Trying to use JSON.parse as filter should throw a catchable SyntaxError exception even when the filter is invoked async");
+
+promise_test(function(t) {
+ return promise_rejects(t, new SyntaxError, Promise.resolve('{').then(JSON.parse));
+}, "Trying to use JSON.parse as a promise callback should allow the next promise to handle the resulting exception.");
+</script>
diff --git a/dom/bindings/test/test_cloneAndImportNode.html b/dom/bindings/test/test_cloneAndImportNode.html
new file mode 100644
index 000000000..fc53c8747
--- /dev/null
+++ b/dom/bindings/test/test_cloneAndImportNode.html
@@ -0,0 +1,48 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=882541
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 882541</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 882541 **/
+ var div = document.createElement("div");
+ div.appendChild(document.createElement("span"));
+
+ var div2;
+
+ div2 = div.cloneNode();
+ is(div2.childNodes.length, 0, "cloneNode() should do a shallow clone");
+
+ div2 = div.cloneNode(undefined);
+ is(div2.childNodes.length, 0, "cloneNode(undefined) should do a shallow clone");
+
+ div2 = div.cloneNode(true);
+ is(div2.childNodes.length, 1, "cloneNode(true) should do a deep clone");
+
+ div2 = document.importNode(div);
+ is(div2.childNodes.length, 0, "importNode(node) should do a deep import");
+
+ div2 = document.importNode(div, undefined);
+ is(div2.childNodes.length, 0, "importNode(undefined) should do a shallow import");
+
+ div2 = document.importNode(div, true);
+ is(div2.childNodes.length, 1, "importNode(true) should do a deep import");
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=882541">Mozilla Bug 882541</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/bindings/test/test_crossOriginWindowSymbolAccess.html b/dom/bindings/test/test_crossOriginWindowSymbolAccess.html
new file mode 100644
index 000000000..7808631b6
--- /dev/null
+++ b/dom/bindings/test/test_crossOriginWindowSymbolAccess.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Test for accessing symbols on a cross-origin window</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<iframe src="http://www1.w3c-test.org/common/blank.html"></iframe>
+<script>
+async_test(function (t) {
+ window.addEventListener("load", t.step_func(
+ function() {
+ assert_equals(document.querySelector("iframe").contentDocument, null, "Should have a crossorigin frame");
+ assert_throws(new Error(), function() {
+ frames[0][Symbol.iterator];
+ }, "Should throw exception on cross-origin Window symbol-named get");
+ assert_throws(new Error(), function() {
+ frames[0].location[Symbol.iterator];
+ }, "Should throw exception on cross-origin Location symbol-named get");
+ t.done();
+ }
+ ));
+}, "Check Symbol access on load");
+</script>
diff --git a/dom/bindings/test/test_defineProperty.html b/dom/bindings/test/test_defineProperty.html
new file mode 100644
index 000000000..f8f5f6283
--- /dev/null
+++ b/dom/bindings/test/test_defineProperty.html
@@ -0,0 +1,157 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=910220
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 910220</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=910220">Mozilla Bug 910220</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<form name="x"></form>
+</div>
+<pre id="test">
+</pre>
+<script type="application/javascript">
+
+/** Test for Bug 910220 **/
+
+function getX() {
+ return "x";
+}
+
+function namedSetStrict(obj) {
+ "use strict";
+ var threw;
+ try {
+ obj.x = 5;
+ threw = false;
+ } catch (e) {
+ threw = true;
+ }
+ ok(threw,
+ "Should throw in strict mode when setting named property on " + obj);
+
+ try {
+ obj[getX()] = 5;
+ threw = false;
+ } catch (e) {
+ threw = true;
+ }
+ ok(threw,
+ "Should throw in strict mode when setting named property via SETELEM on " + obj);
+
+ try {
+ Object.defineProperty(obj, "x", { value: 17 });
+ threw = false;
+ } catch (e) {
+ threw = true;
+ }
+ ok(threw,
+ "Should throw in strict mode when defining named property on " + obj);
+}
+function namedSetNonStrict(obj) {
+ var threw;
+ try {
+ obj.x = 5;
+ threw = false;
+ } catch (e) {
+ threw = true;
+ }
+ ok(!threw,
+ "Should not throw in non-strict mode when setting named property on " + obj);
+
+ try {
+ obj[getX()] = 5;
+ threw = false;
+ } catch (e) {
+ threw = true;
+ }
+ ok(!threw,
+ "Should not throw in non-strict mode when setting named property via SETELEM on" + obj);
+
+ try {
+ Object.defineProperty(obj, "x", { value: 17 });
+ threw = false;
+ } catch (e) {
+ threw = true;
+ }
+ ok(threw,
+ "Should throw in non-strict mode when defining named property on " + obj);
+}
+for (var obj of [ document, document.forms ]) {
+ namedSetStrict(obj);
+ namedSetNonStrict(obj);
+}
+
+function indexedSetStrict(obj) {
+ "use strict";
+ var threw;
+ try {
+ obj[0] = 5;
+ threw = false;
+ } catch (e) {
+ threw = true;
+ }
+ ok(threw,
+ "Should throw in strict mode when setting indexed property on " + obj);
+
+ try {
+ obj[1000] = 5;
+ threw = false;
+ } catch (e) {
+ threw = true;
+ }
+ ok(threw,
+ "Should throw in strict mode when setting out of bounds indexed property on " + obj);
+
+ try {
+ Object.defineProperty(obj, "0", { value: 17 });
+ threw = false;
+ } catch (e) {
+ threw = true;
+ }
+ ok(threw,
+ "Should throw in strict mode when defining indexed property on " + obj);
+}
+function indexedSetNonStrict(obj) {
+ var threw;
+ try {
+ obj[0] = 5;
+ threw = false;
+ } catch (e) {
+ threw = true;
+ }
+ ok(!threw,
+ "Should not throw in non-strict mode when setting indexed property on " + obj);
+
+ try {
+ obj[1000] = 5;
+ threw = false;
+ } catch (e) {
+ threw = true;
+ }
+ ok(!threw,
+ "Should not throw in non-strict mode when setting out of bounds indexed property on " + obj);
+
+ try {
+ Object.defineProperty(obj, "0", { value: 17 });
+ threw = false;
+ } catch (e) {
+ threw = true;
+ }
+ ok(threw,
+ "Should throw in non-strict mode when defining indexed property on " + obj);
+}
+for (var obj of [ document.forms, document.childNodes ]) {
+ indexedSetStrict(obj);
+ indexedSetNonStrict(obj);
+}
+</script>
+</body>
+</html>
diff --git a/dom/bindings/test/test_document_location_set_via_xray.html b/dom/bindings/test/test_document_location_set_via_xray.html
new file mode 100644
index 000000000..cdadc5063
--- /dev/null
+++ b/dom/bindings/test/test_document_location_set_via_xray.html
@@ -0,0 +1,49 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=905493
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 905493</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=905493">Mozilla Bug 905493</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<iframe id="t" src="http://example.org/tests/dom/bindings/test/file_document_location_set_via_xray.html"></iframe>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 905493 **/
+
+function test()
+{
+ var doc = document.getElementById("t").contentWindow.document;
+ ok(!("x" in doc), "Should have an Xray here");
+ is(doc.x, undefined, "Really should have an Xray here");
+ is(doc.wrappedJSObject.x, 5, "And wrapping the right thing");
+ document.getElementById("t").onload = function() {
+ ok(true, "Load happened");
+ SimpleTest.finish();
+ };
+ try {
+ // Test the forwarding location setter
+ doc.location = "chrome://mochikit/content/tests/SimpleTest/test.css";
+ } catch (e) {
+ // Load failed
+ ok(false, "Load failed");
+ SimpleTest.finish();
+ }
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(test);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/bindings/test/test_document_location_via_xray_cached.html b/dom/bindings/test/test_document_location_via_xray_cached.html
new file mode 100644
index 000000000..20eef10fb
--- /dev/null
+++ b/dom/bindings/test/test_document_location_via_xray_cached.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1041731
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1041731</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=1041731">Mozilla Bug 1041731</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<iframe id="t" src="http://example.org/tests/dom/bindings/test/file_document_location_set_via_xray.html"></iframe>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 1041731 **/
+
+function test()
+{
+ var loc = document.getElementById("t").contentWindow.document.location;
+ is(loc.toString, loc.toString, "Unforgeable method on the Xray should be cached");
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(test);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/bindings/test/test_domProxyArrayLengthGetter.html b/dom/bindings/test/test_domProxyArrayLengthGetter.html
new file mode 100644
index 000000000..a62adff2e
--- /dev/null
+++ b/dom/bindings/test/test_domProxyArrayLengthGetter.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1221421
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1221421</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="text/javascript">
+
+ var x = document.documentElement.style;
+ x.__proto__ = [1, 2, 3];
+
+ var res = 0;
+ for (var h = 0; h < 5000; ++h) {
+ res += x.length;
+ }
+ is(res, 15000, "length getter should return array length");
+
+ </script>
+</head>
+
+<body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1221421">Mozilla Bug 1221421</a>
+</body>
+</html>
diff --git a/dom/bindings/test/test_dom_xrays.html b/dom/bindings/test/test_dom_xrays.html
new file mode 100644
index 000000000..0700db2f8
--- /dev/null
+++ b/dom/bindings/test/test_dom_xrays.html
@@ -0,0 +1,231 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=787070
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 787070</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=787070">Mozilla Bug 787070</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<iframe id="t" src="http://example.org/tests/dom/bindings/test/file_dom_xrays.html"></iframe>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 1021066 **/
+
+var Cu = Components.utils;
+
+// values should contain the values that the property should have on each of
+// the objects on the prototype chain of obj. A value of undefined signals
+// that the value should not be present on that prototype.
+function checkXrayProperty(obj, name, values)
+{
+ var instance = obj;
+ do {
+ var value = values.shift();
+ if (typeof value == "undefined") {
+ ok(!obj.hasOwnProperty(name), "hasOwnProperty shouldn't see \"" + name + "\" through Xrays");
+ is(Object.getOwnPropertyDescriptor(obj, name), undefined, "getOwnPropertyDescriptor shouldn't see \"" + name + "\" through Xrays");
+ ok(Object.keys(obj).indexOf(name) == -1, "Enumerating the Xray should not return \"" + name + "\"");
+ } else {
+ ok(obj.hasOwnProperty(name), "hasOwnProperty should see \"" + name + "\" through Xrays");
+ var pd = Object.getOwnPropertyDescriptor(obj, name);
+ ok(pd, "getOwnPropertyDescriptor should see \"" + name + "\" through Xrays");
+ if (pd && pd.get) {
+ is(pd.get.call(instance), value, "Should get the right value for \"" + name + "\" through Xrays");
+ } else {
+ is(obj[name], value, "Should get the right value for \"" + name + "\" through Xrays");
+ }
+ if (pd && pd.enumerable) {
+ ok(Object.keys(obj).indexOf("" + name) > -1, "Enumerating the Xray should return \"" + name + "\"");
+ }
+ }
+ } while ((obj = Object.getPrototypeOf(obj)));
+}
+
+function checkWindowXrayProperty(obj, name, windowValue, windowPrototypeValue, namedPropertiesValue, eventTargetValue)
+{
+ checkXrayProperty(obj, name, [ windowValue, windowPrototypeValue, namedPropertiesValue, eventTargetValue ]);
+}
+
+function test()
+{
+ // Window
+ var win = document.getElementById("t").contentWindow;
+ var doc = document.getElementById("t").contentDocument;
+
+ var winProto = Object.getPrototypeOf(win);
+ is(winProto, win.Window.prototype, "The proto chain of the Xray should mirror the prototype chain of the Xrayed object");
+
+ var namedPropertiesObject = Object.getPrototypeOf(winProto);
+ is(Cu.getClassName(namedPropertiesObject, /* unwrap = */ true), "WindowProperties", "The proto chain of the Xray should mirror the prototype chain of the Xrayed object");
+
+ var eventTargetProto = Object.getPrototypeOf(namedPropertiesObject);
+ is(eventTargetProto, win.EventTarget.prototype, "The proto chain of the Xray should mirror the prototype chain of the Xrayed object");
+
+ // Xrays need to filter expandos.
+ checkWindowXrayProperty(win, "expando", undefined);
+ ok(!("expando" in win), "Xrays should filter expandos");
+
+ checkWindowXrayProperty(win, "shadowedIframe", undefined, undefined, doc.getElementById("shadowedIframe").contentWindow);
+ ok("shadowedIframe" in win, "Named properties should be exposed through Xrays");
+
+ // Named properties live on the named properties object for global objects.
+ checkWindowXrayProperty(win, "iframe", undefined, undefined, doc.getElementById("iframe").contentWindow);
+ ok("iframe" in win, "Named properties should be exposed through Xrays");
+
+ // Window properties live on the instance, shadowing the properties of the named property object.
+ checkWindowXrayProperty(win, "document", doc, undefined, doc.getElementById("document").contentWindow);
+ ok("document" in win, "WebIDL properties should be exposed through Xrays");
+
+ // Unforgeable properties live on the instance, shadowing the properties of the named property object.
+ checkWindowXrayProperty(win, "self", win, undefined, doc.getElementById("self").contentWindow);
+ ok("self" in win, "WebIDL properties should be exposed through Xrays");
+
+ // Object.prototype is at the end of the prototype chain.
+ var obj = win;
+ while ((proto = Object.getPrototypeOf(obj))) {
+ obj = proto;
+ }
+ is(obj, win.Object.prototype, "Object.prototype should be at the end of the prototype chain");
+
+ // Named properties shouldn't shadow WebIDL- or ECMAScript-defined properties.
+ checkWindowXrayProperty(win, "addEventListener", undefined, undefined, undefined, eventTargetProto.addEventListener);
+ is(win.addEventListener, eventTargetProto.addEventListener, "Named properties shouldn't shadow WebIDL-defined properties");
+
+ is(win.toString, win.Object.prototype.toString, "Named properties shouldn't shadow ECMAScript-defined properties");
+
+ // HTMLDocument
+ // Unforgeable properties live on the instance.
+ checkXrayProperty(doc, "location", [ win.location ]);
+ is(String(win.location), document.getElementById("t").src,
+ "Should have the right stringification");
+
+ // HTMLHtmlElement
+ var elem = doc.documentElement;
+
+ var elemProto = Object.getPrototypeOf(elem);
+ is(elemProto, win.HTMLHtmlElement.prototype, "The proto chain of the Xray should mirror the prototype chain of the Xrayed object");
+
+ elemProto = Object.getPrototypeOf(elemProto);
+ is(elemProto, win.HTMLElement.prototype, "The proto chain of the Xray should mirror the prototype chain of the Xrayed object");
+
+ elemProto = Object.getPrototypeOf(elemProto);
+ is(elemProto, win.Element.prototype, "The proto chain of the Xray should mirror the prototype chain of the Xrayed object");
+
+ elemProto = Object.getPrototypeOf(elemProto);
+ is(elemProto, win.Node.prototype, "The proto chain of the Xray should mirror the prototype chain of the Xrayed object");
+
+ elemProto = Object.getPrototypeOf(elemProto);
+ is(elemProto, win.EventTarget.prototype, "The proto chain of the Xray should mirror the prototype chain of the Xrayed object");
+
+ // Xrays need to filter expandos.
+ ok(!("expando" in elem), "Xrays should filter expandos");
+
+ // WebIDL-defined properties live on the prototype.
+ checkXrayProperty(elem, "version", [ undefined, "" ]);
+ is(elem.version, "", "WebIDL properties should be exposed through Xrays");
+
+ // HTMLCollection
+ var coll = doc.getElementsByTagName("iframe");
+
+ // Named properties live on the instance for non-global objects.
+ checkXrayProperty(coll, "iframe", [ doc.getElementById("iframe") ]);
+
+ // Indexed properties live on the instance.
+ checkXrayProperty(coll, 0, [ doc.getElementById("shadowedIframe") ]);
+
+ // WebIDL-defined properties live on the prototype, overriding any named properties.
+ checkXrayProperty(coll, "item", [ undefined, win.HTMLCollection.prototype.item ]);
+
+ // ECMAScript-defined properties live on the prototype, overriding any named properties.
+ checkXrayProperty(coll, "toString", [ undefined, undefined, win.Object.prototype.toString ]);
+
+ // Frozen arrays should come from our compartment, not the target one.
+ var languages1 = win.navigator.languages;
+ isnot(languages1, undefined, "Must have .languages");
+ ok(Array.isArray(languages1), ".languages should be an array");
+ ok(Object.isFrozen(languages1), ".languages should be a frozen array");
+ ok(!Cu.isXrayWrapper(languages1), "Should have our own version of array");
+ is(Cu.getGlobalForObject(languages1), window,
+ "languages1 should come from our window");
+ // We want to get .languages in the content compartment, but without waiving
+ // Xrays altogether.
+ var languages2 = win.eval("navigator.languages");
+ isnot(languages2, undefined, "Must still have .languages");
+ ok(Array.isArray(languages2), ".languages should still be an array");
+ ok(Cu.isXrayWrapper(languages2), "Should have xray for content version of array");
+ is(Cu.getGlobalForObject(languages2), win,
+ "languages2 come from the underlying window");
+ ok(Object.isFrozen(languages2.wrappedJSObject),
+ ".languages should still be a frozen array underneath");
+ isnot(languages1, languages2, "Must have distinct arrays");
+ isnot(languages1, languages2.wrappedJSObject,
+ "Must have distinct arrays no matter how we slice it");
+
+ // Check that DataTransfer's .types has the hack to alias contains()
+ // to includes().
+ var dataTransfer = new win.DataTransfer("foo", true);
+ is(dataTransfer.types.contains, dataTransfer.types.includes,
+ "Should have contains() set up as an alias to includes()");
+ // Waive Xrays on the dataTransfer itself, since the .types we get is
+ // different over Xrays vs not.
+ is(dataTransfer.wrappedJSObject.types.contains, undefined,
+ "Underlying object should not have contains() set up as an alias to " +
+ "includes()");
+
+ // Check that deleters work correctly in the [OverrideBuiltins] case.
+ var elem = win.document.documentElement;
+ var dataset = elem.dataset;
+ is(dataset.foo, undefined, "Should not have a 'foo' property");
+ ok(!('foo' in dataset), "Really should not have a 'foo' property");
+ is(elem.getAttribute("data-foo"), null,
+ "Should not have a 'data-foo' attribute");
+ ok(!elem.hasAttribute("data-foo"),
+ "Really should not have a 'data-foo' attribute");
+ dataset.foo = "bar";
+ is(dataset.foo, "bar", "Should now have a 'foo' property");
+ ok('foo' in dataset, "Really should have a 'foo' property");
+ is(elem.getAttribute("data-foo"), "bar",
+ "Should have a 'data-foo' attribute");
+ ok(elem.hasAttribute("data-foo"),
+ "Really should have a 'data-foo' attribute");
+ delete dataset.foo;
+ is(dataset.foo, undefined, "Should not have a 'foo' property again");
+ ok(!('foo' in dataset), "Really should not have a 'foo' property again");
+ is(elem.getAttribute("data-foo"), null,
+ "Should not have a 'data-foo' attribute again");
+ ok(!elem.hasAttribute("data-foo"),
+ "Really should not have a 'data-foo' attribute again");
+
+ // Check that deleters work correctly in the non-[OverrideBuiltins] case.
+ var storage = win.sessionStorage;
+ is(storage.foo, undefined, "Should not have a 'foo' property");
+ ok(!('foo' in storage), "Really should not have a 'foo' property");
+ is(storage.getItem("foo"), null, "Should not have an item named 'foo'");
+ storage.foo = "bar";
+ is(storage.foo, "bar", "Should have a 'foo' property");
+ ok('foo' in storage, "Really should have a 'foo' property");
+ is(storage.getItem("foo"), "bar", "Should have an item named 'foo'");
+ delete storage.foo
+ is(storage.foo, undefined, "Should not have a 'foo' property again");
+ ok(!('foo' in storage), "Really should not have a 'foo' property again");
+ is(storage.getItem("foo"), null, "Should not have an item named 'foo' again");
+
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(test);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/bindings/test/test_enums.html b/dom/bindings/test/test_enums.html
new file mode 100644
index 000000000..e5dc519a0
--- /dev/null
+++ b/dom/bindings/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/dom/bindings/test/test_exceptionSanitization.html b/dom/bindings/test/test_exceptionSanitization.html
new file mode 100644
index 000000000..9a6ab6088
--- /dev/null
+++ b/dom/bindings/test/test_exceptionSanitization.html
@@ -0,0 +1,45 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1295322
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1295322</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=1295322">Mozilla Bug 1295322</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+ <script type="application/javascript">
+
+ /** Test for Bug 1295322 **/
+ iframe = document.createElement('iframe');
+ iframe.name = "eWin";
+ document.body.appendChild(iframe);
+
+ try{
+ // NOTE: The idea here is to call something that will end up throwing an
+ // exception in a JS component and then propagate back to C++ code before
+ // returning to us. If opening a feed: URI stops doing that, we will need a
+ // new guinea pig here.
+ open('feed://java:script:codeshouldgohere','eWin');
+ ok(false, "Should have thrown!");
+ } catch(e){
+ try {
+ is(e.name, "NS_ERROR_UNEXPECTED", "Should have the right exception");
+ is(e.filename, location.href,
+ "Should not be seeing where the exception really came from");
+ } catch (e2) {
+ ok(false, "Should be able to work with the exception");
+ }
+ }
+ </script>
+</body>
+</html>
diff --git a/dom/bindings/test/test_exceptionThrowing.html b/dom/bindings/test/test_exceptionThrowing.html
new file mode 100644
index 000000000..376c2bc57
--- /dev/null
+++ b/dom/bindings/test/test_exceptionThrowing.html
@@ -0,0 +1,56 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=847119
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 847119</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 847119 **/
+
+ var xhr = new XMLHttpRequest();
+ var domthrows = function() { xhr.open(); }
+
+ var count = 20000;
+
+ function f() {
+ var k = 0;
+ for (var j = 0; j < count; ++j) {
+ try { domthrows(); } catch(e) { ++k; }
+ }
+ return k;
+ }
+ function g() { return count; }
+
+ is(f(), count, "Should get count exceptions");
+ for (var h of [f, g]) {
+ try { is(h(), count, "Should get count exceptions here too"); } catch (e) {}
+ }
+ ok(true, "We should get here");
+
+ var domthrows = function() { xhr.withCredentials = false; }
+ xhr.open("GET", "");
+ xhr.send();
+
+ is(f(), count, "Should get count exceptions from getter");
+ for (var h of [f, g]) {
+ try { is(h(), count, "Should get count exceptions from getter here too"); } catch (e) {}
+ }
+ ok(true, "We should get here too");
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=847119">Mozilla Bug 847119</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/bindings/test/test_exception_messages.html b/dom/bindings/test/test_exception_messages.html
new file mode 100644
index 000000000..a0f0cabe6
--- /dev/null
+++ b/dom/bindings/test/test_exception_messages.html
@@ -0,0 +1,82 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=882653
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 882653</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 882653 **/
+ // Each test is a string to eval, the expected exception message, and the
+ // test description.
+ var tests = [
+ [ 'document.documentElement.appendChild.call({}, new Image())',
+ "'appendChild' called on an object that does not implement interface Node.",
+ "bogus method this object" ],
+ [ 'Object.getOwnPropertyDescriptor(Document.prototype, "documentElement").get.call({})',
+ "'get documentElement' called on an object that does not implement interface Document.",
+ "bogus getter this object" ],
+ [ 'Object.getOwnPropertyDescriptor(Element.prototype, "innerHTML").set.call({})',
+ "'set innerHTML' called on an object that does not implement interface Element.",
+ "bogus setter this object" ],
+ [ 'document.documentElement.appendChild(5)',
+ "Argument 1 of Node.appendChild is not an object.",
+ "bogus interface argument" ],
+ [ 'document.documentElement.appendChild(null)',
+ "Argument 1 of Node.appendChild is not an object.",
+ "null interface argument" ],
+ [ 'document.createTreeWalker(document).currentNode = 5',
+ "Value being assigned to TreeWalker.currentNode is not an object.",
+ "interface setter call" ],
+ [ 'document.documentElement.appendChild({})',
+ "Argument 1 of Node.appendChild does not implement interface Node.",
+ "wrong interface argument" ],
+ [ 'document.createTreeWalker(document).currentNode = {}',
+ "Value being assigned to TreeWalker.currentNode does not implement interface Node.",
+ "wrong interface setter call" ],
+ [ 'document.createElement("canvas").getContext("2d").fill("bogus")',
+ "Argument 1 of CanvasRenderingContext2D.fill 'bogus' is not a valid value for enumeration CanvasWindingRule.",
+ "bogus enum value" ],
+ [ 'document.createTreeWalker(document, 0xFFFFFFFF, { acceptNode: 5 }).nextNode()',
+ "Property 'acceptNode' is not callable.",
+ "non-callable callback interface operation property" ],
+ [ '(new TextDecoder).decode(new Uint8Array(), new RegExp())',
+ "Argument 2 of TextDecoder.decode can't be converted to a dictionary.",
+ "regexp passed for a dictionary" ],
+ [ 'URL.createObjectURL(null, null)',
+ "Argument 1 is not valid for any of the 2-argument overloads of URL.createObjectURL.",
+ "overload resolution failure" ],
+ [ 'document.createElement("select").add({})',
+ "Argument 1 of HTMLSelectElement.add could not be converted to any of: HTMLOptionElement, HTMLOptGroupElement.",
+ "invalid value passed for union" ],
+ [ 'document.createElement("canvas").getContext("2d").createLinearGradient(0, 1, 0, 1).addColorStop(NaN, "")',
+ "Argument 1 of CanvasGradient.addColorStop is not a finite floating-point value.",
+ "invalid float" ]
+ ];
+
+ for (var i = 0; i < tests.length; ++i) {
+ msg = "Correct exception should be thrown for " + tests[i][2];
+ try {
+ eval(tests[i][0]);
+ ok(false, msg);
+ } catch (e) {
+ is(e.message, tests[i][1], msg);
+ }
+ }
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=882653">Mozilla Bug 882653</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/bindings/test/test_exception_options_from_jsimplemented.html b/dom/bindings/test/test_exception_options_from_jsimplemented.html
new file mode 100644
index 000000000..8a98a8fb6
--- /dev/null
+++ b/dom/bindings/test/test_exception_options_from_jsimplemented.html
@@ -0,0 +1,166 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1107592
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1107592</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 1107592 **/
+
+ SimpleTest.waitForExplicitFinish();
+
+ function doTest() {
+ var file = location.href;
+ var asyncFrame;
+ /* Async parent frames from pushPrefEnv don't show up in e10s. */
+ var isE10S = !SpecialPowers.isMainProcess();
+ if (!isE10S && SpecialPowers.getBoolPref("javascript.options.asyncstack")) {
+ asyncFrame = `Async*@${file}:153:3
+`;
+ } else {
+ asyncFrame = "";
+ }
+
+ var t = new TestInterfaceJS();
+ try {
+ t.testThrowError();
+ } catch (e) {
+ ok(e instanceof Error, "Should have an Error here");
+ ok(!(e instanceof DOMException), "Should not have DOMException here");
+ ok(!("code" in e), "Should not have a 'code' property");
+ is(e.name, "Error", "Should not have an interesting name here");
+ is(e.message, "We are an Error", "Should have the right message");
+ is(e.stack,
+ `doTest@${file}:31:7
+${asyncFrame}`,
+ "Exception stack should still only show our code");
+ is(e.fileName,
+ file,
+ "Should have the right file name");
+ is(e.lineNumber, 31, "Should have the right line number");
+ is(e.columnNumber, 7, "Should have the right column number");
+ }
+
+ try {
+ t.testThrowDOMException();
+ } catch (e) {
+ ok(e instanceof Error, "Should also have an Error here");
+ ok(e instanceof DOMException, "Should have DOMException here");
+ is(e.name, "NotSupportedError", "Should have the right name here");
+ is(e.message, "We are a DOMException",
+ "Should also have the right message");
+ is(e.code, DOMException.NOT_SUPPORTED_ERR,
+ "Should have the right 'code'");
+ is(e.stack,
+ `doTest@${file}:50:7
+${asyncFrame}`,
+ "Exception stack should still only show our code");
+ is(e.filename,
+ file,
+ "Should still have the right file name");
+ is(e.lineNumber, 50, "Should still have the right line number");
+ todo_isnot(e.columnNumber, 0,
+ "No column number support for DOMException yet");
+ }
+
+ try {
+ t.testThrowTypeError();
+ } catch (e) {
+ ok(e instanceof TypeError, "Should have a TypeError here");
+ ok(!(e instanceof DOMException), "Should not have DOMException here (2)");
+ ok(!("code" in e), "Should not have a 'code' property (2)");
+ is(e.name, "TypeError", "Should be named TypeError");
+ is(e.message, "We are a TypeError",
+ "Should also have the right message (2)");
+ is(e.stack,
+ `doTest@${file}:72:7
+${asyncFrame}`,
+ "Exception stack for TypeError should only show our code");
+ is(e.fileName,
+ file,
+ "Should still have the right file name for TypeError");
+ is(e.lineNumber, 72, "Should still have the right line number for TypeError");
+ is(e.columnNumber, 7, "Should have the right column number for TypeError");
+ }
+
+ try {
+ t.testThrowCallbackError(function() { Array.indexOf() });
+ } catch (e) {
+ ok(e instanceof TypeError, "Should have a TypeError here (3)");
+ ok(!(e instanceof DOMException), "Should not have DOMException here (3)");
+ ok(!("code" in e), "Should not have a 'code' property (3)");
+ is(e.name, "TypeError", "Should be named TypeError (3)");
+ is(e.message, "missing argument 0 when calling function Array.indexOf",
+ "Should also have the right message (3)");
+ is(e.stack,
+ `doTest/<@${file}:92:45
+doTest@${file}:92:7
+${asyncFrame}`,
+ "Exception stack for TypeError should only show our code (3)");
+ is(e.fileName,
+ file,
+ "Should still have the right file name for TypeError (3)");
+ is(e.lineNumber, 92, "Should still have the right line number for TypeError (3)");
+ is(e.columnNumber, 45, "Should have the right column number for TypeError (3)");
+ }
+
+ try {
+ t.testThrowXraySelfHosted();
+ } catch (e) {
+ ok(!(e instanceof Error), "Should have an Exception here (4)");
+ ok(!(e instanceof DOMException), "Should not have DOMException here (4)");
+ ok(!("code" in e), "Should not have a 'code' property (4)");
+ is(e.name, "NS_ERROR_UNEXPECTED", "Name should be sanitized (4)");
+ is(e.message, "", "Message should be sanitized (5)");
+ is(e.stack,
+ `doTest@${file}:113:7
+${asyncFrame}`,
+ "Exception stack for sanitized exception should only show our code (4)");
+ is(e.filename,
+ file,
+ "Should still have the right file name for sanitized exception (4)");
+ is(e.lineNumber, 113, "Should still have the right line number for sanitized exception (4)");
+ todo_isnot(e.columnNumber, 0, "Should have the right column number for sanitized exception (4)");
+ }
+
+ try {
+ t.testThrowSelfHosted();
+ } catch (e) {
+ ok(!(e instanceof Error), "Should have an Exception here (5)");
+ ok(!(e instanceof DOMException), "Should not have DOMException here (5)");
+ ok(!("code" in e), "Should not have a 'code' property (5)");
+ is(e.name, "NS_ERROR_UNEXPECTED", "Name should be sanitized (5)");
+ is(e.message, "", "Message should be sanitized (5)");
+ is(e.stack,
+ `doTest@${file}:132:7
+${asyncFrame}`,
+ "Exception stack for sanitized exception should only show our code (5)");
+ is(e.filename,
+ file,
+ "Should still have the right file name for sanitized exception (5)");
+ is(e.lineNumber, 132, "Should still have the right line number for sanitized exception (5)");
+ todo_isnot(e.columnNumber, 0, "Should have the right column number for sanitized exception (5)");
+ }
+
+ SimpleTest.finish();
+ }
+
+ SpecialPowers.pushPrefEnv({set: [['dom.expose_test_interfaces', true]]},
+ doTest);
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1107592">Mozilla Bug 1107592</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/bindings/test/test_exceptions_from_jsimplemented.html b/dom/bindings/test/test_exceptions_from_jsimplemented.html
new file mode 100644
index 000000000..d0f599353
--- /dev/null
+++ b/dom/bindings/test/test_exceptions_from_jsimplemented.html
@@ -0,0 +1,56 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=923010
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 923010</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+ /** Test for Bug 923010 **/
+ try {
+ var conn = new RTCPeerConnection();
+
+ var candidate = new RTCIceCandidate({candidate: null });
+ conn.addIceCandidate(candidate)
+ .then(function() {
+ ok(false, "addIceCandidate succeeded when it should have failed");
+ }, function(reason) {
+ is(reason.lineNumber, 17, "Rejection should have been on line 17");
+ is(reason.message,
+ "Invalid candidate passed to addIceCandidate!",
+ "Should have the rejection we expect");
+ })
+ .catch(function(reason) {
+ ok(false, "unexpected error: " + reason);
+ });
+ } catch (e) {
+ // b2g has no WebRTC, apparently
+ todo(false, "No WebRTC on b2g yet");
+ }
+
+ conn.close();
+ try {
+ conn.setIdentityProvider("example.com", "foo");
+ ok(false, "That call to setIdentityProvider should have thrown");
+ } catch (e) {
+ is(e.lineNumber, 36, "Exception should have been on line 36");
+ is(e.message,
+ "Peer connection is closed",
+ "Should have the exception we expect");
+ }
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=923010">Mozilla Bug 923010</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/bindings/test/test_forOf.html b/dom/bindings/test/test_forOf.html
new file mode 100644
index 000000000..53969a23e
--- /dev/null
+++ b/dom/bindings/test/test_forOf.html
@@ -0,0 +1,86 @@
+<!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");
+
+ 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 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/dom/bindings/test/test_integers.html b/dom/bindings/test/test_integers.html
new file mode 100644
index 000000000..c74b68216
--- /dev/null
+++ b/dom/bindings/test/test_integers.html
@@ -0,0 +1,50 @@
+<!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.
+ var gl = $("c").getContext("experimental-webgl");
+ if (!gl) {
+ // No WebGL support on MacOS 10.5. Just skip this test
+ todo(false, "WebGL not supported");
+ return;
+ }
+ var error = gl.getError()
+
+ // on the b2g emulator we get GL_INVALID_FRAMEBUFFER_OPERATION
+ if (error == 0x0506) // GL_INVALID_FRAMEBUFFER_OPERATION
+ return;
+
+ is(error, 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/dom/bindings/test/test_interfaceName.html b/dom/bindings/test/test_interfaceName.html
new file mode 100644
index 000000000..59828a2cf
--- /dev/null
+++ b/dom/bindings/test/test_interfaceName.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1084001
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1084001</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 1084001 **/
+ is(Image.name, "Image", "Image name");
+ is(Promise.name, "Promise", "Promise name");
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1084001">Mozilla Bug 1084001</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/bindings/test/test_interfaceToString.html b/dom/bindings/test/test_interfaceToString.html
new file mode 100644
index 000000000..c97b2f63b
--- /dev/null
+++ b/dom/bindings/test/test_interfaceToString.html
@@ -0,0 +1,47 @@
+<!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.");
+}
+
+try {
+ eventTargetToString = Function.prototype.toString.call(EventTarget);
+ is(eventTargetToString, nativeToString,
+ "Stringifying a DOM interface object via Function.prototype.toString " +
+ "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/dom/bindings/test/test_iterable.html b/dom/bindings/test/test_iterable.html
new file mode 100644
index 000000000..8ce818e76
--- /dev/null
+++ b/dom/bindings/test/test_iterable.html
@@ -0,0 +1,241 @@
+<!-- Any copyright is dedicated to the Public Domain.
+- http://creativecommons.org/publicdomain/zero/1.0/ -->
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Test Iterable Interface</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ </head>
+ <body>
+ <script class="testbody" type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({set: [['dom.expose_test_interfaces', true]]}, function() {
+
+ base_properties = [["entries", "function", 0],
+ ["keys", "function", 0],
+ ["values", "function", 0],
+ ["forEach", "function", 1]]
+ var testExistence = function testExistence(prefix, obj, properties) {
+ for (var [name, type, args] of properties) {
+ // Properties are somewhere up the proto chain, hasOwnProperty won't work
+ isnot(obj[name], undefined,
+ `${prefix} object has property ${name}`);
+
+ is(typeof obj[name], type,
+ `${prefix} object property ${name} is a ${type}`);
+ // Check function length
+ if (type == "function") {
+ is(obj[name].length, args,
+ `${prefix} object property ${name} is length ${args}`);
+ is(obj[name].name, name,
+ `${prefix} object method name is ${name}`);
+ }
+
+ // Find where property is on proto chain, check for enumerablility there.
+ var owner = obj;
+ while (owner) {
+ var propDesc = Object.getOwnPropertyDescriptor(owner, name);
+ if (propDesc) {
+ ok(propDesc.enumerable,
+ `${prefix} object property ${name} is enumerable`);
+ break;
+ }
+ owner = Object.getPrototypeOf(owner);
+ }
+ }
+ }
+
+ var itr;
+ // Simple single type iterable creation and functionality test
+ info("IterableSingle: Testing simple iterable creation and functionality");
+ itr = new TestInterfaceIterableSingle();
+ testExistence("IterableSingle: ", itr, base_properties);
+ is(itr[Symbol.iterator], Array.prototype[Symbol.iterator],
+ "IterableSingle: Should be using %ArrayIterator% for @@iterator");
+ is(itr.keys, Array.prototype.keys,
+ "IterableSingle: Should be using %ArrayIterator% for 'keys'");
+ is(itr.entries, Array.prototype.entries,
+ "IterableSingle: Should be using %ArrayIterator% for 'entries'");
+ is(itr.values, itr[Symbol.iterator],
+ "IterableSingle: Should be using @@iterator for 'values'");
+ is(itr.forEach, Array.prototype.forEach,
+ "IterableSingle: Should be using %ArrayIterator% for 'forEach'");
+ var keys = [...itr.keys()];
+ var values = [...itr.values()];
+ var entries = [...itr.entries()];
+ var key_itr = itr.keys();
+ var value_itr = itr.values();
+ var entries_itr = itr.entries();
+ for (var i = 0; i < 3; ++i) {
+ var key = key_itr.next();
+ var value = value_itr.next();
+ var entry = entries_itr.next();
+ is(key.value, i, "IterableSingle: Key iterator value should be " + i);
+ is(key.value, keys[i],
+ "IterableSingle: Key iterator value should match destructuring " + i);
+ is(value.value, key.value, "IterableSingle: Value iterator value should be " + key.value);
+ is(value.value, values[i],
+ "IterableSingle: Value iterator value should match destructuring " + i);
+ is(entry.value[0], i, "IterableSingle: Entry iterator value 0 should be " + i);
+ is(entry.value[1], i, "IterableSingle: Entry iterator value 1 should be " + i);
+ is(entry.value[0], entries[i][0],
+ "IterableSingle: Entry iterator value 0 should match destructuring " + i);
+ is(entry.value[1], entries[i][1],
+ "IterableSingle: Entry iterator value 1 should match destructuring " + i);
+ }
+
+ var callsToForEachCallback = 0;
+ var thisArg = {};
+ itr.forEach(function(value, index, obj) {
+ is(index, callsToForEachCallback,
+ `IterableSingle: Should have the right index at ${callsToForEachCallback} calls to forEach callback`);
+ is(value, values[index],
+ `IterableSingle: Should have the right value at ${callsToForEachCallback} calls to forEach callback`);
+ is(this, thisArg,
+ "IterableSingle: Should have the right this value for forEach callback");
+ is(obj, itr,
+ "IterableSingle: Should have the right third arg for forEach callback");
+ ++callsToForEachCallback;
+ }, thisArg);
+ is(callsToForEachCallback, 3,
+ "IterableSingle: Should have right total number of calls to forEach callback");
+
+ var key = key_itr.next();
+ var value = value_itr.next();
+ var entry = entries_itr.next();
+ is(key.value, undefined, "IterableSingle: Key iterator value should be undefined");
+ is(key.done, true, "IterableSingle: Key iterator done should be true");
+ is(value.value, undefined, "IterableSingle: Value iterator value should be undefined");
+ is(value.done, true, "IterableSingle: Value iterator done should be true");
+ is(entry.value, undefined, "IterableDouble: Entry iterator value should be undefined");
+ is(entry.done, true, "IterableSingle: Entry iterator done should be true");
+ is(Object.prototype.toString.call(Object.getPrototypeOf(key_itr)),
+ "[object Array Iterator]",
+ "iterator prototype should have the right brand");
+
+ // Simple dual type iterable creation and functionality test
+ info("IterableDouble: Testing simple iterable creation and functionality");
+ itr = new TestInterfaceIterableDouble();
+ testExistence("IterableDouble: ", itr, base_properties);
+ is(itr.entries, itr[Symbol.iterator],
+ "IterableDouble: Should be using @@iterator for 'entries'");
+ var elements = [["a", "b"], ["c", "d"], ["e", "f"]]
+ var keys = [...itr.keys()];
+ var values = [...itr.values()];
+ var entries = [...itr.entries()];
+ var key_itr = itr.keys();
+ var value_itr = itr.values();
+ var entries_itr = itr.entries();
+ for (var i = 0; i < 3; ++i) {
+ var key = key_itr.next();
+ var value = value_itr.next();
+ var entry = entries_itr.next();
+ is(key.value, elements[i][0], "IterableDouble: Key iterator value should be " + elements[i][0]);
+ is(key.value, keys[i],
+ "IterableDouble: Key iterator value should match destructuring " + i);
+ is(value.value, elements[i][1], "IterableDouble: Value iterator value should be " + elements[i][1]);
+ is(value.value, values[i],
+ "IterableDouble: Value iterator value should match destructuring " + i);
+ is(entry.value[0], elements[i][0], "IterableDouble: Entry iterator value 0 should be " + elements[i][0]);
+ is(entry.value[1], elements[i][1], "IterableDouble: Entry iterator value 1 should be " + elements[i][1]);
+ is(entry.value[0], entries[i][0],
+ "IterableDouble: Entry iterator value 0 should match destructuring " + i);
+ is(entry.value[1], entries[i][1],
+ "IterableDouble: Entry iterator value 1 should match destructuring " + i);
+ }
+
+ callsToForEachCallback = 0;
+ thisArg = {};
+ itr.forEach(function(value, key, obj) {
+ is(key, keys[callsToForEachCallback],
+ `IterableDouble: Should have the right key at ${callsToForEachCallback} calls to forEach callback`);
+ is(value, values[callsToForEachCallback],
+ `IterableDouble: Should have the right value at ${callsToForEachCallback} calls to forEach callback`);
+ is(this, thisArg,
+ "IterableDouble: Should have the right this value for forEach callback");
+ is(obj, itr,
+ "IterableSingle: Should have the right third arg for forEach callback");
+ ++callsToForEachCallback;
+ }, thisArg);
+ is(callsToForEachCallback, 3,
+ "IterableDouble: Should have right total number of calls to forEach callback");
+
+ var key = key_itr.next();
+ var value = value_itr.next();
+ var entry = entries_itr.next()
+ is(key.value, undefined, "IterableDouble: Key iterator value should be undefined");
+ is(key.done, true, "IterableDouble: Key iterator done should be true");
+ is(value.value, undefined, "IterableDouble: Value iterator value should be undefined");
+ is(value.done, true, "IterableDouble: Value iterator done should be true");
+ is(entry.value, undefined, "IterableDouble: Entry iterator value should be undefined");
+ is(entry.done, true, "IterableDouble: Entry iterator done should be true");
+ is(Object.prototype.toString.call(Object.getPrototypeOf(key_itr)),
+ "[object TestInterfaceIterableDoubleIteratorPrototype]",
+ "iterator prototype should have the right brand");
+
+ // Simple dual type iterable creation and functionality test
+ info("IterableDoubleUnion: Testing simple iterable creation and functionality");
+ itr = new TestInterfaceIterableDoubleUnion();
+ testExistence("IterableDoubleUnion: ", itr, base_properties);
+ is(itr.entries, itr[Symbol.iterator],
+ "IterableDoubleUnion: Should be using @@iterator for 'entries'");
+ var elements = [["long", 1], ["string", "a"]]
+ var keys = [...itr.keys()];
+ var values = [...itr.values()];
+ var entries = [...itr.entries()];
+ var key_itr = itr.keys();
+ var value_itr = itr.values();
+ var entries_itr = itr.entries();
+ for (var i = 0; i < elements.length; ++i) {
+ var key = key_itr.next();
+ var value = value_itr.next();
+ var entry = entries_itr.next();
+ is(key.value, elements[i][0], "IterableDoubleUnion: Key iterator value should be " + elements[i][0]);
+ is(key.value, keys[i],
+ "IterableDoubleUnion: Key iterator value should match destructuring " + i);
+ is(value.value, elements[i][1], "IterableDoubleUnion: Value iterator value should be " + elements[i][1]);
+ is(value.value, values[i],
+ "IterableDoubleUnion: Value iterator value should match destructuring " + i);
+ is(entry.value[0], elements[i][0], "IterableDoubleUnion: Entry iterator value 0 should be " + elements[i][0]);
+ is(entry.value[1], elements[i][1], "IterableDoubleUnion: Entry iterator value 1 should be " + elements[i][1]);
+ is(entry.value[0], entries[i][0],
+ "IterableDoubleUnion: Entry iterator value 0 should match destructuring " + i);
+ is(entry.value[1], entries[i][1],
+ "IterableDoubleUnion: Entry iterator value 1 should match destructuring " + i);
+ }
+
+ callsToForEachCallback = 0;
+ thisArg = {};
+ itr.forEach(function(value, key, obj) {
+ is(key, keys[callsToForEachCallback],
+ `IterableDoubleUnion: Should have the right key at ${callsToForEachCallback} calls to forEach callback`);
+ is(value, values[callsToForEachCallback],
+ `IterableDoubleUnion: Should have the right value at ${callsToForEachCallback} calls to forEach callback`);
+ is(this, thisArg,
+ "IterableDoubleUnion: Should have the right this value for forEach callback");
+ is(obj, itr,
+ "IterableSingle: Should have the right third arg for forEach callback");
+ ++callsToForEachCallback;
+ }, thisArg);
+ is(callsToForEachCallback, 2,
+ "IterableDoubleUnion: Should have right total number of calls to forEach callback");
+
+ var key = key_itr.next();
+ var value = value_itr.next();
+ var entry = entries_itr.next()
+ is(key.value, undefined, "IterableDoubleUnion: Key iterator value should be undefined");
+ is(key.done, true, "IterableDoubleUnion: Key iterator done should be true");
+ is(value.value, undefined, "IterableDoubleUnion: Value iterator value should be undefined");
+ is(value.done, true, "IterableDoubleUnion: Value iterator done should be true");
+ is(entry.value, undefined, "IterableDoubleUnion: Entry iterator value should be undefined");
+ is(entry.done, true, "IterableDoubleUnion: Entry iterator done should be true");
+ is(Object.prototype.toString.call(Object.getPrototypeOf(key_itr)),
+ "[object TestInterfaceIterableDoubleUnionIteratorPrototype]",
+ "iterator prototype should have the right brand");
+
+ SimpleTest.finish();
+ });
+ </script>
+ </body>
+</html>
diff --git a/dom/bindings/test/test_jsimplemented_eventhandler.html b/dom/bindings/test/test_jsimplemented_eventhandler.html
new file mode 100644
index 000000000..2854a3112
--- /dev/null
+++ b/dom/bindings/test/test_jsimplemented_eventhandler.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1186696
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1186696</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 1186696 **/
+ SimpleTest.waitForExplicitFinish();
+
+ function doTest() {
+ var values = [ function() {}, 5, null, undefined, "some string", {} ];
+
+ while (values.length != 0) {
+ var value = values.pop();
+ var t = new TestInterfaceJS();
+ t.onsomething = value;
+ var gottenValue = t.onsomething;
+ if (typeof value == "object" || typeof value == "function") {
+ is(gottenValue, value, "Should get back the object-or-null we put in");
+ } else {
+ is(gottenValue, null, "Should get back null");
+ }
+ }
+
+ SimpleTest.finish();
+ }
+
+ SpecialPowers.pushPrefEnv({set: [['dom.expose_test_interfaces', true]]},
+ doTest);
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1186696">Mozilla Bug 1186696</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/bindings/test/test_kill_longrunning_prerendered_content.xul b/dom/bindings/test/test_kill_longrunning_prerendered_content.xul
new file mode 100644
index 000000000..d86b15ad9
--- /dev/null
+++ b/dom/bindings/test/test_kill_longrunning_prerendered_content.xul
@@ -0,0 +1,85 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ onload="runTest();">
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+ SimpleTest.waitForExplicitFinish();
+
+ function Listener(aBrowser, aPrerendered, aCallback) {
+ this.init(aBrowser, aPrerendered, aCallback);
+ }
+
+ Listener.prototype = {
+ init: function(aBrowser, aCallback) {
+ this.mBrowser = aBrowser;
+ this.mCallback = aCallback;
+ },
+ QueryInterface: function(aIID) {
+ if (aIID.equals(Components.interfaces.nsIWebProgressListener) ||
+ aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
+ aIID.equals(Components.interfaces.nsISupports))
+ return this;
+ throw Components.results.NS_NOINTERFACE;
+ },
+ onStateChange : function(aWebProgress, aRequest, aStateFlags, aStatus) {
+ if ((aStateFlags & Components.interfaces.nsIWebProgressListener.STATE_STOP) &&
+ (aStateFlags & Components.interfaces.nsIWebProgressListener.STATE_IS_DOCUMENT)) {
+ setTimeout(this.mCallback, 0);
+ }
+ },
+ onProgressChange : function(aWebProgress, aRequest,
+ aCurSelfProgress, aMaxSelfProgress,
+ aCurTotalProgress, aMaxTotalProgress) {},
+ onLocationChange : function(aWebProgress, aRequest, aLocation, aFlags) {},
+ onStatusChange : function(aWebProgress, aRequest, aStatus, aMessage) {},
+ onSecurityChange : function(aWebProgress, aRequest, aState) {},
+ mBrowser: null,
+ mPrerendered: false,
+ mCallback: null
+ };
+
+ var progress, progressListener;
+
+ function runTest() {
+ SpecialPowers.pushPrefEnv({
+ "set": [
+ ["dom.max_script_run_time", 1]
+ ]
+ }, function() {
+ test(function() {
+ ok("The page is successfully interrupted.");
+ SimpleTest.finish();
+ });
+ });
+ }
+
+ function test(aCallback) {
+ var browser = document.getElementById("prerendered");;
+ progressListener = new Listener(browser, aCallback);
+ var docShell = browser.docShell;
+ progress = docShell.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+ .getInterface(Components.interfaces.nsIWebProgress);
+ progress.addProgressListener(progressListener,
+ Components.interfaces.nsIWebProgress.NOTIFY_ALL);
+ browser.loadURI("data:text/html,<script>;for(;;);</script" + ">");
+ }
+
+]]>
+</script>
+
+<body id="html_body" xmlns="http://www.w3.org/1999/xhtml">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1050456">Mozilla Bug 1050456</a>
+<p id="display"></p>
+
+<pre id="test">
+</pre>
+</body>
+<browser prerendered="true" id="prerendered"/>
+</window>
diff --git a/dom/bindings/test/test_lenientThis.html b/dom/bindings/test/test_lenientThis.html
new file mode 100644
index 000000000..cfbdcebcd
--- /dev/null
+++ b/dom/bindings/test/test_lenientThis.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>[LenientThis]</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+function noop1() { }
+function noop2() { }
+
+test(function() {
+ var desc = Object.getOwnPropertyDescriptor(Document.prototype, "onreadystatechange");
+
+ document.onreadystatechange = noop1;
+ assert_equals(document.onreadystatechange, noop1, "document.onreadystatechange == noop1");
+ assert_equals(desc.get.call({ }), undefined, "document.onreadystatechange getter.call({}) == undefined");
+}, "invoking Document.onreadystatechange's getter with an invalid this object returns undefined");
+
+test(function() {
+ var desc = Object.getOwnPropertyDescriptor(Document.prototype, "onreadystatechange");
+
+ document.onreadystatechange = noop1;
+ assert_equals(document.onreadystatechange, noop1, "document.onreadystatechange == noop1");
+ assert_equals(desc.set.call({ }, noop2), undefined, "document.onreadystatechange setter.call({}) == undefined");
+ assert_equals(document.onreadystatechange, noop1, "document.onreadystatechange == noop1 (still)");
+}, "invoking Document.onreadystatechange's setter with an invalid this object does nothing and returns undefined");
+</script>
diff --git a/dom/bindings/test/test_lookupGetter.html b/dom/bindings/test/test_lookupGetter.html
new file mode 100644
index 000000000..306ee4f64
--- /dev/null
+++ b/dom/bindings/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/dom/bindings/test/test_namedNoIndexed.html b/dom/bindings/test/test_namedNoIndexed.html
new file mode 100644
index 000000000..205ec89f9
--- /dev/null
+++ b/dom/bindings/test/test_namedNoIndexed.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=808991
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 808991</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=808991">Mozilla Bug 808991</a>
+<p id="display"></p>
+<div id="content" style="display: none" data-1="foo" data-bar="baz">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 808991 **/
+is($("content").dataset[1], "foo",
+ "Indexed props should work like named on dataset");
+is($("content").dataset["1"], "foo",
+ "Indexed props as strings should work like named on dataset");
+is($("content").dataset.bar, "baz",
+ "Named props should work on dataset");
+is($("content").dataset['bar'], "baz",
+ "Named props as strings should work on dataset");
+
+
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/bindings/test/test_named_getter_enumerability.html b/dom/bindings/test/test_named_getter_enumerability.html
new file mode 100644
index 000000000..641f78ab2
--- /dev/null
+++ b/dom/bindings/test/test_named_getter_enumerability.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Test for named getter enumerability</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+test(function() {
+ var list = document.getElementsByTagName("div");
+ var desc = Object.getOwnPropertyDescriptor(list, "0");
+ assert_equals(typeof desc, "object", "Should have a '0' property");
+ assert_true(desc.enumerable, "'0' property should be enumerable");
+ desc = Object.getOwnPropertyDescriptor(list, "log");
+ assert_equals(typeof desc, "object", "Should have a 'log' property");
+ assert_false(desc.enumerable, "'log' property should not be enumerable");
+}, "Correct getOwnPropertyDescriptor behavior");
+test(function() {
+ var list = document.getElementsByTagName("div");
+ props = [];
+ for (var prop in list) {
+ props.push(prop);
+ }
+ assert_not_equals(props.indexOf("0"), -1, "Should enumerate '0'");
+ assert_equals(props.indexOf("log"), -1, "Should not enumerate 'log'");
+}, "Correct enumeration behavior");
+test(function() {
+ var list = document.getElementsByTagName("div");
+ props = Object.keys(list)
+ assert_not_equals(props.indexOf("0"), -1, "Keys should contain '0'");
+ assert_equals(props.indexOf("log"), -1, "Keys should not contain 'log'");
+}, "Correct keys() behavior");
+test(function() {
+ var list = document.getElementsByTagName("div");
+ props = Object.getOwnPropertyNames(list)
+ assert_not_equals(props.indexOf("0"), -1,
+ "own prop names should contain '0'");
+ assert_not_equals(props.indexOf("log"), -1,
+ "own prop names should contain 'log'");
+}, "Correct getOwnPropertyNames() behavior");
+</script>
diff --git a/dom/bindings/test/test_oom_reporting.html b/dom/bindings/test/test_oom_reporting.html
new file mode 100644
index 000000000..7323736e5
--- /dev/null
+++ b/dom/bindings/test/test_oom_reporting.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug </title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug **/
+ SimpleTest.waitForExplicitFinish();
+
+ SimpleTest.expectUncaughtException();
+ setTimeout(function() {
+ SpecialPowers.Cu.getJSTestingFunctions().throwOutOfMemory();
+ }, 0);
+
+ addEventListener("error", function(e) {
+ is(e.type, "error", "Should have an error event");
+ is(e.message, "uncaught exception: out of memory",
+ "Should have the right error message");
+ // Make sure we finish async, in case the expectUncaughtException assertion
+ // about having seen the exception runs after our listener
+ SimpleTest.executeSoon(SimpleTest.finish);
+ });
+
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug </a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/bindings/test/test_primitive_this.html b/dom/bindings/test/test_primitive_this.html
new file mode 100644
index 000000000..d2b733dff
--- /dev/null
+++ b/dom/bindings/test/test_primitive_this.html
@@ -0,0 +1,45 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=603201
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 603201</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 603201 **/
+
+ SimpleTest.waitForExplicitFinish();
+ function runTest()
+ {
+ var nodes = document.body.childNodes;
+
+ Object.setPrototypeOf(Number.prototype, nodes);
+
+ Object.defineProperty(nodes, "getter", {get: function() {
+ "use strict";
+ is(this, 1);
+ return "getter";
+ }});
+ Object.defineProperty(Object.getPrototypeOf(nodes), "getter2", {get: function() {
+ "use strict";
+ is(this, 1);
+ return "getter2";
+ }});
+
+ var number = 1;
+ is(number.getter, "getter");
+ is(number.getter2, "getter2");
+
+ SimpleTest.finish();
+ }
+
+ </script>
+</head>
+<body onload="runTest();">
+<pre>Test</pre>
+</body>
+</html>
diff --git a/dom/bindings/test/test_promise_rejections_from_jsimplemented.html b/dom/bindings/test/test_promise_rejections_from_jsimplemented.html
new file mode 100644
index 000000000..5428030c5
--- /dev/null
+++ b/dom/bindings/test/test_promise_rejections_from_jsimplemented.html
@@ -0,0 +1,143 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1107592
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1107592</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 1107592 **/
+
+ SimpleTest.waitForExplicitFinish();
+
+ function checkExn(lineNumber, name, message, code, filename, testNumber, stack, exn) {
+ is(exn.lineNumber, lineNumber,
+ "Should have the right line number in test " + testNumber);
+ is(exn.name, name,
+ "Should have the right exception name in test " + testNumber);
+ is("filename" in exn ? exn.filename : exn.fileName, filename,
+ "Should have the right file name in test " + testNumber);
+ is(exn.message, message,
+ "Should have the right message in test " + testNumber);
+ is(exn.code, code, "Should have the right .code in test " + testNumber);
+ if (message === "") {
+ is(exn.name, "InternalError",
+ "Should have one of our synthetic exceptions in test " + testNumber);
+ }
+ is(exn.stack, stack, "Should have the right stack in test " + testNumber);
+ }
+
+ function ensurePromiseFail(testNumber, value) {
+ ok(false, "Test " + testNumber + " should not have a fulfilled promise");
+ }
+
+ function doTest() {
+ var t = new TestInterfaceJS();
+ /* Async parent frames from pushPrefEnv don't show up in e10s. */
+ var isE10S = !SpecialPowers.isMainProcess();
+ var asyncStack = SpecialPowers.getBoolPref("javascript.options.asyncstack");
+ var ourFile = location.href;
+ var unwrapError = "Promise rejection value is a non-unwrappable cross-compartment wrapper.";
+ var parentFrame = (asyncStack && !isE10S) ? `Async*@${ourFile}:130:3
+` : "";
+
+ Promise.all([
+ t.testPromiseWithThrowingChromePromiseInit().then(
+ ensurePromiseFail.bind(null, 1),
+ checkExn.bind(null, 49, "InternalError", unwrapError,
+ undefined, ourFile, 1,
+ `doTest@${ourFile}:49:7
+` +
+ parentFrame)),
+ t.testPromiseWithThrowingContentPromiseInit(function() {
+ thereIsNoSuchContentFunction1();
+ }).then(
+ ensurePromiseFail.bind(null, 2),
+ checkExn.bind(null, 57, "ReferenceError",
+ "thereIsNoSuchContentFunction1 is not defined",
+ undefined, ourFile, 2,
+ `doTest/<@${ourFile}:57:11
+doTest@${ourFile}:56:7
+` +
+ parentFrame)),
+ t.testPromiseWithThrowingChromeThenFunction().then(
+ ensurePromiseFail.bind(null, 3),
+ checkExn.bind(null, 0, "InternalError", unwrapError, undefined, "", 3, asyncStack ? (`Async*doTest@${ourFile}:67:7
+` +
+ parentFrame) : "")),
+ t.testPromiseWithThrowingContentThenFunction(function() {
+ thereIsNoSuchContentFunction2();
+ }).then(
+ ensurePromiseFail.bind(null, 4),
+ checkExn.bind(null, 73, "ReferenceError",
+ "thereIsNoSuchContentFunction2 is not defined",
+ undefined, ourFile, 4,
+ `doTest/<@${ourFile}:73:11
+` +
+ (asyncStack ? `Async*doTest@${ourFile}:72:7
+` : "") +
+ parentFrame)),
+ t.testPromiseWithThrowingChromeThenable().then(
+ ensurePromiseFail.bind(null, 5),
+ checkExn.bind(null, 0, "InternalError", unwrapError, undefined, "", 5, asyncStack ? (`Async*doTest@${ourFile}:84:7
+` +
+ parentFrame) : "")),
+ t.testPromiseWithThrowingContentThenable({
+ then: function() { thereIsNoSuchContentFunction3(); }
+ }).then(
+ ensurePromiseFail.bind(null, 6),
+ checkExn.bind(null, 90, "ReferenceError",
+ "thereIsNoSuchContentFunction3 is not defined",
+ undefined, ourFile, 6,
+ `doTest/<.then@${ourFile}:90:32
+` + (asyncStack ? `Async*doTest@${ourFile}:89:7\n` + parentFrame : ""))),
+ t.testPromiseWithDOMExceptionThrowingPromiseInit().then(
+ ensurePromiseFail.bind(null, 7),
+ checkExn.bind(null, 98, "NotFoundError",
+ "We are a second DOMException",
+ DOMException.NOT_FOUND_ERR, ourFile, 7,
+ `doTest@${ourFile}:98:7
+` +
+ parentFrame)),
+ t.testPromiseWithDOMExceptionThrowingThenFunction().then(
+ ensurePromiseFail.bind(null, 8),
+ checkExn.bind(null, asyncStack ? 106 : 0, "NetworkError",
+ "We are a third DOMException",
+ DOMException.NETWORK_ERR, asyncStack ? ourFile : "", 8,
+ (asyncStack ? `Async*doTest@${ourFile}:106:7
+` +
+ parentFrame : ""))),
+ t.testPromiseWithDOMExceptionThrowingThenable().then(
+ ensurePromiseFail.bind(null, 9),
+ checkExn.bind(null, asyncStack ? 114 : 0, "TypeMismatchError",
+ "We are a fourth DOMException",
+ DOMException.TYPE_MISMATCH_ERR,
+ asyncStack ? ourFile : "", 9,
+ (asyncStack ? `Async*doTest@${ourFile}:114:7
+` +
+ parentFrame : ""))),
+ ]).then(SimpleTest.finish,
+ function(err) {
+ ok(false, "One of our catch statements totally failed with err" + err + ', stack: ' + (err ? err.stack : ''));
+ SimpleTest.finish();
+ });
+ }
+
+ SpecialPowers.pushPrefEnv({set: [['dom.expose_test_interfaces', true]]},
+ doTest);
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1107592">Mozilla Bug 1107592</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/bindings/test/test_proxies_via_xray.html b/dom/bindings/test/test_proxies_via_xray.html
new file mode 100644
index 000000000..59affe6c0
--- /dev/null
+++ b/dom/bindings/test/test_proxies_via_xray.html
@@ -0,0 +1,99 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1021066
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1021066</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=1021066">Mozilla Bug 1021066</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<iframe id="t" src="http://example.org/tests/dom/bindings/test/file_proxies_via_xray.html"></iframe>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 1021066 **/
+
+function test()
+{
+ "use strict"; // So we'll get exceptions on sets
+ var doc = document.getElementById("t").contentWindow.document;
+ ok(!("x" in doc), "Should have an Xray here");
+ is(doc.x, undefined, "Really should have an Xray here");
+ is(doc.wrappedJSObject.x, 5, "And wrapping the right thing");
+
+ // Test overridebuiltins binding without named setter
+ is(doc.y, doc.getElementById("y"),
+ "Named getter should work on Document");
+ try {
+ doc.z = 5;
+ ok(false, "Should have thrown on set of readonly property on Document");
+ } catch (e) {
+ ok(/read-only/.test(e.message),
+ "Threw the right exception on set of readonly property on Document");
+ }
+
+ doc.w = 5;
+ is(doc.w, 5, "Should be able to set things that are not named props");
+
+ // Test non-overridebuiltins binding without named setter
+ var l = doc.getElementsByTagName("img");
+ is(l.y, doc.getElementById("y"),
+ "Named getter should work on HTMLCollection");
+ try {
+ l.z = 5;
+ ok(false, "Should have thrown on set of readonly property on HTMLCollection");
+ } catch (e) {
+ ok(/read-only/.test(e.message),
+ "Should throw the right exception on set of readonly property on HTMLCollection");
+ }
+ try {
+ l[10] = 5;
+ ok(false, "Should have thrown on set of indexed property on HTMLCollection");
+ } catch (e) {
+ ok(/doesn't have an indexed property setter/.test(e.message),
+ "Should throw the right exception on set of indexed property on HTMLCollection");
+ }
+
+ // Test overridebuiltins binding with named setter
+ var d = doc.documentElement.dataset;
+ d.foo = "bar";
+ // Check that this actually got passed on to the underlying object.
+ is(d.wrappedJSObject.foo, "bar",
+ "Set should get forwarded to the underlying object");
+ is(doc.documentElement.getAttribute("data-foo"), "bar",
+ "Attribute setter should have been called");
+ d.foo = "baz";
+ // Check that this actually got passed on to the underlying object.
+ is(d.wrappedJSObject.foo, "baz",
+ "Set should get forwarded to the underlying object again");
+ is(doc.documentElement.getAttribute("data-foo"), "baz",
+ "Attribute setter should have been called again");
+
+ // Test non-overridebuiltins binding with named setter
+ var s = doc.defaultView.localStorage;
+ s["test_proxies_via_xray"] = "bar";
+ // Check that this actually got passed on to the underlying object.
+ is(s.wrappedJSObject["test_proxies_via_xray"], "bar",
+ "Set should get forwarded to the underlying object without overridebuiltins");
+ s["test_proxies_via_xray"] = "baz";
+ // Check that this actually got passed on to the underlying object.
+ is(s.wrappedJSObject["test_proxies_via_xray"], "baz",
+ "Set should get forwarded to the underlying object again without overridebuiltins");
+
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(test);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/bindings/test/test_queryInterface.html b/dom/bindings/test/test_queryInterface.html
new file mode 100644
index 000000000..076bf9e7d
--- /dev/null
+++ b/dom/bindings/test/test_queryInterface.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=827546
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 827546</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 827546 **/
+
+ var notEditable = document.createElement("div");
+ var thrown;
+ try {
+ thrown = false;
+ SpecialPowers.do_QueryInterface(notEditable, "nsIDOMNSEditableElement");
+ } catch (e) {
+ thrown = true;
+ }
+ ok(thrown,
+ "QI to nsIDOMNSEditableElement on a non-editable element should fail");
+
+ var editable = document.createElement("input");
+ ok(SpecialPowers.do_QueryInterface(editable, "nsIDOMNSEditableElement"),
+ "Editable element needs to support QI to nsIDOMNSEditableElement");
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=827546">Mozilla Bug 827546</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/bindings/test/test_returnUnion.html b/dom/bindings/test/test_returnUnion.html
new file mode 100644
index 000000000..5be10ba3c
--- /dev/null
+++ b/dom/bindings/test/test_returnUnion.html
@@ -0,0 +1,59 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1048659
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1048659</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for returning unions from JS-implemented WebIDL. **/
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({set: [['dom.expose_test_interfaces', true]]}, go);
+
+ function go() {
+ var t = new TestInterfaceJS();
+ var t2 = new TestInterfaceJS();
+
+ is(t.pingPongUnion(t2), t2, "ping pong union for left case should be identity");
+ is(t.pingPongUnion(12), 12, "ping pong union for right case should be identity");
+
+ is(t.pingPongUnionContainingNull("this is not a string"), "this is not a string",
+ "ping pong union containing union for left case should be identity");
+ is(t.pingPongUnionContainingNull(null), null,
+ "ping pong union containing null for right case null should be identity");
+ is(t.pingPongUnionContainingNull(t2), t2,
+ "ping pong union containing null for right case should be identity");
+
+ is(t.pingPongNullableUnion(t2), t2, "ping pong nullable union for left case should be identity");
+ is(t.pingPongNullableUnion(12), 12, "ping pong nullable union for right case should be identity");
+ is(t.pingPongNullableUnion(null), null, "ping pong nullable union for null case should be identity");
+
+ var rejectedBadUnion = false;
+ var result = null;
+ try {
+ result = t.returnBadUnion();
+ } catch (e) {
+ rejectedBadUnion = true;
+ }
+ is(result, null, "bad union should not set a value for result");
+ ok(rejectedBadUnion, "bad union should throw an exception");
+
+ SimpleTest.finish();
+ }
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1048659">Mozilla Bug 1048659</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/bindings/test/test_sequence_detection.html b/dom/bindings/test/test_sequence_detection.html
new file mode 100644
index 000000000..80dfac4db
--- /dev/null
+++ b/dom/bindings/test/test_sequence_detection.html
@@ -0,0 +1,53 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1066432
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1066432</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 1066432 **/
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({set: [['dom.expose_test_interfaces', true]]}, function() {
+ var testInterfaceJS = new TestInterfaceJS();
+ ok(testInterfaceJS, "got a TestInterfaceJS object");
+
+ var nonIterableObject = {[Symbol.iterator]: 5};
+
+ try {
+ testInterfaceJS.testSequenceOverload(nonIterableObject);
+ ok(false, "Should have thrown in the overload case"); // see long comment above!
+ } catch (e) {
+ is(e.name, "TypeError", "Should get a TypeError for the overload case");
+ ok(e.message.includes("not iterable"),
+ "Should have a message about being non-iterable in the overload case");
+ }
+
+ try {
+ testInterfaceJS.testSequenceUnion(nonIterableObject);
+ ok(false, "Should have thrown in the union case");
+ } catch (e) {
+ is(e.name, "TypeError", "Should get a TypeError for the union case");
+ ok(e.message.includes("not iterable"),
+ "Should have a message about being non-iterable in the union case");
+ }
+
+ SimpleTest.finish();
+ });
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1066432">Mozilla Bug 1066432</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/bindings/test/test_sequence_wrapping.html b/dom/bindings/test/test_sequence_wrapping.html
new file mode 100644
index 000000000..7132e5601
--- /dev/null
+++ b/dom/bindings/test/test_sequence_wrapping.html
@@ -0,0 +1,59 @@
+<!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() {
+ var gl = $("c").getContext("experimental-webgl");
+ if (!gl) {
+ // 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/dom/bindings/test/test_setWithNamedGetterNoNamedSetter.html b/dom/bindings/test/test_setWithNamedGetterNoNamedSetter.html
new file mode 100644
index 000000000..52f56151d
--- /dev/null
+++ b/dom/bindings/test/test_setWithNamedGetterNoNamedSetter.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1043690
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1043690</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=1043690">Mozilla Bug 1043690</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<form>
+ <input name="action">
+</form>
+</div>
+ <script type="application/javascript">
+
+ /** Test for Bug 1043690 **/
+ var f = document.querySelector("form");
+ var i = document.querySelector("input");
+ is(f.getAttribute("action"), null, "Should have no action attribute");
+ is(f.action, i, "form.action should be the input");
+ f.action = "http://example.org";
+ is(f.getAttribute("action"), "http://example.org",
+ "Should have an action attribute now");
+ is(f.action, i, "form.action should still be the input");
+ i.remove();
+ is(f.action, "http://example.org/",
+ "form.action should no longer be shadowed");
+
+
+ </script>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/bindings/test/test_stringBindings.html b/dom/bindings/test/test_stringBindings.html
new file mode 100644
index 000000000..1895b0342
--- /dev/null
+++ b/dom/bindings/test/test_stringBindings.html
@@ -0,0 +1,59 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1334537
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1334537</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 1334537 **/
+ SimpleTest.waitForExplicitFinish();
+
+ function go() {
+ // Need a new global that will pick up our pref.
+ var ifr = document.createElement("iframe");
+ document.body.appendChild(ifr);
+
+ var t = new ifr.contentWindow.TestFunctions();
+ var testString = "abcdefghijklmnopqrstuvwxyz";
+ const substringLength = 10;
+ var shortTestString = testString.substring(0, substringLength);
+
+ t.setStringData(testString);
+ // Note: we want to do all our gets before we start running code we don't
+ // control inside the test harness, if we really want to exercise the string
+ // cache in controlled ways.
+
+ var asShortDOMString = t.getStringDataAsDOMString(substringLength);
+ var asFullDOMString = t.getStringDataAsDOMString();
+ var asShortAString = t.getStringDataAsAString(substringLength);
+ var asAString = t.getStringDataAsAString();
+
+ is(asShortAString, shortTestString, "Short DOMString should be short");
+ is(asFullDOMString, testString, "Full DOMString should be test string");
+ is(asShortAString, shortTestString, "Short AString should be short");
+ is(asAString, testString, "Full AString should be test string");
+
+ SimpleTest.finish();
+ }
+
+ addLoadEvent(function() {
+ SpecialPowers.pushPrefEnv({set: [['dom.expose_test_interfaces', true]]},
+ go);
+ });
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1334537">Mozilla Bug 1334537</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/bindings/test/test_throwing_method_noDCE.html b/dom/bindings/test/test_throwing_method_noDCE.html
new file mode 100644
index 000000000..e952819a8
--- /dev/null
+++ b/dom/bindings/test/test_throwing_method_noDCE.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Test that we don't DCE functions that can throw</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+test(function() {
+ function test(root) {
+ var threw = false;
+ try {
+ root.querySelectorAll("");
+ } catch(e){ threw = true; };
+ // Hot loop to make sure the JIT heuristics ion-compile this function even
+ // though it's throwing exceptions (which would normally make us back off
+ // of ion compilation).
+ for (var i=0; i<1500; i++) {}
+ return threw;
+ }
+
+ var threw = false;
+ var el = document.createElement("div");
+ for (var i=0; i<200; i++)
+ threw = test(el);
+ assert_true(threw);
+}, "Shouldn't optimize away throwing functions");
+</script>
diff --git a/dom/bindings/test/test_traceProtos.html b/dom/bindings/test/test_traceProtos.html
new file mode 100644
index 000000000..17a5cb96d
--- /dev/null
+++ b/dom/bindings/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(callback);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/bindings/test/test_treat_non_object_as_null.html b/dom/bindings/test/test_treat_non_object_as_null.html
new file mode 100644
index 000000000..fbb6ceb66
--- /dev/null
+++ b/dom/bindings/test/test_treat_non_object_as_null.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=952365
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 952365</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 952365 **/
+
+ var onvolumechange;
+ var x = {};
+
+ (function() {
+ onvolumechange = x;
+ is(onvolumechange, x,
+ "Should preserve an object value when assigning to event handler");
+ // Test that we don't try to actually call the non-callable object
+ window.dispatchEvent(new Event("volumechange"));
+ onvolumechange = 5;
+ is(onvolumechange, null,
+ "Non-object values should become null when assigning to event handler");
+ })();
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=952365">Mozilla Bug 952365</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/bindings/test/test_unforgeablesonexpando.html b/dom/bindings/test/test_unforgeablesonexpando.html
new file mode 100644
index 000000000..419e6ac7d
--- /dev/null
+++ b/dom/bindings/test/test_unforgeablesonexpando.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Test for making sure named getters don't override the unforgeable location on HTMLDocument</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<img name="location">
+<script>
+test(function() {
+ assert_equals(document.location, window.location,
+ 'The <img name="location"> should not override the location getter');
+}, "document.location is the right thing");
+test(function() {
+ var doc = new DOMParser().parseFromString("<img name='location'>", "text/html");
+ assert_equals(doc.location, null,
+ 'The <img name="location"> should not override the location getter on a data document');
+}, "document.location is the right thing on non-rendered document");
+</script>
diff --git a/dom/bindings/test/test_usvstring.html b/dom/bindings/test/test_usvstring.html
new file mode 100644
index 000000000..cbb1e7971
--- /dev/null
+++ b/dom/bindings/test/test_usvstring.html
@@ -0,0 +1,41 @@
+<!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test USVString</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<script class="testbody" type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv({set: [['dom.expose_test_interfaces', true]]}, function() {
+ var testInterfaceJS = new TestInterfaceJS();
+ ok(testInterfaceJS, "got a TestInterfaceJS object");
+ // For expected values, see algorithm definition here:
+ // http://heycam.github.io/webidl/#dfn-obtain-unicode
+ var testList = [
+ { string: "foo",
+ expected: "foo" },
+ { string: "This is U+2070E: \ud841\udf0e",
+ expected: "This is U+2070E: \ud841\udf0e" },
+ { string: "Missing low surrogate: \ud841",
+ expected: "Missing low surrogate: \ufffd" },
+ { string: "Missing low surrogate with trailer: \ud841!!",
+ expected: "Missing low surrogate with trailer: \ufffd!!" },
+ { string: "Missing high surrogate: \udf0e",
+ expected: "Missing high surrogate: \ufffd" },
+ { string: "Missing high surrogate with trailer: \udf0e!!",
+ expected: "Missing high surrogate with trailer: \ufffd!!" },
+ { string: "U+2070E after malformed: \udf0e\ud841\udf0e",
+ expected: "U+2070E after malformed: \ufffd\ud841\udf0e" }
+ ];
+ testList.forEach(function(test) {
+ is(testInterfaceJS.convertSVS(test.string), test.expected, "Convert '" + test.string + "'");
+ });
+ SimpleTest.finish();
+});
+</script>
+</body>
+</html>
diff --git a/dom/bindings/test/test_worker_UnwrapArg.html b/dom/bindings/test/test_worker_UnwrapArg.html
new file mode 100644
index 000000000..1331a83f4
--- /dev/null
+++ b/dom/bindings/test/test_worker_UnwrapArg.html
@@ -0,0 +1,58 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1127206
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1127206</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 1127206 **/
+ SimpleTest.waitForExplicitFinish();
+ var blob = new Blob([
+ `try { new File({}); }
+ catch (e) {
+ postMessage("throwing on random object");
+ }
+ try { new File(new Blob(["abc"])); }
+ catch (e) {
+ postMessage("throwing on Blob");
+ }
+ try { new File("abc"); }
+ catch (e) {
+ postMessage("throwing on string");
+ }
+ postMessage('finishTest')`]);
+ var url = URL.createObjectURL(blob);
+ var w = new Worker(url);
+ var expectedResults = [
+ "throwing on random object",
+ "throwing on Blob",
+ "throwing on string",
+ ];
+ var curIndex = 0;
+ w.onmessage = function(e) {
+ if (curIndex == expectedResults.length) {
+ is(e.data, "finishTest", "What message is this?");
+ SimpleTest.finish();
+ } else {
+ is(e.data, expectedResults[curIndex],
+ "Message " + (curIndex+1) + " should be correct");
+ ++curIndex;
+ }
+ }
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1127206">Mozilla Bug 1127206</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>