Bug 1386110 - Use a smart pointer to reliably de-register NAC regardless of how it goes away. r=masayuki

MozReview-Commit-ID: HTSu5BjxD8I
This commit is contained in:
Bobby Holley 2017-08-02 12:37:44 -07:00
Родитель 693c0c370f
Коммит 1bd3bc937c
9 изменённых файлов: 198 добавлений и 130 удалений

Просмотреть файл

@ -66,6 +66,7 @@
#include "mozilla/IMEStateManager.h" #include "mozilla/IMEStateManager.h"
#include "mozilla/InternalMutationEvent.h" #include "mozilla/InternalMutationEvent.h"
#include "mozilla/Likely.h" #include "mozilla/Likely.h"
#include "mozilla/ManualNAC.h"
#include "mozilla/MouseEvents.h" #include "mozilla/MouseEvents.h"
#include "mozilla/Preferences.h" #include "mozilla/Preferences.h"
#include "mozilla/dom/Selection.h" #include "mozilla/dom/Selection.h"
@ -10348,7 +10349,7 @@ nsContentUtils::AppendNativeAnonymousChildren(
} }
// Get manually created NAC (editor resize handles, etc.). // Get manually created NAC (editor resize handles, etc.).
if (auto nac = static_cast<ManualNAC*>( if (auto nac = static_cast<ManualNACArray*>(
aContent->GetProperty(nsGkAtoms::manualNACProperty))) { aContent->GetProperty(nsGkAtoms::manualNACProperty))) {
aKids.AppendElements(*nac); aKids.AppendElements(*nac);
} }

Просмотреть файл

@ -197,15 +197,6 @@ struct EventNameMapping
typedef bool (*CallOnRemoteChildFunction) (mozilla::dom::TabParent* aTabParent, typedef bool (*CallOnRemoteChildFunction) (mozilla::dom::TabParent* aTabParent,
void* aArg); void* aArg);
namespace mozilla {
// 16 seems to be the maximum number of manual NAC nodes that editor
// creates for a given element.
//
// These need to be manually removed by the machinery that sets the NAC,
// otherwise we'll leak.
typedef AutoTArray<RefPtr<mozilla::dom::Element>,16> ManualNAC;
}
class nsContentUtils class nsContentUtils
{ {
friend class nsAutoScriptBlockerSuppressNodeRemoved; friend class nsAutoScriptBlockerSuppressNodeRemoved;

Просмотреть файл

@ -229,11 +229,11 @@ HTMLEditor::GetElementZIndex(nsIDOMElement* aElement,
return NS_OK; return NS_OK;
} }
already_AddRefed<Element> ManualNACPtr
HTMLEditor::CreateGrabber(nsINode* aParentNode) HTMLEditor::CreateGrabber(nsINode* aParentNode)
{ {
// let's create a grabber through the element factory // let's create a grabber through the element factory
RefPtr<Element> ret = ManualNACPtr ret =
CreateAnonymousElement(nsGkAtoms::span, GetAsDOMNode(aParentNode), CreateAnonymousElement(nsGkAtoms::span, GetAsDOMNode(aParentNode),
NS_LITERAL_STRING("mozGrabber"), false); NS_LITERAL_STRING("mozGrabber"), false);
if (NS_WARN_IF(!ret)) { if (NS_WARN_IF(!ret)) {
@ -245,7 +245,7 @@ HTMLEditor::CreateGrabber(nsINode* aParentNode)
evtTarget->AddEventListener(NS_LITERAL_STRING("mousedown"), evtTarget->AddEventListener(NS_LITERAL_STRING("mousedown"),
mEventListener, false); mEventListener, false);
return ret.forget(); return ret;
} }
NS_IMETHODIMP NS_IMETHODIMP
@ -289,10 +289,8 @@ HTMLEditor::HideGrabber()
// are no document observers to notify, but we still want to // are no document observers to notify, but we still want to
// UnbindFromTree. // UnbindFromTree.
DeleteRefToAnonymousNode(mGrabber, ps); DeleteRefToAnonymousNode(Move(mGrabber), ps);
mGrabber = nullptr; DeleteRefToAnonymousNode(Move(mPositioningShadow), ps);
DeleteRefToAnonymousNode(mPositioningShadow, ps);
mPositioningShadow = nullptr;
return NS_OK; return NS_OK;
} }
@ -391,7 +389,7 @@ HTMLEditor::EndMoving()
nsCOMPtr<nsIPresShell> ps = GetPresShell(); nsCOMPtr<nsIPresShell> ps = GetPresShell();
NS_ENSURE_TRUE(ps, NS_ERROR_NOT_INITIALIZED); NS_ENSURE_TRUE(ps, NS_ERROR_NOT_INITIALIZED);
DeleteRefToAnonymousNode(mPositioningShadow, ps); DeleteRefToAnonymousNode(Move(mPositioningShadow), ps);
mPositioningShadow = nullptr; mPositioningShadow = nullptr;
} }

Просмотреть файл

@ -161,7 +161,7 @@ ElementDeletionObserver::NodeWillBeDestroyed(const nsINode* aNode)
NS_RELEASE_THIS(); NS_RELEASE_THIS();
} }
already_AddRefed<Element> ManualNACPtr
HTMLEditor::CreateAnonymousElement(nsIAtom* aTag, HTMLEditor::CreateAnonymousElement(nsIAtom* aTag,
nsIDOMNode* aParentNode, nsIDOMNode* aParentNode,
const nsAString& aAnonClass, const nsAString& aAnonClass,
@ -196,16 +196,16 @@ HTMLEditor::CreateAnonymousElement(nsIAtom* aTag,
} }
// Create a new node through the element factory // Create a new node through the element factory
RefPtr<Element> newContent = CreateHTMLContent(aTag); RefPtr<Element> newContentRaw = CreateHTMLContent(aTag);
if (NS_WARN_IF(!newContent)) { if (NS_WARN_IF(!newContentRaw)) {
return nullptr; return nullptr;
} }
// add the "hidden" class if needed // add the "hidden" class if needed
if (aIsCreatedHidden) { if (aIsCreatedHidden) {
nsresult rv = nsresult rv =
newContent->SetAttr(kNameSpaceID_None, nsGkAtoms::_class, newContentRaw->SetAttr(kNameSpaceID_None, nsGkAtoms::_class,
NS_LITERAL_STRING("hidden"), true); NS_LITERAL_STRING("hidden"), true);
if (NS_WARN_IF(NS_FAILED(rv))) { if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr; return nullptr;
} }
@ -214,8 +214,8 @@ HTMLEditor::CreateAnonymousElement(nsIAtom* aTag,
// add an _moz_anonclass attribute if needed // add an _moz_anonclass attribute if needed
if (!aAnonClass.IsEmpty()) { if (!aAnonClass.IsEmpty()) {
nsresult rv = nsresult rv =
newContent->SetAttr(kNameSpaceID_None, nsGkAtoms::_moz_anonclass, newContentRaw->SetAttr(kNameSpaceID_None, nsGkAtoms::_moz_anonclass,
aAnonClass, true); aAnonClass, true);
if (NS_WARN_IF(NS_FAILED(rv))) { if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr; return nullptr;
} }
@ -225,24 +225,16 @@ HTMLEditor::CreateAnonymousElement(nsIAtom* aTag,
nsAutoScriptBlocker scriptBlocker; nsAutoScriptBlocker scriptBlocker;
// establish parenthood of the element // establish parenthood of the element
newContent->SetIsNativeAnonymousRoot(); newContentRaw->SetIsNativeAnonymousRoot();
nsresult rv = nsresult rv =
newContent->BindToTree(doc, parentContent, parentContent, true); newContentRaw->BindToTree(doc, parentContent, parentContent, true);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
newContent->UnbindFromTree(); newContentRaw->UnbindFromTree();
return nullptr; return nullptr;
} }
} }
// Record the NAC on the element, so that AllChildrenIterator can find it. ManualNACPtr newContent(newContentRaw.forget());
auto nac = static_cast<ManualNAC*>(
parentContent->GetProperty(nsGkAtoms::manualNACProperty));
if (!nac) {
nac = new ManualNAC();
parentContent->SetProperty(nsGkAtoms::manualNACProperty, nac,
nsINode::DeleteProperty<ManualNAC>);
}
nac->AppendElement(newContent);
// Must style the new element, otherwise the PostRecreateFramesFor call // Must style the new element, otherwise the PostRecreateFramesFor call
// below will do nothing. // below will do nothing.
@ -268,7 +260,7 @@ HTMLEditor::CreateAnonymousElement(nsIAtom* aTag,
// display the element // display the element
ps->PostRecreateFramesFor(newContent); ps->PostRecreateFramesFor(newContent);
return newContent.forget(); return Move(newContent);
} }
// Removes event listener and calls DeleteRefToAnonymousNode. // Removes event listener and calls DeleteRefToAnonymousNode.
@ -276,19 +268,19 @@ void
HTMLEditor::RemoveListenerAndDeleteRef(const nsAString& aEvent, HTMLEditor::RemoveListenerAndDeleteRef(const nsAString& aEvent,
nsIDOMEventListener* aListener, nsIDOMEventListener* aListener,
bool aUseCapture, bool aUseCapture,
Element* aElement, ManualNACPtr aElement,
nsIPresShell* aShell) nsIPresShell* aShell)
{ {
nsCOMPtr<nsIDOMEventTarget> evtTarget(do_QueryInterface(aElement)); nsCOMPtr<nsIDOMEventTarget> evtTarget(do_QueryInterface(aElement));
if (evtTarget) { if (evtTarget) {
evtTarget->RemoveEventListener(aEvent, aListener, aUseCapture); evtTarget->RemoveEventListener(aEvent, aListener, aUseCapture);
} }
DeleteRefToAnonymousNode(aElement, aShell); DeleteRefToAnonymousNode(Move(aElement), aShell);
} }
// Deletes all references to an anonymous element // Deletes all references to an anonymous element
void void
HTMLEditor::DeleteRefToAnonymousNode(nsIContent* aContent, HTMLEditor::DeleteRefToAnonymousNode(ManualNACPtr aContent,
nsIPresShell* aShell) nsIPresShell* aShell)
{ {
// call ContentRemoved() for the anonymous content // call ContentRemoved() for the anonymous content
@ -332,19 +324,7 @@ HTMLEditor::DeleteRefToAnonymousNode(nsIContent* aContent,
} }
} }
// Remove reference from the parent element. // The ManualNACPtr destructor will invoke UnbindFromTree.
auto nac = static_cast<mozilla::ManualNAC*>(
parentContent->GetProperty(nsGkAtoms::manualNACProperty));
// nsIDocument::AdoptNode might remove all properties before destroying
// editor. So we have to consider that NAC could be already removed.
if (nac) {
nac->RemoveElement(aContent);
if (nac->IsEmpty()) {
parentContent->DeleteProperty(nsGkAtoms::manualNACProperty);
}
}
aContent->UnbindFromTree();
} }
// The following method is mostly called by a selection listener. When a // The following method is mostly called by a selection listener. When a

Просмотреть файл

@ -8,6 +8,7 @@
#include "mozilla/Attributes.h" #include "mozilla/Attributes.h"
#include "mozilla/CSSEditUtils.h" #include "mozilla/CSSEditUtils.h"
#include "mozilla/ManualNAC.h"
#include "mozilla/StyleSheet.h" #include "mozilla/StyleSheet.h"
#include "mozilla/TextEditor.h" #include "mozilla/TextEditor.h"
#include "mozilla/UniquePtr.h" #include "mozilla/UniquePtr.h"
@ -857,9 +858,9 @@ protected:
void RemoveListenerAndDeleteRef(const nsAString& aEvent, void RemoveListenerAndDeleteRef(const nsAString& aEvent,
nsIDOMEventListener* aListener, nsIDOMEventListener* aListener,
bool aUseCapture, bool aUseCapture,
Element* aElement, ManualNACPtr aElement,
nsIPresShell* aShell); nsIPresShell* aShell);
void DeleteRefToAnonymousNode(nsIContent* aContent, void DeleteRefToAnonymousNode(ManualNACPtr aContent,
nsIPresShell* aShell); nsIPresShell* aShell);
nsresult ShowResizersInner(nsIDOMElement *aResizedElement); nsresult ShowResizersInner(nsIDOMElement *aResizedElement);
@ -900,19 +901,19 @@ protected:
bool mIsInlineTableEditingEnabled; bool mIsInlineTableEditingEnabled;
// resizing // resizing
nsCOMPtr<Element> mTopLeftHandle; ManualNACPtr mTopLeftHandle;
nsCOMPtr<Element> mTopHandle; ManualNACPtr mTopHandle;
nsCOMPtr<Element> mTopRightHandle; ManualNACPtr mTopRightHandle;
nsCOMPtr<Element> mLeftHandle; ManualNACPtr mLeftHandle;
nsCOMPtr<Element> mRightHandle; ManualNACPtr mRightHandle;
nsCOMPtr<Element> mBottomLeftHandle; ManualNACPtr mBottomLeftHandle;
nsCOMPtr<Element> mBottomHandle; ManualNACPtr mBottomHandle;
nsCOMPtr<Element> mBottomRightHandle; ManualNACPtr mBottomRightHandle;
nsCOMPtr<Element> mActivatedHandle; nsCOMPtr<Element> mActivatedHandle;
nsCOMPtr<Element> mResizingShadow; ManualNACPtr mResizingShadow;
nsCOMPtr<Element> mResizingInfo; ManualNACPtr mResizingInfo;
nsCOMPtr<Element> mResizedObject; nsCOMPtr<Element> mResizedObject;
@ -943,18 +944,17 @@ protected:
nsresult SetAllResizersPosition(); nsresult SetAllResizersPosition();
already_AddRefed<Element> CreateResizer(int16_t aLocation, ManualNACPtr CreateResizer(int16_t aLocation, nsIDOMNode* aParentNode);
nsIDOMNode* aParentNode);
void SetAnonymousElementPosition(int32_t aX, int32_t aY, void SetAnonymousElementPosition(int32_t aX, int32_t aY,
Element* aResizer); Element* aResizer);
already_AddRefed<Element> CreateShadow(nsIDOMNode* aParentNode, ManualNACPtr CreateShadow(nsIDOMNode* aParentNode,
nsIDOMElement* aOriginalObject); nsIDOMElement* aOriginalObject);
nsresult SetShadowPosition(Element* aShadow, Element* aOriginalObject, nsresult SetShadowPosition(Element* aShadow, Element* aOriginalObject,
int32_t aOriginalObjectX, int32_t aOriginalObjectX,
int32_t aOriginalObjectY); int32_t aOriginalObjectY);
already_AddRefed<Element> CreateResizingInfo(nsIDOMNode* aParentNode); ManualNACPtr CreateResizingInfo(nsIDOMNode* aParentNode);
nsresult SetResizingInfoPosition(int32_t aX, int32_t aY, nsresult SetResizingInfoPosition(int32_t aX, int32_t aY,
int32_t aW, int32_t aH); int32_t aW, int32_t aH);
@ -983,12 +983,12 @@ protected:
int32_t mPositionedObjectBorderTop; int32_t mPositionedObjectBorderTop;
nsCOMPtr<Element> mAbsolutelyPositionedObject; nsCOMPtr<Element> mAbsolutelyPositionedObject;
nsCOMPtr<Element> mGrabber; ManualNACPtr mGrabber;
nsCOMPtr<Element> mPositioningShadow; ManualNACPtr mPositioningShadow;
int32_t mGridSize; int32_t mGridSize;
already_AddRefed<Element> CreateGrabber(nsINode* aParentNode); ManualNACPtr CreateGrabber(nsINode* aParentNode);
nsresult StartMoving(nsIDOMElement* aHandle); nsresult StartMoving(nsIDOMElement* aHandle);
nsresult SetFinalPosition(int32_t aX, int32_t aY); nsresult SetFinalPosition(int32_t aX, int32_t aY);
void AddPositioningOffset(int32_t& aX, int32_t& aY); void AddPositioningOffset(int32_t& aX, int32_t& aY);
@ -1001,13 +1001,13 @@ protected:
// inline table editing // inline table editing
nsCOMPtr<nsIDOMElement> mInlineEditedCell; nsCOMPtr<nsIDOMElement> mInlineEditedCell;
RefPtr<Element> mAddColumnBeforeButton; ManualNACPtr mAddColumnBeforeButton;
RefPtr<Element> mRemoveColumnButton; ManualNACPtr mRemoveColumnButton;
RefPtr<Element> mAddColumnAfterButton; ManualNACPtr mAddColumnAfterButton;
RefPtr<Element> mAddRowBeforeButton; ManualNACPtr mAddRowBeforeButton;
RefPtr<Element> mRemoveRowButton; ManualNACPtr mRemoveRowButton;
RefPtr<Element> mAddRowAfterButton; ManualNACPtr mAddRowAfterButton;
void AddMouseClickListener(Element* aElement); void AddMouseClickListener(Element* aElement);
void RemoveMouseClickListener(Element* aElement); void RemoveMouseClickListener(Element* aElement);
@ -1053,11 +1053,10 @@ private:
* is to be added to the created anonymous * is to be added to the created anonymous
* element * element
*/ */
already_AddRefed<Element> CreateAnonymousElement( ManualNACPtr CreateAnonymousElement(nsIAtom* aTag,
nsIAtom* aTag, nsIDOMNode* aParentNode,
nsIDOMNode* aParentNode, const nsAString& aAnonClass,
const nsAString& aAnonClass, bool aIsCreatedHidden);
bool aIsCreatedHidden);
}; };
} // namespace mozilla } // namespace mozilla

Просмотреть файл

@ -131,11 +131,11 @@ ResizerMouseMotionListener::HandleEvent(nsIDOMEvent* aMouseEvent)
* mozilla::HTMLEditor * mozilla::HTMLEditor
******************************************************************************/ ******************************************************************************/
already_AddRefed<Element> ManualNACPtr
HTMLEditor::CreateResizer(int16_t aLocation, HTMLEditor::CreateResizer(int16_t aLocation,
nsIDOMNode* aParentNode) nsIDOMNode* aParentNode)
{ {
RefPtr<Element> ret = ManualNACPtr ret =
CreateAnonymousElement(nsGkAtoms::span, CreateAnonymousElement(nsGkAtoms::span,
aParentNode, aParentNode,
NS_LITERAL_STRING("mozResizer"), NS_LITERAL_STRING("mozResizer"),
@ -182,10 +182,10 @@ HTMLEditor::CreateResizer(int16_t aLocation,
nsresult rv = nsresult rv =
ret->SetAttr(kNameSpaceID_None, nsGkAtoms::anonlocation, locationStr, true); ret->SetAttr(kNameSpaceID_None, nsGkAtoms::anonlocation, locationStr, true);
NS_ENSURE_SUCCESS(rv, nullptr); NS_ENSURE_SUCCESS(rv, nullptr);
return ret.forget(); return Move(ret);
} }
already_AddRefed<Element> ManualNACPtr
HTMLEditor::CreateShadow(nsIDOMNode* aParentNode, HTMLEditor::CreateShadow(nsIDOMNode* aParentNode,
nsIDOMElement* aOriginalObject) nsIDOMElement* aOriginalObject)
{ {
@ -196,20 +196,17 @@ HTMLEditor::CreateShadow(nsIDOMNode* aParentNode,
} else { } else {
name = nsGkAtoms::span; name = nsGkAtoms::span;
} }
RefPtr<Element> ret =
CreateAnonymousElement(name, aParentNode, return CreateAnonymousElement(name, aParentNode,
NS_LITERAL_STRING("mozResizingShadow"), true); NS_LITERAL_STRING("mozResizingShadow"), true);
return ret.forget();
} }
already_AddRefed<Element> ManualNACPtr
HTMLEditor::CreateResizingInfo(nsIDOMNode* aParentNode) HTMLEditor::CreateResizingInfo(nsIDOMNode* aParentNode)
{ {
// let's create an info box through the element factory // let's create an info box through the element factory
RefPtr<Element> ret = return CreateAnonymousElement(nsGkAtoms::span, aParentNode,
CreateAnonymousElement(nsGkAtoms::span, aParentNode, NS_LITERAL_STRING("mozResizingInfo"), true);
NS_LITERAL_STRING("mozResizingInfo"), true);
return ret.forget();
} }
nsresult nsresult
@ -386,44 +383,34 @@ HTMLEditor::HideResizers()
NS_NAMED_LITERAL_STRING(mousedown, "mousedown"); NS_NAMED_LITERAL_STRING(mousedown, "mousedown");
RemoveListenerAndDeleteRef(mousedown, mEventListener, true, RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
mTopLeftHandle, ps); Move(mTopLeftHandle), ps);
mTopLeftHandle = nullptr;
RemoveListenerAndDeleteRef(mousedown, mEventListener, true, RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
mTopHandle, ps); Move(mTopHandle), ps);
mTopHandle = nullptr;
RemoveListenerAndDeleteRef(mousedown, mEventListener, true, RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
mTopRightHandle, ps); Move(mTopRightHandle), ps);
mTopRightHandle = nullptr;
RemoveListenerAndDeleteRef(mousedown, mEventListener, true, RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
mLeftHandle, ps); Move(mLeftHandle), ps);
mLeftHandle = nullptr;
RemoveListenerAndDeleteRef(mousedown, mEventListener, true, RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
mRightHandle, ps); Move(mRightHandle), ps);
mRightHandle = nullptr;
RemoveListenerAndDeleteRef(mousedown, mEventListener, true, RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
mBottomLeftHandle, ps); Move(mBottomLeftHandle), ps);
mBottomLeftHandle = nullptr;
RemoveListenerAndDeleteRef(mousedown, mEventListener, true, RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
mBottomHandle, ps); Move(mBottomHandle), ps);
mBottomHandle = nullptr;
RemoveListenerAndDeleteRef(mousedown, mEventListener, true, RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
mBottomRightHandle, ps); Move(mBottomRightHandle), ps);
mBottomRightHandle = nullptr;
RemoveListenerAndDeleteRef(mousedown, mEventListener, true, RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
mResizingShadow, ps); Move(mResizingShadow), ps);
mResizingShadow = nullptr;
RemoveListenerAndDeleteRef(mousedown, mEventListener, true, RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
mResizingInfo, ps); Move(mResizingInfo), ps);
mResizingInfo = nullptr;
if (mActivatedHandle) { if (mActivatedHandle) {
mActivatedHandle->UnsetAttr(kNameSpaceID_None, nsGkAtoms::_moz_activated, mActivatedHandle->UnsetAttr(kNameSpaceID_None, nsGkAtoms::_moz_activated,

Просмотреть файл

@ -110,18 +110,12 @@ HTMLEditor::HideInlineTableEditingUI()
// are no document observers to notify, but we still want to // are no document observers to notify, but we still want to
// UnbindFromTree. // UnbindFromTree.
DeleteRefToAnonymousNode(mAddColumnBeforeButton, ps); DeleteRefToAnonymousNode(Move(mAddColumnBeforeButton), ps);
mAddColumnBeforeButton = nullptr; DeleteRefToAnonymousNode(Move(mRemoveColumnButton), ps);
DeleteRefToAnonymousNode(mRemoveColumnButton, ps); DeleteRefToAnonymousNode(Move(mAddColumnAfterButton), ps);
mRemoveColumnButton = nullptr; DeleteRefToAnonymousNode(Move(mAddRowBeforeButton), ps);
DeleteRefToAnonymousNode(mAddColumnAfterButton, ps); DeleteRefToAnonymousNode(Move(mRemoveRowButton), ps);
mAddColumnAfterButton = nullptr; DeleteRefToAnonymousNode(Move(mAddRowAfterButton), ps);
DeleteRefToAnonymousNode(mAddRowBeforeButton, ps);
mAddRowBeforeButton = nullptr;
DeleteRefToAnonymousNode(mRemoveRowButton, ps);
mRemoveRowButton = nullptr;
DeleteRefToAnonymousNode(mAddRowAfterButton, ps);
mAddRowAfterButton = nullptr;
return NS_OK; return NS_OK;
} }

Просмотреть файл

@ -0,0 +1,117 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_ManualNAC_h
#define mozilla_ManualNAC_h
#include "mozilla/dom/Element.h"
#include "mozilla/RefPtr.h"
namespace mozilla {
// 16 seems to be the maximum number of manual Native Anonymous Content (NAC)
// nodes that editor creates for a given element.
//
// These need to be manually removed by the machinery that sets the NAC,
// otherwise we'll leak.
typedef AutoTArray<RefPtr<dom::Element>, 16> ManualNACArray;
/**
* Smart pointer class to own "manual" Native Anonymous Content, and perform
* the necessary registration and deregistration on the parent element.
*/
class ManualNACPtr final
{
public:
ManualNACPtr() {}
MOZ_IMPLICIT ManualNACPtr(decltype(nullptr)) {}
explicit ManualNACPtr(already_AddRefed<Element> aNewNAC)
: mPtr(aNewNAC)
{
if (!mPtr) {
return;
}
// Record the NAC on the element, so that AllChildrenIterator can find it.
nsIContent* parentContent = mPtr->GetParent();
auto nac = static_cast<ManualNACArray*>(
parentContent->GetProperty(nsGkAtoms::manualNACProperty));
if (!nac) {
nac = new ManualNACArray();
parentContent->SetProperty(nsGkAtoms::manualNACProperty, nac,
nsINode::DeleteProperty<ManualNACArray>);
}
nac->AppendElement(mPtr);
}
// We use move semantics, and delete the copy-constructor and operator=.
ManualNACPtr(ManualNACPtr&& aOther) : mPtr(aOther.mPtr.forget()) {}
ManualNACPtr(ManualNACPtr& aOther) = delete;
ManualNACPtr& operator=(ManualNACPtr&& aOther)
{
mPtr = aOther.mPtr.forget();
return *this;
}
ManualNACPtr& operator=(ManualNACPtr& aOther) = delete;
~ManualNACPtr() { Reset(); }
void Reset()
{
if (!mPtr) {
return;
}
RefPtr<Element> ptr = mPtr.forget();
nsIContent* parentContent = ptr->GetParent();
if (!parentContent) {
NS_WARNING("Potentially leaking manual NAC");
return;
}
// Remove reference from the parent element.
auto nac = static_cast<mozilla::ManualNACArray*>(
parentContent->GetProperty(nsGkAtoms::manualNACProperty));
// nsIDocument::AdoptNode might remove all properties before destroying
// editor. So we have to consider that NAC could be already removed.
if (nac) {
nac->RemoveElement(ptr);
if (nac->IsEmpty()) {
parentContent->DeleteProperty(nsGkAtoms::manualNACProperty);
}
}
ptr->UnbindFromTree();
}
Element* get() const { return mPtr.get(); }
Element* operator->() const { return get(); }
operator Element*() const &
{
return get();
}
private:
RefPtr<Element> mPtr;
};
} // namespace mozilla
inline void
ImplCycleCollectionUnlink(mozilla::ManualNACPtr& field)
{
field.Reset();
}
inline void
ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& callback,
const mozilla::ManualNACPtr& field,
const char* name,
uint32_t flags = 0)
{
CycleCollectionNoteChild(callback, field.get(), name, flags);
}
#endif // #ifndef mozilla_ManualNAC_h

Просмотреть файл

@ -25,6 +25,7 @@ EXPORTS.mozilla += [
'EditorUtils.h', 'EditorUtils.h',
'EditTransactionBase.h', 'EditTransactionBase.h',
'HTMLEditor.h', 'HTMLEditor.h',
'ManualNAC.h',
'SelectionState.h', 'SelectionState.h',
'TextEditor.h', 'TextEditor.h',
'TextEditRules.h', 'TextEditRules.h',