diff options
Diffstat (limited to 'dom/base/nsDOMClassInfo.cpp')
-rw-r--r-- | dom/base/nsDOMClassInfo.cpp | 2179 |
1 files changed, 2179 insertions, 0 deletions
diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp new file mode 100644 index 000000000..d125e5ad1 --- /dev/null +++ b/dom/base/nsDOMClassInfo.cpp @@ -0,0 +1,2179 @@ +/* -*- 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/ArrayUtils.h" + +#ifdef XP_WIN +#undef GetClassName +#endif + +// JavaScript includes +#include "jsapi.h" +#include "jsfriendapi.h" +#include "WrapperFactory.h" +#include "AccessCheck.h" +#include "XrayWrapper.h" + +#include "xpcpublic.h" +#include "xpcprivate.h" +#include "xpc_make_class.h" +#include "XPCWrapper.h" + +#include "mozilla/DOMEventTargetHelper.h" +#include "mozilla/dom/RegisterBindings.h" + +#include "nscore.h" +#include "nsDOMClassInfo.h" +#include "nsIDOMClassInfo.h" +#include "nsCRT.h" +#include "nsCRTGlue.h" +#include "nsICategoryManager.h" +#include "nsIComponentRegistrar.h" +#include "nsXPCOM.h" +#include "nsISimpleEnumerator.h" +#include "nsISupportsPrimitives.h" +#include "nsIXPConnect.h" +#include "xptcall.h" +#include "nsTArray.h" + +// General helper includes +#include "nsGlobalWindow.h" +#include "nsIContent.h" +#include "nsIDocument.h" +#include "nsIDOMDocument.h" +#include "nsIDOMEvent.h" +#include "nsIDOMEventListener.h" +#include "nsContentUtils.h" +#include "nsIDOMGlobalPropertyInitializer.h" +#include "mozilla/Attributes.h" +#include "mozilla/Telemetry.h" + +// Window scriptable helper includes +#include "nsScriptNameSpaceManager.h" + +// DOM base includes +#include "nsIDOMWindow.h" +#include "nsPIDOMWindow.h" +#include "nsIDOMConstructor.h" + +// DOM core includes +#include "nsError.h" +#include "nsIDOMXULButtonElement.h" +#include "nsIDOMXULCheckboxElement.h" +#include "nsIDOMXULPopupElement.h" + +// Event related includes +#include "nsIDOMEventTarget.h" + +// CSS related includes +#include "nsCSSRules.h" +#include "nsIDOMCSSRule.h" +#include "nsMemory.h" + +// includes needed for the prototype chain interfaces +#include "nsIDOMCSSKeyframeRule.h" +#include "nsIDOMCSSKeyframesRule.h" +#include "nsIDOMCSSImportRule.h" +#include "nsIDOMCSSMediaRule.h" +#include "nsIDOMCSSFontFaceRule.h" +#include "nsIDOMCSSMozDocumentRule.h" +#include "nsIDOMCSSSupportsRule.h" +#include "nsIDOMCSSCounterStyleRule.h" +#include "nsIDOMCSSPageRule.h" +#include "nsIDOMCSSStyleRule.h" +#include "nsIDOMXULCommandDispatcher.h" +#include "nsIControllers.h" +#ifdef MOZ_XUL +#include "nsITreeSelection.h" +#include "nsITreeContentView.h" +#include "nsITreeView.h" +#include "nsIXULTemplateBuilder.h" +#endif + +#include "nsIEventListenerService.h" +#include "nsIMessageManager.h" + +#include "mozilla/dom/TouchEvent.h" + +#include "nsWrapperCacheInlines.h" +#include "mozilla/dom/HTMLCollectionBinding.h" + +#include "nsDebug.h" + +#include "mozilla/dom/BindingUtils.h" +#include "mozilla/Likely.h" +#include "nsIInterfaceInfoManager.h" + +#ifdef MOZ_TIME_MANAGER +#include "TimeManager.h" +#endif + +using namespace mozilla; +using namespace mozilla::dom; + +// NOTE: DEFAULT_SCRIPTABLE_FLAGS and DOM_DEFAULT_SCRIPTABLE_FLAGS +// are defined in nsIDOMClassInfo.h. + +#define DOMCLASSINFO_STANDARD_FLAGS \ + (nsIClassInfo::MAIN_THREAD_ONLY | \ + nsIClassInfo::DOM_OBJECT | \ + nsIClassInfo::SINGLETON_CLASSINFO) + + +#ifdef DEBUG +#define NS_DEFINE_CLASSINFO_DATA_DEBUG(_class) \ + eDOMClassInfo_##_class##_id, +#else +#define NS_DEFINE_CLASSINFO_DATA_DEBUG(_class) \ + // nothing +#endif + +#define NS_DEFINE_CLASSINFO_DATA_HELPER(_class, _helper, _flags, \ + _chromeOnly, _allowXBL) \ + { #_class, \ + nullptr, \ + XPC_MAKE_CLASS_OPS(_flags), \ + XPC_MAKE_CLASS(#_class, _flags, \ + &sClassInfoData[eDOMClassInfo_##_class##_id].mClassOps), \ + _helper::doCreate, \ + nullptr, \ + nullptr, \ + nullptr, \ + _flags, \ + true, \ + _chromeOnly, \ + _allowXBL, \ + false, \ + NS_DEFINE_CLASSINFO_DATA_DEBUG(_class) \ + }, + +#define NS_DEFINE_CLASSINFO_DATA(_class, _helper, _flags) \ + NS_DEFINE_CLASSINFO_DATA_HELPER(_class, _helper, _flags, false, false) + +#define NS_DEFINE_CHROME_ONLY_CLASSINFO_DATA(_class, _helper, _flags) \ + NS_DEFINE_CLASSINFO_DATA_HELPER(_class, _helper, _flags, true, false) + +#define NS_DEFINE_CHROME_XBL_CLASSINFO_DATA(_class, _helper, _flags) \ + NS_DEFINE_CLASSINFO_DATA_HELPER(_class, _helper, _flags, true, true) + + +// This list of NS_DEFINE_CLASSINFO_DATA macros is what gives the DOM +// classes their correct behavior when used through XPConnect. The +// arguments that are passed to NS_DEFINE_CLASSINFO_DATA are +// +// 1. Class name as it should appear in JavaScript, this name is also +// used to find the id of the class in nsDOMClassInfo +// (i.e. e<classname>_id) +// 2. Scriptable helper class +// 3. nsIClassInfo/nsIXPCScriptable flags (i.e. for GetScriptableFlags) + +static nsDOMClassInfoData sClassInfoData[] = { + // Base classes + + NS_DEFINE_CLASSINFO_DATA(DOMPrototype, nsDOMConstructorSH, + DOM_BASE_SCRIPTABLE_FLAGS | + nsIXPCScriptable::WANT_PRECREATE | + nsIXPCScriptable::WANT_RESOLVE | + nsIXPCScriptable::WANT_HASINSTANCE | + nsIXPCScriptable::DONT_ENUM_QUERY_INTERFACE) + NS_DEFINE_CLASSINFO_DATA(DOMConstructor, nsDOMConstructorSH, + DOM_BASE_SCRIPTABLE_FLAGS | + nsIXPCScriptable::WANT_PRECREATE | + nsIXPCScriptable::WANT_RESOLVE | + nsIXPCScriptable::WANT_HASINSTANCE | + nsIXPCScriptable::WANT_CALL | + nsIXPCScriptable::WANT_CONSTRUCT | + nsIXPCScriptable::DONT_ENUM_QUERY_INTERFACE) + + // Misc Core related classes + + // CSS classes + NS_DEFINE_CLASSINFO_DATA(CSSStyleRule, nsDOMGenericSH, + DOM_DEFAULT_SCRIPTABLE_FLAGS) + NS_DEFINE_CLASSINFO_DATA(CSSImportRule, nsDOMGenericSH, + DOM_DEFAULT_SCRIPTABLE_FLAGS) + NS_DEFINE_CLASSINFO_DATA(CSSMediaRule, nsDOMGenericSH, + DOM_DEFAULT_SCRIPTABLE_FLAGS) + NS_DEFINE_CLASSINFO_DATA(CSSNameSpaceRule, nsDOMGenericSH, + DOM_DEFAULT_SCRIPTABLE_FLAGS) + + // XUL classes +#ifdef MOZ_XUL + NS_DEFINE_CHROME_XBL_CLASSINFO_DATA(XULCommandDispatcher, nsDOMGenericSH, + DOM_DEFAULT_SCRIPTABLE_FLAGS) +#endif + NS_DEFINE_CHROME_XBL_CLASSINFO_DATA(XULControllers, nsNonDOMObjectSH, + DEFAULT_SCRIPTABLE_FLAGS) +#ifdef MOZ_XUL + NS_DEFINE_CHROME_XBL_CLASSINFO_DATA(TreeSelection, nsDOMGenericSH, + DEFAULT_SCRIPTABLE_FLAGS) + NS_DEFINE_CHROME_XBL_CLASSINFO_DATA(TreeContentView, nsDOMGenericSH, + DEFAULT_SCRIPTABLE_FLAGS) +#endif + +#ifdef MOZ_XUL + NS_DEFINE_CHROME_XBL_CLASSINFO_DATA(XULTemplateBuilder, nsDOMGenericSH, + DEFAULT_SCRIPTABLE_FLAGS) + + NS_DEFINE_CHROME_XBL_CLASSINFO_DATA(XULTreeBuilder, nsDOMGenericSH, + DEFAULT_SCRIPTABLE_FLAGS) +#endif + + NS_DEFINE_CLASSINFO_DATA(CSSMozDocumentRule, nsDOMGenericSH, + DOM_DEFAULT_SCRIPTABLE_FLAGS) + + NS_DEFINE_CLASSINFO_DATA(CSSSupportsRule, nsDOMGenericSH, + DOM_DEFAULT_SCRIPTABLE_FLAGS) + + NS_DEFINE_CLASSINFO_DATA(CSSFontFaceRule, nsDOMGenericSH, + DOM_DEFAULT_SCRIPTABLE_FLAGS) + + NS_DEFINE_CHROME_ONLY_CLASSINFO_DATA(ContentFrameMessageManager, + nsMessageManagerSH<nsEventTargetSH>, + DOM_DEFAULT_SCRIPTABLE_FLAGS | + nsIXPCScriptable::WANT_ENUMERATE | + nsIXPCScriptable::IS_GLOBAL_OBJECT) + NS_DEFINE_CHROME_ONLY_CLASSINFO_DATA(ContentProcessMessageManager, + nsMessageManagerSH<nsDOMGenericSH>, + DOM_DEFAULT_SCRIPTABLE_FLAGS | + nsIXPCScriptable::WANT_ENUMERATE | + nsIXPCScriptable::IS_GLOBAL_OBJECT) + NS_DEFINE_CHROME_ONLY_CLASSINFO_DATA(ChromeMessageBroadcaster, nsDOMGenericSH, + DOM_DEFAULT_SCRIPTABLE_FLAGS) + NS_DEFINE_CHROME_ONLY_CLASSINFO_DATA(ChromeMessageSender, nsDOMGenericSH, + DOM_DEFAULT_SCRIPTABLE_FLAGS) + + + NS_DEFINE_CLASSINFO_DATA(CSSKeyframeRule, nsDOMGenericSH, + DOM_DEFAULT_SCRIPTABLE_FLAGS) + NS_DEFINE_CLASSINFO_DATA(CSSKeyframesRule, nsDOMGenericSH, + DOM_DEFAULT_SCRIPTABLE_FLAGS) + + NS_DEFINE_CLASSINFO_DATA(CSSCounterStyleRule, nsDOMGenericSH, + DOM_DEFAULT_SCRIPTABLE_FLAGS) + + NS_DEFINE_CLASSINFO_DATA(CSSPageRule, nsDOMGenericSH, + DOM_DEFAULT_SCRIPTABLE_FLAGS) + + NS_DEFINE_CLASSINFO_DATA(CSSFontFeatureValuesRule, nsDOMGenericSH, + DOM_DEFAULT_SCRIPTABLE_FLAGS) + + NS_DEFINE_CHROME_XBL_CLASSINFO_DATA(XULControlElement, nsDOMGenericSH, + DOM_DEFAULT_SCRIPTABLE_FLAGS) + NS_DEFINE_CHROME_XBL_CLASSINFO_DATA(XULLabeledControlElement, nsDOMGenericSH, + DOM_DEFAULT_SCRIPTABLE_FLAGS) + NS_DEFINE_CHROME_XBL_CLASSINFO_DATA(XULButtonElement, nsDOMGenericSH, + DOM_DEFAULT_SCRIPTABLE_FLAGS) + NS_DEFINE_CHROME_XBL_CLASSINFO_DATA(XULCheckboxElement, nsDOMGenericSH, + DOM_DEFAULT_SCRIPTABLE_FLAGS) + NS_DEFINE_CHROME_XBL_CLASSINFO_DATA(XULPopupElement, nsDOMGenericSH, + DOM_DEFAULT_SCRIPTABLE_FLAGS) +}; + +nsIXPConnect *nsDOMClassInfo::sXPConnect = nullptr; +bool nsDOMClassInfo::sIsInitialized = false; + + +jsid nsDOMClassInfo::sConstructor_id = JSID_VOID; +jsid nsDOMClassInfo::sWrappedJSObject_id = JSID_VOID; + +static const JSClass *sObjectClass = nullptr; + +/** + * Set our JSClass pointer for the Object class + */ +static void +FindObjectClass(JSContext* cx, JSObject* aGlobalObject) +{ + NS_ASSERTION(!sObjectClass, + "Double set of sObjectClass"); + JS::Rooted<JSObject*> obj(cx), proto(cx, aGlobalObject); + do { + obj = proto; + js::GetObjectProto(cx, obj, &proto); + } while (proto); + + sObjectClass = js::GetObjectJSClass(obj); +} + +// Helper to handle torn-down inner windows. +static inline nsresult +SetParentToWindow(nsGlobalWindow *win, JSObject **parent) +{ + MOZ_ASSERT(win); + MOZ_ASSERT(win->IsInnerWindow()); + *parent = win->FastGetGlobalJSObject(); + + if (MOZ_UNLIKELY(!*parent)) { + // The inner window has been torn down. The scope is dying, so don't create + // any new wrappers. + return NS_ERROR_FAILURE; + } + return NS_OK; +} + +// static + +nsISupports * +nsDOMClassInfo::GetNative(nsIXPConnectWrappedNative *wrapper, JSObject *obj) +{ + return wrapper ? wrapper->Native() : static_cast<nsISupports*>(js::GetObjectPrivate(obj)); +} + +nsresult +nsDOMClassInfo::DefineStaticJSVals() +{ + AutoJSAPI jsapi; + if (!jsapi.Init(xpc::UnprivilegedJunkScope())) { + return NS_ERROR_UNEXPECTED; + } + JSContext* cx = jsapi.cx(); + +#define SET_JSID_TO_STRING(_id, _cx, _str) \ + if (JSString *str = ::JS_AtomizeAndPinString(_cx, _str)) \ + _id = INTERNED_STRING_TO_JSID(_cx, str); \ + else \ + return NS_ERROR_OUT_OF_MEMORY; + + SET_JSID_TO_STRING(sConstructor_id, cx, "constructor"); + SET_JSID_TO_STRING(sWrappedJSObject_id, cx, "wrappedJSObject"); + + return NS_OK; +} + +// static +bool +nsDOMClassInfo::ObjectIsNativeWrapper(JSContext* cx, JSObject* obj) +{ + return xpc::WrapperFactory::IsXrayWrapper(obj) && + xpc::AccessCheck::wrapperSubsumes(obj); +} + +nsDOMClassInfo::nsDOMClassInfo(nsDOMClassInfoData* aData) : mData(aData) +{ +} + +NS_IMPL_ADDREF(nsDOMClassInfo) +NS_IMPL_RELEASE(nsDOMClassInfo) + +NS_INTERFACE_MAP_BEGIN(nsDOMClassInfo) + if (aIID.Equals(NS_GET_IID(nsXPCClassInfo))) + foundInterface = static_cast<nsIClassInfo*>( + static_cast<nsXPCClassInfo*>(this)); + else + NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable) + NS_INTERFACE_MAP_ENTRY(nsIClassInfo) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIClassInfo) +NS_INTERFACE_MAP_END + + +static const JSClass sDOMConstructorProtoClass = { + "DOM Constructor.prototype", 0 +}; + + +static const char * +CutPrefix(const char *aName) { + static const char prefix_nsIDOM[] = "nsIDOM"; + static const char prefix_nsI[] = "nsI"; + + if (strncmp(aName, prefix_nsIDOM, sizeof(prefix_nsIDOM) - 1) == 0) { + return aName + sizeof(prefix_nsIDOM) - 1; + } + + if (strncmp(aName, prefix_nsI, sizeof(prefix_nsI) - 1) == 0) { + return aName + sizeof(prefix_nsI) - 1; + } + + return aName; +} + +// static +nsresult +nsDOMClassInfo::RegisterClassProtos(int32_t aClassInfoID) +{ + nsScriptNameSpaceManager *nameSpaceManager = GetNameSpaceManager(); + NS_ENSURE_TRUE(nameSpaceManager, NS_ERROR_NOT_INITIALIZED); + bool found_old; + + const nsIID *primary_iid = sClassInfoData[aClassInfoID].mProtoChainInterface; + + if (!primary_iid || primary_iid == &NS_GET_IID(nsISupports)) { + return NS_OK; + } + + nsCOMPtr<nsIInterfaceInfoManager> + iim(do_GetService(NS_INTERFACEINFOMANAGER_SERVICE_CONTRACTID)); + NS_ENSURE_TRUE(iim, NS_ERROR_NOT_AVAILABLE); + + nsCOMPtr<nsIInterfaceInfo> if_info; + bool first = true; + + iim->GetInfoForIID(primary_iid, getter_AddRefs(if_info)); + + while (if_info) { + const nsIID *iid = nullptr; + + if_info->GetIIDShared(&iid); + NS_ENSURE_TRUE(iid, NS_ERROR_UNEXPECTED); + + if (iid->Equals(NS_GET_IID(nsISupports))) { + break; + } + + const char *name = nullptr; + if_info->GetNameShared(&name); + NS_ENSURE_TRUE(name, NS_ERROR_UNEXPECTED); + + nameSpaceManager->RegisterClassProto(CutPrefix(name), iid, &found_old); + + if (first) { + first = false; + } else if (found_old) { + break; + } + + nsCOMPtr<nsIInterfaceInfo> tmp(if_info); + tmp->GetParent(getter_AddRefs(if_info)); + } + + return NS_OK; +} + +#define _DOM_CLASSINFO_MAP_BEGIN(_class, _ifptr, _has_class_if) \ + { \ + nsDOMClassInfoData &d = sClassInfoData[eDOMClassInfo_##_class##_id]; \ + d.mProtoChainInterface = _ifptr; \ + d.mHasClassInterface = _has_class_if; \ + static const nsIID *interface_list[] = { + +#define DOM_CLASSINFO_MAP_BEGIN(_class, _interface) \ + _DOM_CLASSINFO_MAP_BEGIN(_class, &NS_GET_IID(_interface), true) + +#define DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(_class, _interface) \ + _DOM_CLASSINFO_MAP_BEGIN(_class, &NS_GET_IID(_interface), false) + +#define DOM_CLASSINFO_MAP_ENTRY(_if) \ + &NS_GET_IID(_if), + +#define DOM_CLASSINFO_MAP_CONDITIONAL_ENTRY(_if, _cond) \ + (_cond) ? &NS_GET_IID(_if) : nullptr, + +#define DOM_CLASSINFO_MAP_END \ + nullptr \ + }; \ + \ + /* Compact the interface list */ \ + size_t count = ArrayLength(interface_list); \ + /* count is the number of array entries, which is one greater than the */ \ + /* number of interfaces due to the terminating null */ \ + for (size_t i = 0; i < count - 1; ++i) { \ + if (!interface_list[i]) { \ + /* We are moving the element at index i+1 and successors, */ \ + /* so we must move only count - (i+1) elements total. */ \ + memmove(&interface_list[i], &interface_list[i+1], \ + sizeof(nsIID*) * (count - (i+1))); \ + /* Make sure to examine the new pointer we ended up with at this */ \ + /* slot, since it may be null too */ \ + --i; \ + --count; \ + } \ + } \ + \ + d.mInterfaces = interface_list; \ + } + +nsresult +nsDOMClassInfo::Init() +{ + /* Errors that can trigger early returns are done first, + otherwise nsDOMClassInfo is left in a half inited state. */ + static_assert(sizeof(uintptr_t) == sizeof(void*), + "BAD! You'll need to adjust the size of uintptr_t to the " + "size of a pointer on your platform."); + + NS_ENSURE_TRUE(!sIsInitialized, NS_ERROR_ALREADY_INITIALIZED); + + nsScriptNameSpaceManager *nameSpaceManager = GetNameSpaceManager(); + NS_ENSURE_TRUE(nameSpaceManager, NS_ERROR_NOT_INITIALIZED); + + NS_ADDREF(sXPConnect = nsContentUtils::XPConnect()); + + nsCOMPtr<nsIXPCFunctionThisTranslator> elt = new nsEventListenerThisTranslator(); + sXPConnect->SetFunctionThisTranslator(NS_GET_IID(nsIDOMEventListener), elt); + + DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(DOMPrototype, nsIDOMDOMConstructor) + DOM_CLASSINFO_MAP_ENTRY(nsIDOMDOMConstructor) + DOM_CLASSINFO_MAP_END + + DOM_CLASSINFO_MAP_BEGIN(DOMConstructor, nsIDOMDOMConstructor) + DOM_CLASSINFO_MAP_ENTRY(nsIDOMDOMConstructor) + DOM_CLASSINFO_MAP_END + + DOM_CLASSINFO_MAP_BEGIN(CSSStyleRule, nsIDOMCSSStyleRule) + DOM_CLASSINFO_MAP_ENTRY(nsIDOMCSSStyleRule) + DOM_CLASSINFO_MAP_END + + DOM_CLASSINFO_MAP_BEGIN(CSSImportRule, nsIDOMCSSImportRule) + DOM_CLASSINFO_MAP_ENTRY(nsIDOMCSSImportRule) + DOM_CLASSINFO_MAP_END + + DOM_CLASSINFO_MAP_BEGIN(CSSMediaRule, nsIDOMCSSMediaRule) + DOM_CLASSINFO_MAP_ENTRY(nsIDOMCSSMediaRule) + DOM_CLASSINFO_MAP_END + + DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(CSSNameSpaceRule, nsIDOMCSSRule) + DOM_CLASSINFO_MAP_ENTRY(nsIDOMCSSRule) + DOM_CLASSINFO_MAP_END + +#ifdef MOZ_XUL + DOM_CLASSINFO_MAP_BEGIN(XULCommandDispatcher, nsIDOMXULCommandDispatcher) + DOM_CLASSINFO_MAP_ENTRY(nsIDOMXULCommandDispatcher) + DOM_CLASSINFO_MAP_END +#endif + + DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(XULControllers, nsIControllers) + DOM_CLASSINFO_MAP_ENTRY(nsIControllers) + DOM_CLASSINFO_MAP_END + +#ifdef MOZ_XUL + DOM_CLASSINFO_MAP_BEGIN(TreeSelection, nsITreeSelection) + DOM_CLASSINFO_MAP_ENTRY(nsITreeSelection) + DOM_CLASSINFO_MAP_END + + DOM_CLASSINFO_MAP_BEGIN(TreeContentView, nsITreeContentView) + DOM_CLASSINFO_MAP_ENTRY(nsITreeContentView) + DOM_CLASSINFO_MAP_ENTRY(nsITreeView) + DOM_CLASSINFO_MAP_END +#endif + +#ifdef MOZ_XUL + DOM_CLASSINFO_MAP_BEGIN(XULTemplateBuilder, nsIXULTemplateBuilder) + DOM_CLASSINFO_MAP_ENTRY(nsIXULTemplateBuilder) + DOM_CLASSINFO_MAP_END + + DOM_CLASSINFO_MAP_BEGIN(XULTreeBuilder, nsIXULTreeBuilder) + DOM_CLASSINFO_MAP_ENTRY(nsIXULTreeBuilder) + DOM_CLASSINFO_MAP_ENTRY(nsIXULTemplateBuilder) + DOM_CLASSINFO_MAP_ENTRY(nsITreeView) + DOM_CLASSINFO_MAP_END +#endif + + DOM_CLASSINFO_MAP_BEGIN(CSSMozDocumentRule, nsIDOMCSSMozDocumentRule) + DOM_CLASSINFO_MAP_ENTRY(nsIDOMCSSMozDocumentRule) + DOM_CLASSINFO_MAP_END + + DOM_CLASSINFO_MAP_BEGIN(CSSSupportsRule, nsIDOMCSSSupportsRule) + DOM_CLASSINFO_MAP_ENTRY(nsIDOMCSSSupportsRule) + DOM_CLASSINFO_MAP_END + + DOM_CLASSINFO_MAP_BEGIN(CSSFontFaceRule, nsIDOMCSSFontFaceRule) + DOM_CLASSINFO_MAP_ENTRY(nsIDOMCSSFontFaceRule) + DOM_CLASSINFO_MAP_END + + DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(ContentFrameMessageManager, nsISupports) + DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget) + DOM_CLASSINFO_MAP_ENTRY(nsIMessageListenerManager) + DOM_CLASSINFO_MAP_ENTRY(nsIMessageSender) + DOM_CLASSINFO_MAP_ENTRY(nsISyncMessageSender) + DOM_CLASSINFO_MAP_ENTRY(nsIContentFrameMessageManager) + DOM_CLASSINFO_MAP_END + + DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(ContentProcessMessageManager, nsISupports) + DOM_CLASSINFO_MAP_ENTRY(nsIMessageListenerManager) + DOM_CLASSINFO_MAP_ENTRY(nsIMessageSender) + DOM_CLASSINFO_MAP_ENTRY(nsISyncMessageSender) + DOM_CLASSINFO_MAP_ENTRY(nsIContentProcessMessageManager) + DOM_CLASSINFO_MAP_END + + DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(ChromeMessageBroadcaster, nsISupports) + DOM_CLASSINFO_MAP_ENTRY(nsIFrameScriptLoader) + DOM_CLASSINFO_MAP_ENTRY(nsIProcessScriptLoader) + DOM_CLASSINFO_MAP_ENTRY(nsIGlobalProcessScriptLoader) + DOM_CLASSINFO_MAP_ENTRY(nsIMessageListenerManager) + DOM_CLASSINFO_MAP_ENTRY(nsIMessageBroadcaster) + DOM_CLASSINFO_MAP_END + + DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(ChromeMessageSender, nsISupports) + DOM_CLASSINFO_MAP_ENTRY(nsIProcessChecker) + DOM_CLASSINFO_MAP_ENTRY(nsIFrameScriptLoader) + DOM_CLASSINFO_MAP_ENTRY(nsIProcessScriptLoader) + DOM_CLASSINFO_MAP_ENTRY(nsIMessageListenerManager) + DOM_CLASSINFO_MAP_ENTRY(nsIMessageSender) + DOM_CLASSINFO_MAP_END + + DOM_CLASSINFO_MAP_BEGIN(CSSKeyframeRule, nsIDOMCSSKeyframeRule) + DOM_CLASSINFO_MAP_ENTRY(nsIDOMCSSKeyframeRule) + DOM_CLASSINFO_MAP_END + + DOM_CLASSINFO_MAP_BEGIN(CSSKeyframesRule, nsIDOMCSSKeyframesRule) + DOM_CLASSINFO_MAP_ENTRY(nsIDOMCSSKeyframesRule) + DOM_CLASSINFO_MAP_END + + DOM_CLASSINFO_MAP_BEGIN(CSSCounterStyleRule, nsIDOMCSSCounterStyleRule) + DOM_CLASSINFO_MAP_ENTRY(nsIDOMCSSCounterStyleRule) + DOM_CLASSINFO_MAP_END + + DOM_CLASSINFO_MAP_BEGIN(CSSPageRule, nsIDOMCSSPageRule) + DOM_CLASSINFO_MAP_ENTRY(nsIDOMCSSPageRule) + DOM_CLASSINFO_MAP_END + + DOM_CLASSINFO_MAP_BEGIN(CSSFontFeatureValuesRule, nsIDOMCSSFontFeatureValuesRule) + DOM_CLASSINFO_MAP_ENTRY(nsIDOMCSSFontFeatureValuesRule) + DOM_CLASSINFO_MAP_END + + DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(XULControlElement, nsIDOMXULControlElement) + DOM_CLASSINFO_MAP_ENTRY(nsIDOMXULControlElement) + DOM_CLASSINFO_MAP_END + + DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(XULLabeledControlElement, nsIDOMXULLabeledControlElement) + DOM_CLASSINFO_MAP_ENTRY(nsIDOMXULLabeledControlElement) + DOM_CLASSINFO_MAP_END + + DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(XULButtonElement, nsIDOMXULButtonElement) + DOM_CLASSINFO_MAP_ENTRY(nsIDOMXULButtonElement) + DOM_CLASSINFO_MAP_END + + DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(XULCheckboxElement, nsIDOMXULCheckboxElement) + DOM_CLASSINFO_MAP_ENTRY(nsIDOMXULCheckboxElement) + DOM_CLASSINFO_MAP_END + + DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(XULPopupElement, nsIDOMXULPopupElement) + DOM_CLASSINFO_MAP_ENTRY(nsIDOMXULPopupElement) + DOM_CLASSINFO_MAP_END + + static_assert(MOZ_ARRAY_LENGTH(sClassInfoData) == eDOMClassInfoIDCount, + "The number of items in sClassInfoData doesn't match the " + "number of nsIDOMClassInfo ID's, this is bad! Fix it!"); + +#ifdef DEBUG + for (size_t i = 0; i < eDOMClassInfoIDCount; i++) { + if (!sClassInfoData[i].mConstructorFptr || + sClassInfoData[i].mDebugID != i) { + MOZ_CRASH("Class info data out of sync, you forgot to update " + "nsDOMClassInfo.h and nsDOMClassInfo.cpp! Fix this, " + "mozilla will not work without this fixed!"); + } + } + + for (size_t i = 0; i < eDOMClassInfoIDCount; i++) { + if (!sClassInfoData[i].mInterfaces) { + MOZ_CRASH("Class info data without an interface list! Fix this, " + "mozilla will not work without this fixed!"); + } + } +#endif + + // Initialize static JSString's + DefineStaticJSVals(); + + int32_t i; + + for (i = 0; i < eDOMClassInfoIDCount; ++i) { + if (i == eDOMClassInfo_DOMPrototype_id) { + continue; + } + + nsDOMClassInfoData& data = sClassInfoData[i]; + nameSpaceManager->RegisterClassName(data.mName, i, data.mChromeOnly, + data.mAllowXBL, &data.mNameUTF16); + } + + for (i = 0; i < eDOMClassInfoIDCount; ++i) { + RegisterClassProtos(i); + } + + sIsInitialized = true; + + return NS_OK; +} + +NS_IMETHODIMP +nsDOMClassInfo::GetInterfaces(uint32_t *aCount, nsIID ***aArray) +{ + uint32_t count = 0; + + while (mData->mInterfaces[count]) { + count++; + } + + *aCount = count; + + if (!count) { + *aArray = nullptr; + + return NS_OK; + } + + *aArray = static_cast<nsIID **>(moz_xmalloc(count * sizeof(nsIID *))); + NS_ENSURE_TRUE(*aArray, NS_ERROR_OUT_OF_MEMORY); + + uint32_t i; + for (i = 0; i < count; i++) { + nsIID *iid = static_cast<nsIID *>(nsMemory::Clone(mData->mInterfaces[i], + sizeof(nsIID))); + + if (!iid) { + NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(i, *aArray); + + return NS_ERROR_OUT_OF_MEMORY; + } + + *((*aArray) + i) = iid; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsDOMClassInfo::GetScriptableHelper(nsIXPCScriptable **_retval) +{ + nsCOMPtr<nsIXPCScriptable> rval = this; + rval.forget(_retval); + return NS_OK; +} + +NS_IMETHODIMP +nsDOMClassInfo::GetContractID(char **aContractID) +{ + *aContractID = nullptr; + + return NS_OK; +} + +NS_IMETHODIMP +nsDOMClassInfo::GetClassDescription(char **aClassDescription) +{ + return GetClassName(aClassDescription); +} + +NS_IMETHODIMP +nsDOMClassInfo::GetClassID(nsCID **aClassID) +{ + *aClassID = nullptr; + return NS_OK; +} + +NS_IMETHODIMP +nsDOMClassInfo::GetClassIDNoAlloc(nsCID *aClassID) +{ + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +nsDOMClassInfo::GetFlags(uint32_t *aFlags) +{ + *aFlags = DOMCLASSINFO_STANDARD_FLAGS; + + return NS_OK; +} + +// nsIXPCScriptable + +NS_IMETHODIMP +nsDOMClassInfo::GetClassName(char **aClassName) +{ + *aClassName = NS_strdup(mData->mName); + + return NS_OK; +} + +// virtual +uint32_t +nsDOMClassInfo::GetScriptableFlags() +{ + return mData->mScriptableFlags; +} + +// virtual +const js::Class* +nsDOMClassInfo::GetClass() +{ + return &mData->mClass; +} + +NS_IMETHODIMP +nsDOMClassInfo::PreCreate(nsISupports *nativeObj, JSContext *cx, + JSObject *globalObj, JSObject **parentObj) +{ + *parentObj = globalObj; + return NS_OK; +} + +NS_IMETHODIMP +nsDOMClassInfo::AddProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, + JSObject *obj, jsid id, JS::Handle<JS::Value> val, + bool *_retval) +{ + NS_WARNING("nsDOMClassInfo::AddProperty Don't call me!"); + + return NS_ERROR_UNEXPECTED; +} + +NS_IMETHODIMP +nsDOMClassInfo::GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, + JSObject *obj, jsid id, JS::Value *vp, + bool *_retval) +{ + NS_WARNING("nsDOMClassInfo::GetProperty Don't call me!"); + + return NS_OK; +} + +NS_IMETHODIMP +nsDOMClassInfo::SetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, + JSObject *obj, jsid id, JS::Value *vp, + bool *_retval) +{ + NS_WARNING("nsDOMClassInfo::SetProperty Don't call me!"); + + return NS_ERROR_UNEXPECTED; +} + +NS_IMETHODIMP +nsDOMClassInfo::Enumerate(nsIXPConnectWrappedNative *wrapper, JSContext *cx, + JSObject *obj, bool *_retval) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsDOMClassInfo::NewEnumerate(nsIXPConnectWrappedNative *wrapper, JSContext *cx, + JSObject *obj, JS::AutoIdVector &properties, + bool *_retval) +{ + NS_WARNING("nsDOMClassInfo::NewEnumerate Don't call me!"); + + return NS_ERROR_UNEXPECTED; +} + +NS_IMETHODIMP +nsDOMClassInfo::Resolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, + JSObject *aObj, jsid aId, bool *resolvedp, bool *_retval) +{ + JS::Rooted<JSObject*> obj(cx, aObj); + JS::Rooted<jsid> id(cx, aId); + + if (id != sConstructor_id) { + *resolvedp = false; + return NS_OK; + } + + JS::Rooted<JSObject*> global(cx, ::JS_GetGlobalForObject(cx, obj)); + + JS::Rooted<JS::PropertyDescriptor> desc(cx); + if (!JS_GetPropertyDescriptor(cx, global, mData->mName, &desc)) { + return NS_ERROR_UNEXPECTED; + } + + if (desc.object() && !desc.hasGetterOrSetter() && desc.value().isObject()) { + // If val is not an (non-null) object there either is no + // constructor for this class, or someone messed with + // window.classname, just fall through and let the JS engine + // return the Object constructor. + if (!::JS_DefinePropertyById(cx, obj, id, desc.value(), + JSPROP_ENUMERATE, + JS_STUBGETTER, JS_STUBSETTER)) { + return NS_ERROR_UNEXPECTED; + } + + *resolvedp = true; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsDOMClassInfo::Finalize(nsIXPConnectWrappedNative *wrapper, JSFreeOp *fop, + JSObject *obj) +{ + NS_WARNING("nsDOMClassInfo::Finalize Don't call me!"); + + return NS_ERROR_UNEXPECTED; +} + +NS_IMETHODIMP +nsDOMClassInfo::Call(nsIXPConnectWrappedNative *wrapper, JSContext *cx, + JSObject *obj, const JS::CallArgs &args, bool *_retval) +{ + NS_WARNING("nsDOMClassInfo::Call Don't call me!"); + + return NS_ERROR_UNEXPECTED; +} + +NS_IMETHODIMP +nsDOMClassInfo::Construct(nsIXPConnectWrappedNative *wrapper, JSContext *cx, + JSObject *obj, const JS::CallArgs &args, + bool *_retval) +{ + NS_WARNING("nsDOMClassInfo::Construct Don't call me!"); + + return NS_ERROR_UNEXPECTED; +} + +NS_IMETHODIMP +nsDOMClassInfo::HasInstance(nsIXPConnectWrappedNative *wrapper, JSContext *cx, + JSObject *obj, JS::Handle<JS::Value> val, bool *bp, + bool *_retval) +{ + NS_WARNING("nsDOMClassInfo::HasInstance Don't call me!"); + + return NS_ERROR_UNEXPECTED; +} + +static nsresult +ResolvePrototype(nsIXPConnect *aXPConnect, nsGlobalWindow *aWin, JSContext *cx, + JS::Handle<JSObject*> obj, const char16_t *name, + const nsDOMClassInfoData *ci_data, + const nsGlobalNameStruct *name_struct, + nsScriptNameSpaceManager *nameSpaceManager, + JSObject *dot_prototype, + JS::MutableHandle<JS::PropertyDescriptor> ctorDesc); + +NS_IMETHODIMP +nsDOMClassInfo::PostCreatePrototype(JSContext * cx, JSObject * aProto) +{ + JS::Rooted<JSObject*> proto(cx, aProto); + + // This is called before any other location that requires + // sObjectClass, so compute it here. We assume that nobody has had a + // chance to monkey around with proto's prototype chain before this. + if (!sObjectClass) { + FindObjectClass(cx, proto); + NS_ASSERTION(sObjectClass && !strcmp(sObjectClass->name, "Object"), + "Incorrect object class!"); + } + +#ifdef DEBUG + JS::Rooted<JSObject*> proto2(cx); + JS_GetPrototype(cx, proto, &proto2); + NS_ASSERTION(proto2 && JS_GetClass(proto2) == sObjectClass, + "Hmm, somebody did something evil?"); +#endif + +#ifdef DEBUG + if (mData->mHasClassInterface && mData->mProtoChainInterface && + mData->mProtoChainInterface != &NS_GET_IID(nsISupports)) { + nsCOMPtr<nsIInterfaceInfoManager> + iim(do_GetService(NS_INTERFACEINFOMANAGER_SERVICE_CONTRACTID)); + + if (iim) { + nsCOMPtr<nsIInterfaceInfo> if_info; + iim->GetInfoForIID(mData->mProtoChainInterface, + getter_AddRefs(if_info)); + + if (if_info) { + nsXPIDLCString name; + if_info->GetName(getter_Copies(name)); + NS_ASSERTION(nsCRT::strcmp(CutPrefix(name), mData->mName) == 0, + "Class name and proto chain interface name mismatch!"); + } + } + } +#endif + + // Make prototype delegation work correctly. Consider if a site sets + // HTMLElement.prototype.foopy = function () { ... } Now, calling + // document.body.foopy() needs to ensure that looking up foopy on + // document.body's prototype will find the right function. + JS::Rooted<JSObject*> global(cx, ::JS_GetGlobalForObject(cx, proto)); + + // Only do this if the global object is a window. + nsGlobalWindow* win; + if (NS_FAILED(UNWRAP_OBJECT(Window, &global, win))) { + // Not a window. + return NS_OK; + } + + if (win->IsClosedOrClosing()) { + return NS_OK; + } + + // Don't overwrite a property set by content. + bool contentDefinedProperty; + if (!::JS_AlreadyHasOwnUCProperty(cx, global, reinterpret_cast<const char16_t*>(mData->mNameUTF16), + NS_strlen(mData->mNameUTF16), + &contentDefinedProperty)) { + return NS_ERROR_FAILURE; + } + + nsScriptNameSpaceManager *nameSpaceManager = GetNameSpaceManager(); + NS_ENSURE_TRUE(nameSpaceManager, NS_OK); + + JS::Rooted<JS::PropertyDescriptor> desc(cx); + nsresult rv = ResolvePrototype(sXPConnect, win, cx, global, mData->mNameUTF16, + mData, nullptr, nameSpaceManager, proto, + &desc); + NS_ENSURE_SUCCESS(rv, rv); + if (!contentDefinedProperty && desc.object() && !desc.value().isUndefined()) { + desc.attributesRef() |= JSPROP_RESOLVING; + if (!JS_DefineUCProperty(cx, global, mData->mNameUTF16, + NS_strlen(mData->mNameUTF16), desc)) { + return NS_ERROR_UNEXPECTED; + } + } + + return NS_OK; +} + +// static +nsIClassInfo * +NS_GetDOMClassInfoInstance(nsDOMClassInfoID aID) +{ + if (aID >= eDOMClassInfoIDCount) { + NS_ERROR("Bad ID!"); + + return nullptr; + } + + nsresult rv = RegisterDOMNames(); + NS_ENSURE_SUCCESS(rv, nullptr); + + if (!sClassInfoData[aID].mCachedClassInfo) { + nsDOMClassInfoData& data = sClassInfoData[aID]; + + data.mCachedClassInfo = data.mConstructorFptr(&data); + NS_ENSURE_TRUE(data.mCachedClassInfo, nullptr); + + NS_ADDREF(data.mCachedClassInfo); + } + + return sClassInfoData[aID].mCachedClassInfo; +} + +// static +void +nsDOMClassInfo::ShutDown() +{ + if (sClassInfoData[0].mConstructorFptr) { + uint32_t i; + + for (i = 0; i < eDOMClassInfoIDCount; i++) { + NS_IF_RELEASE(sClassInfoData[i].mCachedClassInfo); + } + } + + sConstructor_id = JSID_VOID; + sWrappedJSObject_id = JSID_VOID; + + NS_IF_RELEASE(sXPConnect); + sIsInitialized = false; +} + +static nsresult +BaseStubConstructor(nsIWeakReference* aWeakOwner, + const nsGlobalNameStruct *name_struct, JSContext *cx, + JS::Handle<JSObject*> obj, const JS::CallArgs &args) +{ + MOZ_ASSERT(obj); + MOZ_ASSERT(cx == nsContentUtils::GetCurrentJSContext()); + + nsresult rv; + nsCOMPtr<nsISupports> native; + if (name_struct->mType == nsGlobalNameStruct::eTypeClassConstructor) { + rv = NS_ERROR_NOT_AVAILABLE; + } else { + MOZ_ASSERT(name_struct->mType == + nsGlobalNameStruct::eTypeExternalConstructor); + native = do_CreateInstance(name_struct->mCID, &rv); + } + if (NS_FAILED(rv)) { + NS_ERROR("Failed to create the object"); + return rv; + } + + js::AssertSameCompartment(cx, obj); + return nsContentUtils::WrapNative(cx, native, args.rval(), true); +} + +static nsresult +DefineInterfaceConstants(JSContext *cx, JS::Handle<JSObject*> obj, const nsIID *aIID) +{ + nsCOMPtr<nsIInterfaceInfoManager> + iim(do_GetService(NS_INTERFACEINFOMANAGER_SERVICE_CONTRACTID)); + NS_ENSURE_TRUE(iim, NS_ERROR_UNEXPECTED); + + nsCOMPtr<nsIInterfaceInfo> if_info; + + nsresult rv = iim->GetInfoForIID(aIID, getter_AddRefs(if_info)); + NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && if_info, rv); + + uint16_t constant_count; + + if_info->GetConstantCount(&constant_count); + + if (!constant_count) { + return NS_OK; + } + + nsCOMPtr<nsIInterfaceInfo> parent_if_info; + + rv = if_info->GetParent(getter_AddRefs(parent_if_info)); + NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && parent_if_info, rv); + + uint16_t parent_constant_count, i; + parent_if_info->GetConstantCount(&parent_constant_count); + + JS::Rooted<JS::Value> v(cx); + for (i = parent_constant_count; i < constant_count; i++) { + nsXPIDLCString name; + rv = if_info->GetConstant(i, &v, getter_Copies(name)); + NS_ENSURE_TRUE(NS_SUCCEEDED(rv), rv); + + if (!::JS_DefineProperty(cx, obj, name, v, + JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT, + JS_STUBGETTER, JS_STUBSETTER)) { + return NS_ERROR_UNEXPECTED; + } + } + + return NS_OK; +} + +class nsDOMConstructor final : public nsIDOMDOMConstructor +{ +protected: + nsDOMConstructor(const char16_t* aName, + bool aIsConstructable, + nsPIDOMWindowInner* aOwner) + : mClassName(aName), + mConstructable(aIsConstructable), + mWeakOwner(do_GetWeakReference(aOwner)) + { + } + + ~nsDOMConstructor() {} + +public: + + static nsresult Create(const char16_t* aName, + const nsGlobalNameStruct* aNameStruct, + nsPIDOMWindowInner* aOwner, + nsDOMConstructor** aResult); + + NS_DECL_ISUPPORTS + NS_DECL_NSIDOMDOMCONSTRUCTOR + + nsresult PreCreate(JSContext *cx, JSObject *globalObj, JSObject **parentObj); + + nsresult Construct(nsIXPConnectWrappedNative *wrapper, JSContext *cx, + JS::Handle<JSObject*> obj, const JS::CallArgs &args, + bool *_retval); + + nsresult HasInstance(nsIXPConnectWrappedNative *wrapper, JSContext *cx, + JS::Handle<JSObject*> obj, const JS::Value &val, bool *bp, + bool *_retval); + + nsresult ResolveInterfaceConstants(JSContext *cx, JS::Handle<JSObject*> obj); + +private: + const nsGlobalNameStruct *GetNameStruct() + { + if (!mClassName) { + NS_ERROR("Can't get name"); + return nullptr; + } + + const nsGlobalNameStruct *nameStruct; +#ifdef DEBUG + nsresult rv = +#endif + GetNameStruct(nsDependentString(mClassName), &nameStruct); + + NS_ASSERTION(NS_FAILED(rv) || nameStruct, "Name isn't in hash."); + + return nameStruct; + } + + static nsresult GetNameStruct(const nsAString& aName, + const nsGlobalNameStruct **aNameStruct) + { + *aNameStruct = nullptr; + + nsScriptNameSpaceManager *nameSpaceManager = GetNameSpaceManager(); + if (!nameSpaceManager) { + NS_ERROR("Can't get namespace manager."); + return NS_ERROR_UNEXPECTED; + } + + *aNameStruct = nameSpaceManager->LookupName(aName); + + // Return NS_OK here, aName just isn't a DOM class but nothing failed. + return NS_OK; + } + + static bool IsConstructable(const nsGlobalNameStruct *aNameStruct) + { + return aNameStruct->mType == nsGlobalNameStruct::eTypeExternalConstructor; + } + + const char16_t* mClassName; + const bool mConstructable; + nsWeakPtr mWeakOwner; +}; + +//static +nsresult +nsDOMConstructor::Create(const char16_t* aName, + const nsGlobalNameStruct* aNameStruct, + nsPIDOMWindowInner* aOwner, + nsDOMConstructor** aResult) +{ + *aResult = nullptr; + // Prevent creating a constructor if aOwner is inner window which doesn't have + // an outer window. If the outer window doesn't have an inner window or the + // caller can't access the outer window's current inner window then try to use + // the owner (so long as it is, in fact, an inner window). If that doesn't + // work then prevent creation also. + nsPIDOMWindowOuter* outerWindow = aOwner->GetOuterWindow(); + nsPIDOMWindowInner* currentInner = + outerWindow ? outerWindow->GetCurrentInnerWindow() : aOwner; + if (!currentInner || + (aOwner != currentInner && + !nsContentUtils::CanCallerAccess(currentInner) && + !(currentInner = aOwner)->IsInnerWindow())) { + return NS_ERROR_DOM_SECURITY_ERR; + } + + bool constructable = aNameStruct && IsConstructable(aNameStruct); + + *aResult = new nsDOMConstructor(aName, constructable, currentInner); + NS_ADDREF(*aResult); + return NS_OK; +} + +NS_IMPL_ADDREF(nsDOMConstructor) +NS_IMPL_RELEASE(nsDOMConstructor) +NS_INTERFACE_MAP_BEGIN(nsDOMConstructor) + NS_INTERFACE_MAP_ENTRY(nsIDOMDOMConstructor) + NS_INTERFACE_MAP_ENTRY(nsISupports) + if (aIID.Equals(NS_GET_IID(nsIClassInfo))) { +#ifdef DEBUG + { + const nsGlobalNameStruct *name_struct = GetNameStruct(); + NS_ASSERTION(!name_struct || + mConstructable == IsConstructable(name_struct), + "Can't change constructability dynamically!"); + } +#endif + foundInterface = + NS_GetDOMClassInfoInstance(mConstructable ? + eDOMClassInfo_DOMConstructor_id : + eDOMClassInfo_DOMPrototype_id); + if (!foundInterface) { + *aInstancePtr = nullptr; + return NS_ERROR_OUT_OF_MEMORY; + } + } else +NS_INTERFACE_MAP_END + +nsresult +nsDOMConstructor::PreCreate(JSContext *cx, JSObject *globalObj, JSObject **parentObj) +{ + nsCOMPtr<nsPIDOMWindowInner> owner(do_QueryReferent(mWeakOwner)); + if (!owner) { + // Can't do anything. + return NS_OK; + } + + nsGlobalWindow *win = nsGlobalWindow::Cast(owner); + return SetParentToWindow(win, parentObj); +} + +nsresult +nsDOMConstructor::Construct(nsIXPConnectWrappedNative *wrapper, JSContext * cx, + JS::Handle<JSObject*> obj, const JS::CallArgs &args, + bool *_retval) +{ + MOZ_ASSERT(obj); + + const nsGlobalNameStruct *name_struct = GetNameStruct(); + NS_ENSURE_TRUE(name_struct, NS_ERROR_FAILURE); + + if (!IsConstructable(name_struct)) { + // ignore return value, we return false anyway + return NS_ERROR_DOM_NOT_SUPPORTED_ERR; + } + + return BaseStubConstructor(mWeakOwner, name_struct, cx, obj, args); +} + +nsresult +nsDOMConstructor::HasInstance(nsIXPConnectWrappedNative *wrapper, + JSContext * cx, JS::Handle<JSObject*> obj, + const JS::Value &v, bool *bp, bool *_retval) + +{ + // No need to look these up in the hash. + *bp = false; + if (v.isPrimitive()) { + return NS_OK; + } + + JS::Rooted<JSObject*> dom_obj(cx, v.toObjectOrNull()); + NS_ASSERTION(dom_obj, "nsDOMConstructor::HasInstance couldn't get object"); + + // This might not be the right object, if there are wrappers. Unwrap if we can. + JSObject *wrapped_obj = js::CheckedUnwrap(dom_obj, /* stopAtWindowProxy = */ false); + if (wrapped_obj) + dom_obj = wrapped_obj; + + const JSClass *dom_class = JS_GetClass(dom_obj); + if (!dom_class) { + NS_ERROR("nsDOMConstructor::HasInstance can't get class."); + return NS_ERROR_UNEXPECTED; + } + + const nsGlobalNameStruct *name_struct; + nsresult rv = GetNameStruct(NS_ConvertASCIItoUTF16(dom_class->name), &name_struct); + if (NS_FAILED(rv)) { + return rv; + } + + if (!name_struct) { + // This isn't a normal DOM object, see if this constructor lives on its + // prototype chain. + JS::Rooted<JS::PropertyDescriptor> desc(cx); + if (!JS_GetPropertyDescriptor(cx, obj, "prototype", &desc)) { + return NS_ERROR_UNEXPECTED; + } + + if (!desc.object() || desc.hasGetterOrSetter() || !desc.value().isObject()) { + return NS_OK; + } + + JS::Rooted<JSObject*> dot_prototype(cx, &desc.value().toObject()); + + JS::Rooted<JSObject*> proto(cx, dom_obj); + JSAutoCompartment ac(cx, proto); + + if (!JS_WrapObject(cx, &dot_prototype)) { + return NS_ERROR_UNEXPECTED; + } + + for (;;) { + if (!JS_GetPrototype(cx, proto, &proto)) { + return NS_ERROR_UNEXPECTED; + } + if (!proto) { + break; + } + if (proto == dot_prototype) { + *bp = true; + break; + } + } + + return NS_OK; + } + + if (name_struct->mType != nsGlobalNameStruct::eTypeClassConstructor) { + // Doesn't have DOM interfaces. + return NS_OK; + } + + const nsGlobalNameStruct *class_name_struct = GetNameStruct(); + NS_ENSURE_TRUE(class_name_struct, NS_ERROR_FAILURE); + + if (name_struct == class_name_struct) { + *bp = true; + + return NS_OK; + } + + const nsIID *class_iid; + if (class_name_struct->mType == nsGlobalNameStruct::eTypeClassProto) { + class_iid = &class_name_struct->mIID; + } else if (class_name_struct->mType == nsGlobalNameStruct::eTypeClassConstructor) { + class_iid = + sClassInfoData[class_name_struct->mDOMClassInfoID].mProtoChainInterface; + } else { + *bp = false; + + return NS_OK; + } + + NS_ASSERTION(name_struct->mType == nsGlobalNameStruct::eTypeClassConstructor, + "The constructor was set up with a struct of the wrong type."); + + const nsDOMClassInfoData *ci_data; + if (name_struct->mType == nsGlobalNameStruct::eTypeClassConstructor && + name_struct->mDOMClassInfoID >= 0) { + ci_data = &sClassInfoData[name_struct->mDOMClassInfoID]; + } else { + ci_data = nullptr; + } + + nsCOMPtr<nsIInterfaceInfoManager> + iim(do_GetService(NS_INTERFACEINFOMANAGER_SERVICE_CONTRACTID)); + if (!iim) { + NS_ERROR("nsDOMConstructor::HasInstance can't get interface info mgr."); + return NS_ERROR_UNEXPECTED; + } + + nsCOMPtr<nsIInterfaceInfo> if_info; + uint32_t count = 0; + const nsIID* class_interface; + while ((class_interface = ci_data->mInterfaces[count++])) { + if (class_iid->Equals(*class_interface)) { + *bp = true; + + return NS_OK; + } + + iim->GetInfoForIID(class_interface, getter_AddRefs(if_info)); + if (!if_info) { + NS_ERROR("nsDOMConstructor::HasInstance can't get interface info."); + return NS_ERROR_UNEXPECTED; + } + + if_info->HasAncestor(class_iid, bp); + + if (*bp) { + return NS_OK; + } + } + + return NS_OK; +} + +nsresult +nsDOMConstructor::ResolveInterfaceConstants(JSContext *cx, JS::Handle<JSObject*> obj) +{ + const nsGlobalNameStruct *class_name_struct = GetNameStruct(); + if (!class_name_struct) + return NS_ERROR_UNEXPECTED; + + const nsIID *class_iid; + if (class_name_struct->mType == nsGlobalNameStruct::eTypeClassProto) { + class_iid = &class_name_struct->mIID; + } else if (class_name_struct->mType == nsGlobalNameStruct::eTypeClassConstructor) { + class_iid = + sClassInfoData[class_name_struct->mDOMClassInfoID].mProtoChainInterface; + } else { + return NS_OK; + } + + nsresult rv = DefineInterfaceConstants(cx, obj, class_iid); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +NS_IMETHODIMP +nsDOMConstructor::ToString(nsAString &aResult) +{ + aResult.AssignLiteral("[object "); + aResult.Append(mClassName); + aResult.Append(char16_t(']')); + + return NS_OK; +} + + +static nsresult +GetXPCProto(nsIXPConnect *aXPConnect, JSContext *cx, nsGlobalWindow *aWin, + const nsGlobalNameStruct *aNameStruct, + JS::MutableHandle<JSObject*> aProto) +{ + NS_ASSERTION(aNameStruct->mType == nsGlobalNameStruct::eTypeClassConstructor, + "Wrong type!"); + + int32_t id = aNameStruct->mDOMClassInfoID; + MOZ_ASSERT(id >= 0, "Negative DOM classinfo?!?"); + + nsDOMClassInfoID ci_id = (nsDOMClassInfoID)id; + + nsCOMPtr<nsIClassInfo> ci = NS_GetDOMClassInfoInstance(ci_id); + NS_ENSURE_TRUE(ci, NS_ERROR_UNEXPECTED); + + nsresult rv = + aXPConnect->GetWrappedNativePrototype(cx, aWin->GetGlobalJSObject(), ci, aProto.address()); + NS_ENSURE_SUCCESS(rv, rv); + + return JS_WrapObject(cx, aProto) ? NS_OK : NS_ERROR_FAILURE; +} + +// Either ci_data must be non-null or name_struct must be non-null and of type +// eTypeClassProto. +static nsresult +ResolvePrototype(nsIXPConnect *aXPConnect, nsGlobalWindow *aWin, JSContext *cx, + JS::Handle<JSObject*> obj, const char16_t *name, + const nsDOMClassInfoData *ci_data, + const nsGlobalNameStruct *name_struct, + nsScriptNameSpaceManager *nameSpaceManager, + JSObject* aDot_prototype, + JS::MutableHandle<JS::PropertyDescriptor> ctorDesc) +{ + JS::Rooted<JSObject*> dot_prototype(cx, aDot_prototype); + NS_ASSERTION(ci_data || + (name_struct && + name_struct->mType == nsGlobalNameStruct::eTypeClassProto), + "Wrong type or missing ci_data!"); + + RefPtr<nsDOMConstructor> constructor; + nsresult rv = nsDOMConstructor::Create(name, name_struct, aWin->AsInner(), + getter_AddRefs(constructor)); + NS_ENSURE_SUCCESS(rv, rv); + + JS::Rooted<JS::Value> v(cx); + + js::AssertSameCompartment(cx, obj); + rv = nsContentUtils::WrapNative(cx, constructor, + &NS_GET_IID(nsIDOMDOMConstructor), &v, + false); + NS_ENSURE_SUCCESS(rv, rv); + + FillPropertyDescriptor(ctorDesc, obj, 0, v); + // And make sure we wrap the value into the right compartment. Note that we + // do this with ctorDesc.value(), not with v, because we need v to be in the + // right compartment (that of the reflector of |constructor|) below. + if (!JS_WrapValue(cx, ctorDesc.value())) { + return NS_ERROR_UNEXPECTED; + } + + JS::Rooted<JSObject*> class_obj(cx, &v.toObject()); + + const nsIID *primary_iid = &NS_GET_IID(nsISupports); + + if (!ci_data) { + primary_iid = &name_struct->mIID; + } + else if (ci_data->mProtoChainInterface) { + primary_iid = ci_data->mProtoChainInterface; + } + + nsCOMPtr<nsIInterfaceInfo> if_info; + nsCOMPtr<nsIInterfaceInfo> parent; + const char *class_parent_name = nullptr; + + if (!primary_iid->Equals(NS_GET_IID(nsISupports))) { + JSAutoCompartment ac(cx, class_obj); + + rv = DefineInterfaceConstants(cx, class_obj, primary_iid); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIInterfaceInfoManager> + iim(do_GetService(NS_INTERFACEINFOMANAGER_SERVICE_CONTRACTID)); + NS_ENSURE_TRUE(iim, NS_ERROR_NOT_AVAILABLE); + + iim->GetInfoForIID(primary_iid, getter_AddRefs(if_info)); + NS_ENSURE_TRUE(if_info, NS_ERROR_UNEXPECTED); + + const nsIID *iid = nullptr; + + if (ci_data && !ci_data->mHasClassInterface) { + if_info->GetIIDShared(&iid); + } else { + if_info->GetParent(getter_AddRefs(parent)); + NS_ENSURE_TRUE(parent, NS_ERROR_UNEXPECTED); + + parent->GetIIDShared(&iid); + } + + if (iid) { + if (!iid->Equals(NS_GET_IID(nsISupports))) { + if (ci_data && !ci_data->mHasClassInterface) { + // If the class doesn't have a class interface the primary + // interface is the interface that should be + // constructor.prototype.__proto__. + + if_info->GetNameShared(&class_parent_name); + } else { + // If the class does have a class interface (or there's no + // real class for this name) then the parent of the + // primary interface is what we want on + // constructor.prototype.__proto__. + + NS_ASSERTION(parent, "Whoa, this is bad, null parent here!"); + + parent->GetNameShared(&class_parent_name); + } + } + } + } + + { + JS::Rooted<JSObject*> winobj(cx, aWin->FastGetGlobalJSObject()); + + JS::Rooted<JSObject*> proto(cx); + + if (class_parent_name) { + JSAutoCompartment ac(cx, winobj); + + JS::Rooted<JS::PropertyDescriptor> desc(cx); + if (!JS_GetPropertyDescriptor(cx, winobj, CutPrefix(class_parent_name), &desc)) { + return NS_ERROR_UNEXPECTED; + } + + if (desc.object() && !desc.hasGetterOrSetter() && desc.value().isObject()) { + JS::Rooted<JSObject*> obj(cx, &desc.value().toObject()); + if (!JS_GetPropertyDescriptor(cx, obj, "prototype", &desc)) { + return NS_ERROR_UNEXPECTED; + } + + if (desc.object() && !desc.hasGetterOrSetter() && desc.value().isObject()) { + proto = &desc.value().toObject(); + } + } + } + + if (dot_prototype) { + JSAutoCompartment ac(cx, dot_prototype); + JS::Rooted<JSObject*> xpc_proto_proto(cx); + if (!::JS_GetPrototype(cx, dot_prototype, &xpc_proto_proto)) { + return NS_ERROR_UNEXPECTED; + } + + if (proto && + (!xpc_proto_proto || + JS_GetClass(xpc_proto_proto) == sObjectClass)) { + if (!JS_WrapObject(cx, &proto) || + !JS_SetPrototype(cx, dot_prototype, proto)) { + return NS_ERROR_UNEXPECTED; + } + } + } else { + JSAutoCompartment ac(cx, winobj); + if (!proto) { + proto = JS_GetObjectPrototype(cx, winobj); + } + dot_prototype = ::JS_NewObjectWithUniqueType(cx, + &sDOMConstructorProtoClass, + proto); + NS_ENSURE_TRUE(dot_prototype, NS_ERROR_OUT_OF_MEMORY); + } + } + + v.setObject(*dot_prototype); + + JSAutoCompartment ac(cx, class_obj); + + // Per ECMA, the prototype property is {DontEnum, DontDelete, ReadOnly} + if (!JS_WrapValue(cx, &v) || + !JS_DefineProperty(cx, class_obj, "prototype", v, + JSPROP_PERMANENT | JSPROP_READONLY, + JS_STUBGETTER, JS_STUBSETTER)) { + return NS_ERROR_UNEXPECTED; + } + + return NS_OK; +} + +static bool +OldBindingConstructorEnabled(const nsGlobalNameStruct *aStruct, + nsGlobalWindow *aWin, JSContext *cx) +{ + MOZ_ASSERT(aStruct->mType == nsGlobalNameStruct::eTypeProperty || + aStruct->mType == nsGlobalNameStruct::eTypeClassConstructor); + + // Don't expose chrome only constructors to content windows. + if (aStruct->mChromeOnly) { + bool expose; + if (aStruct->mAllowXBL) { + expose = IsChromeOrXBL(cx, nullptr); + } else { + expose = nsContentUtils::IsSystemPrincipal(aWin->GetPrincipal()); + } + + if (!expose) { + return false; + } + } + + return true; +} + +static nsresult +LookupComponentsShim(JSContext *cx, JS::Handle<JSObject*> global, + nsPIDOMWindowInner *win, + JS::MutableHandle<JS::PropertyDescriptor> desc); + +// static +bool +nsWindowSH::NameStructEnabled(JSContext* aCx, nsGlobalWindow *aWin, + const nsAString& aName, + const nsGlobalNameStruct& aNameStruct) +{ + const nsGlobalNameStruct* nameStruct = &aNameStruct; + return (nameStruct->mType != nsGlobalNameStruct::eTypeProperty && + nameStruct->mType != nsGlobalNameStruct::eTypeClassConstructor) || + OldBindingConstructorEnabled(nameStruct, aWin, aCx); +} + +#ifdef RELEASE_OR_BETA +#define USE_CONTROLLERS_SHIM +#endif + +#ifdef USE_CONTROLLERS_SHIM +static const JSClass ControllersShimClass = { + "XULControllers", 0 +}; +#endif + +// static +nsresult +nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx, + JS::Handle<JSObject*> obj, JS::Handle<jsid> id, + JS::MutableHandle<JS::PropertyDescriptor> desc) +{ + if (id == XPCJSContext::Get()->GetStringID(XPCJSContext::IDX_COMPONENTS)) { + return LookupComponentsShim(cx, obj, aWin->AsInner(), desc); + } + +#ifdef USE_CONTROLLERS_SHIM + // Note: We use |obj| rather than |aWin| to get the principal here, because + // this is called during Window setup when the Document isn't necessarily + // hooked up yet. + if (id == XPCJSContext::Get()->GetStringID(XPCJSContext::IDX_CONTROLLERS) && + !xpc::IsXrayWrapper(obj) && + !nsContentUtils::IsSystemPrincipal(nsContentUtils::ObjectPrincipal(obj))) + { + if (aWin->GetDoc()) { + aWin->GetDoc()->WarnOnceAbout(nsIDocument::eWindow_Controllers); + } + MOZ_ASSERT(JS_IsGlobalObject(obj)); + JS::Rooted<JSObject*> shim(cx, JS_NewObject(cx, &ControllersShimClass)); + if (NS_WARN_IF(!shim)) { + return NS_ERROR_OUT_OF_MEMORY; + } + FillPropertyDescriptor(desc, obj, JS::ObjectValue(*shim), /* readOnly = */ false); + return NS_OK; + } +#endif + + nsScriptNameSpaceManager *nameSpaceManager = GetNameSpaceManager(); + NS_ENSURE_TRUE(nameSpaceManager, NS_ERROR_NOT_INITIALIZED); + + // Note - Our only caller is nsGlobalWindow::DoResolve, which checks that + // JSID_IS_STRING(id) is true. + nsAutoJSString name; + if (!name.init(cx, JSID_TO_STRING(id))) { + return NS_ERROR_OUT_OF_MEMORY; + } + + const char16_t *class_name = nullptr; + const nsGlobalNameStruct *name_struct = + nameSpaceManager->LookupName(name, &class_name); + + if (!name_struct) { + return NS_OK; + } + + // The class_name had better match our name + MOZ_ASSERT(name.Equals(class_name)); + + NS_ENSURE_TRUE(class_name, NS_ERROR_UNEXPECTED); + + nsresult rv = NS_OK; + + if (name_struct->mType == nsGlobalNameStruct::eTypeClassConstructor) { + if (!OldBindingConstructorEnabled(name_struct, aWin, cx)) { + return NS_OK; + } + + // Create the XPConnect prototype for our classinfo, PostCreateProto will + // set up the prototype chain. This will go ahead and define things on the + // actual window's global. + JS::Rooted<JSObject*> dot_prototype(cx); + rv = GetXPCProto(nsDOMClassInfo::sXPConnect, cx, aWin, name_struct, + &dot_prototype); + NS_ENSURE_SUCCESS(rv, rv); + MOZ_ASSERT(dot_prototype); + + bool isXray = xpc::WrapperFactory::IsXrayWrapper(obj); + MOZ_ASSERT_IF(obj != aWin->GetGlobalJSObject(), isXray); + if (!isXray) { + // GetXPCProto already defined the property for us + FillPropertyDescriptor(desc, obj, JS::UndefinedValue(), false); + return NS_OK; + } + + // This is the Xray case. Look up the constructor object for this + // prototype. + return ResolvePrototype(nsDOMClassInfo::sXPConnect, aWin, cx, obj, + class_name, + &sClassInfoData[name_struct->mDOMClassInfoID], + name_struct, nameSpaceManager, dot_prototype, + desc); + } + + if (name_struct->mType == nsGlobalNameStruct::eTypeClassProto) { + // We don't have a XPConnect prototype object, let ResolvePrototype create + // one. + return ResolvePrototype(nsDOMClassInfo::sXPConnect, aWin, cx, obj, + class_name, nullptr, + name_struct, nameSpaceManager, nullptr, desc); + } + + if (name_struct->mType == nsGlobalNameStruct::eTypeExternalConstructor) { + RefPtr<nsDOMConstructor> constructor; + rv = nsDOMConstructor::Create(class_name, name_struct, aWin->AsInner(), + getter_AddRefs(constructor)); + NS_ENSURE_SUCCESS(rv, rv); + + JS::Rooted<JS::Value> val(cx); + js::AssertSameCompartment(cx, obj); + rv = nsContentUtils::WrapNative(cx, constructor, + &NS_GET_IID(nsIDOMDOMConstructor), &val, + true); + NS_ENSURE_SUCCESS(rv, rv); + + NS_ASSERTION(val.isObject(), "Why didn't we get a JSObject?"); + + FillPropertyDescriptor(desc, obj, 0, val); + + return NS_OK; + } + + if (name_struct->mType == nsGlobalNameStruct::eTypeProperty) { + if (!OldBindingConstructorEnabled(name_struct, aWin, cx)) + return NS_OK; + + // Before defining a global property, check for a named subframe of the + // same name. If it exists, we don't want to shadow it. + if (nsCOMPtr<nsPIDOMWindowOuter> childWin = aWin->GetChildWindow(name)) { + return NS_OK; + } + + nsCOMPtr<nsISupports> native(do_CreateInstance(name_struct->mCID, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + + JS::Rooted<JS::Value> prop_val(cx, JS::UndefinedValue()); // Property value. + + nsCOMPtr<nsIDOMGlobalPropertyInitializer> gpi(do_QueryInterface(native)); + if (gpi) { + rv = gpi->Init(aWin->AsInner(), &prop_val); + NS_ENSURE_SUCCESS(rv, rv); + } + + if (prop_val.isPrimitive() && !prop_val.isNull()) { + rv = nsContentUtils::WrapNative(cx, native, &prop_val, true); + } + + NS_ENSURE_SUCCESS(rv, rv); + + if (!JS_WrapValue(cx, &prop_val)) { + return NS_ERROR_UNEXPECTED; + } + + FillPropertyDescriptor(desc, obj, prop_val, false); + + return NS_OK; + } + + return rv; +} + +struct InterfaceShimEntry { + const char *geckoName; + const char *domName; +}; + +// We add shims from Components.interfaces.nsIDOMFoo to window.Foo for each +// interface that has interface constants that sites might be getting off +// of Ci. +const InterfaceShimEntry kInterfaceShimMap[] = +{ { "nsIXMLHttpRequest", "XMLHttpRequest" }, + { "nsIDOMDOMException", "DOMException" }, + { "nsIDOMNode", "Node" }, + { "nsIDOMCSSPrimitiveValue", "CSSPrimitiveValue" }, + { "nsIDOMCSSRule", "CSSRule" }, + { "nsIDOMCSSValue", "CSSValue" }, + { "nsIDOMEvent", "Event" }, + { "nsIDOMNSEvent", "Event" }, + { "nsIDOMKeyEvent", "KeyEvent" }, + { "nsIDOMMouseEvent", "MouseEvent" }, + { "nsIDOMMouseScrollEvent", "MouseScrollEvent" }, + { "nsIDOMMutationEvent", "MutationEvent" }, + { "nsIDOMSimpleGestureEvent", "SimpleGestureEvent" }, + { "nsIDOMUIEvent", "UIEvent" }, + { "nsIDOMHTMLMediaElement", "HTMLMediaElement" }, + { "nsIDOMOfflineResourceList", "OfflineResourceList" }, + { "nsIDOMRange", "Range" }, + { "nsIDOMSVGLength", "SVGLength" }, + { "nsIDOMNodeFilter", "NodeFilter" }, + { "nsIDOMXPathResult", "XPathResult" } }; + +static nsresult +LookupComponentsShim(JSContext *cx, JS::Handle<JSObject*> global, + nsPIDOMWindowInner *win, + JS::MutableHandle<JS::PropertyDescriptor> desc) +{ + // Keep track of how often this happens. + Telemetry::Accumulate(Telemetry::COMPONENTS_SHIM_ACCESSED_BY_CONTENT, true); + + // Warn once. + nsCOMPtr<nsIDocument> doc = win->GetExtantDoc(); + if (doc) { + doc->WarnOnceAbout(nsIDocument::eComponents, /* asError = */ true); + } + + // Create a fake Components object. + AssertSameCompartment(cx, global); + JS::Rooted<JSObject*> components(cx, JS_NewPlainObject(cx)); + NS_ENSURE_TRUE(components, NS_ERROR_OUT_OF_MEMORY); + + // Create a fake interfaces object. + JS::Rooted<JSObject*> interfaces(cx, JS_NewPlainObject(cx)); + NS_ENSURE_TRUE(interfaces, NS_ERROR_OUT_OF_MEMORY); + bool ok = + JS_DefineProperty(cx, components, "interfaces", interfaces, + JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY, + JS_STUBGETTER, JS_STUBSETTER); + NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY); + + // Define a bunch of shims from the Ci.nsIDOMFoo to window.Foo for DOM + // interfaces with constants. + for (uint32_t i = 0; i < ArrayLength(kInterfaceShimMap); ++i) { + + // Grab the names from the table. + const char *geckoName = kInterfaceShimMap[i].geckoName; + const char *domName = kInterfaceShimMap[i].domName; + + // Look up the appopriate interface object on the global. + JS::Rooted<JS::Value> v(cx, JS::UndefinedValue()); + ok = JS_GetProperty(cx, global, domName, &v); + NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY); + if (!v.isObject()) { + NS_WARNING("Unable to find interface object on global"); + continue; + } + + // Define the shim on the interfaces object. + ok = JS_DefineProperty(cx, interfaces, geckoName, v, + JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY, + JS_STUBGETTER, JS_STUBSETTER); + NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY); + } + + FillPropertyDescriptor(desc, global, JS::ObjectValue(*components), false); + + return NS_OK; +} + +// EventTarget helper + +NS_IMETHODIMP +nsEventTargetSH::PreCreate(nsISupports *nativeObj, JSContext *cx, + JSObject *aGlobalObj, JSObject **parentObj) +{ + JS::Rooted<JSObject*> globalObj(cx, aGlobalObj); + DOMEventTargetHelper* target = DOMEventTargetHelper::FromSupports(nativeObj); + + nsCOMPtr<nsIScriptGlobalObject> native_parent; + target->GetParentObject(getter_AddRefs(native_parent)); + + *parentObj = native_parent ? native_parent->GetGlobalJSObject() : globalObj; + + return *parentObj ? NS_OK : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsEventTargetSH::AddProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, + JSObject *obj, jsid id, JS::Handle<JS::Value> val, + bool *_retval) +{ + nsEventTargetSH::PreserveWrapper(GetNative(wrapper, obj)); + + return NS_OK; +} + +void +nsEventTargetSH::PreserveWrapper(nsISupports *aNative) +{ + DOMEventTargetHelper* target = DOMEventTargetHelper::FromSupports(aNative); + target->PreserveWrapper(aNative); +} + +// nsIDOMEventListener::HandleEvent() 'this' converter helper + +NS_INTERFACE_MAP_BEGIN(nsEventListenerThisTranslator) + NS_INTERFACE_MAP_ENTRY(nsIXPCFunctionThisTranslator) + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + + +NS_IMPL_ADDREF(nsEventListenerThisTranslator) +NS_IMPL_RELEASE(nsEventListenerThisTranslator) + + +NS_IMETHODIMP +nsEventListenerThisTranslator::TranslateThis(nsISupports *aInitialThis, + nsISupports **_retval) +{ + nsCOMPtr<nsIDOMEvent> event(do_QueryInterface(aInitialThis)); + NS_ENSURE_TRUE(event, NS_ERROR_UNEXPECTED); + + nsCOMPtr<EventTarget> target = event->InternalDOMEvent()->GetCurrentTarget(); + target.forget(_retval); + return NS_OK; +} + +NS_IMETHODIMP +nsDOMConstructorSH::PreCreate(nsISupports *nativeObj, JSContext *cx, + JSObject *aGlobalObj, JSObject **parentObj) +{ + JS::Rooted<JSObject*> globalObj(cx, aGlobalObj); + nsDOMConstructor *wrapped = static_cast<nsDOMConstructor *>(nativeObj); + +#ifdef DEBUG + { + nsCOMPtr<nsIDOMDOMConstructor> is_constructor = + do_QueryInterface(nativeObj); + NS_ASSERTION(is_constructor, "How did we not get a constructor?"); + } +#endif + + return wrapped->PreCreate(cx, globalObj, parentObj); +} + +NS_IMETHODIMP +nsDOMConstructorSH::Resolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, + JSObject *aObj, jsid aId, bool *resolvedp, + bool *_retval) +{ + JS::Rooted<JSObject*> obj(cx, aObj); + JS::Rooted<jsid> id(cx, aId); + // For regular DOM constructors, we have our interface constants defined on + // us by nsWindowSH::GlobalResolve. However, XrayWrappers can't see these + // interface constants (as they look like expando properties) so we have to + // specially resolve those constants here, but only for Xray wrappers. + if (!ObjectIsNativeWrapper(cx, obj)) { + return NS_OK; + } + + JS::Rooted<JSObject*> nativePropsObj(cx, xpc::XrayUtils::GetNativePropertiesObject(cx, obj)); + nsDOMConstructor *wrapped = + static_cast<nsDOMConstructor *>(wrapper->Native()); + nsresult rv = wrapped->ResolveInterfaceConstants(cx, nativePropsObj); + NS_ENSURE_SUCCESS(rv, rv); + + // Now re-lookup the ID to see if we should report back that we resolved the + // looked-for constant. Note that we don't have to worry about infinitely + // recurring back here because the Xray wrapper's holder object doesn't call + // Resolve hooks. + bool found; + if (!JS_HasPropertyById(cx, nativePropsObj, id, &found)) { + *_retval = false; + return NS_OK; + } + + if (found) { + *resolvedp = true; + } + return NS_OK; +} + +NS_IMETHODIMP +nsDOMConstructorSH::Call(nsIXPConnectWrappedNative *wrapper, JSContext *cx, + JSObject *aObj, const JS::CallArgs &args, bool *_retval) +{ + JS::Rooted<JSObject*> obj(cx, aObj); + MOZ_ASSERT(obj); + + nsDOMConstructor *wrapped = + static_cast<nsDOMConstructor *>(wrapper->Native()); + +#ifdef DEBUG + { + nsCOMPtr<nsIDOMDOMConstructor> is_constructor = + do_QueryWrappedNative(wrapper); + NS_ASSERTION(is_constructor, "How did we not get a constructor?"); + } +#endif + + return wrapped->Construct(wrapper, cx, obj, args, _retval); +} + +NS_IMETHODIMP +nsDOMConstructorSH::Construct(nsIXPConnectWrappedNative *wrapper, JSContext *cx, + JSObject *aObj, const JS::CallArgs &args, bool *_retval) +{ + JS::Rooted<JSObject*> obj(cx, aObj); + MOZ_ASSERT(obj); + + nsDOMConstructor *wrapped = + static_cast<nsDOMConstructor *>(wrapper->Native()); + +#ifdef DEBUG + { + nsCOMPtr<nsIDOMDOMConstructor> is_constructor = + do_QueryWrappedNative(wrapper); + NS_ASSERTION(is_constructor, "How did we not get a constructor?"); + } +#endif + + return wrapped->Construct(wrapper, cx, obj, args, _retval); +} + +NS_IMETHODIMP +nsDOMConstructorSH::HasInstance(nsIXPConnectWrappedNative *wrapper, + JSContext *cx, JSObject *aObj, JS::Handle<JS::Value> val, + bool *bp, bool *_retval) +{ + JS::Rooted<JSObject*> obj(cx, aObj); + nsDOMConstructor *wrapped = + static_cast<nsDOMConstructor *>(wrapper->Native()); + +#ifdef DEBUG + { + nsCOMPtr<nsIDOMDOMConstructor> is_constructor = + do_QueryWrappedNative(wrapper); + NS_ASSERTION(is_constructor, "How did we not get a constructor?"); + } +#endif + + return wrapped->HasInstance(wrapper, cx, obj, val, bp, _retval); +} + +NS_IMETHODIMP +nsNonDOMObjectSH::GetFlags(uint32_t *aFlags) +{ + // This is NOT a DOM Object. Use this helper class for cases when you need + // to do something like implement nsISecurityCheckedComponent in a meaningful + // way. + *aFlags = nsIClassInfo::MAIN_THREAD_ONLY | nsIClassInfo::SINGLETON_CLASSINFO; + return NS_OK; +} + +// nsContentFrameMessageManagerSH + +template<typename Super> +NS_IMETHODIMP +nsMessageManagerSH<Super>::Resolve(nsIXPConnectWrappedNative* wrapper, + JSContext* cx, JSObject* obj_, + jsid id_, bool* resolvedp, + bool* _retval) +{ + JS::Rooted<JSObject*> obj(cx, obj_); + JS::Rooted<jsid> id(cx, id_); + + *_retval = SystemGlobalResolve(cx, obj, id, resolvedp); + NS_ENSURE_TRUE(*_retval, NS_ERROR_FAILURE); + + if (*resolvedp) { + return NS_OK; + } + + return Super::Resolve(wrapper, cx, obj, id, resolvedp, _retval); +} + +template<typename Super> +NS_IMETHODIMP +nsMessageManagerSH<Super>::Enumerate(nsIXPConnectWrappedNative* wrapper, + JSContext* cx, JSObject* obj_, + bool* _retval) +{ + JS::Rooted<JSObject*> obj(cx, obj_); + + *_retval = SystemGlobalEnumerate(cx, obj); + NS_ENSURE_TRUE(*_retval, NS_ERROR_FAILURE); + + // Don't call up to our superclass, since neither nsDOMGenericSH nor + // nsEventTargetSH have WANT_ENUMERATE. + return NS_OK; +} |