author | Brian Grinstead <bgrinstead@mozilla.com> |
Thu, 07 Nov 2019 00:35:32 +0000 | |
changeset 501017 | def5ec4549e70a978fd588a1fc300d0b73b3285b |
parent 501016 | 1fff2450c69f4baff0070b6c0fb73e9f5b54e807 |
child 501018 | eadda7d0287cc25ef6d5eb332a98b0d7b6142eee |
push id | 36778 |
push user | apavel@mozilla.com |
push date | Thu, 07 Nov 2019 09:49:05 +0000 |
treeherder | mozilla-central@d271c572a9bc [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | bzbarsky |
bugs | 1593119 |
milestone | 72.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/.eslintrc.js +++ b/.eslintrc.js @@ -231,17 +231,16 @@ module.exports = { "dom/tests/mochitest/**", "dom/u2f/**", "dom/vr/**", "dom/webauthn/**", "dom/webgpu/**", "dom/websocket/**", "dom/workers/**", "dom/worklet/**", - "dom/xbl/**", "dom/xml/**", "dom/xslt/**", "dom/xul/**", ], "rules": { "consistent-return": "off", "mozilla/avoid-removeChild": "off", "mozilla/consistent-if-bracing": "off",
--- a/dom/base/moz.build +++ b/dom/base/moz.build @@ -520,19 +520,16 @@ LOCAL_INCLUDES += [ '/layout/xul', '/netwerk/base', '/netwerk/url-classifier', '/security/manager/ssl', '/widget', '/xpcom/ds', ] -if CONFIG['MOZ_XBL']: - LOCAL_INCLUDES += ['/dom/xbl'] - if CONFIG['MOZ_WEBRTC']: LOCAL_INCLUDES += [ '/netwerk/sctp/datachannel', ] include('/ipc/chromium/chromium-config.mozbuild') FINAL_LIBRARY = 'xul'
--- a/dom/bindings/moz.build +++ b/dom/bindings/moz.build @@ -88,19 +88,16 @@ LOCAL_INCLUDES += [ '/layout/xul/tree', '/media/mtransport', '/media/webrtc/', '/media/webrtc/signaling/src/common/time_profiling', '/media/webrtc/signaling/src/peerconnection', '/media/webrtc/trunk/', ] -if CONFIG['MOZ_XBL']: - LOCAL_INCLUDES += ['/dom/xbl'] - LOCAL_INCLUDES += ['/third_party/msgpack/include'] DEFINES['GOOGLE_PROTOBUF_NO_RTTI'] = True DEFINES['GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER'] = True UNIFIED_SOURCES += [ 'BindingUtils.cpp', 'CallbackInterface.cpp',
--- a/dom/html/moz.build +++ b/dom/html/moz.build @@ -248,18 +248,15 @@ LOCAL_INCLUDES += [ '/layout/forms', '/layout/generic', '/layout/style', '/layout/tables', '/layout/xul', '/netwerk/base', ] -if CONFIG['MOZ_XBL']: - LOCAL_INCLUDES += ['/dom/xbl'] - FINAL_LIBRARY = 'xul' if CONFIG['MOZ_ANDROID_HLS_SUPPORT']: DEFINES['MOZ_ANDROID_HLS_SUPPORT'] = True if CONFIG['CC_TYPE'] in ('clang', 'gcc'): CXXFLAGS += ['-Wno-error=shadow']
--- a/dom/moz.build +++ b/dom/moz.build @@ -103,21 +103,16 @@ DIRS += [ 'serviceworkers', 'simpledb', 'reporting', 'localstorage', 'prio', 'l10n', ] -if CONFIG['MOZ_XBL']: - DIRS += ['xbl'] -else: - EXPORTS += ['xbl/stub/nsXBLBinding.h'] - if CONFIG['OS_ARCH'] == 'WINNT': DIRS += ['plugins/ipc/hangui'] DIRS += ['presentation'] TEST_DIRS += [ 'tests', 'imptests',
--- a/dom/prototype/moz.build +++ b/dom/prototype/moz.build @@ -15,14 +15,11 @@ SOURCES += [ 'PrototypeDocumentContentSink.cpp', ] LOCAL_INCLUDES += [ '/dom/base', '/dom/xul', ] -if CONFIG['MOZ_XBL']: - LOCAL_INCLUDES += ['/dom/xbl'] - MOCHITEST_CHROME_MANIFESTS += ['tests/chrome/chrome.ini'] FINAL_LIBRARY = 'xul'
--- a/dom/svg/moz.build +++ b/dom/svg/moz.build @@ -254,13 +254,10 @@ LOCAL_INCLUDES += [ '/dom/xml', '/layout/base', '/layout/generic', '/layout/style', '/layout/svg', '/layout/xul', ] -if CONFIG['MOZ_XBL']: - LOCAL_INCLUDES += ['/dom/xbl'] - if CONFIG['CC_TYPE'] in ('clang', 'gcc'): CXXFLAGS += ['-Wno-error=shadow']
deleted file mode 100644 --- a/dom/xbl/XBLChildrenElement.cpp +++ /dev/null @@ -1,177 +0,0 @@ -/* -*- 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/XBLChildrenElement.h" - -#include "mozilla/PresShell.h" -#include "mozilla/dom/NodeListBinding.h" -#include "nsAttrValueOrString.h" -#include "nsCharSeparatedTokenizer.h" - -namespace mozilla { -namespace dom { - -XBLChildrenElement::~XBLChildrenElement() {} - -NS_IMPL_ELEMENT_CLONE(XBLChildrenElement) - -nsresult XBLChildrenElement::BeforeSetAttr(int32_t aNamespaceID, nsAtom* aName, - const nsAttrValueOrString* aValue, - bool aNotify) { - if (aNamespaceID == kNameSpaceID_None) { - if (aName == nsGkAtoms::includes) { - mIncludes.Clear(); - if (aValue) { - nsCharSeparatedTokenizer tok( - aValue->String(), '|', - nsCharSeparatedTokenizer::SEPARATOR_OPTIONAL); - while (tok.hasMoreTokens()) { - mIncludes.AppendElement(NS_Atomize(tok.nextToken())); - } - } - } - } - - return nsXMLElement::BeforeSetAttr(aNamespaceID, aName, aValue, aNotify); -} - -void XBLChildrenElement::DoRemoveDefaultContent(bool aNotify) { - // Default content is going away, need to tell layout about it first. - MOZ_ASSERT(HasChildren(), "Why bothering?"); - MOZ_ASSERT(GetParentElement()); - - // We don't want to do this from frame construction while setting up the - // binding initially. - if (aNotify) { - Element* parent = GetParentElement(); - if (Document* doc = parent->GetComposedDoc()) { - if (PresShell* presShell = doc->GetPresShell()) { - presShell->DestroyFramesForAndRestyle(parent); - } - } - } - - for (nsIContent* child = static_cast<nsINode*>(this)->GetFirstChild(); child; - child = child->GetNextSibling()) { - MOZ_ASSERT(!child->GetPrimaryFrame()); - MOZ_ASSERT(!child->IsElement() || !child->AsElement()->HasServoData()); - child->SetXBLInsertionPoint(nullptr); - } -} - -} // namespace dom -} // namespace mozilla - -using namespace mozilla::dom; - -NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsAnonymousContentList, mParent) - -NS_IMPL_CYCLE_COLLECTING_ADDREF(nsAnonymousContentList) -NS_IMPL_CYCLE_COLLECTING_RELEASE(nsAnonymousContentList) - -NS_INTERFACE_TABLE_HEAD(nsAnonymousContentList) - NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY - NS_INTERFACE_TABLE(nsAnonymousContentList, nsINodeList) - NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsAnonymousContentList) -NS_INTERFACE_MAP_END - -uint32_t nsAnonymousContentList::Length() { - if (!mParent) { - return 0; - } - - uint32_t count = 0; - for (nsIContent* child = mParent->GetFirstChild(); child; - child = child->GetNextSibling()) { - if (child->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) { - XBLChildrenElement* point = static_cast<XBLChildrenElement*>(child); - if (point->HasInsertedChildren()) { - count += point->InsertedChildrenLength(); - } else { - count += point->GetChildCount(); - } - } else { - ++count; - } - } - - return count; -} - -nsIContent* nsAnonymousContentList::Item(uint32_t aIndex) { - if (!mParent) { - return nullptr; - } - - uint32_t remIndex = aIndex; - for (nsIContent* child = mParent->GetFirstChild(); child; - child = child->GetNextSibling()) { - if (child->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) { - XBLChildrenElement* point = static_cast<XBLChildrenElement*>(child); - if (point->HasInsertedChildren()) { - if (remIndex < point->InsertedChildrenLength()) { - return point->InsertedChild(remIndex); - } - remIndex -= point->InsertedChildrenLength(); - } else { - if (remIndex < point->GetChildCount()) { - return point->GetChildAt_Deprecated(remIndex); - } - remIndex -= point->GetChildCount(); - } - } else { - if (remIndex == 0) { - return child; - } - --remIndex; - } - } - - return nullptr; -} - -int32_t nsAnonymousContentList::IndexOf(nsIContent* aContent) { - NS_ASSERTION( - !aContent->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL), - "Looking for insertion point"); - - if (!mParent) { - return -1; - } - - int32_t index = 0; - for (nsIContent* child = mParent->GetFirstChild(); child; - child = child->GetNextSibling()) { - if (child->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) { - XBLChildrenElement* point = static_cast<XBLChildrenElement*>(child); - if (point->HasInsertedChildren()) { - int32_t insIndex = point->IndexOfInsertedChild(aContent); - if (insIndex != -1) { - return index + insIndex; - } - index += point->InsertedChildrenLength(); - } else { - int32_t insIndex = point->ComputeIndexOf(aContent); - if (insIndex != -1) { - return index + insIndex; - } - index += point->GetChildCount(); - } - } else { - if (child == aContent) { - return index; - } - ++index; - } - } - - return -1; -} - -JSObject* nsAnonymousContentList::WrapObject( - JSContext* cx, JS::Handle<JSObject*> aGivenProto) { - return mozilla::dom::NodeList_Binding::Wrap(cx, this, aGivenProto); -}
deleted file mode 100644 --- a/dom/xbl/XBLChildrenElement.h +++ /dev/null @@ -1,155 +0,0 @@ -/* -*- 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 nsXBLChildrenElement_h___ -#define nsXBLChildrenElement_h___ - -#include "nsINodeList.h" -#include "nsBindingManager.h" -#include "mozilla/dom/nsXMLElement.h" - -class nsAnonymousContentList; - -namespace mozilla { -namespace dom { - -class XBLChildrenElement : public nsXMLElement { - public: - explicit XBLChildrenElement( - already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo) - : nsXMLElement(std::move(aNodeInfo)) {} - - // nsISupports - NS_INLINE_DECL_REFCOUNTING_INHERITED(XBLChildrenElement, nsXMLElement) - - // nsINode interface methods - virtual nsresult Clone(dom::NodeInfo*, nsINode** aResult) const override; - - void AppendInsertedChild(nsIContent* aChild, bool aNotify) { - // Appending an inserted child causes the inserted - // children to be projected instead of default content. - MaybeRemoveDefaultContent(aNotify); - - mInsertedChildren.AppendElement(aChild); - aChild->SetXBLInsertionPoint(this); - } - - void InsertInsertedChildAt(nsIContent* aChild, uint32_t aIndex) { - // Inserting an inserted child causes the inserted - // children to be projected instead of default content. - MaybeRemoveDefaultContent(true); - - mInsertedChildren.InsertElementAt(aIndex, aChild); - aChild->SetXBLInsertionPoint(this); - } - - void RemoveInsertedChild(nsIContent* aChild) { - // Can't use this assertion as we cheat for dynamic insertions and - // only insert in the innermost insertion point. - // NS_ASSERTION(mInsertedChildren.Contains(aChild), - // "Removing child that's not there"); - mInsertedChildren.RemoveElement(aChild); - - // After removing the inserted child, default content - // may be projected into this insertion point. - // - // FIXME: Layout should be told about this before clearing - // mInsertedChildren, this leaves stale styles and frames in the frame tree. - MaybeSetupDefaultContent(); - } - - void ClearInsertedChildren() { - for (auto* child : mInsertedChildren) { - if (child->GetXBLInsertionPoint() == this) { - child->SetXBLInsertionPoint(nullptr); - } - } - mInsertedChildren.Clear(); - - // After clearing inserted children, default content - // will be projected into this insertion point. - // - // FIXME: Layout should be told about this before clearing - // mInsertedChildren, this leaves stale styles and frames in the frame tree. - MaybeSetupDefaultContent(); - } - - void MaybeSetupDefaultContent() { - if (!HasInsertedChildren()) { - for (nsIContent* child = static_cast<nsINode*>(this)->GetFirstChild(); - child; child = child->GetNextSibling()) { - child->SetXBLInsertionPoint(this); - } - } - } - - void MaybeRemoveDefaultContent(bool aNotify) { - if (!HasInsertedChildren() && HasChildren()) { - DoRemoveDefaultContent(aNotify); - } - } - - uint32_t InsertedChildrenLength() { return mInsertedChildren.Length(); } - - bool HasInsertedChildren() { return !mInsertedChildren.IsEmpty(); } - - int32_t IndexOfInsertedChild(nsIContent* aChild) { - return mInsertedChildren.IndexOf(aChild); - } - - bool Includes(nsIContent* aChild) { - NS_ASSERTION(!mIncludes.IsEmpty(), - "Shouldn't check for includes on default insertion point"); - return mIncludes.Contains(aChild->NodeInfo()->NameAtom()); - } - - bool IsDefaultInsertion() { return mIncludes.IsEmpty(); } - - nsIContent* InsertedChild(uint32_t aIndex) { - return mInsertedChildren[aIndex]; - } - - protected: - ~XBLChildrenElement(); - virtual nsresult BeforeSetAttr(int32_t aNamespaceID, nsAtom* aName, - const nsAttrValueOrString* aValue, - bool aNotify) override; - - void DoRemoveDefaultContent(bool aNotify); - - private: - nsTArray<nsIContent*> mInsertedChildren; // WEAK - nsTArray<RefPtr<nsAtom> > mIncludes; -}; - -} // namespace dom -} // namespace mozilla - -class nsAnonymousContentList final : public nsINodeList { - public: - explicit nsAnonymousContentList(nsIContent* aParent) : mParent(aParent) {} - - NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsAnonymousContentList) - - // nsINodeList interface - virtual int32_t IndexOf(nsIContent* aContent) override; - virtual nsINode* GetParentObject() override { return mParent; } - virtual nsIContent* Item(uint32_t aIndex) override; - uint32_t Length() override; - - virtual JSObject* WrapObject(JSContext* cx, - JS::Handle<JSObject*> aGivenProto) override; - - bool IsListFor(nsIContent* aContent) { return mParent == aContent; } - - private: - virtual ~nsAnonymousContentList() {} - - nsCOMPtr<nsIContent> mParent; -}; - -#endif // nsXBLChildrenElement_h___
deleted file mode 100644 --- a/dom/xbl/moz.build +++ /dev/null @@ -1,54 +0,0 @@ -# -*- 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/. - -with Files("**"): - BUG_COMPONENT = ("Core", "XBL") - -EXPORTS += [ - 'nsBindingManager.h', - 'nsXBLBinding.h', - 'nsXBLPrototypeHandler.h', - 'nsXBLService.h', -] - -EXPORTS.mozilla.dom += [ - 'XBLChildrenElement.h', -] - -UNIFIED_SOURCES += [ - 'nsBindingManager.cpp', - 'nsXBLBinding.cpp', - 'nsXBLContentSink.cpp', - 'nsXBLDocumentInfo.cpp', - 'nsXBLEventHandler.cpp', - 'nsXBLProtoImpl.cpp', - 'nsXBLProtoImplField.cpp', - 'nsXBLProtoImplMethod.cpp', - 'nsXBLProtoImplProperty.cpp', - 'nsXBLPrototypeBinding.cpp', - 'nsXBLPrototypeHandler.cpp', - 'nsXBLSerialize.cpp', - 'nsXBLService.cpp', - 'XBLChildrenElement.cpp', -] - -LOCAL_INCLUDES += [ - '/dom/base', - '/dom/html', - '/dom/xml', - '/dom/xul', - '/layout/style', -] - -include('/ipc/chromium/chromium-config.mozbuild') - -FINAL_LIBRARY = 'xul' - -MOCHITEST_MANIFESTS += ['test/mochitest.ini'] -MOCHITEST_CHROME_MANIFESTS += ['test/chrome.ini'] - -if CONFIG['CC_TYPE'] in ('clang', 'gcc'): - CXXFLAGS += ['-Wno-error=shadow']
deleted file mode 100644 --- a/dom/xbl/nsBindingManager.cpp +++ /dev/null @@ -1,876 +0,0 @@ -/* -*- 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 "nsBindingManager.h" - -#include "nsAutoPtr.h" -#include "nsCOMPtr.h" -#include "nsXBLService.h" -#include "nsIInputStream.h" -#include "nsIURI.h" -#include "nsIURL.h" -#include "nsIChannel.h" -#include "nsString.h" -#include "plstr.h" -#include "nsIContent.h" -#include "nsIContentInlines.h" -#include "mozilla/dom/Document.h" -#include "nsContentUtils.h" -#include "nsIXMLContentSink.h" -#include "nsContentCID.h" -#include "mozilla/dom/XMLDocument.h" -#include "nsIStreamListener.h" -#include "ChildIterator.h" -#include "nsITimer.h" - -#include "nsXBLBinding.h" -#include "nsXBLPrototypeBinding.h" -#include "nsXBLDocumentInfo.h" -#include "mozilla/dom/XBLChildrenElement.h" -#ifdef MOZ_XUL -# include "nsXULPrototypeCache.h" -#endif - -#include "nsIWeakReference.h" - -#include "nsWrapperCacheInlines.h" -#include "nsIXPConnect.h" -#include "nsDOMCID.h" -#include "nsIScriptGlobalObject.h" -#include "nsTHashtable.h" - -#include "nsIScriptContext.h" -#include "xpcpublic.h" -#include "js/Wrapper.h" - -#include "nsThreadUtils.h" -#include "mozilla/dom/NodeListBinding.h" -#include "mozilla/dom/ScriptSettings.h" -#include "mozilla/PresShell.h" -#include "mozilla/PresShellInlines.h" -#include "mozilla/Unused.h" - -using namespace mozilla; -using namespace mozilla::dom; - -// Implement our nsISupports methods - -NS_IMPL_CYCLE_COLLECTION_CLASS(nsBindingManager) - -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsBindingManager) - tmp->mDestroyed = true; - - if (tmp->mBoundContentSet) tmp->mBoundContentSet->Clear(); - - if (tmp->mDocumentTable) tmp->mDocumentTable->Clear(); - - if (tmp->mLoadingDocTable) tmp->mLoadingDocTable->Clear(); - - if (tmp->mWrapperTable) { - tmp->mWrapperTable->Clear(); - tmp->mWrapperTable = nullptr; - } - - NS_IMPL_CYCLE_COLLECTION_UNLINK(mAttachedStack) - - if (tmp->mProcessAttachedQueueEvent) { - tmp->mProcessAttachedQueueEvent->Revoke(); - } -NS_IMPL_CYCLE_COLLECTION_UNLINK_END - -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsBindingManager) - // The hashes keyed on nsIContent are traversed from the nsIContent itself. - if (tmp->mDocumentTable) { - for (auto iter = tmp->mDocumentTable->Iter(); !iter.Done(); iter.Next()) { - NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mDocumentTable value"); - cb.NoteXPCOMChild(iter.UserData()); - } - } - if (tmp->mLoadingDocTable) { - for (auto iter = tmp->mLoadingDocTable->Iter(); !iter.Done(); iter.Next()) { - NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mLoadingDocTable value"); - cb.NoteXPCOMChild(iter.UserData()); - } - } - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAttachedStack) - // No need to traverse mProcessAttachedQueueEvent, since it'll just - // fire at some point or become revoke and drop its ref to us. -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsBindingManager) - NS_INTERFACE_MAP_ENTRY(nsIMutationObserver) - NS_INTERFACE_MAP_ENTRY(nsISupports) -NS_INTERFACE_MAP_END - -NS_IMPL_CYCLE_COLLECTING_ADDREF(nsBindingManager) -NS_IMPL_CYCLE_COLLECTING_RELEASE(nsBindingManager) - -// Constructors/Destructors -nsBindingManager::nsBindingManager(Document* aDocument) - : mProcessingAttachedStack(false), - mDestroyed(false), - mAttachedStackSizeOnOutermost(0), - mDocument(aDocument) {} - -nsBindingManager::~nsBindingManager(void) { mDestroyed = true; } - -nsXBLBinding* nsBindingManager::GetBindingWithContent( - const nsIContent* aContent) { - nsXBLBinding* binding = aContent ? aContent->GetXBLBinding() : nullptr; - return binding ? binding->GetBindingWithContent() : nullptr; -} - -void nsBindingManager::AddBoundContent(nsIContent* aContent) { - if (!mBoundContentSet) { - mBoundContentSet = new nsTHashtable<nsRefPtrHashKey<nsIContent>>; - } - mBoundContentSet->PutEntry(aContent); -} - -void nsBindingManager::RemoveBoundContent(nsIContent* aContent) { - if (mBoundContentSet) { - mBoundContentSet->RemoveEntry(aContent); - } - - // The death of the bindings means the death of the JS wrapper. - SetWrappedJS(aContent, nullptr); -} - -nsIXPConnectWrappedJS* nsBindingManager::GetWrappedJS(nsIContent* aContent) { - if (!mWrapperTable) { - return nullptr; - } - - if (!aContent || !aContent->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) { - return nullptr; - } - - return mWrapperTable->GetWeak(aContent); -} - -nsresult nsBindingManager::SetWrappedJS(nsIContent* aContent, - nsIXPConnectWrappedJS* aWrappedJS) { - if (mDestroyed) { - return NS_OK; - } - - if (aWrappedJS) { - // lazily create the table, but only when adding elements - if (!mWrapperTable) { - mWrapperTable = new WrapperHashtable(); - } - aContent->SetFlags(NODE_MAY_BE_IN_BINDING_MNGR); - - NS_ASSERTION(aContent, "key must be non-null"); - if (!aContent) return NS_ERROR_INVALID_ARG; - - mWrapperTable->Put(aContent, aWrappedJS); - - return NS_OK; - } - - // no value, so remove the key from the table - if (mWrapperTable) { - mWrapperTable->Remove(aContent); - } - - return NS_OK; -} - -void nsBindingManager::RemovedFromDocumentInternal( - nsIContent* aContent, Document* aOldDocument, - DestructorHandling aDestructorHandling) { - MOZ_ASSERT(aOldDocument != nullptr, "no old document"); - - RefPtr<nsXBLBinding> binding = aContent->GetXBLBinding(); - if (binding) { - // The binding manager may have been destroyed before a runnable - // has had a chance to reach this point. If so, we bail out on calling - // BindingDetached (which may invoke a XBL destructor) and - // ChangeDocument, but we still want to clear out the binding - // and insertion parent that may hold references. - if (!mDestroyed && aDestructorHandling == eRunDtor) { - binding->PrototypeBinding()->BindingDetached(binding->GetBoundElement()); - binding->ChangeDocument(aOldDocument, nullptr); - } - - aContent->AsElement()->SetXBLBinding(nullptr, this); - } - - // Clear out insertion point and content lists. - aContent->SetXBLInsertionPoint(nullptr); -} - -nsINodeList* nsBindingManager::GetAnonymousNodesFor(nsIContent* aContent) { - nsXBLBinding* binding = GetBindingWithContent(aContent); - return binding ? binding->GetAnonymousNodeList() : nullptr; -} - -nsresult nsBindingManager::ClearBinding(Element* aElement) { - // Hold a ref to the binding so it won't die when we remove it from our table - RefPtr<nsXBLBinding> binding = aElement ? aElement->GetXBLBinding() : nullptr; - - if (!binding) { - return NS_OK; - } - - // For now we can only handle removing a binding if it's the only one - NS_ENSURE_FALSE(binding->GetBaseBinding(), NS_ERROR_FAILURE); - - // Hold strong ref in case removing the binding tries to close the - // window or something. - // XXXbz should that be ownerdoc? Wouldn't we need a ref to the - // currentdoc too? What's the one that should be passed to - // ChangeDocument? - nsCOMPtr<Document> doc = aElement->OwnerDoc(); - - // Destroy the frames here before the UnbindFromTree happens. - if (PresShell* presShell = doc->GetPresShell()) { - presShell->DestroyFramesForAndRestyle(aElement); - } - - // Finally remove the binding... - // XXXbz this doesn't remove the implementation! Should fix! Until - // then we need the explicit UnhookEventHandlers here. - binding->UnhookEventHandlers(); - binding->ChangeDocument(doc, nullptr); - aElement->SetXBLBinding(nullptr, this); - binding->MarkForDeath(); - - // ...and recreate its frames. We need to do this since the frames may have - // been removed and style may have changed due to the removal of the - // anonymous children. - // XXXbz this should be using the current doc (if any), not the owner doc. - // get the shell again, just in case it changed - PresShell* presShell = doc->GetPresShell(); - NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE); - presShell->PostRecreateFramesFor(aElement); - return NS_OK; -} - -void nsBindingManager::RemoveFromAttachedQueue(nsXBLBinding* aBinding) { - // Don't remove items here as that could mess up an executing - // ProcessAttachedQueue. Instead, null the entry in the queue. - size_t index = mAttachedStack.IndexOf(aBinding); - if (index != nsBindingList::NoIndex) { - mAttachedStack[index] = nullptr; - } -} - -nsresult nsBindingManager::AddToAttachedQueue(nsXBLBinding* aBinding) { - mAttachedStack.AppendElement(aBinding); - - // If we're in the middle of processing our queue already, don't - // bother posting the event. - if (!mProcessingAttachedStack && !mProcessAttachedQueueEvent) { - PostProcessAttachedQueueEvent(); - } - - // Make sure that flushes will flush out the new items as needed. - if (PresShell* presShell = mDocument->GetPresShell()) { - presShell->SetNeedStyleFlush(); - } - - return NS_OK; -} - -void nsBindingManager::PostProcessAttachedQueueEvent() { - MOZ_ASSERT(NS_IsMainThread()); - if (!mDocument) { - return; - } - mProcessAttachedQueueEvent = - NewRunnableMethod("nsBindingManager::DoProcessAttachedQueue", this, - &nsBindingManager::DoProcessAttachedQueue); - nsresult rv = mDocument->EventTargetFor(TaskCategory::Other) - ->Dispatch(do_AddRef(mProcessAttachedQueueEvent)); - if (NS_SUCCEEDED(rv)) { - mDocument->BlockOnload(); - } -} - -// static -void nsBindingManager::PostPAQEventCallback(nsITimer* aTimer, void* aClosure) { - RefPtr<nsBindingManager> mgr = already_AddRefed<nsBindingManager>( - static_cast<nsBindingManager*>(aClosure)); - mgr->PostProcessAttachedQueueEvent(); - NS_RELEASE(aTimer); -} - -void nsBindingManager::DoProcessAttachedQueue() { - if (!mProcessingAttachedStack) { - ProcessAttachedQueue(); - - NS_ASSERTION(mAttachedStack.Length() == 0, - "Shouldn't have pending bindings!"); - - mProcessAttachedQueueEvent = nullptr; - } else { - // Someone's doing event processing from inside a constructor. - // They're evil, but we'll fight back! Just poll on them being - // done and repost the attached queue event. - // - // But don't poll in a tight loop -- otherwise we keep the Gecko - // event loop non-empty and trigger bug 1021240 on OS X. - nsresult rv = NS_ERROR_FAILURE; - nsCOMPtr<nsITimer> timer; - rv = NS_NewTimerWithFuncCallback( - getter_AddRefs(timer), PostPAQEventCallback, this, 100, - nsITimer::TYPE_ONE_SHOT, "nsBindingManager::DoProcessAttachedQueue"); - if (NS_SUCCEEDED(rv)) { - NS_ADDREF_THIS(); - // We drop our reference to the timer here, since the timer callback is - // responsible for releasing the object. - Unused << timer.forget().take(); - } - } - - // No matter what, unblock onload for the event that's fired. - if (mDocument) { - // Hold a strong reference while calling UnblockOnload since that might - // run script. - nsCOMPtr<Document> doc = mDocument; - doc->UnblockOnload(true); - } -} - -void nsBindingManager::ProcessAttachedQueueInternal(uint32_t aSkipSize) { - mProcessingAttachedStack = true; - - // Excute constructors. Do this from high index to low - while (mAttachedStack.Length() > aSkipSize) { - uint32_t lastItem = mAttachedStack.Length() - 1; - RefPtr<nsXBLBinding> binding = mAttachedStack.ElementAt(lastItem); - mAttachedStack.RemoveElementAt(lastItem); - if (binding) { - binding->ExecuteAttachedHandler(); - } - } - - // If NodeWillBeDestroyed has run we don't want to clobber - // mProcessingAttachedStack set there. - if (mDocument) { - mProcessingAttachedStack = false; - } - - NS_ASSERTION(mAttachedStack.Length() == aSkipSize, "How did we get here?"); - - mAttachedStack.Compact(); -} - -// Keep bindings and bound elements alive while executing detached handlers. -void nsBindingManager::ExecuteDetachedHandlers() { - // Walk our hashtable of bindings. - if (!mBoundContentSet) { - return; - } - - nsCOMArray<nsIContent> boundElements; - nsBindingList bindings; - - for (auto iter = mBoundContentSet->Iter(); !iter.Done(); iter.Next()) { - nsXBLBinding* binding = iter.Get()->GetKey()->GetXBLBinding(); - if (binding && bindings.AppendElement(binding)) { - if (!boundElements.AppendObject(binding->GetBoundElement())) { - bindings.RemoveLastElement(); - } - } - } - - uint32_t i, count = bindings.Length(); - for (i = 0; i < count; ++i) { - bindings[i]->ExecuteDetachedHandler(); - } -} - -nsresult nsBindingManager::PutXBLDocumentInfo( - nsXBLDocumentInfo* aDocumentInfo) { - MOZ_ASSERT(aDocumentInfo, "Must have a non-null documentinfo!"); - - if (!mDocumentTable) { - mDocumentTable = new nsRefPtrHashtable<nsURIHashKey, nsXBLDocumentInfo>(); - } - - mDocumentTable->Put(aDocumentInfo->DocumentURI(), aDocumentInfo); - - return NS_OK; -} - -void nsBindingManager::RemoveXBLDocumentInfo(nsXBLDocumentInfo* aDocumentInfo) { - if (mDocumentTable) { - mDocumentTable->Remove(aDocumentInfo->DocumentURI()); - } -} - -nsXBLDocumentInfo* nsBindingManager::GetXBLDocumentInfo(nsIURI* aURL) { - if (!mDocumentTable) return nullptr; - - return mDocumentTable->GetWeak(aURL); -} - -nsresult nsBindingManager::PutLoadingDocListener(nsIURI* aURL, - nsIStreamListener* aListener) { - MOZ_ASSERT(aListener, "Must have a non-null listener!"); - - if (!mLoadingDocTable) { - mLoadingDocTable = - new nsInterfaceHashtable<nsURIHashKey, nsIStreamListener>(); - } - mLoadingDocTable->Put(aURL, aListener); - - return NS_OK; -} - -nsIStreamListener* nsBindingManager::GetLoadingDocListener(nsIURI* aURL) { - if (!mLoadingDocTable) return nullptr; - - return mLoadingDocTable->GetWeak(aURL); -} - -void nsBindingManager::RemoveLoadingDocListener(nsIURI* aURL) { - if (mLoadingDocTable) { - mLoadingDocTable->Remove(aURL); - } -} - -// Used below to protect from recurring in QI calls through XPConnect. -struct AntiRecursionData { - nsIContent* element; - REFNSIID iid; - AntiRecursionData* next; - - AntiRecursionData(nsIContent* aElement, REFNSIID aIID, - AntiRecursionData* aNext) - : element(aElement), iid(aIID), next(aNext) {} -}; - -nsresult nsBindingManager::GetBindingImplementation(nsIContent* aContent, - REFNSIID aIID, - void** aResult) { - *aResult = nullptr; - nsXBLBinding* binding = aContent ? aContent->GetXBLBinding() : nullptr; - if (binding) { - // The binding should not be asked for nsISupports - NS_ASSERTION(!aIID.Equals(NS_GET_IID(nsISupports)), - "Asking a binding for nsISupports"); - if (binding->ImplementsInterface(aIID)) { - nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS = GetWrappedJS(aContent); - - if (wrappedJS) { - // Protect from recurring in QI calls through XPConnect. - // This can happen when a second binding is being resolved. - // At that point a wrappedJS exists, but it doesn't yet know about - // the iid we are asking for. So, without this protection, - // AggregatedQueryInterface would end up recurring back into itself - // through this code. - // - // With this protection, when we detect the recursion we return - // NS_NOINTERFACE in the inner call. The outer call will then fall - // through (see below) and build a new chained wrappedJS for the iid. - // - // We're careful to not assume that only one direct nesting can occur - // because there is a call into JS in the middle and we can't assume - // that this code won't be reached by some more complex nesting path. - // - // NOTE: We *assume* this is single threaded, so we can use a - // static linked list to do the check. - - static AntiRecursionData* list = nullptr; - - for (AntiRecursionData* p = list; p; p = p->next) { - if (p->element == aContent && p->iid.Equals(aIID)) { - *aResult = nullptr; - return NS_NOINTERFACE; - } - } - - AntiRecursionData item(aContent, aIID, list); - list = &item; - - nsresult rv = wrappedJS->AggregatedQueryInterface(aIID, aResult); - - list = item.next; - - if (*aResult) return rv; - - // No result was found, so this must be another XBL interface. - // Fall through to create a new wrapper. - } - - // We have never made a wrapper for this implementation. - // Create an XPC wrapper for the script object and hand it back. - AutoJSAPI jsapi; - jsapi.Init(); - JSContext* cx = jsapi.cx(); - - nsIXPConnect* xpConnect = nsContentUtils::XPConnect(); - - JS::Rooted<JSObject*> jsobj(cx, aContent->GetWrapper()); - NS_ENSURE_TRUE(jsobj, NS_NOINTERFACE); - - // If we're using an XBL scope, we need to use the Xray view to the bound - // content in order to view the full array of methods defined in the - // binding, some of which may not be exposed on the prototype of - // untrusted content. We don't need to consider add-on scopes here - // because they're chrome-only and no Xrays are involved. - // - // If there's no separate XBL scope, or if the reflector itself lives in - // the XBL scope, we'll end up with the global of the reflector. - JS::Rooted<JSObject*> xblScope(cx, xpc::GetXBLScopeOrGlobal(cx, jsobj)); - NS_ENSURE_TRUE(xblScope, NS_ERROR_UNEXPECTED); - JSAutoRealm ar(cx, xblScope); - bool ok = JS_WrapObject(cx, &jsobj); - NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY); - MOZ_ASSERT_IF(js::IsWrapper(jsobj), xpc::IsXrayWrapper(jsobj)); - - nsresult rv = xpConnect->WrapJSAggregatedToNative(aContent, cx, jsobj, - aIID, aResult); - if (NS_FAILED(rv)) return rv; - - // We successfully created a wrapper. We will own this wrapper for as - // long as the binding remains alive. At the time the binding is cleared - // out of the bindingManager, we will remove the wrapper from the - // bindingManager as well. - nsISupports* supp = static_cast<nsISupports*>(*aResult); - wrappedJS = do_QueryInterface(supp); - SetWrappedJS(aContent, wrappedJS); - - return rv; - } - } - - *aResult = nullptr; - return NS_NOINTERFACE; -} - -static void InsertAppendedContent(XBLChildrenElement* aPoint, - nsIContent* aFirstNewContent) { - int32_t insertionIndex; - if (nsIContent* prevSibling = aFirstNewContent->GetPreviousSibling()) { - // If we have a previous sibling, then it must already be in aPoint. Find - // it and insert after it. - insertionIndex = aPoint->IndexOfInsertedChild(prevSibling); - MOZ_ASSERT(insertionIndex != -1); - - // Our insertion index is one after our previous sibling's index. - ++insertionIndex; - } else { - // Otherwise, we append. - // TODO This is wrong for nested insertion points. In that case, we need to - // keep track of the right index to insert into. - insertionIndex = aPoint->InsertedChildrenLength(); - } - - // Do the inserting. - for (nsIContent* currentChild = aFirstNewContent; currentChild; - currentChild = currentChild->GetNextSibling()) { - aPoint->InsertInsertedChildAt(currentChild, insertionIndex++); - } -} - -void nsBindingManager::ContentAppended(nsIContent* aFirstNewContent) { - // Try to find insertion points for all the new kids. - XBLChildrenElement* point = nullptr; - nsIContent* container = aFirstNewContent->GetParent(); - nsIContent* parent = container; - - // Handle appending of default content. - if (parent && parent->IsActiveChildrenElement()) { - XBLChildrenElement* childrenEl = static_cast<XBLChildrenElement*>(parent); - if (childrenEl->HasInsertedChildren()) { - // Appending default content that isn't being used. Ignore. - return; - } - - childrenEl->MaybeSetupDefaultContent(); - parent = childrenEl->GetParent(); - } - - bool first = true; - do { - nsXBLBinding* binding = GetBindingWithContent(parent); - if (!binding) { - break; - } - - if (binding->HasFilteredInsertionPoints()) { - // There are filtered insertion points involved, handle each child - // separately. - // We could optimize this in the case when we've nested a few levels - // deep already, without hitting bindings that have filtered insertion - // points. - for (nsIContent* currentChild = aFirstNewContent; currentChild; - currentChild = currentChild->GetNextSibling()) { - HandleChildInsertion(container, currentChild, true); - } - - return; - } - - point = binding->GetDefaultInsertionPoint(); - if (!point) { - break; - } - - // Even though we're in ContentAppended, nested insertion points force us - // to deal with this append as an insertion except in the outermost - // binding. - if (first) { - first = false; - for (nsIContent* child = aFirstNewContent; child; - child = child->GetNextSibling()) { - point->AppendInsertedChild(child, true); - } - } else { - InsertAppendedContent(point, aFirstNewContent); - } - - nsIContent* newParent = point->GetParent(); - if (newParent == parent) { - break; - } - parent = newParent; - } while (parent); -} - -void nsBindingManager::ContentInserted(nsIContent* aChild) { - HandleChildInsertion(aChild->GetParent(), aChild, false); -} - -void nsBindingManager::ContentRemoved(nsIContent* aChild, - nsIContent* aPreviousSibling) { - aChild->SetXBLInsertionPoint(nullptr); - - XBLChildrenElement* point = nullptr; - nsIContent* parent = aChild->GetParent(); - - // Handle appending of default content. - if (parent && parent->IsActiveChildrenElement()) { - XBLChildrenElement* childrenEl = static_cast<XBLChildrenElement*>(parent); - if (childrenEl->HasInsertedChildren()) { - // Removing default content that isn't being used. Ignore. - return; - } - - parent = childrenEl->GetParent(); - } - - do { - nsXBLBinding* binding = GetBindingWithContent(parent); - if (!binding) { - // If aChild is XBL content, it might have <xbl:children> elements - // somewhere under it. We need to inform those elements that they're no - // longer in the tree so they can tell their distributed children that - // they're no longer distributed under them. - // XXX This is wrong. We need to do far more work to update the parent - // binding's list of insertion points and to get the new insertion parent - // for the newly-distributed children correct. - if (aChild->GetBindingParent()) { - ClearInsertionPointsRecursively(aChild); - } - return; - } - - point = binding->FindInsertionPointFor(aChild); - if (!point) { - break; - } - - point->RemoveInsertedChild(aChild); - - nsIContent* newParent = point->GetParent(); - if (newParent == parent) { - break; - } - parent = newParent; - } while (parent); -} - -void nsBindingManager::ClearInsertionPointsRecursively(nsIContent* aContent) { - if (aContent->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) { - static_cast<XBLChildrenElement*>(aContent)->ClearInsertedChildren(); - } - - for (nsIContent* child = aContent->GetFirstChild(); child; - child = child->GetNextSibling()) { - ClearInsertionPointsRecursively(child); - } -} - -void nsBindingManager::DropDocumentReference() { - mDestroyed = true; - - // Make sure to not run any more XBL constructors - mProcessingAttachedStack = true; - if (mProcessAttachedQueueEvent) { - mProcessAttachedQueueEvent->Revoke(); - } - - if (mBoundContentSet) { - mBoundContentSet->Clear(); - } - - mDocument = nullptr; -} - -void nsBindingManager::Traverse(nsIContent* aContent, - nsCycleCollectionTraversalCallback& cb) { - if (!aContent->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR) || - !aContent->IsElement()) { - // Don't traverse if content is not in this binding manager. - // We also don't traverse non-elements because there should not - // be bindings (checking the flag alone is not sufficient because - // the flag is also set on children of insertion points that may be - // non-elements). - return; - } - - if (mBoundContentSet && mBoundContentSet->Contains(aContent)) { - NS_CYCLE_COLLECTION_NOTE_EDGE_NAME( - cb, "[via binding manager] mBoundContentSet entry"); - cb.NoteXPCOMChild(aContent); - } - - nsIXPConnectWrappedJS* value = GetWrappedJS(aContent); - if (value) { - NS_CYCLE_COLLECTION_NOTE_EDGE_NAME( - cb, "[via binding manager] mWrapperTable key"); - cb.NoteXPCOMChild(aContent); - NS_CYCLE_COLLECTION_NOTE_EDGE_NAME( - cb, "[via binding manager] mWrapperTable value"); - cb.NoteXPCOMChild(value); - } -} - -void nsBindingManager::HandleChildInsertion(nsIContent* aContainer, - nsIContent* aChild, bool aAppend) { - MOZ_ASSERT(aChild, "Must have child"); - - XBLChildrenElement* point = nullptr; - nsIContent* parent = aContainer; - - // Handle insertion of default content. - if (parent && parent->IsActiveChildrenElement()) { - XBLChildrenElement* childrenEl = static_cast<XBLChildrenElement*>(parent); - if (childrenEl->HasInsertedChildren()) { - // Inserting default content that isn't being used. Ignore. - return; - } - - childrenEl->MaybeSetupDefaultContent(); - parent = childrenEl->GetParent(); - } - - while (parent) { - nsXBLBinding* binding = GetBindingWithContent(parent); - if (!binding) { - break; - } - - point = binding->FindInsertionPointFor(aChild); - if (!point) { - break; - } - - // Insert the child into the proper insertion point. - // TODO If there were multiple insertion points, this approximation can be - // wrong. We need to re-run the distribution algorithm. In the meantime, - // this should work well enough. - uint32_t index = aAppend ? point->InsertedChildrenLength() : 0; - for (nsIContent* currentSibling = aChild->GetPreviousSibling(); - currentSibling; - currentSibling = currentSibling->GetPreviousSibling()) { - // If we find one of our previous siblings in the insertion point, the - // index following it is the correct insertion point. Otherwise, we guess - // based on whether we're appending or inserting. - int32_t pointIndex = point->IndexOfInsertedChild(currentSibling); - if (pointIndex != -1) { - index = pointIndex + 1; - break; - } - } - - point->InsertInsertedChildAt(aChild, index); - - nsIContent* newParent = point->GetParent(); - if (newParent == parent) { - break; - } - - parent = newParent; - } -} - -nsIContent* nsBindingManager::FindNestedSingleInsertionPoint( - nsIContent* aContainer, bool* aMulti) { - *aMulti = false; - - nsIContent* parent = aContainer; - if (aContainer->IsActiveChildrenElement()) { - if (static_cast<XBLChildrenElement*>(aContainer)->HasInsertedChildren()) { - return nullptr; - } - parent = aContainer->GetParent(); - } - - while (parent) { - nsXBLBinding* binding = GetBindingWithContent(parent); - if (!binding) { - break; - } - - if (binding->HasFilteredInsertionPoints()) { - *aMulti = true; - return nullptr; - } - - XBLChildrenElement* point = binding->GetDefaultInsertionPoint(); - if (!point) { - return nullptr; - } - - nsIContent* newParent = point->GetParent(); - if (newParent == parent) { - break; - } - parent = newParent; - } - - return parent; -} - -size_t nsBindingManager::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { - size_t n = aMallocSizeOf(this); - -#define SHALLOW_SIZE_INCLUDING(field_) \ - n += field_ ? field_->ShallowSizeOfIncludingThis(aMallocSizeOf) : 0; - SHALLOW_SIZE_INCLUDING(mBoundContentSet); - SHALLOW_SIZE_INCLUDING(mWrapperTable); - SHALLOW_SIZE_INCLUDING(mLoadingDocTable); -#undef SHALLOW_SIZE_INCLUDING - n += mAttachedStack.ShallowSizeOfExcludingThis(aMallocSizeOf); - - if (mDocumentTable) { - n += mDocumentTable->ShallowSizeOfIncludingThis(aMallocSizeOf); -#ifdef MOZ_XUL - nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance(); -#endif - for (auto iter = mDocumentTable->Iter(); !iter.Done(); iter.Next()) { - nsXBLDocumentInfo* docInfo = iter.UserData(); -#ifdef MOZ_XUL - nsXBLDocumentInfo* cachedInfo = cache->GetXBLDocumentInfo(iter.Key()); - if (cachedInfo == docInfo) { - // If this binding has been cached, skip it since it can be - // reused by other documents. - continue; - } -#endif - n += docInfo->SizeOfIncludingThis(aMallocSizeOf); - } - } - - return n; -}
deleted file mode 100644 --- a/dom/xbl/nsBindingManager.h +++ /dev/null @@ -1,204 +0,0 @@ -/* -*- 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 nsBindingManager_h_ -#define nsBindingManager_h_ - -#include "nsAutoPtr.h" -#include "nsIContent.h" -#include "nsStubMutationObserver.h" -#include "nsHashKeys.h" -#include "nsInterfaceHashtable.h" -#include "nsRefPtrHashtable.h" -#include "nsURIHashKey.h" -#include "nsCycleCollectionParticipant.h" -#include "nsXBLBinding.h" -#include "nsTArray.h" -#include "nsThreadUtils.h" -#include "mozilla/MediaFeatureChange.h" -#include "mozilla/MemoryReporting.h" -#include "mozilla/EventStates.h" - -struct ElementDependentRuleProcessorData; -class nsIXPConnectWrappedJS; -class nsAtom; -class nsIURI; -class nsXBLDocumentInfo; -class nsIStreamListener; -class nsXBLBinding; -typedef nsTArray<RefPtr<nsXBLBinding> > nsBindingList; -class nsIPrincipal; -class nsITimer; - -class nsBindingManager final : public nsStubMutationObserver { - ~nsBindingManager(); - - public: - NS_DECL_CYCLE_COLLECTING_ISUPPORTS - - NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED - NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED - NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED - - explicit nsBindingManager(mozilla::dom::Document* aDocument); - - nsXBLBinding* GetBindingWithContent(const nsIContent* aContent); - - void AddBoundContent(nsIContent* aContent); - void RemoveBoundContent(nsIContent* aContent); - - /** - * Notify the binding manager that an element - * has been removed from its document, - * so that it can update any bindings or - * nsIAnonymousContentCreator-created anonymous - * content that may depend on the document. - * @param aContent the element that's being moved - * @param aOldDocument the old document in which the - * content resided. - * @param aDestructorHandling whether or not to run the possible XBL - * destructor. - */ - - enum DestructorHandling { eRunDtor, eDoNotRunDtor }; - void RemovedFromDocument(nsIContent* aContent, - mozilla::dom::Document* aOldDocument, - DestructorHandling aDestructorHandling) { - if (aContent->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) { - RemovedFromDocumentInternal(aContent, aOldDocument, aDestructorHandling); - } - } - void RemovedFromDocumentInternal(nsIContent* aContent, - mozilla::dom::Document* aOldDocument, - DestructorHandling aDestructorHandling); - - /** - * Return the nodelist of "anonymous" kids for this node. This might - * actually include some of the nodes actual DOM kids, if there are - * <children> tags directly as kids of <content>. This will only end up - * returning a non-null list for nodes which have a binding attached. - */ - nsINodeList* GetAnonymousNodesFor(nsIContent* aContent); - - nsresult ClearBinding(mozilla::dom::Element* aElement); - - nsresult AddToAttachedQueue(nsXBLBinding* aBinding); - void RemoveFromAttachedQueue(nsXBLBinding* aBinding); - void ProcessAttachedQueue(uint32_t aSkipSize = 0) { - if (mProcessingAttachedStack || mAttachedStack.Length() <= aSkipSize) { - return; - } - - ProcessAttachedQueueInternal(aSkipSize); - } - - private: - void ProcessAttachedQueueInternal(uint32_t aSkipSize); - - public: - void ExecuteDetachedHandlers(); - - nsresult PutXBLDocumentInfo(nsXBLDocumentInfo* aDocumentInfo); - nsXBLDocumentInfo* GetXBLDocumentInfo(nsIURI* aURI); - void RemoveXBLDocumentInfo(nsXBLDocumentInfo* aDocumentInfo); - - nsresult PutLoadingDocListener(nsIURI* aURL, nsIStreamListener* aListener); - nsIStreamListener* GetLoadingDocListener(nsIURI* aURL); - void RemoveLoadingDocListener(nsIURI* aURL); - - nsresult GetBindingImplementation(nsIContent* aContent, REFNSIID aIID, - void** aResult); - - void Traverse(nsIContent* aContent, nsCycleCollectionTraversalCallback& cb); - - NS_DECL_CYCLE_COLLECTION_CLASS(nsBindingManager) - - // Notify the binding manager when an outermost update begins and - // ends. The end method can execute script. - void BeginOutermostUpdate() { - mAttachedStackSizeOnOutermost = mAttachedStack.Length(); - } - - void EndOutermostUpdate() { - if (!mProcessingAttachedStack) { - ProcessAttachedQueue(mAttachedStackSizeOnOutermost); - mAttachedStackSizeOnOutermost = 0; - } - } - - // When removing an insertion point or a parent of one, clear the insertion - // points and their insertion parents. - void ClearInsertionPointsRecursively(nsIContent* aContent); - - // Called when the document is going away - void DropDocumentReference(); - - nsIContent* FindNestedSingleInsertionPoint(nsIContent* aContainer, - bool* aMulti); - - size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; - - protected: - nsIXPConnectWrappedJS* GetWrappedJS(nsIContent* aContent); - nsresult SetWrappedJS(nsIContent* aContent, nsIXPConnectWrappedJS* aResult); - - // Called by ContentAppended and ContentInserted to handle a single child - // insertion. aChild must not be null. aContainer may be null. - // aAppend is true if this child is being appended, not inserted. - void HandleChildInsertion(nsIContent* aContainer, nsIContent* aChild, - bool aAppend); - - // Same as ProcessAttachedQueue, but also nulls out - // mProcessAttachedQueueEvent - void DoProcessAttachedQueue(); - - // Post an event to process the attached queue. - void PostProcessAttachedQueueEvent(); - - // Call PostProcessAttachedQueueEvent() on a timer. - static void PostPAQEventCallback(nsITimer* aTimer, void* aClosure); - - // MEMBER VARIABLES - // A set of nsIContent that currently have a binding installed. - nsAutoPtr<nsTHashtable<nsRefPtrHashKey<nsIContent> > > mBoundContentSet; - - // A mapping from nsIContent* to nsIXPWrappedJS* (an XPConnect - // wrapper for JS objects). For XBL bindings that implement XPIDL - // interfaces, and that get referred to from C++, this table caches - // the XPConnect wrapper for the binding. By caching it, I control - // its lifetime, and I prevent a re-wrap of the same script object - // (in the case where multiple bindings in an XBL inheritance chain - // both implement an XPIDL interface). - typedef nsInterfaceHashtable<nsISupportsHashKey, nsIXPConnectWrappedJS> - WrapperHashtable; - nsAutoPtr<WrapperHashtable> mWrapperTable; - - // A mapping from a URL (a string) to nsXBLDocumentInfo*. This table - // is the cache of all binding documents that have been loaded by a - // given bound document. - nsAutoPtr<nsRefPtrHashtable<nsURIHashKey, nsXBLDocumentInfo> > mDocumentTable; - - // A mapping from a URL (a string) to a nsIStreamListener. This - // table is the currently loading binding docs. If they're in this - // table, they have not yet finished loading. - nsAutoPtr<nsInterfaceHashtable<nsURIHashKey, nsIStreamListener> > - mLoadingDocTable; - - // A queue of binding attached event handlers that are awaiting execution. - nsBindingList mAttachedStack; - bool mProcessingAttachedStack; - bool mDestroyed; - uint32_t mAttachedStackSizeOnOutermost; - - // Our posted event to process the attached queue, if any - friend class nsRunnableMethod<nsBindingManager>; - RefPtr<nsRunnableMethod<nsBindingManager> > mProcessAttachedQueueEvent; - - // Our document. This is a weak ref; the document owns us - mozilla::dom::Document* mDocument; -}; - -#endif
deleted file mode 100644 --- a/dom/xbl/nsXBLBinding.cpp +++ /dev/null @@ -1,1028 +0,0 @@ -/* -*- 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 "nsCOMPtr.h" -#include "nsAtom.h" -#include "nsXBLDocumentInfo.h" -#include "nsIInputStream.h" -#include "nsNameSpaceManager.h" -#include "nsIURI.h" -#include "nsIURL.h" -#include "nsIChannel.h" -#include "nsString.h" -#include "nsReadableUtils.h" -#include "plstr.h" -#include "nsIContent.h" -#include "mozilla/dom/BindContext.h" -#include "mozilla/dom/Document.h" -#include "nsContentUtils.h" -#include "ChildIterator.h" -#include "nsIXMLContentSink.h" -#include "nsContentCID.h" -#include "mozilla/dom/XMLDocument.h" -#include "jsapi.h" -#include "nsXBLService.h" -#include "nsIXPConnect.h" -#include "nsIScriptContext.h" -#include "nsCRT.h" - -// Event listeners -#include "mozilla/EventListenerManager.h" -#include "nsIDOMEventListener.h" -#include "nsAttrName.h" - -#include "nsGkAtoms.h" - -#include "nsXBLPrototypeHandler.h" - -#include "nsXBLPrototypeBinding.h" -#include "nsXBLBinding.h" -#include "nsIPrincipal.h" -#include "nsIScriptSecurityManager.h" -#include "mozilla/dom/XBLChildrenElement.h" - -#include "nsJSUtils.h" - -#include "mozilla/DeferredFinalize.h" -#include "mozilla/dom/Element.h" -#include "mozilla/dom/ScriptSettings.h" - -using namespace mozilla; -using namespace mozilla::dom; - -// Helper classes - -/***********************************************************************/ -// -// The JS class for XBLBinding -// -static void XBLFinalize(JSFreeOp* fop, JSObject* obj) { - nsXBLDocumentInfo* docInfo = - static_cast<nsXBLDocumentInfo*>(::JS_GetPrivate(obj)); - DeferredFinalize(docInfo); -} - -static bool XBLEnumerate(JSContext* cx, JS::Handle<JSObject*> obj) { - nsXBLPrototypeBinding* protoBinding = static_cast<nsXBLPrototypeBinding*>( - ::JS_GetReservedSlot(obj, 0).toPrivate()); - MOZ_ASSERT(protoBinding); - - return protoBinding->ResolveAllFields(cx, obj); -} - -static const JSClassOps gPrototypeJSClassOps = { - nullptr, nullptr, XBLEnumerate, nullptr, nullptr, nullptr, - XBLFinalize, nullptr, nullptr, nullptr, nullptr}; - -static const JSClass gPrototypeJSClass = { - "XBL prototype JSClass", - JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS | - JSCLASS_FOREGROUND_FINALIZE | - // Our one reserved slot holds the relevant nsXBLPrototypeBinding - JSCLASS_HAS_RESERVED_SLOTS(1), - &gPrototypeJSClassOps}; - -// Implementation ////////////////////////////////////////////////////////////// - -// Constructors/Destructors -nsXBLBinding::nsXBLBinding(nsXBLPrototypeBinding* aBinding) - : mMarkedForDeath(false), - mPrototypeBinding(aBinding), - mBoundElement(nullptr) { - NS_ASSERTION(mPrototypeBinding, "Must have a prototype binding!"); - // Grab a ref to the document info so the prototype binding won't die - NS_ADDREF(mPrototypeBinding->XBLDocumentInfo()); -} - -nsXBLBinding::~nsXBLBinding() { - if (mContent) { - nsXBLBinding::UnbindAnonymousContent(mContent->OwnerDoc(), mContent); - } - nsXBLDocumentInfo* info = mPrototypeBinding->XBLDocumentInfo(); - NS_RELEASE(info); -} - -NS_IMPL_CYCLE_COLLECTION_CLASS(nsXBLBinding) - -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXBLBinding) - // XXX Probably can't unlink mPrototypeBinding->XBLDocumentInfo(), because - // mPrototypeBinding is weak. - if (tmp->mContent) { - nsXBLBinding::UnbindAnonymousContent(tmp->mContent->OwnerDoc(), - tmp->mContent); - } - NS_IMPL_CYCLE_COLLECTION_UNLINK(mContent) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mNextBinding) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mDefaultInsertionPoint) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mInsertionPoints) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnonymousContentList) -NS_IMPL_CYCLE_COLLECTION_UNLINK_END -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXBLBinding) - NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, - "mPrototypeBinding->XBLDocumentInfo()"); - cb.NoteXPCOMChild(tmp->mPrototypeBinding->XBLDocumentInfo()); - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContent) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNextBinding) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDefaultInsertionPoint) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInsertionPoints) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnonymousContentList) -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END -NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsXBLBinding, AddRef) -NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsXBLBinding, Release) - -void nsXBLBinding::SetBaseBinding(nsXBLBinding* aBinding) { - if (mNextBinding) { - NS_ERROR("Base XBL binding is already defined!"); - return; - } - - mNextBinding = aBinding; // Comptr handles rel/add -} - -nsXBLBinding* nsXBLBinding::GetBindingWithContent() { - if (mContent) { - return this; - } - - return mNextBinding ? mNextBinding->GetBindingWithContent() : nullptr; -} - -void nsXBLBinding::BindAnonymousContent(nsIContent* aAnonParent, - nsIContent* aElement) { - // We need to ensure two things. - // (1) The anonymous content should be fooled into thinking it's in the bound - // element's document, assuming that the bound element is in a document - // Note that we don't change the current doc of aAnonParent here, since that - // quite simply does not matter. aAnonParent is just a way of keeping refs - // to all its kids, which are anonymous content from the point of view of - // aElement. - // (2) The children's parent back pointer should not be to this synthetic root - // but should instead point to the enclosing parent element. - Element* element = aElement->AsElement(); - - nsAutoScriptBlocker scriptBlocker; - BindContext context(*this, *element); - for (nsIContent* child = aAnonParent->GetFirstChild(); child; - child = child->GetNextSibling()) { - child->UnbindFromTree(); - child->SetFlags(NODE_IS_ANONYMOUS_ROOT); - nsresult rv = child->BindToTree(context, *element); - if (NS_FAILED(rv)) { - // Oh, well... Just give up. - // XXXbz This really shouldn't be a void method! - child->UnbindFromTree(); - return; - } - } -} - -void nsXBLBinding::UnbindAnonymousContent(Document* aDocument, - nsIContent* aAnonParent, - bool aNullParent) { - nsAutoScriptBlocker scriptBlocker; - // Hold a strong ref while doing this, just in case. - nsCOMPtr<nsIContent> anonParent = aAnonParent; - for (nsIContent* child = aAnonParent->GetFirstChild(); child; - child = child->GetNextSibling()) { - child->UnbindFromTree(aNullParent); - } -} - -void nsXBLBinding::SetBoundElement(Element* aElement) { - mBoundElement = aElement; - if (mNextBinding) mNextBinding->SetBoundElement(aElement); -} - -void nsXBLBinding::GenerateAnonymousContent() { - NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(), - "Someone forgot a script blocker"); - - // Fetch the content element for this binding. - Element* content = mPrototypeBinding->GetImmediateChild(nsGkAtoms::content); - - if (!content) { - // We have no anonymous content. - if (mNextBinding) mNextBinding->GenerateAnonymousContent(); - - return; - } - - // Find out if we're really building kids or if we're just - // using the attribute-setting shorthand hack. - uint32_t contentCount = content->GetChildCount(); - - // Plan to build the content by default. - bool hasContent = (contentCount > 0); - if (hasContent) { - Document* doc = mBoundElement->OwnerDoc(); - - nsCOMPtr<nsINode> clonedNode = - content->Clone(true, doc->NodeInfoManager(), nullptr, IgnoreErrors()); - // FIXME: Bug 1399558, Why is this code OK assuming that nsINode::Clone - // never fails? - mContent = clonedNode->AsElement(); - - // Search for <xbl:children> elements in the XBL content. In the presence - // of multiple default insertion points, we use the last one in document - // order. - for (nsIContent* child = mContent; child; - child = child->GetNextNode(mContent)) { - if (child->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) { - XBLChildrenElement* point = static_cast<XBLChildrenElement*>(child); - if (point->IsDefaultInsertion()) { - mDefaultInsertionPoint = point; - } else { - mInsertionPoints.AppendElement(point); - } - } - } - - // Do this after looking for <children> as this messes up the parent - // pointer which would make the GetNextNode call above fail - BindAnonymousContent(mContent, mBoundElement); - - // Insert explicit children into insertion points - if (mDefaultInsertionPoint && mInsertionPoints.IsEmpty()) { - ExplicitChildIterator iter(mBoundElement); - for (nsIContent* child = iter.GetNextChild(); child; - child = iter.GetNextChild()) { - // Pass aNotify = false because we're just setting up the whole thing. - // Furthermore we do it from frame construction, so passing true here - // would reenter into it which is... not great. - mDefaultInsertionPoint->AppendInsertedChild(child, false); - } - } else { - // It is odd to come into this code if mInsertionPoints is not empty, but - // we need to make sure to do the compatibility hack below if the bound - // node has any non <xul:template> or <xul:observes> children. - ExplicitChildIterator iter(mBoundElement); - for (nsIContent* child = iter.GetNextChild(); child; - child = iter.GetNextChild()) { - XBLChildrenElement* point = FindInsertionPointForInternal(child); - if (point) { - // Pass aNotify = false because we're just setting up the whole thing. - // (see the similar call above for more details). - point->AppendInsertedChild(child, false); - } else { - NodeInfo* ni = child->NodeInfo(); - if (!child->TextIsOnlyWhitespace() && - (ni->NamespaceID() != kNameSpaceID_XUL || - (!ni->Equals(nsGkAtoms::_template) && - !ni->Equals(nsGkAtoms::observes)))) { - // Compatibility hack. For some reason the original XBL - // implementation dropped the content of a binding if any child of - // the bound element didn't match any of the <children> in the - // binding. This became a pseudo-API that we have to maintain. - - // Undo BindAnonymousContent - UnbindAnonymousContent(doc, mContent); - - // Clear out our children elements to avoid dangling references. - ClearInsertionPoints(); - - // Pretend as though there was no content in the binding. - mContent = nullptr; - return; - } - } - } - } - - // Set binding parent on default content if need - if (mDefaultInsertionPoint) { - mDefaultInsertionPoint->MaybeSetupDefaultContent(); - } - for (uint32_t i = 0; i < mInsertionPoints.Length(); ++i) { - mInsertionPoints[i]->MaybeSetupDefaultContent(); - } - - mPrototypeBinding->SetInitialAttributes(mBoundElement, mContent); - } - - // Always check the content element for potential attributes. - // This shorthand hack always happens, even when we didn't - // build anonymous content. - BorrowedAttrInfo attrInfo; - for (uint32_t i = 0; (attrInfo = content->GetAttrInfoAt(i)); ++i) { - int32_t namespaceID = attrInfo.mName->NamespaceID(); - // Hold a strong reference here so that the atom doesn't go away during - // UnsetAttr. - RefPtr<nsAtom> name = attrInfo.mName->LocalName(); - - if (name != nsGkAtoms::includes) { - if (!nsContentUtils::HasNonEmptyAttr(mBoundElement, namespaceID, name)) { - nsAutoString value2; - attrInfo.mValue->ToString(value2); - mBoundElement->SetAttr(namespaceID, name, attrInfo.mName->GetPrefix(), - value2, false); - } - } - - // Conserve space by wiping the attributes off the clone. - // - // FIXME(emilio): It'd be nice to make `mContent` a `RefPtr<Element>`. - if (mContent) mContent->AsElement()->UnsetAttr(namespaceID, name, false); - } -} - -nsIURI* nsXBLBinding::GetSourceDocURI() { - nsIContent* targetContent = - mPrototypeBinding->GetImmediateChild(nsGkAtoms::content); - if (!targetContent) { - return nullptr; - } - - return targetContent->OwnerDoc()->GetDocumentURI(); -} - -XBLChildrenElement* nsXBLBinding::FindInsertionPointFor(nsIContent* aChild) { - // XXX We should get rid of this function as it causes us to traverse the - // binding chain multiple times - if (mContent) { - return FindInsertionPointForInternal(aChild); - } - - return mNextBinding ? mNextBinding->FindInsertionPointFor(aChild) : nullptr; -} - -XBLChildrenElement* nsXBLBinding::FindInsertionPointForInternal( - nsIContent* aChild) { - for (uint32_t i = 0; i < mInsertionPoints.Length(); ++i) { - XBLChildrenElement* point = mInsertionPoints[i]; - if (point->Includes(aChild)) { - return point; - } - } - - return mDefaultInsertionPoint; -} - -void nsXBLBinding::ClearInsertionPoints() { - if (mDefaultInsertionPoint) { - mDefaultInsertionPoint->ClearInsertedChildren(); - } - - for (const auto& insertionPoint : mInsertionPoints) { - insertionPoint->ClearInsertedChildren(); - } -} - -nsAnonymousContentList* nsXBLBinding::GetAnonymousNodeList() { - if (!mContent) { - return mNextBinding ? mNextBinding->GetAnonymousNodeList() : nullptr; - } - - if (!mAnonymousContentList) { - mAnonymousContentList = new nsAnonymousContentList(mContent); - } - - return mAnonymousContentList; -} - -void nsXBLBinding::InstallEventHandlers() { - // Don't install handlers if scripts aren't allowed. - if (AllowScripts()) { - // Fetch the handlers prototypes for this binding. - nsXBLPrototypeHandler* handlerChain = - mPrototypeBinding->GetPrototypeHandlers(); - - if (handlerChain) { - EventListenerManager* manager = - mBoundElement->GetOrCreateListenerManager(); - if (!manager) return; - - bool isChromeDoc = nsContentUtils::IsChromeDoc(mBoundElement->OwnerDoc()); - bool isChromeBinding = mPrototypeBinding->IsChrome(); - nsXBLPrototypeHandler* curr; - for (curr = handlerChain; curr; curr = curr->GetNextHandler()) { - // Fetch the event type. - RefPtr<nsAtom> eventAtom = curr->GetEventName(); - if (!eventAtom || eventAtom == nsGkAtoms::keyup || - eventAtom == nsGkAtoms::keydown || eventAtom == nsGkAtoms::keypress) - continue; - - nsXBLEventHandler* handler = curr->GetEventHandler(); - if (handler) { - // Figure out if we're using capturing or not. - EventListenerFlags flags; - flags.mCapture = (curr->GetPhase() == NS_PHASE_CAPTURING); - - // If this is a command, add it in the system event group - if ((curr->GetType() & - (NS_HANDLER_TYPE_XBL_COMMAND | NS_HANDLER_TYPE_SYSTEM)) && - (isChromeBinding || - mBoundElement->IsInNativeAnonymousSubtree())) { - flags.mInSystemGroup = true; - } - - bool hasAllowUntrustedAttr = curr->HasAllowUntrustedAttr(); - if ((hasAllowUntrustedAttr && curr->AllowUntrustedEvents()) || - (!hasAllowUntrustedAttr && !isChromeDoc)) { - flags.mAllowUntrustedEvents = true; - } - - manager->AddEventListenerByType( - handler, nsDependentAtomString(eventAtom), flags); - } - } - - const nsCOMArray<nsXBLKeyEventHandler>* keyHandlers = - mPrototypeBinding->GetKeyEventHandlers(); - int32_t i; - for (i = 0; i < keyHandlers->Count(); ++i) { - nsXBLKeyEventHandler* handler = keyHandlers->ObjectAt(i); - handler->SetIsBoundToChrome(isChromeDoc); - - nsAutoString type; - handler->GetEventName(type); - - // If this is a command, add it in the system event group, otherwise - // add it to the standard event group. - - // Figure out if we're using capturing or not. - EventListenerFlags flags; - flags.mCapture = (handler->GetPhase() == NS_PHASE_CAPTURING); - - if ((handler->GetType() & - (NS_HANDLER_TYPE_XBL_COMMAND | NS_HANDLER_TYPE_SYSTEM)) && - (isChromeBinding || mBoundElement->IsInNativeAnonymousSubtree())) { - flags.mInSystemGroup = true; - } - - // For key handlers we have to set mAllowUntrustedEvents flag. - // Whether the handling of the event is allowed or not is handled in - // nsXBLKeyEventHandler::HandleEvent - flags.mAllowUntrustedEvents = true; - - manager->AddEventListenerByType(handler, type, flags); - } - } - } - - if (mNextBinding) mNextBinding->InstallEventHandlers(); -} - -nsresult nsXBLBinding::InstallImplementation() { - // Always install the base class properties first, so that - // derived classes can reference the base class properties. - - if (mNextBinding) { - nsresult rv = mNextBinding->InstallImplementation(); - NS_ENSURE_SUCCESS(rv, rv); - } - - // iterate through each property in the prototype's list and install the - // property. - if (AllowScripts()) return mPrototypeBinding->InstallImplementation(this); - - return NS_OK; -} - -void nsXBLBinding::AttributeChanged(nsAtom* aAttribute, int32_t aNameSpaceID, - bool aRemoveFlag, bool aNotify) { - // XXX Change if we ever allow multiple bindings in a chain to contribute - // anonymous content - if (!mContent) { - if (mNextBinding) - mNextBinding->AttributeChanged(aAttribute, aNameSpaceID, aRemoveFlag, - aNotify); - } else { - mPrototypeBinding->AttributeChanged(aAttribute, aNameSpaceID, aRemoveFlag, - mBoundElement, mContent, aNotify); - } -} - -void nsXBLBinding::ExecuteAttachedHandler() { - if (mNextBinding) mNextBinding->ExecuteAttachedHandler(); - - if (AllowScripts()) mPrototypeBinding->BindingAttached(mBoundElement); -} - -void nsXBLBinding::ExecuteDetachedHandler() { - if (AllowScripts()) mPrototypeBinding->BindingDetached(mBoundElement); - - if (mNextBinding) mNextBinding->ExecuteDetachedHandler(); -} - -void nsXBLBinding::UnhookEventHandlers() { - nsXBLPrototypeHandler* handlerChain = - mPrototypeBinding->GetPrototypeHandlers(); - - if (handlerChain) { - EventListenerManager* manager = mBoundElement->GetExistingListenerManager(); - if (!manager) { - return; - } - - bool isChromeBinding = mPrototypeBinding->IsChrome(); - nsXBLPrototypeHandler* curr; - for (curr = handlerChain; curr; curr = curr->GetNextHandler()) { - nsXBLEventHandler* handler = curr->GetCachedEventHandler(); - if (!handler) { - continue; - } - - RefPtr<nsAtom> eventAtom = curr->GetEventName(); - if (!eventAtom || eventAtom == nsGkAtoms::keyup || - eventAtom == nsGkAtoms::keydown || eventAtom == nsGkAtoms::keypress) - continue; - - // Figure out if we're using capturing or not. - EventListenerFlags flags; - flags.mCapture = (curr->GetPhase() == NS_PHASE_CAPTURING); - - // If this is a command, remove it from the system event group, - // otherwise remove it from the standard event group. - - if ((curr->GetType() & - (NS_HANDLER_TYPE_XBL_COMMAND | NS_HANDLER_TYPE_SYSTEM)) && - (isChromeBinding || mBoundElement->IsInNativeAnonymousSubtree())) { - flags.mInSystemGroup = true; - } - - manager->RemoveEventListenerByType( - handler, nsDependentAtomString(eventAtom), flags); - } - - const nsCOMArray<nsXBLKeyEventHandler>* keyHandlers = - mPrototypeBinding->GetKeyEventHandlers(); - int32_t i; - for (i = 0; i < keyHandlers->Count(); ++i) { - nsXBLKeyEventHandler* handler = keyHandlers->ObjectAt(i); - - nsAutoString type; - handler->GetEventName(type); - - // Figure out if we're using capturing or not. - EventListenerFlags flags; - flags.mCapture = (handler->GetPhase() == NS_PHASE_CAPTURING); - - // If this is a command, remove it from the system event group, otherwise - // remove it from the standard event group. - - if ((handler->GetType() & - (NS_HANDLER_TYPE_XBL_COMMAND | NS_HANDLER_TYPE_SYSTEM)) && - (isChromeBinding || mBoundElement->IsInNativeAnonymousSubtree())) { - flags.mInSystemGroup = true; - } - - manager->RemoveEventListenerByType(handler, type, flags); - } - } -} - -void nsXBLBinding::ChangeDocument(Document* aOldDocument, - Document* aNewDocument) { - if (aOldDocument == aNewDocument) return; - - // Now the binding dies. Unhook our prototypes. - if (mPrototypeBinding->HasImplementation()) { - AutoJSAPI jsapi; - // Init might fail here if we've cycle-collected the global object, since - // the Unlink phase of cycle collection happens after JS GC finalization. - // But in that case, we don't care about fixing the prototype chain, since - // everything's going away immediately. - if (jsapi.Init(aOldDocument->GetScopeObject())) { - JSContext* cx = jsapi.cx(); - - JS::Rooted<JSObject*> scriptObject(cx, mBoundElement->GetWrapper()); - if (scriptObject) { - // XXX Stay in sync! What if a layered binding has an - // <interface>?! - // XXXbz what does that comment mean, really? It seems to date - // back to when there was such a thing as an <interface>, whever - // that was... - - // Find the right prototype. - JSAutoRealm ar(cx, scriptObject); - - JS::Rooted<JSObject*> base(cx, scriptObject); - JS::Rooted<JSObject*> proto(cx); - for (; true; base = proto) { // Will break out on null proto - if (!JS_GetPrototype(cx, base, &proto)) { - return; - } - if (!proto) { - break; - } - - if (JS_GetClass(proto) != &gPrototypeJSClass) { - // Clearly not the right class - continue; - } - - RefPtr<nsXBLDocumentInfo> docInfo = - static_cast<nsXBLDocumentInfo*>(::JS_GetPrivate(proto)); - if (!docInfo) { - // Not the proto we seek - continue; - } - - JS::Value protoBinding = ::JS_GetReservedSlot(proto, 0); - - if (protoBinding.toPrivate() != mPrototypeBinding) { - // Not the right binding - continue; - } - - // Alright! This is the right prototype. Pull it out of the - // proto chain. - JS::Rooted<JSObject*> grandProto(cx); - if (!JS_GetPrototype(cx, proto, &grandProto)) { - return; - } - ::JS_SetPrototype(cx, base, grandProto); - break; - } - - mPrototypeBinding->UndefineFields(cx, scriptObject); - - // Don't remove the reference from the document to the - // wrapper here since it'll be removed by the element - // itself when that's taken out of the document. - } - } - } - - // Remove our event handlers - UnhookEventHandlers(); - - { - nsAutoScriptBlocker scriptBlocker; - - // Then do our ancestors. This reverses the construction order, so that at - // all times things are consistent as far as everyone is concerned. - if (mNextBinding) { - mNextBinding->ChangeDocument(aOldDocument, aNewDocument); - } - - // Update the anonymous content. - // XXXbz why not only for style bindings? - if (mContent) { - nsXBLBinding::UnbindAnonymousContent(aOldDocument, mContent); - } - - ClearInsertionPoints(); - } -} - -// Internal helper methods ///////////////////////////////////////////////////// - -// Get or create a WeakMap object on a given XBL-hosting global. -// -// The scheme is as follows. XBL-hosting globals (either privileged content -// Windows or XBL scopes) get two lazily-defined WeakMap properties. Each -// WeakMap is keyed by the grand-proto - i.e. the original prototype of the -// content before it was bound, and the prototype of the class object that we -// splice in. The values in the WeakMap are simple dictionary-style objects, -// mapping from XBL class names to class objects. -static JSObject* GetOrCreateClassObjectMap(JSContext* cx, - JS::Handle<JSObject*> scope, - const char* mapName) { - AssertSameCompartment(cx, scope); - MOZ_ASSERT(JS_IsGlobalObject(scope)); - MOZ_ASSERT(scope == xpc::GetXBLScopeOrGlobal(cx, scope)); - - // First, see if the map is already defined. - JS::Rooted<JS::PropertyDescriptor> desc(cx); - if (!JS_GetOwnPropertyDescriptor(cx, scope, mapName, &desc)) { - return nullptr; - } - if (desc.object() && desc.value().isObject() && - JS::IsWeakMapObject(&desc.value().toObject())) { - return &desc.value().toObject(); - } - - // It's not there. Create and define it. - JS::Rooted<JSObject*> map(cx, JS::NewWeakMapObject(cx)); - if (!map || !JS_DefineProperty(cx, scope, mapName, map, - JSPROP_PERMANENT | JSPROP_READONLY)) { - return nullptr; - } - return map; -} - -static JSObject* GetOrCreateMapEntryForPrototype(JSContext* cx, - JS::Handle<JSObject*> proto) { - AssertSameCompartment(cx, proto); - // We want to hang our class objects off the XBL scope. But since we also - // hoist anonymous content into the XBL scope, this creates the potential for - // tricky collisions, since we can simultaneously have a bound in-content - // node with grand-proto HTMLDivElement and a bound anonymous node whose - // grand-proto is the XBL scope's cross-compartment wrapper to HTMLDivElement. - // Since we have to wrap the WeakMap keys into its scope, this distinction - // would be lost if we don't do something about it. - // - // So we define two maps - one class objects that live in content (prototyped - // to content prototypes), and the other for class objects that live in the - // XBL scope (prototyped to cross-compartment-wrapped content prototypes). - const char* name = xpc::IsInContentXBLScope(proto) - ? "__ContentClassObjectMap__" - : "__XBLClassObjectMap__"; - - // Now, enter the XBL scope, since that's where we need to operate, and wrap - // the proto accordingly. We hang the map off of the content XBL scope for - // content, and the Window for chrome (whether add-ons are involved or not). - JS::Rooted<JSObject*> scope( - cx, xpc::GetXBLScopeOrGlobal(cx, JS::CurrentGlobalOrNull(cx))); - NS_ENSURE_TRUE(scope, nullptr); - MOZ_ASSERT(JS_IsGlobalObject(scope)); - - JS::Rooted<JSObject*> wrappedProto(cx, proto); - JSAutoRealm ar(cx, scope); - if (!JS_WrapObject(cx, &wrappedProto)) { - return nullptr; - } - - // Grab the appropriate WeakMap. - JS::Rooted<JSObject*> map(cx, GetOrCreateClassObjectMap(cx, scope, name)); - if (!map) { - return nullptr; - } - - // See if we already have a map entry for that prototype. - JS::Rooted<JS::Value> val(cx); - if (!JS::GetWeakMapEntry(cx, map, wrappedProto, &val)) { - return nullptr; - } - if (val.isObject()) { - return &val.toObject(); - } - - // We don't have an entry. Create one and stick it in the map. - JS::Rooted<JSObject*> entry(cx); - entry = JS_NewObjectWithGivenProto(cx, nullptr, nullptr); - if (!entry) { - return nullptr; - } - JS::Rooted<JS::Value> entryVal(cx, JS::ObjectValue(*entry)); - if (!JS::SetWeakMapEntry(cx, map, wrappedProto, entryVal)) { - NS_WARNING( - "SetWeakMapEntry failed, probably due to non-preservable WeakMap " - "key. XBL binding will fail for this element."); - return nullptr; - } - return entry; -} - -static nsXBLPrototypeBinding* GetProtoBindingFromClassObject(JSObject* obj) { - MOZ_ASSERT(JS_GetClass(obj) == &gPrototypeJSClass); - return static_cast<nsXBLPrototypeBinding*>( - ::JS_GetReservedSlot(obj, 0).toPrivate()); -} - -// static -nsresult nsXBLBinding::DoInitJSClass(JSContext* cx, JS::Handle<JSObject*> obj, - const nsString& aClassName, - nsXBLPrototypeBinding* aProtoBinding, - JS::MutableHandle<JSObject*> aClassObject, - bool* aNew) { - MOZ_ASSERT(obj); - - // Note that, now that NAC reflectors are created in the XBL scope, the - // reflector is not necessarily same-compartment with the document. So we'll - // end up creating a separate instance of the oddly-named XBL class object - // and defining it as a property on the XBL scope's global. This works fine, - // but we need to make sure never to assume that the the reflector and - // prototype are same-compartment with the bound document. - JS::Rooted<JSObject*> global(cx, JS::GetNonCCWObjectGlobal(obj)); - - // We must be in obj's realm. - MOZ_ASSERT(JS::CurrentGlobalOrNull(cx) == global); - - // We never store class objects in add-on scopes. - JS::Rooted<JSObject*> xblScope(cx, xpc::GetXBLScopeOrGlobal(cx, global)); - NS_ENSURE_TRUE(xblScope, NS_ERROR_UNEXPECTED); - - JS::Rooted<JSObject*> parent_proto(cx); - { - JS::RootedObject wrapped(cx, obj); - JSAutoRealm ar(cx, xblScope); - if (!JS_WrapObject(cx, &wrapped)) { - return NS_ERROR_FAILURE; - } - if (!JS_GetPrototype(cx, wrapped, &parent_proto)) { - return NS_ERROR_FAILURE; - } - } - if (!JS_WrapObject(cx, &parent_proto)) { - return NS_ERROR_FAILURE; - } - - // Get the map entry for the parent prototype. In the one-off case that the - // parent prototype is null, we somewhat hackily just use the WeakMap itself - // as a property holder. - JS::Rooted<JSObject*> holder(cx); - if (parent_proto) { - holder = GetOrCreateMapEntryForPrototype(cx, parent_proto); - } else { - JSAutoRealm innerAR(cx, xblScope); - holder = - GetOrCreateClassObjectMap(cx, xblScope, "__ContentClassObjectMap__"); - } - if (NS_WARN_IF(!holder)) { - return NS_ERROR_FAILURE; - } - js::AssertSameCompartment(holder, xblScope); - JSAutoRealm ar(cx, holder); - - // Look up the class on the property holder. The only properties on the - // holder should be class objects. If we don't find the class object, we need - // to create and define it. - JS::Rooted<JSObject*> proto(cx); - JS::Rooted<JS::PropertyDescriptor> desc(cx); - if (!JS_GetOwnUCPropertyDescriptor(cx, holder, aClassName.get(), - aClassName.Length(), &desc)) { - return NS_ERROR_OUT_OF_MEMORY; - } - *aNew = !desc.object(); - if (desc.object()) { - proto = &desc.value().toObject(); - DebugOnly<nsXBLPrototypeBinding*> cachedBinding = - GetProtoBindingFromClassObject(js::UncheckedUnwrap(proto)); - MOZ_ASSERT(cachedBinding == aProtoBinding); - } else { - // We need to create the prototype. First, enter the realm where it's - // going to live, and create it. - JSAutoRealm ar2(cx, global); - proto = JS_NewObjectWithGivenProto(cx, &gPrototypeJSClass, parent_proto); - if (!proto) { - return NS_ERROR_OUT_OF_MEMORY; - } - - // Keep this proto binding alive while we're alive. Do this first so that - // we can guarantee that in XBLFinalize this will be non-null. - // Note that we can't just store aProtoBinding in the private and - // addref/release the nsXBLDocumentInfo through it, because cycle - // collection doesn't seem to work right if the private is not an - // nsISupports. - nsXBLDocumentInfo* docInfo = aProtoBinding->XBLDocumentInfo(); - ::JS_SetPrivate(proto, docInfo); - NS_ADDREF(docInfo); - JS_SetReservedSlot(proto, 0, JS::PrivateValue(aProtoBinding)); - - // Don't collect the proto while recording/replaying, to avoid - // non-deterministically releasing the docInfo reference. - recordreplay::HoldJSObject(proto); - - // Next, enter the realm of the property holder, wrap the proto, and - // stick it on. - JSAutoRealm ar3(cx, holder); - if (!JS_WrapObject(cx, &proto) || - !JS_DefineUCProperty(cx, holder, aClassName.get(), -1, proto, - JSPROP_READONLY | JSPROP_PERMANENT)) { - return NS_ERROR_OUT_OF_MEMORY; - } - } - - // Whew. We have the proto. Wrap it back into the realm of |obj|, - // splice it in, and return it. - JSAutoRealm ar4(cx, obj); - if (!JS_WrapObject(cx, &proto) || !JS_SetPrototype(cx, obj, proto)) { - return NS_ERROR_FAILURE; - } - aClassObject.set(proto); - return NS_OK; -} - -bool nsXBLBinding::AllowScripts() { - return mBoundElement && mPrototypeBinding->GetAllowScripts(); -} - -nsXBLBinding* nsXBLBinding::RootBinding() { - if (mNextBinding) return mNextBinding->RootBinding(); - - return this; -} - -bool nsXBLBinding::ResolveAllFields(JSContext* cx, - JS::Handle<JSObject*> obj) const { - if (!mPrototypeBinding->ResolveAllFields(cx, obj)) { - return false; - } - - if (mNextBinding) { - return mNextBinding->ResolveAllFields(cx, obj); - } - - return true; -} - -bool nsXBLBinding::LookupMember( - JSContext* aCx, JS::Handle<jsid> aId, - JS::MutableHandle<JS::PropertyDescriptor> aDesc) { - // We should never enter this function with a pre-filled property descriptor. - MOZ_ASSERT(!aDesc.object()); - - // Get the string as an nsString before doing anything, so we can make - // convenient comparisons during our search. - if (!JSID_IS_STRING(aId)) { - return true; - } - nsAutoJSString name; - if (!name.init(aCx, JSID_TO_STRING(aId))) { - return false; - } - - // We have a weak reference to our bound element, so make sure it's alive. - if (!mBoundElement || !mBoundElement->GetWrapper()) { - return false; - } - - // Get the scope of mBoundElement and the associated XBL scope. We should only - // be calling into this machinery if we're running in a separate XBL scope. - // - // Note that we only end up in LookupMember for XrayWrappers from XBL scopes - // into content. So for NAC reflectors that live in the XBL scope, we should - // never get here. But on the off-chance that someone adds new callsites to - // LookupMember, we do a release-mode assertion as belt-and-braces. - // We do a release-mode assertion here to be extra safe. - // - // This code is only called for content XBL, so we don't have to worry about - // add-on scopes here. - JS::Rooted<JSObject*> boundScope( - aCx, JS::GetNonCCWObjectGlobal(mBoundElement->GetWrapper())); - MOZ_RELEASE_ASSERT(!xpc::IsInContentXBLScope(boundScope)); - JS::Rooted<JSObject*> xblScope(aCx, xpc::GetXBLScope(aCx, boundScope)); - NS_ENSURE_TRUE(xblScope, false); - MOZ_ASSERT(boundScope != xblScope); - - // Enter the xbl scope and invoke the internal version. - { - JSAutoRealm ar(aCx, xblScope); - JS::Rooted<jsid> id(aCx, aId); - if (!LookupMemberInternal(aCx, name, id, aDesc, xblScope)) { - return false; - } - } - - // Wrap into the caller's scope. - return JS_WrapPropertyDescriptor(aCx, aDesc); -} - -bool nsXBLBinding::LookupMemberInternal( - JSContext* aCx, nsString& aName, JS::Handle<jsid> aNameAsId, - JS::MutableHandle<JS::PropertyDescriptor> aDesc, - JS::Handle<JSObject*> aXBLScope) { - // First, see if we have an implementation. If we don't, it means that this - // binding doesn't have a class object, and thus doesn't have any members. - // Skip it. - if (!PrototypeBinding()->HasImplementation()) { - if (!mNextBinding) { - return true; - } - return mNextBinding->LookupMemberInternal(aCx, aName, aNameAsId, aDesc, - aXBLScope); - } - - // Find our class object. It's in a protected scope and permanent just in - // case, so should be there no matter what. - JS::Rooted<JS::Value> classObject(aCx); - if (!JS_GetUCProperty(aCx, aXBLScope, PrototypeBinding()->ClassName().get(), - -1, &classObject)) { - return false; - } - - // The bound element may have been adoped by a document and have a different - // wrapper (and different xbl scope) than when the binding was applied, in - // this case getting the class object will fail. Behave as if the class - // object did not exist. - if (classObject.isUndefined()) { - return true; - } - - MOZ_ASSERT(classObject.isObject()); - - // Look for the property on this binding. If it's not there, try the next - // binding on the chain. - nsXBLProtoImpl* impl = mPrototypeBinding->GetImplementation(); - JS::Rooted<JSObject*> object(aCx, &classObject.toObject()); - if (impl && !impl->LookupMember(aCx, aName, aNameAsId, aDesc, object)) { - return false; - } - if (aDesc.object() || !mNextBinding) { - return true; - } - - return mNextBinding->LookupMemberInternal(aCx, aName, aNameAsId, aDesc, - aXBLScope); -} - -bool nsXBLBinding::HasField(nsString& aName) { - // See if this binding has such a field. - return mPrototypeBinding->FindField(aName) || - (mNextBinding && mNextBinding->HasField(aName)); -} - -void nsXBLBinding::MarkForDeath() { - mMarkedForDeath = true; - ExecuteDetachedHandler(); -} - -bool nsXBLBinding::ImplementsInterface(REFNSIID aIID) const { - return mPrototypeBinding->ImplementsInterface(aIID) || - (mNextBinding && mNextBinding->ImplementsInterface(aIID)); -}
deleted file mode 100644 --- a/dom/xbl/nsXBLBinding.h +++ /dev/null @@ -1,176 +0,0 @@ -/* -*- 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 nsXBLBinding_h_ -#define nsXBLBinding_h_ - -#include "nsXBLService.h" -#include "nsCOMPtr.h" -#include "nsINodeList.h" -#include "nsClassHashtable.h" -#include "nsTArray.h" -#include "nsCycleCollectionParticipant.h" -#include "nsISupportsImpl.h" -#include "js/TypeDecls.h" - -class nsXBLPrototypeBinding; -class nsIContent; -class nsAtom; -struct RawServoAuthorStyles; - -namespace mozilla { -namespace dom { -class Document; -class XBLChildrenElement; -} // namespace dom -} // namespace mozilla - -class nsAnonymousContentList; - -// *********************************************************************/ -// The XBLBinding class - -class nsXBLBinding final { - public: - explicit nsXBLBinding(nsXBLPrototypeBinding* aProtoBinding); - - /** - * XBLBindings are refcounted. They are held onto in 3 ways: - * 1. The binding manager's binding table holds onto all bindings that are - * currently attached to a content node. - * 2. Bindings hold onto their base binding. This is important since - * the base binding itself may not be attached to anything. - * 3. The binding manager holds an additional reference to bindings - * which are queued to fire their constructors. - */ - - NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(nsXBLBinding) - - NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(nsXBLBinding) - - nsXBLPrototypeBinding* PrototypeBinding() const { return mPrototypeBinding; } - nsIContent* GetAnonymousContent() { return mContent.get(); } - nsXBLBinding* GetBindingWithContent(); - - nsXBLBinding* GetBaseBinding() const { return mNextBinding; } - void SetBaseBinding(nsXBLBinding* aBinding); - - mozilla::dom::Element* GetBoundElement() { return mBoundElement; } - void SetBoundElement(mozilla::dom::Element* aElement); - - /* - * Does a lookup for a method or attribute provided by one of the bindings' - * prototype implementation. If found, |desc| will be set up appropriately, - * and wrapped into cx->compartment. - * - * May only be called when XBL code is being run in a separate scope, because - * otherwise we don't have untainted data with which to do a proper lookup. - */ - bool LookupMember(JSContext* aCx, JS::Handle<jsid> aId, - JS::MutableHandle<JS::PropertyDescriptor> aDesc); - - /* - * Determines whether the binding has a field with the given name. - */ - bool HasField(nsString& aName); - - protected: - ~nsXBLBinding(); - - /* - * Internal version. Requires that aCx is in appropriate xbl scope. - */ - bool LookupMemberInternal(JSContext* aCx, nsString& aName, - JS::Handle<jsid> aNameAsId, - JS::MutableHandle<JS::PropertyDescriptor> aDesc, - JS::Handle<JSObject*> aXBLScope); - - public: - void MarkForDeath(); - bool MarkedForDeath() const { return mMarkedForDeath; } - - bool ImplementsInterface(REFNSIID aIID) const; - - void GenerateAnonymousContent(); - void BindAnonymousContent(nsIContent* aAnonParent, nsIContent* aElement); - static void UnbindAnonymousContent(mozilla::dom::Document* aDocument, - nsIContent* aAnonParent, - bool aNullParent = true); - void InstallEventHandlers(); - nsresult InstallImplementation(); - - void ExecuteAttachedHandler(); - void ExecuteDetachedHandler(); - void UnhookEventHandlers(); - - nsXBLBinding* RootBinding(); - - // Resolve all the fields for this binding and all ancestor bindings on the - // object |obj|. False return means a JS exception was set. - bool ResolveAllFields(JSContext* cx, JS::Handle<JSObject*> obj) const; - - void AttributeChanged(nsAtom* aAttribute, int32_t aNameSpaceID, - bool aRemoveFlag, bool aNotify); - - void ChangeDocument(mozilla::dom::Document* aOldDocument, - mozilla::dom::Document* aNewDocument); - - const RawServoAuthorStyles* GetServoStyles() const; - - static nsresult DoInitJSClass(JSContext* cx, JS::Handle<JSObject*> obj, - const nsString& aClassName, - nsXBLPrototypeBinding* aProtoBinding, - JS::MutableHandle<JSObject*> aClassObject, - bool* aNew); - - bool AllowScripts(); - - mozilla::dom::XBLChildrenElement* FindInsertionPointFor(nsIContent* aChild); - - bool HasFilteredInsertionPoints() { return !mInsertionPoints.IsEmpty(); } - - mozilla::dom::XBLChildrenElement* GetDefaultInsertionPoint() { - return mDefaultInsertionPoint; - } - - // Removes all inserted node from <xbl:children> insertion points under us. - void ClearInsertionPoints(); - - // Returns a live node list that iterates over the anonymous nodes generated - // by this binding. - nsAnonymousContentList* GetAnonymousNodeList(); - - nsIURI* GetSourceDocURI(); - - // MEMBER VARIABLES - protected: - bool mMarkedForDeath; - - nsXBLPrototypeBinding* - mPrototypeBinding; // Weak, but we're holding a ref to the docinfo - nsCOMPtr<nsIContent> - mContent; // Strong. Our anonymous content stays around with us. - RefPtr<nsXBLBinding> mNextBinding; // Strong. The derived binding owns the - // base class bindings. - - mozilla::dom::Element* - mBoundElement; // [WEAK] We have a reference, but we don't own it. - - // The <xbl:children> elements that we found in our <xbl:content> when we - // processed this binding. The default insertion point has no includes - // attribute and all other insertion points must have at least one includes - // attribute. These points must be up-to-date with respect to their parent's - // children, even if their parent has another binding attached to it, - // preventing us from rendering their contents directly. - RefPtr<mozilla::dom::XBLChildrenElement> mDefaultInsertionPoint; - nsTArray<RefPtr<mozilla::dom::XBLChildrenElement> > mInsertionPoints; - RefPtr<nsAnonymousContentList> mAnonymousContentList; - - mozilla::dom::XBLChildrenElement* FindInsertionPointForInternal( - nsIContent* aChild); -}; - -#endif // nsXBLBinding_h_
deleted file mode 100644 --- a/dom/xbl/nsXBLContentSink.cpp +++ /dev/null @@ -1,802 +0,0 @@ -/* -*- 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" - -#include "nsXBLContentSink.h" -#include "mozilla/dom/Document.h" -#include "nsBindingManager.h" -#include "nsGkAtoms.h" -#include "nsNameSpaceManager.h" -#include "nsIURI.h" -#include "nsTextFragment.h" -#ifdef MOZ_XUL -# include "nsXULElement.h" -#endif -#include "nsXBLProtoImplProperty.h" -#include "nsXBLProtoImplMethod.h" -#include "nsXBLProtoImplField.h" -#include "nsXBLPrototypeBinding.h" -#include "nsContentUtils.h" -#include "nsIConsoleService.h" -#include "nsIScriptError.h" -#include "nsNodeInfoManager.h" -#include "nsIPrincipal.h" -#include "mozilla/dom/Element.h" - -using namespace mozilla; -using namespace mozilla::dom; - -nsresult NS_NewXBLContentSink(nsIXMLContentSink** aResult, Document* aDoc, - nsIURI* aURI, nsISupports* aContainer) { - NS_ENSURE_ARG_POINTER(aResult); - - RefPtr<nsXBLContentSink> it = new nsXBLContentSink(); - nsresult rv = it->Init(aDoc, aURI, aContainer); - NS_ENSURE_SUCCESS(rv, rv); - - it.forget(aResult); - return NS_OK; -} - -nsXBLContentSink::nsXBLContentSink() - : mState(eXBL_InDocument), - mSecondaryState(eXBL_None), - mDocInfo(nullptr), - mIsChromeOrResource(false), - mFoundFirstBinding(false), - mBinding(nullptr), - mHandler(nullptr), - mImplementation(nullptr), - mImplMember(nullptr), - mImplField(nullptr), - mProperty(nullptr), - mMethod(nullptr), - mField(nullptr) { - mPrettyPrintXML = false; -} - -nsXBLContentSink::~nsXBLContentSink() {} - -nsresult nsXBLContentSink::Init(Document* aDoc, nsIURI* aURI, - nsISupports* aContainer) { - nsresult rv; - rv = nsXMLContentSink::Init(aDoc, aURI, aContainer, nullptr); - return rv; -} - -void nsXBLContentSink::MaybeStartLayout(bool aIgnorePendingSheets) {} - -nsresult nsXBLContentSink::FlushText(bool aReleaseTextNode) { - if (mTextLength != 0) { - const nsAString& text = Substring(mText, mText + mTextLength); - if (mState == eXBL_InHandlers) { - NS_ASSERTION(mBinding, "Must have binding here"); - // Get the text and add it to the event handler. - if (mSecondaryState == eXBL_InHandler) mHandler->AppendHandlerText(text); - mTextLength = 0; - return NS_OK; - } else if (mState == eXBL_InImplementation) { - NS_ASSERTION(mBinding, "Must have binding here"); - if (mSecondaryState == eXBL_InConstructor || - mSecondaryState == eXBL_InDestructor) { - // Construct a method for the constructor/destructor. - nsXBLProtoImplMethod* method; - if (mSecondaryState == eXBL_InConstructor) - method = mBinding->GetConstructor(); - else - method = mBinding->GetDestructor(); - - // Get the text and add it to the constructor/destructor. - method->AppendBodyText(text); - } else if (mSecondaryState == eXBL_InGetter || - mSecondaryState == eXBL_InSetter) { - // Get the text and add it to the getter/setter - if (mSecondaryState == eXBL_InGetter) - mProperty->AppendGetterText(text); - else - mProperty->AppendSetterText(text); - } else if (mSecondaryState == eXBL_InBody) { - // Get the text and add it to the method - if (mMethod) mMethod->AppendBodyText(text); - } else if (mSecondaryState == eXBL_InField) { - // Get the text and add it to the method - if (mField) mField->AppendFieldText(text); - } - mTextLength = 0; - return NS_OK; - } - - nsIContent* content = GetCurrentContent(); - if (content && (content->NodeInfo()->NamespaceEquals(kNameSpaceID_XBL) || - (content->IsXULElement() && - !content->IsAnyOfXULElements(nsGkAtoms::label, - nsGkAtoms::description)))) { - bool isWS = true; - if (mTextLength > 0) { - const char16_t* cp = mText; - const char16_t* end = mText + mTextLength; - while (cp < end) { - char16_t ch = *cp++; - if (!dom::IsSpaceCharacter(ch)) { - isWS = false; - break; - } - } - } - - if (isWS && mTextLength > 0) { - mTextLength = 0; - // Make sure to drop the textnode, if any - return nsXMLContentSink::FlushText(aReleaseTextNode); - } - } - } - - return nsXMLContentSink::FlushText(aReleaseTextNode); -} - -NS_IMETHODIMP -nsXBLContentSink::ReportError(const char16_t* aErrorText, - const char16_t* aSourceText, - nsIScriptError* aError, bool* _retval) { - MOZ_ASSERT(aError && aSourceText && aErrorText, "Check arguments!!!"); - - // XXX FIXME This function overrides and calls on - // nsXMLContentSink::ReportError, and probably should die. See bug 347826. - - // XXX We should make sure the binding has no effect, but that it also - // gets destroyed properly without leaking. Perhaps we should even - // ensure that the content that was bound is displayed with no - // binding. - -#ifdef DEBUG - // Report the error to stderr. - fprintf(stderr, "\n%s\n%s\n\n", NS_LossyConvertUTF16toASCII(aErrorText).get(), - NS_LossyConvertUTF16toASCII(aSourceText).get()); -#endif - - // Most of what this does won't be too useful, but whatever... - // nsXMLContentSink::ReportError will handle the console logging. - return nsXMLContentSink::ReportError(aErrorText, aSourceText, aError, - _retval); -} - -nsresult nsXBLContentSink::ReportUnexpectedElement(nsAtom* aElementName, - uint32_t aLineNumber) { - // XXX we should really somehow stop the parse and drop the binding - // instead of just letting the XML sink build the content model like - // we do... - mState = eXBL_Error; - AutoTArray<nsString, 1> params; - aElementName->ToString(*params.AppendElement()); - - return nsContentUtils::ReportToConsole( - nsIScriptError::errorFlag, NS_LITERAL_CSTRING("XBL Content Sink"), - mDocument, nsContentUtils::eXBL_PROPERTIES, "UnexpectedElement", params, - nullptr, EmptyString() /* source line */, aLineNumber); -} - -void nsXBLContentSink::AddMember(nsXBLProtoImplMember* aMember) { - // Add this member to our chain. - if (mImplMember) - mImplMember->SetNext( - aMember); // Already have a chain. Just append to the end. - else - mImplementation->SetMemberList( - aMember); // We're the first member in the chain. - - mImplMember = aMember; // Adjust our pointer to point to the new last member - // in the chain. -} - -void nsXBLContentSink::AddField(nsXBLProtoImplField* aField) { - // Add this field to our chain. - if (mImplField) - mImplField->SetNext( - aField); // Already have a chain. Just append to the end. - else - mImplementation->SetFieldList( - aField); // We're the first member in the chain. - - mImplField = aField; // Adjust our pointer to point to the new last field in - // the chain. -} - -NS_IMETHODIMP -nsXBLContentSink::HandleStartElement(const char16_t* aName, - const char16_t** aAtts, - uint32_t aAttsCount, uint32_t aLineNumber, - uint32_t aColumnNumber) { - nsresult rv = nsXMLContentSink::HandleStartElement( - aName, aAtts, aAttsCount, aLineNumber, aColumnNumber); - if (NS_FAILED(rv)) return rv; - - if (mState == eXBL_InBinding && !mBinding) { - rv = ConstructBinding(aLineNumber); - if (NS_FAILED(rv)) return rv; - - // mBinding may still be null, if the binding had no id. If so, - // we'll deal with that later in the sink. - } - - return rv; -} - -NS_IMETHODIMP -nsXBLContentSink::HandleEndElement(const char16_t* aName) { - FlushText(); - - if (mState != eXBL_InDocument) { - int32_t nameSpaceID; - RefPtr<nsAtom> prefix, localName; - nsContentUtils::SplitExpatName(aName, getter_AddRefs(prefix), - getter_AddRefs(localName), &nameSpaceID); - - if (nameSpaceID == kNameSpaceID_XBL) { - if (mState == eXBL_Error) { - // Check whether we've opened this tag before; we may not have if - // it was a real XBL tag before the error occurred. - if (!GetCurrentContent()->NodeInfo()->Equals(localName, nameSpaceID)) { - // OK, this tag was never opened as far as the XML sink is - // concerned. Just drop the HandleEndElement - return NS_OK; - } - } else if (mState == eXBL_InHandlers) { - if (localName == nsGkAtoms::handlers) { - mState = eXBL_InBinding; - mHandler = nullptr; - } else if (localName == nsGkAtoms::handler) - mSecondaryState = eXBL_None; - return NS_OK; - } else if (mState == eXBL_InImplementation) { - if (localName == nsGkAtoms::implementation) - mState = eXBL_InBinding; - else if (localName == nsGkAtoms::property) { - mSecondaryState = eXBL_None; - mProperty = nullptr; - } else if (localName == nsGkAtoms::method) { - mSecondaryState = eXBL_None; - mMethod = nullptr; - } else if (localName == nsGkAtoms::field) { - mSecondaryState = eXBL_None; - mField = nullptr; - } else if (localName == nsGkAtoms::constructor || - localName == nsGkAtoms::destructor) - mSecondaryState = eXBL_None; - else if (localName == nsGkAtoms::getter || - localName == nsGkAtoms::setter) - mSecondaryState = eXBL_InProperty; - else if (localName == nsGkAtoms::parameter || - localName == nsGkAtoms::body) - mSecondaryState = eXBL_InMethod; - return NS_OK; - } else if (mState == eXBL_InBindings && - localName == nsGkAtoms::bindings) { - mState = eXBL_InDocument; - } - - nsresult rv = nsXMLContentSink::HandleEndElement(aName); - if (NS_FAILED(rv)) return rv; - - if (mState == eXBL_InBinding && localName == nsGkAtoms::binding) { - mState = eXBL_InBindings; - if (mBinding) { // See comment in HandleStartElement() - mBinding->Initialize(); - mBinding = nullptr; // Clear our current binding ref. - } - } - - return NS_OK; - } - } - - return nsXMLContentSink::HandleEndElement(aName); -} - -NS_IMETHODIMP -nsXBLContentSink::HandleCDataSection(const char16_t* aData, uint32_t aLength) { - if (mState == eXBL_InHandlers || mState == eXBL_InImplementation) - return AddText(aData, aLength); - return nsXMLContentSink::HandleCDataSection(aData, aLength); -} - -#define ENSURE_XBL_STATE(_cond) \ - PR_BEGIN_MACRO \ - if (!(_cond)) { \ - ReportUnexpectedElement(aTagName, aLineNumber); \ - return true; \ - } \ - PR_END_MACRO - -bool nsXBLContentSink::OnOpenContainer(const char16_t** aAtts, - uint32_t aAttsCount, - int32_t aNameSpaceID, nsAtom* aTagName, - uint32_t aLineNumber) { - if (mState == eXBL_Error) { - return true; - } - - if (aNameSpaceID != kNameSpaceID_XBL) { - // Construct non-XBL nodes - return true; - } - - bool ret = true; - if (aTagName == nsGkAtoms::bindings) { - ENSURE_XBL_STATE(mState == eXBL_InDocument); - - NS_ASSERTION(mDocument, "Must have a document!"); - RefPtr<nsXBLDocumentInfo> info = new nsXBLDocumentInfo(mDocument); - - // We keep a weak ref. We're creating a cycle between doc/binding - // manager/doc info. - mDocInfo = info; - - if (!mDocInfo) { - mState = eXBL_Error; - return true; - } - - mDocument->BindingManager()->PutXBLDocumentInfo(mDocInfo); - - nsIURI* uri = mDocument->GetDocumentURI(); - - mIsChromeOrResource = uri->SchemeIs("chrome") || uri->SchemeIs("resource"); - - mState = eXBL_InBindings; - } else if (aTagName == nsGkAtoms::binding) { - ENSURE_XBL_STATE(mState == eXBL_InBindings); - mState = eXBL_InBinding; - } else if (aTagName == nsGkAtoms::handlers) { - ENSURE_XBL_STATE(mState == eXBL_InBinding && mBinding); - mState = eXBL_InHandlers; - ret = false; - } else if (aTagName == nsGkAtoms::handler) { - ENSURE_XBL_STATE(mState == eXBL_InHandlers); - mSecondaryState = eXBL_InHandler; - ConstructHandler(aAtts, aLineNumber); - ret = false; - } else if (aTagName == nsGkAtoms::implementation) { - ENSURE_XBL_STATE(mState == eXBL_InBinding && mBinding); - mState = eXBL_InImplementation; - ConstructImplementation(aAtts); - // Note that this mState will cause us to return false, so no need - // to set ret to false. - } else if (aTagName == nsGkAtoms::constructor) { - ENSURE_XBL_STATE(mState == eXBL_InImplementation && - mSecondaryState == eXBL_None); - NS_ASSERTION(mBinding, "Must have binding here"); - - mSecondaryState = eXBL_InConstructor; - nsAutoString name; - if (!mCurrentBindingID.IsEmpty()) { - name.Assign(mCurrentBindingID); - name.AppendLiteral("_XBL_Constructor"); - } else { - name.AppendLiteral("XBL_Constructor"); - } - nsXBLProtoImplAnonymousMethod* newMethod = - new nsXBLProtoImplAnonymousMethod(name.get()); - newMethod->SetLineNumber(aLineNumber); - mBinding->SetConstructor(newMethod); - AddMember(newMethod); - } else if (aTagName == nsGkAtoms::destructor) { - ENSURE_XBL_STATE(mState == eXBL_InImplementation && - mSecondaryState == eXBL_None); - NS_ASSERTION(mBinding, "Must have binding here"); - mSecondaryState = eXBL_InDestructor; - nsAutoString name; - if (!mCurrentBindingID.IsEmpty()) { - name.Assign(mCurrentBindingID); - name.AppendLiteral("_XBL_Destructor"); - } else { - name.AppendLiteral("XBL_Destructor"); - } - nsXBLProtoImplAnonymousMethod* newMethod = - new nsXBLProtoImplAnonymousMethod(name.get()); - newMethod->SetLineNumber(aLineNumber); - mBinding->SetDestructor(newMethod); - AddMember(newMethod); - } else if (aTagName == nsGkAtoms::field) { - ENSURE_XBL_STATE(mState == eXBL_InImplementation && - mSecondaryState == eXBL_None); - NS_ASSERTION(mBinding, "Must have binding here"); - mSecondaryState = eXBL_InField; - ConstructField(aAtts, aLineNumber); - } else if (aTagName == nsGkAtoms::property) { - ENSURE_XBL_STATE(mState == eXBL_InImplementation && - mSecondaryState == eXBL_None); - NS_ASSERTION(mBinding, "Must have binding here"); - mSecondaryState = eXBL_InProperty; - ConstructProperty(aAtts, aLineNumber); - } else if (aTagName == nsGkAtoms::getter) { - ENSURE_XBL_STATE(mSecondaryState == eXBL_InProperty && mProperty); - NS_ASSERTION(mState == eXBL_InImplementation, "Unexpected state"); - mProperty->SetGetterLineNumber(aLineNumber); - mSecondaryState = eXBL_InGetter; - } else if (aTagName == nsGkAtoms::setter) { - ENSURE_XBL_STATE(mSecondaryState == eXBL_InProperty && mProperty); - NS_ASSERTION(mState == eXBL_InImplementation, "Unexpected state"); - mProperty->SetSetterLineNumber(aLineNumber); - mSecondaryState = eXBL_InSetter; - } else if (aTagName == nsGkAtoms::method) { - ENSURE_XBL_STATE(mState == eXBL_InImplementation && - mSecondaryState == eXBL_None); - NS_ASSERTION(mBinding, "Must have binding here"); - mSecondaryState = eXBL_InMethod; - ConstructMethod(aAtts); - } else if (aTagName == nsGkAtoms::parameter) { - ENSURE_XBL_STATE(mSecondaryState == eXBL_InMethod && mMethod); - NS_ASSERTION(mState == eXBL_InImplementation, "Unexpected state"); - ConstructParameter(aAtts); - } else if (aTagName == nsGkAtoms::body) { - ENSURE_XBL_STATE(mSecondaryState == eXBL_InMethod && mMethod); - NS_ASSERTION(mState == eXBL_InImplementation, "Unexpected state"); - // stash away the line number - mMethod->SetLineNumber(aLineNumber); - mSecondaryState = eXBL_InBody; - } - - return ret && mState != eXBL_InImplementation; -} - -#undef ENSURE_XBL_STATE - -nsresult nsXBLContentSink::ConstructBinding(uint32_t aLineNumber) { - // This is only called from HandleStartElement, so it'd better be an element. - RefPtr<Element> binding = GetCurrentContent()->AsElement(); - binding->GetAttr(kNameSpaceID_None, nsGkAtoms::id, mCurrentBindingID); - NS_ConvertUTF16toUTF8 cid(mCurrentBindingID); - - nsresult rv = NS_OK; - - // Don't create a binding with no id. nsXBLPrototypeBinding::Read also - // performs this check. - if (!cid.IsEmpty()) { - mBinding = new nsXBLPrototypeBinding(); - - rv = mBinding->Init(cid, mDocInfo, binding, !mFoundFirstBinding); - if (NS_SUCCEEDED(rv) && - NS_SUCCEEDED(mDocInfo->SetPrototypeBinding(cid, mBinding))) { - if (!mFoundFirstBinding) { - mFoundFirstBinding = true; - mDocInfo->SetFirstPrototypeBinding(mBinding); - } - binding->UnsetAttr(kNameSpaceID_None, nsGkAtoms::id, false); - } else { - delete mBinding; - mBinding = nullptr; - } - } else { - nsContentUtils::ReportToConsole( - nsIScriptError::errorFlag, NS_LITERAL_CSTRING("XBL Content Sink"), - nullptr, nsContentUtils::eXBL_PROPERTIES, "MissingIdAttr", - nsTArray<nsString>(), mDocumentURI, EmptyString(), aLineNumber); - } - - return rv; -} - -static bool FindValue(const char16_t** aAtts, nsAtom* aAtom, - const char16_t** aResult) { - RefPtr<nsAtom> prefix, localName; - for (; *aAtts; aAtts += 2) { - int32_t nameSpaceID; - nsContentUtils::SplitExpatName(aAtts[0], getter_AddRefs(prefix), - getter_AddRefs(localName), &nameSpaceID); - - // Is this attribute one of the ones we care about? - if (nameSpaceID == kNameSpaceID_None && localName == aAtom) { - *aResult = aAtts[1]; - - return true; - } - } - - return false; -} - -void nsXBLContentSink::ConstructHandler(const char16_t** aAtts, - uint32_t aLineNumber) { - const char16_t* event = nullptr; - const char16_t* modifiers = nullptr; - const char16_t* button = nullptr; - const char16_t* clickcount = nullptr; - const char16_t* keycode = nullptr; - const char16_t* charcode = nullptr; - const char16_t* phase = nullptr; - const char16_t* command = nullptr; - const char16_t* action = nullptr; - const char16_t* group = nullptr; - const char16_t* preventdefault = nullptr; - const char16_t* allowuntrusted = nullptr; - - RefPtr<nsAtom> prefix, localName; - for (; *aAtts; aAtts += 2) { - int32_t nameSpaceID; - nsContentUtils::SplitExpatName(aAtts[0], getter_AddRefs(prefix), - getter_AddRefs(localName), &nameSpaceID); - - if (nameSpaceID != kNameSpaceID_None) { - continue; - } - - // Is this attribute one of the ones we care about? - if (localName == nsGkAtoms::event) - event = aAtts[1]; - else if (localName == nsGkAtoms::modifiers) - modifiers = aAtts[1]; - else if (localName == nsGkAtoms::button) - button = aAtts[1]; - else if (localName == nsGkAtoms::clickcount) - clickcount = aAtts[1]; - else if (localName == nsGkAtoms::keycode) - keycode = aAtts[1]; - else if (localName == nsGkAtoms::key || localName == nsGkAtoms::charcode) - charcode = aAtts[1]; - else if (localName == nsGkAtoms::phase) - phase = aAtts[1]; - else if (localName == nsGkAtoms::command) - command = aAtts[1]; - else if (localName == nsGkAtoms::action) - action = aAtts[1]; - else if (localName == nsGkAtoms::group) - group = aAtts[1]; - else if (localName == nsGkAtoms::preventdefault) - preventdefault = aAtts[1]; - else if (localName == nsGkAtoms::allowuntrusted) - allowuntrusted = aAtts[1]; - } - - if (command && !mIsChromeOrResource) { - // Make sure the XBL doc is chrome or resource if we have a command - // shorthand syntax. - mState = eXBL_Error; - nsContentUtils::ReportToConsole( - nsIScriptError::errorFlag, NS_LITERAL_CSTRING("XBL Content Sink"), - mDocument, nsContentUtils::eXBL_PROPERTIES, "CommandNotInChrome", - nsTArray<nsString>(), nullptr, EmptyString() /* source line */, - aLineNumber); - return; // Don't even make this handler. - } - - // All of our pointers are now filled in. Construct our handler with all of - // these parameters. - nsXBLPrototypeHandler* newHandler; - newHandler = new nsXBLPrototypeHandler( - event, phase, action, command, keycode, charcode, modifiers, button, - clickcount, group, preventdefault, allowuntrusted, mBinding, aLineNumber); - - // Add this handler to our chain of handlers. - if (mHandler) { - // Already have a chain. Just append to the end. - mHandler->SetNextHandler(newHandler); - } else { - // We're the first handler in the chain. - mBinding->SetPrototypeHandlers(newHandler); - } - // Adjust our mHandler pointer to point to the new last handler in the - // chain. - mHandler = newHandler; -} - -void nsXBLContentSink::ConstructImplementation(const char16_t** aAtts) { - mImplementation = nullptr; - mImplMember = nullptr; - mImplField = nullptr; - - if (!mBinding) return; - - const char16_t* name = nullptr; - - RefPtr<nsAtom> prefix, localName; - for (; *aAtts; aAtts += 2) { - int32_t nameSpaceID; - nsContentUtils::SplitExpatName(aAtts[0], getter_AddRefs(prefix), - getter_AddRefs(localName), &nameSpaceID); - - if (nameSpaceID != kNameSpaceID_None) { - continue; - } - - // Is this attribute one of the ones we care about? - if (localName == nsGkAtoms::name) { - name = aAtts[1]; - } else if (localName == nsGkAtoms::implements) { - // Only allow implementation of interfaces via XBL if the principal of - // our XBL document is the system principal. - if (nsContentUtils::IsSystemPrincipal(mDocument->NodePrincipal())) { - mBinding->ConstructInterfaceTable(nsDependentString(aAtts[1])); - } - } - } - - NS_NewXBLProtoImpl(mBinding, name, &mImplementation); -} - -void nsXBLContentSink::ConstructField(const char16_t** aAtts, - uint32_t aLineNumber) { - const char16_t* name = nullptr; - const char16_t* readonly = nullptr; - - RefPtr<nsAtom> prefix, localName; - for (; *aAtts; aAtts += 2) { - int32_t nameSpaceID; - nsContentUtils::SplitExpatName(aAtts[0], getter_AddRefs(prefix), - getter_AddRefs(localName), &nameSpaceID); - - if (nameSpaceID != kNameSpaceID_None) { - continue; - } - - // Is this attribute one of the ones we care about? - if (localName == nsGkAtoms::name) { - name = aAtts[1]; - } else if (localName == nsGkAtoms::readonly) { - readonly = aAtts[1]; - } - } - - if (name) { - // All of our pointers are now filled in. Construct our field with all of - // these parameters. - mField = new nsXBLProtoImplField(name, readonly); - mField->SetLineNumber(aLineNumber); - AddField(mField); - } -} - -void nsXBLContentSink::ConstructProperty(const char16_t** aAtts, - uint32_t aLineNumber) { - const char16_t* name = nullptr; - const char16_t* readonly = nullptr; - const char16_t* onget = nullptr; - const char16_t* onset = nullptr; - bool exposeToUntrustedContent = false; - - RefPtr<nsAtom> prefix, localName; - for (; *aAtts; aAtts += 2) { - int32_t nameSpaceID; - nsContentUtils::SplitExpatName(aAtts[0], getter_AddRefs(prefix), - getter_AddRefs(localName), &nameSpaceID); - - if (nameSpaceID != kNameSpaceID_None) { - continue; - } - - // Is this attribute one of the ones we care about? - if (localName == nsGkAtoms::name) { - name = aAtts[1]; - } else if (localName == nsGkAtoms::readonly) { - readonly = aAtts[1]; - } else if (localName == nsGkAtoms::onget) { - onget = aAtts[1]; - } else if (localName == nsGkAtoms::onset) { - onset = aAtts[1]; - } else if (localName == nsGkAtoms::exposeToUntrustedContent && - nsDependentString(aAtts[1]).EqualsLiteral("true")) { - exposeToUntrustedContent = true; - } - } - - if (name) { - // All of our pointers are now filled in. Construct our property with all of - // these parameters. - mProperty = - new nsXBLProtoImplProperty(name, onget, onset, readonly, aLineNumber); - if (exposeToUntrustedContent) { - mProperty->SetExposeToUntrustedContent(true); - } - AddMember(mProperty); - } -} - -void nsXBLContentSink::ConstructMethod(const char16_t** aAtts) { - mMethod = nullptr; - - const char16_t* name = nullptr; - const char16_t* expose = nullptr; - if (FindValue(aAtts, nsGkAtoms::name, &name)) { - mMethod = new nsXBLProtoImplMethod(name); - if (FindValue(aAtts, nsGkAtoms::exposeToUntrustedContent, &expose) && - nsDependentString(expose).EqualsLiteral("true")) { - mMethod->SetExposeToUntrustedContent(true); - } - } - - if (mMethod) { - AddMember(mMethod); - } -} - -void nsXBLContentSink::ConstructParameter(const char16_t** aAtts) { - if (!mMethod) return; - - const char16_t* name = nullptr; - if (FindValue(aAtts, nsGkAtoms::name, &name)) { - mMethod->AddParameter(nsDependentString(name)); - } -} - -nsresult nsXBLContentSink::CreateElement( - const char16_t** aAtts, uint32_t aAttsCount, - mozilla::dom::NodeInfo* aNodeInfo, uint32_t aLineNumber, - uint32_t aColumnNumber, nsIContent** aResult, bool* aAppendContent, - FromParser aFromParser) { -#ifdef MOZ_XUL - if (!aNodeInfo->NamespaceEquals(kNameSpaceID_XUL)) { -#endif - return nsXMLContentSink::CreateElement(aAtts, aAttsCount, aNodeInfo, - aLineNumber, aColumnNumber, aResult, - aAppendContent, aFromParser); -#ifdef MOZ_XUL - } - - // Note that this needs to match the code in - // nsXBLPrototypeBinding::ReadContentNode. - - *aAppendContent = true; - RefPtr<nsXULPrototypeElement> prototype = - new nsXULPrototypeElement(aNodeInfo); - - AddAttributesToXULPrototype(aAtts, aAttsCount, prototype); - - Element* result; - nsresult rv = nsXULElement::CreateFromPrototype(prototype, mDocument, false, - false, &result); - *aResult = result; - return rv; -#endif -} - -nsresult nsXBLContentSink::AddAttributes(const char16_t** aAtts, - Element* aElement) { - if (aElement->IsXULElement()) - return NS_OK; // Nothing to do, since the proto already has the attrs. - - return nsXMLContentSink::AddAttributes(aAtts, aElement); -} - -#ifdef MOZ_XUL -nsresult nsXBLContentSink::AddAttributesToXULPrototype( - const char16_t** aAtts, uint32_t aAttsCount, - nsXULPrototypeElement* aElement) { - // Add tag attributes to the element - nsresult rv; - - // Create storage for the attributes - nsXULPrototypeAttribute* attrs = nullptr; - if (aAttsCount > 0) { - attrs = aElement->mAttributes.AppendElements(aAttsCount); - } - - // Copy the attributes into the prototype - RefPtr<nsAtom> prefix, localName; - - uint32_t i; - for (i = 0; i < aAttsCount; ++i) { - int32_t nameSpaceID; - nsContentUtils::SplitExpatName(aAtts[i * 2], getter_AddRefs(prefix), - getter_AddRefs(localName), &nameSpaceID); - - if (nameSpaceID == kNameSpaceID_None) { - attrs[i].mName.SetTo(localName); - } else { - RefPtr<NodeInfo> ni; - ni = mNodeInfoManager->GetNodeInfo(localName, prefix, nameSpaceID, - nsINode::ATTRIBUTE_NODE); - attrs[i].mName.SetTo(ni); - } - - rv = aElement->SetAttrAt(i, nsDependentString(aAtts[i * 2 + 1]), - mDocumentURI); - NS_ENSURE_SUCCESS(rv, rv); - } - - return NS_OK; -} -#endif
deleted file mode 100644 --- a/dom/xbl/nsXBLContentSink.h +++ /dev/null @@ -1,145 +0,0 @@ -/* -*- 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 nsXBLContentSink_h__ -#define nsXBLContentSink_h__ - -#include "mozilla/Attributes.h" -#include "nsXMLContentSink.h" -#include "nsXBLDocumentInfo.h" -#include "nsXBLPrototypeHandler.h" -#include "nsXBLProtoImpl.h" -#include "nsLayoutCID.h" - -/* - * Enum that describes the primary state of the parsing process - */ -typedef enum { - eXBL_InDocument, /* outside any bindings */ - eXBL_InBindings, /* Inside a <bindings> element */ - eXBL_InBinding, /* Inside a <binding> */ - eXBL_InImplementation, /* Inside a <implementation> */ - eXBL_InHandlers, /* Inside a <handlers> */ - eXBL_Error /* An error has occurred. Suspend binding construction */ -} XBLPrimaryState; - -/* - * Enum that describes our substate (typically when parsing something - * like <handlers> or <implementation>). - */ -typedef enum { - eXBL_None, - eXBL_InHandler, - eXBL_InMethod, - eXBL_InProperty, - eXBL_InField, - eXBL_InBody, - eXBL_InGetter, - eXBL_InSetter, - eXBL_InConstructor, - eXBL_InDestructor -} XBLSecondaryState; - -class nsXULPrototypeElement; -class nsXBLProtoImplMember; -class nsXBLProtoImplProperty; -class nsXBLProtoImplMethod; -class nsXBLProtoImplField; -class nsXBLPrototypeBinding; - -// The XBL content sink overrides the XML content sink to -// builds its own lightweight data structures for the <resources>, -// <handlers>, <implementation>, and - -class nsXBLContentSink : public nsXMLContentSink { - public: - nsXBLContentSink(); - ~nsXBLContentSink(); - - nsresult Init(mozilla::dom::Document* aDoc, nsIURI* aURL, - nsISupports* aContainer); - - // nsIContentSink overrides - NS_IMETHOD HandleStartElement(const char16_t* aName, const char16_t** aAtts, - uint32_t aAttsCount, uint32_t aLineNumber, - uint32_t aColumnNumber) override; - - NS_IMETHOD HandleEndElement(const char16_t* aName) override; - - NS_IMETHOD HandleCDataSection(const char16_t* aData, - uint32_t aLength) override; - - protected: - // nsXMLContentSink overrides - virtual void MaybeStartLayout(bool aIgnorePendingSheets) override; - - bool OnOpenContainer(const char16_t** aAtts, uint32_t aAttsCount, - int32_t aNameSpaceID, nsAtom* aTagName, - uint32_t aLineNumber) override; - - bool NotifyForDocElement() override { return false; } - - nsresult CreateElement(const char16_t** aAtts, uint32_t aAttsCount, - mozilla::dom::NodeInfo* aNodeInfo, - uint32_t aLineNumber, uint32_t aColumnNumber, - nsIContent** aResult, bool* aAppendContent, - mozilla::dom::FromParser aFromParser) override; - - nsresult AddAttributes(const char16_t** aAtts, - mozilla::dom::Element* aElement) override; - -#ifdef MOZ_XUL - nsresult AddAttributesToXULPrototype(const char16_t** aAtts, - uint32_t aAttsCount, - nsXULPrototypeElement* aElement); -#endif - - // Our own helpers for constructing XBL prototype objects. - nsresult ConstructBinding(uint32_t aLineNumber); - void ConstructHandler(const char16_t** aAtts, uint32_t aLineNumber); - void ConstructImplementation(const char16_t** aAtts); - void ConstructProperty(const char16_t** aAtts, uint32_t aLineNumber); - void ConstructMethod(const char16_t** aAtts); - void ConstructParameter(const char16_t** aAtts); - void ConstructField(const char16_t** aAtts, uint32_t aLineNumber); - - // nsXMLContentSink overrides - nsresult FlushText(bool aReleaseTextNode = true) override; - - // nsIExpatSink overrides - NS_IMETHOD ReportError(const char16_t* aErrorText, - const char16_t* aSourceText, nsIScriptError* aError, - bool* _retval) override; - - protected: - nsresult ReportUnexpectedElement(nsAtom* aElementName, uint32_t aLineNumber); - - void AddMember(nsXBLProtoImplMember* aMember); - void AddField(nsXBLProtoImplField* aField); - - XBLPrimaryState mState; - XBLSecondaryState mSecondaryState; - nsXBLDocumentInfo* mDocInfo; - bool mIsChromeOrResource; // For bug #45989 - bool mFoundFirstBinding; - - nsString mCurrentBindingID; - - nsXBLPrototypeBinding* mBinding; - nsXBLPrototypeHandler* - mHandler; // current handler, owned by its PrototypeBinding - nsXBLProtoImpl* mImplementation; - nsXBLProtoImplMember* mImplMember; - nsXBLProtoImplField* mImplField; - nsXBLProtoImplProperty* mProperty; - nsXBLProtoImplMethod* mMethod; - nsXBLProtoImplField* mField; -}; - -nsresult NS_NewXBLContentSink(nsIXMLContentSink** aResult, - mozilla::dom::Document* aDoc, nsIURI* aURL, - nsISupports* aContainer); -#endif // nsXBLContentSink_h__
deleted file mode 100644 --- a/dom/xbl/nsXBLDocumentInfo.cpp +++ /dev/null @@ -1,311 +0,0 @@ -/* -*- 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/DebugOnly.h" - -#include "nsXBLDocumentInfo.h" -#include "mozilla/dom/Document.h" -#include "nsXBLPrototypeBinding.h" -#include "nsIScriptObjectPrincipal.h" -#include "nsIScriptContext.h" -#include "jsapi.h" -#include "jsfriendapi.h" -#include "nsIURI.h" -#include "nsIConsoleService.h" -#include "nsIScriptError.h" -#include "nsIChromeRegistry.h" -#include "nsIPrincipal.h" -#include "nsJSPrincipals.h" -#include "nsIScriptSecurityManager.h" -#include "nsContentUtils.h" -#include "nsDOMJSUtils.h" -#include "nsWindowSizes.h" -#include "mozilla/Services.h" -#include "xpcpublic.h" -#include "mozilla/scache/StartupCache.h" -#include "mozilla/scache/StartupCacheUtils.h" -#include "nsCCUncollectableMarker.h" -#include "mozilla/dom/BindingUtils.h" -#include "mozilla/dom/URL.h" - -using namespace mozilla; -using namespace mozilla::scache; -using namespace mozilla::dom; - -static const char kXBLCachePrefix[] = "xblcache"; - -/* Implementation file */ -NS_IMPL_CYCLE_COLLECTION_CLASS(nsXBLDocumentInfo) - -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXBLDocumentInfo) - if (tmp->mBindingTable) { - for (auto iter = tmp->mBindingTable->ConstIter(); !iter.Done(); - iter.Next()) { - iter.UserData()->Unlink(); - } - } - NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument) -NS_IMPL_CYCLE_COLLECTION_UNLINK_END -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXBLDocumentInfo) - if (tmp->mDocument && nsCCUncollectableMarker::InGeneration( - cb, tmp->mDocument->GetMarkedCCGeneration())) { - return NS_SUCCESS_INTERRUPTED_TRAVERSE; - } - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument) - if (tmp->mBindingTable) { - for (auto iter = tmp->mBindingTable->ConstIter(); !iter.Done(); - iter.Next()) { - iter.UserData()->Traverse(cb); - } - } -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END -NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsXBLDocumentInfo) - if (tmp->mBindingTable) { - for (auto iter = tmp->mBindingTable->ConstIter(); !iter.Done(); - iter.Next()) { - iter.UserData()->Trace(aCallbacks, aClosure); - } - } -NS_IMPL_CYCLE_COLLECTION_TRACE_END - -static void UnmarkXBLJSObject(JS::GCCellPtr aPtr, const char* aName, - void* aClosure) { - JS::ExposeObjectToActiveJS(&aPtr.as<JSObject>()); -} - -void nsXBLDocumentInfo::MarkInCCGeneration(uint32_t aGeneration) { - if (mDocument) { - mDocument->MarkUncollectableForCCGeneration(aGeneration); - } - // Unmark any JS we hold - if (mBindingTable) { - for (auto iter = mBindingTable->Iter(); !iter.Done(); iter.Next()) { - iter.UserData()->Trace(TraceCallbackFunc(UnmarkXBLJSObject), nullptr); - } - } -} - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXBLDocumentInfo) - NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) - NS_INTERFACE_MAP_ENTRY(nsISupports) -NS_INTERFACE_MAP_END - -NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXBLDocumentInfo) -NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXBLDocumentInfo) - -nsXBLDocumentInfo::nsXBLDocumentInfo(Document* aDocument) - : mDocument(aDocument), - mScriptAccess(true), - mIsChrome(false), - mFirstBinding(nullptr) { - nsIURI* uri = aDocument->GetDocumentURI(); - if (IsChromeURI(uri)) { - // Cache whether or not this chrome XBL can execute scripts. - nsCOMPtr<nsIXULChromeRegistry> reg = - mozilla::services::GetXULChromeRegistryService(); - if (reg) { - bool allow = true; - reg->AllowScriptsForPackage(uri, &allow); - mScriptAccess = allow; - } - mIsChrome = true; - } else { - // If this binding isn't running with system principal, then it's running - // from a remote-XUL whitelisted domain. This is already a not-really- - // supported configuration (among other things, we don't use XBL scopes in - // that configuration for compatibility reasons). But we should still at - // least make an effort to prevent binding code from running if content - // script is disabled or if the source domain is blacklisted (since the - // source domain for remote XBL must always be the same as the source domain - // of the bound content). - // - // If we just ask the binding document if script is enabled, it will - // discover that it has no inner window, and return false. So instead, we - // short-circuit the normal compartment-managed script-disabling machinery, - // and query the policy for the URI directly. - bool allow; - nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); - nsresult rv = ssm->PolicyAllowsScript(uri, &allow); - mScriptAccess = NS_SUCCEEDED(rv) && allow; - } -} - -nsXBLDocumentInfo::~nsXBLDocumentInfo() { mozilla::DropJSObjects(this); } - -nsXBLPrototypeBinding* nsXBLDocumentInfo::GetPrototypeBinding( - const nsACString& aRef) { - if (!mBindingTable) return nullptr; - - if (aRef.IsEmpty()) { - // Return our first binding - return mFirstBinding; - } - - return mBindingTable->Get(aRef); -} - -nsresult nsXBLDocumentInfo::SetPrototypeBinding( - const nsACString& aRef, nsXBLPrototypeBinding* aBinding) { - if (!mBindingTable) { - mBindingTable = - new nsClassHashtable<nsCStringHashKey, nsXBLPrototypeBinding>(); - mozilla::HoldJSObjects(this); - } - - NS_ENSURE_STATE(!mBindingTable->Get(aRef)); - mBindingTable->Put(aRef, aBinding); - - return NS_OK; -} - -void nsXBLDocumentInfo::RemovePrototypeBinding(const nsACString& aRef) { - if (mBindingTable) { - nsAutoPtr<nsXBLPrototypeBinding> bindingToRemove; - mBindingTable->Remove(aRef, &bindingToRemove); - - // We do not want to destroy the binding, so just forget it. - bindingToRemove.forget(); - } -} - -// static -nsresult nsXBLDocumentInfo::ReadPrototypeBindings(nsIURI* aURI, - nsXBLDocumentInfo** aDocInfo, - Document* aBoundDocument) { - *aDocInfo = nullptr; - - nsAutoCString spec(kXBLCachePrefix); - nsresult rv = PathifyURI(aURI, spec); - NS_ENSURE_SUCCESS(rv, rv); - - StartupCache* startupCache = StartupCache::GetSingleton(); - if (!startupCache) { - return NS_ERROR_FAILURE; - } - - const char* buf; - uint32_t len; - rv = startupCache->GetBuffer(spec.get(), &buf, &len); - // GetBuffer will fail if the binding is not in the cache. - if (NS_FAILED(rv)) return rv; - - nsCOMPtr<nsIObjectInputStream> stream; - rv = NewObjectInputStreamFromBuffer(buf, len, getter_AddRefs(stream)); - NS_ENSURE_SUCCESS(rv, rv); - - // The file compatibility.ini stores the build id. This is checked in - // nsAppRunner.cpp and will delete the cache if a different build is - // present. However, we check that the version matches here to be safe. - uint32_t version; - rv = stream->Read32(&version); - NS_ENSURE_SUCCESS(rv, rv); - if (version != XBLBinding_Serialize_Version) { - // The version that exists is different than expected, likely created with a - // different build, so invalidate the cache. - startupCache->InvalidateCache(); - return NS_ERROR_NOT_AVAILABLE; - } - - nsCOMPtr<nsIPrincipal> principal; - nsContentUtils::GetSecurityManager()->GetSystemPrincipal( - getter_AddRefs(principal)); - - nsCOMPtr<Document> doc; - rv = NS_NewXBLDocument(getter_AddRefs(doc), aURI, nullptr, principal); - NS_ENSURE_SUCCESS(rv, rv); - - RefPtr<nsXBLDocumentInfo> docInfo = new nsXBLDocumentInfo(doc); - - while (1) { - uint8_t flags; - nsresult rv = stream->Read8(&flags); - NS_ENSURE_SUCCESS(rv, rv); - if (flags == XBLBinding_Serialize_NoMoreBindings) break; - - rv = nsXBLPrototypeBinding::ReadNewBinding(stream, docInfo, doc, flags); - if (NS_FAILED(rv)) { - return rv; - } - } - - docInfo.forget(aDocInfo); - return NS_OK; -} - -nsresult nsXBLDocumentInfo::WritePrototypeBindings() { - // Only write out bindings with the system principal - if (!nsContentUtils::IsSystemPrincipal(mDocument->NodePrincipal())) - return NS_OK; - - nsAutoCString spec(kXBLCachePrefix); - nsresult rv = PathifyURI(DocumentURI(), spec); - NS_ENSURE_SUCCESS(rv, rv); - - StartupCache* startupCache = StartupCache::GetSingleton(); - if (!startupCache) { - return rv; - } - - nsCOMPtr<nsIObjectOutputStream> stream; - nsCOMPtr<nsIStorageStream> storageStream; - rv = NewObjectOutputWrappedStorageStream(getter_AddRefs(stream), - getter_AddRefs(storageStream), true); - NS_ENSURE_SUCCESS(rv, rv); - - rv = stream->Write32(XBLBinding_Serialize_Version); - NS_ENSURE_SUCCESS(rv, rv); - - if (mBindingTable) { - for (auto iter = mBindingTable->Iter(); !iter.Done(); iter.Next()) { - iter.UserData()->Write(stream); - } - } - - // write a end marker at the end - rv = stream->Write8(XBLBinding_Serialize_NoMoreBindings); - NS_ENSURE_SUCCESS(rv, rv); - - stream->Close(); - NS_ENSURE_SUCCESS(rv, rv); - - uint32_t len; - UniquePtr<char[]> buf; - rv = NewBufferFromStorageStream(storageStream, &buf, &len); - NS_ENSURE_SUCCESS(rv, rv); - - return startupCache->PutBuffer(spec.get(), std::move(buf), len); -} - -void nsXBLDocumentInfo::SetFirstPrototypeBinding( - nsXBLPrototypeBinding* aBinding) { - mFirstBinding = aBinding; -} - -#ifdef DEBUG -void AssertInCompilationScope() { - AutoJSContext cx; - MOZ_ASSERT(xpc::CompilationScope() == JS::CurrentGlobalOrNull(cx)); -} -#endif - -size_t nsXBLDocumentInfo::SizeOfIncludingThis( - MallocSizeOf aMallocSizeOf) const { - size_t n = aMallocSizeOf(this); - if (mDocument) { - SizeOfState state(aMallocSizeOf); - nsWindowSizes windowSizes(state); - mDocument->DocAddSizeOfIncludingThis(windowSizes); - n += windowSizes.getTotalSize(); - } - if (mBindingTable) { - n += mBindingTable->ShallowSizeOfIncludingThis(aMallocSizeOf); - for (auto iter = mBindingTable->Iter(); !iter.Done(); iter.Next()) { - nsXBLPrototypeBinding* binding = iter.UserData(); - n += binding->SizeOfIncludingThis(aMallocSizeOf); - } - } - return n; -}
deleted file mode 100644 --- a/dom/xbl/nsXBLDocumentInfo.h +++ /dev/null @@ -1,75 +0,0 @@ -/* -*- 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 nsXBLDocumentInfo_h__ -#define nsXBLDocumentInfo_h__ - -#include "mozilla/Attributes.h" -#include "mozilla/MemoryReporting.h" -#include "nsCOMPtr.h" -#include "nsAutoPtr.h" -#include "nsWeakReference.h" -#include "mozilla/dom/Document.h" -#include "nsCycleCollectionParticipant.h" - -class nsXBLPrototypeBinding; - -class nsXBLDocumentInfo final : public nsSupportsWeakReference { - public: - NS_DECL_CYCLE_COLLECTING_ISUPPORTS - - explicit nsXBLDocumentInfo(mozilla::dom::Document* aDocument); - - mozilla::dom::Document* GetDocument() const { return mDocument; } - - bool GetScriptAccess() const { return mScriptAccess; } - - nsIURI* DocumentURI() { return mDocument->GetDocumentURI(); } - - nsXBLPrototypeBinding* GetPrototypeBinding(const nsACString& aRef); - nsresult SetPrototypeBinding(const nsACString& aRef, - nsXBLPrototypeBinding* aBinding); - - // This removes the binding without deleting it - void RemovePrototypeBinding(const nsACString& aRef); - - nsresult WritePrototypeBindings(); - - void SetFirstPrototypeBinding(nsXBLPrototypeBinding* aBinding); - - bool IsChrome() { return mIsChrome; } - - void MarkInCCGeneration(uint32_t aGeneration); - - static nsresult ReadPrototypeBindings(nsIURI* aURI, - nsXBLDocumentInfo** aDocInfo, - mozilla::dom::Document* aBoundDocument); - - size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; - - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsXBLDocumentInfo) - - private: - virtual ~nsXBLDocumentInfo(); - - RefPtr<mozilla::dom::Document> mDocument; - bool mScriptAccess; - bool mIsChrome; - // the binding table owns each nsXBLPrototypeBinding - nsAutoPtr<nsClassHashtable<nsCStringHashKey, nsXBLPrototypeBinding>> - mBindingTable; - - // non-owning pointer to the first binding in the table - nsXBLPrototypeBinding* mFirstBinding; -}; - -#ifdef DEBUG -void AssertInCompilationScope(); -#else -inline void AssertInCompilationScope() {} -#endif - -#endif
deleted file mode 100644 --- a/dom/xbl/nsXBLEventHandler.cpp +++ /dev/null @@ -1,161 +0,0 @@ -/* -*- 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 "nsCOMPtr.h" -#include "nsAtom.h" -#include "nsIDOMEventListener.h" -#include "nsXBLPrototypeHandler.h" -#include "nsContentUtils.h" -#include "mozilla/dom/Event.h" // for Event -#include "mozilla/dom/EventBinding.h" // for Event -#include "mozilla/dom/EventTarget.h" -#include "mozilla/dom/KeyboardEvent.h" -#include "mozilla/TextEvents.h" - -using namespace mozilla; -using namespace mozilla::dom; - -nsXBLEventHandler::nsXBLEventHandler(nsXBLPrototypeHandler* aHandler) - : mProtoHandler(aHandler) {} - -nsXBLEventHandler::~nsXBLEventHandler() {} - -NS_IMPL_ISUPPORTS(nsXBLEventHandler, nsIDOMEventListener) - -NS_IMETHODIMP -nsXBLEventHandler::HandleEvent(Event* aEvent) { - if (!mProtoHandler) return NS_ERROR_FAILURE; - - uint8_t phase = mProtoHandler->GetPhase(); - if (phase == NS_PHASE_TARGET) { - if (aEvent->EventPhase() != Event_Binding::AT_TARGET) { - return NS_OK; - } - } - - if (!EventMatched(aEvent)) return NS_OK; - - RefPtr<EventTarget> currentTarget = aEvent->GetCurrentTarget(); - mProtoHandler->ExecuteHandler(currentTarget, aEvent); - - return NS_OK; -} - -nsXBLMouseEventHandler::nsXBLMouseEventHandler(nsXBLPrototypeHandler* aHandler) - : nsXBLEventHandler(aHandler) {} - -nsXBLMouseEventHandler::~nsXBLMouseEventHandler() {} - -bool nsXBLMouseEventHandler::EventMatched(Event* aEvent) { - MouseEvent* mouse = aEvent->AsMouseEvent(); - return mouse && mProtoHandler->MouseEventMatched(mouse); -} - -nsXBLKeyEventHandler::nsXBLKeyEventHandler(nsAtom* aEventType, uint8_t aPhase, - uint8_t aType) - : mEventType(aEventType), - mPhase(aPhase), - mType(aType), - mIsBoundToChrome(false) {} - -nsXBLKeyEventHandler::~nsXBLKeyEventHandler() {} - -NS_IMPL_ISUPPORTS(nsXBLKeyEventHandler, nsIDOMEventListener) - -bool nsXBLKeyEventHandler::ExecuteMatchedHandlers( - KeyboardEvent* aKeyEvent, uint32_t aCharCode, - const IgnoreModifierState& aIgnoreModifierState) { - WidgetEvent* event = aKeyEvent->WidgetEventPtr(); - nsCOMPtr<EventTarget> target = aKeyEvent->GetCurrentTarget(); - - bool executed = false; - for (uint32_t i = 0; i < mProtoHandlers.Length(); ++i) { - nsXBLPrototypeHandler* handler = mProtoHandlers[i]; - bool hasAllowUntrustedAttr = handler->HasAllowUntrustedAttr(); - if ((event->IsTrusted() || - (hasAllowUntrustedAttr && handler->AllowUntrustedEvents()) || - (!hasAllowUntrustedAttr && !mIsBoundToChrome)) && - handler->KeyEventMatched(aKeyEvent, aCharCode, aIgnoreModifierState)) { - handler->ExecuteHandler(target, aKeyEvent); - executed = true; - } - } -#ifdef XP_WIN - // Windows native applications ignore Windows-Logo key state when checking - // shortcut keys even if the key is pressed. Therefore, if there is no - // shortcut key which exactly matches current modifier state, we should - // retry to look for a shortcut key without the Windows-Logo key press. - if (!executed && !aIgnoreModifierState.mOS) { - WidgetKeyboardEvent* keyEvent = event->AsKeyboardEvent(); - if (keyEvent && keyEvent->IsOS()) { - IgnoreModifierState ignoreModifierState(aIgnoreModifierState); - ignoreModifierState.mOS = true; - return ExecuteMatchedHandlers(aKeyEvent, aCharCode, ignoreModifierState); - } - } -#endif - return executed; -} - -NS_IMETHODIMP -nsXBLKeyEventHandler::HandleEvent(Event* aEvent) { - uint32_t count = mProtoHandlers.Length(); - if (count == 0) return NS_ERROR_FAILURE; - - if (mPhase == NS_PHASE_TARGET) { - if (aEvent->EventPhase() != Event_Binding::AT_TARGET) { - return NS_OK; - } - } - - RefPtr<KeyboardEvent> key = aEvent->AsKeyboardEvent(); - if (!key) { - return NS_OK; - } - - WidgetKeyboardEvent* nativeKeyboardEvent = - aEvent->WidgetEventPtr()->AsKeyboardEvent(); - MOZ_ASSERT(nativeKeyboardEvent); - AutoShortcutKeyCandidateArray shortcutKeys; - nativeKeyboardEvent->GetShortcutKeyCandidates(shortcutKeys); - - if (shortcutKeys.IsEmpty()) { - ExecuteMatchedHandlers(key, 0, IgnoreModifierState()); - return NS_OK; - } - - for (uint32_t i = 0; i < shortcutKeys.Length(); ++i) { - IgnoreModifierState ignoreModifierState; - ignoreModifierState.mShift = shortcutKeys[i].mIgnoreShift; - if (ExecuteMatchedHandlers(key, shortcutKeys[i].mCharCode, - ignoreModifierState)) { - return NS_OK; - } - } - return NS_OK; -} - -/////////////////////////////////////////////////////////////////////////////////// - -already_AddRefed<nsXBLEventHandler> NS_NewXBLEventHandler( - nsXBLPrototypeHandler* aHandler, nsAtom* aEventType) { - RefPtr<nsXBLEventHandler> handler; - - switch (nsContentUtils::GetEventClassID(nsDependentAtomString(aEventType))) { - case eDragEventClass: - case eMouseEventClass: - case eMouseScrollEventClass: - case eWheelEventClass: - case eSimpleGestureEventClass: - handler = new nsXBLMouseEventHandler(aHandler); - break; - default: - handler = new nsXBLEventHandler(aHandler); - break; - } - - return handler.forget(); -}
deleted file mode 100644 --- a/dom/xbl/nsXBLEventHandler.h +++ /dev/null @@ -1,99 +0,0 @@ -/* -*- 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 nsXBLEventHandler_h__ -#define nsXBLEventHandler_h__ - -#include "mozilla/Attributes.h" -#include "nsCOMPtr.h" -#include "nsIDOMEventListener.h" -#include "nsTArray.h" - -class nsAtom; -class nsXBLPrototypeHandler; - -namespace mozilla { -struct IgnoreModifierState; -namespace dom { -class Event; -class KeyboardEvent; -} // namespace dom -} // namespace mozilla - -class nsXBLEventHandler : public nsIDOMEventListener { - public: - explicit nsXBLEventHandler(nsXBLPrototypeHandler* aHandler); - - NS_DECL_ISUPPORTS - - NS_DECL_NSIDOMEVENTLISTENER - - protected: - virtual ~nsXBLEventHandler(); - nsXBLPrototypeHandler* mProtoHandler; - - private: - nsXBLEventHandler(); - virtual bool EventMatched(mozilla::dom::Event* aEvent) { return true; } -}; - -class nsXBLMouseEventHandler : public nsXBLEventHandler { - public: - explicit nsXBLMouseEventHandler(nsXBLPrototypeHandler* aHandler); - virtual ~nsXBLMouseEventHandler(); - - private: - bool EventMatched(mozilla::dom::Event* aEvent) override; -}; - -class nsXBLKeyEventHandler : public nsIDOMEventListener { - typedef mozilla::IgnoreModifierState IgnoreModifierState; - - public: - nsXBLKeyEventHandler(nsAtom* aEventType, uint8_t aPhase, uint8_t aType); - - NS_DECL_ISUPPORTS - - NS_DECL_NSIDOMEVENTLISTENER - - void AddProtoHandler(nsXBLPrototypeHandler* aProtoHandler) { - mProtoHandlers.AppendElement(aProtoHandler); - } - - bool Matches(nsAtom* aEventType, uint8_t aPhase, uint8_t aType) const { - return (mEventType == aEventType && mPhase == aPhase && mType == aType); - } - - void GetEventName(nsAString& aString) const { mEventType->ToString(aString); } - - uint8_t GetPhase() const { return mPhase; } - - uint8_t GetType() const { return mType; } - - void SetIsBoundToChrome(bool aIsBoundToChrome) { - mIsBoundToChrome = aIsBoundToChrome; - } - - private: - nsXBLKeyEventHandler(); - virtual ~nsXBLKeyEventHandler(); - - MOZ_CAN_RUN_SCRIPT - bool ExecuteMatchedHandlers(mozilla::dom::KeyboardEvent* aEvent, - uint32_t aCharCode, - const IgnoreModifierState& aIgnoreModifierState); - - nsTArray<nsXBLPrototypeHandler*> mProtoHandlers; - RefPtr<nsAtom> mEventType; - uint8_t mPhase; - uint8_t mType; - bool mIsBoundToChrome; -}; - -already_AddRefed<nsXBLEventHandler> NS_NewXBLEventHandler( - nsXBLPrototypeHandler* aHandler, nsAtom* aEventType); - -#endif
deleted file mode 100644 --- a/dom/xbl/nsXBLMaybeCompiled.h +++ /dev/null @@ -1,144 +0,0 @@ -/* -*- 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 nsXBLMaybeCompiled_h__ -#define nsXBLMaybeCompiled_h__ - -#include "js/RootingAPI.h" - -/* - * A union containing either a pointer representing uncompiled source or a - * JSObject* representing the compiled result. The class is templated on the - * source object type. - * - * The purpose of abstracting this as a separate class is to allow it to be - * wrapped in a JS::Heap<T> to correctly handle post-barriering of the JSObject - * pointer, when present. - * - * No implementation of rootKind() is provided, which prevents - * Root<nsXBLMaybeCompiled<UncompiledT>> from being used. - */ -template <class UncompiledT> -class nsXBLMaybeCompiled { - public: - nsXBLMaybeCompiled() : mUncompiled(BIT_UNCOMPILED) {} - - explicit nsXBLMaybeCompiled(UncompiledT* uncompiled) - : mUncompiled(reinterpret_cast<uintptr_t>(uncompiled) | BIT_UNCOMPILED) {} - - explicit nsXBLMaybeCompiled(JSObject* compiled) : mCompiled(compiled) {} - - bool IsCompiled() const { return !(mUncompiled & BIT_UNCOMPILED); } - - UncompiledT* GetUncompiled() const { - MOZ_ASSERT(!IsCompiled(), "Attempt to get compiled function as uncompiled"); - uintptr_t unmasked = mUncompiled & ~BIT_UNCOMPILED; - return reinterpret_cast<UncompiledT*>(unmasked); - } - - JSObject* GetJSFunction() const { - MOZ_ASSERT(IsCompiled(), "Attempt to get uncompiled function as compiled"); - if (mCompiled) { - JS::ExposeObjectToActiveJS(mCompiled); - } - return mCompiled; - } - - // This is appropriate for use in tracing methods, etc. - JSObject* GetJSFunctionPreserveColor() const { - MOZ_ASSERT(IsCompiled(), "Attempt to get uncompiled function as compiled"); - return mCompiled; - } - - private: - JSObject*& UnsafeGetJSFunction() { - MOZ_ASSERT(IsCompiled(), "Attempt to get uncompiled function as compiled"); - return mCompiled; - } - - enum { BIT_UNCOMPILED = 1 << 0 }; - - union { - // An pointer that represents the function before being compiled, with - // BIT_UNCOMPILED set. - uintptr_t mUncompiled; - - // The JS object for the compiled result. - JSObject* mCompiled; - }; - - friend struct js::BarrierMethods<nsXBLMaybeCompiled<UncompiledT>>; -}; - -namespace js { - -template <class UncompiledT> -struct BarrierMethods<nsXBLMaybeCompiled<UncompiledT>> { - typedef struct BarrierMethods<JSObject*> Base; - - static void postWriteBarrier(nsXBLMaybeCompiled<UncompiledT>* functionp, - nsXBLMaybeCompiled<UncompiledT> prev, - nsXBLMaybeCompiled<UncompiledT> next) { - if (next.IsCompiled()) { - Base::postWriteBarrier( - &functionp->UnsafeGetJSFunction(), - prev.IsCompiled() ? prev.UnsafeGetJSFunction() : nullptr, - next.UnsafeGetJSFunction()); - } else if (prev.IsCompiled()) { - Base::postWriteBarrier(&prev.UnsafeGetJSFunction(), - prev.UnsafeGetJSFunction(), nullptr); - } - } - static void exposeToJS(nsXBLMaybeCompiled<UncompiledT> fun) { - if (fun.IsCompiled()) { - JS::ExposeObjectToActiveJS(fun.UnsafeGetJSFunction()); - } - } -}; - -template <class T> -struct IsHeapConstructibleType< - nsXBLMaybeCompiled<T>> { // Yes, this is the exception to the rule. Sorry. - static constexpr bool value = true; -}; - -template <class UncompiledT, class Wrapper> -class HeapBase<nsXBLMaybeCompiled<UncompiledT>, Wrapper> { - const Wrapper& wrapper() const { return *static_cast<const Wrapper*>(this); } - - Wrapper& wrapper() { return *static_cast<Wrapper*>(this); } - - const nsXBLMaybeCompiled<UncompiledT>* extract() const { - return wrapper().address(); - } - - nsXBLMaybeCompiled<UncompiledT>* extract() { return wrapper().unsafeGet(); } - - public: - bool IsCompiled() const { return extract()->IsCompiled(); } - UncompiledT* GetUncompiled() const { return extract()->GetUncompiled(); } - JSObject* GetJSFunction() const { return extract()->GetJSFunction(); } - JSObject* GetJSFunctionPreserveColor() const { - return extract()->GetJSFunctionPreserveColor(); - } - - void SetUncompiled(UncompiledT* source) { - wrapper() = nsXBLMaybeCompiled<UncompiledT>(source); - } - - void SetJSFunction(JSObject* function) { - wrapper() = nsXBLMaybeCompiled<UncompiledT>(function); - } - - JS::Heap<JSObject*>& AsHeapObject() { - MOZ_ASSERT(extract()->IsCompiled()); - return *reinterpret_cast<JS::Heap<JSObject*>*>(this); - } -}; - -} /* namespace js */ - -#endif // nsXBLMaybeCompiled_h__
deleted file mode 100644 --- a/dom/xbl/nsXBLProtoImpl.cpp +++ /dev/null @@ -1,503 +0,0 @@ -/* -*- 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/DebugOnly.h" - -#include "nsXBLProtoImpl.h" -#include "nsIContent.h" -#include "mozilla/dom/Document.h" -#include "nsContentUtils.h" -#include "nsIXPConnect.h" -#include "nsIServiceManager.h" -#include "nsXBLPrototypeBinding.h" -#include "nsXBLProtoImplProperty.h" -#include "nsIURI.h" -#include "mozilla/dom/ScriptSettings.h" -#include "mozilla/dom/XULElementBinding.h" -#include "xpcpublic.h" -#include "js/CharacterEncoding.h" - -using namespace mozilla; -using namespace mozilla::dom; -using js::AssertSameCompartment; - -nsresult nsXBLProtoImpl::InstallImplementation( - nsXBLPrototypeBinding* aPrototypeBinding, nsXBLBinding* aBinding) { - // This function is called to install a concrete implementation on a bound - // element using this prototype implementation as a guide. The prototype - // implementation is compiled lazily, so for the first bound element that - // needs a concrete implementation, we also build the prototype - // implementation. - if (!mMembers && - !mFields) // Constructor and destructor also live in mMembers - return NS_OK; // Nothing to do, so let's not waste time. - - // If the way this gets the script context changes, fix - // nsXBLProtoImplAnonymousMethod::Execute - Document* document = aBinding->GetBoundElement()->OwnerDoc(); - - // This sometimes gets called when we have no outer window and if we don't - // catch this, we get leaks during crashtests and reftests. - if (NS_WARN_IF(!document->GetWindow())) { - return NS_OK; - } - - // |propertyHolder| (below) can be an existing object, so in theory we might - // hit something that could end up running script. We never want that to - // happen here, so we use an AutoJSAPI instead of an AutoEntryScript. - dom::AutoJSAPI jsapi; - if (NS_WARN_IF(!jsapi.Init(document->GetScopeObject()))) { - return NS_OK; - } - JSContext* cx = jsapi.cx(); - - // InitTarget objects gives us back the JS object that represents the bound - // element and the class object in the bound document that represents the - // concrete version of this implementation. This function also has the side - // effect of building up the prototype implementation if it has not been built - // already. - JS::Rooted<JSObject*> targetClassObject(cx, nullptr); - bool targetObjectIsNew = false; - nsresult rv = - InitTargetObjects(aPrototypeBinding, aBinding->GetBoundElement(), - &targetClassObject, &targetObjectIsNew); - NS_ENSURE_SUCCESS(rv, rv); // kick out if we were unable to properly - // intialize our target objects - MOZ_ASSERT(targetClassObject); - - // If the prototype already existed, we don't need to install anything. return - // early. - if (!targetObjectIsNew) return NS_OK; - - // We want to define the canonical set of members in a safe place. If we're - // using a separate XBL scope, we want to define them there first (so that - // they'll be available for Xray lookups, among other things), and then copy - // the properties to the content-side prototype as needed. We don't need to - // bother about the field accessors here, since we don't use/support those - // for in-content bindings. - - // First, start by entering the realm of the XBL scope. This may or may - // not be the same realm as globalObject. - JS::Rooted<JSObject*> globalObject( - cx, JS::GetNonCCWObjectGlobal(targetClassObject)); - JS::Rooted<JSObject*> scopeObject(cx, - xpc::GetXBLScopeOrGlobal(cx, globalObject)); - NS_ENSURE_TRUE(scopeObject, NS_ERROR_OUT_OF_MEMORY); - MOZ_ASSERT(JS_IsGlobalObject(scopeObject)); - JSAutoRealm ar(cx, scopeObject); - - // Determine the appropriate property holder. - // - // Note: If |targetIsNew| is false, we'll early-return above. However, that - // only tells us if the content-side object is new, which may be the case even - // if we've already set up the binding on the XBL side. For example, if we - // apply a binding #foo to a <span> when we've already applied it to a <div>, - // we'll end up with a different content prototype, but we'll already have a - // property holder called |foo| in the XBL scope. Check for that to avoid - // wasteful and weird property holder duplication. - const nsString& className = aPrototypeBinding->ClassName(); - const char16_t* classNameChars = className.get(); - const size_t classNameLen = className.Length(); - - JS::Rooted<JSObject*> propertyHolder(cx); - JS::Rooted<JS::PropertyDescriptor> existingHolder(cx); - if (scopeObject != globalObject && - !JS_GetOwnUCPropertyDescriptor(cx, scopeObject, classNameChars, - classNameLen, &existingHolder)) { - return NS_ERROR_FAILURE; - } - bool propertyHolderIsNew = - !existingHolder.object() || !existingHolder.value().isObject(); - - if (!propertyHolderIsNew) { - propertyHolder = &existingHolder.value().toObject(); - } else if (scopeObject != globalObject) { - // This is just a property holder, so it doesn't need any special JSClass. - propertyHolder = JS_NewObjectWithGivenProto(cx, nullptr, nullptr); - NS_ENSURE_TRUE(propertyHolder, NS_ERROR_OUT_OF_MEMORY); - - // Define it as a property on the scopeObject, using the same name used on - // the content side. - bool ok = - JS_DefineUCProperty(cx, scopeObject, classNameChars, classNameLen, - propertyHolder, JSPROP_PERMANENT | JSPROP_READONLY); - NS_ENSURE_TRUE(ok, NS_ERROR_UNEXPECTED); - } else { - propertyHolder = targetClassObject; - } - - // Walk our member list and install each one in turn on the XBL scope object. - if (propertyHolderIsNew) { - for (nsXBLProtoImplMember* curr = mMembers; curr; curr = curr->GetNext()) - curr->InstallMember(cx, propertyHolder); - } - - // Now, if we're using a separate XBL scope, enter the compartment of the - // bound node and copy exposable properties to the prototype there. This - // rewraps them appropriately, which should result in cross-compartment - // function wrappers. - if (propertyHolder != targetClassObject) { - AssertSameCompartment(propertyHolder, scopeObject); - AssertSameCompartment(targetClassObject, globalObject); - bool inContentXBLScope = xpc::IsInContentXBLScope(scopeObject); - for (nsXBLProtoImplMember* curr = mMembers; curr; curr = curr->GetNext()) { - if (!inContentXBLScope || curr->ShouldExposeToUntrustedContent()) { - JS::Rooted<jsid> id(cx); - JS::TwoByteChars chars(curr->GetName(), NS_strlen(curr->GetName())); - bool ok = JS_CharsToId(cx, chars, &id); - NS_ENSURE_TRUE(ok, NS_ERROR_UNEXPECTED); - - bool found; - ok = JS_HasPropertyById(cx, propertyHolder, id, &found); - NS_ENSURE_TRUE(ok, NS_ERROR_UNEXPECTED); - if (!found) { - // Some members don't install anything in InstallMember (e.g., - // nsXBLProtoImplAnonymousMethod). We need to skip copying in - // those cases. - continue; - } - - ok = JS_CopyPropertyFrom(cx, id, targetClassObject, propertyHolder); - NS_ENSURE_TRUE(ok, NS_ERROR_UNEXPECTED); - } - } - } - - // From here on out, work in the scope of the bound element. - JSAutoRealm ar2(cx, targetClassObject); - - // Install all of our field accessors. - for (nsXBLProtoImplField* curr = mFields; curr; curr = curr->GetNext()) - curr->InstallAccessors(cx, targetClassObject); - - return NS_OK; -} - -nsresult nsXBLProtoImpl::InitTargetObjects( - nsXBLPrototypeBinding* aBinding, nsIContent* aBoundElement, - JS::MutableHandle<JSObject*> aTargetClassObject, bool* aTargetIsNew) { - nsresult rv = NS_OK; - - if (!mPrecompiledMemberHolder) { - rv = CompilePrototypeMembers( - aBinding); // This is the first time we've ever installed this binding - // on an element. We need to go ahead and compile all - // methods and properties on a class in our prototype - // binding. - if (NS_FAILED(rv)) return rv; - - MOZ_ASSERT(mPrecompiledMemberHolder); - } - - Document* ownerDoc = aBoundElement->OwnerDoc(); - nsIGlobalObject* sgo; - - if (!(sgo = ownerDoc->GetScopeObject())) { - return NS_ERROR_UNEXPECTED; - } - - // Because our prototype implementation has a class, we need to build up a - // corresponding class for the concrete implementation in the bound document. - AutoJSContext cx; - JS::Rooted<JSObject*> global(cx, sgo->GetGlobalJSObject()); - JS::Rooted<JS::Value> v(cx); - - JSAutoRealm ar(cx, global); - // Make sure the interface object is created before the prototype object - // so that XULElement is hidden from content. See bug 909340. - bool defineOnGlobal = dom::XULElement_Binding::ConstructorEnabled(cx, global); - dom::XULElement_Binding::GetConstructorObjectHandle(cx, defineOnGlobal); - - rv = nsContentUtils::WrapNative(cx, aBoundElement, &v, - /* aAllowWrapping = */ false); - NS_ENSURE_SUCCESS(rv, rv); - - JS::Rooted<JSObject*> value(cx, &v.toObject()); - - // We passed aAllowWrapping = false to nsContentUtils::WrapNative so we - // should not have a wrapper. - MOZ_ASSERT(!js::IsWrapper(value)); - - JSAutoRealm ar2(cx, value); - - // All of the above code was just obtaining the bound element's script object - // and its immediate concrete base class. We need to alter the object so that - // our concrete class is interposed between the object and its base class. We - // become the new base class of the object, and the object's old base class - // becomes the new class' base class. - rv = aBinding->InitClass(mClassName, cx, value, aTargetClassObject, - aTargetIsNew); - if (NS_FAILED(rv)) { - return rv; - } - - aBoundElement->PreserveWrapper(aBoundElement); - - return rv; -} - -nsresult nsXBLProtoImpl::CompilePrototypeMembers( - nsXBLPrototypeBinding* aBinding) { - // We want to pre-compile our implementation's members against a "prototype - // context". Then when we actually bind the prototype to a real xbl instance, - // we'll clone the pre-compiled JS into the real instance's context. - AutoJSAPI jsapi; - if (NS_WARN_IF(!jsapi.Init(xpc::CompilationScope()))) return NS_ERROR_FAILURE; - JSContext* cx = jsapi.cx(); - - mPrecompiledMemberHolder = JS_NewObjectWithGivenProto(cx, nullptr, nullptr); - if (!mPrecompiledMemberHolder) return NS_ERROR_OUT_OF_MEMORY; - - // Now that we have a class object installed, we walk our member list and - // compile each of our properties and methods in turn. - JS::Rooted<JSObject*> rootedHolder(cx, mPrecompiledMemberHolder); - for (nsXBLProtoImplMember* curr = mMembers; curr; curr = curr->GetNext()) { - nsresult rv = curr->CompileMember(jsapi, mClassName, rootedHolder); - if (NS_FAILED(rv)) { - DestroyMembers(); - return rv; - } - } - - return NS_OK; -} - -bool nsXBLProtoImpl::LookupMember( - JSContext* aCx, nsString& aName, JS::Handle<jsid> aNameAsId, - JS::MutableHandle<JS::PropertyDescriptor> aDesc, - JS::Handle<JSObject*> aClassObject) { - for (nsXBLProtoImplMember* m = mMembers; m; m = m->GetNext()) { - if (aName.Equals(m->GetName())) { - return JS_GetPropertyDescriptorById(aCx, aClassObject, aNameAsId, aDesc); - } - } - return true; -} - -void nsXBLProtoImpl::Trace(const TraceCallbacks& aCallbacks, void* aClosure) { - // If we don't have a class object then we either didn't compile members - // or we only have fields, in both cases there are no cycles through our - // members. - if (!mPrecompiledMemberHolder) { - return; - } - - nsXBLProtoImplMember* member; - for (member = mMembers; member; member = member->GetNext()) { - member->Trace(aCallbacks, aClosure); - } -} - -void nsXBLProtoImpl::UnlinkJSObjects() { - if (mPrecompiledMemberHolder) { - DestroyMembers(); - } -} - -nsXBLProtoImplField* nsXBLProtoImpl::FindField( - const nsString& aFieldName) const { - for (nsXBLProtoImplField* f = mFields; f; f = f->GetNext()) { - if (aFieldName.Equals(f->GetName())) { - return f; - } - } - - return nullptr; -} - -bool nsXBLProtoImpl::ResolveAllFields(JSContext* cx, - JS::Handle<JSObject*> obj) const { - for (nsXBLProtoImplField* f = mFields; f; f = f->GetNext()) { - nsDependentString name(f->GetName()); - bool dummy; - if (!::JS_HasUCProperty(cx, obj, name.get(), name.Length(), &dummy)) { - return false; - } - } - - return true; -} - -void nsXBLProtoImpl::UndefineFields(JSContext* cx, - JS::Handle<JSObject*> obj) const { - for (nsXBLProtoImplField* f = mFields; f; f = f->GetNext()) { - nsDependentString name(f->GetName()); - - const char16_t* s = name.get(); - bool hasProp; - if (::JS_AlreadyHasOwnUCProperty(cx, obj, s, name.Length(), &hasProp) && - hasProp) { - JS::ObjectOpResult ignored; - ::JS_DeleteUCProperty(cx, obj, s, name.Length(), ignored); - } - } -} - -void nsXBLProtoImpl::DestroyMembers() { - MOZ_ASSERT(mPrecompiledMemberHolder); - - delete mMembers; - mMembers = nullptr; - mConstructor = nullptr; - mDestructor = nullptr; -} - -nsresult nsXBLProtoImpl::Read(nsIObjectInputStream* aStream, - nsXBLPrototypeBinding* aBinding) { - AssertInCompilationScope(); - AutoJSContext cx; - // Set up a class object first so that deserialization is possible - mPrecompiledMemberHolder = JS_NewObjectWithGivenProto(cx, nullptr, nullptr); - if (!mPrecompiledMemberHolder) return NS_ERROR_OUT_OF_MEMORY; - - nsXBLProtoImplField* previousField = nullptr; - nsXBLProtoImplMember* previousMember = nullptr; - - do { - XBLBindingSerializeDetails type; - nsresult rv = aStream->Read8(&type); - NS_ENSURE_SUCCESS(rv, rv); - if (type == XBLBinding_Serialize_NoMoreItems) break; - - switch (type & XBLBinding_Serialize_Mask) { - case XBLBinding_Serialize_Field: { - nsXBLProtoImplField* field = - new nsXBLProtoImplField(type & XBLBinding_Serialize_ReadOnly); - rv = field->Read(aStream); - if (NS_FAILED(rv)) { - delete field; - return rv; - } - - if (previousField) { - previousField->SetNext(field); - } else { - mFields = field; - } - previousField = field; - - break; - } - case XBLBinding_Serialize_GetterProperty: - case XBLBinding_Serialize_SetterProperty: - case XBLBinding_Serialize_GetterSetterProperty: { - nsAutoString name; - nsresult rv = aStream->ReadString(name); - NS_ENSURE_SUCCESS(rv, rv); - - nsXBLProtoImplProperty* prop = new nsXBLProtoImplProperty( - name.get(), type & XBLBinding_Serialize_ReadOnly); - rv = prop->Read(aStream, type & XBLBinding_Serialize_Mask); - if (NS_FAILED(rv)) { - delete prop; - return rv; - } - - previousMember = AddMember(prop, previousMember); - break; - } - case XBLBinding_Serialize_Method: { - nsAutoString name; - rv = aStream->ReadString(name); - NS_ENSURE_SUCCESS(rv, rv); - - nsXBLProtoImplMethod* method = new nsXBLProtoImplMethod(name.get()); - rv = method->Read(aStream); - if (NS_FAILED(rv)) { - delete method; - return rv; - } - - previousMember = AddMember(method, previousMember); - break; - } - case XBLBinding_Serialize_Constructor: { - nsAutoString name; - rv = aStream->ReadString(name); - NS_ENSURE_SUCCESS(rv, rv); - - mConstructor = new nsXBLProtoImplAnonymousMethod(name.get()); - rv = mConstructor->Read(aStream); - if (NS_FAILED(rv)) { - delete mConstructor; - mConstructor = nullptr; - return rv; - } - - previousMember = AddMember(mConstructor, previousMember); - break; - } - case XBLBinding_Serialize_Destructor: { - nsAutoString name; - rv = aStream->ReadString(name); - NS_ENSURE_SUCCESS(rv, rv); - - mDestructor = new nsXBLProtoImplAnonymousMethod(name.get()); - rv = mDestructor->Read(aStream); - if (NS_FAILED(rv)) { - delete mDestructor; - mDestructor = nullptr; - return rv; - } - - previousMember = AddMember(mDestructor, previousMember); - break; - } - default: - NS_ERROR("Unexpected binding member type"); - break; - } - } while (1); - - return NS_OK; -} - -nsresult nsXBLProtoImpl::Write(nsIObjectOutputStream* aStream, - nsXBLPrototypeBinding* aBinding) { - nsresult rv; - - if (!mPrecompiledMemberHolder) { - rv = CompilePrototypeMembers(aBinding); - NS_ENSURE_SUCCESS(rv, rv); - } - - rv = aStream->WriteUtf8Z(mClassName.get()); - NS_ENSURE_SUCCESS(rv, rv); - - for (nsXBLProtoImplField* curr = mFields; curr; curr = curr->GetNext()) { - rv = curr->Write(aStream); - NS_ENSURE_SUCCESS(rv, rv); - } - for (nsXBLProtoImplMember* curr = mMembers; curr; curr = curr->GetNext()) { - if (curr == mConstructor) { - rv = mConstructor->Write(aStream, XBLBinding_Serialize_Constructor); - } else if (curr == mDestructor) { - rv = mDestructor->Write(aStream, XBLBinding_Serialize_Destructor); - } else { - rv = curr->Write(aStream); - } - NS_ENSURE_SUCCESS(rv, rv); - } - - return aStream->Write8(XBLBinding_Serialize_NoMoreItems); -} - -void NS_NewXBLProtoImpl(nsXBLPrototypeBinding* aBinding, - const char16_t* aClassName, nsXBLProtoImpl** aResult) { - nsXBLProtoImpl* impl = new nsXBLProtoImpl(); - if (aClassName) { - impl->mClassName = aClassName; - } else { - nsCString spec; - nsresult rv = aBinding->BindingURI()->GetSpec(spec); - // XXX: should handle this better - MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv)); - impl->mClassName = NS_ConvertUTF8toUTF16(spec); - } - - aBinding->SetImplementation(impl); - *aResult = impl; -}
deleted file mode 100644 --- a/dom/xbl/nsXBLProtoImpl.h +++ /dev/null @@ -1,117 +0,0 @@ -/* -*- 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 nsXBLProtoImpl_h__ -#define nsXBLProtoImpl_h__ - -#include "nsMemory.h" -#include "nsXBLPrototypeHandler.h" -#include "nsXBLProtoImplMember.h" -#include "nsXBLProtoImplField.h" -#include "nsXBLBinding.h" - -class nsXBLPrototypeBinding; -class nsXBLProtoImplAnonymousMethod; - -class nsXBLProtoImpl final { - public: - nsXBLProtoImpl() - : mPrecompiledMemberHolder(nullptr), - mMembers(nullptr), - mFields(nullptr), - mConstructor(nullptr), - mDestructor(nullptr) { - MOZ_COUNT_CTOR(nsXBLProtoImpl); - } - ~nsXBLProtoImpl() { - MOZ_COUNT_DTOR(nsXBLProtoImpl); - // Note: the constructor and destructor are in mMembers, so we'll - // clean them up automatically. - delete mMembers; - delete mFields; - } - - nsresult InstallImplementation(nsXBLPrototypeBinding* aPrototypeBinding, - nsXBLBinding* aBinding); - - private: - nsresult InitTargetObjects(nsXBLPrototypeBinding* aBinding, - nsIContent* aBoundElement, - JS::MutableHandle<JSObject*> aTargetClassObject, - bool* aTargetIsNew); - - public: - nsresult CompilePrototypeMembers(nsXBLPrototypeBinding* aBinding); - - bool LookupMember(JSContext* aCx, nsString& aName, JS::Handle<jsid> aNameAsId, - JS::MutableHandle<JS::PropertyDescriptor> aDesc, - JS::Handle<JSObject*> aClassObject); - - void SetMemberList(nsXBLProtoImplMember* aMemberList) { - delete mMembers; - mMembers = aMemberList; - } - - void SetFieldList(nsXBLProtoImplField* aFieldList) { - delete mFields; - mFields = aFieldList; - } - - void Trace(const TraceCallbacks& aCallbacks, void* aClosure); - void UnlinkJSObjects(); - - nsXBLProtoImplField* FindField(const nsString& aFieldName) const; - - // Resolve all the fields for this implementation on the object |obj| False - // return means a JS exception was set. - bool ResolveAllFields(JSContext* cx, JS::Handle<JSObject*> obj) const; - - // Undefine all our fields from object |obj| (which should be a - // JSObject for a bound element). - void UndefineFields(JSContext* cx, JS::Handle<JSObject*> obj) const; - - bool CompiledMembers() const { return mPrecompiledMemberHolder != nullptr; } - - nsresult Read(nsIObjectInputStream* aStream, nsXBLPrototypeBinding* aBinding); - nsresult Write(nsIObjectOutputStream* aStream, - nsXBLPrototypeBinding* aBinding); - - protected: - // used by Read to add each member - nsXBLProtoImplMember* AddMember(nsXBLProtoImplMember* aMember, - nsXBLProtoImplMember* aPreviousMember) { - if (aPreviousMember) - aPreviousMember->SetNext(aMember); - else - mMembers = aMember; - return aMember; - } - - void DestroyMembers(); - - public: - nsString mClassName; // The name of the class. - - protected: - JSObject* - mPrecompiledMemberHolder; // The class object for the binding. We'll use - // this to pre-compile properties and methods - // for the binding. - - nsXBLProtoImplMember* mMembers; // The members of an implementation are - // chained in this singly-linked list. - - nsXBLProtoImplField* mFields; // Our fields - - public: - nsXBLProtoImplAnonymousMethod* mConstructor; // Our class constructor. - nsXBLProtoImplAnonymousMethod* mDestructor; // Our class destructor. -}; - -void NS_NewXBLProtoImpl(nsXBLPrototypeBinding* aBinding, - const char16_t* aClassName, nsXBLProtoImpl** aResult); - -#endif // nsXBLProtoImpl_h__
deleted file mode 100644 --- a/dom/xbl/nsXBLProtoImplField.cpp +++ /dev/null @@ -1,484 +0,0 @@ -/* -*- 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 "nsAtom.h" -#include "nsIContent.h" -#include "nsString.h" -#include "nsJSUtils.h" -#include "jsapi.h" -#include "js/CharacterEncoding.h" -#include "nsUnicharUtils.h" -#include "nsReadableUtils.h" -#include "nsXBLProtoImplField.h" -#include "nsIScriptContext.h" -#include "nsIURI.h" -#include "nsXBLSerialize.h" -#include "nsXBLPrototypeBinding.h" -#include "mozilla/CycleCollectedJSContext.h" -#include "mozilla/dom/BindingUtils.h" -#include "mozilla/dom/ElementBinding.h" -#include "mozilla/dom/ScriptSettings.h" -#include "nsGlobalWindow.h" -#include "xpcpublic.h" -#include "WrapperFactory.h" - -using namespace mozilla; -using namespace mozilla::dom; - -nsXBLProtoImplField::nsXBLProtoImplField(const char16_t* aName, - const char16_t* aReadOnly) - : mNext(nullptr), mFieldText(nullptr), mFieldTextLength(0), mLineNumber(0) { - MOZ_COUNT_CTOR(nsXBLProtoImplField); - mName = NS_xstrdup(aName); // XXXbz make more sense to use a stringbuffer? - - mJSAttributes = JSPROP_ENUMERATE; - if (aReadOnly) { - nsAutoString readOnly; - readOnly.Assign(aReadOnly); - if (readOnly.LowerCaseEqualsLiteral("true")) - mJSAttributes |= JSPROP_READONLY; - } -} - -nsXBLProtoImplField::nsXBLProtoImplField(const bool aIsReadOnly) - : mNext(nullptr), - mName(nullptr), - mFieldText(nullptr), - mFieldTextLength(0), - mLineNumber(0) { - MOZ_COUNT_CTOR(nsXBLProtoImplField); - - mJSAttributes = JSPROP_ENUMERATE; - if (aIsReadOnly) mJSAttributes |= JSPROP_READONLY; -} - -nsXBLProtoImplField::~nsXBLProtoImplField() { - MOZ_COUNT_DTOR(nsXBLProtoImplField); - if (mFieldText) free(mFieldText); - free(mName); - NS_CONTENT_DELETE_LIST_MEMBER(nsXBLProtoImplField, this, mNext); -} - -void nsXBLProtoImplField::AppendFieldText(const nsAString& aText) { - if (mFieldText) { - nsDependentString fieldTextStr(mFieldText, mFieldTextLength); - nsAutoString newFieldText = fieldTextStr + aText; - char16_t* temp = mFieldText; - mFieldText = ToNewUnicode(newFieldText); - mFieldTextLength = newFieldText.Length(); - free(temp); - } else { - mFieldText = ToNewUnicode(aText); - mFieldTextLength = aText.Length(); - } -} - -// XBL fields are represented on elements inheriting that field a bit trickily. -// When setting up the XBL prototype object, we install accessors for the fields -// on the prototype object. Those accessors, when used, will then (via -// InstallXBLField below) reify a property for the field onto the actual -// XBL-backed element. -// -// The accessor property is a plain old property backed by a getter function and -// a setter function. These properties are backed by the FieldGetter and -// FieldSetter natives; they're created by InstallAccessors. The precise field -// to be reified is identified using two extra slots on the getter/setter -// functions. XBLPROTO_SLOT stores the XBL prototype object that provides the -// field. FIELD_SLOT stores the name of the field, i.e. its JavaScript property -// name. -// -// This two-step field installation process -- creating an accessor on the -// prototype, then have that reify an own property on the actual element -- is -// admittedly convoluted. Better would be for XBL-backed elements to be proxies -// that could resolve fields onto themselves. But given that XBL bindings are -// associated with elements mutably -- you can add/remove/change -moz-binding -// whenever you want, alas -- doing so would require all elements to be proxies, -// which isn't performant now. So we do this two-step instead. -static const uint32_t XBLPROTO_SLOT = 0; -static const uint32_t FIELD_SLOT = 1; - -bool ValueHasISupportsPrivate(JS::Handle<JS::Value> v) { - if (!v.isObject()) { - return false; - } - - const DOMJSClass* domClass = GetDOMClass(&v.toObject()); - if (domClass) { - return domClass->mDOMObjectIsISupports; - } - - const JSClass* clasp = ::JS_GetClass(&v.toObject()); - const uint32_t HAS_PRIVATE_NSISUPPORTS = - JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS; - return (clasp->flags & HAS_PRIVATE_NSISUPPORTS) == HAS_PRIVATE_NSISUPPORTS; -} - -#ifdef DEBUG -static bool ValueHasISupportsPrivate(JSContext* cx, const JS::Value& aVal) { - JS::Rooted<JS::Value> v(cx, aVal); - return ValueHasISupportsPrivate(v); -} -#endif - -// Define a shadowing property on |this| for the XBL field defined by the -// contents of the callee's reserved slots. If the property was defined, -// *installed will be true, and idp will be set to the property name that was -// defined. -static bool InstallXBLField(JSContext* cx, JS::Handle<JSObject*> callee, - JS::Handle<JSObject*> thisObj, - JS::MutableHandle<jsid> idp, bool* installed) { - *installed = false; - - // First ensure |this| is a reasonable XBL bound node. - // - // FieldAccessorGuard already determined whether |thisObj| was acceptable as - // |this| in terms of not throwing a TypeError. Assert this for good measure. - MOZ_ASSERT(ValueHasISupportsPrivate(cx, JS::ObjectValue(*thisObj))); - - // But there are some cases where we must accept |thisObj| but not install a - // property on it, or otherwise touch it. Hence this split of |this|-vetting - // duties. - // - // OK to use ReflectorToISupportsStatic, because we only care about nodes - // here. - nsCOMPtr<nsISupports> native = xpc::ReflectorToISupportsStatic(thisObj); - if (!native) { - // Looks like whatever |thisObj| is it's not our nsIContent. It might well - // be the proto our binding installed, however, where the private is the - // nsXBLDocumentInfo, so just baul out quietly. Do NOT throw an exception - // here. - // - // We could make this stricter by checking the class maybe, but whatever. - return true; - } - - nsCOMPtr<nsIContent> xblNode = do_QueryInterface(native); - if (!xblNode) { - xpc::Throw(cx, NS_ERROR_UNEXPECTED); - return false; - } - - // Now that |this| is okay, actually install the field. - - // Because of the possibility (due to XBL binding inheritance, because each - // XBL binding lives in its own global object) that |this| might be in a - // different realm from the callee (not to mention that this method can - // be called with an arbitrary |this| regardless of how insane XBL is), and - // because in this method we've entered |this|'s realm (see in - // Field[GS]etter where we attempt a cross-realm call), we must enter - // the callee's realm to access its reserved slots. - nsXBLPrototypeBinding* protoBinding; - nsAutoJSString fieldName; - { - JSAutoRealm ar(cx, callee); - - JS::Rooted<JSObject*> xblProto(cx); - xblProto = &js::GetFunctionNativeReserved(callee, XBLPROTO_SLOT).toObject(); - - JS::Rooted<JS::Value> name( - cx, js::GetFunctionNativeReserved(callee, FIELD_SLOT)); - if (!fieldName.init(cx, name.toString())) { - return false; - } - - MOZ_ALWAYS_TRUE(JS_ValueToId(cx, name, idp)); - - // If a separate XBL scope is being used, the callee is not same-realm - // with the xbl prototype, and the object is a cross-compartment wrapper. - xblProto = js::UncheckedUnwrap(xblProto); - JSAutoRealm ar2(cx, xblProto); - JS::Value slotVal = ::JS_GetReservedSlot(xblProto, 0); - protoBinding = static_cast<nsXBLPrototypeBinding*>(slotVal.toPrivate()); - MOZ_ASSERT(protoBinding); - } - - nsXBLProtoImplField* field = protoBinding->FindField(fieldName); - MOZ_ASSERT(field); - - nsresult rv = field->InstallField(thisObj, *protoBinding, installed); - if (NS_SUCCEEDED(rv)) { - return true; - } - - if (!::JS_IsExceptionPending(cx)) { - xpc::Throw(cx, rv); - } - return false; -} - -bool FieldGetterImpl(JSContext* cx, const JS::CallArgs& args) { - JS::Handle<JS::Value> thisv = args.thisv(); - MOZ_ASSERT(ValueHasISupportsPrivate(thisv)); - - JS::Rooted<JSObject*> thisObj(cx, &thisv.toObject()); - - // We should be in the realm of |this|. If we got here via nativeCall, - // |this| is not same-compartment with |callee|, and it's possible via - // asymmetric security semantics that |args.calleev()| is actually a security - // wrapper. In this case, we know we want to do an unsafe unwrap, and - // InstallXBLField knows how to handle cross-compartment pointers. - bool installed = false; - JS::Rooted<JSObject*> callee(cx, - js::UncheckedUnwrap(&args.calleev().toObject())); - JS::Rooted<jsid> id(cx); - if (!InstallXBLField(cx, callee, thisObj, &id, &installed)) { - return false; - } - - if (!installed) { - args.rval().setUndefined(); - return true; - } - - return JS_GetPropertyById(cx, thisObj, id, args.rval()); -} - -static bool FieldGetter(JSContext* cx, unsigned argc, JS::Value* vp) { - JS::CallArgs args = JS::CallArgsFromVp(argc, vp); - return JS::CallNonGenericMethod<ValueHasISupportsPrivate, FieldGetterImpl>( - cx, args); -} - -bool FieldSetterImpl(JSContext* cx, const JS::CallArgs& args) { - JS::Handle<JS::Value> thisv = args.thisv(); - MOZ_ASSERT(ValueHasISupportsPrivate(thisv)); - - JS::Rooted<JSObject*> thisObj(cx, &thisv.toObject()); - - // We should be in the realm of |this|. If we got here via nativeCall, - // |this| is not same-compartment with |callee|, and it's possible via - // asymmetric security semantics that |args.calleev()| is actually a security - // wrapper. In this case, we know we want to do an unsafe unwrap, and - // InstallXBLField knows how to handle cross-compartment pointers. - bool installed = false; - JS::Rooted<JSObject*> callee(cx, - js::UncheckedUnwrap(&args.calleev().toObject())); - JS::Rooted<jsid> id(cx); - if (!InstallXBLField(cx, callee, thisObj, &id, &installed)) { - return false; - } - - if (installed) { - if (!::JS_SetPropertyById(cx, thisObj, id, args.get(0))) { - return false; - } - } - args.rval().setUndefined(); - return true; -} - -static bool FieldSetter(JSContext* cx, unsigned argc, JS::Value* vp) { - JS::CallArgs args = JS::CallArgsFromVp(argc, vp); - return JS::CallNonGenericMethod<ValueHasISupportsPrivate, FieldSetterImpl>( - cx, args); -} - -nsresult nsXBLProtoImplField::InstallAccessors( - JSContext* aCx, JS::Handle<JSObject*> aTargetClassObject) { - MOZ_ASSERT(js::IsObjectInContextCompartment(aTargetClassObject, aCx)); - JS::Rooted<JSObject*> globalObject( - aCx, JS::GetNonCCWObjectGlobal(aTargetClassObject)); - JS::Rooted<JSObject*> scopeObject( - aCx, xpc::GetXBLScopeOrGlobal(aCx, globalObject)); - NS_ENSURE_TRUE(scopeObject, NS_ERROR_OUT_OF_MEMORY); - - // Don't install it if the field is empty; see also InstallField which also - // must implement the not-empty requirement. - if (IsEmpty()) { - return NS_OK; - } - - // Install a getter/setter pair which will resolve the field onto the actual - // object, when invoked. - - // Get the field name as an id. - JS::Rooted<jsid> id(aCx); - JS::TwoByteChars chars(mName, NS_strlen(mName)); - if (!JS_CharsToId(aCx, chars, &id)) return NS_ERROR_OUT_OF_MEMORY; - - // Properties/Methods have historically taken precendence over fields. We - // install members first, so just bounce here if the property is already - // defined. - bool found = false; - if (!JS_AlreadyHasOwnPropertyById(aCx, aTargetClassObject, id, &found)) - return NS_ERROR_FAILURE; - if (found) return NS_OK; - - // FieldGetter and FieldSetter need to run in the XBL scope so that they can - // see through any SOWs on their targets. - - // First, enter the XBL scope, and compile the functions there. - JSAutoRealm ar(aCx, scopeObject); - JS::Rooted<JS::Value> wrappedClassObj(aCx, - JS::ObjectValue(*aTargetClassObject)); - if (!JS_WrapValue(aCx, &wrappedClassObj)) return NS_ERROR_OUT_OF_MEMORY; - - JS::Rooted<JSObject*> get( - aCx, JS_GetFunctionObject( - js::NewFunctionByIdWithReserved(aCx, FieldGetter, 0, 0, id))); - if (!get) { - return NS_ERROR_OUT_OF_MEMORY; - } - js::SetFunctionNativeReserved(get, XBLPROTO_SLOT, wrappedClassObj); - js::SetFunctionNativeReserved(get, FIELD_SLOT, - JS::StringValue(JSID_TO_STRING(id))); - - JS::Rooted<JSObject*> set( - aCx, JS_GetFunctionObject( - js::NewFunctionByIdWithReserved(aCx, FieldSetter, 1, 0, id))); - if (!set) { - return NS_ERROR_OUT_OF_MEMORY; - } - js::SetFunctionNativeReserved(set, XBLPROTO_SLOT, wrappedClassObj); - js::SetFunctionNativeReserved(set, FIELD_SLOT, - JS::StringValue(JSID_TO_STRING(id))); - - // Now, re-enter the class object's scope, wrap the getters/setters, and - // define them there. - JSAutoRealm ar2(aCx, aTargetClassObject); - if (!JS_WrapObject(aCx, &get) || !JS_WrapObject(aCx, &set)) { - return NS_ERROR_OUT_OF_MEMORY; - } - - if (!::JS_DefinePropertyById(aCx, aTargetClassObject, id, get, set, - AccessorAttributes())) { - return NS_ERROR_OUT_OF_MEMORY; - } - - return NS_OK; -} - -nsresult nsXBLProtoImplField::InstallField( - JS::Handle<JSObject*> aBoundNode, - const nsXBLPrototypeBinding& aProtoBinding, bool* aDidInstall) const { - MOZ_ASSERT(aBoundNode, - "uh-oh, bound node should NOT be null or bad things will happen"); - - *aDidInstall = false; - - // Empty fields are treated as not actually present. - if (IsEmpty()) { - return NS_OK; - } - - nsAutoMicroTask mt; - - nsAutoCString uriSpec; - nsresult rv = aProtoBinding.DocURI()->GetSpec(uriSpec); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - nsIGlobalObject* globalObject = xpc::WindowGlobalOrNull(aBoundNode); - if (!globalObject) { - return NS_OK; - } - - // We are going to run script via EvaluateString, so we need a script entry - // point, but as this is XBL related it does not appear in the HTML spec. - // We need an actual JSContext to do GetXBLScopeOrGlobal, and it needs to - // be in the realm of globalObject. But we want our XBL execution scope - // to be our entry global. - AutoJSAPI jsapi; - if (!jsapi.Init(globalObject)) { - return NS_ERROR_UNEXPECTED; - } - MOZ_ASSERT(!::JS_IsExceptionPending(jsapi.cx()), - "Shouldn't get here when an exception is pending!"); - - // Note: the UNWRAP_OBJECT may mutate boundNode; don't use it after that call. - JS::Rooted<JSObject*> boundNode(jsapi.cx(), aBoundNode); - Element* boundElement = nullptr; - rv = UNWRAP_OBJECT(Element, &boundNode, boundElement); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - // First, enter the xbl scope, build the element's scope chain, and use - // that as the scope chain for the evaluation. - JS::Rooted<JSObject*> scopeObject( - jsapi.cx(), xpc::GetXBLScopeOrGlobal(jsapi.cx(), aBoundNode)); - NS_ENSURE_TRUE(scopeObject, NS_ERROR_OUT_OF_MEMORY); - - AutoEntryScript aes(scopeObject, "XBL <field> initialization", true); - JSContext* cx = aes.cx(); - - JS::Rooted<JS::Value> result(cx); - JS::CompileOptions options(cx); - options.setFileAndLine(uriSpec.get(), mLineNumber); - JS::RootedVector<JSObject*> scopeChain(cx); - if (!nsJSUtils::GetScopeChainForXBL(cx, boundElement, aProtoBinding, - &scopeChain)) { - return NS_ERROR_OUT_OF_MEMORY; - } - rv = NS_OK; - { - nsJSUtils::ExecutionContext exec(cx, scopeObject); - exec.SetScopeChain(scopeChain); - exec.Compile(options, nsDependentString(mFieldText, mFieldTextLength)); - rv = exec.ExecScript(&result); - } - - if (NS_FAILED(rv)) { - return rv; - } - - if (rv == NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW) { - // Report the exception now, before we try using the JSContext for - // the JS_DefineUCProperty call. Note that this reports in our current - // realm, which is the XBL scope. - aes.ReportException(); - } - - // Now, enter the node's realm, wrap the eval result, and define it on - // the bound node. - JSAutoRealm ar2(cx, aBoundNode); - nsDependentString name(mName); - if (!JS_WrapValue(cx, &result) || - !::JS_DefineUCProperty(cx, aBoundNode, - reinterpret_cast<const char16_t*>(mName), - name.Length(), result, mJSAttributes)) { - return NS_ERROR_OUT_OF_MEMORY; - } - - *aDidInstall = true; - return NS_OK; -} - -nsresult nsXBLProtoImplField::Read(nsIObjectInputStream* aStream) { - nsAutoString name; - nsresult rv = aStream->ReadString(name); - NS_ENSURE_SUCCESS(rv, rv); - mName = ToNewUnicode(name); - - rv = aStream->Read32(&mLineNumber); - NS_ENSURE_SUCCESS(rv, rv); - - nsAutoString fieldText; - rv = aStream->ReadString(fieldText); - NS_ENSURE_SUCCESS(rv, rv); - mFieldTextLength = fieldText.Length(); - if (mFieldTextLength) mFieldText = ToNewUnicode(fieldText); - - return NS_OK; -} - -nsresult nsXBLProtoImplField::Write(nsIObjectOutputStream* aStream) { - XBLBindingSerializeDetails type = XBLBinding_Serialize_Field; - - if (mJSAttributes & JSPROP_READONLY) { - type |= XBLBinding_Serialize_ReadOnly; - } - - nsresult rv = aStream->Write8(type); - NS_ENSURE_SUCCESS(rv, rv); - rv = aStream->WriteWStringZ(mName); - NS_ENSURE_SUCCESS(rv, rv); - rv = aStream->Write32(mLineNumber); - NS_ENSURE_SUCCESS(rv, rv); - - return aStream->WriteWStringZ(mFieldText ? mFieldText : u""); -}
deleted file mode 100644 --- a/dom/xbl/nsXBLProtoImplField.h +++ /dev/null @@ -1,60 +0,0 @@ -/* -*- 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 nsXBLProtoImplField_h__ -#define nsXBLProtoImplField_h__ - -#include "nsAtom.h" -#include "nsString.h" -#include "jsapi.h" -#include "nsString.h" -#include "nsXBLProtoImplMember.h" - -class nsIObjectInputStream; -class nsIObjectOutputStream; -class nsIURI; - -class nsXBLProtoImplField { - public: - nsXBLProtoImplField(const char16_t* aName, const char16_t* aReadOnly); - explicit nsXBLProtoImplField(const bool aIsReadOnly); - ~nsXBLProtoImplField(); - - void AppendFieldText(const nsAString& aText); - void SetLineNumber(uint32_t aLineNumber) { mLineNumber = aLineNumber; } - - nsXBLProtoImplField* GetNext() const { return mNext; } - void SetNext(nsXBLProtoImplField* aNext) { mNext = aNext; } - - nsresult InstallField(JS::Handle<JSObject*> aBoundNode, - const nsXBLPrototypeBinding& aProtoBinding, - bool* aDidInstall) const; - - nsresult InstallAccessors(JSContext* aCx, - JS::Handle<JSObject*> aTargetClassObject); - - nsresult Read(nsIObjectInputStream* aStream); - nsresult Write(nsIObjectOutputStream* aStream); - - const char16_t* GetName() const { return mName; } - - unsigned AccessorAttributes() const { - return JSPROP_GETTER | JSPROP_SETTER | - (mJSAttributes & (JSPROP_ENUMERATE | JSPROP_PERMANENT)); - } - - bool IsEmpty() const { return mFieldTextLength == 0; } - - protected: - nsXBLProtoImplField* mNext; - char16_t* mName; - char16_t* mFieldText; - uint32_t mFieldTextLength; - uint32_t mLineNumber; - unsigned mJSAttributes; -}; - -#endif // nsXBLProtoImplField_h__
deleted file mode 100644 --- a/dom/xbl/nsXBLProtoImplMember.h +++ /dev/null @@ -1,90 +0,0 @@ -/* -*- 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 nsXBLProtoImplMember_h__ -#define nsXBLProtoImplMember_h__ - -#include "nsAtom.h" -#include "nsString.h" -#include "nsString.h" -#include "nsIServiceManager.h" -#include "nsContentUtils.h" // For NS_CONTENT_DELETE_LIST_MEMBER. -#include "nsCycleCollectionParticipant.h" - -class nsIObjectOutputStream; - -struct nsXBLTextWithLineNumber { - char16_t* mText; - uint32_t mLineNumber; - - nsXBLTextWithLineNumber() : mText(nullptr), mLineNumber(0) { - MOZ_COUNT_CTOR(nsXBLTextWithLineNumber); - } - - ~nsXBLTextWithLineNumber() { - MOZ_COUNT_DTOR(nsXBLTextWithLineNumber); - if (mText) { - free(mText); - } - } - - void AppendText(const nsAString& aText) { - if (mText) { - char16_t* temp = mText; - mText = ToNewUnicode(nsDependentString(temp) + aText); - free(temp); - } else { - mText = ToNewUnicode(aText); - } - } - - char16_t* GetText() { return mText; } - - void SetLineNumber(uint32_t aLineNumber) { mLineNumber = aLineNumber; } - - uint32_t GetLineNumber() { return mLineNumber; } -}; - -class nsXBLProtoImplMember { - public: - explicit nsXBLProtoImplMember(const char16_t* aName) - : mNext(nullptr), mExposeToUntrustedContent(false) { - mName = ToNewUnicode(nsDependentString(aName)); - } - virtual ~nsXBLProtoImplMember() { - free(mName); - NS_CONTENT_DELETE_LIST_MEMBER(nsXBLProtoImplMember, this, mNext); - } - - nsXBLProtoImplMember* GetNext() { return mNext; } - void SetNext(nsXBLProtoImplMember* aNext) { mNext = aNext; } - bool ShouldExposeToUntrustedContent() { return mExposeToUntrustedContent; } - void SetExposeToUntrustedContent(bool aExpose) { - mExposeToUntrustedContent = aExpose; - } - const char16_t* GetName() { return mName; } - - virtual nsresult InstallMember(JSContext* aCx, - JS::Handle<JSObject*> aTargetClassObject) = 0; - virtual nsresult CompileMember(mozilla::dom::AutoJSAPI& jsapi, - const nsString& aClassStr, - JS::Handle<JSObject*> aClassObject) = 0; - - virtual void Trace(const TraceCallbacks& aCallbacks, void* aClosure) = 0; - - virtual nsresult Write(nsIObjectOutputStream* aStream) { return NS_OK; } - - protected: - nsXBLProtoImplMember* mNext; // The members of an implementation are chained. - char16_t* mName; // The name of the field, method, or property. - - bool mExposeToUntrustedContent; // If this binding is installed on an element - // in an untrusted scope, should this - // implementation member be accessible to the - // content? -}; - -#endif // nsXBLProtoImplMember_h__
deleted file mode 100644 --- a/dom/xbl/nsXBLProtoImplMethod.cpp +++ /dev/null @@ -1,328 +0,0 @@ -/* -*- 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 "nsAtom.h" -#include "nsString.h" -#include "jsapi.h" -#include "nsIContent.h" -#include "mozilla/dom/Document.h" -#include "nsIGlobalObject.h" -#include "nsUnicharUtils.h" -#include "nsReadableUtils.h" -#include "nsXBLProtoImplMethod.h" -#include "nsJSUtils.h" -#include "nsContentUtils.h" -#include "nsIScriptSecurityManager.h" -#include "nsIXPConnect.h" -#include "xpcpublic.h" -#include "nsXBLPrototypeBinding.h" -#include "mozilla/CycleCollectedJSContext.h" -#include "mozilla/dom/Element.h" -#include "mozilla/dom/ScriptSettings.h" - -using namespace mozilla; -using namespace mozilla::dom; - -nsXBLProtoImplMethod::nsXBLProtoImplMethod(const char16_t* aName) - : nsXBLProtoImplMember(aName), mMethod() { - MOZ_COUNT_CTOR(nsXBLProtoImplMethod); -} - -nsXBLProtoImplMethod::~nsXBLProtoImplMethod() { - MOZ_COUNT_DTOR(nsXBLProtoImplMethod); - - if (!IsCompiled()) { - delete GetUncompiledMethod(); - } -} - -void nsXBLProtoImplMethod::AppendBodyText(const nsAString& aText) { - MOZ_ASSERT(!IsCompiled(), - "Must not be compiled when accessing uncompiled method"); - - nsXBLUncompiledMethod* uncompiledMethod = GetUncompiledMethod(); - if (!uncompiledMethod) { - uncompiledMethod = new nsXBLUncompiledMethod(); - SetUncompiledMethod(uncompiledMethod); - } - - uncompiledMethod->AppendBodyText(aText); -} - -void nsXBLProtoImplMethod::AddParameter(const nsAString& aText) { - MOZ_ASSERT(!IsCompiled(), - "Must not be compiled when accessing uncompiled method"); - - if (aText.IsEmpty()) { - NS_WARNING("Empty name attribute in xbl:parameter!"); - return; - } - - nsXBLUncompiledMethod* uncompiledMethod = GetUncompiledMethod(); - if (!uncompiledMethod) { - uncompiledMethod = new nsXBLUncompiledMethod(); - SetUncompiledMethod(uncompiledMethod); - } - - uncompiledMethod->AddParameter(aText); -} - -void nsXBLProtoImplMethod::SetLineNumber(uint32_t aLineNumber) { - MOZ_ASSERT(!IsCompiled(), - "Must not be compiled when accessing uncompiled method"); - - nsXBLUncompiledMethod* uncompiledMethod = GetUncompiledMethod(); - if (!uncompiledMethod) { - uncompiledMethod = new nsXBLUncompiledMethod(); - SetUncompiledMethod(uncompiledMethod); - } - - uncompiledMethod->SetLineNumber(aLineNumber); -} - -nsresult nsXBLProtoImplMethod::InstallMember( - JSContext* aCx, JS::Handle<JSObject*> aTargetClassObject) { - MOZ_ASSERT(IsCompiled(), "Should not be installing an uncompiled method"); - MOZ_ASSERT(js::IsObjectInContextCompartment(aTargetClassObject, aCx)); - -#ifdef DEBUG - { - JS::Rooted<JSObject*> globalObject( - aCx, JS::GetNonCCWObjectGlobal(aTargetClassObject)); - MOZ_ASSERT(xpc::IsInContentXBLScope(globalObject) || - globalObject == xpc::GetXBLScope(aCx, globalObject)); - MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx) == globalObject); - } -#endif - - JS::Rooted<JSObject*> jsMethodObject(aCx, GetCompiledMethod()); - if (jsMethodObject) { - nsDependentString name(mName); - - JS::Rooted<JSObject*> method(aCx, - JS::CloneFunctionObject(aCx, jsMethodObject)); - NS_ENSURE_TRUE(method, NS_ERROR_OUT_OF_MEMORY); - - if (!::JS_DefineUCProperty(aCx, aTargetClassObject, - static_cast<const char16_t*>(mName), - name.Length(), method, JSPROP_ENUMERATE)) { - return NS_ERROR_OUT_OF_MEMORY; - } - } - return NS_OK; -} - -nsresult nsXBLProtoImplMethod::CompileMember( - AutoJSAPI& jsapi, const nsString& aClassStr, - JS::Handle<JSObject*> aClassObject) { - AssertInCompilationScope(); - MOZ_ASSERT(!IsCompiled(), "Trying to compile an already-compiled method"); - MOZ_ASSERT(aClassObject, "Must have class object to compile"); - - nsXBLUncompiledMethod* uncompiledMethod = GetUncompiledMethod(); - - // No parameters or body was supplied, so don't install method. - if (!uncompiledMethod) { - // Early return after which we consider ourselves compiled. - SetCompiledMethod(nullptr); - - return NS_OK; - } - - // Don't install method if no name was supplied. - if (!mName) { - delete uncompiledMethod; - - // Early return after which we consider ourselves compiled. - SetCompiledMethod(nullptr); - - return NS_OK; - } - - // We have a method. - // Allocate an array for our arguments. - int32_t paramCount = uncompiledMethod->GetParameterCount(); - char** args = nullptr; - if (paramCount > 0) { - args = new char*[paramCount]; - - // Add our parameters to our args array. - int32_t argPos = 0; - for (nsXBLParameter* curr = uncompiledMethod->mParameters; curr; - curr = curr->mNext) { - args[argPos] = curr->mName; - argPos++; - } - } - - // Get the body - nsDependentString body; - char16_t* bodyText = uncompiledMethod->mBodyText.GetText(); - if (bodyText) body.Rebind(bodyText); - - // Now that we have a body and args, compile the function - // and then define it. - NS_ConvertUTF16toUTF8 cname(mName); - NS_ConvertUTF16toUTF8 functionUri(aClassStr); - int32_t hash = functionUri.RFindChar('#'); - if (hash != kNotFound) { - functionUri.Truncate(hash); - } - - JSContext* cx = jsapi.cx(); - JSAutoRealm ar(cx, aClassObject); - JS::CompileOptions options(cx); - options.setFileAndLine(functionUri.get(), - uncompiledMethod->mBodyText.GetLineNumber()); - JS::Rooted<JSObject*> methodObject(cx); - JS::RootedVector<JSObject*> emptyVector(cx); - nsresult rv = nsJSUtils::CompileFunction( - jsapi, emptyVector, options, cname, paramCount, - const_cast<const char**>(args), body, methodObject.address()); - - // Destroy our uncompiled method and delete our arg list. - delete uncompiledMethod; - delete[] args; - if (NS_FAILED(rv)) { - SetUncompiledMethod(nullptr); - return rv; - } - - SetCompiledMethod(methodObject); - - return NS_OK; -} - -void nsXBLProtoImplMethod::Trace(const TraceCallbacks& aCallbacks, - void* aClosure) { - if (IsCompiled() && GetCompiledMethodPreserveColor()) { - aCallbacks.Trace(&mMethod.AsHeapObject(), "mMethod", aClosure); - } -} - -nsresult nsXBLProtoImplMethod::Read(nsIObjectInputStream* aStream) { - AssertInCompilationScope(); - MOZ_ASSERT(!IsCompiled() && !GetUncompiledMethod()); - - AutoJSContext cx; - JS::Rooted<JSObject*> methodObject(cx); - nsresult rv = XBL_DeserializeFunction(aStream, &methodObject); - if (NS_FAILED(rv)) { - SetUncompiledMethod(nullptr); - return rv; - } - - SetCompiledMethod(methodObject); - - return NS_OK; -} - -nsresult nsXBLProtoImplMethod::Write(nsIObjectOutputStream* aStream) { - AssertInCompilationScope(); - MOZ_ASSERT(IsCompiled()); - if (GetCompiledMethodPreserveColor()) { - nsresult rv = aStream->Write8(XBLBinding_Serialize_Method); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aStream->WriteWStringZ(mName); - NS_ENSURE_SUCCESS(rv, rv); - - JS::Rooted<JSObject*> method(RootingCx(), GetCompiledMethod()); - return XBL_SerializeFunction(aStream, method); - } - - return NS_OK; -} - -nsresult nsXBLProtoImplAnonymousMethod::Execute( - nsIContent* aBoundElement, const nsXBLPrototypeBinding& aProtoBinding) { - MOZ_ASSERT(aBoundElement->IsElement()); - MOZ_ASSERT(IsCompiled(), "Can't execute uncompiled method"); - - if (!GetCompiledMethod()) { - // Nothing to do here - return NS_OK; - } - - // Get the script context the same way - // nsXBLProtoImpl::InstallImplementation does. - Document* document = aBoundElement->OwnerDoc(); - - nsCOMPtr<nsIGlobalObject> global = - do_QueryInterface(document->GetInnerWindow()); - if (!global) { - return NS_OK; - } - - nsAutoMicroTask mt; - - // We are going to run script via JS::Call, so we need a script entry point, - // but as this is XBL related it does not appear in the HTML spec. - // We need an actual JSContext to do GetXBLScopeOrGlobal, and it needs to - // be in the compartment of globalObject. But we want our XBL execution scope - // to be our entry global. - AutoJSAPI jsapi; - if (!jsapi.Init(global)) { - return NS_ERROR_UNEXPECTED; - } - - JS::Rooted<JSObject*> scopeObject( - jsapi.cx(), - xpc::GetXBLScopeOrGlobal(jsapi.cx(), global->GetGlobalJSObject())); - NS_ENSURE_TRUE(scopeObject, NS_ERROR_OUT_OF_MEMORY); - - dom::AutoEntryScript aes(scopeObject, - "XBL <constructor>/<destructor> invocation", true); - JSContext* cx = aes.cx(); - JS::RootedVector<JSObject*> scopeChain(cx); - if (!nsJSUtils::GetScopeChainForXBL(cx, aBoundElement->AsElement(), - aProtoBinding, &scopeChain)) { - return NS_ERROR_OUT_OF_MEMORY; - } - MOZ_ASSERT(scopeChain.length() != 0); - - // Clone the function object, using our scope chain (for backwards - // compat to the days when this was an event handler). - JS::Rooted<JSObject*> jsMethodObject(cx, GetCompiledMethod()); - JS::Rooted<JSObject*> method( - cx, JS::CloneFunctionObject(cx, jsMethodObject, scopeChain)); - if (!method) return NS_ERROR_OUT_OF_MEMORY; - - // Now call the method - - // Check whether script is enabled. - bool scriptAllowed = xpc::Scriptability::Get(method).Allowed(); - - if (scriptAllowed) { - JS::Rooted<JS::Value> retval(cx); - JS::Rooted<JS::Value> methodVal(cx, JS::ObjectValue(*method)); - // No need to check the return here as AutoEntryScript has taken ownership - // of error reporting. - ::JS::Call(cx, scopeChain[0], methodVal, JS::HandleValueArray::empty(), - &retval); - } - - return NS_OK; -} - -nsresult nsXBLProtoImplAnonymousMethod::Write( - nsIObjectOutputStream* aStream, XBLBindingSerializeDetails aType) { - AssertInCompilationScope(); - MOZ_ASSERT(IsCompiled()); - if (GetCompiledMethodPreserveColor()) { - nsresult rv = aStream->Write8(aType); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aStream->WriteWStringZ(mName); - NS_ENSURE_SUCCESS(rv, rv); - - JS::Rooted<JSObject*> method(RootingCx(), GetCompiledMethod()); - rv = XBL_SerializeFunction(aStream, method); - NS_ENSURE_SUCCESS(rv, rv); - } - - return NS_OK; -}
deleted file mode 100644 --- a/dom/xbl/nsXBLProtoImplMethod.h +++ /dev/null @@ -1,139 +0,0 @@ -/* -*- 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 nsXBLProtoImplMethod_h__ -#define nsXBLProtoImplMethod_h__ - -#include "mozilla/Attributes.h" -#include "nsAtom.h" -#include "nsString.h" -#include "nsString.h" -#include "nsXBLMaybeCompiled.h" -#include "nsXBLProtoImplMember.h" -#include "nsXBLSerialize.h" - -class nsIContent; - -struct nsXBLParameter { - nsXBLParameter* mNext; - char* mName; - - explicit nsXBLParameter(const nsAString& aName) { - MOZ_COUNT_CTOR(nsXBLParameter); - mName = ToNewCString(aName); - mNext = nullptr; - } - - ~nsXBLParameter() { - MOZ_COUNT_DTOR(nsXBLParameter); - free(mName); - NS_CONTENT_DELETE_LIST_MEMBER(nsXBLParameter, this, mNext); - } -}; - -struct nsXBLUncompiledMethod { - nsXBLParameter* mParameters; - nsXBLParameter* mLastParameter; - nsXBLTextWithLineNumber mBodyText; - - nsXBLUncompiledMethod() - : mParameters(nullptr), mLastParameter(nullptr), mBodyText() { - MOZ_COUNT_CTOR(nsXBLUncompiledMethod); - } - - ~nsXBLUncompiledMethod() { - MOZ_COUNT_DTOR(nsXBLUncompiledMethod); - delete mParameters; - } - - int32_t GetParameterCount() { - int32_t result = 0; - for (nsXBLParameter* curr = mParameters; curr; curr = curr->mNext) result++; - return result; - } - - void AppendBodyText(const nsAString& aText) { mBodyText.AppendText(aText); } - - void AddParameter(const nsAString& aText) { - nsXBLParameter* param = new nsXBLParameter(aText); - if (!mParameters) - mParameters = param; - else - mLastParameter->mNext = param; - mLastParameter = param; - } - - void SetLineNumber(uint32_t aLineNumber) { - mBodyText.SetLineNumber(aLineNumber); - } -}; - -class nsXBLProtoImplMethod : public nsXBLProtoImplMember { - public: - explicit nsXBLProtoImplMethod(const char16_t* aName); - virtual ~nsXBLProtoImplMethod(); - - void AppendBodyText(const nsAString& aBody); - void AddParameter(const nsAString& aName); - - void SetLineNumber(uint32_t aLineNumber); - - virtual nsresult InstallMember( - JSContext* aCx, JS::Handle<JSObject*> aTargetClassObject) override; - virtual nsresult CompileMember(mozilla::dom::AutoJSAPI& jsapi, - const nsString& aClassStr, - JS::Handle<JSObject*> aClassObject) override; - - virtual void Trace(const TraceCallbacks& aCallbacks, void* aClosure) override; - - nsresult Read(nsIObjectInputStream* aStream); - virtual nsresult Write(nsIObjectOutputStream* aStream) override; - - bool IsCompiled() const { return mMethod.IsCompiled(); } - - void SetUncompiledMethod(nsXBLUncompiledMethod* aUncompiledMethod) { - mMethod.SetUncompiled(aUncompiledMethod); - } - - nsXBLUncompiledMethod* GetUncompiledMethod() const { - return mMethod.GetUncompiled(); - } - - protected: - void SetCompiledMethod(JSObject* aCompiledMethod) { - mMethod.SetJSFunction(aCompiledMethod); - } - - JSObject* GetCompiledMethod() const { return mMethod.GetJSFunction(); } - - JSObject* GetCompiledMethodPreserveColor() const { - return mMethod.GetJSFunctionPreserveColor(); - } - - JS::Heap<nsXBLMaybeCompiled<nsXBLUncompiledMethod> > mMethod; -}; - -class nsXBLProtoImplAnonymousMethod : public nsXBLProtoImplMethod { - public: - explicit nsXBLProtoImplAnonymousMethod(const char16_t* aName) - : nsXBLProtoImplMethod(aName) {} - - nsresult Execute(nsIContent* aBoundElement, const nsXBLPrototypeBinding&); - - // Override InstallMember; these methods never get installed as members on - // binding instantiations (though they may hang out in mMembers on the - // prototype implementation). - virtual nsresult InstallMember( - JSContext* aCx, JS::Handle<JSObject*> aTargetClassObject) override { - return NS_OK; - } - - using nsXBLProtoImplMethod::Write; - nsresult Write(nsIObjectOutputStream* aStream, - XBLBindingSerializeDetails aType); -}; - -#endif // nsXBLProtoImplMethod_h__
deleted file mode 100644 --- a/dom/xbl/nsXBLProtoImplProperty.cpp +++ /dev/null @@ -1,345 +0,0 @@ -/* -*- 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 "nsAtom.h" -#include "nsString.h" -#include "jsapi.h" -#include "nsIContent.h" -#include "nsXBLProtoImplProperty.h" -#include "nsUnicharUtils.h" -#include "nsReadableUtils.h" -#include "nsJSUtils.h" -#include "nsXBLPrototypeBinding.h" -#include "nsXBLSerialize.h" -#include "xpcpublic.h" - -using namespace mozilla; -using namespace mozilla::dom; - -nsXBLProtoImplProperty::nsXBLProtoImplProperty(const char16_t* aName, - const char16_t* aGetter, - const char16_t* aSetter, - const char16_t* aReadOnly, - uint32_t aLineNumber) - : nsXBLProtoImplMember(aName), - mJSAttributes(JSPROP_ENUMERATE) -#ifdef DEBUG - , - mIsCompiled(false) -#endif -{ - MOZ_COUNT_CTOR(nsXBLProtoImplProperty); - - if (aReadOnly) { - nsAutoString readOnly; - readOnly.Assign(*aReadOnly); - if (readOnly.LowerCaseEqualsLiteral("true")) - mJSAttributes |= JSPROP_READONLY; - } - - if (aGetter) { - AppendGetterText(nsDependentString(aGetter)); - SetGetterLineNumber(aLineNumber); - } - if (aSetter) { - AppendSetterText(nsDependentString(aSetter)); - SetSetterLineNumber(aLineNumber); - } -} - -nsXBLProtoImplProperty::nsXBLProtoImplProperty(const char16_t* aName, - const bool aIsReadOnly) - : nsXBLProtoImplMember(aName), - mJSAttributes(JSPROP_ENUMERATE) -#ifdef DEBUG - , - mIsCompiled(false) -#endif -{ - MOZ_COUNT_CTOR(nsXBLProtoImplProperty); - - if (aIsReadOnly) mJSAttributes |= JSPROP_READONLY; -} - -nsXBLProtoImplProperty::~nsXBLProtoImplProperty() { - MOZ_COUNT_DTOR(nsXBLProtoImplProperty); - - if (!mGetter.IsCompiled()) { - delete mGetter.GetUncompiled(); - } - - if (!mSetter.IsCompiled()) { - delete mSetter.GetUncompiled(); - } -} - -void nsXBLProtoImplProperty::EnsureUncompiledText(PropertyOp& aPropertyOp) { - if (!aPropertyOp.GetUncompiled()) { - nsXBLTextWithLineNumber* text = new nsXBLTextWithLineNumber(); - aPropertyOp.SetUncompiled(text); - } -} - -void nsXBLProtoImplProperty::AppendGetterText(const nsAString& aText) { - MOZ_ASSERT(!mIsCompiled, "Must not be compiled when accessing getter text"); - EnsureUncompiledText(mGetter); - mGetter.GetUncompiled()->AppendText(aText); -} - -void nsXBLProtoImplProperty::AppendSetterText(const nsAString& aText) { - MOZ_ASSERT(!mIsCompiled, "Must not be compiled when accessing setter text"); - EnsureUncompiledText(mSetter); - mSetter.GetUncompiled()->AppendText(aText); -} - -void nsXBLProtoImplProperty::SetGetterLineNumber(uint32_t aLineNumber) { - MOZ_ASSERT(!mIsCompiled, "Must not be compiled when accessing getter text"); - EnsureUncompiledText(mGetter); - mGetter.GetUncompiled()->SetLineNumber(aLineNumber); -} - -void nsXBLProtoImplProperty::SetSetterLineNumber(uint32_t aLineNumber) { - MOZ_ASSERT(!mIsCompiled, "Must not be compiled when accessing setter text"); - EnsureUncompiledText(mSetter); - mSetter.GetUncompiled()->SetLineNumber(aLineNumber); -} - -const char* gPropertyArgs[] = {"val"}; - -nsresult nsXBLProtoImplProperty::InstallMember( - JSContext* aCx, JS::Handle<JSObject*> aTargetClassObject) { - MOZ_ASSERT(mIsCompiled, "Should not be installing an uncompiled property"); - MOZ_ASSERT(mGetter.IsCompiled() && mSetter.IsCompiled()); - MOZ_ASSERT(js::IsObjectInContextCompartment(aTargetClassObject, aCx)); - -#ifdef DEBUG - { - JS::Rooted<JSObject*> globalObject( - aCx, JS::GetNonCCWObjectGlobal(aTargetClassObject)); - MOZ_ASSERT(xpc::IsInContentXBLScope(globalObject) || - globalObject == xpc::GetXBLScope(aCx, globalObject)); - MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx) == globalObject); - } -#endif - - JS::Rooted<JSObject*> getter(aCx, mGetter.GetJSFunction()); - JS::Rooted<JSObject*> setter(aCx, mSetter.GetJSFunction()); - if (getter || setter) { - if (getter) { - if (!(getter = JS::CloneFunctionObject(aCx, getter))) - return NS_ERROR_OUT_OF_MEMORY; - } - - if (setter) { - if (!(setter = JS::CloneFunctionObject(aCx, setter))) - return NS_ERROR_OUT_OF_MEMORY; - } - - nsDependentString name(mName); - if (!::JS_DefineUCProperty(aCx, aTargetClassObject, - static_cast<const char16_t*>(mName), - name.Length(), getter, setter, mJSAttributes)) - return NS_ERROR_OUT_OF_MEMORY; - } - return NS_OK; -} - -nsresult nsXBLProtoImplProperty::CompileMember( - AutoJSAPI& jsapi, const nsString& aClassStr, - JS::Handle<JSObject*> aClassObject) { - AssertInCompilationScope(); - MOZ_ASSERT(!mIsCompiled, "Trying to compile an already-compiled property"); - MOZ_ASSERT(aClassObject, "Must have class object to compile"); - MOZ_ASSERT(!mGetter.IsCompiled() && !mSetter.IsCompiled()); - JSContext* cx = jsapi.cx(); - - if (!mName) - return NS_ERROR_FAILURE; // Without a valid name, we can't install the - // member. - - // We have a property. - nsresult rv = NS_OK; - - nsAutoCString functionUri; - if (mGetter.GetUncompiled() || mSetter.GetUncompiled()) { - functionUri = NS_ConvertUTF16toUTF8(aClassStr); - int32_t hash = functionUri.RFindChar('#'); - if (hash != kNotFound) { - functionUri.Truncate(hash); - } - } - - bool deletedGetter = false; - nsXBLTextWithLineNumber* getterText = mGetter.GetUncompiled(); - if (getterText && getterText->GetText()) { - nsDependentString getter(getterText->GetText()); - if (!getter.IsEmpty()) { - JSAutoRealm ar(cx, aClassObject); - JS::CompileOptions options(cx); - options.setFileAndLine(functionUri.get(), getterText->GetLineNumber()); - nsCString name = - NS_LITERAL_CSTRING("get_") + NS_ConvertUTF16toUTF8(mName); - JS::Rooted<JSObject*> getterObject(cx); - JS::RootedVector<JSObject*> emptyVector(cx); - rv = nsJSUtils::CompileFunction(jsapi, emptyVector, options, name, 0, - nullptr, getter, getterObject.address()); - - delete getterText; - deletedGetter = true; - - mGetter.SetJSFunction(getterObject); - - if (mGetter.GetJSFunction() && NS_SUCCEEDED(rv)) { - mJSAttributes |= JSPROP_GETTER; - } - if (NS_FAILED(rv)) { - mGetter.SetJSFunction(nullptr); - mJSAttributes &= ~JSPROP_GETTER; - /*chaining to return failure*/ - } - } - } // if getter is not empty - - if (!deletedGetter) { // Empty getter - delete getterText; - mGetter.SetJSFunction(nullptr); - } - - if (NS_FAILED(rv)) { - // We failed to compile our getter. So either we've set it to null, or - // it's still set to the text object. In either case, it's safe to return - // the error here, since then we'll be cleaned up as uncompiled and that - // will be ok. Going on and compiling the setter and _then_ returning an - // error, on the other hand, will try to clean up a compiled setter as - // uncompiled and crash. - return rv; - } - - bool deletedSetter = false; - nsXBLTextWithLineNumber* setterText = mSetter.GetUncompiled(); - if (setterText && setterText->GetText()) { - nsDependentString setter(setterText->GetText()); - if (!setter.IsEmpty()) { - JSAutoRealm ar(cx, aClassObject); - JS::CompileOptions options(cx); - options.setFileAndLine(functionUri.get(), setterText->GetLineNumber()); - nsCString name = - NS_LITERAL_CSTRING("set_") + NS_ConvertUTF16toUTF8(mName); - JS::Rooted<JSObject*> setterObject(cx); - JS::RootedVector<JSObject*> emptyVector(cx); - rv = nsJSUtils::CompileFunction(jsapi, emptyVector, options, name, 1, - gPropertyArgs, setter, - setterObject.address()); - - delete setterText; - deletedSetter = true; - mSetter.SetJSFunction(setterObject); - - if (mSetter.GetJSFunction() && NS_SUCCEEDED(rv)) { - mJSAttributes |= JSPROP_SETTER; - } - if (NS_FAILED(rv)) { - mSetter.SetJSFunction(nullptr); - mJSAttributes &= ~JSPROP_SETTER; - /*chaining to return failure*/ - } - } - } // if setter wasn't empty.... - - if (!deletedSetter) { // Empty setter - delete setterText; - mSetter.SetJSFunction(nullptr); - } - -#ifdef DEBUG - mIsCompiled = NS_SUCCEEDED(rv); -#endif - - return rv; -} - -void nsXBLProtoImplProperty::Trace(const TraceCallbacks& aCallbacks, - void* aClosure) { - if (mJSAttributes & JSPROP_GETTER) { - aCallbacks.Trace(&mGetter.AsHeapObject(), "mGetter", aClosure); - } - - if (mJSAttributes & JSPROP_SETTER) { - aCallbacks.Trace(&mSetter.AsHeapObject(), "mSetter", aClosure); - } -} - -nsresult nsXBLProtoImplProperty::Read(nsIObjectInputStream* aStream, - XBLBindingSerializeDetails aType) { - AssertInCompilationScope(); - MOZ_ASSERT(!mIsCompiled); - MOZ_ASSERT(!mGetter.GetUncompiled() && !mSetter.GetUncompiled()); - - AutoJSContext cx; - JS::Rooted<JSObject*> getterObject(cx); - if (aType == XBLBinding_Serialize_GetterProperty || - aType == XBLBinding_Serialize_GetterSetterProperty) { - nsresult rv = XBL_DeserializeFunction(aStream, &getterObject); - NS_ENSURE_SUCCESS(rv, rv); - - mJSAttributes |= JSPROP_GETTER; - } - mGetter.SetJSFunction(getterObject); - - JS::Rooted<JSObject*> setterObject(cx); - if (aType == XBLBinding_Serialize_SetterProperty || - aType == XBLBinding_Serialize_GetterSetterProperty) { - nsresult rv = XBL_DeserializeFunction(aStream, &setterObject); - NS_ENSURE_SUCCESS(rv, rv); - - mJSAttributes |= JSPROP_SETTER; - } - mSetter.SetJSFunction(setterObject); - -#ifdef DEBUG - mIsCompiled = true; -#endif - - return NS_OK; -} - -nsresult nsXBLProtoImplProperty::Write(nsIObjectOutputStream* aStream) { - AssertInCompilationScope(); - XBLBindingSerializeDetails type; - - if (mJSAttributes & JSPROP_GETTER) { - type = mJSAttributes & JSPROP_SETTER - ? XBLBinding_Serialize_GetterSetterProperty - : XBLBinding_Serialize_GetterProperty; - } else { - type = XBLBinding_Serialize_SetterProperty; - } - - if (mJSAttributes & JSPROP_READONLY) { - type |= XBLBinding_Serialize_ReadOnly; - } - - nsresult rv = aStream->Write8(type); - NS_ENSURE_SUCCESS(rv, rv); - rv = aStream->WriteWStringZ(mName); - NS_ENSURE_SUCCESS(rv, rv); - - MOZ_ASSERT_IF(mJSAttributes & (JSPROP_GETTER | JSPROP_SETTER), mIsCompiled); - - if (mJSAttributes & JSPROP_GETTER) { - JS::Rooted<JSObject*> function(RootingCx(), mGetter.GetJSFunction()); - rv = XBL_SerializeFunction(aStream, function); - NS_ENSURE_SUCCESS(rv, rv); - } - - if (mJSAttributes & JSPROP_SETTER) { - JS::Rooted<JSObject*> function(RootingCx(), mSetter.GetJSFunction()); - rv = XBL_SerializeFunction(aStream, function); - NS_ENSURE_SUCCESS(rv, rv); - } - - return NS_OK; -}
deleted file mode 100644 --- a/dom/xbl/nsXBLProtoImplProperty.h +++ /dev/null @@ -1,65 +0,0 @@ -/* -*- 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 nsXBLProtoImplProperty_h__ -#define nsXBLProtoImplProperty_h__ - -#include "mozilla/Attributes.h" -#include "nsAtom.h" -#include "nsString.h" -#include "nsString.h" -#include "nsXBLSerialize.h" -#include "nsXBLMaybeCompiled.h" -#include "nsXBLProtoImplMember.h" - -class nsXBLProtoImplProperty : public nsXBLProtoImplMember { - public: - nsXBLProtoImplProperty(const char16_t* aName, const char16_t* aGetter, - const char16_t* aSetter, const char16_t* aReadOnly, - uint32_t aLineNumber); - - nsXBLProtoImplProperty(const char16_t* aName, const bool aIsReadOnly); - - virtual ~nsXBLProtoImplProperty(); - - void AppendGetterText(const nsAString& aGetter); - void AppendSetterText(const nsAString& aSetter); - - void SetGetterLineNumber(uint32_t aLineNumber); - void SetSetterLineNumber(uint32_t aLineNumber); - - virtual nsresult InstallMember( - JSContext* aCx, JS::Handle<JSObject*> aTargetClassObject) override; - virtual nsresult CompileMember(mozilla::dom::AutoJSAPI& jsapi, - const nsString& aClassStr, - JS::Handle<JSObject*> aClassObject) override; - - virtual void Trace(const TraceCallbacks& aCallback, void* aClosure) override; - - nsresult Read(nsIObjectInputStream* aStream, - XBLBindingSerializeDetails aType); - virtual nsresult Write(nsIObjectOutputStream* aStream) override; - - protected: - typedef JS::Heap<nsXBLMaybeCompiled<nsXBLTextWithLineNumber> > PropertyOp; - - void EnsureUncompiledText(PropertyOp& aPropertyOp); - - // The raw text for the getter, or the JS object (after compilation). - PropertyOp mGetter; - - // The raw text for the setter, or the JS object (after compilation). - PropertyOp mSetter; - - unsigned mJSAttributes; // A flag for all our JS properties - // (getter/setter/readonly/shared/enum) - -#ifdef DEBUG - bool mIsCompiled; -#endif -}; - -#endif // nsXBLProtoImplProperty_h__
deleted file mode 100644 --- a/dom/xbl/nsXBLPrototypeBinding.cpp +++ /dev/null @@ -1,1337 +0,0 @@ -/* -*- 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" - -#include "nsCOMPtr.h" -#include "nsAtom.h" -#include "nsIInputStream.h" -#include "nsNameSpaceManager.h" -#include "nsIURI.h" -#include "nsIURIMutator.h" -#include "nsIURL.h" -#include "nsIChannel.h" -#include "nsString.h" -#include "nsReadableUtils.h" -#include "nsNetUtil.h" -#include "plstr.h" -#include "nsContentCreatorFunctions.h" -#include "mozilla/dom/Document.h" -#include "nsIXMLContentSink.h" -#include "nsContentCID.h" -#include "mozilla/dom/XMLDocument.h" -#include "nsXBLService.h" -#include "nsXBLBinding.h" -#include "nsXBLPrototypeBinding.h" -#include "nsXBLContentSink.h" -#include "xptinfo.h" -#include "nsIDocumentObserver.h" -#include "nsGkAtoms.h" -#include "nsXBLProtoImpl.h" -#include "nsCRT.h" -#include "nsContentUtils.h" -#include "nsTextFragment.h" -#include "nsTextNode.h" -#include "nsIScriptError.h" - -#include "mozilla/dom/CDATASection.h" -#include "mozilla/dom/CharacterData.h" -#include "mozilla/dom/Comment.h" -#include "mozilla/dom/Element.h" - -#ifdef MOZ_XUL -# include "nsXULElement.h" -#endif - -using namespace mozilla; -using namespace mozilla::dom; - -// Helper Classes ============================================================== - -// nsXBLAttributeEntry and helpers. This class is used to efficiently handle -// attribute changes in anonymous content. - -class nsXBLAttributeEntry { - public: - nsXBLAttributeEntry(nsAtom* aSrcAtom, nsAtom* aDstAtom, int32_t aDstNameSpace, - Element* aElement) - : mElement(aElement), - mSrcAttribute(aSrcAtom), - mDstAttribute(aDstAtom), - mDstNameSpace(aDstNameSpace), - mNext(nullptr) {} - - ~nsXBLAttributeEntry() { - NS_CONTENT_DELETE_LIST_MEMBER(nsXBLAttributeEntry, this, mNext); - } - - nsAtom* GetSrcAttribute() { return mSrcAttribute; } - nsAtom* GetDstAttribute() { return mDstAttribute; } - int32_t GetDstNameSpace() { return mDstNameSpace; } - - Element* GetElement() { return mElement; } - - nsXBLAttributeEntry* GetNext() { return mNext; } - const nsXBLAttributeEntry* GetNext() const { return mNext; } - void SetNext(nsXBLAttributeEntry* aEntry) { mNext = aEntry; } - - size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const; - - protected: - Element* mElement; - - RefPtr<nsAtom> mSrcAttribute; - RefPtr<nsAtom> mDstAttribute; - int32_t mDstNameSpace; - nsXBLAttributeEntry* mNext; -}; - -size_t nsXBLAttributeEntry::SizeOfIncludingThis( - MallocSizeOf aMallocSizeOf) const { - size_t n = 0; - for (const nsXBLAttributeEntry* entry = this; entry; - entry = entry->GetNext()) { - n += aMallocSizeOf(entry); - } - return n; -} - -// ============================================================================= - -// Implementation ////////////////////////////////////////////////////////////// - -// Constructors/Destructors -nsXBLPrototypeBinding::nsXBLPrototypeBinding() - : mImplementation(nullptr), - mBaseBinding(nullptr), - mCheckedBaseProto(false), - mKeyHandlersRegistered(false), - mBindToUntrustedContent(false), - mSimpleScopeChain(false), - mXBLDocInfoWeak(nullptr) { - MOZ_COUNT_CTOR(nsXBLPrototypeBinding); -} - -nsresult nsXBLPrototypeBinding::Init(const nsACString& aID, - nsXBLDocumentInfo* aInfo, - Element* aElement, bool aFirstBinding) { - nsresult rv; - nsCOMPtr<nsIURI> bindingURI = aInfo->DocumentURI(); - - // The binding URI might be an immutable URI (e.g. for about: URIs). In that - // case, we'll fail in SetRef below, but that doesn't matter much for now. - if (aFirstBinding) { - mAlternateBindingURI = bindingURI; - } - rv = NS_MutateURI(bindingURI).SetRef(aID).Finalize(mBindingURI); - if (NS_FAILED(rv)) { - // If SetRef failed, mBindingURI should be a clone. - mBindingURI = bindingURI; - } - - mXBLDocInfoWeak = aInfo; - - // aElement will be null when reading from the cache, but the element will - // still be set later. - if (aElement) { - SetBindingElement(aElement); - } - return NS_OK; -} - -bool nsXBLPrototypeBinding::CompareBindingURI(nsIURI* aURI) const { - bool equal = false; - mBindingURI->Equals(aURI, &equal); - if (!equal && mAlternateBindingURI) { - mAlternateBindingURI->Equals(aURI, &equal); - } - return equal; -} - -void nsXBLPrototypeBinding::Traverse( - nsCycleCollectionTraversalCallback& cb) const { - NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "proto mBinding"); - cb.NoteXPCOMChild(mBinding); - ImplCycleCollectionTraverse(cb, mInterfaceTable, "proto mInterfaceTable"); -} - -void nsXBLPrototypeBinding::Unlink() { - if (mImplementation) { - mImplementation->UnlinkJSObjects(); - } -} - -void nsXBLPrototypeBinding::Trace(const TraceCallbacks& aCallbacks, - void* aClosure) const { - if (mImplementation) mImplementation->Trace(aCallbacks, aClosure); -} - -void nsXBLPrototypeBinding::Initialize() { - if (Element* content = GetImmediateChild(nsGkAtoms::content)) { - ConstructAttributeTable(content); - } -} - -nsXBLPrototypeBinding::~nsXBLPrototypeBinding(void) { - delete mImplementation; - MOZ_COUNT_DTOR(nsXBLPrototypeBinding); -} - -void nsXBLPrototypeBinding::SetBasePrototype(nsXBLPrototypeBinding* aBinding) { - if (mBaseBinding == aBinding) return; - - if (mBaseBinding) { - NS_ERROR("Base XBL prototype binding is already defined!"); - return; - } - - mBaseBinding = aBinding; -} - -void nsXBLPrototypeBinding::SetBindingElement(Element* aElement) { - mBinding = aElement; - - mBindToUntrustedContent = mBinding->AttrValueIs( - kNameSpaceID_None, nsGkAtoms::bindToUntrustedContent, nsGkAtoms::_true, - eCaseMatters); - - // TODO(emilio): Should we imply mBindToUntrustedContent -> mSimpleScopeChain? - mSimpleScopeChain = - mBinding->AttrValueIs(kNameSpaceID_None, nsGkAtoms::simpleScopeChain, - nsGkAtoms::_true, eCaseMatters); -} - -bool nsXBLPrototypeBinding::GetAllowScripts() const { - return mXBLDocInfoWeak->GetScriptAccess(); -} - -nsresult nsXBLPrototypeBinding::BindingAttached(nsIContent* aBoundElement) { - if (mImplementation && mImplementation->CompiledMembers() && - mImplementation->mConstructor) - return mImplementation->mConstructor->Execute(aBoundElement, *this); - return NS_OK; -} - -nsresult nsXBLPrototypeBinding::BindingDetached(nsIContent* aBoundElement) { - if (mImplementation && mImplementation->CompiledMembers() && - mImplementation->mDestructor) - return mImplementation->mDestructor->Execute(aBoundElement, *this); - return NS_OK; -} - -nsXBLProtoImplAnonymousMethod* nsXBLPrototypeBinding::GetConstructor() { - if (mImplementation) return mImplementation->mConstructor; - - return nullptr; -} - -nsXBLProtoImplAnonymousMethod* nsXBLPrototypeBinding::GetDestructor() { - if (mImplementation) return mImplementation->mDestructor; - - return nullptr; -} - -nsresult nsXBLPrototypeBinding::SetConstructor( - nsXBLProtoImplAnonymousMethod* aMethod) { - if (!mImplementation) return NS_ERROR_FAILURE; - mImplementation->mConstructor = aMethod; - return NS_OK; -} - -nsresult nsXBLPrototypeBinding::SetDestructor( - nsXBLProtoImplAnonymousMethod* aMethod) { - if (!mImplementation) return NS_ERROR_FAILURE; - mImplementation->mDestructor = aMethod; - return NS_OK; -} - -nsresult nsXBLPrototypeBinding::InstallImplementation(nsXBLBinding* aBinding) { - if (mImplementation) - return mImplementation->InstallImplementation(this, aBinding); - return NS_OK; -} - -// XXXbz this duplicates lots of SetAttrs -void nsXBLPrototypeBinding::AttributeChanged( - nsAtom* aAttribute, int32_t aNameSpaceID, bool aRemoveFlag, - Element* aChangedElement, nsIContent* aAnonymousContent, bool aNotify) { - if (!mAttributeTable) return; - - InnerAttributeTable* attributesNS = mAttributeTable->Get(aNameSpaceID); - if (!attributesNS) return; - - nsXBLAttributeEntry* xblAttr = attributesNS->Get(aAttribute); - if (!xblAttr) return; - - // Iterate over the elements in the array. - RefPtr<Element> content = GetImmediateChild(nsGkAtoms::content); - while (xblAttr) { - Element* element = xblAttr->GetElement(); - - RefPtr<Element> realElement = - LocateInstance(aChangedElement, content, aAnonymousContent, element); - - if (realElement) { - // Hold a strong reference here so that the atom doesn't go away during - // UnsetAttr. - RefPtr<nsAtom> dstAttr = xblAttr->GetDstAttribute(); - int32_t dstNs = xblAttr->GetDstNameSpace(); - - if (aRemoveFlag) - realElement->UnsetAttr(dstNs, dstAttr, aNotify); - else { - bool attrPresent = true; - nsAutoString value; - // Check to see if the src attribute is xbl:text. If so, then we need - // to obtain the children of the real element and get the text nodes' - // values. - if (aAttribute == nsGkAtoms::text && aNameSpaceID == kNameSpaceID_XBL) { - nsContentUtils::GetNodeTextContent(aChangedElement, false, value); - value.StripChar(char16_t('\n')); - value.StripChar(char16_t('\r')); - nsAutoString stripVal(value); - stripVal.StripWhitespace(); - if (stripVal.IsEmpty()) attrPresent = false; - } else { - attrPresent = - aChangedElement->GetAttr(aNameSpaceID, aAttribute, value); - } - - if (attrPresent) realElement->SetAttr(dstNs, dstAttr, value, aNotify); - } - - // See if we're the <html> tag in XUL, and see if value is being - // set or unset on us. We may also be a tag that is having - // xbl:text set on us. - - if ((dstAttr == nsGkAtoms::text && dstNs == kNameSpaceID_XBL) || - (realElement->NodeInfo()->Equals(nsGkAtoms::html, kNameSpaceID_XUL) && - dstAttr == nsGkAtoms::value)) { - // Flush out all our kids. - while (realElement->HasChildren()) { - realElement->RemoveChildNode(realElement->GetFirstChild(), aNotify); - } - - if (!aRemoveFlag) { - // Construct a new text node and insert it. - nsAutoString value; - aChangedElement->GetAttr(aNameSpaceID, aAttribute, value); - if (!value.IsEmpty()) { - RefPtr<nsTextNode> textContent = - new nsTextNode(realElement->NodeInfo()->NodeInfoManager()); - - textContent->SetText(value, true); - realElement->AppendChildTo(textContent, true); - } - } - } - } - - xblAttr = xblAttr->GetNext(); - } -} - -bool nsXBLPrototypeBinding::ImplementsInterface(REFNSIID aIID) const { - // Check our IID table. - return !!mInterfaceTable.GetWeak(aIID); -} - -// Internal helpers //////////////////////////////////////////////////////////// - -Element* nsXBLPrototypeBinding::GetImmediateChild(nsAtom* aTag) { - for (nsIContent* child = mBinding->GetFirstChild(); child; - child = child->GetNextSibling()) { - if (child->NodeInfo()->Equals(aTag, kNameSpaceID_XBL)) { - return child->AsElement(); - } - } - - return nullptr; -} - -nsresult nsXBLPrototypeBinding::InitClass( - const nsString& aClassName, JSContext* aContext, - JS::Handle<JSObject*> aScriptObject, - JS::MutableHandle<JSObject*> aClassObject, bool* aNew) { - return nsXBLBinding::DoInitJSClass(aContext, aScriptObject, aClassName, this, - aClassObject, aNew); -} - -Element* nsXBLPrototypeBinding::LocateInstance(Element* aBoundElement, - nsIContent* aTemplRoot, - nsIContent* aCopyRoot, - Element* aTemplChild) { - // XXX We will get in trouble if the binding instantiation deviates from the - // template in the prototype. - if (aTemplChild == aTemplRoot || !aTemplChild) return nullptr; - - Element* templParent = aTemplChild->GetParentElement(); - - // We may be disconnected from our parent during cycle collection. - if (!templParent) return nullptr; - - nsIContent* copyParent = - templParent == aTemplRoot - ? aCopyRoot - : LocateInstance(aBoundElement, aTemplRoot, aCopyRoot, templParent); - - if (!copyParent) return nullptr; - - nsIContent* child = copyParent->GetChildAt_Deprecated( - templParent->ComputeIndexOf(aTemplChild)); - if (child && child->IsElement()) { - return child->AsElement(); - } - return nullptr; -} - -void nsXBLPrototypeBinding::SetInitialAttributes( - Element* aBoundElement, nsIContent* aAnonymousContent) { - if (!mAttributeTable) { - return; - } - - for (auto iter1 = mAttributeTable->Iter(); !iter1.Done(); iter1.Next()) { - InnerAttributeTable* xblAttributes = iter1.UserData(); - if (xblAttributes) { - int32_t srcNamespace = iter1.Key(); - - for (auto iter2 = xblAttributes->Iter(); !iter2.Done(); iter2.Next()) { - // XXXbz this duplicates lots of AttributeChanged - nsXBLAttributeEntry* entry = iter2.UserData(); - nsAtom* src = entry->GetSrcAttribute(); - nsAutoString value; - bool attrPresent = true; - - if (src == nsGkAtoms::text && srcNamespace == kNameSpaceID_XBL) { - nsContentUtils::GetNodeTextContent(aBoundElement, false, value); - value.StripChar(char16_t('\n')); - value.StripChar(char16_t('\r')); - nsAutoString stripVal(value); - stripVal.StripWhitespace(); - - if (stripVal.IsEmpty()) { - attrPresent = false; - } - } else { - attrPresent = aBoundElement->GetAttr(srcNamespace, src, value); - } - - if (attrPresent) { - nsIContent* content = GetImmediateChild(nsGkAtoms::content); - - nsXBLAttributeEntry* curr = entry; - while (curr) { - nsAtom* dst = curr->GetDstAttribute(); - int32_t dstNs = curr->GetDstNameSpace(); - Element* element = curr->GetElement(); - - Element* realElement = LocateInstance(aBoundElement, content, - aAnonymousContent, element); - - if (realElement) { - realElement->SetAttr(dstNs, dst, value, false); - - // XXXndeakin shouldn't this be done in lieu of SetAttr? - if ((dst == nsGkAtoms::text && dstNs == kNameSpaceID_XBL) || - (realElement->NodeInfo()->Equals(nsGkAtoms::html, - kNameSpaceID_XUL) && - dst == nsGkAtoms::value && !value.IsEmpty())) { - RefPtr<nsTextNode> textContent = - new nsTextNode(realElement->NodeInfo()->NodeInfoManager()); - - textContent->SetText(value, false); - realElement->AppendChildTo(textContent, false); - } - } - - curr = curr->GetNext(); - } - } - } - } - } -} - -void nsXBLPrototypeBinding::EnsureAttributeTable() { - if (!mAttributeTable) { - mAttributeTable = - new nsClassHashtable<nsUint32HashKey, InnerAttributeTable>(2); - } -} - -void nsXBLPrototypeBinding::AddToAttributeTable(int32_t aSourceNamespaceID, - nsAtom* aSourceTag, - int32_t aDestNamespaceID, - nsAtom* aDestTag, - Element* aElement) { - InnerAttributeTable* attributesNS = mAttributeTable->Get(aSourceNamespaceID); - if (!attributesNS) { - attributesNS = new InnerAttributeTable(2); - mAttributeTable->Put(aSourceNamespaceID, attributesNS); - } - - nsXBLAttributeEntry* xblAttr = - new nsXBLAttributeEntry(aSourceTag, aDestTag, aDestNamespaceID, aElement); - - nsXBLAttributeEntry* entry = attributesNS->Get(aSourceTag); - if (!entry) { - attributesNS->Put(aSourceTag, xblAttr); - } else { - while (entry->GetNext()) entry = entry->GetNext(); - entry->SetNext(xblAttr); - } -} - -void nsXBLPrototypeBinding::ConstructAttributeTable(Element* aElement) { - // Don't add entries for <children> elements, since those will get - // removed from the DOM when we construct the insertion point table. - if (!aElement->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) { - nsAutoString inherits; - aElement->GetAttr(kNameSpaceID_XBL, nsGkAtoms::inherits, inherits); - - if (!inherits.IsEmpty()) { - EnsureAttributeTable(); - - // The user specified at least one attribute. - char* str = ToNewCString(inherits); - char* newStr; - // XXX We should use a strtok function that tokenizes PRUnichars - // so that we don't have to convert from Unicode to ASCII and then back - - char* token = nsCRT::strtok(str, ", ", &newStr); - while (token != nullptr) { - // Build an atom out of this attribute. - RefPtr<nsAtom> atom; - int32_t atomNsID = kNameSpaceID_None; - RefPtr<nsAtom> attribute; - int32_t attributeNsID = kNameSpaceID_None; - - // Figure out if this token contains a :. - NS_ConvertASCIItoUTF16 attrTok(token); - int32_t index = attrTok.Find("=", true); - nsresult rv; - if (index != -1) { - // This attribute maps to something different. - nsAutoString left, right; - attrTok.Left(left, index); - attrTok.Right(right, attrTok.Length() - index - 1); - - rv = nsContentUtils::SplitQName(aElement, left, &attributeNsID, - getter_AddRefs(attribute)); - if (NS_FAILED(rv)) return; - - rv = nsContentUtils::SplitQName(aElement, right, &atomNsID, - getter_AddRefs(atom)); - if (NS_FAILED(rv)) return; - } else { - NS_ConvertASCIItoUTF16 tok(token); - rv = nsContentUtils::SplitQName(aElement, tok, &atomNsID, - getter_AddRefs(atom)); - if (NS_FAILED(rv)) return; - attribute = atom; - attributeNsID = atomNsID; - } - - AddToAttributeTable(atomNsID, atom, attributeNsID, attribute, aElement); - - // Now remove the inherits attribute from the element so that it doesn't - // show up on clones of the element. It is used - // by the template only, and we don't need it anymore. - // XXXdwh Don't do this for XUL elements, since it faults them into - // heavyweight elements. Should nuke from the prototype instead. - // aElement->UnsetAttr(kNameSpaceID_XBL, nsGkAtoms::inherits, false); - - token = nsCRT::strtok(newStr, ", ", &newStr); - } - - free(str); - } - } - - // Recur into our children. - for (nsIContent* child = aElement->GetFirstChild(); child; - child = child->GetNextSibling()) { - if (child->IsElement()) { - ConstructAttributeTable(child->AsElement()); - } - } -} - -nsresult nsXBLPrototypeBinding::ConstructInterfaceTable( - const nsAString& aImpls) { - if (!aImpls.IsEmpty()) { - // The user specified at least one attribute. - NS_ConvertUTF16toUTF8 utf8impl(aImpls); - char* str = utf8impl.BeginWriting(); - char* newStr; - // XXX We should use a strtok function that tokenizes PRUnichars - // so that we don't have to convert from Unicode to ASCII and then back - - char* token = nsCRT::strtok(str, ", ", &newStr); - while (token != nullptr) { - // get the InterfaceInfo for the name - const nsXPTInterfaceInfo* iinfo = nsXPTInterfaceInfo::ByName(token); - - if (iinfo) { - // Add the iid to our table. - mInterfaceTable.Put(iinfo->IID(), mBinding); - - // this block adds the parent interfaces of each interface - // defined in the xbl definition (implements="nsI...") - const nsXPTInterfaceInfo* parentInfo; - // if it has a parent, add it to the table - while ((parentInfo = iinfo->GetParent())) { - // don't add nsISupports to the table - if (parentInfo->IID().Equals(NS_GET_IID(nsISupports))) { - break; - } - - // add the iid to the table - mInterfaceTable.Put(parentInfo->IID(), mBinding); - - // look for the next parent - iinfo = parentInfo; - } - } - - token = nsCRT::strtok(newStr, ", ", &newStr); - } - } - - return NS_OK; -} - -void nsXBLPrototypeBinding::CreateKeyHandlers() { - nsXBLPrototypeHandler* curr = mPrototypeHandler; - while (curr) { - RefPtr<nsAtom> eventAtom = curr->GetEventName(); - if (eventAtom == nsGkAtoms::keyup || eventAtom == nsGkAtoms::keydown || - eventAtom == nsGkAtoms::keypress) { - uint8_t phase = curr->GetPhase(); - uint8_t type = curr->GetType(); - - int32_t count = mKeyHandlers.Count(); - int32_t i; - nsXBLKeyEventHandler* handler = nullptr; - for (i = 0; i < count; ++i) { - handler = mKeyHandlers[i]; - if (handler->Matches(eventAtom, phase, type)) break; - } - - if (i == count) { - RefPtr<nsXBLKeyEventHandler> newHandler = - new nsXBLKeyEventHandler(eventAtom, phase, type); - mKeyHandlers.AppendObject(newHandler); - handler = newHandler; - } - - if (handler) handler->AddProtoHandler(curr); - } - - curr = curr->GetNextHandler(); - } -} - -class XBLPrototypeSetupCleanup { - public: - XBLPrototypeSetupCleanup(nsXBLDocumentInfo* aDocInfo, const nsACString& aID) - : mDocInfo(aDocInfo), mID(aID) {} - - ~XBLPrototypeSetupCleanup() { - if (mDocInfo) { - mDocInfo->RemovePrototypeBinding(mID); - } - } - - void Disconnect() { mDocInfo = nullptr; } - - nsXBLDocumentInfo* mDocInfo; - nsAutoCString mID; -}; - -nsresult nsXBLPrototypeBinding::Read(nsIObjectInputStream* aStream, - nsXBLDocumentInfo* aDocInfo, - Document* aDocument, uint8_t aFlags) { - mBindToUntrustedContent = - (aFlags & XBLBinding_Serialize_BindToUntrustedContent) ? true : false; - mSimpleScopeChain = - (aFlags & XBLBinding_Serialize_SimpleScopeChain) ? true : false; - - // nsXBLContentSink::ConstructBinding doesn't create a binding with an empty - // id, so we don't here either. - nsAutoCString id; - nsresult rv = aStream->ReadCString(id); - - NS_ENSURE_SUCCESS(rv, rv); - NS_ENSURE_TRUE(!id.IsEmpty(), NS_ERROR_FAILURE); - - nsAutoCString baseBindingURI; - rv = aStream->ReadCString(baseBindingURI); - NS_ENSURE_SUCCESS(rv, rv); - mCheckedBaseProto = true; - - if (!baseBindingURI.IsEmpty()) { - rv = NS_NewURI(getter_AddRefs(mBaseBindingURI), baseBindingURI); - NS_ENSURE_SUCCESS(rv, rv); - } - - mBinding = aDocument->CreateElem(NS_LITERAL_STRING("binding"), nullptr, - kNameSpaceID_XBL); - - nsCOMPtr<nsIContent> child; - rv = ReadContentNode(aStream, aDocument, aDocument->NodeInfoManager(), - getter_AddRefs(child)); - NS_ENSURE_SUCCESS(rv, rv); - - Element* rootElement = aDocument->GetRootElement(); - if (rootElement) rootElement->AppendChildTo(mBinding, false); - - if (child) { - mBinding->AppendChildTo(child, false); - } - - uint32_t interfaceCount; - rv = aStream->Read32(&interfaceCount); - NS_ENSURE_SUCCESS(rv, rv); - - for (; interfaceCount > 0; interfaceCount--) { - nsIID iid; - rv = aStream->ReadID(&iid); - NS_ENSURE_SUCCESS(rv, rv); - mInterfaceTable.Put(iid, mBinding); - } - - // We're not directly using this AutoJSAPI here, but callees use it via - // AutoJSContext. - AutoJSAPI jsapi; - if (!jsapi.Init(xpc::CompilationScope())) { - return NS_ERROR_UNEXPECTED; - } - - bool isFirstBinding = aFlags & XBLBinding_Serialize_IsFirstBinding; - rv = Init(id, aDocInfo, nullptr, isFirstBinding); - NS_ENSURE_SUCCESS(rv, rv); - - // We need to set the prototype binding before reading the nsXBLProtoImpl, - // as it may be retrieved within. - rv = aDocInfo->SetPrototypeBinding(id, this); - NS_ENSURE_SUCCESS(rv, rv); - - XBLPrototypeSetupCleanup cleanup(aDocInfo, id); - - nsAutoCString className; - rv = aStream->ReadCString(className); - NS_ENSURE_SUCCESS(rv, rv); - - if (!className.IsEmpty()) { - nsXBLProtoImpl* impl; // NS_NewXBLProtoImpl will set mImplementation for us - NS_NewXBLProtoImpl(this, NS_ConvertUTF8toUTF16(className).get(), &impl); - - // This needs to happen after SetPrototypeBinding as calls are made to - // retrieve the mapped bindings from within here. However, if an error - // occurs, the mapping should be removed again so that we don't keep an - // invalid binding around. - rv = mImplementation->Read(aStream, this); - NS_ENSURE_SUCCESS(rv, rv); - } - - // Next read in the handlers. - nsXBLPrototypeHandler* previousHandler = nullptr; - - do { - XBLBindingSerializeDetails type; - rv = aStream->Read8(&type); - NS_ENSURE_SUCCESS(rv, rv); - - if (type == XBLBinding_Serialize_NoMoreItems) break; - - NS_ASSERTION( - (type & XBLBinding_Serialize_Mask) == XBLBinding_Serialize_Handler, - "invalid handler type"); - - nsXBLPrototypeHandler* handler = new nsXBLPrototypeHandler(this); - rv = handler->Read(aStream); - if (NS_FAILED(rv)) { - delete handler; - return rv; - } - - if (previousHandler) { - previousHandler->SetNextHandler(handler); - } else { - SetPrototypeHandlers(handler); - } - previousHandler = handler; - } while (1); - - if (mBinding) { - while (true) { - XBLBindingSerializeDetails type; - rv = aStream->Read8(&type); - NS_ENSURE_SUCCESS(rv, rv); - - if (type != XBLBinding_Serialize_Attribute) { - break; - } - - int32_t attrNamespace; - rv = ReadNamespace(aStream, attrNamespace); - NS_ENSURE_SUCCESS(rv, rv); - - nsAutoString attrPrefix, attrName, attrValue; - rv = aStream->ReadString(attrPrefix); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aStream->ReadString(attrName); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aStream->ReadString(attrValue); - NS_ENSURE_SUCCESS(rv, rv); - - RefPtr<nsAtom> atomPrefix = NS_Atomize(attrPrefix); - RefPtr<nsAtom> atomName = NS_Atomize(attrName); - mBinding->SetAttr(attrNamespace, atomName, atomPrefix, attrValue, false); - } - } - - if (isFirstBinding) { - aDocInfo->SetFirstPrototypeBinding(this); - } - - cleanup.Disconnect(); - return NS_OK; -} - -// static -nsresult nsXBLPrototypeBinding::ReadNewBinding(nsIObjectInputStream* aStream, - nsXBLDocumentInfo* aDocInfo, - Document* aDocument, - uint8_t aFlags) { - // If the Read() succeeds, |binding| will end up being owned by aDocInfo's - // binding table. Otherwise, we must manually delete it. - nsXBLPrototypeBinding* binding = new nsXBLPrototypeBinding(); - nsresult rv = binding->Read(aStream, aDocInfo, aDocument, aFlags); - if (NS_FAILED(rv)) { - delete binding; - } - return rv; -} - -nsresult nsXBLPrototypeBinding::Write(nsIObjectOutputStream* aStream) { - // This writes out the binding. Note that mCheckedBaseProto, - // mKeyHandlersRegistered and mKeyHandlers are not serialized as they are - // computed on demand. - - // We're not directly using this AutoJSAPI here, but callees use it via - // AutoJSContext. - AutoJSAPI jsapi; - if (!jsapi.Init(xpc::CompilationScope())) { - return NS_ERROR_UNEXPECTED; - } - - uint8_t flags = 0; - - // mAlternateBindingURI is only set on the first binding. - if (mAlternateBindingURI) { - flags |= XBLBinding_Serialize_IsFirstBinding; - } - - if (mBindToUntrustedContent) { - flags |= XBLBinding_Serialize_BindToUntrustedContent; - } - - if (mSimpleScopeChain) { - flags |= XBLBinding_Serialize_SimpleScopeChain; - } - - nsresult rv = aStream->Write8(flags); - NS_ENSURE_SUCCESS(rv, rv); - - nsAutoCString id; - mBindingURI->GetRef(id); - rv = aStream->WriteStringZ(id.get()); - NS_ENSURE_SUCCESS(rv, rv); - - // write out the extends and display attribute values - nsAutoCString extends; - ResolveBaseBinding(); - if (mBaseBindingURI) { - rv = mBaseBindingURI->GetSpec(extends); - NS_ENSURE_SUCCESS(rv, rv); - } - - rv = aStream->WriteStringZ(extends.get()); - NS_ENSURE_SUCCESS(rv, rv); - - nsIContent* content = GetImmediateChild(nsGkAtoms::content); - if (content) { - rv = WriteContentNode(aStream, content); - NS_ENSURE_SUCCESS(rv, rv); - } else { - // Write a marker to indicate that there is no content. - rv = aStream->Write8(XBLBinding_Serialize_NoContent); - NS_ENSURE_SUCCESS(rv, rv); - } - - // Enumerate and write out the implemented interfaces. - rv = aStream->Write32(mInterfaceTable.Count()); - NS_ENSURE_SUCCESS(rv, rv); - - for (auto iter = mInterfaceTable.Iter(); !iter.Done(); iter.Next()) { - // We can just write out the ids. The cache will be invalidated when a - // different build is used, so we don't need to worry about ids changing. - aStream->WriteID(iter.Key()); - } - - // Write out the implementation details. - if (mImplementation) { - rv = mImplementation->Write(aStream, this); - NS_ENSURE_SUCCESS(rv, rv); - } else { - // Write out an empty classname. This indicates that the binding does not - // define an implementation. - rv = aStream->WriteUtf8Z(EmptyString().get()); - NS_ENSURE_SUCCESS(rv, rv); - } - - // Write out the handlers. - nsXBLPrototypeHandler* handler = mPrototypeHandler; - while (handler) { - rv = handler->Write(aStream); - NS_ENSURE_SUCCESS(rv, rv); - - handler = handler->GetNextHandler(); - } - - aStream->Write8(XBLBinding_Serialize_NoMoreItems); - NS_ENSURE_SUCCESS(rv, rv); - - if (mBinding) { - uint32_t attributes = mBinding->GetAttrCount(); - nsAutoString attrValue; - for (uint32_t i = 0; i < attributes; ++i) { - BorrowedAttrInfo attrInfo = mBinding->GetAttrInfoAt(i); - const nsAttrName* name = attrInfo.mName; - nsDependentAtomString attrName(attrInfo.mName->LocalName()); - attrInfo.mValue->ToString(attrValue); - - rv = aStream->Write8(XBLBinding_Serialize_Attribute); - NS_ENSURE_SUCCESS(rv, rv); - - rv = WriteNamespace(aStream, name->NamespaceID()); - NS_ENSURE_SUCCESS(rv, rv); - - nsAtom* prefix = name->GetPrefix(); - nsAutoString prefixString; - if (prefix) { - prefix->ToString(prefixString); - } - - rv = aStream->WriteWStringZ(prefixString.get()); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aStream->WriteWStringZ(attrName.get()); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aStream->WriteWStringZ(attrValue.get()); - NS_ENSURE_SUCCESS(rv, rv); - } - } - - // Write out an end mark at the end. - return aStream->Write8(XBLBinding_Serialize_NoMoreItems); -} - -nsresult nsXBLPrototypeBinding::ReadContentNode(nsIObjectInputStream* aStream, - Document* aDocument, - nsNodeInfoManager* aNim, - nsIContent** aContent) { - *aContent = nullptr; - - int32_t namespaceID; - nsresult rv = ReadNamespace(aStream, namespaceID); - NS_ENSURE_SUCCESS(rv, rv); - - // There is no content to read so just return. - if (namespaceID == XBLBinding_Serialize_NoContent) return NS_OK; - - // If this is a text type, just read the string and return. - if (namespaceID == XBLBinding_Serialize_TextNode || - namespaceID == XBLBinding_Serialize_CDATANode || - namespaceID == XBLBinding_Serialize_CommentNode) { - RefPtr<CharacterData> content; - switch (namespaceID) { - case XBLBinding_Serialize_TextNode: - content = new nsTextNode(aNim); - break; - case XBLBinding_Serialize_CDATANode: - content = new CDATASection(aNim); - break; - case XBLBinding_Serialize_CommentNode: - content = new Comment(aNim); - break; - default: - break; - } - - nsAutoString text; - rv = aStream->ReadString(text); - NS_ENSURE_SUCCESS(rv, rv); - - content->SetText(text, false); - content.forget(aContent); - return NS_OK; - } - - // Otherwise, it's an element, so read its tag, attributes and children. - nsAutoString prefix, tag; - rv = aStream->ReadString(prefix); - NS_ENSURE_SUCCESS(rv, rv); - - RefPtr<nsAtom> prefixAtom; - if (!prefix.IsEmpty()) prefixAtom = NS_Atomize(prefix); - - rv = aStream->ReadString(tag); - NS_ENSURE_SUCCESS(rv, rv); - - RefPtr<nsAtom> tagAtom = NS_Atomize(tag); - RefPtr<NodeInfo> nodeInfo = aNim->GetNodeInfo( - tagAtom, prefixAtom, namespaceID, nsINode::ELEMENT_NODE); - - uint32_t attrCount; - rv = aStream->Read32(&attrCount); - NS_ENSURE_SUCCESS(rv, rv); - - RefPtr<Element> element; - // Create XUL prototype elements, or regular elements for other namespaces. - // This needs to match the code in nsXBLContentSink::CreateElement. -#ifdef MOZ_XUL - if (namespaceID == kNameSpaceID_XUL) { - nsIURI* documentURI = aDocument->GetDocumentURI(); - - RefPtr<nsXULPrototypeElement> prototype = - new nsXULPrototypeElement(nodeInfo); - - nsXULPrototypeAttribute* attrs = nullptr; - if (attrCount > 0) { - attrs = prototype->mAttributes.AppendElements(attrCount); - } - - for (uint32_t i = 0; i < attrCount; i++) { - rv = ReadNamespace(aStream, namespaceID); - NS_ENSURE_SUCCESS(rv, rv); - - nsAutoString prefix, name, val; - rv = aStream->ReadString(prefix); - NS_ENSURE_SUCCESS(rv, rv); - rv = aStream->ReadString(name); - NS_ENSURE_SUCCESS(rv, rv); - rv = aStream->ReadString(val); - NS_ENSURE_SUCCESS(rv, rv); - - RefPtr<nsAtom> nameAtom = NS_Atomize(name); - if (namespaceID == kNameSpaceID_None) { - attrs[i].mName.SetTo(nameAtom); - } else { - RefPtr<nsAtom> prefixAtom; - if (!prefix.IsEmpty()) prefixAtom = NS_Atomize(prefix); - - RefPtr<NodeInfo> ni = aNim->GetNodeInfo( - nameAtom, prefixAtom, namespaceID, nsINode::ATTRIBUTE_NODE); - attrs[i].mName.SetTo(ni); - } - - rv = prototype->SetAttrAt(i, val, documentURI); - NS_ENSURE_SUCCESS(rv, rv); - } - - nsresult rv = nsXULElement::CreateFromPrototype( - prototype, aDocument, false, false, getter_AddRefs(element)); - NS_ENSURE_SUCCESS(rv, rv); - } else { -#endif - NS_NewElement(getter_AddRefs(element), nodeInfo.forget(), NOT_FROM_PARSER); - - for (uint32_t i = 0; i < attrCount; i++) { - rv = ReadNamespace(aStream, namespaceID); - NS_ENSURE_SUCCESS(rv, rv); - - nsAutoString prefix, name, val; - rv = aStream->ReadString(prefix); - NS_ENSURE_SUCCESS(rv, rv); - rv = aStream->ReadString(name); - NS_ENSURE_SUCCESS(rv, rv); - rv = aStream->ReadString(val); - NS_ENSURE_SUCCESS(rv, rv); - - RefPtr<nsAtom> prefixAtom; - if (!prefix.IsEmpty()) prefixAtom = NS_Atomize(prefix); - - RefPtr<nsAtom> nameAtom = NS_Atomize(name); - element->SetAttr(namespaceID, nameAtom, prefixAtom, val, false); - } - -#ifdef MOZ_XUL - } -#endif - - // Now read the attribute forwarding entries (xbl:inherits) - - int32_t srcNamespaceID, destNamespaceID; - rv = ReadNamespace(aStream, srcNamespaceID); - NS_ENSURE_SUCCESS(rv, rv); - - while (srcNamespaceID != XBLBinding_Serialize_NoMoreAttributes) { - nsAutoString srcAttribute, destAttribute; - rv = aStream->ReadString(srcAttribute); - NS_ENSURE_SUCCESS(rv, rv); - rv = ReadNamespace(aStream, destNamespaceID); - NS_ENSURE_SUCCESS(rv, rv); - rv = aStream->ReadString(destAttribute); - NS_ENSURE_SUCCESS(rv, rv); - - RefPtr<nsAtom> srcAtom = NS_Atomize(srcAttribute); - RefPtr<nsAtom> destAtom = NS_Atomize(destAttribute); - - EnsureAttributeTable(); - AddToAttributeTable(srcNamespaceID, srcAtom, destNamespaceID, destAtom, - element); - - rv = ReadNamespace(aStream, srcNamespaceID); - NS_ENSURE_SUCCESS(rv, rv); - } - - // Finally, read in the child nodes. - uint32_t childCount; - rv = aStream->Read32(&childCount); - NS_ENSURE_SUCCESS(rv, rv); - - for (uint32_t i = 0; i < childCount; i++) { - nsCOMPtr<nsIContent> child; - ReadContentNode(aStream, aDocument, aNim, getter_AddRefs(child)); - - // Child may be null if this was a comment for example and can just be - // ignored. - if (child) { - element->AppendChildTo(child, false); - } - } - - element.forget(aContent); - return NS_OK; -} - -nsresult nsXBLPrototypeBinding::WriteContentNode(nsIObjectOutputStream* aStream, - nsIContent* aNode) { - nsresult rv; - - if (!aNode->IsElement()) { - // Text is writen out as a single byte for the type, followed by the text. - uint8_t type = XBLBinding_Serialize_NoContent; - switch (aNode->NodeType()) { - case nsINode::TEXT_NODE: - type = XBLBinding_Serialize_TextNode; - break; - case nsINode::CDATA_SECTION_NODE: - type = XBLBinding_Serialize_CDATANode; - break; - case nsINode::COMMENT_NODE: - type = XBLBinding_Serialize_CommentNode; - break; - default: - break; - } - - rv = aStream->Write8(type); - NS_ENSURE_SUCCESS(rv, rv); - - nsAutoString content; - aNode->GetText()->AppendTo(content); - return aStream->WriteWStringZ(content.get()); - } - - // Otherwise, this is an element. - Element* element = aNode->AsElement(); - - // Write the namespace id followed by the tag name - rv = WriteNamespace(aStream, element->GetNameSpaceID()); - NS_ENSURE_SUCCESS(rv, rv); - - nsAutoString prefixStr; - element->NodeInfo()->GetPrefix(prefixStr); - rv = aStream->WriteWStringZ(prefixStr.get()); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aStream->WriteWStringZ( - nsDependentAtomString(element->NodeInfo()->NameAtom()).get()); - NS_ENSURE_SUCCESS(rv, rv); - - // Write attributes - uint32_t count = element->GetAttrCount(); - rv = aStream->Write32(count); - NS_ENSURE_SUCCESS(rv, rv); - - uint32_t i; - for (i = 0; i < count; i++) { - // Write out the namespace id, the namespace prefix, the local tag name, - // and the value, in that order. - - const BorrowedAttrInfo attrInfo = element->GetAttrInfoAt(i); - const nsAttrName* name = attrInfo.mName; - - // XXXndeakin don't write out xbl:inherits? - int32_t namespaceID = name->NamespaceID(); - rv = WriteNamespace(aStream, namespaceID); - NS_ENSURE_SUCCESS(rv, rv); - - nsAutoString prefixStr; - nsAtom* prefix = name->GetPrefix(); - if (prefix) prefix->ToString(prefixStr); - rv = aStream->WriteWStringZ(prefixStr.get()); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aStream->WriteWStringZ(nsDependentAtomString(name->LocalName()).get()); - NS_ENSURE_SUCCESS(rv, rv); - - nsAutoString val; - attrInfo.mValue->ToString(val); - rv = aStream->WriteWStringZ(val.get()); - NS_ENSURE_SUCCESS(rv, rv); - } - - // Write out the attribute fowarding information - if (mAttributeTable) { - for (auto iter1 = mAttributeTable->Iter(); !iter1.Done(); iter1.Next()) { - int32_t srcNamespace = iter1.Key(); - InnerAttributeTable* xblAttributes = iter1.UserData(); - - for (auto iter2 = xblAttributes->Iter(); !iter2.Done(); iter2.Next()) { - nsXBLAttributeEntry* entry = iter2.UserData(); - - do { - if (entry->GetElement() == element) { - WriteNamespace(aStream, srcNamespace); - aStream->WriteWStringZ( - nsDependentAtomString(entry->GetSrcAttribute()).get()); - WriteNamespace(aStream, entry->GetDstNameSpace()); - aStream->WriteWStringZ( - nsDependentAtomString(entry->GetDstAttribute()).get()); - } - - entry = entry->GetNext(); - } while (entry); - } - } - } - rv = aStream->Write8(XBLBinding_Serialize_NoMoreAttributes); - NS_ENSURE_SUCCESS(rv, rv); - - // Finally, write out the child nodes. - count = element->GetChildCount(); - rv = aStream->Write32(count); - NS_ENSURE_SUCCESS(rv, rv); - - for (i = 0; i < count; i++) { - rv = WriteContentNode(aStream, element->GetChildAt_Deprecated(i)); - NS_ENSURE_SUCCESS(rv, rv); - } - - return NS_OK; -} - -nsresult nsXBLPrototypeBinding::ReadNamespace(nsIObjectInputStream* aStream, - int32_t& aNameSpaceID) { - uint8_t namespaceID; - nsresult rv = aStream->Read8(&namespaceID); - NS_ENSURE_SUCCESS(rv, rv); - - if (namespaceID == XBLBinding_Serialize_CustomNamespace) { - nsAutoString namesp; - rv = aStream->ReadString(namesp); - NS_ENSURE_SUCCESS(rv, rv); - - nsContentUtils::NameSpaceManager()->RegisterNameSpace(namesp, aNameSpaceID); - } else { - aNameSpaceID = namespaceID; - } - - return NS_OK; -} - -nsresult nsXBLPrototypeBinding::WriteNamespace(nsIObjectOutputStream* aStream, - int32_t aNameSpaceID) { - // Namespaces are stored as a single byte id for well-known namespaces. - // This saves time and space as other namespaces aren't very common in - // XBL. If another namespace is used however, the namespace id will be - // XBLBinding_Serialize_CustomNamespace and the string namespace written - // out directly afterwards. - nsresult rv; - - if (aNameSpaceID <= kNameSpaceID_LastBuiltin) { - rv = aStream->Write8((int8_t)aNameSpaceID); - NS_ENSURE_SUCCESS(rv, rv); - } else { - rv = aStream->Write8(XBLBinding_Serialize_CustomNamespace); - NS_ENSURE_SUCCESS(rv, rv); - - nsAutoString namesp; - nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNameSpaceID, namesp); - aStream->WriteWStringZ(namesp.get()); - } - - return NS_OK; -} - -nsresult nsXBLPrototypeBinding::ResolveBaseBinding() { - if (mCheckedBaseProto) return NS_OK; - mCheckedBaseProto = true; - - RefPtr<Document> doc = mXBLDocInfoWeak->GetDocument(); - - NS_WARNING_ASSERTION(!mBinding->HasAttr(nsGkAtoms::display), - "display is no longer supported"); - - // Check for the presence of 'extends'. - nsAutoString extends; - mBinding->GetAttr(kNameSpaceID_None, nsGkAtoms::extends, extends); - if (extends.IsEmpty()) { - return NS_OK; - } - - return NS_NewURI(getter_AddRefs(mBaseBindingURI), extends, - doc->GetDocumentCharacterSet(), doc->GetDocBaseURI()); -} - -size_t nsXBLPrototypeBinding::SizeOfIncludingThis( - MallocSizeOf aMallocSizeOf) const { - size_t n = aMallocSizeOf(this); - n += mPrototypeHandler ? mPrototypeHandler->SizeOfIncludingThis(aMallocSizeOf) - : 0; - - if (mAttributeTable) { - n += mAttributeTable->ShallowSizeOfIncludingThis(aMallocSizeOf); - for (auto iter = mAttributeTable->Iter(); !iter.Done(); iter.Next()) { - InnerAttributeTable* table = iter.UserData(); - n += table->ShallowSizeOfIncludingThis(aMallocSizeOf); - for (auto iter2 = table->Iter(); !iter2.Done(); iter2.Next()) { - n += iter2.UserData()->SizeOfIncludingThis(aMallocSizeOf); - } - } - } - - n += mInterfaceTable.ShallowSizeOfExcludingThis(aMallocSizeOf); - n += mKeyHandlers.ShallowSizeOfExcludingThis(aMallocSizeOf); - - // Measurement of the following members may be added later if DMD finds it - // is worthwhile: - // - mBindingURI - // - mAlternateBindingURI - // - mBaseBindingURI - // - mImplementation - - return n; -}
deleted file mode 100644 --- a/dom/xbl/nsXBLPrototypeBinding.h +++ /dev/null @@ -1,319 +0,0 @@ -/* -*- 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 nsXBLPrototypeBinding_h__ -#define nsXBLPrototypeBinding_h__ - -#include "nsAutoPtr.h" -#include "nsClassHashtable.h" -#include "nsCOMArray.h" -#include "nsCOMPtr.h" -#include "nsICSSLoaderObserver.h" -#include "nsInterfaceHashtable.h" -#include "nsXBLDocumentInfo.h" -#include "nsXBLProtoImpl.h" -#include "nsXBLProtoImplMethod.h" -#include "nsXBLPrototypeHandler.h" -#include "mozilla/MemoryReporting.h" -#include "mozilla/WeakPtr.h" - -class nsAtom; -class nsIContent; - -class nsXBLAttributeEntry; -class nsXBLBinding; -class nsXBLProtoImplField; - -// *********************************************************************/ -// The XBLPrototypeBinding class - -// Instances of this class are owned by the nsXBLDocumentInfo object returned -// by XBLDocumentInfo(). Consumers who want to refcount things should refcount -// that. -class nsXBLPrototypeBinding final - : public mozilla::SupportsWeakPtr<nsXBLPrototypeBinding> { - public: - MOZ_DECLARE_WEAKREFERENCE_TYPENAME(nsXBLPrototypeBinding) - - mozilla::dom::Element* GetBindingElement() const { return mBinding; } - void SetBindingElement(mozilla::dom::Element* aElement); - - nsIURI* BindingURI() const { return mBindingURI; } - nsIURI* AlternateBindingURI() const { return mAlternateBindingURI; } - nsIURI* DocURI() const { return mXBLDocInfoWeak->DocumentURI(); } - nsIURI* GetBaseBindingURI() const { return mBaseBindingURI; } - - // Checks if aURI refers to this binding by comparing to both possible - // binding URIs. - bool CompareBindingURI(nsIURI* aURI) const; - - bool GetAllowScripts() const; - - nsresult BindingAttached(nsIContent* aBoundElement); - nsresult BindingDetached(nsIContent* aBoundElement); - - nsXBLPrototypeHandler* GetPrototypeHandlers() { return mPrototypeHandler; } - void SetPrototypeHandlers(nsXBLPrototypeHandler* aHandler) { - mPrototypeHandler = aHandler; - } - - nsXBLProtoImplAnonymousMethod* GetConstructor(); - nsresult SetConstructor(nsXBLProtoImplAnonymousMethod* aConstructor); - nsXBLProtoImplAnonymousMethod* GetDestructor(); - nsresult SetDestructor(nsXBLProtoImplAnonymousMethod* aDestructor); - - nsXBLProtoImplField* FindField(const nsString& aFieldName) const { - return mImplementation ? mImplementation->FindField(aFieldName) : nullptr; - } - - // Resolve all the fields for this binding on the object |obj|. - // False return means a JS exception was set. - bool ResolveAllFields(JSContext* cx, JS::Handle<JSObject*> obj) const { - return !mImplementation || mImplementation->ResolveAllFields(cx, obj); - } - - // Undefine all our fields from object |obj| (which should be a - // JSObject for a bound element). - void UndefineFields(JSContext* cx, JS::Handle<JSObject*> obj) const { - if (mImplementation) { - mImplementation->UndefineFields(cx, obj); - } - } - - const nsString& ClassName() const { - return mImplementation ? mImplementation->mClassName : EmptyString(); - } - - nsresult InitClass(const nsString& aClassName, JSContext* aContext, - JS::Handle<JSObject*> aScriptObject, - JS::MutableHandle<JSObject*> aClassObject, bool* aNew); - - nsresult ConstructInterfaceTable(const nsAString& aImpls); - - void SetImplementation(nsXBLProtoImpl* aImpl) { mImplementation = aImpl; } - nsXBLProtoImpl* GetImplementation() { return mImplementation; } - nsresult InstallImplementation(nsXBLBinding* aBinding); - bool HasImplementation() const { return mImplementation != nullptr; } - - void AttributeChanged(nsAtom* aAttribute, int32_t aNameSpaceID, - bool aRemoveFlag, - mozilla::dom::Element* aChangedElement, - nsIContent* aAnonymousContent, bool aNotify); - - void SetBasePrototype(nsXBLPrototypeBinding* aBinding); - nsXBLPrototypeBinding* GetBasePrototype() { return mBaseBinding; } - - nsXBLDocumentInfo* XBLDocumentInfo() const { return mXBLDocInfoWeak; } - bool IsChrome() { return mXBLDocInfoWeak->IsChrome(); } - - void SetInitialAttributes(mozilla::dom::Element* aBoundElement, - nsIContent* aAnonymousContent); - - nsAtom* GetBaseTag(int32_t* aNamespaceID); - void SetBaseTag(int32_t aNamespaceID, nsAtom* aTag); - - bool ImplementsInterface(REFNSIID aIID) const; - - void Initialize(); - - nsresult ResolveBaseBinding(); - - const nsCOMArray<nsXBLKeyEventHandler>* GetKeyEventHandlers() { - if (!mKeyHandlersRegistered) { - CreateKeyHandlers(); - mKeyHandlersRegistered = true; - } - - return &mKeyHandlers; - } - - private: - nsresult Read(nsIObjectInputStream* aStream, nsXBLDocumentInfo* aDocInfo, - mozilla::dom::Document* aDocument, uint8_t aFlags); - - /** - * Read a new binding from the stream aStream into the xbl document aDocument. - * aDocInfo should be the xbl document info for the binding document. - * aFlags can contain XBLBinding_Serialize_InheritStyle to indicate that - * mInheritStyle flag should be set, and XBLBinding_Serialize_IsFirstBinding - * to indicate the first binding in a document. - * XBLBinding_Serialize_BindToUntrustedContent indicates that - * nsXBLPrototypeBinding::mBindToUntrustedContent should be true. - */ - public: - static nsresult ReadNewBinding(nsIObjectInputStream* aStream, - nsXBLDocumentInfo* aDocInfo, - mozilla::dom::Document* aDocument, - uint8_t aFlags); - - /** - * Write this binding to the stream. - */ - nsresult Write(nsIObjectOutputStream* aStream); - - /** - * Read a content node from aStream and return it in aChild. - * aDocument and aNim are the document and node info manager for the document - * the child will be inserted into. - */ - nsresult ReadContentNode(nsIObjectInputStream* aStream, - mozilla::dom::Document* aDocument, - nsNodeInfoManager* aNim, nsIContent** aChild); - - /** - * Write the content node aNode to aStream. - * - * This method is called recursively for each child descendant. For the - * topmost call, aNode must be an element. - * - * Text, CDATA and comment nodes are serialized as: - * the constant XBLBinding_Serialize_TextNode, - * XBLBinding_Serialize_CDATANode or XBLBinding_Serialize_CommentNode the text - * for the node Elements are serialized in the following format: node's - * namespace, written with WriteNamespace node's namespace prefix node's tag - * 32-bit attribute count - * table of attributes: - * attribute's namespace, written with WriteNamespace - * attribute's namespace prefix - * attribute's tag - * attribute's value - * attribute forwarding table: - * source namespace - * source attribute - * destination namespace - * destination attribute - * the constant XBLBinding_Serialize_NoMoreAttributes - * 32-bit count of the number of child nodes - * each child node is serialized in the same manner in sequence - * the constant XBLBinding_Serialize_NoContent - */ - nsresult WriteContentNode(nsIObjectOutputStream* aStream, nsIContent* aNode); - - /** - * Read or write a namespace id from or to aStream. If the namespace matches - * one of the built-in ones defined in nsNameSpaceManager.h, it will be - * written as a single byte with that value. Otherwise, - * XBLBinding_Serialize_CustomNamespace is written out, followed by a string - * written with writeWStringZ. - */ - nsresult ReadNamespace(nsIObjectInputStream* aStream, int32_t& aNameSpaceID); - nsresult WriteNamespace(nsIObjectOutputStream* aStream, int32_t aNameSpaceID); - - public: - nsXBLPrototypeBinding(); - ~nsXBLPrototypeBinding(); - - // Init must be called after construction to initialize the prototype - // binding. It may well throw errors (eg on out-of-memory). Do not confuse - // this with the Initialize() method, which must be called after the - // binding's handlers, properties, etc are all set. - nsresult Init(const nsACString& aRef, nsXBLDocumentInfo* aInfo, - mozilla::dom::Element* aElement, bool aFirstBinding = false); - - void Traverse(nsCycleCollectionTraversalCallback& cb) const; - void Unlink(); - void Trace(const TraceCallbacks& aCallbacks, void* aClosure) const; - - size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; - - // Internal member functions. - public: - /** - * GetImmediateChild locates the immediate child of our binding element which - * has the localname given by aTag and is in the XBL namespace. - */ - mozilla::dom::Element* GetImmediateChild(nsAtom* aTag); - mozilla::dom::Element* LocateInstance(mozilla::dom::Element* aBoundElt, - nsIContent* aTemplRoot, - nsIContent* aCopyRoot, - mozilla::dom::Element* aTemplChild); - - bool SimpleScopeChain() const { return mSimpleScopeChain; } - bool BindToUntrustedContent() const { return mBindToUntrustedContent; } - - typedef nsClassHashtable<nsRefPtrHashKey<nsAtom>, nsXBLAttributeEntry> - InnerAttributeTable; - - protected: - // Ensure that mAttributeTable has been created. - void EnsureAttributeTable(); - // Ad an entry to the attribute table - void AddToAttributeTable(int32_t aSourceNamespaceID, nsAtom* aSourceTag, - int32_t aDestNamespaceID, nsAtom* aDestTag, - mozilla::dom::Element* aContent); - void ConstructAttributeTable(mozilla::dom::Element* aElement); - void CreateKeyHandlers(); - - // MEMBER VARIABLES - protected: - nsCOMPtr<nsIURI> mBindingURI; - nsCOMPtr<nsIURI> mAlternateBindingURI; // Alternate id-less URI that is only - // non-null on the first binding. - RefPtr<mozilla::dom::Element> - mBinding; // Strong. We own a ref to our content element in the binding - // doc. - nsAutoPtr<nsXBLPrototypeHandler> - mPrototypeHandler; // Strong. DocInfo owns us, and we own the handlers. - - // the url of the base binding - nsCOMPtr<nsIURI> mBaseBindingURI; - - nsXBLProtoImpl* mImplementation; // Our prototype implementation (includes - // methods, properties, fields, the - // constructor, and the destructor). - - // Weak. The docinfo will own our base binding. - mozilla::WeakPtr<nsXBLPrototypeBinding> mBaseBinding; - bool mCheckedBaseProto; - bool mKeyHandlersRegistered; - // FIXME(emilio): This is dead code now. - bool mBindToUntrustedContent; - // True if constructors, handlers, etc for this binding would skip the scope - // chain for parent elements and go directly to the document. - // FIXME(emilio): This is dead code now. - bool mSimpleScopeChain; - - nsXBLDocumentInfo* mXBLDocInfoWeak; // A pointer back to our doc info. Weak, - // since it owns us. - - // A table for attribute containers. Namespace IDs are used as - // keys in the table. Containers are InnerAttributeTables. - // This table is used to efficiently handle attribute changes. - nsAutoPtr<nsClassHashtable<nsUint32HashKey, InnerAttributeTable>> - mAttributeTable; - - class IIDHashKey : public PLDHashEntryHdr { - public: - typedef const nsIID& KeyType; - typedef const nsIID* KeyTypePointer; - - explicit IIDHashKey(const nsIID* aKey) : mKey(*aKey) {} - IIDHashKey(const IIDHashKey& aOther) : mKey(aOther.GetKey()) {} - ~IIDHashKey() {} - - KeyType GetKey() const { return mKey; } - bool KeyEquals(const KeyTypePointer aKey) const { - return mKey.Equals(*aKey); - } - - static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; } - static PLDHashNumber HashKey(const KeyTypePointer aKey) { - // Just use the 32-bit m0 field. - return aKey->m0; - } - - enum { ALLOW_MEMMOVE = true }; - - private: - nsIID mKey; - }; - nsInterfaceHashtable<IIDHashKey, nsIContent> - mInterfaceTable; // A table of cached interfaces that we support. - - nsCOMArray<nsXBLKeyEventHandler> mKeyHandlers; -}; - -#endif
deleted file mode 100644 --- a/dom/xbl/nsXBLPrototypeHandler.cpp +++ /dev/null @@ -1,1058 +0,0 @@ -/* -*- 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" - -#include "nsCOMPtr.h" -#include "nsQueryObject.h" -#include "nsXBLPrototypeHandler.h" -#include "nsXBLPrototypeBinding.h" -#include "nsContentUtils.h" -#include "nsGlobalWindow.h" -#include "nsGlobalWindowCommands.h" -#include "nsIContent.h" -#include "nsAtom.h" -#include "nsNameSpaceManager.h" -#include "mozilla/dom/Document.h" -#include "nsIController.h" -#include "nsIControllers.h" -#include "nsXULElement.h" -#include "nsIURI.h" -#include "nsFocusManager.h" -#include "nsIFormControl.h" -#include "nsIDOMEventListener.h" -#include "nsPIDOMWindow.h" -#include "nsPIWindowRoot.h" -#include "nsIServiceManager.h" -#include "nsIScriptError.h" -#include "nsIWeakReferenceUtils.h" -#include "nsString.h" -#include "nsReadableUtils.h" -#include "nsGkAtoms.h" -#include "nsIXPConnect.h" -#include "nsDOMCID.h" -#include "nsUnicharUtils.h" -#include "nsCRT.h" -#include "nsXBLEventHandler.h" -#include "nsXBLSerialize.h" -#include "nsJSUtils.h" -#include "mozilla/BasicEvents.h" -#include "mozilla/JSEventHandler.h" -#include "mozilla/Preferences.h" -#include "mozilla/TextEvents.h" -#include "mozilla/dom/Element.h" -#include "mozilla/dom/Event.h" -#include "mozilla/dom/EventHandlerBinding.h" -#include "mozilla/dom/HTMLInputElement.h" -#include "mozilla/dom/HTMLTextAreaElement.h" -#include "mozilla/dom/KeyboardEvent.h" -#include "mozilla/dom/KeyboardEventBinding.h" -#include "mozilla/dom/MouseEvent.h" -#include "mozilla/dom/ScriptSettings.h" -#include "mozilla/layers/KeyboardMap.h" -#include "xpcpublic.h" - -using namespace mozilla; -using namespace mozilla::dom; -using namespace mozilla::layers; - -uint32_t nsXBLPrototypeHandler::gRefCnt = 0; - -int32_t nsXBLPrototypeHandler::kMenuAccessKey = -1; - -const int32_t nsXBLPrototypeHandler::cShift = (1 << 0); -const int32_t nsXBLPrototypeHandler::cAlt = (1 << 1); -const int32_t nsXBLPrototypeHandler::cControl = (1 << 2); -const int32_t nsXBLPrototypeHandler::cMeta = (1 << 3); -const int32_t nsXBLPrototypeHandler::cOS = (1 << 4); - -const int32_t nsXBLPrototypeHandler::cShiftMask = (1 << 5); -const int32_t nsXBLPrototypeHandler::cAltMask = (1 << 6); -const int32_t nsXBLPrototypeHandler::cControlMask = (1 << 7); -const int32_t nsXBLPrototypeHandler::cMetaMask = (1 << 8); -const int32_t nsXBLPrototypeHandler::cOSMask = (1 << 9); - -const int32_t nsXBLPrototypeHandler::cAllModifiers = - cShiftMask | cAltMask | cControlMask | cMetaMask | cOSMask; - -nsXBLPrototypeHandler::nsXBLPrototypeHandler( - const char16_t* aEvent, const char16_t* aPhase, const char16_t* aAction, - const char16_t* aCommand, const char16_t* aKeyCode, - const char16_t* aCharCode, const char16_t* aModifiers, - const char16_t* aButton, const char16_t* aClickCount, - const char16_t* aGroup, const char16_t* aPreventDefault, - const char16_t* aAllowUntrusted, nsXBLPrototypeBinding* aBinding, - uint32_t aLineNumber) - : mHandlerText(nullptr), - mLineNumber(aLineNumber), - mReserved(XBLReservedKey_False), - mNextHandler(nullptr), - mPrototypeBinding(aBinding) { - Init(); - - ConstructPrototype(nullptr, aEvent, aPhase, aAction, aCommand, aKeyCode, - aCharCode, aModifiers, aButton, aClickCount, aGroup, - aPreventDefault, aAllowUntrusted); -} - -nsXBLPrototypeHandler::nsXBLPrototypeHandler(Element* aHandlerElement, - XBLReservedKey aReserved) - : mHandlerElement(nullptr), - mLineNumber(0), - mReserved(aReserved), - mNextHandler(nullptr), - mPrototypeBinding(nullptr) { - Init(); - - // Make sure our prototype is initialized. - ConstructPrototype(aHandlerElement); -} - -nsXBLPrototypeHandler::nsXBLPrototypeHandler(nsXBLPrototypeBinding* aBinding) - : mHandlerText(nullptr), - mLineNumber(0), - mPhase(0), - mType(0), - mMisc(0), - mReserved(XBLReservedKey_False), - mKeyMask(0), - mDetail(0), - mNextHandler(nullptr), - mPrototypeBinding(aBinding) { - Init(); -} - -nsXBLPrototypeHandler::~nsXBLPrototypeHandler() { - --gRefCnt; - if (mType & NS_HANDLER_TYPE_XUL) { - NS_IF_RELEASE(mHandlerElement); - } else if (mHandlerText) { - free(mHandlerText); - } - - // We own the next handler in the chain, so delete it now. - NS_CONTENT_DELETE_LIST_MEMBER(nsXBLPrototypeHandler, this, mNextHandler); -} - -bool nsXBLPrototypeHandler::TryConvertToKeyboardShortcut( - KeyboardShortcut* aOut) const { - // Convert the event type - KeyboardInput::KeyboardEventType eventType; - - if (mEventName == nsGkAtoms::keydown) { - eventType = KeyboardInput::KEY_DOWN; - } else if (mEventName == nsGkAtoms::keypress) { - eventType = KeyboardInput::KEY_PRESS; - } else if (mEventName == nsGkAtoms::keyup) { - eventType = KeyboardInput::KEY_UP; - } else { - return false; - } - - // Convert the modifiers - Modifiers modifiersMask = GetModifiersMask(); - Modifiers modifiers = GetModifiers(); - - // Mask away any bits that won't be compared - modifiers &= modifiersMask; - - // Convert the keyCode or charCode - uint32_t keyCode; - uint32_t charCode; - - if (mMisc) { - keyCode = 0; - charCode = static_cast<uint32_t>(mDetail); - } else { - keyCode = static_cast<uint32_t>(mDetail); - charCode = 0; - } - - NS_LossyConvertUTF16toASCII commandText(mHandlerText); - KeyboardScrollAction action; - if (!nsGlobalWindowCommands::FindScrollCommand(commandText.get(), &action)) { - // This action doesn't represent a scroll so we need to create a dispatch - // to content keyboard shortcut so APZ handles this command correctly - *aOut = KeyboardShortcut(eventType, keyCode, charCode, modifiers, - modifiersMask); - return true; - } - - // This prototype is a command which represents a scroll action, so create - // a keyboard shortcut to handle it - *aOut = KeyboardShortcut(eventType, keyCode, charCode, modifiers, - modifiersMask, action); - return true; -} - -already_AddRefed<Element> nsXBLPrototypeHandler::GetHandlerElement() { - if (mType & NS_HANDLER_TYPE_XUL) { - nsCOMPtr<Element> element = do_QueryReferent(mHandlerElement); - return element.forget(); - } - - return nullptr; -} - -void nsXBLPrototypeHandler::AppendHandlerText(const nsAString& aText) { - if (mHandlerText) { - // Append our text to the existing text. - char16_t* temp = mHandlerText; - mHandlerText = ToNewUnicode(nsDependentString(temp) + aText); - free(temp); - } else { - mHandlerText = ToNewUnicode(aText); - } -} - -///////////////////////////////////////////////////////////////////////////// -// Get the menu access key from prefs. -// XXX Eventually pick up using CSS3 key-equivalent property or somesuch -void nsXBLPrototypeHandler::InitAccessKeys() { - if (kMenuAccessKey >= 0) { - return; - } - - // Compiled-in defaults, in case we can't get the pref -- - // mac doesn't have menu shortcuts, other platforms use alt. -#ifdef XP_MACOSX - kMenuAccessKey = 0; -#else - kMenuAccessKey = KeyboardEvent_Binding::DOM_VK_ALT; -#endif - - // Get the menu access key value from prefs, overriding the default: - kMenuAccessKey = Preferences::GetInt("ui.key.menuAccessKey", kMenuAccessKey); -} - -nsresult nsXBLPrototypeHandler::ExecuteHandler(EventTarget* aTarget, - Event* aEvent) { - nsresult rv = NS_ERROR_FAILURE; - - // Prevent default action? - if (mType & NS_HANDLER_TYPE_PREVENTDEFAULT) { - aEvent->PreventDefault(); - // If we prevent default, then it's okay for - // mHandlerElement and mHandlerText to be null - rv = NS_OK; - } - - if (!mHandlerElement) // This works for both types of handlers. In both - // cases, the union's var should be defined. - return rv; - - // See if our event receiver is a content node (and not us). - bool isXULKey = !!(mType & NS_HANDLER_TYPE_XUL); - bool isXBLCommand = !!(mType & NS_HANDLER_TYPE_XBL_COMMAND); - NS_ASSERTION(!(isXULKey && isXBLCommand), - "can't be both a key and xbl command handler"); - - // XUL handlers and commands shouldn't be triggered by non-trusted - // events. - if (isXULKey || isXBLCommand) { - if (!aEvent->IsTrusted()) return NS_OK; - } - - if (isXBLCommand) { - return DispatchXBLCommand(aTarget, aEvent); - } - - // If we're executing on a XUL key element, just dispatch a command - // event at the element. It will take care of retargeting it to its - // command element, if applicable, and executing the event handler. - if (isXULKey) { - return DispatchXULKeyCommand(aEvent); - } - - // Look for a compiled handler on the element. - // Should be compiled and bound with "on" in front of the name. - RefPtr<nsAtom> onEventAtom = NS_Atomize(NS_LITERAL_STRING("onxbl") + - nsDependentAtomString(mEventName)); - - // Compile the handler and bind it to the element. - nsCOMPtr<nsIScriptGlobalObject> boundGlobal; - nsCOMPtr<nsPIWindowRoot> winRoot = do_QueryInterface(aTarget); - if (winRoot) { - if (nsCOMPtr<nsPIDOMWindowOuter> window = winRoot->GetWindow()) { - NS_ENSURE_TRUE(window->GetCurrentInnerWindow(), NS_ERROR_UNEXPECTED); - - boundGlobal = do_QueryInterface(window->GetPrivateRoot()); - } - } else - boundGlobal = do_QueryInterface(aTarget); - - if (!boundGlobal) { - nsCOMPtr<Document> boundDocument(do_QueryInterface(aTarget)); - if (!boundDocument) { - // We must be an element. - nsCOMPtr<nsIContent> content(do_QueryInterface(aTarget)); - if (!content) return NS_OK; - boundDocument = content->OwnerDoc(); - } - - boundGlobal = do_QueryInterface(boundDocument->GetScopeObject()); - } - - if (!boundGlobal) return NS_OK; - - nsISupports* scriptTarget; - - if (winRoot) { - scriptTarget = boundGlobal; - } else { - scriptTarget = aTarget; - } - - // We're about to create a new JSEventHandler, which means that we need to - // Initiatize an AutoJSAPI with aTarget's bound global to make sure any errors - // are reported to the correct place. - AutoJSAPI jsapi; - if (NS_WARN_IF(!jsapi.Init(boundGlobal))) { - return NS_OK; - } - JSContext* cx = jsapi.cx(); - JS::Rooted<JSObject*> handler(cx); - - rv = EnsureEventHandler(jsapi, onEventAtom, &handler); - NS_ENSURE_SUCCESS(rv, rv); - - JS::Rooted<JSObject*> scopeObject( - cx, xpc::GetXBLScopeOrGlobal(cx, boundGlobal->GetGlobalJSObject())); - NS_ENSURE_TRUE(scopeObject, NS_ERROR_OUT_OF_MEMORY); - - // Bind it to the bound element. Note that if we're using a separate XBL - // scope, we'll actually be binding the event handler to a cross-compartment - // wrapper to the bound element's reflector. - - // First, enter our XBL scope. This is where the generic handler should have - // been compiled, above. - JSAutoRealm ar(cx, scopeObject); - JS::Rooted<JSObject*> genericHandler(cx, handler.get()); - bool ok = JS_WrapObject(cx, &genericHandler); - NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY); - MOZ_ASSERT(!js::IsCrossCompartmentWrapper(genericHandler)); - - // Build a scope chain in the XBL scope. - RefPtr<Element> targetElement = do_QueryObject(scriptTarget); - JS::RootedVector<JSObject*> scopeChain(cx); - ok = nsJSUtils::GetScopeChainForXBL(cx, targetElement, *mPrototypeBinding, - &scopeChain); - NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY); - - // Next, clone the generic handler with our desired scope chain. - JS::Rooted<JSObject*> bound( - cx, JS::CloneFunctionObject(cx, genericHandler, scopeChain)); - NS_ENSURE_TRUE(bound, NS_ERROR_FAILURE); - - RefPtr<EventHandlerNonNull> handlerCallback = new EventHandlerNonNull( - static_cast<JSContext*>(nullptr), bound, scopeObject, - /* aIncumbentGlobal = */ nullptr); - - TypedEventHandler typedHandler(handlerCallback); - - // Execute it. - nsCOMPtr<JSEventHandler> jsEventHandler; - rv = NS_NewJSEventHandler(scriptTarget, onEventAtom, typedHandler, - getter_AddRefs(jsEventHandler)); - NS_ENSURE_SUCCESS(rv, rv); - - // Handle the event. - jsEventHandler->HandleEvent(aEvent); - jsEventHandler->Disconnect(); - return NS_OK; -} - -nsresult nsXBLPrototypeHandler::EnsureEventHandler( - AutoJSAPI& jsapi, nsAtom* aName, JS::MutableHandle<JSObject*> aHandler) { - JSContext* cx = jsapi.cx(); - - // Check to see if we've already compiled this - JS::Rooted<JSObject*> globalObject(cx, JS::CurrentGlobalOrNull(cx)); - nsCOMPtr<nsPIDOMWindowInner> pWindow = xpc::WindowOrNull(globalObject); - if (pWindow) { - JS::Rooted<JSObject*> cachedHandler( - cx, pWindow->GetCachedXBLPrototypeHandler(this)); - if (cachedHandler) { - JS::ExposeObjectToActiveJS(cachedHandler); - aHandler.set(cachedHandler); - NS_ENSURE_TRUE(aHandler, NS_ERROR_FAILURE); - return NS_OK; - } - } - - // Ensure that we have something to compile - nsDependentString handlerText(mHandlerText); - NS_ENSURE_TRUE(!handlerText.IsEmpty(), NS_ERROR_FAILURE); - - JS::Rooted<JSObject*> scopeObject(cx, - xpc::GetXBLScopeOrGlobal(cx, globalObject)); - NS_ENSURE_TRUE(scopeObject, NS_ERROR_OUT_OF_MEMORY); - - nsAutoCString bindingURI; - nsresult rv = mPrototypeBinding->DocURI()->GetSpec(bindingURI); - NS_ENSURE_SUCCESS(rv, rv); - - uint32_t argCount; - const char** argNames; - nsContentUtils::GetEventArgNames(kNameSpaceID_XBL, aName, false, &argCount, - &argNames); - - // Compile the event handler in the xbl scope. - JSAutoRealm ar(cx, scopeObject); - JS::CompileOptions options(cx); - options.setFileAndLine(bindingURI.get(), mLineNumber); - - JS::Rooted<JSObject*> handlerFun(cx); - JS::RootedVector<JSObject*> emptyVector(cx); - rv = nsJSUtils::CompileFunction(jsapi, emptyVector, options, - nsAtomCString(aName), argCount, argNames, - handlerText, handlerFun.address()); - NS_ENSURE_SUCCESS(rv, rv); - NS_ENSURE_TRUE(handlerFun, NS_ERROR_FAILURE); - - // Wrap the handler into the content scope, since we're about to stash it - // on the DOM window and such. - JSAutoRealm ar2(cx, globalObject); - bool ok = JS_WrapObject(cx, &handlerFun); - NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY); - aHandler.set(handlerFun); - NS_ENSURE_TRUE(aHandler, NS_ERROR_FAILURE); - - if (pWindow) { - pWindow->CacheXBLPrototypeHandler(this, aHandler); - } - - return NS_OK; -} - -nsresult nsXBLPrototypeHandler::DispatchXBLCommand(EventTarget* aTarget, - Event* aEvent) { - // This is a special-case optimization to make command handling fast. - // It isn't really a part of XBL, but it helps speed things up. - - if (aEvent) { - // See if preventDefault has been set. If so, don't execute. - if (aEvent->DefaultPrevented()) { - return NS_OK; - } - bool dispatchStopped = aEvent->IsDispatchStopped(); - if (dispatchStopped) { - return NS_OK; - } - } - - // Instead of executing JS, let's get the controller for the bound - // element and call doCommand on it. - nsCOMPtr<nsIController> controller; - - nsCOMPtr<nsPIDOMWindowOuter> privateWindow; - nsCOMPtr<nsPIWindowRoot> windowRoot = do_QueryInterface(aTarget); - if (windowRoot) { - privateWindow = windowRoot->GetWindow(); - } else { - privateWindow = do_QueryInterface(aTarget); - if (!privateWindow) { - nsCOMPtr<nsIContent> elt(do_QueryInterface(aTarget)); - nsCOMPtr<Document> doc; - // XXXbz sXBL/XBL2 issue -- this should be the "scope doc" or - // something... whatever we use when wrapping DOM nodes - // normally. It's not clear that the owner doc is the right - // thing. - if (elt) doc = elt->OwnerDoc(); - - if (!doc) doc = do_QueryInterface(aTarget); - - if (!doc) return NS_ERROR_FAILURE; - - privateWindow = doc->GetWindow(); - if (!privateWindow) return NS_ERROR_FAILURE; - } - - windowRoot = privateWindow->GetTopWindowRoot(); - } - - NS_LossyConvertUTF16toASCII command(mHandlerText); - if (windowRoot) { - // If user tries to do something, user must try to do it in visible window. - // So, let's retrieve controller of visible window. - windowRoot->GetControllerForCommand(command.get(), true, - getter_AddRefs(controller)); - } else { - controller = - GetController(aTarget); // We're attached to the receiver possibly. - } - - // We are the default action for this command. - // Stop any other default action from executing. - aEvent->PreventDefault(); - - if (mEventName == nsGkAtoms::keypress && - mDetail == KeyboardEvent_Binding::DOM_VK_SPACE && mMisc == 1) { - // get the focused element so that we can pageDown only at - // certain times. - - nsCOMPtr<nsPIDOMWindowOuter> windowToCheck; - if (windowRoot) - windowToCheck = windowRoot->GetWindow(); - else - windowToCheck = privateWindow->GetPrivateRoot(); - - nsCOMPtr<nsIContent> focusedContent; - if (windowToCheck) { - nsCOMPtr<nsPIDOMWindowOuter> focusedWindow; - focusedContent = nsFocusManager::GetFocusedDescendant( - windowToCheck, nsFocusManager::eIncludeAllDescendants, - getter_AddRefs(focusedWindow)); - } - - // If the focus is in an editable region, don't scroll. - if (focusedContent && focusedContent->IsEditable()) { - return NS_OK; - } - - // If the focus is in a form control, don't scroll. - for (nsIContent* c = focusedContent; c; c = c->GetParent()) { - nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(c); - if (formControl) { - return NS_OK; - } - } - } - - if (controller) controller->DoCommand(command.get()); - - return NS_OK; -} - -nsresult nsXBLPrototypeHandler::DispatchXULKeyCommand(Event* aEvent) { - nsCOMPtr<Element> handlerElement = GetHandlerElement(); - NS_ENSURE_STATE(handlerElement); - if (handlerElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled, - nsGkAtoms::_true, eCaseMatters)) { - // Don't dispatch command events for disabled keys. - return NS_OK; - } - - aEvent->PreventDefault(); - - // Copy the modifiers from the key event. - RefPtr<KeyboardEvent> keyEvent = aEvent->AsKeyboardEvent(); - if (!keyEvent) { - NS_ERROR("Trying to execute a key handler for a non-key event!"); - return NS_ERROR_FAILURE; - } - - // XXX We should use mozilla::Modifiers for supporting all modifiers. - - bool isAlt = keyEvent->AltKey(); - bool isControl = keyEvent->CtrlKey(); - bool isShift = keyEvent->ShiftKey(); - bool isMeta = keyEvent->MetaKey(); - - nsContentUtils::DispatchXULCommand(handlerElement, true, nullptr, nullptr, - isControl, isAlt, isShift, isMeta); - return NS_OK; -} - -Modifiers nsXBLPrototypeHandler::GetModifiers() const { - Modifiers modifiers = 0; - - if (mKeyMask & cMeta) { - modifiers |= MODIFIER_META; - } - if (mKeyMask & cOS) { - modifiers |= MODIFIER_OS; - } - if (mKeyMask & cShift) { - modifiers |= MODIFIER_SHIFT; - } - if (mKeyMask & cAlt) { - modifiers |= MODIFIER_ALT; - } - if (mKeyMask & cControl) { - modifiers |= MODIFIER_CONTROL; - } - - return modifiers; -} - -Modifiers nsXBLPrototypeHandler::GetModifiersMask() const { - Modifiers modifiersMask = 0; - - if (mKeyMask & cMetaMask) { - modifiersMask |= MODIFIER_META; - } - if (mKeyMask & cOSMask) { - modifiersMask |= MODIFIER_OS; - } - if (mKeyMask & cShiftMask) { - modifiersMask |= MODIFIER_SHIFT; - } - if (mKeyMask & cAltMask) { - modifiersMask |= MODIFIER_ALT; - } - if (mKeyMask & cControlMask) { - modifiersMask |= MODIFIER_CONTROL; - } - - return modifiersMask; -} - -already_AddRefed<nsAtom> nsXBLPrototypeHandler::GetEventName() { - RefPtr<nsAtom> eventName = mEventName; - return eventName.forget(); -} - -already_AddRefed<nsIController> nsXBLPrototypeHandler::GetController( - EventTarget* aTarget) { - // XXX Fix this so there's a generic interface that describes controllers, - // This code should have no special knowledge of what objects might have - // controllers. - nsCOMPtr<nsIControllers> controllers; - - nsCOMPtr<nsIContent> targetContent(do_QueryInterface(aTarget)); - RefPtr<nsXULElement> xulElement = nsXULElement::FromNodeOrNull(targetContent); - if (xulElement) { - controllers = xulElement->GetControllers(IgnoreErrors()); - } - - if (!controllers) { - HTMLTextAreaElement* htmlTextArea = - HTMLTextAreaElement::FromNode(targetContent); - if (htmlTextArea) htmlTextArea->GetControllers(getter_AddRefs(controllers)); - } - - if (!controllers) { - HTMLInputElement* htmlInputElement = - HTMLInputElement::FromNode(targetContent); - if (htmlInputElement) - htmlInputElement->GetControllers(getter_AddRefs(controllers)); - } - - if (!controllers) { - nsCOMPtr<nsPIDOMWindowOuter> domWindow(do_QueryInterface(aTarget)); - if (domWindow) { - domWindow->GetControllers(getter_AddRefs(controllers)); - } - } - - // Return the first controller. - // XXX This code should be checking the command name and using supportscommand - // and iscommandenabled. - nsCOMPtr<nsIController> controller; - if (controllers) { - controllers->GetControllerAt(0, getter_AddRefs(controller)); - } - - return controller.forget(); -} - -bool nsXBLPrototypeHandler::KeyEventMatched( - KeyboardEvent* aKeyEvent, uint32_t aCharCode, - const IgnoreModifierState& aIgnoreModifierState) { - if (mDetail != -1) { - // Get the keycode or charcode of the key event. - uint32_t code; - - if (mMisc) { - if (aCharCode) - code = aCharCode; - else - code = aKeyEvent->CharCode(); - if (IS_IN_BMP(code)) code = ToLowerCase(char16_t(code)); - } else - code = aKeyEvent->KeyCode(); - - if (code != uint32_t(mDetail)) return false; - } - - return ModifiersMatchMask(aKeyEvent, aIgnoreModifierState); -} - -bool nsXBLPrototypeHandler::MouseEventMatched(MouseEvent* aMouseEvent) { - if (mDetail == -1 && mMisc == 0 && (mKeyMask & cAllModifiers) == 0) - return true; // No filters set up. It's generic. - - if (mDetail != -1 && (aMouseEvent->Button() != mDetail)) { - return false; - } - - if (mMisc != 0 && (aMouseEvent->Detail() != mMisc)) { - return false; - } - - return ModifiersMatchMask(aMouseEvent, IgnoreModifierState()); -} - -struct keyCodeData { - const char* str; - uint16_t strlength; - uint16_t keycode; -}; - -// All of these must be uppercase, since the function below does -// case-insensitive comparison by converting to uppercase. -// XXX: be sure to check this periodically for new symbol additions! -static const keyCodeData gKeyCodes[] = { - -#define NS_DEFINE_VK(aDOMKeyName, aDOMKeyCode) \ - {#aDOMKeyName, sizeof(#aDOMKeyName) - 1, aDOMKeyCode}, -#include "mozilla/VirtualKeyCodeList.h" -#undef NS_DEFINE_VK - - {nullptr, 0, 0}}; - -int32_t nsXBLPrototypeHandler::GetMatchingKeyCode(const nsAString& aKeyName) { - nsAutoCString keyName; - LossyCopyUTF16toASCII(aKeyName, keyName); - ToUpperCase(keyName); // We want case-insensitive comparison with data - // stored as uppercase. - - uint32_t keyNameLength = keyName.Length(); - const char* keyNameStr = keyName.get(); - for (uint16_t i = 0; i < ArrayLength(gKeyCodes) - 1; ++i) { - if (keyNameLength == gKeyCodes[i].strlength && - !nsCRT::strcmp(gKeyCodes[i].str, keyNameStr)) { - return gKeyCodes[i].keycode; - } - } - - return 0; -} - -int32_t nsXBLPrototypeHandler::KeyToMask(int32_t key) { - switch (key) { - case KeyboardEvent_Binding::DOM_VK_META: - return cMeta | cMetaMask; - - case KeyboardEvent_Binding::DOM_VK_WIN: - return cOS | cOSMask; - - case KeyboardEvent_Binding::DOM_VK_ALT: - return cAlt | cAltMask; - - case KeyboardEvent_Binding::DOM_VK_CONTROL: - default: - return cControl | cControlMask; - } - return cControl | cControlMask; // for warning avoidance -} - -// static -int32_t nsXBLPrototypeHandler::AccelKeyMask() { - switch (WidgetInputEvent::AccelModifier()) { - case MODIFIER_ALT: - return KeyToMask(KeyboardEvent_Binding::DOM_VK_ALT); - case MODIFIER_CONTROL: - return KeyToMask(KeyboardEvent_Binding::DOM_VK_CONTROL); - case MODIFIER_META: - return KeyToMask(KeyboardEvent_Binding::DOM_VK_META); - case MODIFIER_OS: - return KeyToMask(KeyboardEvent_Binding::DOM_VK_WIN); - default: - MOZ_CRASH("Handle the new result of WidgetInputEvent::AccelModifier()"); - return 0; - } -} - -void nsXBLPrototypeHandler::GetEventType(nsAString& aEvent) { - nsCOMPtr<Element> handlerElement = GetHandlerElement(); - if (!handlerElement) { - aEvent.Truncate(); - return; - } - handlerElement->GetAttr(kNameSpaceID_None, nsGkAtoms::event, aEvent); - - if (aEvent.IsEmpty() && (mType & NS_HANDLER_TYPE_XUL)) - // If no type is specified for a XUL <key> element, let's assume that we're - // "keypress". - aEvent.AssignLiteral("keypress"); -} - -void nsXBLPrototypeHandler::ConstructPrototype( - Element* aKeyElement, const char16_t* aEvent, const char16_t* aPhase, - const char16_t* aAction, const char16_t* aCommand, const char16_t* aKeyCode, - const char16_t* aCharCode, const char16_t* aModifiers, - const char16_t* aButton, const char16_t* aClickCount, - const char16_t* aGroup, const char16_t* aPreventDefault, - const char16_t* aAllowUntrusted) { - mType = 0; - - if (aKeyElement) { - mType |= NS_HANDLER_TYPE_XUL; - MOZ_ASSERT(!mPrototypeBinding); - nsWeakPtr weak = do_GetWeakReference(aKeyElement); - if (!weak) { - return; - } - weak.swap(mHandlerElement); - } else { - mType |= aCommand ? NS_HANDLER_TYPE_XBL_COMMAND : NS_HANDLER_TYPE_XBL_JS; - mHandlerText = nullptr;