1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=2 sw=2 et tw=78: */
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is mozilla.org code.
17 : *
18 : * The Initial Developer of the Original Code is Mozilla.org.
19 : * Portions created by the Initial Developer are Copyright (C) 2008
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Robert O'Callahan <robert@ocallahan.org>
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either of the GNU General Public License Version 2 or later (the "GPL"),
27 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "nsReferencedElement.h"
40 : #include "nsContentUtils.h"
41 : #include "nsIURI.h"
42 : #include "nsBindingManager.h"
43 : #include "nsEscape.h"
44 : #include "nsXBLPrototypeBinding.h"
45 : #include "nsIDOMNode.h"
46 : #include "nsIDOMDocument.h"
47 : #include "nsIDOMElement.h"
48 : #include "nsCycleCollectionParticipant.h"
49 :
50 : void
51 0 : nsReferencedElement::Reset(nsIContent* aFromContent, nsIURI* aURI,
52 : bool aWatch, bool aReferenceImage)
53 : {
54 0 : NS_ABORT_IF_FALSE(aFromContent, "Reset() expects non-null content pointer");
55 :
56 0 : Unlink();
57 :
58 0 : if (!aURI)
59 0 : return;
60 :
61 0 : nsCAutoString refPart;
62 0 : aURI->GetRef(refPart);
63 : // Unescape %-escapes in the reference. The result will be in the
64 : // origin charset of the URL, hopefully...
65 0 : NS_UnescapeURL(refPart);
66 :
67 0 : nsCAutoString charset;
68 0 : aURI->GetOriginCharset(charset);
69 0 : nsAutoString ref;
70 0 : nsresult rv = nsContentUtils::ConvertStringFromCharset(charset, refPart, ref);
71 0 : if (NS_FAILED(rv)) {
72 0 : CopyUTF8toUTF16(refPart, ref);
73 : }
74 0 : if (ref.IsEmpty())
75 : return;
76 :
77 : // Get the current document
78 0 : nsIDocument *doc = aFromContent->GetCurrentDoc();
79 0 : if (!doc)
80 : return;
81 :
82 0 : nsIContent* bindingParent = aFromContent->GetBindingParent();
83 0 : if (bindingParent) {
84 0 : nsXBLBinding* binding = doc->BindingManager()->GetBinding(bindingParent);
85 0 : if (binding) {
86 : bool isEqualExceptRef;
87 : rv = aURI->EqualsExceptRef(binding->PrototypeBinding()->DocURI(),
88 0 : &isEqualExceptRef);
89 0 : if (NS_SUCCEEDED(rv) && isEqualExceptRef) {
90 : // XXX sXBL/XBL2 issue
91 : // Our content is an anonymous XBL element from a binding inside the
92 : // same document that the referenced URI points to. In order to avoid
93 : // the risk of ID collisions we restrict ourselves to anonymous
94 : // elements from this binding; specifically, URIs that are relative to
95 : // the binding document should resolve to the copy of the target
96 : // element that has been inserted into the bound document.
97 : // If the URI points to a different document we don't need this
98 : // restriction.
99 : nsINodeList* anonymousChildren =
100 0 : doc->BindingManager()->GetAnonymousNodesFor(bindingParent);
101 :
102 0 : if (anonymousChildren) {
103 : PRUint32 length;
104 0 : anonymousChildren->GetLength(&length);
105 0 : for (PRUint32 i = 0; i < length && !mElement; ++i) {
106 : mElement =
107 0 : nsContentUtils::MatchElementId(anonymousChildren->GetNodeAt(i), ref);
108 : }
109 : }
110 :
111 : // We don't have watching working yet for XBL, so bail out here.
112 : return;
113 : }
114 : }
115 : }
116 :
117 : bool isEqualExceptRef;
118 0 : rv = aURI->EqualsExceptRef(doc->GetDocumentURI(), &isEqualExceptRef);
119 0 : if (NS_FAILED(rv) || !isEqualExceptRef) {
120 0 : nsRefPtr<nsIDocument::ExternalResourceLoad> load;
121 : doc = doc->RequestExternalResource(aURI, aFromContent,
122 0 : getter_AddRefs(load));
123 0 : if (!doc) {
124 0 : if (!load || !aWatch) {
125 : // Nothing will ever happen here
126 : return;
127 : }
128 :
129 : DocumentLoadNotification* observer =
130 0 : new DocumentLoadNotification(this, ref);
131 0 : mPendingNotification = observer;
132 0 : if (observer) {
133 0 : load->AddObserver(observer);
134 : }
135 : // Keep going so we set up our watching stuff a bit
136 : }
137 : }
138 :
139 0 : if (aWatch) {
140 0 : nsCOMPtr<nsIAtom> atom = do_GetAtom(ref);
141 0 : if (!atom)
142 : return;
143 0 : atom.swap(mWatchID);
144 : }
145 :
146 0 : mReferencingImage = aReferenceImage;
147 :
148 0 : HaveNewDocument(doc, aWatch, ref);
149 : }
150 :
151 : void
152 0 : nsReferencedElement::ResetWithID(nsIContent* aFromContent, const nsString& aID,
153 : bool aWatch)
154 : {
155 0 : nsIDocument *doc = aFromContent->GetCurrentDoc();
156 0 : if (!doc)
157 0 : return;
158 :
159 : // XXX Need to take care of XBL/XBL2
160 :
161 0 : if (aWatch) {
162 0 : nsCOMPtr<nsIAtom> atom = do_GetAtom(aID);
163 0 : if (!atom)
164 : return;
165 0 : atom.swap(mWatchID);
166 : }
167 :
168 0 : mReferencingImage = false;
169 :
170 0 : HaveNewDocument(doc, aWatch, aID);
171 : }
172 :
173 : void
174 0 : nsReferencedElement::HaveNewDocument(nsIDocument* aDocument, bool aWatch,
175 : const nsString& aRef)
176 : {
177 0 : if (aWatch) {
178 0 : mWatchDocument = aDocument;
179 0 : if (mWatchDocument) {
180 0 : mElement = mWatchDocument->AddIDTargetObserver(mWatchID, Observe, this,
181 0 : mReferencingImage);
182 : }
183 0 : return;
184 : }
185 :
186 0 : if (!aDocument) {
187 0 : return;
188 : }
189 :
190 0 : Element *e = mReferencingImage ? aDocument->LookupImageElement(aRef) :
191 0 : aDocument->GetElementById(aRef);
192 0 : if (e) {
193 0 : mElement = e;
194 : }
195 : }
196 :
197 : void
198 0 : nsReferencedElement::Traverse(nsCycleCollectionTraversalCallback* aCB)
199 : {
200 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCB, "mWatchDocument");
201 0 : aCB->NoteXPCOMChild(mWatchDocument);
202 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCB, "mContent");
203 0 : aCB->NoteXPCOMChild(mElement);
204 0 : }
205 :
206 : void
207 0 : nsReferencedElement::Unlink()
208 : {
209 0 : if (mWatchDocument && mWatchID) {
210 0 : mWatchDocument->RemoveIDTargetObserver(mWatchID, Observe, this,
211 0 : mReferencingImage);
212 : }
213 0 : if (mPendingNotification) {
214 0 : mPendingNotification->Clear();
215 0 : mPendingNotification = nsnull;
216 : }
217 0 : mWatchDocument = nsnull;
218 0 : mWatchID = nsnull;
219 0 : mElement = nsnull;
220 0 : mReferencingImage = false;
221 0 : }
222 :
223 : bool
224 0 : nsReferencedElement::Observe(Element* aOldElement,
225 : Element* aNewElement, void* aData)
226 : {
227 0 : nsReferencedElement* p = static_cast<nsReferencedElement*>(aData);
228 0 : if (p->mPendingNotification) {
229 0 : p->mPendingNotification->SetTo(aNewElement);
230 : } else {
231 0 : NS_ASSERTION(aOldElement == p->mElement, "Failed to track content!");
232 : ChangeNotification* watcher =
233 0 : new ChangeNotification(p, aOldElement, aNewElement);
234 0 : p->mPendingNotification = watcher;
235 0 : nsContentUtils::AddScriptRunner(watcher);
236 : }
237 0 : bool keepTracking = p->IsPersistent();
238 0 : if (!keepTracking) {
239 0 : p->mWatchDocument = nsnull;
240 0 : p->mWatchID = nsnull;
241 : }
242 0 : return keepTracking;
243 : }
244 :
245 0 : NS_IMPL_ISUPPORTS_INHERITED0(nsReferencedElement::ChangeNotification,
246 : nsRunnable)
247 :
248 0 : NS_IMPL_ISUPPORTS1(nsReferencedElement::DocumentLoadNotification,
249 : nsIObserver)
250 :
251 : NS_IMETHODIMP
252 0 : nsReferencedElement::DocumentLoadNotification::Observe(nsISupports* aSubject,
253 : const char* aTopic,
254 : const PRUnichar* aData)
255 : {
256 0 : NS_ASSERTION(PL_strcmp(aTopic, "external-resource-document-created") == 0,
257 : "Unexpected topic");
258 0 : if (mTarget) {
259 0 : nsCOMPtr<nsIDocument> doc = do_QueryInterface(aSubject);
260 0 : mTarget->mPendingNotification = nsnull;
261 0 : NS_ASSERTION(!mTarget->mElement, "Why do we have content here?");
262 : // If we got here, that means we had Reset() called with aWatch ==
263 : // true. So keep watching if IsPersistent().
264 0 : mTarget->HaveNewDocument(doc, mTarget->IsPersistent(), mRef);
265 0 : mTarget->ElementChanged(nsnull, mTarget->mElement);
266 : }
267 0 : return NS_OK;
268 : }
|