Line data Source code
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/. */
6 :
7 : #include "nsReferencedElement.h"
8 : #include "nsContentUtils.h"
9 : #include "nsIURI.h"
10 : #include "nsBindingManager.h"
11 : #include "nsEscape.h"
12 : #include "nsXBLPrototypeBinding.h"
13 : #include "nsIDOMNode.h"
14 : #include "nsIDOMElement.h"
15 : #include "nsCycleCollectionParticipant.h"
16 :
17 : void
18 26 : nsReferencedElement::Reset(nsIContent* aFromContent, nsIURI* aURI,
19 : bool aWatch, bool aReferenceImage)
20 : {
21 26 : MOZ_ASSERT(aFromContent, "Reset() expects non-null content pointer");
22 :
23 26 : Unlink();
24 :
25 26 : if (!aURI)
26 2 : return;
27 :
28 50 : nsAutoCString refPart;
29 25 : aURI->GetRef(refPart);
30 : // Unescape %-escapes in the reference. The result will be in the
31 : // origin charset of the URL, hopefully...
32 25 : NS_UnescapeURL(refPart);
33 :
34 50 : nsAutoCString charset;
35 25 : aURI->GetOriginCharset(charset);
36 25 : auto encoding = Encoding::ForLabelNoReplacement(charset);
37 25 : if (!encoding) {
38 0 : encoding = UTF_8_ENCODING;
39 : }
40 50 : nsAutoString ref;
41 25 : nsresult rv = encoding->DecodeWithoutBOMHandling(refPart, ref);
42 25 : if (NS_FAILED(rv) || ref.IsEmpty()) {
43 0 : return;
44 : }
45 25 : rv = NS_OK;
46 :
47 : // Get the current document
48 25 : nsIDocument *doc = aFromContent->OwnerDoc();
49 25 : if (!doc)
50 0 : return;
51 :
52 25 : nsIContent* bindingParent = aFromContent->GetBindingParent();
53 25 : if (bindingParent) {
54 0 : nsXBLBinding* binding = bindingParent->GetXBLBinding();
55 0 : if (!binding) {
56 : // This happens, for example, if aFromContent is part of the content
57 : // inserted by a call to nsIDocument::InsertAnonymousContent, which we
58 : // also want to handle. (It also happens for <use>'s anonymous
59 : // content etc.)
60 : Element* anonRoot =
61 0 : doc->GetAnonRootIfInAnonymousContentContainer(aFromContent);
62 0 : if (anonRoot) {
63 0 : mElement = nsContentUtils::MatchElementId(anonRoot, ref);
64 : // We don't have watching working yet for anonymous content, so bail out here.
65 0 : return;
66 : }
67 : } else {
68 : bool isEqualExceptRef;
69 0 : rv = aURI->EqualsExceptRef(binding->PrototypeBinding()->DocURI(),
70 0 : &isEqualExceptRef);
71 0 : if (NS_SUCCEEDED(rv) && isEqualExceptRef) {
72 : // XXX sXBL/XBL2 issue
73 : // Our content is an anonymous XBL element from a binding inside the
74 : // same document that the referenced URI points to. In order to avoid
75 : // the risk of ID collisions we restrict ourselves to anonymous
76 : // elements from this binding; specifically, URIs that are relative to
77 : // the binding document should resolve to the copy of the target
78 : // element that has been inserted into the bound document.
79 : // If the URI points to a different document we don't need this
80 : // restriction.
81 : nsINodeList* anonymousChildren =
82 0 : doc->BindingManager()->GetAnonymousNodesFor(bindingParent);
83 :
84 0 : if (anonymousChildren) {
85 : uint32_t length;
86 0 : anonymousChildren->GetLength(&length);
87 0 : for (uint32_t i = 0; i < length && !mElement; ++i) {
88 : mElement =
89 0 : nsContentUtils::MatchElementId(anonymousChildren->Item(i), ref);
90 : }
91 : }
92 :
93 : // We don't have watching working yet for XBL, so bail out here.
94 0 : return;
95 : }
96 : }
97 : }
98 :
99 : bool isEqualExceptRef;
100 25 : rv = aURI->EqualsExceptRef(doc->GetDocumentURI(), &isEqualExceptRef);
101 25 : if (NS_FAILED(rv) || !isEqualExceptRef) {
102 0 : RefPtr<nsIDocument::ExternalResourceLoad> load;
103 0 : doc = doc->RequestExternalResource(aURI, aFromContent,
104 0 : getter_AddRefs(load));
105 0 : if (!doc) {
106 0 : if (!load || !aWatch) {
107 : // Nothing will ever happen here
108 0 : return;
109 : }
110 :
111 : DocumentLoadNotification* observer =
112 0 : new DocumentLoadNotification(this, ref);
113 0 : mPendingNotification = observer;
114 0 : if (observer) {
115 0 : load->AddObserver(observer);
116 : }
117 : // Keep going so we set up our watching stuff a bit
118 : }
119 : }
120 :
121 25 : if (aWatch) {
122 50 : nsCOMPtr<nsIAtom> atom = NS_Atomize(ref);
123 25 : if (!atom)
124 0 : return;
125 25 : atom.swap(mWatchID);
126 : }
127 :
128 25 : mReferencingImage = aReferenceImage;
129 :
130 25 : HaveNewDocument(doc, aWatch, ref);
131 : }
132 :
133 : void
134 0 : nsReferencedElement::ResetWithID(nsIContent* aFromContent, const nsString& aID,
135 : bool aWatch)
136 : {
137 0 : nsIDocument *doc = aFromContent->OwnerDoc();
138 0 : if (!doc)
139 0 : return;
140 :
141 : // XXX Need to take care of XBL/XBL2
142 :
143 0 : if (aWatch) {
144 0 : nsCOMPtr<nsIAtom> atom = NS_Atomize(aID);
145 0 : if (!atom)
146 0 : return;
147 0 : atom.swap(mWatchID);
148 : }
149 :
150 0 : mReferencingImage = false;
151 :
152 0 : HaveNewDocument(doc, aWatch, aID);
153 : }
154 :
155 : void
156 25 : nsReferencedElement::HaveNewDocument(nsIDocument* aDocument, bool aWatch,
157 : const nsString& aRef)
158 : {
159 25 : if (aWatch) {
160 25 : mWatchDocument = aDocument;
161 25 : if (mWatchDocument) {
162 25 : mElement = mWatchDocument->AddIDTargetObserver(mWatchID, Observe, this,
163 25 : mReferencingImage);
164 : }
165 25 : return;
166 : }
167 :
168 0 : if (!aDocument) {
169 0 : return;
170 : }
171 :
172 0 : Element *e = mReferencingImage ? aDocument->LookupImageElement(aRef) :
173 0 : aDocument->GetElementById(aRef);
174 0 : if (e) {
175 0 : mElement = e;
176 : }
177 : }
178 :
179 : void
180 0 : nsReferencedElement::Traverse(nsCycleCollectionTraversalCallback* aCB)
181 : {
182 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCB, "mWatchDocument");
183 0 : aCB->NoteXPCOMChild(mWatchDocument);
184 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCB, "mContent");
185 0 : aCB->NoteXPCOMChild(mElement);
186 0 : }
187 :
188 : void
189 26 : nsReferencedElement::Unlink()
190 : {
191 26 : if (mWatchDocument && mWatchID) {
192 0 : mWatchDocument->RemoveIDTargetObserver(mWatchID, Observe, this,
193 0 : mReferencingImage);
194 : }
195 26 : if (mPendingNotification) {
196 0 : mPendingNotification->Clear();
197 0 : mPendingNotification = nullptr;
198 : }
199 26 : mWatchDocument = nullptr;
200 26 : mWatchID = nullptr;
201 26 : mElement = nullptr;
202 26 : mReferencingImage = false;
203 26 : }
204 :
205 : bool
206 0 : nsReferencedElement::Observe(Element* aOldElement,
207 : Element* aNewElement, void* aData)
208 : {
209 0 : nsReferencedElement* p = static_cast<nsReferencedElement*>(aData);
210 0 : if (p->mPendingNotification) {
211 0 : p->mPendingNotification->SetTo(aNewElement);
212 : } else {
213 0 : NS_ASSERTION(aOldElement == p->mElement, "Failed to track content!");
214 : ChangeNotification* watcher =
215 0 : new ChangeNotification(p, aOldElement, aNewElement);
216 0 : p->mPendingNotification = watcher;
217 0 : nsContentUtils::AddScriptRunner(watcher);
218 : }
219 0 : bool keepTracking = p->IsPersistent();
220 0 : if (!keepTracking) {
221 0 : p->mWatchDocument = nullptr;
222 0 : p->mWatchID = nullptr;
223 : }
224 0 : return keepTracking;
225 : }
226 :
227 0 : NS_IMPL_ISUPPORTS_INHERITED0(nsReferencedElement::ChangeNotification,
228 : mozilla::Runnable)
229 :
230 0 : NS_IMPL_ISUPPORTS(nsReferencedElement::DocumentLoadNotification,
231 : nsIObserver)
232 :
233 : NS_IMETHODIMP
234 0 : nsReferencedElement::DocumentLoadNotification::Observe(nsISupports* aSubject,
235 : const char* aTopic,
236 : const char16_t* aData)
237 : {
238 0 : NS_ASSERTION(PL_strcmp(aTopic, "external-resource-document-created") == 0,
239 : "Unexpected topic");
240 0 : if (mTarget) {
241 0 : nsCOMPtr<nsIDocument> doc = do_QueryInterface(aSubject);
242 0 : mTarget->mPendingNotification = nullptr;
243 0 : NS_ASSERTION(!mTarget->mElement, "Why do we have content here?");
244 : // If we got here, that means we had Reset() called with aWatch ==
245 : // true. So keep watching if IsPersistent().
246 0 : mTarget->HaveNewDocument(doc, mTarget->IsPersistent(), mRef);
247 0 : mTarget->ElementChanged(nullptr, mTarget->mElement);
248 : }
249 0 : return NS_OK;
250 : }
|