1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
9 #include "mozilla/Encoding.h"
10 #include "nsContentUtils.h"
12 #include "nsBindingManager.h"
14 #include "nsXBLPrototypeBinding.h"
15 #include "nsCycleCollectionParticipant.h"
20 static DocumentOrShadowRoot
* DocOrShadowFromContent(nsIContent
& aContent
) {
21 ShadowRoot
* shadow
= aContent
.GetContainingShadow();
23 // We never look in <svg:use> shadow trees, for backwards compat.
24 while (shadow
&& shadow
->Host()->IsSVGElement(nsGkAtoms::use
)) {
25 shadow
= shadow
->Host()->GetContainingShadow();
32 return aContent
.OwnerDoc();
35 void IDTracker::ResetToURIFragmentID(nsIContent
* aFromContent
, nsIURI
* aURI
,
37 uint32_t aReferrerPolicy
, bool aWatch
,
38 bool aReferenceImage
) {
39 MOZ_ASSERT(aFromContent
,
40 "ResetToURIFragmentID() expects non-null content pointer");
46 nsAutoCString refPart
;
47 aURI
->GetRef(refPart
);
48 // Unescape %-escapes in the reference. The result will be in the
49 // document charset, hopefully...
50 NS_UnescapeURL(refPart
);
52 // Get the thing to observe changes to.
53 Document
* doc
= aFromContent
->OwnerDoc();
54 DocumentOrShadowRoot
* docOrShadow
= DocOrShadowFromContent(*aFromContent
);
55 auto encoding
= doc
->GetDocumentCharacterSet();
58 nsresult rv
= encoding
->DecodeWithoutBOMHandling(refPart
, ref
);
59 if (NS_FAILED(rv
) || ref
.IsEmpty()) {
64 nsIContent
* bindingParent
= aFromContent
->GetBindingParent();
65 if (bindingParent
&& !aFromContent
->IsInShadowTree()) {
66 nsXBLBinding
* binding
= bindingParent
->GetXBLBinding();
68 // This happens, for example, if aFromContent is part of the content
69 // inserted by a call to Document::InsertAnonymousContent, which we
70 // also want to handle. (It also happens for <use>'s anonymous
73 doc
->GetAnonRootIfInAnonymousContentContainer(aFromContent
);
75 mElement
= nsContentUtils::MatchElementId(anonRoot
, ref
);
76 // We don't have watching working yet for anonymous content, so bail out
81 bool isEqualExceptRef
;
82 rv
= aURI
->EqualsExceptRef(binding
->PrototypeBinding()->DocURI(),
84 if (NS_SUCCEEDED(rv
) && isEqualExceptRef
) {
85 // XXX sXBL/XBL2 issue
86 // Our content is an anonymous XBL element from a binding inside the
87 // same document that the referenced URI points to. In order to avoid
88 // the risk of ID collisions we restrict ourselves to anonymous
89 // elements from this binding; specifically, URIs that are relative to
90 // the binding document should resolve to the copy of the target
91 // element that has been inserted into the bound document.
92 // If the URI points to a different document we don't need this
94 nsINodeList
* anonymousChildren
=
95 doc
->BindingManager()->GetAnonymousNodesFor(bindingParent
);
97 if (anonymousChildren
) {
98 uint32_t length
= anonymousChildren
->Length();
99 for (uint32_t i
= 0; i
< length
&& !mElement
; ++i
) {
101 nsContentUtils::MatchElementId(anonymousChildren
->Item(i
), ref
);
105 // We don't have watching working yet for XBL, so bail out here.
111 bool isEqualExceptRef
;
112 rv
= aURI
->EqualsExceptRef(doc
->GetDocumentURI(), &isEqualExceptRef
);
113 if (NS_FAILED(rv
) || !isEqualExceptRef
) {
114 RefPtr
<Document::ExternalResourceLoad
> load
;
115 doc
= doc
->RequestExternalResource(aURI
, aReferrer
, aReferrerPolicy
,
116 aFromContent
, getter_AddRefs(load
));
119 if (!load
|| !aWatch
) {
120 // Nothing will ever happen here
124 DocumentLoadNotification
* observer
=
125 new DocumentLoadNotification(this, ref
);
126 mPendingNotification
= observer
;
127 load
->AddObserver(observer
);
128 // Keep going so we set up our watching stuff a bit
133 mWatchID
= NS_Atomize(ref
);
136 mReferencingImage
= aReferenceImage
;
137 HaveNewDocumentOrShadowRoot(docOrShadow
, aWatch
, ref
);
140 void IDTracker::ResetWithID(Element
& aFrom
, nsAtom
* aID
, bool aWatch
) {
147 mReferencingImage
= false;
149 DocumentOrShadowRoot
* docOrShadow
= DocOrShadowFromContent(aFrom
);
150 HaveNewDocumentOrShadowRoot(docOrShadow
, aWatch
, nsDependentAtomString(aID
));
153 void IDTracker::HaveNewDocumentOrShadowRoot(DocumentOrShadowRoot
* aDocOrShadow
,
154 bool aWatch
, const nsString
& aRef
) {
156 mWatchDocumentOrShadowRoot
= nullptr;
158 mWatchDocumentOrShadowRoot
= &aDocOrShadow
->AsNode();
159 mElement
= aDocOrShadow
->AddIDTargetObserver(mWatchID
, Observe
, this,
169 Element
* e
= mReferencingImage
? aDocOrShadow
->LookupImageElement(aRef
)
170 : aDocOrShadow
->GetElementById(aRef
);
176 void IDTracker::Traverse(nsCycleCollectionTraversalCallback
* aCB
) {
177 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCB
, "mWatchDocumentOrShadowRoot");
178 aCB
->NoteXPCOMChild(mWatchDocumentOrShadowRoot
);
179 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCB
, "mElement");
180 aCB
->NoteXPCOMChild(mElement
);
183 void IDTracker::Unlink() {
185 if (DocumentOrShadowRoot
* docOrShadow
= GetWatchDocOrShadowRoot()) {
186 docOrShadow
->RemoveIDTargetObserver(mWatchID
, Observe
, this,
190 if (mPendingNotification
) {
191 mPendingNotification
->Clear();
192 mPendingNotification
= nullptr;
194 mWatchDocumentOrShadowRoot
= nullptr;
197 mReferencingImage
= false;
200 bool IDTracker::Observe(Element
* aOldElement
, Element
* aNewElement
,
202 IDTracker
* p
= static_cast<IDTracker
*>(aData
);
203 if (p
->mPendingNotification
) {
204 p
->mPendingNotification
->SetTo(aNewElement
);
206 NS_ASSERTION(aOldElement
== p
->mElement
, "Failed to track content!");
207 ChangeNotification
* watcher
=
208 new ChangeNotification(p
, aOldElement
, aNewElement
);
209 p
->mPendingNotification
= watcher
;
210 nsContentUtils::AddScriptRunner(watcher
);
212 bool keepTracking
= p
->IsPersistent();
214 p
->mWatchDocumentOrShadowRoot
= nullptr;
215 p
->mWatchID
= nullptr;
220 NS_IMPL_ISUPPORTS_INHERITED0(IDTracker::ChangeNotification
, mozilla::Runnable
)
221 NS_IMPL_ISUPPORTS(IDTracker::DocumentLoadNotification
, nsIObserver
)
224 IDTracker::DocumentLoadNotification::Observe(nsISupports
* aSubject
,
226 const char16_t
* aData
) {
227 NS_ASSERTION(PL_strcmp(aTopic
, "external-resource-document-created") == 0,
230 nsCOMPtr
<Document
> doc
= do_QueryInterface(aSubject
);
231 mTarget
->mPendingNotification
= nullptr;
232 NS_ASSERTION(!mTarget
->mElement
, "Why do we have content here?");
233 // If we got here, that means we had Reset*() called with
234 // aWatch == true. So keep watching if IsPersistent().
235 mTarget
->HaveNewDocumentOrShadowRoot(doc
, mTarget
->IsPersistent(), mRef
);
236 mTarget
->ElementChanged(nullptr, mTarget
->mElement
);
242 } // namespace mozilla