|
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
|
2 /* vim: set ts=4 sw=4 et 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 /* |
|
8 |
|
9 An implementation for the XUL document. This implementation serves |
|
10 as the basis for generating an NGLayout content model. |
|
11 |
|
12 Notes |
|
13 ----- |
|
14 |
|
15 1. We do some monkey business in the document observer methods to` |
|
16 keep the element map in sync for HTML elements. Why don't we just |
|
17 do it for _all_ elements? Well, in the case of XUL elements, |
|
18 which may be lazily created during frame construction, the |
|
19 document observer methods will never be called because we'll be |
|
20 adding the XUL nodes into the content model "quietly". |
|
21 |
|
22 */ |
|
23 |
|
24 #include "mozilla/ArrayUtils.h" |
|
25 |
|
26 // Note the ALPHABETICAL ORDERING |
|
27 #include "XULDocument.h" |
|
28 |
|
29 #include "nsError.h" |
|
30 #include "nsIBoxObject.h" |
|
31 #include "nsIChromeRegistry.h" |
|
32 #include "nsView.h" |
|
33 #include "nsViewManager.h" |
|
34 #include "nsIContentViewer.h" |
|
35 #include "nsIDOMXULElement.h" |
|
36 #include "nsIRDFNode.h" |
|
37 #include "nsIRDFRemoteDataSource.h" |
|
38 #include "nsIRDFService.h" |
|
39 #include "nsIStreamListener.h" |
|
40 #include "nsITimer.h" |
|
41 #include "nsDocShell.h" |
|
42 #include "nsGkAtoms.h" |
|
43 #include "nsXMLContentSink.h" |
|
44 #include "nsXULContentSink.h" |
|
45 #include "nsXULContentUtils.h" |
|
46 #include "nsIXULOverlayProvider.h" |
|
47 #include "nsNetUtil.h" |
|
48 #include "nsParserCIID.h" |
|
49 #include "nsPIBoxObject.h" |
|
50 #include "nsRDFCID.h" |
|
51 #include "nsILocalStore.h" |
|
52 #include "nsXPIDLString.h" |
|
53 #include "nsPIDOMWindow.h" |
|
54 #include "nsPIWindowRoot.h" |
|
55 #include "nsXULCommandDispatcher.h" |
|
56 #include "nsXULElement.h" |
|
57 #include "prlog.h" |
|
58 #include "rdf.h" |
|
59 #include "nsIFrame.h" |
|
60 #include "nsXBLService.h" |
|
61 #include "nsCExternalHandlerService.h" |
|
62 #include "nsMimeTypes.h" |
|
63 #include "nsIObjectInputStream.h" |
|
64 #include "nsIObjectOutputStream.h" |
|
65 #include "nsContentList.h" |
|
66 #include "nsIScriptGlobalObject.h" |
|
67 #include "nsIScriptSecurityManager.h" |
|
68 #include "nsNodeInfoManager.h" |
|
69 #include "nsContentCreatorFunctions.h" |
|
70 #include "nsContentUtils.h" |
|
71 #include "nsIParser.h" |
|
72 #include "nsCharsetSource.h" |
|
73 #include "nsIParserService.h" |
|
74 #include "nsCSSStyleSheet.h" |
|
75 #include "mozilla/css/Loader.h" |
|
76 #include "nsIScriptError.h" |
|
77 #include "nsIStyleSheetLinkingElement.h" |
|
78 #include "nsIObserverService.h" |
|
79 #include "nsNodeUtils.h" |
|
80 #include "nsIDocShellTreeOwner.h" |
|
81 #include "nsIXULWindow.h" |
|
82 #include "nsXULPopupManager.h" |
|
83 #include "nsCCUncollectableMarker.h" |
|
84 #include "nsURILoader.h" |
|
85 #include "mozilla/BasicEvents.h" |
|
86 #include "mozilla/dom/Element.h" |
|
87 #include "mozilla/dom/ProcessingInstruction.h" |
|
88 #include "mozilla/dom/XULDocumentBinding.h" |
|
89 #include "mozilla/EventDispatcher.h" |
|
90 #include "mozilla/Preferences.h" |
|
91 #include "nsTextNode.h" |
|
92 #include "nsJSUtils.h" |
|
93 #include "mozilla/dom/URL.h" |
|
94 |
|
95 using namespace mozilla; |
|
96 using namespace mozilla::dom; |
|
97 |
|
98 //---------------------------------------------------------------------- |
|
99 // |
|
100 // CIDs |
|
101 // |
|
102 |
|
103 static NS_DEFINE_CID(kParserCID, NS_PARSER_CID); |
|
104 |
|
105 static bool IsOverlayAllowed(nsIURI* aURI) |
|
106 { |
|
107 bool canOverlay = false; |
|
108 if (NS_SUCCEEDED(aURI->SchemeIs("about", &canOverlay)) && canOverlay) |
|
109 return true; |
|
110 if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &canOverlay)) && canOverlay) |
|
111 return true; |
|
112 return false; |
|
113 } |
|
114 |
|
115 //---------------------------------------------------------------------- |
|
116 // |
|
117 // Miscellaneous Constants |
|
118 // |
|
119 |
|
120 const nsForwardReference::Phase nsForwardReference::kPasses[] = { |
|
121 nsForwardReference::eConstruction, |
|
122 nsForwardReference::eHookup, |
|
123 nsForwardReference::eDone |
|
124 }; |
|
125 |
|
126 const uint32_t kMaxAttrNameLength = 512; |
|
127 const uint32_t kMaxAttributeLength = 4096; |
|
128 |
|
129 //---------------------------------------------------------------------- |
|
130 // |
|
131 // Statics |
|
132 // |
|
133 |
|
134 int32_t XULDocument::gRefCnt = 0; |
|
135 |
|
136 nsIRDFService* XULDocument::gRDFService; |
|
137 nsIRDFResource* XULDocument::kNC_persist; |
|
138 nsIRDFResource* XULDocument::kNC_attribute; |
|
139 nsIRDFResource* XULDocument::kNC_value; |
|
140 |
|
141 PRLogModuleInfo* XULDocument::gXULLog; |
|
142 |
|
143 //---------------------------------------------------------------------- |
|
144 |
|
145 struct BroadcasterMapEntry : public PLDHashEntryHdr { |
|
146 Element* mBroadcaster; // [WEAK] |
|
147 nsSmallVoidArray mListeners; // [OWNING] of BroadcastListener objects |
|
148 }; |
|
149 |
|
150 struct BroadcastListener { |
|
151 nsWeakPtr mListener; |
|
152 nsCOMPtr<nsIAtom> mAttribute; |
|
153 }; |
|
154 |
|
155 Element* |
|
156 nsRefMapEntry::GetFirstElement() |
|
157 { |
|
158 return static_cast<Element*>(mRefContentList.SafeElementAt(0)); |
|
159 } |
|
160 |
|
161 void |
|
162 nsRefMapEntry::AppendAll(nsCOMArray<nsIContent>* aElements) |
|
163 { |
|
164 for (int32_t i = 0; i < mRefContentList.Count(); ++i) { |
|
165 aElements->AppendObject(static_cast<nsIContent*>(mRefContentList[i])); |
|
166 } |
|
167 } |
|
168 |
|
169 bool |
|
170 nsRefMapEntry::AddElement(Element* aElement) |
|
171 { |
|
172 if (mRefContentList.IndexOf(aElement) >= 0) |
|
173 return true; |
|
174 return mRefContentList.AppendElement(aElement); |
|
175 } |
|
176 |
|
177 bool |
|
178 nsRefMapEntry::RemoveElement(Element* aElement) |
|
179 { |
|
180 mRefContentList.RemoveElement(aElement); |
|
181 return mRefContentList.Count() == 0; |
|
182 } |
|
183 |
|
184 //---------------------------------------------------------------------- |
|
185 // |
|
186 // ctors & dtors |
|
187 // |
|
188 |
|
189 namespace mozilla { |
|
190 namespace dom { |
|
191 |
|
192 XULDocument::XULDocument(void) |
|
193 : XMLDocument("application/vnd.mozilla.xul+xml"), |
|
194 mDocLWTheme(Doc_Theme_Uninitialized), |
|
195 mState(eState_Master), |
|
196 mResolutionPhase(nsForwardReference::eStart) |
|
197 { |
|
198 // NOTE! nsDocument::operator new() zeroes out all members, so don't |
|
199 // bother initializing members to 0. |
|
200 |
|
201 // Override the default in nsDocument |
|
202 mCharacterSet.AssignLiteral("UTF-8"); |
|
203 |
|
204 mDefaultElementType = kNameSpaceID_XUL; |
|
205 mIsXUL = true; |
|
206 |
|
207 mDelayFrameLoaderInitialization = true; |
|
208 |
|
209 mAllowXULXBL = eTriTrue; |
|
210 } |
|
211 |
|
212 XULDocument::~XULDocument() |
|
213 { |
|
214 NS_ASSERTION(mNextSrcLoadWaiter == nullptr, |
|
215 "unreferenced document still waiting for script source to load?"); |
|
216 |
|
217 // In case we failed somewhere early on and the forward observer |
|
218 // decls never got resolved. |
|
219 mForwardReferences.Clear(); |
|
220 // Likewise for any references we have to IDs where we might |
|
221 // look for persisted data: |
|
222 mPersistenceIds.Clear(); |
|
223 |
|
224 // Destroy our broadcaster map. |
|
225 if (mBroadcasterMap) { |
|
226 PL_DHashTableDestroy(mBroadcasterMap); |
|
227 } |
|
228 |
|
229 if (mLocalStore) { |
|
230 nsCOMPtr<nsIRDFRemoteDataSource> remote = |
|
231 do_QueryInterface(mLocalStore); |
|
232 if (remote) |
|
233 remote->Flush(); |
|
234 } |
|
235 |
|
236 delete mTemplateBuilderTable; |
|
237 |
|
238 Preferences::UnregisterCallback(XULDocument::DirectionChanged, |
|
239 "intl.uidirection.", this); |
|
240 |
|
241 if (--gRefCnt == 0) { |
|
242 NS_IF_RELEASE(gRDFService); |
|
243 |
|
244 NS_IF_RELEASE(kNC_persist); |
|
245 NS_IF_RELEASE(kNC_attribute); |
|
246 NS_IF_RELEASE(kNC_value); |
|
247 } |
|
248 |
|
249 if (mOffThreadCompileStringBuf) { |
|
250 js_free(mOffThreadCompileStringBuf); |
|
251 } |
|
252 } |
|
253 |
|
254 } // namespace dom |
|
255 } // namespace mozilla |
|
256 |
|
257 nsresult |
|
258 NS_NewXULDocument(nsIXULDocument** result) |
|
259 { |
|
260 NS_PRECONDITION(result != nullptr, "null ptr"); |
|
261 if (! result) |
|
262 return NS_ERROR_NULL_POINTER; |
|
263 |
|
264 XULDocument* doc = new XULDocument(); |
|
265 if (! doc) |
|
266 return NS_ERROR_OUT_OF_MEMORY; |
|
267 |
|
268 NS_ADDREF(doc); |
|
269 |
|
270 nsresult rv; |
|
271 if (NS_FAILED(rv = doc->Init())) { |
|
272 NS_RELEASE(doc); |
|
273 return rv; |
|
274 } |
|
275 |
|
276 *result = doc; |
|
277 return NS_OK; |
|
278 } |
|
279 |
|
280 |
|
281 namespace mozilla { |
|
282 namespace dom { |
|
283 |
|
284 //---------------------------------------------------------------------- |
|
285 // |
|
286 // nsISupports interface |
|
287 // |
|
288 |
|
289 static PLDHashOperator |
|
290 TraverseTemplateBuilders(nsISupports* aKey, nsIXULTemplateBuilder* aData, |
|
291 void* aContext) |
|
292 { |
|
293 nsCycleCollectionTraversalCallback *cb = |
|
294 static_cast<nsCycleCollectionTraversalCallback*>(aContext); |
|
295 |
|
296 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mTemplateBuilderTable key"); |
|
297 cb->NoteXPCOMChild(aKey); |
|
298 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mTemplateBuilderTable value"); |
|
299 cb->NoteXPCOMChild(aData); |
|
300 |
|
301 return PL_DHASH_NEXT; |
|
302 } |
|
303 |
|
304 static PLDHashOperator |
|
305 TraverseObservers(nsIURI* aKey, nsIObserver* aData, void* aContext) |
|
306 { |
|
307 nsCycleCollectionTraversalCallback *cb = |
|
308 static_cast<nsCycleCollectionTraversalCallback*>(aContext); |
|
309 |
|
310 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mOverlayLoadObservers/mPendingOverlayLoadNotifications value"); |
|
311 cb->NoteXPCOMChild(aData); |
|
312 |
|
313 return PL_DHASH_NEXT; |
|
314 } |
|
315 |
|
316 NS_IMPL_CYCLE_COLLECTION_CLASS(XULDocument) |
|
317 |
|
318 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(XULDocument, XMLDocument) |
|
319 NS_ASSERTION(!nsCCUncollectableMarker::InGeneration(cb, tmp->GetMarkedCCGeneration()), |
|
320 "Shouldn't traverse XULDocument!"); |
|
321 // XXX tmp->mForwardReferences? |
|
322 // XXX tmp->mContextStack? |
|
323 |
|
324 // An element will only have a template builder as long as it's in the |
|
325 // document, so we'll traverse the table here instead of from the element. |
|
326 if (tmp->mTemplateBuilderTable) |
|
327 tmp->mTemplateBuilderTable->EnumerateRead(TraverseTemplateBuilders, &cb); |
|
328 |
|
329 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCurrentPrototype) |
|
330 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMasterPrototype) |
|
331 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCommandDispatcher) |
|
332 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrototypes); |
|
333 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocalStore) |
|
334 |
|
335 if (tmp->mOverlayLoadObservers) { |
|
336 tmp->mOverlayLoadObservers->EnumerateRead(TraverseObservers, &cb); |
|
337 } |
|
338 if (tmp->mPendingOverlayLoadNotifications) { |
|
339 tmp->mPendingOverlayLoadNotifications->EnumerateRead(TraverseObservers, &cb); |
|
340 } |
|
341 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
|
342 |
|
343 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(XULDocument, XMLDocument) |
|
344 delete tmp->mTemplateBuilderTable; |
|
345 tmp->mTemplateBuilderTable = nullptr; |
|
346 |
|
347 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCommandDispatcher) |
|
348 //XXX We should probably unlink all the objects we traverse. |
|
349 NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
|
350 |
|
351 NS_IMPL_ADDREF_INHERITED(XULDocument, XMLDocument) |
|
352 NS_IMPL_RELEASE_INHERITED(XULDocument, XMLDocument) |
|
353 |
|
354 |
|
355 // QueryInterface implementation for XULDocument |
|
356 NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(XULDocument) |
|
357 NS_INTERFACE_TABLE_INHERITED(XULDocument, nsIXULDocument, |
|
358 nsIDOMXULDocument, nsIStreamLoaderObserver, |
|
359 nsICSSLoaderObserver, nsIOffThreadScriptReceiver) |
|
360 NS_INTERFACE_TABLE_TAIL_INHERITING(XMLDocument) |
|
361 |
|
362 |
|
363 //---------------------------------------------------------------------- |
|
364 // |
|
365 // nsIDocument interface |
|
366 // |
|
367 |
|
368 void |
|
369 XULDocument::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup) |
|
370 { |
|
371 NS_NOTREACHED("Reset"); |
|
372 } |
|
373 |
|
374 void |
|
375 XULDocument::ResetToURI(nsIURI* aURI, nsILoadGroup* aLoadGroup, |
|
376 nsIPrincipal* aPrincipal) |
|
377 { |
|
378 NS_NOTREACHED("ResetToURI"); |
|
379 } |
|
380 |
|
381 void |
|
382 XULDocument::SetContentType(const nsAString& aContentType) |
|
383 { |
|
384 NS_ASSERTION(aContentType.EqualsLiteral("application/vnd.mozilla.xul+xml"), |
|
385 "xul-documents always has content-type application/vnd.mozilla.xul+xml"); |
|
386 // Don't do anything, xul always has the mimetype |
|
387 // application/vnd.mozilla.xul+xml |
|
388 } |
|
389 |
|
390 // This is called when the master document begins loading, whether it's |
|
391 // being cached or not. |
|
392 nsresult |
|
393 XULDocument::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel, |
|
394 nsILoadGroup* aLoadGroup, |
|
395 nsISupports* aContainer, |
|
396 nsIStreamListener **aDocListener, |
|
397 bool aReset, nsIContentSink* aSink) |
|
398 { |
|
399 #ifdef PR_LOGGING |
|
400 if (PR_LOG_TEST(gXULLog, PR_LOG_WARNING)) { |
|
401 |
|
402 nsCOMPtr<nsIURI> uri; |
|
403 nsresult rv = aChannel->GetOriginalURI(getter_AddRefs(uri)); |
|
404 if (NS_SUCCEEDED(rv)) { |
|
405 nsAutoCString urlspec; |
|
406 rv = uri->GetSpec(urlspec); |
|
407 if (NS_SUCCEEDED(rv)) { |
|
408 PR_LOG(gXULLog, PR_LOG_WARNING, |
|
409 ("xul: load document '%s'", urlspec.get())); |
|
410 } |
|
411 } |
|
412 } |
|
413 #endif |
|
414 // NOTE: If this ever starts calling nsDocument::StartDocumentLoad |
|
415 // we'll possibly need to reset our content type afterwards. |
|
416 mStillWalking = true; |
|
417 mMayStartLayout = false; |
|
418 mDocumentLoadGroup = do_GetWeakReference(aLoadGroup); |
|
419 |
|
420 mChannel = aChannel; |
|
421 |
|
422 mHaveInputEncoding = true; |
|
423 |
|
424 // Get the URI. Note that this should match nsDocShell::OnLoadingSite |
|
425 nsresult rv = |
|
426 NS_GetFinalChannelURI(aChannel, getter_AddRefs(mDocumentURI)); |
|
427 NS_ENSURE_SUCCESS(rv, rv); |
|
428 |
|
429 ResetStylesheetsToURI(mDocumentURI); |
|
430 |
|
431 RetrieveRelevantHeaders(aChannel); |
|
432 |
|
433 // Look in the chrome cache: we've got this puppy loaded |
|
434 // already. |
|
435 nsXULPrototypeDocument* proto = IsChromeURI(mDocumentURI) ? |
|
436 nsXULPrototypeCache::GetInstance()->GetPrototype(mDocumentURI) : |
|
437 nullptr; |
|
438 |
|
439 // Same comment as nsChromeProtocolHandler::NewChannel and |
|
440 // XULDocument::ResumeWalk |
|
441 // - Ben Goodger |
|
442 // |
|
443 // We don't abort on failure here because there are too many valid |
|
444 // cases that can return failure, and the null-ness of |proto| is enough |
|
445 // to trigger the fail-safe parse-from-disk solution. Example failure cases |
|
446 // (for reference) include: |
|
447 // |
|
448 // NS_ERROR_NOT_AVAILABLE: the URI cannot be found in the startup cache, |
|
449 // parse from disk |
|
450 // other: the startup cache file could not be found, probably |
|
451 // due to being accessed before a profile has been selected (e.g. |
|
452 // loading chrome for the profile manager itself). This must be |
|
453 // parsed from disk. |
|
454 |
|
455 if (proto) { |
|
456 // If we're racing with another document to load proto, wait till the |
|
457 // load has finished loading before trying to add cloned style sheets. |
|
458 // XULDocument::EndLoad will call proto->NotifyLoadDone, which will |
|
459 // find all racing documents and notify them via OnPrototypeLoadDone, |
|
460 // which will add style sheet clones to each document. |
|
461 bool loaded; |
|
462 rv = proto->AwaitLoadDone(this, &loaded); |
|
463 if (NS_FAILED(rv)) return rv; |
|
464 |
|
465 mMasterPrototype = mCurrentPrototype = proto; |
|
466 |
|
467 // Set up the right principal on ourselves. |
|
468 SetPrincipal(proto->DocumentPrincipal()); |
|
469 |
|
470 // We need a listener, even if proto is not yet loaded, in which |
|
471 // event the listener's OnStopRequest method does nothing, and all |
|
472 // the interesting work happens below XULDocument::EndLoad, from |
|
473 // the call there to mCurrentPrototype->NotifyLoadDone(). |
|
474 *aDocListener = new CachedChromeStreamListener(this, loaded); |
|
475 if (! *aDocListener) |
|
476 return NS_ERROR_OUT_OF_MEMORY; |
|
477 } |
|
478 else { |
|
479 bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled(); |
|
480 bool fillXULCache = (useXULCache && IsChromeURI(mDocumentURI)); |
|
481 |
|
482 |
|
483 // It's just a vanilla document load. Create a parser to deal |
|
484 // with the stream n' stuff. |
|
485 |
|
486 nsCOMPtr<nsIParser> parser; |
|
487 rv = PrepareToLoad(aContainer, aCommand, aChannel, aLoadGroup, |
|
488 getter_AddRefs(parser)); |
|
489 if (NS_FAILED(rv)) return rv; |
|
490 |
|
491 // Predicate mIsWritingFastLoad on the XUL cache being enabled, |
|
492 // so we don't have to re-check whether the cache is enabled all |
|
493 // the time. |
|
494 mIsWritingFastLoad = useXULCache; |
|
495 |
|
496 nsCOMPtr<nsIStreamListener> listener = do_QueryInterface(parser, &rv); |
|
497 NS_ASSERTION(NS_SUCCEEDED(rv), "parser doesn't support nsIStreamListener"); |
|
498 if (NS_FAILED(rv)) return rv; |
|
499 |
|
500 *aDocListener = listener; |
|
501 |
|
502 parser->Parse(mDocumentURI); |
|
503 |
|
504 // Put the current prototype, created under PrepareToLoad, into the |
|
505 // XUL prototype cache now. We can't do this under PrepareToLoad or |
|
506 // overlay loading will break; search for PutPrototype in ResumeWalk |
|
507 // and see the comment there. |
|
508 if (fillXULCache) { |
|
509 nsXULPrototypeCache::GetInstance()->PutPrototype(mCurrentPrototype); |
|
510 } |
|
511 } |
|
512 |
|
513 NS_IF_ADDREF(*aDocListener); |
|
514 return NS_OK; |
|
515 } |
|
516 |
|
517 // This gets invoked after a prototype for this document or one of |
|
518 // its overlays is fully built in the content sink. |
|
519 void |
|
520 XULDocument::EndLoad() |
|
521 { |
|
522 // This can happen if an overlay fails to load |
|
523 if (!mCurrentPrototype) |
|
524 return; |
|
525 |
|
526 nsresult rv; |
|
527 |
|
528 // Whack the prototype document into the cache so that the next |
|
529 // time somebody asks for it, they don't need to load it by hand. |
|
530 |
|
531 nsCOMPtr<nsIURI> uri = mCurrentPrototype->GetURI(); |
|
532 bool isChrome = IsChromeURI(uri); |
|
533 |
|
534 // Remember if the XUL cache is on |
|
535 bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled(); |
|
536 |
|
537 // If the current prototype is an overlay document (non-master prototype) |
|
538 // and we're filling the FastLoad disk cache, tell the cache we're done |
|
539 // loading it, and write the prototype. The master prototype is put into |
|
540 // the cache earlier in XULDocument::StartDocumentLoad. |
|
541 if (useXULCache && mIsWritingFastLoad && isChrome && |
|
542 mMasterPrototype != mCurrentPrototype) { |
|
543 nsXULPrototypeCache::GetInstance()->WritePrototype(mCurrentPrototype); |
|
544 } |
|
545 |
|
546 if (IsOverlayAllowed(uri)) { |
|
547 nsCOMPtr<nsIXULOverlayProvider> reg = |
|
548 mozilla::services::GetXULOverlayProviderService(); |
|
549 |
|
550 if (reg) { |
|
551 nsCOMPtr<nsISimpleEnumerator> overlays; |
|
552 rv = reg->GetStyleOverlays(uri, getter_AddRefs(overlays)); |
|
553 if (NS_FAILED(rv)) return; |
|
554 |
|
555 bool moreSheets; |
|
556 nsCOMPtr<nsISupports> next; |
|
557 nsCOMPtr<nsIURI> sheetURI; |
|
558 |
|
559 while (NS_SUCCEEDED(rv = overlays->HasMoreElements(&moreSheets)) && |
|
560 moreSheets) { |
|
561 overlays->GetNext(getter_AddRefs(next)); |
|
562 |
|
563 sheetURI = do_QueryInterface(next); |
|
564 if (!sheetURI) { |
|
565 NS_ERROR("Chrome registry handed me a non-nsIURI object!"); |
|
566 continue; |
|
567 } |
|
568 |
|
569 if (IsChromeURI(sheetURI)) { |
|
570 mCurrentPrototype->AddStyleSheetReference(sheetURI); |
|
571 } |
|
572 } |
|
573 } |
|
574 |
|
575 if (isChrome && useXULCache) { |
|
576 // If it's a chrome prototype document, then notify any |
|
577 // documents that raced to load the prototype, and awaited |
|
578 // its load completion via proto->AwaitLoadDone(). |
|
579 rv = mCurrentPrototype->NotifyLoadDone(); |
|
580 if (NS_FAILED(rv)) return; |
|
581 } |
|
582 } |
|
583 |
|
584 OnPrototypeLoadDone(true); |
|
585 #ifdef PR_LOGGING |
|
586 if (PR_LOG_TEST(gXULLog, PR_LOG_WARNING)) { |
|
587 nsAutoCString urlspec; |
|
588 rv = uri->GetSpec(urlspec); |
|
589 if (NS_SUCCEEDED(rv)) { |
|
590 PR_LOG(gXULLog, PR_LOG_WARNING, |
|
591 ("xul: Finished loading document '%s'", urlspec.get())); |
|
592 } |
|
593 } |
|
594 #endif |
|
595 } |
|
596 |
|
597 NS_IMETHODIMP |
|
598 XULDocument::OnPrototypeLoadDone(bool aResumeWalk) |
|
599 { |
|
600 nsresult rv; |
|
601 |
|
602 // Add the style overlays from chrome registry, if any. |
|
603 rv = AddPrototypeSheets(); |
|
604 if (NS_FAILED(rv)) return rv; |
|
605 |
|
606 rv = PrepareToWalk(); |
|
607 NS_ASSERTION(NS_SUCCEEDED(rv), "unable to prepare for walk"); |
|
608 if (NS_FAILED(rv)) return rv; |
|
609 |
|
610 if (aResumeWalk) { |
|
611 rv = ResumeWalk(); |
|
612 } |
|
613 return rv; |
|
614 } |
|
615 |
|
616 // called when an error occurs parsing a document |
|
617 bool |
|
618 XULDocument::OnDocumentParserError() |
|
619 { |
|
620 // don't report errors that are from overlays |
|
621 if (mCurrentPrototype && mMasterPrototype != mCurrentPrototype) { |
|
622 nsCOMPtr<nsIURI> uri = mCurrentPrototype->GetURI(); |
|
623 if (IsChromeURI(uri)) { |
|
624 nsCOMPtr<nsIObserverService> os = |
|
625 mozilla::services::GetObserverService(); |
|
626 if (os) |
|
627 os->NotifyObservers(uri, "xul-overlay-parsererror", |
|
628 EmptyString().get()); |
|
629 } |
|
630 |
|
631 return false; |
|
632 } |
|
633 |
|
634 return true; |
|
635 } |
|
636 |
|
637 static void |
|
638 ClearBroadcasterMapEntry(PLDHashTable* aTable, PLDHashEntryHdr* aEntry) |
|
639 { |
|
640 BroadcasterMapEntry* entry = |
|
641 static_cast<BroadcasterMapEntry*>(aEntry); |
|
642 for (int32_t i = entry->mListeners.Count() - 1; i >= 0; --i) { |
|
643 delete (BroadcastListener*)entry->mListeners[i]; |
|
644 } |
|
645 |
|
646 // N.B. that we need to manually run the dtor because we |
|
647 // constructed the nsSmallVoidArray object in-place. |
|
648 entry->mListeners.~nsSmallVoidArray(); |
|
649 } |
|
650 |
|
651 static bool |
|
652 CanBroadcast(int32_t aNameSpaceID, nsIAtom* aAttribute) |
|
653 { |
|
654 // Don't push changes to the |id|, |ref|, |persist|, |command| or |
|
655 // |observes| attribute. |
|
656 if (aNameSpaceID == kNameSpaceID_None) { |
|
657 if ((aAttribute == nsGkAtoms::id) || |
|
658 (aAttribute == nsGkAtoms::ref) || |
|
659 (aAttribute == nsGkAtoms::persist) || |
|
660 (aAttribute == nsGkAtoms::command) || |
|
661 (aAttribute == nsGkAtoms::observes)) { |
|
662 return false; |
|
663 } |
|
664 } |
|
665 return true; |
|
666 } |
|
667 |
|
668 struct nsAttrNameInfo |
|
669 { |
|
670 nsAttrNameInfo(int32_t aNamespaceID, nsIAtom* aName, nsIAtom* aPrefix) : |
|
671 mNamespaceID(aNamespaceID), mName(aName), mPrefix(aPrefix) {} |
|
672 nsAttrNameInfo(const nsAttrNameInfo& aOther) : |
|
673 mNamespaceID(aOther.mNamespaceID), mName(aOther.mName), |
|
674 mPrefix(aOther.mPrefix) {} |
|
675 int32_t mNamespaceID; |
|
676 nsCOMPtr<nsIAtom> mName; |
|
677 nsCOMPtr<nsIAtom> mPrefix; |
|
678 }; |
|
679 |
|
680 void |
|
681 XULDocument::SynchronizeBroadcastListener(Element *aBroadcaster, |
|
682 Element *aListener, |
|
683 const nsAString &aAttr) |
|
684 { |
|
685 if (!nsContentUtils::IsSafeToRunScript()) { |
|
686 nsDelayedBroadcastUpdate delayedUpdate(aBroadcaster, aListener, |
|
687 aAttr); |
|
688 mDelayedBroadcasters.AppendElement(delayedUpdate); |
|
689 MaybeBroadcast(); |
|
690 return; |
|
691 } |
|
692 bool notify = mDocumentLoaded || mHandlingDelayedBroadcasters; |
|
693 |
|
694 if (aAttr.EqualsLiteral("*")) { |
|
695 uint32_t count = aBroadcaster->GetAttrCount(); |
|
696 nsTArray<nsAttrNameInfo> attributes(count); |
|
697 for (uint32_t i = 0; i < count; ++i) { |
|
698 const nsAttrName* attrName = aBroadcaster->GetAttrNameAt(i); |
|
699 int32_t nameSpaceID = attrName->NamespaceID(); |
|
700 nsIAtom* name = attrName->LocalName(); |
|
701 |
|
702 // _Don't_ push the |id|, |ref|, or |persist| attribute's value! |
|
703 if (! CanBroadcast(nameSpaceID, name)) |
|
704 continue; |
|
705 |
|
706 attributes.AppendElement(nsAttrNameInfo(nameSpaceID, name, |
|
707 attrName->GetPrefix())); |
|
708 } |
|
709 |
|
710 count = attributes.Length(); |
|
711 while (count-- > 0) { |
|
712 int32_t nameSpaceID = attributes[count].mNamespaceID; |
|
713 nsIAtom* name = attributes[count].mName; |
|
714 nsAutoString value; |
|
715 if (aBroadcaster->GetAttr(nameSpaceID, name, value)) { |
|
716 aListener->SetAttr(nameSpaceID, name, attributes[count].mPrefix, |
|
717 value, notify); |
|
718 } |
|
719 |
|
720 #if 0 |
|
721 // XXX we don't fire the |onbroadcast| handler during |
|
722 // initial hookup: doing so would potentially run the |
|
723 // |onbroadcast| handler before the |onload| handler, |
|
724 // which could define JS properties that mask XBL |
|
725 // properties, etc. |
|
726 ExecuteOnBroadcastHandlerFor(aBroadcaster, aListener, name); |
|
727 #endif |
|
728 } |
|
729 } |
|
730 else { |
|
731 // Find out if the attribute is even present at all. |
|
732 nsCOMPtr<nsIAtom> name = do_GetAtom(aAttr); |
|
733 |
|
734 nsAutoString value; |
|
735 if (aBroadcaster->GetAttr(kNameSpaceID_None, name, value)) { |
|
736 aListener->SetAttr(kNameSpaceID_None, name, value, notify); |
|
737 } else { |
|
738 aListener->UnsetAttr(kNameSpaceID_None, name, notify); |
|
739 } |
|
740 |
|
741 #if 0 |
|
742 // XXX we don't fire the |onbroadcast| handler during initial |
|
743 // hookup: doing so would potentially run the |onbroadcast| |
|
744 // handler before the |onload| handler, which could define JS |
|
745 // properties that mask XBL properties, etc. |
|
746 ExecuteOnBroadcastHandlerFor(aBroadcaster, aListener, name); |
|
747 #endif |
|
748 } |
|
749 } |
|
750 |
|
751 NS_IMETHODIMP |
|
752 XULDocument::AddBroadcastListenerFor(nsIDOMElement* aBroadcaster, |
|
753 nsIDOMElement* aListener, |
|
754 const nsAString& aAttr) |
|
755 { |
|
756 ErrorResult rv; |
|
757 nsCOMPtr<Element> broadcaster = do_QueryInterface(aBroadcaster); |
|
758 nsCOMPtr<Element> listener = do_QueryInterface(aListener); |
|
759 NS_ENSURE_ARG(broadcaster && listener); |
|
760 AddBroadcastListenerFor(*broadcaster, *listener, aAttr, rv); |
|
761 return rv.ErrorCode(); |
|
762 } |
|
763 |
|
764 void |
|
765 XULDocument::AddBroadcastListenerFor(Element& aBroadcaster, Element& aListener, |
|
766 const nsAString& aAttr, ErrorResult& aRv) |
|
767 { |
|
768 nsresult rv = |
|
769 nsContentUtils::CheckSameOrigin(this, &aBroadcaster); |
|
770 |
|
771 if (NS_FAILED(rv)) { |
|
772 aRv.Throw(rv); |
|
773 return; |
|
774 } |
|
775 |
|
776 rv = nsContentUtils::CheckSameOrigin(this, &aListener); |
|
777 |
|
778 if (NS_FAILED(rv)) { |
|
779 aRv.Throw(rv); |
|
780 return; |
|
781 } |
|
782 |
|
783 static const PLDHashTableOps gOps = { |
|
784 PL_DHashAllocTable, |
|
785 PL_DHashFreeTable, |
|
786 PL_DHashVoidPtrKeyStub, |
|
787 PL_DHashMatchEntryStub, |
|
788 PL_DHashMoveEntryStub, |
|
789 ClearBroadcasterMapEntry, |
|
790 PL_DHashFinalizeStub, |
|
791 nullptr |
|
792 }; |
|
793 |
|
794 if (! mBroadcasterMap) { |
|
795 mBroadcasterMap = |
|
796 PL_NewDHashTable(&gOps, nullptr, sizeof(BroadcasterMapEntry), |
|
797 PL_DHASH_MIN_SIZE); |
|
798 |
|
799 if (! mBroadcasterMap) { |
|
800 aRv.Throw(NS_ERROR_OUT_OF_MEMORY); |
|
801 return; |
|
802 } |
|
803 } |
|
804 |
|
805 BroadcasterMapEntry* entry = |
|
806 static_cast<BroadcasterMapEntry*> |
|
807 (PL_DHashTableOperate(mBroadcasterMap, &aBroadcaster, |
|
808 PL_DHASH_LOOKUP)); |
|
809 |
|
810 if (PL_DHASH_ENTRY_IS_FREE(entry)) { |
|
811 entry = |
|
812 static_cast<BroadcasterMapEntry*> |
|
813 (PL_DHashTableOperate(mBroadcasterMap, &aBroadcaster, |
|
814 PL_DHASH_ADD)); |
|
815 |
|
816 if (! entry) { |
|
817 aRv.Throw(NS_ERROR_OUT_OF_MEMORY); |
|
818 return; |
|
819 } |
|
820 |
|
821 entry->mBroadcaster = &aBroadcaster; |
|
822 |
|
823 // N.B. placement new to construct the nsSmallVoidArray object |
|
824 // in-place |
|
825 new (&entry->mListeners) nsSmallVoidArray(); |
|
826 } |
|
827 |
|
828 // Only add the listener if it's not there already! |
|
829 nsCOMPtr<nsIAtom> attr = do_GetAtom(aAttr); |
|
830 |
|
831 BroadcastListener* bl; |
|
832 for (int32_t i = entry->mListeners.Count() - 1; i >= 0; --i) { |
|
833 bl = static_cast<BroadcastListener*>(entry->mListeners[i]); |
|
834 |
|
835 nsCOMPtr<Element> blListener = do_QueryReferent(bl->mListener); |
|
836 |
|
837 if (blListener == &aListener && bl->mAttribute == attr) |
|
838 return; |
|
839 } |
|
840 |
|
841 bl = new BroadcastListener; |
|
842 |
|
843 bl->mListener = do_GetWeakReference(&aListener); |
|
844 bl->mAttribute = attr; |
|
845 |
|
846 entry->mListeners.AppendElement(bl); |
|
847 |
|
848 SynchronizeBroadcastListener(&aBroadcaster, &aListener, aAttr); |
|
849 } |
|
850 |
|
851 NS_IMETHODIMP |
|
852 XULDocument::RemoveBroadcastListenerFor(nsIDOMElement* aBroadcaster, |
|
853 nsIDOMElement* aListener, |
|
854 const nsAString& aAttr) |
|
855 { |
|
856 nsCOMPtr<Element> broadcaster = do_QueryInterface(aBroadcaster); |
|
857 nsCOMPtr<Element> listener = do_QueryInterface(aListener); |
|
858 NS_ENSURE_ARG(broadcaster && listener); |
|
859 RemoveBroadcastListenerFor(*broadcaster, *listener, aAttr); |
|
860 return NS_OK; |
|
861 } |
|
862 |
|
863 void |
|
864 XULDocument::RemoveBroadcastListenerFor(Element& aBroadcaster, |
|
865 Element& aListener, |
|
866 const nsAString& aAttr) |
|
867 { |
|
868 // If we haven't added any broadcast listeners, then there sure |
|
869 // aren't any to remove. |
|
870 if (! mBroadcasterMap) |
|
871 return; |
|
872 |
|
873 BroadcasterMapEntry* entry = |
|
874 static_cast<BroadcasterMapEntry*> |
|
875 (PL_DHashTableOperate(mBroadcasterMap, &aBroadcaster, |
|
876 PL_DHASH_LOOKUP)); |
|
877 |
|
878 if (PL_DHASH_ENTRY_IS_BUSY(entry)) { |
|
879 nsCOMPtr<nsIAtom> attr = do_GetAtom(aAttr); |
|
880 for (int32_t i = entry->mListeners.Count() - 1; i >= 0; --i) { |
|
881 BroadcastListener* bl = |
|
882 static_cast<BroadcastListener*>(entry->mListeners[i]); |
|
883 |
|
884 nsCOMPtr<Element> blListener = do_QueryReferent(bl->mListener); |
|
885 |
|
886 if (blListener == &aListener && bl->mAttribute == attr) { |
|
887 entry->mListeners.RemoveElementAt(i); |
|
888 delete bl; |
|
889 |
|
890 if (entry->mListeners.Count() == 0) |
|
891 PL_DHashTableOperate(mBroadcasterMap, &aBroadcaster, |
|
892 PL_DHASH_REMOVE); |
|
893 |
|
894 break; |
|
895 } |
|
896 } |
|
897 } |
|
898 } |
|
899 |
|
900 nsresult |
|
901 XULDocument::ExecuteOnBroadcastHandlerFor(Element* aBroadcaster, |
|
902 Element* aListener, |
|
903 nsIAtom* aAttr) |
|
904 { |
|
905 // Now we execute the onchange handler in the context of the |
|
906 // observer. We need to find the observer in order to |
|
907 // execute the handler. |
|
908 |
|
909 for (nsIContent* child = aListener->GetFirstChild(); |
|
910 child; |
|
911 child = child->GetNextSibling()) { |
|
912 |
|
913 // Look for an <observes> element beneath the listener. This |
|
914 // ought to have an |element| attribute that refers to |
|
915 // aBroadcaster, and an |attribute| element that tells us what |
|
916 // attriubtes we're listening for. |
|
917 if (!child->NodeInfo()->Equals(nsGkAtoms::observes, kNameSpaceID_XUL)) |
|
918 continue; |
|
919 |
|
920 // Is this the element that was listening to us? |
|
921 nsAutoString listeningToID; |
|
922 child->GetAttr(kNameSpaceID_None, nsGkAtoms::element, listeningToID); |
|
923 |
|
924 nsAutoString broadcasterID; |
|
925 aBroadcaster->GetAttr(kNameSpaceID_None, nsGkAtoms::id, broadcasterID); |
|
926 |
|
927 if (listeningToID != broadcasterID) |
|
928 continue; |
|
929 |
|
930 // We are observing the broadcaster, but is this the right |
|
931 // attribute? |
|
932 nsAutoString listeningToAttribute; |
|
933 child->GetAttr(kNameSpaceID_None, nsGkAtoms::attribute, |
|
934 listeningToAttribute); |
|
935 |
|
936 if (!aAttr->Equals(listeningToAttribute) && |
|
937 !listeningToAttribute.EqualsLiteral("*")) { |
|
938 continue; |
|
939 } |
|
940 |
|
941 // This is the right <observes> element. Execute the |
|
942 // |onbroadcast| event handler |
|
943 WidgetEvent event(true, NS_XUL_BROADCAST); |
|
944 |
|
945 nsCOMPtr<nsIPresShell> shell = GetShell(); |
|
946 if (shell) { |
|
947 nsRefPtr<nsPresContext> aPresContext = shell->GetPresContext(); |
|
948 |
|
949 // Handle the DOM event |
|
950 nsEventStatus status = nsEventStatus_eIgnore; |
|
951 EventDispatcher::Dispatch(child, aPresContext, &event, nullptr, |
|
952 &status); |
|
953 } |
|
954 } |
|
955 |
|
956 return NS_OK; |
|
957 } |
|
958 |
|
959 void |
|
960 XULDocument::AttributeWillChange(nsIDocument* aDocument, |
|
961 Element* aElement, int32_t aNameSpaceID, |
|
962 nsIAtom* aAttribute, int32_t aModType) |
|
963 { |
|
964 NS_ABORT_IF_FALSE(aElement, "Null content!"); |
|
965 NS_PRECONDITION(aAttribute, "Must have an attribute that's changing!"); |
|
966 |
|
967 // XXXbz check aNameSpaceID, dammit! |
|
968 // See if we need to update our ref map. |
|
969 if (aAttribute == nsGkAtoms::ref || |
|
970 (aAttribute == nsGkAtoms::id && !aElement->GetIDAttributeName())) { |
|
971 // Might not need this, but be safe for now. |
|
972 nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this); |
|
973 RemoveElementFromRefMap(aElement); |
|
974 } |
|
975 } |
|
976 |
|
977 void |
|
978 XULDocument::AttributeChanged(nsIDocument* aDocument, |
|
979 Element* aElement, int32_t aNameSpaceID, |
|
980 nsIAtom* aAttribute, int32_t aModType) |
|
981 { |
|
982 NS_ASSERTION(aDocument == this, "unexpected doc"); |
|
983 |
|
984 // Might not need this, but be safe for now. |
|
985 nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this); |
|
986 |
|
987 // XXXbz check aNameSpaceID, dammit! |
|
988 // See if we need to update our ref map. |
|
989 if (aAttribute == nsGkAtoms::ref || |
|
990 (aAttribute == nsGkAtoms::id && !aElement->GetIDAttributeName())) { |
|
991 AddElementToRefMap(aElement); |
|
992 } |
|
993 |
|
994 nsresult rv; |
|
995 |
|
996 // Synchronize broadcast listeners |
|
997 if (mBroadcasterMap && |
|
998 CanBroadcast(aNameSpaceID, aAttribute)) { |
|
999 BroadcasterMapEntry* entry = |
|
1000 static_cast<BroadcasterMapEntry*> |
|
1001 (PL_DHashTableOperate(mBroadcasterMap, aElement, |
|
1002 PL_DHASH_LOOKUP)); |
|
1003 |
|
1004 if (PL_DHASH_ENTRY_IS_BUSY(entry)) { |
|
1005 // We've got listeners: push the value. |
|
1006 nsAutoString value; |
|
1007 bool attrSet = aElement->GetAttr(kNameSpaceID_None, aAttribute, value); |
|
1008 |
|
1009 int32_t i; |
|
1010 for (i = entry->mListeners.Count() - 1; i >= 0; --i) { |
|
1011 BroadcastListener* bl = |
|
1012 static_cast<BroadcastListener*>(entry->mListeners[i]); |
|
1013 |
|
1014 if ((bl->mAttribute == aAttribute) || |
|
1015 (bl->mAttribute == nsGkAtoms::_asterix)) { |
|
1016 nsCOMPtr<Element> listenerEl |
|
1017 = do_QueryReferent(bl->mListener); |
|
1018 if (listenerEl) { |
|
1019 nsAutoString currentValue; |
|
1020 bool hasAttr = listenerEl->GetAttr(kNameSpaceID_None, |
|
1021 aAttribute, |
|
1022 currentValue); |
|
1023 // We need to update listener only if we're |
|
1024 // (1) removing an existing attribute, |
|
1025 // (2) adding a new attribute or |
|
1026 // (3) changing the value of an attribute. |
|
1027 bool needsAttrChange = |
|
1028 attrSet != hasAttr || !value.Equals(currentValue); |
|
1029 nsDelayedBroadcastUpdate delayedUpdate(aElement, |
|
1030 listenerEl, |
|
1031 aAttribute, |
|
1032 value, |
|
1033 attrSet, |
|
1034 needsAttrChange); |
|
1035 |
|
1036 uint32_t index = |
|
1037 mDelayedAttrChangeBroadcasts.IndexOf(delayedUpdate, |
|
1038 0, nsDelayedBroadcastUpdate::Comparator()); |
|
1039 if (index != mDelayedAttrChangeBroadcasts.NoIndex) { |
|
1040 if (mHandlingDelayedAttrChange) { |
|
1041 NS_WARNING("Broadcasting loop!"); |
|
1042 continue; |
|
1043 } |
|
1044 mDelayedAttrChangeBroadcasts.RemoveElementAt(index); |
|
1045 } |
|
1046 |
|
1047 mDelayedAttrChangeBroadcasts.AppendElement(delayedUpdate); |
|
1048 } |
|
1049 } |
|
1050 } |
|
1051 } |
|
1052 } |
|
1053 |
|
1054 // checks for modifications in broadcasters |
|
1055 bool listener, resolved; |
|
1056 CheckBroadcasterHookup(aElement, &listener, &resolved); |
|
1057 |
|
1058 // See if there is anything we need to persist in the localstore. |
|
1059 // |
|
1060 // XXX Namespace handling broken :-( |
|
1061 nsAutoString persist; |
|
1062 aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::persist, persist); |
|
1063 if (!persist.IsEmpty()) { |
|
1064 // XXXldb This should check that it's a token, not just a substring. |
|
1065 if (persist.Find(nsDependentAtomString(aAttribute)) >= 0) { |
|
1066 rv = Persist(aElement, kNameSpaceID_None, aAttribute); |
|
1067 if (NS_FAILED(rv)) return; |
|
1068 } |
|
1069 } |
|
1070 } |
|
1071 |
|
1072 void |
|
1073 XULDocument::ContentAppended(nsIDocument* aDocument, |
|
1074 nsIContent* aContainer, |
|
1075 nsIContent* aFirstNewContent, |
|
1076 int32_t aNewIndexInContainer) |
|
1077 { |
|
1078 NS_ASSERTION(aDocument == this, "unexpected doc"); |
|
1079 |
|
1080 // Might not need this, but be safe for now. |
|
1081 nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this); |
|
1082 |
|
1083 // Update our element map |
|
1084 nsresult rv = NS_OK; |
|
1085 for (nsIContent* cur = aFirstNewContent; cur && NS_SUCCEEDED(rv); |
|
1086 cur = cur->GetNextSibling()) { |
|
1087 rv = AddSubtreeToDocument(cur); |
|
1088 } |
|
1089 } |
|
1090 |
|
1091 void |
|
1092 XULDocument::ContentInserted(nsIDocument* aDocument, |
|
1093 nsIContent* aContainer, |
|
1094 nsIContent* aChild, |
|
1095 int32_t aIndexInContainer) |
|
1096 { |
|
1097 NS_ASSERTION(aDocument == this, "unexpected doc"); |
|
1098 |
|
1099 // Might not need this, but be safe for now. |
|
1100 nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this); |
|
1101 |
|
1102 AddSubtreeToDocument(aChild); |
|
1103 } |
|
1104 |
|
1105 void |
|
1106 XULDocument::ContentRemoved(nsIDocument* aDocument, |
|
1107 nsIContent* aContainer, |
|
1108 nsIContent* aChild, |
|
1109 int32_t aIndexInContainer, |
|
1110 nsIContent* aPreviousSibling) |
|
1111 { |
|
1112 NS_ASSERTION(aDocument == this, "unexpected doc"); |
|
1113 |
|
1114 // Might not need this, but be safe for now. |
|
1115 nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this); |
|
1116 |
|
1117 RemoveSubtreeFromDocument(aChild); |
|
1118 } |
|
1119 |
|
1120 //---------------------------------------------------------------------- |
|
1121 // |
|
1122 // nsIXULDocument interface |
|
1123 // |
|
1124 |
|
1125 void |
|
1126 XULDocument::GetElementsForID(const nsAString& aID, |
|
1127 nsCOMArray<nsIContent>& aElements) |
|
1128 { |
|
1129 aElements.Clear(); |
|
1130 |
|
1131 nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aID); |
|
1132 if (entry) { |
|
1133 entry->AppendAllIdContent(&aElements); |
|
1134 } |
|
1135 nsRefMapEntry *refEntry = mRefMap.GetEntry(aID); |
|
1136 if (refEntry) { |
|
1137 refEntry->AppendAll(&aElements); |
|
1138 } |
|
1139 } |
|
1140 |
|
1141 nsresult |
|
1142 XULDocument::AddForwardReference(nsForwardReference* aRef) |
|
1143 { |
|
1144 if (mResolutionPhase < aRef->GetPhase()) { |
|
1145 if (!mForwardReferences.AppendElement(aRef)) { |
|
1146 delete aRef; |
|
1147 return NS_ERROR_OUT_OF_MEMORY; |
|
1148 } |
|
1149 } |
|
1150 else { |
|
1151 NS_ERROR("forward references have already been resolved"); |
|
1152 delete aRef; |
|
1153 } |
|
1154 |
|
1155 return NS_OK; |
|
1156 } |
|
1157 |
|
1158 nsresult |
|
1159 XULDocument::ResolveForwardReferences() |
|
1160 { |
|
1161 if (mResolutionPhase == nsForwardReference::eDone) |
|
1162 return NS_OK; |
|
1163 |
|
1164 NS_ASSERTION(mResolutionPhase == nsForwardReference::eStart, |
|
1165 "nested ResolveForwardReferences()"); |
|
1166 |
|
1167 // Resolve each outstanding 'forward' reference. We iterate |
|
1168 // through the list of forward references until no more forward |
|
1169 // references can be resolved. This annealing process is |
|
1170 // guaranteed to converge because we've "closed the gate" to new |
|
1171 // forward references. |
|
1172 |
|
1173 const nsForwardReference::Phase* pass = nsForwardReference::kPasses; |
|
1174 while ((mResolutionPhase = *pass) != nsForwardReference::eDone) { |
|
1175 uint32_t previous = 0; |
|
1176 while (mForwardReferences.Length() && |
|
1177 mForwardReferences.Length() != previous) { |
|
1178 previous = mForwardReferences.Length(); |
|
1179 |
|
1180 for (uint32_t i = 0; i < mForwardReferences.Length(); ++i) { |
|
1181 nsForwardReference* fwdref = mForwardReferences[i]; |
|
1182 |
|
1183 if (fwdref->GetPhase() == *pass) { |
|
1184 nsForwardReference::Result result = fwdref->Resolve(); |
|
1185 |
|
1186 switch (result) { |
|
1187 case nsForwardReference::eResolve_Succeeded: |
|
1188 case nsForwardReference::eResolve_Error: |
|
1189 mForwardReferences.RemoveElementAt(i); |
|
1190 |
|
1191 // fixup because we removed from list |
|
1192 --i; |
|
1193 break; |
|
1194 |
|
1195 case nsForwardReference::eResolve_Later: |
|
1196 // do nothing. we'll try again later |
|
1197 ; |
|
1198 } |
|
1199 |
|
1200 if (mResolutionPhase == nsForwardReference::eStart) { |
|
1201 // Resolve() loaded a dynamic overlay, |
|
1202 // (see XULDocument::LoadOverlayInternal()). |
|
1203 // Return for now, we will be called again. |
|
1204 return NS_OK; |
|
1205 } |
|
1206 } |
|
1207 } |
|
1208 } |
|
1209 |
|
1210 ++pass; |
|
1211 } |
|
1212 |
|
1213 mForwardReferences.Clear(); |
|
1214 return NS_OK; |
|
1215 } |
|
1216 |
|
1217 //---------------------------------------------------------------------- |
|
1218 // |
|
1219 // nsIDOMDocument interface |
|
1220 // |
|
1221 |
|
1222 NS_IMETHODIMP |
|
1223 XULDocument::GetElementsByAttribute(const nsAString& aAttribute, |
|
1224 const nsAString& aValue, |
|
1225 nsIDOMNodeList** aReturn) |
|
1226 { |
|
1227 *aReturn = GetElementsByAttribute(aAttribute, aValue).take(); |
|
1228 return NS_OK; |
|
1229 } |
|
1230 |
|
1231 already_AddRefed<nsINodeList> |
|
1232 XULDocument::GetElementsByAttribute(const nsAString& aAttribute, |
|
1233 const nsAString& aValue) |
|
1234 { |
|
1235 nsCOMPtr<nsIAtom> attrAtom(do_GetAtom(aAttribute)); |
|
1236 void* attrValue = new nsString(aValue); |
|
1237 nsRefPtr<nsContentList> list = new nsContentList(this, |
|
1238 MatchAttribute, |
|
1239 nsContentUtils::DestroyMatchString, |
|
1240 attrValue, |
|
1241 true, |
|
1242 attrAtom, |
|
1243 kNameSpaceID_Unknown); |
|
1244 |
|
1245 return list.forget(); |
|
1246 } |
|
1247 |
|
1248 NS_IMETHODIMP |
|
1249 XULDocument::GetElementsByAttributeNS(const nsAString& aNamespaceURI, |
|
1250 const nsAString& aAttribute, |
|
1251 const nsAString& aValue, |
|
1252 nsIDOMNodeList** aReturn) |
|
1253 { |
|
1254 ErrorResult rv; |
|
1255 *aReturn = GetElementsByAttributeNS(aNamespaceURI, aAttribute, |
|
1256 aValue, rv).take(); |
|
1257 return rv.ErrorCode(); |
|
1258 } |
|
1259 |
|
1260 already_AddRefed<nsINodeList> |
|
1261 XULDocument::GetElementsByAttributeNS(const nsAString& aNamespaceURI, |
|
1262 const nsAString& aAttribute, |
|
1263 const nsAString& aValue, |
|
1264 ErrorResult& aRv) |
|
1265 { |
|
1266 nsCOMPtr<nsIAtom> attrAtom(do_GetAtom(aAttribute)); |
|
1267 void* attrValue = new nsString(aValue); |
|
1268 |
|
1269 int32_t nameSpaceId = kNameSpaceID_Wildcard; |
|
1270 if (!aNamespaceURI.EqualsLiteral("*")) { |
|
1271 nsresult rv = |
|
1272 nsContentUtils::NameSpaceManager()->RegisterNameSpace(aNamespaceURI, |
|
1273 nameSpaceId); |
|
1274 if (NS_FAILED(rv)) { |
|
1275 aRv.Throw(rv); |
|
1276 return nullptr; |
|
1277 } |
|
1278 } |
|
1279 |
|
1280 nsRefPtr<nsContentList> list = new nsContentList(this, |
|
1281 MatchAttribute, |
|
1282 nsContentUtils::DestroyMatchString, |
|
1283 attrValue, |
|
1284 true, |
|
1285 attrAtom, |
|
1286 nameSpaceId); |
|
1287 return list.forget(); |
|
1288 } |
|
1289 |
|
1290 NS_IMETHODIMP |
|
1291 XULDocument::Persist(const nsAString& aID, |
|
1292 const nsAString& aAttr) |
|
1293 { |
|
1294 // If we're currently reading persisted attributes out of the |
|
1295 // localstore, _don't_ re-enter and try to set them again! |
|
1296 if (mApplyingPersistedAttrs) |
|
1297 return NS_OK; |
|
1298 |
|
1299 Element* element = nsDocument::GetElementById(aID); |
|
1300 if (!element) |
|
1301 return NS_OK; |
|
1302 |
|
1303 nsCOMPtr<nsIAtom> tag; |
|
1304 int32_t nameSpaceID; |
|
1305 |
|
1306 nsCOMPtr<nsINodeInfo> ni = element->GetExistingAttrNameFromQName(aAttr); |
|
1307 nsresult rv; |
|
1308 if (ni) { |
|
1309 tag = ni->NameAtom(); |
|
1310 nameSpaceID = ni->NamespaceID(); |
|
1311 } |
|
1312 else { |
|
1313 // Make sure that this QName is going to be valid. |
|
1314 const char16_t *colon; |
|
1315 rv = nsContentUtils::CheckQName(PromiseFlatString(aAttr), true, &colon); |
|
1316 |
|
1317 if (NS_FAILED(rv)) { |
|
1318 // There was an invalid character or it was malformed. |
|
1319 return NS_ERROR_INVALID_ARG; |
|
1320 } |
|
1321 |
|
1322 if (colon) { |
|
1323 // We don't really handle namespace qualifiers in attribute names. |
|
1324 return NS_ERROR_NOT_IMPLEMENTED; |
|
1325 } |
|
1326 |
|
1327 tag = do_GetAtom(aAttr); |
|
1328 NS_ENSURE_TRUE(tag, NS_ERROR_OUT_OF_MEMORY); |
|
1329 |
|
1330 nameSpaceID = kNameSpaceID_None; |
|
1331 } |
|
1332 |
|
1333 rv = Persist(element, nameSpaceID, tag); |
|
1334 if (NS_FAILED(rv)) return rv; |
|
1335 |
|
1336 return NS_OK; |
|
1337 } |
|
1338 |
|
1339 nsresult |
|
1340 XULDocument::Persist(nsIContent* aElement, int32_t aNameSpaceID, |
|
1341 nsIAtom* aAttribute) |
|
1342 { |
|
1343 // For non-chrome documents, persistance is simply broken |
|
1344 if (!nsContentUtils::IsSystemPrincipal(NodePrincipal())) |
|
1345 return NS_ERROR_NOT_AVAILABLE; |
|
1346 |
|
1347 // First make sure we _have_ a local store to stuff the persisted |
|
1348 // information into. (We might not have one if profile information |
|
1349 // hasn't been loaded yet...) |
|
1350 if (!mLocalStore) |
|
1351 return NS_OK; |
|
1352 |
|
1353 nsresult rv; |
|
1354 |
|
1355 nsCOMPtr<nsIRDFResource> element; |
|
1356 rv = nsXULContentUtils::GetElementResource(aElement, getter_AddRefs(element)); |
|
1357 if (NS_FAILED(rv)) return rv; |
|
1358 |
|
1359 // No ID, so nothing to persist. |
|
1360 if (! element) |
|
1361 return NS_OK; |
|
1362 |
|
1363 // Ick. Construct a property from the attribute. Punt on |
|
1364 // namespaces for now. |
|
1365 // Don't bother with unreasonable attributes. We clamp long values, |
|
1366 // but truncating attribute names turns it into a different attribute |
|
1367 // so there's no point in persisting anything at all |
|
1368 nsAtomCString attrstr(aAttribute); |
|
1369 if (attrstr.Length() > kMaxAttrNameLength) { |
|
1370 NS_WARNING("Can't persist, Attribute name too long"); |
|
1371 return NS_ERROR_ILLEGAL_VALUE; |
|
1372 } |
|
1373 |
|
1374 nsCOMPtr<nsIRDFResource> attr; |
|
1375 rv = gRDFService->GetResource(attrstr, |
|
1376 getter_AddRefs(attr)); |
|
1377 if (NS_FAILED(rv)) return rv; |
|
1378 |
|
1379 // Turn the value into a literal |
|
1380 nsAutoString valuestr; |
|
1381 aElement->GetAttr(kNameSpaceID_None, aAttribute, valuestr); |
|
1382 |
|
1383 // prevent over-long attributes that choke the parser (bug 319846) |
|
1384 // (can't simply Truncate without testing, it's implemented |
|
1385 // using SetLength and will grow a short string) |
|
1386 if (valuestr.Length() > kMaxAttributeLength) { |
|
1387 NS_WARNING("Truncating persisted attribute value"); |
|
1388 valuestr.Truncate(kMaxAttributeLength); |
|
1389 } |
|
1390 |
|
1391 // See if there was an old value... |
|
1392 nsCOMPtr<nsIRDFNode> oldvalue; |
|
1393 rv = mLocalStore->GetTarget(element, attr, true, getter_AddRefs(oldvalue)); |
|
1394 if (NS_FAILED(rv)) return rv; |
|
1395 |
|
1396 if (oldvalue && valuestr.IsEmpty()) { |
|
1397 // ...there was an oldvalue, and they've removed it. XXXThis |
|
1398 // handling isn't quite right... |
|
1399 rv = mLocalStore->Unassert(element, attr, oldvalue); |
|
1400 } |
|
1401 else { |
|
1402 // Now either 'change' or 'assert' based on whether there was |
|
1403 // an old value. |
|
1404 nsCOMPtr<nsIRDFLiteral> newvalue; |
|
1405 rv = gRDFService->GetLiteral(valuestr.get(), getter_AddRefs(newvalue)); |
|
1406 if (NS_FAILED(rv)) return rv; |
|
1407 |
|
1408 if (oldvalue) { |
|
1409 if (oldvalue != newvalue) |
|
1410 rv = mLocalStore->Change(element, attr, oldvalue, newvalue); |
|
1411 else |
|
1412 rv = NS_OK; |
|
1413 } |
|
1414 else { |
|
1415 rv = mLocalStore->Assert(element, attr, newvalue, true); |
|
1416 } |
|
1417 } |
|
1418 |
|
1419 if (NS_FAILED(rv)) return rv; |
|
1420 |
|
1421 // Add it to the persisted set for this document (if it's not |
|
1422 // there already). |
|
1423 { |
|
1424 nsAutoCString docurl; |
|
1425 rv = mDocumentURI->GetSpec(docurl); |
|
1426 if (NS_FAILED(rv)) return rv; |
|
1427 |
|
1428 nsCOMPtr<nsIRDFResource> doc; |
|
1429 rv = gRDFService->GetResource(docurl, getter_AddRefs(doc)); |
|
1430 if (NS_FAILED(rv)) return rv; |
|
1431 |
|
1432 bool hasAssertion; |
|
1433 rv = mLocalStore->HasAssertion(doc, kNC_persist, element, true, &hasAssertion); |
|
1434 if (NS_FAILED(rv)) return rv; |
|
1435 |
|
1436 if (! hasAssertion) { |
|
1437 rv = mLocalStore->Assert(doc, kNC_persist, element, true); |
|
1438 if (NS_FAILED(rv)) return rv; |
|
1439 } |
|
1440 } |
|
1441 |
|
1442 return NS_OK; |
|
1443 } |
|
1444 |
|
1445 |
|
1446 nsresult |
|
1447 XULDocument::GetViewportSize(int32_t* aWidth, |
|
1448 int32_t* aHeight) |
|
1449 { |
|
1450 *aWidth = *aHeight = 0; |
|
1451 |
|
1452 FlushPendingNotifications(Flush_Layout); |
|
1453 |
|
1454 nsIPresShell *shell = GetShell(); |
|
1455 NS_ENSURE_TRUE(shell, NS_ERROR_FAILURE); |
|
1456 |
|
1457 nsIFrame* frame = shell->GetRootFrame(); |
|
1458 NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE); |
|
1459 |
|
1460 nsSize size = frame->GetSize(); |
|
1461 |
|
1462 *aWidth = nsPresContext::AppUnitsToIntCSSPixels(size.width); |
|
1463 *aHeight = nsPresContext::AppUnitsToIntCSSPixels(size.height); |
|
1464 |
|
1465 return NS_OK; |
|
1466 } |
|
1467 |
|
1468 NS_IMETHODIMP |
|
1469 XULDocument::GetWidth(int32_t* aWidth) |
|
1470 { |
|
1471 NS_ENSURE_ARG_POINTER(aWidth); |
|
1472 |
|
1473 int32_t height; |
|
1474 return GetViewportSize(aWidth, &height); |
|
1475 } |
|
1476 |
|
1477 int32_t |
|
1478 XULDocument::GetWidth(ErrorResult& aRv) |
|
1479 { |
|
1480 int32_t width; |
|
1481 aRv = GetWidth(&width); |
|
1482 return width; |
|
1483 } |
|
1484 |
|
1485 NS_IMETHODIMP |
|
1486 XULDocument::GetHeight(int32_t* aHeight) |
|
1487 { |
|
1488 NS_ENSURE_ARG_POINTER(aHeight); |
|
1489 |
|
1490 int32_t width; |
|
1491 return GetViewportSize(&width, aHeight); |
|
1492 } |
|
1493 |
|
1494 int32_t |
|
1495 XULDocument::GetHeight(ErrorResult& aRv) |
|
1496 { |
|
1497 int32_t height; |
|
1498 aRv = GetHeight(&height); |
|
1499 return height; |
|
1500 } |
|
1501 |
|
1502 JSObject* |
|
1503 GetScopeObjectOfNode(nsIDOMNode* node) |
|
1504 { |
|
1505 MOZ_ASSERT(node, "Must not be called with null."); |
|
1506 |
|
1507 // Window root occasionally keeps alive a node of a document whose |
|
1508 // window is already dead. If in this brief period someone calls |
|
1509 // GetPopupNode and we return that node, nsNodeSH::PreCreate will throw, |
|
1510 // because it will not know which scope this node belongs to. Returning |
|
1511 // an orphan node like that to JS would be a bug anyway, so to avoid |
|
1512 // this, let's do the same check as nsNodeSH::PreCreate does to |
|
1513 // determine the scope and if it fails let's just return null in |
|
1514 // XULDocument::GetPopupNode. |
|
1515 nsCOMPtr<nsINode> inode = do_QueryInterface(node); |
|
1516 MOZ_ASSERT(inode, "How can this happen?"); |
|
1517 |
|
1518 nsIDocument* doc = inode->OwnerDoc(); |
|
1519 MOZ_ASSERT(inode, "This should never happen."); |
|
1520 |
|
1521 nsIGlobalObject* global = doc->GetScopeObject(); |
|
1522 return global ? global->GetGlobalJSObject() : nullptr; |
|
1523 } |
|
1524 |
|
1525 //---------------------------------------------------------------------- |
|
1526 // |
|
1527 // nsIDOMXULDocument interface |
|
1528 // |
|
1529 |
|
1530 NS_IMETHODIMP |
|
1531 XULDocument::GetPopupNode(nsIDOMNode** aNode) |
|
1532 { |
|
1533 *aNode = nullptr; |
|
1534 |
|
1535 nsCOMPtr<nsIDOMNode> node; |
|
1536 nsCOMPtr<nsPIWindowRoot> rootWin = GetWindowRoot(); |
|
1537 if (rootWin) |
|
1538 node = rootWin->GetPopupNode(); // addref happens here |
|
1539 |
|
1540 if (!node) { |
|
1541 nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); |
|
1542 if (pm) { |
|
1543 node = pm->GetLastTriggerPopupNode(this); |
|
1544 } |
|
1545 } |
|
1546 |
|
1547 if (node && nsContentUtils::CanCallerAccess(node) |
|
1548 && GetScopeObjectOfNode(node)) { |
|
1549 node.swap(*aNode); |
|
1550 } |
|
1551 |
|
1552 return NS_OK; |
|
1553 } |
|
1554 |
|
1555 already_AddRefed<nsINode> |
|
1556 XULDocument::GetPopupNode() |
|
1557 { |
|
1558 nsCOMPtr<nsIDOMNode> node; |
|
1559 DebugOnly<nsresult> rv = GetPopupNode(getter_AddRefs(node)); |
|
1560 MOZ_ASSERT(NS_SUCCEEDED(rv)); |
|
1561 nsCOMPtr<nsINode> retval(do_QueryInterface(node)); |
|
1562 return retval.forget(); |
|
1563 } |
|
1564 |
|
1565 NS_IMETHODIMP |
|
1566 XULDocument::SetPopupNode(nsIDOMNode* aNode) |
|
1567 { |
|
1568 if (aNode) { |
|
1569 // only allow real node objects |
|
1570 nsCOMPtr<nsINode> node = do_QueryInterface(aNode); |
|
1571 NS_ENSURE_ARG(node); |
|
1572 } |
|
1573 |
|
1574 nsCOMPtr<nsPIWindowRoot> rootWin = GetWindowRoot(); |
|
1575 if (rootWin) |
|
1576 rootWin->SetPopupNode(aNode); // addref happens here |
|
1577 |
|
1578 return NS_OK; |
|
1579 } |
|
1580 |
|
1581 void |
|
1582 XULDocument::SetPopupNode(nsINode* aNode) |
|
1583 { |
|
1584 nsCOMPtr<nsIDOMNode> node(do_QueryInterface(aNode)); |
|
1585 DebugOnly<nsresult> rv = SetPopupNode(node); |
|
1586 MOZ_ASSERT(NS_SUCCEEDED(rv)); |
|
1587 } |
|
1588 |
|
1589 // Returns the rangeOffset element from the XUL Popup Manager. This is for |
|
1590 // chrome callers only. |
|
1591 NS_IMETHODIMP |
|
1592 XULDocument::GetPopupRangeParent(nsIDOMNode** aRangeParent) |
|
1593 { |
|
1594 NS_ENSURE_ARG_POINTER(aRangeParent); |
|
1595 *aRangeParent = nullptr; |
|
1596 |
|
1597 nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); |
|
1598 if (!pm) |
|
1599 return NS_ERROR_FAILURE; |
|
1600 |
|
1601 int32_t offset; |
|
1602 pm->GetMouseLocation(aRangeParent, &offset); |
|
1603 |
|
1604 if (*aRangeParent && !nsContentUtils::CanCallerAccess(*aRangeParent)) { |
|
1605 NS_RELEASE(*aRangeParent); |
|
1606 return NS_ERROR_DOM_SECURITY_ERR; |
|
1607 } |
|
1608 |
|
1609 return NS_OK; |
|
1610 } |
|
1611 |
|
1612 already_AddRefed<nsINode> |
|
1613 XULDocument::GetPopupRangeParent(ErrorResult& aRv) |
|
1614 { |
|
1615 nsCOMPtr<nsIDOMNode> node; |
|
1616 aRv = GetPopupRangeParent(getter_AddRefs(node)); |
|
1617 nsCOMPtr<nsINode> retval(do_QueryInterface(node)); |
|
1618 return retval.forget(); |
|
1619 } |
|
1620 |
|
1621 |
|
1622 // Returns the rangeOffset element from the XUL Popup Manager. We check the |
|
1623 // rangeParent to determine if the caller has rights to access to the data. |
|
1624 NS_IMETHODIMP |
|
1625 XULDocument::GetPopupRangeOffset(int32_t* aRangeOffset) |
|
1626 { |
|
1627 ErrorResult rv; |
|
1628 *aRangeOffset = GetPopupRangeOffset(rv); |
|
1629 return rv.ErrorCode(); |
|
1630 } |
|
1631 |
|
1632 int32_t |
|
1633 XULDocument::GetPopupRangeOffset(ErrorResult& aRv) |
|
1634 { |
|
1635 nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); |
|
1636 if (!pm) { |
|
1637 aRv.Throw(NS_ERROR_FAILURE); |
|
1638 return 0; |
|
1639 } |
|
1640 |
|
1641 int32_t offset; |
|
1642 nsCOMPtr<nsIDOMNode> parent; |
|
1643 pm->GetMouseLocation(getter_AddRefs(parent), &offset); |
|
1644 |
|
1645 if (parent && !nsContentUtils::CanCallerAccess(parent)) { |
|
1646 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); |
|
1647 return 0; |
|
1648 } |
|
1649 return offset; |
|
1650 } |
|
1651 |
|
1652 NS_IMETHODIMP |
|
1653 XULDocument::GetTooltipNode(nsIDOMNode** aNode) |
|
1654 { |
|
1655 *aNode = nullptr; |
|
1656 |
|
1657 nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); |
|
1658 if (pm) { |
|
1659 nsCOMPtr<nsIDOMNode> node = pm->GetLastTriggerTooltipNode(this); |
|
1660 if (node && nsContentUtils::CanCallerAccess(node)) |
|
1661 node.swap(*aNode); |
|
1662 } |
|
1663 |
|
1664 return NS_OK; |
|
1665 } |
|
1666 |
|
1667 already_AddRefed<nsINode> |
|
1668 XULDocument::GetTooltipNode() |
|
1669 { |
|
1670 nsCOMPtr<nsIDOMNode> node; |
|
1671 DebugOnly<nsresult> rv = GetTooltipNode(getter_AddRefs(node)); |
|
1672 MOZ_ASSERT(NS_SUCCEEDED(rv)); |
|
1673 nsCOMPtr<nsINode> retval(do_QueryInterface(node)); |
|
1674 return retval.forget(); |
|
1675 } |
|
1676 |
|
1677 NS_IMETHODIMP |
|
1678 XULDocument::SetTooltipNode(nsIDOMNode* aNode) |
|
1679 { |
|
1680 // do nothing |
|
1681 return NS_OK; |
|
1682 } |
|
1683 |
|
1684 |
|
1685 NS_IMETHODIMP |
|
1686 XULDocument::GetCommandDispatcher(nsIDOMXULCommandDispatcher** aTracker) |
|
1687 { |
|
1688 *aTracker = mCommandDispatcher; |
|
1689 NS_IF_ADDREF(*aTracker); |
|
1690 return NS_OK; |
|
1691 } |
|
1692 |
|
1693 Element* |
|
1694 XULDocument::GetElementById(const nsAString& aId) |
|
1695 { |
|
1696 if (!CheckGetElementByIdArg(aId)) |
|
1697 return nullptr; |
|
1698 |
|
1699 nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aId); |
|
1700 if (entry) { |
|
1701 Element* element = entry->GetIdElement(); |
|
1702 if (element) |
|
1703 return element; |
|
1704 } |
|
1705 |
|
1706 nsRefMapEntry* refEntry = mRefMap.GetEntry(aId); |
|
1707 if (refEntry) { |
|
1708 NS_ASSERTION(refEntry->GetFirstElement(), |
|
1709 "nsRefMapEntries should have nonempty content lists"); |
|
1710 return refEntry->GetFirstElement(); |
|
1711 } |
|
1712 return nullptr; |
|
1713 } |
|
1714 |
|
1715 nsresult |
|
1716 XULDocument::AddElementToDocumentPre(Element* aElement) |
|
1717 { |
|
1718 // Do a bunch of work that's necessary when an element gets added |
|
1719 // to the XUL Document. |
|
1720 nsresult rv; |
|
1721 |
|
1722 // 1. Add the element to the resource-to-element map. Also add it to |
|
1723 // the id map, since it seems this can be called when creating |
|
1724 // elements from prototypes. |
|
1725 nsIAtom* id = aElement->GetID(); |
|
1726 if (id) { |
|
1727 // FIXME: Shouldn't BindToTree take care of this? |
|
1728 nsAutoScriptBlocker scriptBlocker; |
|
1729 AddToIdTable(aElement, id); |
|
1730 } |
|
1731 rv = AddElementToRefMap(aElement); |
|
1732 if (NS_FAILED(rv)) return rv; |
|
1733 |
|
1734 // 2. If the element is a 'command updater' (i.e., has a |
|
1735 // "commandupdater='true'" attribute), then add the element to the |
|
1736 // document's command dispatcher |
|
1737 if (aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::commandupdater, |
|
1738 nsGkAtoms::_true, eCaseMatters)) { |
|
1739 rv = nsXULContentUtils::SetCommandUpdater(this, aElement); |
|
1740 if (NS_FAILED(rv)) return rv; |
|
1741 } |
|
1742 |
|
1743 // 3. Check for a broadcaster hookup attribute, in which case |
|
1744 // we'll hook the node up as a listener on a broadcaster. |
|
1745 bool listener, resolved; |
|
1746 rv = CheckBroadcasterHookup(aElement, &listener, &resolved); |
|
1747 if (NS_FAILED(rv)) return rv; |
|
1748 |
|
1749 // If it's not there yet, we may be able to defer hookup until |
|
1750 // later. |
|
1751 if (listener && !resolved && (mResolutionPhase != nsForwardReference::eDone)) { |
|
1752 BroadcasterHookup* hookup = new BroadcasterHookup(this, aElement); |
|
1753 if (! hookup) |
|
1754 return NS_ERROR_OUT_OF_MEMORY; |
|
1755 |
|
1756 rv = AddForwardReference(hookup); |
|
1757 if (NS_FAILED(rv)) return rv; |
|
1758 } |
|
1759 |
|
1760 return NS_OK; |
|
1761 } |
|
1762 |
|
1763 nsresult |
|
1764 XULDocument::AddElementToDocumentPost(Element* aElement) |
|
1765 { |
|
1766 // We need to pay special attention to the keyset tag to set up a listener |
|
1767 if (aElement->NodeInfo()->Equals(nsGkAtoms::keyset, kNameSpaceID_XUL)) { |
|
1768 // Create our XUL key listener and hook it up. |
|
1769 nsXBLService::AttachGlobalKeyHandler(aElement); |
|
1770 } |
|
1771 |
|
1772 // See if we need to attach a XUL template to this node |
|
1773 bool needsHookup; |
|
1774 nsresult rv = CheckTemplateBuilderHookup(aElement, &needsHookup); |
|
1775 if (NS_FAILED(rv)) |
|
1776 return rv; |
|
1777 |
|
1778 if (needsHookup) { |
|
1779 if (mResolutionPhase == nsForwardReference::eDone) { |
|
1780 rv = CreateTemplateBuilder(aElement); |
|
1781 if (NS_FAILED(rv)) |
|
1782 return rv; |
|
1783 } |
|
1784 else { |
|
1785 TemplateBuilderHookup* hookup = new TemplateBuilderHookup(aElement); |
|
1786 if (! hookup) |
|
1787 return NS_ERROR_OUT_OF_MEMORY; |
|
1788 |
|
1789 rv = AddForwardReference(hookup); |
|
1790 if (NS_FAILED(rv)) |
|
1791 return rv; |
|
1792 } |
|
1793 } |
|
1794 |
|
1795 return NS_OK; |
|
1796 } |
|
1797 |
|
1798 NS_IMETHODIMP |
|
1799 XULDocument::AddSubtreeToDocument(nsIContent* aContent) |
|
1800 { |
|
1801 NS_ASSERTION(aContent->GetCurrentDoc() == this, "Element not in doc!"); |
|
1802 // From here on we only care about elements. |
|
1803 if (!aContent->IsElement()) { |
|
1804 return NS_OK; |
|
1805 } |
|
1806 |
|
1807 Element* aElement = aContent->AsElement(); |
|
1808 |
|
1809 // Do pre-order addition magic |
|
1810 nsresult rv = AddElementToDocumentPre(aElement); |
|
1811 if (NS_FAILED(rv)) return rv; |
|
1812 |
|
1813 // Recurse to children |
|
1814 for (nsIContent* child = aElement->GetLastChild(); |
|
1815 child; |
|
1816 child = child->GetPreviousSibling()) { |
|
1817 |
|
1818 rv = AddSubtreeToDocument(child); |
|
1819 if (NS_FAILED(rv)) |
|
1820 return rv; |
|
1821 } |
|
1822 |
|
1823 // Do post-order addition magic |
|
1824 return AddElementToDocumentPost(aElement); |
|
1825 } |
|
1826 |
|
1827 NS_IMETHODIMP |
|
1828 XULDocument::RemoveSubtreeFromDocument(nsIContent* aContent) |
|
1829 { |
|
1830 // From here on we only care about elements. |
|
1831 if (!aContent->IsElement()) { |
|
1832 return NS_OK; |
|
1833 } |
|
1834 |
|
1835 Element* aElement = aContent->AsElement(); |
|
1836 |
|
1837 // Do a bunch of cleanup to remove an element from the XUL |
|
1838 // document. |
|
1839 nsresult rv; |
|
1840 |
|
1841 if (aElement->NodeInfo()->Equals(nsGkAtoms::keyset, kNameSpaceID_XUL)) { |
|
1842 nsXBLService::DetachGlobalKeyHandler(aElement); |
|
1843 } |
|
1844 |
|
1845 // 1. Remove any children from the document. |
|
1846 for (nsIContent* child = aElement->GetLastChild(); |
|
1847 child; |
|
1848 child = child->GetPreviousSibling()) { |
|
1849 |
|
1850 rv = RemoveSubtreeFromDocument(child); |
|
1851 if (NS_FAILED(rv)) |
|
1852 return rv; |
|
1853 } |
|
1854 |
|
1855 // 2. Remove the element from the resource-to-element map. |
|
1856 // Also remove it from the id map, since we added it in |
|
1857 // AddElementToDocumentPre(). |
|
1858 RemoveElementFromRefMap(aElement); |
|
1859 nsIAtom* id = aElement->GetID(); |
|
1860 if (id) { |
|
1861 // FIXME: Shouldn't UnbindFromTree take care of this? |
|
1862 nsAutoScriptBlocker scriptBlocker; |
|
1863 RemoveFromIdTable(aElement, id); |
|
1864 } |
|
1865 |
|
1866 // 3. If the element is a 'command updater', then remove the |
|
1867 // element from the document's command dispatcher. |
|
1868 if (aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::commandupdater, |
|
1869 nsGkAtoms::_true, eCaseMatters)) { |
|
1870 nsCOMPtr<nsIDOMElement> domelement = do_QueryInterface(aElement); |
|
1871 NS_ASSERTION(domelement != nullptr, "not a DOM element"); |
|
1872 if (! domelement) |
|
1873 return NS_ERROR_UNEXPECTED; |
|
1874 |
|
1875 rv = mCommandDispatcher->RemoveCommandUpdater(domelement); |
|
1876 if (NS_FAILED(rv)) return rv; |
|
1877 } |
|
1878 |
|
1879 // 4. Remove the element from our broadcaster map, since it is no longer |
|
1880 // in the document. |
|
1881 nsCOMPtr<Element> broadcaster, listener; |
|
1882 nsAutoString attribute, broadcasterID; |
|
1883 rv = FindBroadcaster(aElement, getter_AddRefs(listener), |
|
1884 broadcasterID, attribute, getter_AddRefs(broadcaster)); |
|
1885 if (rv == NS_FINDBROADCASTER_FOUND) { |
|
1886 RemoveBroadcastListenerFor(*broadcaster, *listener, attribute); |
|
1887 } |
|
1888 |
|
1889 return NS_OK; |
|
1890 } |
|
1891 |
|
1892 NS_IMETHODIMP |
|
1893 XULDocument::SetTemplateBuilderFor(nsIContent* aContent, |
|
1894 nsIXULTemplateBuilder* aBuilder) |
|
1895 { |
|
1896 if (! mTemplateBuilderTable) { |
|
1897 if (!aBuilder) { |
|
1898 return NS_OK; |
|
1899 } |
|
1900 mTemplateBuilderTable = new BuilderTable; |
|
1901 } |
|
1902 |
|
1903 if (aBuilder) { |
|
1904 mTemplateBuilderTable->Put(aContent, aBuilder); |
|
1905 } |
|
1906 else { |
|
1907 mTemplateBuilderTable->Remove(aContent); |
|
1908 } |
|
1909 |
|
1910 return NS_OK; |
|
1911 } |
|
1912 |
|
1913 NS_IMETHODIMP |
|
1914 XULDocument::GetTemplateBuilderFor(nsIContent* aContent, |
|
1915 nsIXULTemplateBuilder** aResult) |
|
1916 { |
|
1917 if (mTemplateBuilderTable) { |
|
1918 mTemplateBuilderTable->Get(aContent, aResult); |
|
1919 } |
|
1920 else |
|
1921 *aResult = nullptr; |
|
1922 |
|
1923 return NS_OK; |
|
1924 } |
|
1925 |
|
1926 static void |
|
1927 GetRefMapAttribute(Element* aElement, nsAutoString* aValue) |
|
1928 { |
|
1929 aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::ref, *aValue); |
|
1930 if (aValue->IsEmpty() && !aElement->GetIDAttributeName()) { |
|
1931 aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::id, *aValue); |
|
1932 } |
|
1933 } |
|
1934 |
|
1935 nsresult |
|
1936 XULDocument::AddElementToRefMap(Element* aElement) |
|
1937 { |
|
1938 // Look at the element's 'ref' attribute, and if set, |
|
1939 // add an entry in the resource-to-element map to the element. |
|
1940 nsAutoString value; |
|
1941 GetRefMapAttribute(aElement, &value); |
|
1942 if (!value.IsEmpty()) { |
|
1943 nsRefMapEntry *entry = mRefMap.PutEntry(value); |
|
1944 if (!entry) |
|
1945 return NS_ERROR_OUT_OF_MEMORY; |
|
1946 if (!entry->AddElement(aElement)) |
|
1947 return NS_ERROR_OUT_OF_MEMORY; |
|
1948 } |
|
1949 |
|
1950 return NS_OK; |
|
1951 } |
|
1952 |
|
1953 void |
|
1954 XULDocument::RemoveElementFromRefMap(Element* aElement) |
|
1955 { |
|
1956 // Remove the element from the resource-to-element map. |
|
1957 nsAutoString value; |
|
1958 GetRefMapAttribute(aElement, &value); |
|
1959 if (!value.IsEmpty()) { |
|
1960 nsRefMapEntry *entry = mRefMap.GetEntry(value); |
|
1961 if (!entry) |
|
1962 return; |
|
1963 if (entry->RemoveElement(aElement)) { |
|
1964 mRefMap.RawRemoveEntry(entry); |
|
1965 } |
|
1966 } |
|
1967 } |
|
1968 |
|
1969 //---------------------------------------------------------------------- |
|
1970 // |
|
1971 // nsIDOMNode interface |
|
1972 // |
|
1973 |
|
1974 nsresult |
|
1975 XULDocument::Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const |
|
1976 { |
|
1977 // We don't allow cloning of a XUL document |
|
1978 *aResult = nullptr; |
|
1979 return NS_ERROR_DOM_NOT_SUPPORTED_ERR; |
|
1980 } |
|
1981 |
|
1982 |
|
1983 //---------------------------------------------------------------------- |
|
1984 // |
|
1985 // Implementation methods |
|
1986 // |
|
1987 |
|
1988 nsresult |
|
1989 XULDocument::Init() |
|
1990 { |
|
1991 nsresult rv = XMLDocument::Init(); |
|
1992 NS_ENSURE_SUCCESS(rv, rv); |
|
1993 |
|
1994 // Create our command dispatcher and hook it up. |
|
1995 mCommandDispatcher = new nsXULCommandDispatcher(this); |
|
1996 NS_ENSURE_TRUE(mCommandDispatcher, NS_ERROR_OUT_OF_MEMORY); |
|
1997 |
|
1998 // this _could_ fail; e.g., if we've tried to grab the local store |
|
1999 // before profiles have initialized. If so, no big deal; nothing |
|
2000 // will persist. |
|
2001 mLocalStore = do_GetService(NS_LOCALSTORE_CONTRACTID); |
|
2002 |
|
2003 if (gRefCnt++ == 0) { |
|
2004 // Keep the RDF service cached in a member variable to make using |
|
2005 // it a bit less painful |
|
2006 rv = CallGetService("@mozilla.org/rdf/rdf-service;1", &gRDFService); |
|
2007 NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF Service"); |
|
2008 if (NS_FAILED(rv)) return rv; |
|
2009 |
|
2010 gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "persist"), |
|
2011 &kNC_persist); |
|
2012 gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "attribute"), |
|
2013 &kNC_attribute); |
|
2014 gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "value"), |
|
2015 &kNC_value); |
|
2016 |
|
2017 // ensure that the XUL prototype cache is instantiated successfully, |
|
2018 // so that we can use nsXULPrototypeCache::GetInstance() without |
|
2019 // null-checks in the rest of the class. |
|
2020 nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance(); |
|
2021 if (!cache) { |
|
2022 NS_ERROR("Could not instantiate nsXULPrototypeCache"); |
|
2023 return NS_ERROR_FAILURE; |
|
2024 } |
|
2025 } |
|
2026 |
|
2027 Preferences::RegisterCallback(XULDocument::DirectionChanged, |
|
2028 "intl.uidirection.", this); |
|
2029 |
|
2030 #ifdef PR_LOGGING |
|
2031 if (! gXULLog) |
|
2032 gXULLog = PR_NewLogModule("XULDocument"); |
|
2033 #endif |
|
2034 |
|
2035 return NS_OK; |
|
2036 } |
|
2037 |
|
2038 |
|
2039 nsresult |
|
2040 XULDocument::StartLayout(void) |
|
2041 { |
|
2042 mMayStartLayout = true; |
|
2043 nsCOMPtr<nsIPresShell> shell = GetShell(); |
|
2044 if (shell) { |
|
2045 // Resize-reflow this time |
|
2046 nsPresContext *cx = shell->GetPresContext(); |
|
2047 NS_ASSERTION(cx != nullptr, "no pres context"); |
|
2048 if (! cx) |
|
2049 return NS_ERROR_UNEXPECTED; |
|
2050 |
|
2051 nsCOMPtr<nsIDocShell> docShell = cx->GetDocShell(); |
|
2052 NS_ASSERTION(docShell != nullptr, "container is not a docshell"); |
|
2053 if (! docShell) |
|
2054 return NS_ERROR_UNEXPECTED; |
|
2055 |
|
2056 nsresult rv = NS_OK; |
|
2057 nsRect r = cx->GetVisibleArea(); |
|
2058 rv = shell->Initialize(r.width, r.height); |
|
2059 NS_ENSURE_SUCCESS(rv, rv); |
|
2060 } |
|
2061 |
|
2062 return NS_OK; |
|
2063 } |
|
2064 |
|
2065 /* static */ |
|
2066 bool |
|
2067 XULDocument::MatchAttribute(nsIContent* aContent, |
|
2068 int32_t aNamespaceID, |
|
2069 nsIAtom* aAttrName, |
|
2070 void* aData) |
|
2071 { |
|
2072 NS_PRECONDITION(aContent, "Must have content node to work with!"); |
|
2073 nsString* attrValue = static_cast<nsString*>(aData); |
|
2074 if (aNamespaceID != kNameSpaceID_Unknown && |
|
2075 aNamespaceID != kNameSpaceID_Wildcard) { |
|
2076 return attrValue->EqualsLiteral("*") ? |
|
2077 aContent->HasAttr(aNamespaceID, aAttrName) : |
|
2078 aContent->AttrValueIs(aNamespaceID, aAttrName, *attrValue, |
|
2079 eCaseMatters); |
|
2080 } |
|
2081 |
|
2082 // Qualified name match. This takes more work. |
|
2083 |
|
2084 uint32_t count = aContent->GetAttrCount(); |
|
2085 for (uint32_t i = 0; i < count; ++i) { |
|
2086 const nsAttrName* name = aContent->GetAttrNameAt(i); |
|
2087 bool nameMatch; |
|
2088 if (name->IsAtom()) { |
|
2089 nameMatch = name->Atom() == aAttrName; |
|
2090 } else if (aNamespaceID == kNameSpaceID_Wildcard) { |
|
2091 nameMatch = name->NodeInfo()->Equals(aAttrName); |
|
2092 } else { |
|
2093 nameMatch = name->NodeInfo()->QualifiedNameEquals(aAttrName); |
|
2094 } |
|
2095 |
|
2096 if (nameMatch) { |
|
2097 return attrValue->EqualsLiteral("*") || |
|
2098 aContent->AttrValueIs(name->NamespaceID(), name->LocalName(), |
|
2099 *attrValue, eCaseMatters); |
|
2100 } |
|
2101 } |
|
2102 |
|
2103 return false; |
|
2104 } |
|
2105 |
|
2106 nsresult |
|
2107 XULDocument::PrepareToLoad(nsISupports* aContainer, |
|
2108 const char* aCommand, |
|
2109 nsIChannel* aChannel, |
|
2110 nsILoadGroup* aLoadGroup, |
|
2111 nsIParser** aResult) |
|
2112 { |
|
2113 // Get the document's principal |
|
2114 nsCOMPtr<nsIPrincipal> principal; |
|
2115 nsContentUtils::GetSecurityManager()-> |
|
2116 GetChannelPrincipal(aChannel, getter_AddRefs(principal)); |
|
2117 return PrepareToLoadPrototype(mDocumentURI, aCommand, principal, aResult); |
|
2118 } |
|
2119 |
|
2120 |
|
2121 nsresult |
|
2122 XULDocument::PrepareToLoadPrototype(nsIURI* aURI, const char* aCommand, |
|
2123 nsIPrincipal* aDocumentPrincipal, |
|
2124 nsIParser** aResult) |
|
2125 { |
|
2126 nsresult rv; |
|
2127 |
|
2128 // Create a new prototype document. |
|
2129 rv = NS_NewXULPrototypeDocument(getter_AddRefs(mCurrentPrototype)); |
|
2130 if (NS_FAILED(rv)) return rv; |
|
2131 |
|
2132 rv = mCurrentPrototype->InitPrincipal(aURI, aDocumentPrincipal); |
|
2133 if (NS_FAILED(rv)) { |
|
2134 mCurrentPrototype = nullptr; |
|
2135 return rv; |
|
2136 } |
|
2137 |
|
2138 // Bootstrap the master document prototype. |
|
2139 if (! mMasterPrototype) { |
|
2140 mMasterPrototype = mCurrentPrototype; |
|
2141 // Set our principal based on the master proto. |
|
2142 SetPrincipal(aDocumentPrincipal); |
|
2143 } |
|
2144 |
|
2145 // Create a XUL content sink, a parser, and kick off a load for |
|
2146 // the overlay. |
|
2147 nsRefPtr<XULContentSinkImpl> sink = new XULContentSinkImpl(); |
|
2148 if (!sink) return NS_ERROR_OUT_OF_MEMORY; |
|
2149 |
|
2150 rv = sink->Init(this, mCurrentPrototype); |
|
2151 NS_ASSERTION(NS_SUCCEEDED(rv), "Unable to initialize datasource sink"); |
|
2152 if (NS_FAILED(rv)) return rv; |
|
2153 |
|
2154 nsCOMPtr<nsIParser> parser = do_CreateInstance(kParserCID, &rv); |
|
2155 NS_ASSERTION(NS_SUCCEEDED(rv), "unable to create parser"); |
|
2156 if (NS_FAILED(rv)) return rv; |
|
2157 |
|
2158 parser->SetCommand(nsCRT::strcmp(aCommand, "view-source") ? eViewNormal : |
|
2159 eViewSource); |
|
2160 |
|
2161 parser->SetDocumentCharset(NS_LITERAL_CSTRING("UTF-8"), |
|
2162 kCharsetFromDocTypeDefault); |
|
2163 parser->SetContentSink(sink); // grabs a reference to the parser |
|
2164 |
|
2165 *aResult = parser; |
|
2166 NS_ADDREF(*aResult); |
|
2167 return NS_OK; |
|
2168 } |
|
2169 |
|
2170 |
|
2171 nsresult |
|
2172 XULDocument::ApplyPersistentAttributes() |
|
2173 { |
|
2174 // For non-chrome documents, persistance is simply broken |
|
2175 if (!nsContentUtils::IsSystemPrincipal(NodePrincipal())) |
|
2176 return NS_ERROR_NOT_AVAILABLE; |
|
2177 |
|
2178 // Add all of the 'persisted' attributes into the content |
|
2179 // model. |
|
2180 if (!mLocalStore) |
|
2181 return NS_OK; |
|
2182 |
|
2183 mApplyingPersistedAttrs = true; |
|
2184 ApplyPersistentAttributesInternal(); |
|
2185 mApplyingPersistedAttrs = false; |
|
2186 |
|
2187 // After we've applied persistence once, we should only reapply |
|
2188 // it to nodes created by overlays |
|
2189 mRestrictPersistence = true; |
|
2190 mPersistenceIds.Clear(); |
|
2191 |
|
2192 return NS_OK; |
|
2193 } |
|
2194 |
|
2195 |
|
2196 nsresult |
|
2197 XULDocument::ApplyPersistentAttributesInternal() |
|
2198 { |
|
2199 nsCOMArray<nsIContent> elements; |
|
2200 |
|
2201 nsAutoCString docurl; |
|
2202 mDocumentURI->GetSpec(docurl); |
|
2203 |
|
2204 nsCOMPtr<nsIRDFResource> doc; |
|
2205 gRDFService->GetResource(docurl, getter_AddRefs(doc)); |
|
2206 |
|
2207 nsCOMPtr<nsISimpleEnumerator> persisted; |
|
2208 mLocalStore->GetTargets(doc, kNC_persist, true, getter_AddRefs(persisted)); |
|
2209 |
|
2210 while (1) { |
|
2211 bool hasmore = false; |
|
2212 persisted->HasMoreElements(&hasmore); |
|
2213 if (! hasmore) |
|
2214 break; |
|
2215 |
|
2216 nsCOMPtr<nsISupports> isupports; |
|
2217 persisted->GetNext(getter_AddRefs(isupports)); |
|
2218 |
|
2219 nsCOMPtr<nsIRDFResource> resource = do_QueryInterface(isupports); |
|
2220 if (! resource) { |
|
2221 NS_WARNING("expected element to be a resource"); |
|
2222 continue; |
|
2223 } |
|
2224 |
|
2225 const char *uri; |
|
2226 resource->GetValueConst(&uri); |
|
2227 if (! uri) |
|
2228 continue; |
|
2229 |
|
2230 nsAutoString id; |
|
2231 nsXULContentUtils::MakeElementID(this, nsDependentCString(uri), id); |
|
2232 |
|
2233 if (id.IsEmpty()) |
|
2234 continue; |
|
2235 |
|
2236 if (mRestrictPersistence && !mPersistenceIds.Contains(id)) |
|
2237 continue; |
|
2238 |
|
2239 // This will clear the array if there are no elements. |
|
2240 GetElementsForID(id, elements); |
|
2241 |
|
2242 if (!elements.Count()) |
|
2243 continue; |
|
2244 |
|
2245 ApplyPersistentAttributesToElements(resource, elements); |
|
2246 } |
|
2247 |
|
2248 return NS_OK; |
|
2249 } |
|
2250 |
|
2251 |
|
2252 nsresult |
|
2253 XULDocument::ApplyPersistentAttributesToElements(nsIRDFResource* aResource, |
|
2254 nsCOMArray<nsIContent>& aElements) |
|
2255 { |
|
2256 nsresult rv; |
|
2257 |
|
2258 nsCOMPtr<nsISimpleEnumerator> attrs; |
|
2259 rv = mLocalStore->ArcLabelsOut(aResource, getter_AddRefs(attrs)); |
|
2260 if (NS_FAILED(rv)) return rv; |
|
2261 |
|
2262 while (1) { |
|
2263 bool hasmore; |
|
2264 rv = attrs->HasMoreElements(&hasmore); |
|
2265 if (NS_FAILED(rv)) return rv; |
|
2266 |
|
2267 if (! hasmore) |
|
2268 break; |
|
2269 |
|
2270 nsCOMPtr<nsISupports> isupports; |
|
2271 rv = attrs->GetNext(getter_AddRefs(isupports)); |
|
2272 if (NS_FAILED(rv)) return rv; |
|
2273 |
|
2274 nsCOMPtr<nsIRDFResource> property = do_QueryInterface(isupports); |
|
2275 if (! property) { |
|
2276 NS_WARNING("expected a resource"); |
|
2277 continue; |
|
2278 } |
|
2279 |
|
2280 const char* attrname; |
|
2281 rv = property->GetValueConst(&attrname); |
|
2282 if (NS_FAILED(rv)) return rv; |
|
2283 |
|
2284 nsCOMPtr<nsIAtom> attr = do_GetAtom(attrname); |
|
2285 if (! attr) |
|
2286 return NS_ERROR_OUT_OF_MEMORY; |
|
2287 |
|
2288 // XXX could hang namespace off here, as well... |
|
2289 |
|
2290 nsCOMPtr<nsIRDFNode> node; |
|
2291 rv = mLocalStore->GetTarget(aResource, property, true, |
|
2292 getter_AddRefs(node)); |
|
2293 if (NS_FAILED(rv)) return rv; |
|
2294 |
|
2295 nsCOMPtr<nsIRDFLiteral> literal = do_QueryInterface(node); |
|
2296 if (! literal) { |
|
2297 NS_WARNING("expected a literal"); |
|
2298 continue; |
|
2299 } |
|
2300 |
|
2301 const char16_t* value; |
|
2302 rv = literal->GetValueConst(&value); |
|
2303 if (NS_FAILED(rv)) return rv; |
|
2304 |
|
2305 nsDependentString wrapper(value); |
|
2306 |
|
2307 uint32_t cnt = aElements.Count(); |
|
2308 |
|
2309 for (int32_t i = int32_t(cnt) - 1; i >= 0; --i) { |
|
2310 nsCOMPtr<nsIContent> element = aElements.SafeObjectAt(i); |
|
2311 if (!element) |
|
2312 continue; |
|
2313 |
|
2314 rv = element->SetAttr(/* XXX */ kNameSpaceID_None, |
|
2315 attr, |
|
2316 wrapper, |
|
2317 true); |
|
2318 } |
|
2319 } |
|
2320 |
|
2321 return NS_OK; |
|
2322 } |
|
2323 |
|
2324 void |
|
2325 XULDocument::TraceProtos(JSTracer* aTrc, uint32_t aGCNumber) |
|
2326 { |
|
2327 uint32_t i, count = mPrototypes.Length(); |
|
2328 for (i = 0; i < count; ++i) { |
|
2329 mPrototypes[i]->TraceProtos(aTrc, aGCNumber); |
|
2330 } |
|
2331 } |
|
2332 |
|
2333 //---------------------------------------------------------------------- |
|
2334 // |
|
2335 // XULDocument::ContextStack |
|
2336 // |
|
2337 |
|
2338 XULDocument::ContextStack::ContextStack() |
|
2339 : mTop(nullptr), mDepth(0) |
|
2340 { |
|
2341 } |
|
2342 |
|
2343 XULDocument::ContextStack::~ContextStack() |
|
2344 { |
|
2345 while (mTop) { |
|
2346 Entry* doomed = mTop; |
|
2347 mTop = mTop->mNext; |
|
2348 NS_IF_RELEASE(doomed->mElement); |
|
2349 delete doomed; |
|
2350 } |
|
2351 } |
|
2352 |
|
2353 nsresult |
|
2354 XULDocument::ContextStack::Push(nsXULPrototypeElement* aPrototype, |
|
2355 nsIContent* aElement) |
|
2356 { |
|
2357 Entry* entry = new Entry; |
|
2358 if (! entry) |
|
2359 return NS_ERROR_OUT_OF_MEMORY; |
|
2360 |
|
2361 entry->mPrototype = aPrototype; |
|
2362 entry->mElement = aElement; |
|
2363 NS_IF_ADDREF(entry->mElement); |
|
2364 entry->mIndex = 0; |
|
2365 |
|
2366 entry->mNext = mTop; |
|
2367 mTop = entry; |
|
2368 |
|
2369 ++mDepth; |
|
2370 return NS_OK; |
|
2371 } |
|
2372 |
|
2373 nsresult |
|
2374 XULDocument::ContextStack::Pop() |
|
2375 { |
|
2376 if (mDepth == 0) |
|
2377 return NS_ERROR_UNEXPECTED; |
|
2378 |
|
2379 Entry* doomed = mTop; |
|
2380 mTop = mTop->mNext; |
|
2381 --mDepth; |
|
2382 |
|
2383 NS_IF_RELEASE(doomed->mElement); |
|
2384 delete doomed; |
|
2385 return NS_OK; |
|
2386 } |
|
2387 |
|
2388 nsresult |
|
2389 XULDocument::ContextStack::Peek(nsXULPrototypeElement** aPrototype, |
|
2390 nsIContent** aElement, |
|
2391 int32_t* aIndex) |
|
2392 { |
|
2393 if (mDepth == 0) |
|
2394 return NS_ERROR_UNEXPECTED; |
|
2395 |
|
2396 *aPrototype = mTop->mPrototype; |
|
2397 *aElement = mTop->mElement; |
|
2398 NS_IF_ADDREF(*aElement); |
|
2399 *aIndex = mTop->mIndex; |
|
2400 |
|
2401 return NS_OK; |
|
2402 } |
|
2403 |
|
2404 |
|
2405 nsresult |
|
2406 XULDocument::ContextStack::SetTopIndex(int32_t aIndex) |
|
2407 { |
|
2408 if (mDepth == 0) |
|
2409 return NS_ERROR_UNEXPECTED; |
|
2410 |
|
2411 mTop->mIndex = aIndex; |
|
2412 return NS_OK; |
|
2413 } |
|
2414 |
|
2415 |
|
2416 //---------------------------------------------------------------------- |
|
2417 // |
|
2418 // Content model walking routines |
|
2419 // |
|
2420 |
|
2421 nsresult |
|
2422 XULDocument::PrepareToWalk() |
|
2423 { |
|
2424 // Prepare to walk the mCurrentPrototype |
|
2425 nsresult rv; |
|
2426 |
|
2427 // Keep an owning reference to the prototype document so that its |
|
2428 // elements aren't yanked from beneath us. |
|
2429 mPrototypes.AppendElement(mCurrentPrototype); |
|
2430 |
|
2431 // Get the prototype's root element and initialize the context |
|
2432 // stack for the prototype walk. |
|
2433 nsXULPrototypeElement* proto = mCurrentPrototype->GetRootElement(); |
|
2434 |
|
2435 if (! proto) { |
|
2436 #ifdef PR_LOGGING |
|
2437 if (PR_LOG_TEST(gXULLog, PR_LOG_ERROR)) { |
|
2438 nsCOMPtr<nsIURI> url = mCurrentPrototype->GetURI(); |
|
2439 |
|
2440 nsAutoCString urlspec; |
|
2441 rv = url->GetSpec(urlspec); |
|
2442 if (NS_FAILED(rv)) return rv; |
|
2443 |
|
2444 PR_LOG(gXULLog, PR_LOG_ERROR, |
|
2445 ("xul: error parsing '%s'", urlspec.get())); |
|
2446 } |
|
2447 #endif |
|
2448 |
|
2449 return NS_OK; |
|
2450 } |
|
2451 |
|
2452 uint32_t piInsertionPoint = 0; |
|
2453 if (mState != eState_Master) { |
|
2454 int32_t indexOfRoot = IndexOf(GetRootElement()); |
|
2455 NS_ASSERTION(indexOfRoot >= 0, |
|
2456 "No root content when preparing to walk overlay!"); |
|
2457 piInsertionPoint = indexOfRoot; |
|
2458 } |
|
2459 |
|
2460 const nsTArray<nsRefPtr<nsXULPrototypePI> >& processingInstructions = |
|
2461 mCurrentPrototype->GetProcessingInstructions(); |
|
2462 |
|
2463 uint32_t total = processingInstructions.Length(); |
|
2464 for (uint32_t i = 0; i < total; ++i) { |
|
2465 rv = CreateAndInsertPI(processingInstructions[i], |
|
2466 this, piInsertionPoint + i); |
|
2467 if (NS_FAILED(rv)) return rv; |
|
2468 } |
|
2469 |
|
2470 // Now check the chrome registry for any additional overlays. |
|
2471 rv = AddChromeOverlays(); |
|
2472 if (NS_FAILED(rv)) return rv; |
|
2473 |
|
2474 // Do one-time initialization if we're preparing to walk the |
|
2475 // master document's prototype. |
|
2476 nsRefPtr<Element> root; |
|
2477 |
|
2478 if (mState == eState_Master) { |
|
2479 // Add the root element |
|
2480 rv = CreateElementFromPrototype(proto, getter_AddRefs(root), true); |
|
2481 if (NS_FAILED(rv)) return rv; |
|
2482 |
|
2483 rv = AppendChildTo(root, false); |
|
2484 if (NS_FAILED(rv)) return rv; |
|
2485 |
|
2486 rv = AddElementToRefMap(root); |
|
2487 if (NS_FAILED(rv)) return rv; |
|
2488 |
|
2489 // Block onload until we've finished building the complete |
|
2490 // document content model. |
|
2491 BlockOnload(); |
|
2492 } |
|
2493 |
|
2494 // There'd better not be anything on the context stack at this |
|
2495 // point! This is the basis case for our "induction" in |
|
2496 // ResumeWalk(), below, which'll assume that there's always a |
|
2497 // content element on the context stack if either 1) we're in the |
|
2498 // "master" document, or 2) we're in an overlay, and we've got |
|
2499 // more than one prototype element (the single, root "overlay" |
|
2500 // element) on the stack. |
|
2501 NS_ASSERTION(mContextStack.Depth() == 0, "something's on the context stack already"); |
|
2502 if (mContextStack.Depth() != 0) |
|
2503 return NS_ERROR_UNEXPECTED; |
|
2504 |
|
2505 rv = mContextStack.Push(proto, root); |
|
2506 if (NS_FAILED(rv)) return rv; |
|
2507 |
|
2508 return NS_OK; |
|
2509 } |
|
2510 |
|
2511 nsresult |
|
2512 XULDocument::CreateAndInsertPI(const nsXULPrototypePI* aProtoPI, |
|
2513 nsINode* aParent, uint32_t aIndex) |
|
2514 { |
|
2515 NS_PRECONDITION(aProtoPI, "null ptr"); |
|
2516 NS_PRECONDITION(aParent, "null ptr"); |
|
2517 |
|
2518 nsRefPtr<ProcessingInstruction> node = |
|
2519 NS_NewXMLProcessingInstruction(mNodeInfoManager, aProtoPI->mTarget, |
|
2520 aProtoPI->mData); |
|
2521 |
|
2522 nsresult rv; |
|
2523 if (aProtoPI->mTarget.EqualsLiteral("xml-stylesheet")) { |
|
2524 rv = InsertXMLStylesheetPI(aProtoPI, aParent, aIndex, node); |
|
2525 } else if (aProtoPI->mTarget.EqualsLiteral("xul-overlay")) { |
|
2526 rv = InsertXULOverlayPI(aProtoPI, aParent, aIndex, node); |
|
2527 } else { |
|
2528 // No special processing, just add the PI to the document. |
|
2529 rv = aParent->InsertChildAt(node, aIndex, false); |
|
2530 } |
|
2531 |
|
2532 return rv; |
|
2533 } |
|
2534 |
|
2535 nsresult |
|
2536 XULDocument::InsertXMLStylesheetPI(const nsXULPrototypePI* aProtoPI, |
|
2537 nsINode* aParent, |
|
2538 uint32_t aIndex, |
|
2539 nsIContent* aPINode) |
|
2540 { |
|
2541 nsCOMPtr<nsIStyleSheetLinkingElement> ssle(do_QueryInterface(aPINode)); |
|
2542 NS_ASSERTION(ssle, "passed XML Stylesheet node does not " |
|
2543 "implement nsIStyleSheetLinkingElement!"); |
|
2544 |
|
2545 nsresult rv; |
|
2546 |
|
2547 ssle->InitStyleLinkElement(false); |
|
2548 // We want to be notified when the style sheet finishes loading, so |
|
2549 // disable style sheet loading for now. |
|
2550 ssle->SetEnableUpdates(false); |
|
2551 ssle->OverrideBaseURI(mCurrentPrototype->GetURI()); |
|
2552 |
|
2553 rv = aParent->InsertChildAt(aPINode, aIndex, false); |
|
2554 if (NS_FAILED(rv)) return rv; |
|
2555 |
|
2556 ssle->SetEnableUpdates(true); |
|
2557 |
|
2558 // load the stylesheet if necessary, passing ourselves as |
|
2559 // nsICSSObserver |
|
2560 bool willNotify; |
|
2561 bool isAlternate; |
|
2562 rv = ssle->UpdateStyleSheet(this, &willNotify, &isAlternate); |
|
2563 if (NS_SUCCEEDED(rv) && willNotify && !isAlternate) { |
|
2564 ++mPendingSheets; |
|
2565 } |
|
2566 |
|
2567 // Ignore errors from UpdateStyleSheet; we don't want failure to |
|
2568 // do that to break the XUL document load. But do propagate out |
|
2569 // NS_ERROR_OUT_OF_MEMORY. |
|
2570 if (rv == NS_ERROR_OUT_OF_MEMORY) { |
|
2571 return rv; |
|
2572 } |
|
2573 |
|
2574 return NS_OK; |
|
2575 } |
|
2576 |
|
2577 nsresult |
|
2578 XULDocument::InsertXULOverlayPI(const nsXULPrototypePI* aProtoPI, |
|
2579 nsINode* aParent, |
|
2580 uint32_t aIndex, |
|
2581 nsIContent* aPINode) |
|
2582 { |
|
2583 nsresult rv; |
|
2584 |
|
2585 rv = aParent->InsertChildAt(aPINode, aIndex, false); |
|
2586 if (NS_FAILED(rv)) return rv; |
|
2587 |
|
2588 // xul-overlay PI is special only in prolog |
|
2589 if (!nsContentUtils::InProlog(aPINode)) { |
|
2590 return NS_OK; |
|
2591 } |
|
2592 |
|
2593 nsAutoString href; |
|
2594 nsContentUtils::GetPseudoAttributeValue(aProtoPI->mData, |
|
2595 nsGkAtoms::href, |
|
2596 href); |
|
2597 |
|
2598 // If there was no href, we can't do anything with this PI |
|
2599 if (href.IsEmpty()) { |
|
2600 return NS_OK; |
|
2601 } |
|
2602 |
|
2603 // Add the overlay to our list of overlays that need to be processed. |
|
2604 nsCOMPtr<nsIURI> uri; |
|
2605 |
|
2606 rv = NS_NewURI(getter_AddRefs(uri), href, nullptr, |
|
2607 mCurrentPrototype->GetURI()); |
|
2608 if (NS_SUCCEEDED(rv)) { |
|
2609 // We insert overlays into mUnloadedOverlays at the same index in |
|
2610 // document order, so they end up in the reverse of the document |
|
2611 // order in mUnloadedOverlays. |
|
2612 // This is needed because the code in ResumeWalk loads the overlays |
|
2613 // by processing the last item of mUnloadedOverlays and removing it |
|
2614 // from the array. |
|
2615 mUnloadedOverlays.InsertElementAt(0, uri); |
|
2616 rv = NS_OK; |
|
2617 } else if (rv == NS_ERROR_MALFORMED_URI) { |
|
2618 // The URL is bad, move along. Don't propagate for now. |
|
2619 // XXX report this to the Error Console (bug 359846) |
|
2620 rv = NS_OK; |
|
2621 } |
|
2622 |
|
2623 return rv; |
|
2624 } |
|
2625 |
|
2626 nsresult |
|
2627 XULDocument::AddChromeOverlays() |
|
2628 { |
|
2629 nsresult rv; |
|
2630 |
|
2631 nsCOMPtr<nsIURI> docUri = mCurrentPrototype->GetURI(); |
|
2632 |
|
2633 /* overlays only apply to chrome or about URIs */ |
|
2634 if (!IsOverlayAllowed(docUri)) return NS_OK; |
|
2635 |
|
2636 nsCOMPtr<nsIXULOverlayProvider> chromeReg = |
|
2637 mozilla::services::GetXULOverlayProviderService(); |
|
2638 // In embedding situations, the chrome registry may not provide overlays, |
|
2639 // or even exist at all; that's OK. |
|
2640 NS_ENSURE_TRUE(chromeReg, NS_OK); |
|
2641 |
|
2642 nsCOMPtr<nsISimpleEnumerator> overlays; |
|
2643 rv = chromeReg->GetXULOverlays(docUri, getter_AddRefs(overlays)); |
|
2644 NS_ENSURE_SUCCESS(rv, rv); |
|
2645 |
|
2646 bool moreOverlays; |
|
2647 nsCOMPtr<nsISupports> next; |
|
2648 nsCOMPtr<nsIURI> uri; |
|
2649 |
|
2650 while (NS_SUCCEEDED(rv = overlays->HasMoreElements(&moreOverlays)) && |
|
2651 moreOverlays) { |
|
2652 |
|
2653 rv = overlays->GetNext(getter_AddRefs(next)); |
|
2654 if (NS_FAILED(rv) || !next) break; |
|
2655 |
|
2656 uri = do_QueryInterface(next); |
|
2657 if (!uri) { |
|
2658 NS_ERROR("Chrome registry handed me a non-nsIURI object!"); |
|
2659 continue; |
|
2660 } |
|
2661 |
|
2662 // Same comment as in XULDocument::InsertXULOverlayPI |
|
2663 mUnloadedOverlays.InsertElementAt(0, uri); |
|
2664 } |
|
2665 |
|
2666 return rv; |
|
2667 } |
|
2668 |
|
2669 NS_IMETHODIMP |
|
2670 XULDocument::LoadOverlay(const nsAString& aURL, nsIObserver* aObserver) |
|
2671 { |
|
2672 nsresult rv; |
|
2673 |
|
2674 nsCOMPtr<nsIURI> uri; |
|
2675 rv = NS_NewURI(getter_AddRefs(uri), aURL, nullptr); |
|
2676 if (NS_FAILED(rv)) return rv; |
|
2677 |
|
2678 if (aObserver) { |
|
2679 nsIObserver* obs = nullptr; |
|
2680 if (!mOverlayLoadObservers) { |
|
2681 mOverlayLoadObservers = new nsInterfaceHashtable<nsURIHashKey,nsIObserver>; |
|
2682 } |
|
2683 obs = mOverlayLoadObservers->GetWeak(uri); |
|
2684 |
|
2685 if (obs) { |
|
2686 // We don't support loading the same overlay twice into the same |
|
2687 // document - that doesn't make sense anyway. |
|
2688 return NS_ERROR_FAILURE; |
|
2689 } |
|
2690 mOverlayLoadObservers->Put(uri, aObserver); |
|
2691 } |
|
2692 bool shouldReturn, failureFromContent; |
|
2693 rv = LoadOverlayInternal(uri, true, &shouldReturn, &failureFromContent); |
|
2694 if (NS_FAILED(rv) && mOverlayLoadObservers) |
|
2695 mOverlayLoadObservers->Remove(uri); // remove the observer if LoadOverlayInternal generated an error |
|
2696 return rv; |
|
2697 } |
|
2698 |
|
2699 nsresult |
|
2700 XULDocument::LoadOverlayInternal(nsIURI* aURI, bool aIsDynamic, |
|
2701 bool* aShouldReturn, |
|
2702 bool* aFailureFromContent) |
|
2703 { |
|
2704 nsresult rv; |
|
2705 |
|
2706 *aShouldReturn = false; |
|
2707 *aFailureFromContent = false; |
|
2708 |
|
2709 #ifdef PR_LOGGING |
|
2710 if (PR_LOG_TEST(gXULLog, PR_LOG_DEBUG)) { |
|
2711 nsAutoCString urlspec; |
|
2712 aURI->GetSpec(urlspec); |
|
2713 nsAutoCString parentDoc; |
|
2714 nsCOMPtr<nsIURI> uri; |
|
2715 nsresult rv = mChannel->GetOriginalURI(getter_AddRefs(uri)); |
|
2716 if (NS_SUCCEEDED(rv)) |
|
2717 rv = uri->GetSpec(parentDoc); |
|
2718 if (!(parentDoc.get())) |
|
2719 parentDoc = ""; |
|
2720 |
|
2721 PR_LOG(gXULLog, PR_LOG_DEBUG, |
|
2722 ("xul: %s loading overlay %s", parentDoc.get(), urlspec.get())); |
|
2723 } |
|
2724 #endif |
|
2725 |
|
2726 if (aIsDynamic) |
|
2727 mResolutionPhase = nsForwardReference::eStart; |
|
2728 |
|
2729 // Chrome documents are allowed to load overlays from anywhere. |
|
2730 // In all other cases, the overlay is only allowed to load if |
|
2731 // the master document and prototype document have the same origin. |
|
2732 |
|
2733 bool documentIsChrome = IsChromeURI(mDocumentURI); |
|
2734 if (!documentIsChrome) { |
|
2735 // Make sure we're allowed to load this overlay. |
|
2736 rv = NodePrincipal()->CheckMayLoad(aURI, true, false); |
|
2737 if (NS_FAILED(rv)) { |
|
2738 *aFailureFromContent = true; |
|
2739 return rv; |
|
2740 } |
|
2741 } |
|
2742 |
|
2743 // Look in the prototype cache for the prototype document with |
|
2744 // the specified overlay URI. Only use the cache if the containing |
|
2745 // document is chrome otherwise it may not have a system principal and |
|
2746 // the cached document will, see bug 565610. |
|
2747 bool overlayIsChrome = IsChromeURI(aURI); |
|
2748 mCurrentPrototype = overlayIsChrome && documentIsChrome ? |
|
2749 nsXULPrototypeCache::GetInstance()->GetPrototype(aURI) : nullptr; |
|
2750 |
|
2751 // Same comment as nsChromeProtocolHandler::NewChannel and |
|
2752 // XULDocument::StartDocumentLoad |
|
2753 // - Ben Goodger |
|
2754 // |
|
2755 // We don't abort on failure here because there are too many valid |
|
2756 // cases that can return failure, and the null-ness of |proto| is |
|
2757 // enough to trigger the fail-safe parse-from-disk solution. |
|
2758 // Example failure cases (for reference) include: |
|
2759 // |
|
2760 // NS_ERROR_NOT_AVAILABLE: the URI was not found in the FastLoad file, |
|
2761 // parse from disk |
|
2762 // other: the FastLoad file, XUL.mfl, could not be found, probably |
|
2763 // due to being accessed before a profile has been selected |
|
2764 // (e.g. loading chrome for the profile manager itself). |
|
2765 // The .xul file must be parsed from disk. |
|
2766 |
|
2767 bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled(); |
|
2768 if (useXULCache && mCurrentPrototype) { |
|
2769 bool loaded; |
|
2770 rv = mCurrentPrototype->AwaitLoadDone(this, &loaded); |
|
2771 if (NS_FAILED(rv)) return rv; |
|
2772 |
|
2773 if (! loaded) { |
|
2774 // Return to the main event loop and eagerly await the |
|
2775 // prototype overlay load's completion. When the content |
|
2776 // sink completes, it will trigger an EndLoad(), which'll |
|
2777 // wind us back up here, in ResumeWalk(). |
|
2778 *aShouldReturn = true; |
|
2779 return NS_OK; |
|
2780 } |
|
2781 |
|
2782 PR_LOG(gXULLog, PR_LOG_DEBUG, ("xul: overlay was cached")); |
|
2783 |
|
2784 // Found the overlay's prototype in the cache, fully loaded. If |
|
2785 // this is a dynamic overlay, this will call ResumeWalk. |
|
2786 // Otherwise, we'll return to ResumeWalk, which called us. |
|
2787 return OnPrototypeLoadDone(aIsDynamic); |
|
2788 } |
|
2789 else { |
|
2790 // Not there. Initiate a load. |
|
2791 PR_LOG(gXULLog, PR_LOG_DEBUG, ("xul: overlay was not cached")); |
|
2792 |
|
2793 if (mIsGoingAway) { |
|
2794 PR_LOG(gXULLog, PR_LOG_DEBUG, ("xul: ...and document already destroyed")); |
|
2795 return NS_ERROR_NOT_AVAILABLE; |
|
2796 } |
|
2797 |
|
2798 // We'll set the right principal on the proto doc when we get |
|
2799 // OnStartRequest from the parser, so just pass in a null principal for |
|
2800 // now. |
|
2801 nsCOMPtr<nsIParser> parser; |
|
2802 rv = PrepareToLoadPrototype(aURI, "view", nullptr, getter_AddRefs(parser)); |
|
2803 if (NS_FAILED(rv)) return rv; |
|
2804 |
|
2805 // Predicate mIsWritingFastLoad on the XUL cache being enabled, |
|
2806 // so we don't have to re-check whether the cache is enabled all |
|
2807 // the time. |
|
2808 mIsWritingFastLoad = useXULCache; |
|
2809 |
|
2810 nsCOMPtr<nsIStreamListener> listener = do_QueryInterface(parser); |
|
2811 if (! listener) |
|
2812 return NS_ERROR_UNEXPECTED; |
|
2813 |
|
2814 // Add an observer to the parser; this'll get called when |
|
2815 // Necko fires its On[Start|Stop]Request() notifications, |
|
2816 // and will let us recover from a missing overlay. |
|
2817 ParserObserver* parserObserver = |
|
2818 new ParserObserver(this, mCurrentPrototype); |
|
2819 if (! parserObserver) |
|
2820 return NS_ERROR_OUT_OF_MEMORY; |
|
2821 |
|
2822 NS_ADDREF(parserObserver); |
|
2823 parser->Parse(aURI, parserObserver); |
|
2824 NS_RELEASE(parserObserver); |
|
2825 |
|
2826 nsCOMPtr<nsILoadGroup> group = do_QueryReferent(mDocumentLoadGroup); |
|
2827 nsCOMPtr<nsIChannel> channel; |
|
2828 rv = NS_NewChannel(getter_AddRefs(channel), aURI, nullptr, group); |
|
2829 |
|
2830 if (NS_SUCCEEDED(rv)) { |
|
2831 // Set the owner of the channel to be our principal so |
|
2832 // that the overlay's JSObjects etc end up being created |
|
2833 // with the right principal and in the correct |
|
2834 // compartment. |
|
2835 channel->SetOwner(NodePrincipal()); |
|
2836 |
|
2837 rv = channel->AsyncOpen(listener, nullptr); |
|
2838 } |
|
2839 |
|
2840 if (NS_FAILED(rv)) { |
|
2841 // Abandon this prototype |
|
2842 mCurrentPrototype = nullptr; |
|
2843 |
|
2844 // The parser won't get an OnStartRequest and |
|
2845 // OnStopRequest, so it needs a Terminate. |
|
2846 parser->Terminate(); |
|
2847 |
|
2848 // Just move on to the next overlay. NS_OpenURI could fail |
|
2849 // just because a channel could not be opened, which can happen |
|
2850 // if a file or chrome package does not exist. |
|
2851 ReportMissingOverlay(aURI); |
|
2852 |
|
2853 // XXX the error could indicate an internal error as well... |
|
2854 *aFailureFromContent = true; |
|
2855 return rv; |
|
2856 } |
|
2857 |
|
2858 // If it's a 'chrome:' prototype document, then put it into |
|
2859 // the prototype cache; other XUL documents will be reloaded |
|
2860 // each time. We must do this after NS_OpenURI and AsyncOpen, |
|
2861 // or chrome code will wrongly create a cached chrome channel |
|
2862 // instead of a real one. Prototypes are only cached when the |
|
2863 // document to be overlayed is chrome to avoid caching overlay |
|
2864 // scripts with incorrect principals, see bug 565610. |
|
2865 if (useXULCache && overlayIsChrome && documentIsChrome) { |
|
2866 nsXULPrototypeCache::GetInstance()->PutPrototype(mCurrentPrototype); |
|
2867 } |
|
2868 |
|
2869 // Return to the main event loop and eagerly await the |
|
2870 // overlay load's completion. When the content sink |
|
2871 // completes, it will trigger an EndLoad(), which'll wind |
|
2872 // us back in ResumeWalk(). |
|
2873 if (!aIsDynamic) |
|
2874 *aShouldReturn = true; |
|
2875 } |
|
2876 return NS_OK; |
|
2877 } |
|
2878 |
|
2879 static PLDHashOperator |
|
2880 FirePendingMergeNotification(nsIURI* aKey, nsCOMPtr<nsIObserver>& aObserver, void* aClosure) |
|
2881 { |
|
2882 aObserver->Observe(aKey, "xul-overlay-merged", EmptyString().get()); |
|
2883 |
|
2884 typedef nsInterfaceHashtable<nsURIHashKey,nsIObserver> table; |
|
2885 table* observers = static_cast<table*>(aClosure); |
|
2886 if (observers) { |
|
2887 observers->Remove(aKey); |
|
2888 } |
|
2889 |
|
2890 return PL_DHASH_REMOVE; |
|
2891 } |
|
2892 |
|
2893 nsresult |
|
2894 XULDocument::ResumeWalk() |
|
2895 { |
|
2896 // Walk the prototype and build the delegate content model. The |
|
2897 // walk is performed in a top-down, left-to-right fashion. That |
|
2898 // is, a parent is built before any of its children; a node is |
|
2899 // only built after all of its siblings to the left are fully |
|
2900 // constructed. |
|
2901 // |
|
2902 // It is interruptable so that transcluded documents (e.g., |
|
2903 // <html:script src="..." />) can be properly re-loaded if the |
|
2904 // cached copy of the document becomes stale. |
|
2905 nsresult rv; |
|
2906 nsCOMPtr<nsIURI> overlayURI = |
|
2907 mCurrentPrototype ? mCurrentPrototype->GetURI() : nullptr; |
|
2908 |
|
2909 while (1) { |
|
2910 // Begin (or resume) walking the current prototype. |
|
2911 |
|
2912 while (mContextStack.Depth() > 0) { |
|
2913 // Look at the top of the stack to determine what we're |
|
2914 // currently working on. |
|
2915 // This will always be a node already constructed and |
|
2916 // inserted to the actual document. |
|
2917 nsXULPrototypeElement* proto; |
|
2918 nsCOMPtr<nsIContent> element; |
|
2919 int32_t indx; // all children of proto before indx (not |
|
2920 // inclusive) have already been constructed |
|
2921 rv = mContextStack.Peek(&proto, getter_AddRefs(element), &indx); |
|
2922 if (NS_FAILED(rv)) return rv; |
|
2923 |
|
2924 if (indx >= (int32_t)proto->mChildren.Length()) { |
|
2925 if (element) { |
|
2926 // We've processed all of the prototype's children. If |
|
2927 // we're in the master prototype, do post-order |
|
2928 // document-level hookup. (An overlay will get its |
|
2929 // document hookup done when it's successfully |
|
2930 // resolved.) |
|
2931 if (mState == eState_Master) { |
|
2932 AddElementToDocumentPost(element->AsElement()); |
|
2933 |
|
2934 if (element->NodeInfo()->Equals(nsGkAtoms::style, |
|
2935 kNameSpaceID_XHTML) || |
|
2936 element->NodeInfo()->Equals(nsGkAtoms::style, |
|
2937 kNameSpaceID_SVG)) { |
|
2938 // XXX sucks that we have to do this - |
|
2939 // see bug 370111 |
|
2940 nsCOMPtr<nsIStyleSheetLinkingElement> ssle = |
|
2941 do_QueryInterface(element); |
|
2942 NS_ASSERTION(ssle, "<html:style> doesn't implement " |
|
2943 "nsIStyleSheetLinkingElement?"); |
|
2944 bool willNotify; |
|
2945 bool isAlternate; |
|
2946 ssle->UpdateStyleSheet(nullptr, &willNotify, |
|
2947 &isAlternate); |
|
2948 } |
|
2949 } |
|
2950 } |
|
2951 // Now pop the context stack back up to the parent |
|
2952 // element and continue the prototype walk. |
|
2953 mContextStack.Pop(); |
|
2954 continue; |
|
2955 } |
|
2956 |
|
2957 // Grab the next child, and advance the current context stack |
|
2958 // to the next sibling to our right. |
|
2959 nsXULPrototypeNode* childproto = proto->mChildren[indx]; |
|
2960 mContextStack.SetTopIndex(++indx); |
|
2961 |
|
2962 // Whether we're in the "first ply" of an overlay: |
|
2963 // the "hookup" nodes. In the case !processingOverlayHookupNodes, |
|
2964 // we're in the master document -or- we're in an overlay, and far |
|
2965 // enough down into the overlay's content that we can simply build |
|
2966 // the delegates and attach them to the parent node. |
|
2967 bool processingOverlayHookupNodes = (mState == eState_Overlay) && |
|
2968 (mContextStack.Depth() == 1); |
|
2969 |
|
2970 NS_ASSERTION(element || processingOverlayHookupNodes, |
|
2971 "no element on context stack"); |
|
2972 |
|
2973 switch (childproto->mType) { |
|
2974 case nsXULPrototypeNode::eType_Element: { |
|
2975 // An 'element', which may contain more content. |
|
2976 nsXULPrototypeElement* protoele = |
|
2977 static_cast<nsXULPrototypeElement*>(childproto); |
|
2978 |
|
2979 nsRefPtr<Element> child; |
|
2980 |
|
2981 if (!processingOverlayHookupNodes) { |
|
2982 rv = CreateElementFromPrototype(protoele, |
|
2983 getter_AddRefs(child), |
|
2984 false); |
|
2985 if (NS_FAILED(rv)) return rv; |
|
2986 |
|
2987 // ...and append it to the content model. |
|
2988 rv = element->AppendChildTo(child, false); |
|
2989 if (NS_FAILED(rv)) return rv; |
|
2990 |
|
2991 // If we're only restoring persisted things on |
|
2992 // some elements, store the ID here to do that. |
|
2993 if (mRestrictPersistence) { |
|
2994 nsIAtom* id = child->GetID(); |
|
2995 if (id) { |
|
2996 mPersistenceIds.PutEntry(nsDependentAtomString(id)); |
|
2997 } |
|
2998 } |
|
2999 |
|
3000 // do pre-order document-level hookup, but only if |
|
3001 // we're in the master document. For an overlay, |
|
3002 // this will happen when the overlay is |
|
3003 // successfully resolved. |
|
3004 if (mState == eState_Master) |
|
3005 AddElementToDocumentPre(child); |
|
3006 } |
|
3007 else { |
|
3008 // We're in the "first ply" of an overlay: the |
|
3009 // "hookup" nodes. Create an 'overlay' element so |
|
3010 // that we can continue to build content, and |
|
3011 // enter a forward reference so we can hook it up |
|
3012 // later. |
|
3013 rv = CreateOverlayElement(protoele, getter_AddRefs(child)); |
|
3014 if (NS_FAILED(rv)) return rv; |
|
3015 } |
|
3016 |
|
3017 // If it has children, push the element onto the context |
|
3018 // stack and begin to process them. |
|
3019 if (protoele->mChildren.Length() > 0) { |
|
3020 rv = mContextStack.Push(protoele, child); |
|
3021 if (NS_FAILED(rv)) return rv; |
|
3022 } |
|
3023 else { |
|
3024 if (mState == eState_Master) { |
|
3025 // If there are no children, and we're in the |
|
3026 // master document, do post-order document hookup |
|
3027 // immediately. |
|
3028 AddElementToDocumentPost(child); |
|
3029 } |
|
3030 } |
|
3031 } |
|
3032 break; |
|
3033 |
|
3034 case nsXULPrototypeNode::eType_Script: { |
|
3035 // A script reference. Execute the script immediately; |
|
3036 // this may have side effects in the content model. |
|
3037 nsXULPrototypeScript* scriptproto = |
|
3038 static_cast<nsXULPrototypeScript*>(childproto); |
|
3039 |
|
3040 if (scriptproto->mSrcURI) { |
|
3041 // A transcluded script reference; this may |
|
3042 // "block" our prototype walk if the script isn't |
|
3043 // cached, or the cached copy of the script is |
|
3044 // stale and must be reloaded. |
|
3045 bool blocked; |
|
3046 rv = LoadScript(scriptproto, &blocked); |
|
3047 // If the script cannot be loaded, just keep going! |
|
3048 |
|
3049 if (NS_SUCCEEDED(rv) && blocked) |
|
3050 return NS_OK; |
|
3051 } |
|
3052 else if (scriptproto->GetScriptObject()) { |
|
3053 // An inline script |
|
3054 rv = ExecuteScript(scriptproto); |
|
3055 if (NS_FAILED(rv)) return rv; |
|
3056 } |
|
3057 } |
|
3058 break; |
|
3059 |
|
3060 case nsXULPrototypeNode::eType_Text: { |
|
3061 // A simple text node. |
|
3062 |
|
3063 if (!processingOverlayHookupNodes) { |
|
3064 // This does mean that text nodes that are direct children |
|
3065 // of <overlay> get ignored. |
|
3066 |
|
3067 nsRefPtr<nsTextNode> text = |
|
3068 new nsTextNode(mNodeInfoManager); |
|
3069 |
|
3070 nsXULPrototypeText* textproto = |
|
3071 static_cast<nsXULPrototypeText*>(childproto); |
|
3072 text->SetText(textproto->mValue, false); |
|
3073 |
|
3074 rv = element->AppendChildTo(text, false); |
|
3075 NS_ENSURE_SUCCESS(rv, rv); |
|
3076 } |
|
3077 } |
|
3078 break; |
|
3079 |
|
3080 case nsXULPrototypeNode::eType_PI: { |
|
3081 nsXULPrototypePI* piProto = |
|
3082 static_cast<nsXULPrototypePI*>(childproto); |
|
3083 |
|
3084 // <?xul-overlay?> and <?xml-stylesheet?> don't have effect |
|
3085 // outside the prolog, like they used to. Issue a warning. |
|
3086 |
|
3087 if (piProto->mTarget.EqualsLiteral("xml-stylesheet") || |
|
3088 piProto->mTarget.EqualsLiteral("xul-overlay")) { |
|
3089 |
|
3090 const char16_t* params[] = { piProto->mTarget.get() }; |
|
3091 |
|
3092 nsContentUtils::ReportToConsole( |
|
3093 nsIScriptError::warningFlag, |
|
3094 NS_LITERAL_CSTRING("XUL Document"), nullptr, |
|
3095 nsContentUtils::eXUL_PROPERTIES, |
|
3096 "PINotInProlog", |
|
3097 params, ArrayLength(params), |
|
3098 overlayURI); |
|
3099 } |
|
3100 |
|
3101 nsIContent* parent = processingOverlayHookupNodes ? |
|
3102 GetRootElement() : element.get(); |
|
3103 |
|
3104 if (parent) { |
|
3105 // an inline script could have removed the root element |
|
3106 rv = CreateAndInsertPI(piProto, parent, |
|
3107 parent->GetChildCount()); |
|
3108 NS_ENSURE_SUCCESS(rv, rv); |
|
3109 } |
|
3110 } |
|
3111 break; |
|
3112 |
|
3113 default: |
|
3114 NS_NOTREACHED("Unexpected nsXULPrototypeNode::Type value"); |
|
3115 } |
|
3116 } |
|
3117 |
|
3118 // Once we get here, the context stack will have been |
|
3119 // depleted. That means that the entire prototype has been |
|
3120 // walked and content has been constructed. |
|
3121 |
|
3122 // If we're not already, mark us as now processing overlays. |
|
3123 mState = eState_Overlay; |
|
3124 |
|
3125 // If there are no overlay URIs, then we're done. |
|
3126 uint32_t count = mUnloadedOverlays.Length(); |
|
3127 if (! count) |
|
3128 break; |
|
3129 |
|
3130 nsCOMPtr<nsIURI> uri = mUnloadedOverlays[count-1]; |
|
3131 mUnloadedOverlays.RemoveElementAt(count - 1); |
|
3132 |
|
3133 bool shouldReturn, failureFromContent; |
|
3134 rv = LoadOverlayInternal(uri, false, &shouldReturn, |
|
3135 &failureFromContent); |
|
3136 if (failureFromContent) |
|
3137 // The failure |rv| was the result of a problem in the content |
|
3138 // rather than an unexpected problem in our implementation, so |
|
3139 // just continue with the next overlay. |
|
3140 continue; |
|
3141 if (NS_FAILED(rv)) |
|
3142 return rv; |
|
3143 if (mOverlayLoadObservers) { |
|
3144 nsIObserver *obs = mOverlayLoadObservers->GetWeak(overlayURI); |
|
3145 if (obs) { |
|
3146 // This overlay has an unloaded overlay, so it will never |
|
3147 // notify. The best we can do is to notify for the unloaded |
|
3148 // overlay instead, assuming nobody is already notifiable |
|
3149 // for it. Note that this will confuse the observer. |
|
3150 if (!mOverlayLoadObservers->GetWeak(uri)) |
|
3151 mOverlayLoadObservers->Put(uri, obs); |
|
3152 mOverlayLoadObservers->Remove(overlayURI); |
|
3153 } |
|
3154 } |
|
3155 if (shouldReturn) |
|
3156 return NS_OK; |
|
3157 overlayURI.swap(uri); |
|
3158 } |
|
3159 |
|
3160 // If we get here, there is nothing left for us to walk. The content |
|
3161 // model is built and ready for layout. |
|
3162 rv = ResolveForwardReferences(); |
|
3163 if (NS_FAILED(rv)) return rv; |
|
3164 |
|
3165 ApplyPersistentAttributes(); |
|
3166 |
|
3167 mStillWalking = false; |
|
3168 if (mPendingSheets == 0) { |
|
3169 rv = DoneWalking(); |
|
3170 } |
|
3171 return rv; |
|
3172 } |
|
3173 |
|
3174 nsresult |
|
3175 XULDocument::DoneWalking() |
|
3176 { |
|
3177 NS_PRECONDITION(mPendingSheets == 0, "there are sheets to be loaded"); |
|
3178 NS_PRECONDITION(!mStillWalking, "walk not done"); |
|
3179 |
|
3180 // XXXldb This is where we should really be setting the chromehidden |
|
3181 // attribute. |
|
3182 |
|
3183 uint32_t count = mOverlaySheets.Length(); |
|
3184 for (uint32_t i = 0; i < count; ++i) { |
|
3185 AddStyleSheet(mOverlaySheets[i]); |
|
3186 } |
|
3187 mOverlaySheets.Clear(); |
|
3188 |
|
3189 if (!mDocumentLoaded) { |
|
3190 // Make sure we don't reenter here from StartLayout(). Note that |
|
3191 // setting mDocumentLoaded to true here means that if StartLayout() |
|
3192 // causes ResumeWalk() to be reentered, we'll take the other branch of |
|
3193 // the |if (!mDocumentLoaded)| check above and since |
|
3194 // mInitialLayoutComplete will be false will follow the else branch |
|
3195 // there too. See the big comment there for how such reentry can |
|
3196 // happen. |
|
3197 mDocumentLoaded = true; |
|
3198 |
|
3199 NotifyPossibleTitleChange(false); |
|
3200 |
|
3201 // Before starting layout, check whether we're a toplevel chrome |
|
3202 // window. If we are, set our chrome flags now, so that we don't have |
|
3203 // to restyle the whole frame tree after StartLayout. |
|
3204 nsCOMPtr<nsIDocShellTreeItem> item = GetDocShell(); |
|
3205 if (item) { |
|
3206 nsCOMPtr<nsIDocShellTreeOwner> owner; |
|
3207 item->GetTreeOwner(getter_AddRefs(owner)); |
|
3208 nsCOMPtr<nsIXULWindow> xulWin = do_GetInterface(owner); |
|
3209 if (xulWin) { |
|
3210 nsCOMPtr<nsIDocShell> xulWinShell; |
|
3211 xulWin->GetDocShell(getter_AddRefs(xulWinShell)); |
|
3212 if (SameCOMIdentity(xulWinShell, item)) { |
|
3213 // We're the chrome document! Apply our chrome flags now. |
|
3214 xulWin->ApplyChromeFlags(); |
|
3215 } |
|
3216 } |
|
3217 } |
|
3218 |
|
3219 StartLayout(); |
|
3220 |
|
3221 if (mIsWritingFastLoad && IsChromeURI(mDocumentURI)) |
|
3222 nsXULPrototypeCache::GetInstance()->WritePrototype(mMasterPrototype); |
|
3223 |
|
3224 NS_ASSERTION(mDelayFrameLoaderInitialization, |
|
3225 "mDelayFrameLoaderInitialization should be true!"); |
|
3226 mDelayFrameLoaderInitialization = false; |
|
3227 NS_WARN_IF_FALSE(mUpdateNestLevel == 0, |
|
3228 "Constructing XUL document in middle of an update?"); |
|
3229 if (mUpdateNestLevel == 0) { |
|
3230 MaybeInitializeFinalizeFrameLoaders(); |
|
3231 } |
|
3232 |
|
3233 NS_DOCUMENT_NOTIFY_OBSERVERS(EndLoad, (this)); |
|
3234 |
|
3235 // DispatchContentLoadedEvents undoes the onload-blocking we |
|
3236 // did in PrepareToWalk(). |
|
3237 DispatchContentLoadedEvents(); |
|
3238 |
|
3239 mInitialLayoutComplete = true; |
|
3240 |
|
3241 // Walk the set of pending load notifications and notify any observers. |
|
3242 // See below for detail. |
|
3243 if (mPendingOverlayLoadNotifications) |
|
3244 mPendingOverlayLoadNotifications->Enumerate( |
|
3245 FirePendingMergeNotification, mOverlayLoadObservers.get()); |
|
3246 } |
|
3247 else { |
|
3248 if (mOverlayLoadObservers) { |
|
3249 nsCOMPtr<nsIURI> overlayURI = mCurrentPrototype->GetURI(); |
|
3250 nsCOMPtr<nsIObserver> obs; |
|
3251 if (mInitialLayoutComplete) { |
|
3252 // We have completed initial layout, so just send the notification. |
|
3253 mOverlayLoadObservers->Get(overlayURI, getter_AddRefs(obs)); |
|
3254 if (obs) |
|
3255 obs->Observe(overlayURI, "xul-overlay-merged", EmptyString().get()); |
|
3256 mOverlayLoadObservers->Remove(overlayURI); |
|
3257 } |
|
3258 else { |
|
3259 // If we have not yet displayed the document for the first time |
|
3260 // (i.e. we came in here as the result of a dynamic overlay load |
|
3261 // which was spawned by a binding-attached event caused by |
|
3262 // StartLayout() on the master prototype - we must remember that |
|
3263 // this overlay has been merged and tell the listeners after |
|
3264 // StartLayout() is completely finished rather than doing so |
|
3265 // immediately - otherwise we may be executing code that needs to |
|
3266 // access XBL Binding implementations on nodes for which frames |
|
3267 // have not yet been constructed because their bindings have not |
|
3268 // yet been attached. This can be a race condition because dynamic |
|
3269 // overlay loading can take varying amounts of time depending on |
|
3270 // whether or not the overlay prototype is in the XUL cache. The |
|
3271 // most likely effect of this bug is odd UI initialization due to |
|
3272 // methods and properties that do not work. |
|
3273 // XXXbz really, we shouldn't be firing binding constructors |
|
3274 // until after StartLayout returns! |
|
3275 |
|
3276 if (!mPendingOverlayLoadNotifications) { |
|
3277 mPendingOverlayLoadNotifications = |
|
3278 new nsInterfaceHashtable<nsURIHashKey,nsIObserver>; |
|
3279 } |
|
3280 |
|
3281 mPendingOverlayLoadNotifications->Get(overlayURI, getter_AddRefs(obs)); |
|
3282 if (!obs) { |
|
3283 mOverlayLoadObservers->Get(overlayURI, getter_AddRefs(obs)); |
|
3284 NS_ASSERTION(obs, "null overlay load observer?"); |
|
3285 mPendingOverlayLoadNotifications->Put(overlayURI, obs); |
|
3286 } |
|
3287 } |
|
3288 } |
|
3289 } |
|
3290 |
|
3291 return NS_OK; |
|
3292 } |
|
3293 |
|
3294 NS_IMETHODIMP |
|
3295 XULDocument::StyleSheetLoaded(nsCSSStyleSheet* aSheet, |
|
3296 bool aWasAlternate, |
|
3297 nsresult aStatus) |
|
3298 { |
|
3299 if (!aWasAlternate) { |
|
3300 // Don't care about when alternate sheets finish loading |
|
3301 |
|
3302 NS_ASSERTION(mPendingSheets > 0, |
|
3303 "Unexpected StyleSheetLoaded notification"); |
|
3304 |
|
3305 --mPendingSheets; |
|
3306 |
|
3307 if (!mStillWalking && mPendingSheets == 0) { |
|
3308 return DoneWalking(); |
|
3309 } |
|
3310 } |
|
3311 |
|
3312 return NS_OK; |
|
3313 } |
|
3314 |
|
3315 void |
|
3316 XULDocument::MaybeBroadcast() |
|
3317 { |
|
3318 // Only broadcast when not in an update and when safe to run scripts. |
|
3319 if (mUpdateNestLevel == 0 && |
|
3320 (mDelayedAttrChangeBroadcasts.Length() || |
|
3321 mDelayedBroadcasters.Length())) { |
|
3322 if (!nsContentUtils::IsSafeToRunScript()) { |
|
3323 if (!mInDestructor) { |
|
3324 nsContentUtils::AddScriptRunner( |
|
3325 NS_NewRunnableMethod(this, &XULDocument::MaybeBroadcast)); |
|
3326 } |
|
3327 return; |
|
3328 } |
|
3329 if (!mHandlingDelayedAttrChange) { |
|
3330 mHandlingDelayedAttrChange = true; |
|
3331 for (uint32_t i = 0; i < mDelayedAttrChangeBroadcasts.Length(); ++i) { |
|
3332 nsIAtom* attrName = mDelayedAttrChangeBroadcasts[i].mAttrName; |
|
3333 if (mDelayedAttrChangeBroadcasts[i].mNeedsAttrChange) { |
|
3334 nsCOMPtr<nsIContent> listener = |
|
3335 do_QueryInterface(mDelayedAttrChangeBroadcasts[i].mListener); |
|
3336 nsString value = mDelayedAttrChangeBroadcasts[i].mAttr; |
|
3337 if (mDelayedAttrChangeBroadcasts[i].mSetAttr) { |
|
3338 listener->SetAttr(kNameSpaceID_None, attrName, value, |
|
3339 true); |
|
3340 } else { |
|
3341 listener->UnsetAttr(kNameSpaceID_None, attrName, |
|
3342 true); |
|
3343 } |
|
3344 } |
|
3345 ExecuteOnBroadcastHandlerFor(mDelayedAttrChangeBroadcasts[i].mBroadcaster, |
|
3346 mDelayedAttrChangeBroadcasts[i].mListener, |
|
3347 attrName); |
|
3348 } |
|
3349 mDelayedAttrChangeBroadcasts.Clear(); |
|
3350 mHandlingDelayedAttrChange = false; |
|
3351 } |
|
3352 |
|
3353 uint32_t length = mDelayedBroadcasters.Length(); |
|
3354 if (length) { |
|
3355 bool oldValue = mHandlingDelayedBroadcasters; |
|
3356 mHandlingDelayedBroadcasters = true; |
|
3357 nsTArray<nsDelayedBroadcastUpdate> delayedBroadcasters; |
|
3358 mDelayedBroadcasters.SwapElements(delayedBroadcasters); |
|
3359 for (uint32_t i = 0; i < length; ++i) { |
|
3360 SynchronizeBroadcastListener(delayedBroadcasters[i].mBroadcaster, |
|
3361 delayedBroadcasters[i].mListener, |
|
3362 delayedBroadcasters[i].mAttr); |
|
3363 } |
|
3364 mHandlingDelayedBroadcasters = oldValue; |
|
3365 } |
|
3366 } |
|
3367 } |
|
3368 |
|
3369 void |
|
3370 XULDocument::EndUpdate(nsUpdateType aUpdateType) |
|
3371 { |
|
3372 XMLDocument::EndUpdate(aUpdateType); |
|
3373 |
|
3374 MaybeBroadcast(); |
|
3375 } |
|
3376 |
|
3377 void |
|
3378 XULDocument::ReportMissingOverlay(nsIURI* aURI) |
|
3379 { |
|
3380 NS_PRECONDITION(aURI, "Must have a URI"); |
|
3381 |
|
3382 nsAutoCString spec; |
|
3383 aURI->GetSpec(spec); |
|
3384 |
|
3385 NS_ConvertUTF8toUTF16 utfSpec(spec); |
|
3386 const char16_t* params[] = { utfSpec.get() }; |
|
3387 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, |
|
3388 NS_LITERAL_CSTRING("XUL Document"), this, |
|
3389 nsContentUtils::eXUL_PROPERTIES, |
|
3390 "MissingOverlay", |
|
3391 params, ArrayLength(params)); |
|
3392 } |
|
3393 |
|
3394 nsresult |
|
3395 XULDocument::LoadScript(nsXULPrototypeScript* aScriptProto, bool* aBlock) |
|
3396 { |
|
3397 // Load a transcluded script |
|
3398 nsresult rv; |
|
3399 |
|
3400 bool isChromeDoc = IsChromeURI(mDocumentURI); |
|
3401 |
|
3402 if (isChromeDoc && aScriptProto->GetScriptObject()) { |
|
3403 rv = ExecuteScript(aScriptProto); |
|
3404 |
|
3405 // Ignore return value from execution, and don't block |
|
3406 *aBlock = false; |
|
3407 return NS_OK; |
|
3408 } |
|
3409 |
|
3410 // Try the XUL script cache, in case two XUL documents source the same |
|
3411 // .js file (e.g., strres.js from navigator.xul and utilityOverlay.xul). |
|
3412 // XXXbe the cache relies on aScriptProto's GC root! |
|
3413 bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled(); |
|
3414 |
|
3415 if (isChromeDoc && useXULCache) { |
|
3416 JSScript* newScriptObject = |
|
3417 nsXULPrototypeCache::GetInstance()->GetScript( |
|
3418 aScriptProto->mSrcURI); |
|
3419 if (newScriptObject) { |
|
3420 // The script language for a proto must remain constant - we |
|
3421 // can't just change it for this unexpected language. |
|
3422 aScriptProto->Set(newScriptObject); |
|
3423 } |
|
3424 |
|
3425 if (aScriptProto->GetScriptObject()) { |
|
3426 rv = ExecuteScript(aScriptProto); |
|
3427 |
|
3428 // Ignore return value from execution, and don't block |
|
3429 *aBlock = false; |
|
3430 return NS_OK; |
|
3431 } |
|
3432 } |
|
3433 |
|
3434 // Allow security manager and content policies to veto the load. Note that |
|
3435 // at this point we already lost context information of the script. |
|
3436 rv = nsScriptLoader::ShouldLoadScript( |
|
3437 this, |
|
3438 static_cast<nsIDocument*>(this), |
|
3439 aScriptProto->mSrcURI, |
|
3440 NS_LITERAL_STRING("application/x-javascript")); |
|
3441 if (NS_FAILED(rv)) { |
|
3442 *aBlock = false; |
|
3443 return rv; |
|
3444 } |
|
3445 |
|
3446 // Release script objects from FastLoad since we decided against using them |
|
3447 aScriptProto->UnlinkJSObjects(); |
|
3448 |
|
3449 // Set the current script prototype so that OnStreamComplete can report |
|
3450 // the right file if there are errors in the script. |
|
3451 NS_ASSERTION(!mCurrentScriptProto, |
|
3452 "still loading a script when starting another load?"); |
|
3453 mCurrentScriptProto = aScriptProto; |
|
3454 |
|
3455 if (aScriptProto->mSrcLoading) { |
|
3456 // Another XULDocument load has started, which is still in progress. |
|
3457 // Remember to ResumeWalk this document when the load completes. |
|
3458 mNextSrcLoadWaiter = aScriptProto->mSrcLoadWaiters; |
|
3459 aScriptProto->mSrcLoadWaiters = this; |
|
3460 NS_ADDREF_THIS(); |
|
3461 } |
|
3462 else { |
|
3463 nsCOMPtr<nsILoadGroup> group = do_QueryReferent(mDocumentLoadGroup); |
|
3464 |
|
3465 // Note: the loader will keep itself alive while it's loading. |
|
3466 nsCOMPtr<nsIStreamLoader> loader; |
|
3467 rv = NS_NewStreamLoader(getter_AddRefs(loader), aScriptProto->mSrcURI, |
|
3468 this, nullptr, group); |
|
3469 if (NS_FAILED(rv)) { |
|
3470 mCurrentScriptProto = nullptr; |
|
3471 return rv; |
|
3472 } |
|
3473 |
|
3474 aScriptProto->mSrcLoading = true; |
|
3475 } |
|
3476 |
|
3477 // Block until OnStreamComplete resumes us. |
|
3478 *aBlock = true; |
|
3479 return NS_OK; |
|
3480 } |
|
3481 |
|
3482 NS_IMETHODIMP |
|
3483 XULDocument::OnStreamComplete(nsIStreamLoader* aLoader, |
|
3484 nsISupports* context, |
|
3485 nsresult aStatus, |
|
3486 uint32_t stringLen, |
|
3487 const uint8_t* string) |
|
3488 { |
|
3489 nsCOMPtr<nsIRequest> request; |
|
3490 aLoader->GetRequest(getter_AddRefs(request)); |
|
3491 nsCOMPtr<nsIChannel> channel = do_QueryInterface(request); |
|
3492 |
|
3493 #ifdef DEBUG |
|
3494 // print a load error on bad status |
|
3495 if (NS_FAILED(aStatus)) { |
|
3496 if (channel) { |
|
3497 nsCOMPtr<nsIURI> uri; |
|
3498 channel->GetURI(getter_AddRefs(uri)); |
|
3499 if (uri) { |
|
3500 nsAutoCString uriSpec; |
|
3501 uri->GetSpec(uriSpec); |
|
3502 printf("Failed to load %s\n", uriSpec.get()); |
|
3503 } |
|
3504 } |
|
3505 } |
|
3506 #endif |
|
3507 |
|
3508 // This is the completion routine that will be called when a |
|
3509 // transcluded script completes. Compile and execute the script |
|
3510 // if the load was successful, then continue building content |
|
3511 // from the prototype. |
|
3512 nsresult rv = aStatus; |
|
3513 |
|
3514 NS_ASSERTION(mCurrentScriptProto && mCurrentScriptProto->mSrcLoading, |
|
3515 "script source not loading on unichar stream complete?"); |
|
3516 if (!mCurrentScriptProto) { |
|
3517 // XXX Wallpaper for bug 270042 |
|
3518 return NS_OK; |
|
3519 } |
|
3520 |
|
3521 if (NS_SUCCEEDED(aStatus)) { |
|
3522 // If the including XUL document is a FastLoad document, and we're |
|
3523 // compiling an out-of-line script (one with src=...), then we must |
|
3524 // be writing a new FastLoad file. If we were reading this script |
|
3525 // from the FastLoad file, XULContentSinkImpl::OpenScript (over in |
|
3526 // nsXULContentSink.cpp) would have already deserialized a non-null |
|
3527 // script->mScriptObject, causing control flow at the top of LoadScript |
|
3528 // not to reach here. |
|
3529 nsCOMPtr<nsIURI> uri = mCurrentScriptProto->mSrcURI; |
|
3530 |
|
3531 // XXX should also check nsIHttpChannel::requestSucceeded |
|
3532 |
|
3533 MOZ_ASSERT(!mOffThreadCompiling && (mOffThreadCompileStringLength == 0 && |
|
3534 !mOffThreadCompileStringBuf), |
|
3535 "XULDocument can't load multiple scripts at once"); |
|
3536 |
|
3537 rv = nsScriptLoader::ConvertToUTF16(channel, string, stringLen, |
|
3538 EmptyString(), this, |
|
3539 mOffThreadCompileStringBuf, |
|
3540 mOffThreadCompileStringLength); |
|
3541 if (NS_SUCCEEDED(rv)) { |
|
3542 // Attempt to give ownership of the buffer to the JS engine. If |
|
3543 // we hit offthread compilation, however, we will have to take it |
|
3544 // back below in order to keep the memory alive until compilation |
|
3545 // completes. |
|
3546 JS::SourceBufferHolder srcBuf(mOffThreadCompileStringBuf, |
|
3547 mOffThreadCompileStringLength, |
|
3548 JS::SourceBufferHolder::GiveOwnership); |
|
3549 mOffThreadCompileStringBuf = nullptr; |
|
3550 mOffThreadCompileStringLength = 0; |
|
3551 |
|
3552 rv = mCurrentScriptProto->Compile(srcBuf, |
|
3553 uri, 1, this, |
|
3554 mMasterPrototype, |
|
3555 this); |
|
3556 if (NS_SUCCEEDED(rv) && !mCurrentScriptProto->GetScriptObject()) { |
|
3557 // We will be notified via OnOffThreadCompileComplete when the |
|
3558 // compile finishes. Keep the contents of the compiled script |
|
3559 // alive until the compilation finishes. |
|
3560 mOffThreadCompiling = true; |
|
3561 // If the JS engine did not take the source buffer, then take |
|
3562 // it back here to ensure it remains alive. |
|
3563 mOffThreadCompileStringBuf = srcBuf.take(); |
|
3564 if (mOffThreadCompileStringBuf) { |
|
3565 mOffThreadCompileStringLength = srcBuf.length(); |
|
3566 } |
|
3567 BlockOnload(); |
|
3568 return NS_OK; |
|
3569 } |
|
3570 } |
|
3571 } |
|
3572 |
|
3573 return OnScriptCompileComplete(mCurrentScriptProto->GetScriptObject(), rv); |
|
3574 } |
|
3575 |
|
3576 NS_IMETHODIMP |
|
3577 XULDocument::OnScriptCompileComplete(JSScript* aScript, nsresult aStatus) |
|
3578 { |
|
3579 // When compiling off thread the script will not have been attached to the |
|
3580 // script proto yet. |
|
3581 if (aScript && !mCurrentScriptProto->GetScriptObject()) |
|
3582 mCurrentScriptProto->Set(aScript); |
|
3583 |
|
3584 // Allow load events to be fired once off thread compilation finishes. |
|
3585 if (mOffThreadCompiling) { |
|
3586 mOffThreadCompiling = false; |
|
3587 UnblockOnload(false); |
|
3588 } |
|
3589 |
|
3590 // After compilation finishes the script's characters are no longer needed. |
|
3591 if (mOffThreadCompileStringBuf) { |
|
3592 js_free(mOffThreadCompileStringBuf); |
|
3593 mOffThreadCompileStringBuf = nullptr; |
|
3594 mOffThreadCompileStringLength = 0; |
|
3595 } |
|
3596 |
|
3597 // Clear mCurrentScriptProto now, but save it first for use below in |
|
3598 // the execute code, and in the while loop that resumes walks of other |
|
3599 // documents that raced to load this script. |
|
3600 nsXULPrototypeScript* scriptProto = mCurrentScriptProto; |
|
3601 mCurrentScriptProto = nullptr; |
|
3602 |
|
3603 // Clear the prototype's loading flag before executing the script or |
|
3604 // resuming document walks, in case any of those control flows starts a |
|
3605 // new script load. |
|
3606 scriptProto->mSrcLoading = false; |
|
3607 |
|
3608 nsresult rv = aStatus; |
|
3609 if (NS_SUCCEEDED(rv)) { |
|
3610 rv = ExecuteScript(scriptProto); |
|
3611 |
|
3612 // If the XUL cache is enabled, save the script object there in |
|
3613 // case different XUL documents source the same script. |
|
3614 // |
|
3615 // But don't save the script in the cache unless the master XUL |
|
3616 // document URL is a chrome: URL. It is valid for a URL such as |
|
3617 // about:config to translate into a master document URL, whose |
|
3618 // prototype document nodes -- including prototype scripts that |
|
3619 // hold GC roots protecting their mJSObject pointers -- are not |
|
3620 // cached in the XUL prototype cache. See StartDocumentLoad, |
|
3621 // the fillXULCache logic. |
|
3622 // |
|
3623 // A document such as about:config is free to load a script via |
|
3624 // a URL such as chrome://global/content/config.js, and we must |
|
3625 // not cache that script object without a prototype cache entry |
|
3626 // containing a companion nsXULPrototypeScript node that owns a |
|
3627 // GC root protecting the script object. Otherwise, the script |
|
3628 // cache entry will dangle once the uncached prototype document |
|
3629 // is released when its owning XULDocument is unloaded. |
|
3630 // |
|
3631 // (See http://bugzilla.mozilla.org/show_bug.cgi?id=98207 for |
|
3632 // the true crime story.) |
|
3633 bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled(); |
|
3634 |
|
3635 if (useXULCache && IsChromeURI(mDocumentURI) && scriptProto->GetScriptObject()) { |
|
3636 nsXULPrototypeCache::GetInstance()->PutScript( |
|
3637 scriptProto->mSrcURI, |
|
3638 scriptProto->GetScriptObject()); |
|
3639 } |
|
3640 |
|
3641 if (mIsWritingFastLoad && mCurrentPrototype != mMasterPrototype) { |
|
3642 // If we are loading an overlay script, try to serialize |
|
3643 // it to the FastLoad file here. Master scripts will be |
|
3644 // serialized when the master prototype document gets |
|
3645 // written, at the bottom of ResumeWalk. That way, master |
|
3646 // out-of-line scripts are serialized in the same order that |
|
3647 // they'll be read, in the FastLoad file, which reduces the |
|
3648 // number of seeks that dump the underlying stream's buffer. |
|
3649 // |
|
3650 // Ignore the return value, as we don't need to propagate |
|
3651 // a failure to write to the FastLoad file, because this |
|
3652 // method aborts that whole process on error. |
|
3653 scriptProto->SerializeOutOfLine(nullptr, mCurrentPrototype); |
|
3654 } |
|
3655 // ignore any evaluation errors |
|
3656 } |
|
3657 |
|
3658 rv = ResumeWalk(); |
|
3659 |
|
3660 // Load a pointer to the prototype-script's list of XULDocuments who |
|
3661 // raced to load the same script |
|
3662 XULDocument** docp = &scriptProto->mSrcLoadWaiters; |
|
3663 |
|
3664 // Resume walking other documents that waited for this one's load, first |
|
3665 // executing the script we just compiled, in each doc's script context |
|
3666 XULDocument* doc; |
|
3667 while ((doc = *docp) != nullptr) { |
|
3668 NS_ASSERTION(doc->mCurrentScriptProto == scriptProto, |
|
3669 "waiting for wrong script to load?"); |
|
3670 doc->mCurrentScriptProto = nullptr; |
|
3671 |
|
3672 // Unlink doc from scriptProto's list before executing and resuming |
|
3673 *docp = doc->mNextSrcLoadWaiter; |
|
3674 doc->mNextSrcLoadWaiter = nullptr; |
|
3675 |
|
3676 // Execute only if we loaded and compiled successfully, then resume |
|
3677 if (NS_SUCCEEDED(aStatus) && scriptProto->GetScriptObject()) { |
|
3678 doc->ExecuteScript(scriptProto); |
|
3679 } |
|
3680 doc->ResumeWalk(); |
|
3681 NS_RELEASE(doc); |
|
3682 } |
|
3683 |
|
3684 return rv; |
|
3685 } |
|
3686 |
|
3687 nsresult |
|
3688 XULDocument::ExecuteScript(nsIScriptContext * aContext, |
|
3689 JS::Handle<JSScript*> aScriptObject) |
|
3690 { |
|
3691 NS_PRECONDITION(aScriptObject != nullptr && aContext != nullptr, "null ptr"); |
|
3692 if (! aScriptObject || ! aContext) |
|
3693 return NS_ERROR_NULL_POINTER; |
|
3694 |
|
3695 NS_ENSURE_TRUE(mScriptGlobalObject, NS_ERROR_NOT_INITIALIZED); |
|
3696 |
|
3697 // Execute the precompiled script with the given version |
|
3698 nsAutoMicroTask mt; |
|
3699 JSContext *cx = aContext->GetNativeContext(); |
|
3700 AutoCxPusher pusher(cx); |
|
3701 JS::Rooted<JSObject*> global(cx, mScriptGlobalObject->GetGlobalJSObject()); |
|
3702 NS_ENSURE_TRUE(global, NS_ERROR_FAILURE); |
|
3703 NS_ENSURE_TRUE(nsContentUtils::GetSecurityManager()->ScriptAllowed(global), NS_OK); |
|
3704 JS::ExposeObjectToActiveJS(global); |
|
3705 xpc_UnmarkGrayScript(aScriptObject); |
|
3706 JSAutoCompartment ac(cx, global); |
|
3707 |
|
3708 // The script is in the compilation scope. Clone it into the target scope |
|
3709 // and execute it. |
|
3710 if (!JS::CloneAndExecuteScript(cx, global, aScriptObject)) |
|
3711 nsJSUtils::ReportPendingException(cx); |
|
3712 return NS_OK; |
|
3713 } |
|
3714 |
|
3715 nsresult |
|
3716 XULDocument::ExecuteScript(nsXULPrototypeScript *aScript) |
|
3717 { |
|
3718 NS_PRECONDITION(aScript != nullptr, "null ptr"); |
|
3719 NS_ENSURE_TRUE(aScript, NS_ERROR_NULL_POINTER); |
|
3720 NS_ENSURE_TRUE(mScriptGlobalObject, NS_ERROR_NOT_INITIALIZED); |
|
3721 |
|
3722 nsresult rv; |
|
3723 rv = mScriptGlobalObject->EnsureScriptEnvironment(); |
|
3724 NS_ENSURE_SUCCESS(rv, rv); |
|
3725 |
|
3726 nsCOMPtr<nsIScriptContext> context = |
|
3727 mScriptGlobalObject->GetScriptContext(); |
|
3728 // failure getting a script context is fatal. |
|
3729 NS_ENSURE_TRUE(context != nullptr, NS_ERROR_UNEXPECTED); |
|
3730 |
|
3731 if (aScript->GetScriptObject()) |
|
3732 rv = ExecuteScript(context, aScript->GetScriptObject()); |
|
3733 else |
|
3734 rv = NS_ERROR_UNEXPECTED; |
|
3735 return rv; |
|
3736 } |
|
3737 |
|
3738 |
|
3739 nsresult |
|
3740 XULDocument::CreateElementFromPrototype(nsXULPrototypeElement* aPrototype, |
|
3741 Element** aResult, |
|
3742 bool aIsRoot) |
|
3743 { |
|
3744 // Create a content model element from a prototype element. |
|
3745 NS_PRECONDITION(aPrototype != nullptr, "null ptr"); |
|
3746 if (! aPrototype) |
|
3747 return NS_ERROR_NULL_POINTER; |
|
3748 |
|
3749 *aResult = nullptr; |
|
3750 nsresult rv = NS_OK; |
|
3751 |
|
3752 #ifdef PR_LOGGING |
|
3753 if (PR_LOG_TEST(gXULLog, PR_LOG_NOTICE)) { |
|
3754 PR_LOG(gXULLog, PR_LOG_NOTICE, |
|
3755 ("xul: creating <%s> from prototype", |
|
3756 NS_ConvertUTF16toUTF8(aPrototype->mNodeInfo->QualifiedName()).get())); |
|
3757 } |
|
3758 #endif |
|
3759 |
|
3760 nsRefPtr<Element> result; |
|
3761 |
|
3762 if (aPrototype->mNodeInfo->NamespaceEquals(kNameSpaceID_XUL)) { |
|
3763 // If it's a XUL element, it'll be lightweight until somebody |
|
3764 // monkeys with it. |
|
3765 rv = nsXULElement::Create(aPrototype, this, true, aIsRoot, getter_AddRefs(result)); |
|
3766 if (NS_FAILED(rv)) return rv; |
|
3767 } |
|
3768 else { |
|
3769 // If it's not a XUL element, it's gonna be heavyweight no matter |
|
3770 // what. So we need to copy everything out of the prototype |
|
3771 // into the element. Get a nodeinfo from our nodeinfo manager |
|
3772 // for this node. |
|
3773 nsCOMPtr<nsINodeInfo> newNodeInfo; |
|
3774 newNodeInfo = mNodeInfoManager->GetNodeInfo(aPrototype->mNodeInfo->NameAtom(), |
|
3775 aPrototype->mNodeInfo->GetPrefixAtom(), |
|
3776 aPrototype->mNodeInfo->NamespaceID(), |
|
3777 nsIDOMNode::ELEMENT_NODE); |
|
3778 if (!newNodeInfo) return NS_ERROR_OUT_OF_MEMORY; |
|
3779 nsCOMPtr<nsINodeInfo> xtfNi = newNodeInfo; |
|
3780 rv = NS_NewElement(getter_AddRefs(result), newNodeInfo.forget(), |
|
3781 NOT_FROM_PARSER); |
|
3782 if (NS_FAILED(rv)) |
|
3783 return rv; |
|
3784 |
|
3785 rv = AddAttributes(aPrototype, result); |
|
3786 if (NS_FAILED(rv)) return rv; |
|
3787 } |
|
3788 |
|
3789 result.swap(*aResult); |
|
3790 |
|
3791 return NS_OK; |
|
3792 } |
|
3793 |
|
3794 nsresult |
|
3795 XULDocument::CreateOverlayElement(nsXULPrototypeElement* aPrototype, |
|
3796 Element** aResult) |
|
3797 { |
|
3798 nsresult rv; |
|
3799 |
|
3800 nsRefPtr<Element> element; |
|
3801 rv = CreateElementFromPrototype(aPrototype, getter_AddRefs(element), false); |
|
3802 if (NS_FAILED(rv)) return rv; |
|
3803 |
|
3804 OverlayForwardReference* fwdref = |
|
3805 new OverlayForwardReference(this, element); |
|
3806 if (! fwdref) |
|
3807 return NS_ERROR_OUT_OF_MEMORY; |
|
3808 |
|
3809 // transferring ownership to ya... |
|
3810 rv = AddForwardReference(fwdref); |
|
3811 if (NS_FAILED(rv)) return rv; |
|
3812 |
|
3813 NS_ADDREF(*aResult = element); |
|
3814 return NS_OK; |
|
3815 } |
|
3816 |
|
3817 nsresult |
|
3818 XULDocument::AddAttributes(nsXULPrototypeElement* aPrototype, |
|
3819 nsIContent* aElement) |
|
3820 { |
|
3821 nsresult rv; |
|
3822 |
|
3823 for (uint32_t i = 0; i < aPrototype->mNumAttributes; ++i) { |
|
3824 nsXULPrototypeAttribute* protoattr = &(aPrototype->mAttributes[i]); |
|
3825 nsAutoString valueStr; |
|
3826 protoattr->mValue.ToString(valueStr); |
|
3827 |
|
3828 rv = aElement->SetAttr(protoattr->mName.NamespaceID(), |
|
3829 protoattr->mName.LocalName(), |
|
3830 protoattr->mName.GetPrefix(), |
|
3831 valueStr, |
|
3832 false); |
|
3833 if (NS_FAILED(rv)) return rv; |
|
3834 } |
|
3835 |
|
3836 return NS_OK; |
|
3837 } |
|
3838 |
|
3839 |
|
3840 nsresult |
|
3841 XULDocument::CheckTemplateBuilderHookup(nsIContent* aElement, |
|
3842 bool* aNeedsHookup) |
|
3843 { |
|
3844 // See if the element already has a `database' attribute. If it |
|
3845 // does, then the template builder has already been created. |
|
3846 // |
|
3847 // XXX This approach will crash and burn (well, maybe not _that_ |
|
3848 // bad) if aElement is not a XUL element. |
|
3849 // |
|
3850 // XXXvarga Do we still want to support non XUL content? |
|
3851 nsCOMPtr<nsIDOMXULElement> xulElement = do_QueryInterface(aElement); |
|
3852 if (xulElement) { |
|
3853 nsCOMPtr<nsIRDFCompositeDataSource> ds; |
|
3854 xulElement->GetDatabase(getter_AddRefs(ds)); |
|
3855 if (ds) { |
|
3856 *aNeedsHookup = false; |
|
3857 return NS_OK; |
|
3858 } |
|
3859 } |
|
3860 |
|
3861 // Check aElement for a 'datasources' attribute, if it has |
|
3862 // one a XUL template builder needs to be hooked up. |
|
3863 *aNeedsHookup = aElement->HasAttr(kNameSpaceID_None, |
|
3864 nsGkAtoms::datasources); |
|
3865 return NS_OK; |
|
3866 } |
|
3867 |
|
3868 /* static */ nsresult |
|
3869 XULDocument::CreateTemplateBuilder(nsIContent* aElement) |
|
3870 { |
|
3871 // Check if need to construct a tree builder or content builder. |
|
3872 bool isTreeBuilder = false; |
|
3873 |
|
3874 // return successful if the element is not is a document, as an inline |
|
3875 // script could have removed it |
|
3876 nsIDocument *document = aElement->GetCurrentDoc(); |
|
3877 NS_ENSURE_TRUE(document, NS_OK); |
|
3878 |
|
3879 int32_t nameSpaceID; |
|
3880 nsIAtom* baseTag = document->BindingManager()-> |
|
3881 ResolveTag(aElement, &nameSpaceID); |
|
3882 |
|
3883 if ((nameSpaceID == kNameSpaceID_XUL) && (baseTag == nsGkAtoms::tree)) { |
|
3884 // By default, we build content for a tree and then we attach |
|
3885 // the tree content view. However, if the `dont-build-content' |
|
3886 // flag is set, then we we'll attach a tree builder which |
|
3887 // directly implements the tree view. |
|
3888 |
|
3889 nsAutoString flags; |
|
3890 aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::flags, flags); |
|
3891 if (flags.Find(NS_LITERAL_STRING("dont-build-content")) >= 0) { |
|
3892 isTreeBuilder = true; |
|
3893 } |
|
3894 } |
|
3895 |
|
3896 if (isTreeBuilder) { |
|
3897 // Create and initialize a tree builder. |
|
3898 nsCOMPtr<nsIXULTemplateBuilder> builder = |
|
3899 do_CreateInstance("@mozilla.org/xul/xul-tree-builder;1"); |
|
3900 |
|
3901 if (! builder) |
|
3902 return NS_ERROR_FAILURE; |
|
3903 |
|
3904 builder->Init(aElement); |
|
3905 |
|
3906 // Create a <treechildren> if one isn't there already. |
|
3907 // XXXvarga what about attributes? |
|
3908 nsCOMPtr<nsIContent> bodyContent; |
|
3909 nsXULContentUtils::FindChildByTag(aElement, kNameSpaceID_XUL, |
|
3910 nsGkAtoms::treechildren, |
|
3911 getter_AddRefs(bodyContent)); |
|
3912 |
|
3913 if (! bodyContent) { |
|
3914 nsresult rv = |
|
3915 document->CreateElem(nsDependentAtomString(nsGkAtoms::treechildren), |
|
3916 nullptr, kNameSpaceID_XUL, |
|
3917 getter_AddRefs(bodyContent)); |
|
3918 NS_ENSURE_SUCCESS(rv, rv); |
|
3919 |
|
3920 aElement->AppendChildTo(bodyContent, false); |
|
3921 } |
|
3922 } |
|
3923 else { |
|
3924 // Create and initialize a content builder. |
|
3925 nsCOMPtr<nsIXULTemplateBuilder> builder |
|
3926 = do_CreateInstance("@mozilla.org/xul/xul-template-builder;1"); |
|
3927 |
|
3928 if (! builder) |
|
3929 return NS_ERROR_FAILURE; |
|
3930 |
|
3931 builder->Init(aElement); |
|
3932 builder->CreateContents(aElement, false); |
|
3933 } |
|
3934 |
|
3935 return NS_OK; |
|
3936 } |
|
3937 |
|
3938 |
|
3939 nsresult |
|
3940 XULDocument::AddPrototypeSheets() |
|
3941 { |
|
3942 nsresult rv; |
|
3943 |
|
3944 const nsCOMArray<nsIURI>& sheets = mCurrentPrototype->GetStyleSheetReferences(); |
|
3945 |
|
3946 for (int32_t i = 0; i < sheets.Count(); i++) { |
|
3947 nsCOMPtr<nsIURI> uri = sheets[i]; |
|
3948 |
|
3949 nsRefPtr<nsCSSStyleSheet> incompleteSheet; |
|
3950 rv = CSSLoader()->LoadSheet(uri, |
|
3951 mCurrentPrototype->DocumentPrincipal(), |
|
3952 EmptyCString(), this, |
|
3953 getter_AddRefs(incompleteSheet)); |
|
3954 |
|
3955 // XXXldb We need to prevent bogus sheets from being held in the |
|
3956 // prototype's list, but until then, don't propagate the failure |
|
3957 // from LoadSheet (and thus exit the loop). |
|
3958 if (NS_SUCCEEDED(rv)) { |
|
3959 ++mPendingSheets; |
|
3960 if (!mOverlaySheets.AppendElement(incompleteSheet)) { |
|
3961 return NS_ERROR_OUT_OF_MEMORY; |
|
3962 } |
|
3963 } |
|
3964 } |
|
3965 |
|
3966 return NS_OK; |
|
3967 } |
|
3968 |
|
3969 |
|
3970 //---------------------------------------------------------------------- |
|
3971 // |
|
3972 // XULDocument::OverlayForwardReference |
|
3973 // |
|
3974 |
|
3975 nsForwardReference::Result |
|
3976 XULDocument::OverlayForwardReference::Resolve() |
|
3977 { |
|
3978 // Resolve a forward reference from an overlay element; attempt to |
|
3979 // hook it up into the main document. |
|
3980 nsresult rv; |
|
3981 nsCOMPtr<nsIContent> target; |
|
3982 |
|
3983 nsIPresShell *shell = mDocument->GetShell(); |
|
3984 bool notify = shell && shell->DidInitialize(); |
|
3985 |
|
3986 nsAutoString id; |
|
3987 mOverlay->GetAttr(kNameSpaceID_None, nsGkAtoms::id, id); |
|
3988 if (id.IsEmpty()) { |
|
3989 // mOverlay is a direct child of <overlay> and has no id. |
|
3990 // Insert it under the root element in the base document. |
|
3991 Element* root = mDocument->GetRootElement(); |
|
3992 if (!root) { |
|
3993 return eResolve_Error; |
|
3994 } |
|
3995 |
|
3996 rv = mDocument->InsertElement(root, mOverlay, notify); |
|
3997 if (NS_FAILED(rv)) return eResolve_Error; |
|
3998 |
|
3999 target = mOverlay; |
|
4000 } |
|
4001 else { |
|
4002 // The hook-up element has an id, try to match it with an element |
|
4003 // with the same id in the base document. |
|
4004 target = mDocument->GetElementById(id); |
|
4005 |
|
4006 // If we can't find the element in the document, defer the hookup |
|
4007 // until later. |
|
4008 if (!target) |
|
4009 return eResolve_Later; |
|
4010 |
|
4011 rv = Merge(target, mOverlay, notify); |
|
4012 if (NS_FAILED(rv)) return eResolve_Error; |
|
4013 } |
|
4014 |
|
4015 // Check if 'target' is still in our document --- it might not be! |
|
4016 if (!notify && target->GetCurrentDoc() == mDocument) { |
|
4017 // Add child and any descendants to the element map |
|
4018 // XXX this is bogus, the content in 'target' might already be |
|
4019 // in the document |
|
4020 rv = mDocument->AddSubtreeToDocument(target); |
|
4021 if (NS_FAILED(rv)) return eResolve_Error; |
|
4022 } |
|
4023 |
|
4024 #ifdef PR_LOGGING |
|
4025 if (PR_LOG_TEST(gXULLog, PR_LOG_NOTICE)) { |
|
4026 nsAutoCString idC; |
|
4027 idC.AssignWithConversion(id); |
|
4028 PR_LOG(gXULLog, PR_LOG_NOTICE, |
|
4029 ("xul: overlay resolved '%s'", |
|
4030 idC.get())); |
|
4031 } |
|
4032 #endif |
|
4033 |
|
4034 mResolved = true; |
|
4035 return eResolve_Succeeded; |
|
4036 } |
|
4037 |
|
4038 |
|
4039 |
|
4040 nsresult |
|
4041 XULDocument::OverlayForwardReference::Merge(nsIContent* aTargetNode, |
|
4042 nsIContent* aOverlayNode, |
|
4043 bool aNotify) |
|
4044 { |
|
4045 // This function is given: |
|
4046 // aTargetNode: the node in the document whose 'id' attribute |
|
4047 // matches a toplevel node in our overlay. |
|
4048 // aOverlayNode: the node in the overlay document that matches |
|
4049 // a node in the actual document. |
|
4050 // aNotify: whether or not content manipulation methods should |
|
4051 // use the aNotify parameter. After the initial |
|
4052 // reflow (i.e. in the dynamic overlay merge case), |
|
4053 // we want all the content manipulation methods we |
|
4054 // call to notify so that frames are constructed |
|
4055 // etc. Otherwise do not, since that's during initial |
|
4056 // document construction before StartLayout has been |
|
4057 // called which will do everything for us. |
|
4058 // |
|
4059 // This function merges the tree from the overlay into the tree in |
|
4060 // the document, overwriting attributes and appending child content |
|
4061 // nodes appropriately. (See XUL overlay reference for details) |
|
4062 |
|
4063 nsresult rv; |
|
4064 |
|
4065 // Merge attributes from the overlay content node to that of the |
|
4066 // actual document. |
|
4067 uint32_t i; |
|
4068 const nsAttrName* name; |
|
4069 for (i = 0; (name = aOverlayNode->GetAttrNameAt(i)); ++i) { |
|
4070 // We don't want to swap IDs, they should be the same. |
|
4071 if (name->Equals(nsGkAtoms::id)) |
|
4072 continue; |
|
4073 |
|
4074 // In certain cases merging command or observes is unsafe, so don't. |
|
4075 if (!aNotify) { |
|
4076 if (aTargetNode->NodeInfo()->Equals(nsGkAtoms::observes, |
|
4077 kNameSpaceID_XUL)) |
|
4078 continue; |
|
4079 |
|
4080 if (name->Equals(nsGkAtoms::observes) && |
|
4081 aTargetNode->HasAttr(kNameSpaceID_None, nsGkAtoms::observes)) |
|
4082 continue; |
|
4083 |
|
4084 if (name->Equals(nsGkAtoms::command) && |
|
4085 aTargetNode->HasAttr(kNameSpaceID_None, nsGkAtoms::command) && |
|
4086 !aTargetNode->NodeInfo()->Equals(nsGkAtoms::key, |
|
4087 kNameSpaceID_XUL) && |
|
4088 !aTargetNode->NodeInfo()->Equals(nsGkAtoms::menuitem, |
|
4089 kNameSpaceID_XUL)) |
|
4090 continue; |
|
4091 } |
|
4092 |
|
4093 int32_t nameSpaceID = name->NamespaceID(); |
|
4094 nsIAtom* attr = name->LocalName(); |
|
4095 nsIAtom* prefix = name->GetPrefix(); |
|
4096 |
|
4097 nsAutoString value; |
|
4098 aOverlayNode->GetAttr(nameSpaceID, attr, value); |
|
4099 |
|
4100 // Element in the overlay has the 'removeelement' attribute set |
|
4101 // so remove it from the actual document. |
|
4102 if (attr == nsGkAtoms::removeelement && |
|
4103 value.EqualsLiteral("true")) { |
|
4104 |
|
4105 nsCOMPtr<nsINode> parent = aTargetNode->GetParentNode(); |
|
4106 if (!parent) return NS_ERROR_FAILURE; |
|
4107 rv = RemoveElement(parent, aTargetNode); |
|
4108 if (NS_FAILED(rv)) return rv; |
|
4109 |
|
4110 return NS_OK; |
|
4111 } |
|
4112 |
|
4113 rv = aTargetNode->SetAttr(nameSpaceID, attr, prefix, value, aNotify); |
|
4114 if (!NS_FAILED(rv) && !aNotify) |
|
4115 rv = mDocument->BroadcastAttributeChangeFromOverlay(aTargetNode, |
|
4116 nameSpaceID, |
|
4117 attr, prefix, |
|
4118 value); |
|
4119 if (NS_FAILED(rv)) return rv; |
|
4120 } |
|
4121 |
|
4122 |
|
4123 // Walk our child nodes, looking for elements that have the 'id' |
|
4124 // attribute set. If we find any, we must do a parent check in the |
|
4125 // actual document to ensure that the structure matches that of |
|
4126 // the actual document. If it does, we can call ourselves and attempt |
|
4127 // to merge inside that subtree. If not, we just append the tree to |
|
4128 // the parent like any other. |
|
4129 |
|
4130 uint32_t childCount = aOverlayNode->GetChildCount(); |
|
4131 |
|
4132 // This must be a strong reference since it will be the only |
|
4133 // reference to a content object during part of this loop. |
|
4134 nsCOMPtr<nsIContent> currContent; |
|
4135 |
|
4136 for (i = 0; i < childCount; ++i) { |
|
4137 currContent = aOverlayNode->GetFirstChild(); |
|
4138 |
|
4139 nsIAtom *idAtom = currContent->GetID(); |
|
4140 |
|
4141 nsIContent *elementInDocument = nullptr; |
|
4142 if (idAtom) { |
|
4143 nsDependentAtomString id(idAtom); |
|
4144 |
|
4145 if (!id.IsEmpty()) { |
|
4146 nsIDocument *doc = aTargetNode->GetDocument(); |
|
4147 if (!doc) return NS_ERROR_FAILURE; |
|
4148 |
|
4149 elementInDocument = doc->GetElementById(id); |
|
4150 } |
|
4151 } |
|
4152 |
|
4153 // The item has an 'id' attribute set, and we need to check with |
|
4154 // the actual document to see if an item with this id exists at |
|
4155 // this locale. If so, we want to merge the subtree under that |
|
4156 // node. Otherwise, we just do an append as if the element had |
|
4157 // no id attribute. |
|
4158 if (elementInDocument) { |
|
4159 // Given two parents, aTargetNode and aOverlayNode, we want |
|
4160 // to call merge on currContent if we find an associated |
|
4161 // node in the document with the same id as currContent that |
|
4162 // also has aTargetNode as its parent. |
|
4163 |
|
4164 nsIContent *elementParent = elementInDocument->GetParent(); |
|
4165 |
|
4166 nsIAtom *parentID = elementParent->GetID(); |
|
4167 if (parentID && |
|
4168 aTargetNode->AttrValueIs(kNameSpaceID_None, nsGkAtoms::id, |
|
4169 nsDependentAtomString(parentID), |
|
4170 eCaseMatters)) { |
|
4171 // The element matches. "Go Deep!" |
|
4172 rv = Merge(elementInDocument, currContent, aNotify); |
|
4173 if (NS_FAILED(rv)) return rv; |
|
4174 aOverlayNode->RemoveChildAt(0, false); |
|
4175 |
|
4176 continue; |
|
4177 } |
|
4178 } |
|
4179 |
|
4180 aOverlayNode->RemoveChildAt(0, false); |
|
4181 |
|
4182 rv = InsertElement(aTargetNode, currContent, aNotify); |
|
4183 if (NS_FAILED(rv)) return rv; |
|
4184 } |
|
4185 |
|
4186 return NS_OK; |
|
4187 } |
|
4188 |
|
4189 |
|
4190 |
|
4191 XULDocument::OverlayForwardReference::~OverlayForwardReference() |
|
4192 { |
|
4193 #ifdef PR_LOGGING |
|
4194 if (PR_LOG_TEST(gXULLog, PR_LOG_WARNING) && !mResolved) { |
|
4195 nsAutoString id; |
|
4196 mOverlay->GetAttr(kNameSpaceID_None, nsGkAtoms::id, id); |
|
4197 |
|
4198 nsAutoCString idC; |
|
4199 idC.AssignWithConversion(id); |
|
4200 |
|
4201 nsIURI *protoURI = mDocument->mCurrentPrototype->GetURI(); |
|
4202 nsAutoCString urlspec; |
|
4203 protoURI->GetSpec(urlspec); |
|
4204 |
|
4205 nsCOMPtr<nsIURI> docURI; |
|
4206 nsAutoCString parentDoc; |
|
4207 nsresult rv = mDocument->mChannel->GetOriginalURI(getter_AddRefs(docURI)); |
|
4208 if (NS_SUCCEEDED(rv)) |
|
4209 docURI->GetSpec(parentDoc); |
|
4210 PR_LOG(gXULLog, PR_LOG_WARNING, |
|
4211 ("xul: %s overlay failed to resolve '%s' in %s", |
|
4212 urlspec.get(), idC.get(), parentDoc.get())); |
|
4213 } |
|
4214 #endif |
|
4215 } |
|
4216 |
|
4217 |
|
4218 //---------------------------------------------------------------------- |
|
4219 // |
|
4220 // XULDocument::BroadcasterHookup |
|
4221 // |
|
4222 |
|
4223 nsForwardReference::Result |
|
4224 XULDocument::BroadcasterHookup::Resolve() |
|
4225 { |
|
4226 nsresult rv; |
|
4227 |
|
4228 bool listener; |
|
4229 rv = mDocument->CheckBroadcasterHookup(mObservesElement, &listener, &mResolved); |
|
4230 if (NS_FAILED(rv)) return eResolve_Error; |
|
4231 |
|
4232 return mResolved ? eResolve_Succeeded : eResolve_Later; |
|
4233 } |
|
4234 |
|
4235 |
|
4236 XULDocument::BroadcasterHookup::~BroadcasterHookup() |
|
4237 { |
|
4238 #ifdef PR_LOGGING |
|
4239 if (PR_LOG_TEST(gXULLog, PR_LOG_WARNING) && !mResolved) { |
|
4240 // Tell the world we failed |
|
4241 nsIAtom *tag = mObservesElement->Tag(); |
|
4242 |
|
4243 nsAutoString broadcasterID; |
|
4244 nsAutoString attribute; |
|
4245 |
|
4246 if (tag == nsGkAtoms::observes) { |
|
4247 mObservesElement->GetAttr(kNameSpaceID_None, nsGkAtoms::element, broadcasterID); |
|
4248 mObservesElement->GetAttr(kNameSpaceID_None, nsGkAtoms::attribute, attribute); |
|
4249 } |
|
4250 else { |
|
4251 mObservesElement->GetAttr(kNameSpaceID_None, nsGkAtoms::observes, broadcasterID); |
|
4252 attribute.AssignLiteral("*"); |
|
4253 } |
|
4254 |
|
4255 nsAutoCString attributeC,broadcasteridC; |
|
4256 attributeC.AssignWithConversion(attribute); |
|
4257 broadcasteridC.AssignWithConversion(broadcasterID); |
|
4258 PR_LOG(gXULLog, PR_LOG_WARNING, |
|
4259 ("xul: broadcaster hookup failed <%s attribute='%s'> to %s", |
|
4260 nsAtomCString(tag).get(), |
|
4261 attributeC.get(), |
|
4262 broadcasteridC.get())); |
|
4263 } |
|
4264 #endif |
|
4265 } |
|
4266 |
|
4267 |
|
4268 //---------------------------------------------------------------------- |
|
4269 // |
|
4270 // XULDocument::TemplateBuilderHookup |
|
4271 // |
|
4272 |
|
4273 nsForwardReference::Result |
|
4274 XULDocument::TemplateBuilderHookup::Resolve() |
|
4275 { |
|
4276 bool needsHookup; |
|
4277 nsresult rv = CheckTemplateBuilderHookup(mElement, &needsHookup); |
|
4278 if (NS_FAILED(rv)) |
|
4279 return eResolve_Error; |
|
4280 |
|
4281 if (needsHookup) { |
|
4282 rv = CreateTemplateBuilder(mElement); |
|
4283 if (NS_FAILED(rv)) |
|
4284 return eResolve_Error; |
|
4285 } |
|
4286 |
|
4287 return eResolve_Succeeded; |
|
4288 } |
|
4289 |
|
4290 |
|
4291 //---------------------------------------------------------------------- |
|
4292 |
|
4293 nsresult |
|
4294 XULDocument::BroadcastAttributeChangeFromOverlay(nsIContent* aNode, |
|
4295 int32_t aNameSpaceID, |
|
4296 nsIAtom* aAttribute, |
|
4297 nsIAtom* aPrefix, |
|
4298 const nsAString& aValue) |
|
4299 { |
|
4300 nsresult rv = NS_OK; |
|
4301 |
|
4302 if (!mBroadcasterMap || !CanBroadcast(aNameSpaceID, aAttribute)) |
|
4303 return rv; |
|
4304 |
|
4305 if (!aNode->IsElement()) |
|
4306 return rv; |
|
4307 |
|
4308 BroadcasterMapEntry* entry = static_cast<BroadcasterMapEntry*> |
|
4309 (PL_DHashTableOperate(mBroadcasterMap, aNode->AsElement(), PL_DHASH_LOOKUP)); |
|
4310 if (!PL_DHASH_ENTRY_IS_BUSY(entry)) |
|
4311 return rv; |
|
4312 |
|
4313 // We've got listeners: push the value. |
|
4314 int32_t i; |
|
4315 for (i = entry->mListeners.Count() - 1; i >= 0; --i) { |
|
4316 BroadcastListener* bl = static_cast<BroadcastListener*> |
|
4317 (entry->mListeners[i]); |
|
4318 |
|
4319 if ((bl->mAttribute != aAttribute) && |
|
4320 (bl->mAttribute != nsGkAtoms::_asterix)) |
|
4321 continue; |
|
4322 |
|
4323 nsCOMPtr<nsIContent> l = do_QueryReferent(bl->mListener); |
|
4324 if (l) { |
|
4325 rv = l->SetAttr(aNameSpaceID, aAttribute, |
|
4326 aPrefix, aValue, false); |
|
4327 if (NS_FAILED(rv)) return rv; |
|
4328 } |
|
4329 } |
|
4330 return rv; |
|
4331 } |
|
4332 |
|
4333 nsresult |
|
4334 XULDocument::FindBroadcaster(Element* aElement, |
|
4335 Element** aListener, |
|
4336 nsString& aBroadcasterID, |
|
4337 nsString& aAttribute, |
|
4338 Element** aBroadcaster) |
|
4339 { |
|
4340 nsINodeInfo *ni = aElement->NodeInfo(); |
|
4341 *aListener = nullptr; |
|
4342 *aBroadcaster = nullptr; |
|
4343 |
|
4344 if (ni->Equals(nsGkAtoms::observes, kNameSpaceID_XUL)) { |
|
4345 // It's an <observes> element, which means that the actual |
|
4346 // listener is the _parent_ node. This element should have an |
|
4347 // 'element' attribute that specifies the ID of the |
|
4348 // broadcaster element, and an 'attribute' element, which |
|
4349 // specifies the name of the attribute to observe. |
|
4350 nsIContent* parent = aElement->GetParent(); |
|
4351 if (!parent) { |
|
4352 // <observes> is the root element |
|
4353 return NS_FINDBROADCASTER_NOT_FOUND; |
|
4354 } |
|
4355 |
|
4356 // If we're still parented by an 'overlay' tag, then we haven't |
|
4357 // made it into the real document yet. Defer hookup. |
|
4358 if (parent->NodeInfo()->Equals(nsGkAtoms::overlay, |
|
4359 kNameSpaceID_XUL)) { |
|
4360 return NS_FINDBROADCASTER_AWAIT_OVERLAYS; |
|
4361 } |
|
4362 |
|
4363 *aListener = parent->IsElement() ? parent->AsElement() : nullptr; |
|
4364 NS_IF_ADDREF(*aListener); |
|
4365 |
|
4366 aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::element, aBroadcasterID); |
|
4367 if (aBroadcasterID.IsEmpty()) { |
|
4368 return NS_FINDBROADCASTER_NOT_FOUND; |
|
4369 } |
|
4370 aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::attribute, aAttribute); |
|
4371 } |
|
4372 else { |
|
4373 // It's a generic element, which means that we'll use the |
|
4374 // value of the 'observes' attribute to determine the ID of |
|
4375 // the broadcaster element, and we'll watch _all_ of its |
|
4376 // values. |
|
4377 aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::observes, aBroadcasterID); |
|
4378 |
|
4379 // Bail if there's no aBroadcasterID |
|
4380 if (aBroadcasterID.IsEmpty()) { |
|
4381 // Try the command attribute next. |
|
4382 aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::command, aBroadcasterID); |
|
4383 if (!aBroadcasterID.IsEmpty()) { |
|
4384 // We've got something in the command attribute. We |
|
4385 // only treat this as a normal broadcaster if we are |
|
4386 // not a menuitem or a key. |
|
4387 |
|
4388 if (ni->Equals(nsGkAtoms::menuitem, kNameSpaceID_XUL) || |
|
4389 ni->Equals(nsGkAtoms::key, kNameSpaceID_XUL)) { |
|
4390 return NS_FINDBROADCASTER_NOT_FOUND; |
|
4391 } |
|
4392 } |
|
4393 else { |
|
4394 return NS_FINDBROADCASTER_NOT_FOUND; |
|
4395 } |
|
4396 } |
|
4397 |
|
4398 *aListener = aElement; |
|
4399 NS_ADDREF(*aListener); |
|
4400 |
|
4401 aAttribute.AssignLiteral("*"); |
|
4402 } |
|
4403 |
|
4404 // Make sure we got a valid listener. |
|
4405 NS_ENSURE_TRUE(*aListener, NS_ERROR_UNEXPECTED); |
|
4406 |
|
4407 // Try to find the broadcaster element in the document. |
|
4408 *aBroadcaster = GetElementById(aBroadcasterID); |
|
4409 |
|
4410 // If we can't find the broadcaster, then we'll need to defer the |
|
4411 // hookup. We may need to resolve some of the other overlays |
|
4412 // first. |
|
4413 if (! *aBroadcaster) { |
|
4414 return NS_FINDBROADCASTER_AWAIT_OVERLAYS; |
|
4415 } |
|
4416 |
|
4417 NS_ADDREF(*aBroadcaster); |
|
4418 |
|
4419 return NS_FINDBROADCASTER_FOUND; |
|
4420 } |
|
4421 |
|
4422 nsresult |
|
4423 XULDocument::CheckBroadcasterHookup(Element* aElement, |
|
4424 bool* aNeedsHookup, |
|
4425 bool* aDidResolve) |
|
4426 { |
|
4427 // Resolve a broadcaster hookup. Look at the element that we're |
|
4428 // trying to resolve: it could be an '<observes>' element, or just |
|
4429 // a vanilla element with an 'observes' attribute on it. |
|
4430 nsresult rv; |
|
4431 |
|
4432 *aDidResolve = false; |
|
4433 |
|
4434 nsCOMPtr<Element> listener; |
|
4435 nsAutoString broadcasterID; |
|
4436 nsAutoString attribute; |
|
4437 nsCOMPtr<Element> broadcaster; |
|
4438 |
|
4439 rv = FindBroadcaster(aElement, getter_AddRefs(listener), |
|
4440 broadcasterID, attribute, getter_AddRefs(broadcaster)); |
|
4441 switch (rv) { |
|
4442 case NS_FINDBROADCASTER_NOT_FOUND: |
|
4443 *aNeedsHookup = false; |
|
4444 return NS_OK; |
|
4445 case NS_FINDBROADCASTER_AWAIT_OVERLAYS: |
|
4446 *aNeedsHookup = true; |
|
4447 return NS_OK; |
|
4448 case NS_FINDBROADCASTER_FOUND: |
|
4449 break; |
|
4450 default: |
|
4451 return rv; |
|
4452 } |
|
4453 |
|
4454 NS_ENSURE_ARG(broadcaster && listener); |
|
4455 ErrorResult domRv; |
|
4456 AddBroadcastListenerFor(*broadcaster, *listener, attribute, domRv); |
|
4457 if (domRv.Failed()) { |
|
4458 return domRv.ErrorCode(); |
|
4459 } |
|
4460 |
|
4461 #ifdef PR_LOGGING |
|
4462 // Tell the world we succeeded |
|
4463 if (PR_LOG_TEST(gXULLog, PR_LOG_NOTICE)) { |
|
4464 nsCOMPtr<nsIContent> content = |
|
4465 do_QueryInterface(listener); |
|
4466 |
|
4467 NS_ASSERTION(content != nullptr, "not an nsIContent"); |
|
4468 if (! content) |
|
4469 return rv; |
|
4470 |
|
4471 nsAutoCString attributeC,broadcasteridC; |
|
4472 attributeC.AssignWithConversion(attribute); |
|
4473 broadcasteridC.AssignWithConversion(broadcasterID); |
|
4474 PR_LOG(gXULLog, PR_LOG_NOTICE, |
|
4475 ("xul: broadcaster hookup <%s attribute='%s'> to %s", |
|
4476 nsAtomCString(content->Tag()).get(), |
|
4477 attributeC.get(), |
|
4478 broadcasteridC.get())); |
|
4479 } |
|
4480 #endif |
|
4481 |
|
4482 *aNeedsHookup = false; |
|
4483 *aDidResolve = true; |
|
4484 return NS_OK; |
|
4485 } |
|
4486 |
|
4487 nsresult |
|
4488 XULDocument::InsertElement(nsINode* aParent, nsIContent* aChild, |
|
4489 bool aNotify) |
|
4490 { |
|
4491 // Insert aChild appropriately into aParent, accounting for a |
|
4492 // 'pos' attribute set on aChild. |
|
4493 |
|
4494 nsAutoString posStr; |
|
4495 bool wasInserted = false; |
|
4496 |
|
4497 // insert after an element of a given id |
|
4498 aChild->GetAttr(kNameSpaceID_None, nsGkAtoms::insertafter, posStr); |
|
4499 bool isInsertAfter = true; |
|
4500 |
|
4501 if (posStr.IsEmpty()) { |
|
4502 aChild->GetAttr(kNameSpaceID_None, nsGkAtoms::insertbefore, posStr); |
|
4503 isInsertAfter = false; |
|
4504 } |
|
4505 |
|
4506 if (!posStr.IsEmpty()) { |
|
4507 nsIDocument *document = aParent->OwnerDoc(); |
|
4508 |
|
4509 nsIContent *content = nullptr; |
|
4510 |
|
4511 char* str = ToNewCString(posStr); |
|
4512 char* rest; |
|
4513 char* token = nsCRT::strtok(str, ", ", &rest); |
|
4514 |
|
4515 while (token) { |
|
4516 content = document->GetElementById(NS_ConvertASCIItoUTF16(token)); |
|
4517 if (content) |
|
4518 break; |
|
4519 |
|
4520 token = nsCRT::strtok(rest, ", ", &rest); |
|
4521 } |
|
4522 nsMemory::Free(str); |
|
4523 |
|
4524 if (content) { |
|
4525 int32_t pos = aParent->IndexOf(content); |
|
4526 |
|
4527 if (pos != -1) { |
|
4528 pos = isInsertAfter ? pos + 1 : pos; |
|
4529 nsresult rv = aParent->InsertChildAt(aChild, pos, aNotify); |
|
4530 if (NS_FAILED(rv)) |
|
4531 return rv; |
|
4532 |
|
4533 wasInserted = true; |
|
4534 } |
|
4535 } |
|
4536 } |
|
4537 |
|
4538 if (!wasInserted) { |
|
4539 |
|
4540 aChild->GetAttr(kNameSpaceID_None, nsGkAtoms::position, posStr); |
|
4541 if (!posStr.IsEmpty()) { |
|
4542 nsresult rv; |
|
4543 // Positions are one-indexed. |
|
4544 int32_t pos = posStr.ToInteger(&rv); |
|
4545 // Note: if the insertion index (which is |pos - 1|) would be less |
|
4546 // than 0 or greater than the number of children aParent has, then |
|
4547 // don't insert, since the position is bogus. Just skip on to |
|
4548 // appending. |
|
4549 if (NS_SUCCEEDED(rv) && pos > 0 && |
|
4550 uint32_t(pos - 1) <= aParent->GetChildCount()) { |
|
4551 rv = aParent->InsertChildAt(aChild, pos - 1, aNotify); |
|
4552 if (NS_SUCCEEDED(rv)) |
|
4553 wasInserted = true; |
|
4554 // If the insertion fails, then we should still |
|
4555 // attempt an append. Thus, rather than returning rv |
|
4556 // immediately, we fall through to the final |
|
4557 // "catch-all" case that just does an AppendChildTo. |
|
4558 } |
|
4559 } |
|
4560 } |
|
4561 |
|
4562 if (!wasInserted) { |
|
4563 return aParent->AppendChildTo(aChild, aNotify); |
|
4564 } |
|
4565 return NS_OK; |
|
4566 } |
|
4567 |
|
4568 nsresult |
|
4569 XULDocument::RemoveElement(nsINode* aParent, nsINode* aChild) |
|
4570 { |
|
4571 int32_t nodeOffset = aParent->IndexOf(aChild); |
|
4572 |
|
4573 aParent->RemoveChildAt(nodeOffset, true); |
|
4574 return NS_OK; |
|
4575 } |
|
4576 |
|
4577 //---------------------------------------------------------------------- |
|
4578 // |
|
4579 // CachedChromeStreamListener |
|
4580 // |
|
4581 |
|
4582 XULDocument::CachedChromeStreamListener::CachedChromeStreamListener(XULDocument* aDocument, bool aProtoLoaded) |
|
4583 : mDocument(aDocument), |
|
4584 mProtoLoaded(aProtoLoaded) |
|
4585 { |
|
4586 NS_ADDREF(mDocument); |
|
4587 } |
|
4588 |
|
4589 |
|
4590 XULDocument::CachedChromeStreamListener::~CachedChromeStreamListener() |
|
4591 { |
|
4592 NS_RELEASE(mDocument); |
|
4593 } |
|
4594 |
|
4595 |
|
4596 NS_IMPL_ISUPPORTS(XULDocument::CachedChromeStreamListener, |
|
4597 nsIRequestObserver, nsIStreamListener) |
|
4598 |
|
4599 NS_IMETHODIMP |
|
4600 XULDocument::CachedChromeStreamListener::OnStartRequest(nsIRequest *request, |
|
4601 nsISupports* acontext) |
|
4602 { |
|
4603 return NS_ERROR_PARSED_DATA_CACHED; |
|
4604 } |
|
4605 |
|
4606 |
|
4607 NS_IMETHODIMP |
|
4608 XULDocument::CachedChromeStreamListener::OnStopRequest(nsIRequest *request, |
|
4609 nsISupports* aContext, |
|
4610 nsresult aStatus) |
|
4611 { |
|
4612 if (! mProtoLoaded) |
|
4613 return NS_OK; |
|
4614 |
|
4615 return mDocument->OnPrototypeLoadDone(true); |
|
4616 } |
|
4617 |
|
4618 |
|
4619 NS_IMETHODIMP |
|
4620 XULDocument::CachedChromeStreamListener::OnDataAvailable(nsIRequest *request, |
|
4621 nsISupports* aContext, |
|
4622 nsIInputStream* aInStr, |
|
4623 uint64_t aSourceOffset, |
|
4624 uint32_t aCount) |
|
4625 { |
|
4626 NS_NOTREACHED("CachedChromeStream doesn't receive data"); |
|
4627 return NS_ERROR_UNEXPECTED; |
|
4628 } |
|
4629 |
|
4630 //---------------------------------------------------------------------- |
|
4631 // |
|
4632 // ParserObserver |
|
4633 // |
|
4634 |
|
4635 XULDocument::ParserObserver::ParserObserver(XULDocument* aDocument, |
|
4636 nsXULPrototypeDocument* aPrototype) |
|
4637 : mDocument(aDocument), mPrototype(aPrototype) |
|
4638 { |
|
4639 } |
|
4640 |
|
4641 XULDocument::ParserObserver::~ParserObserver() |
|
4642 { |
|
4643 } |
|
4644 |
|
4645 NS_IMPL_ISUPPORTS(XULDocument::ParserObserver, nsIRequestObserver) |
|
4646 |
|
4647 NS_IMETHODIMP |
|
4648 XULDocument::ParserObserver::OnStartRequest(nsIRequest *request, |
|
4649 nsISupports* aContext) |
|
4650 { |
|
4651 // Guard against buggy channels calling OnStartRequest multiple times. |
|
4652 if (mPrototype) { |
|
4653 nsCOMPtr<nsIChannel> channel = do_QueryInterface(request); |
|
4654 nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager(); |
|
4655 if (channel && secMan) { |
|
4656 nsCOMPtr<nsIPrincipal> principal; |
|
4657 secMan->GetChannelPrincipal(channel, getter_AddRefs(principal)); |
|
4658 |
|
4659 // Failure there is ok -- it'll just set a (safe) null principal |
|
4660 mPrototype->SetDocumentPrincipal(principal); |
|
4661 } |
|
4662 |
|
4663 // Make sure to avoid cycles |
|
4664 mPrototype = nullptr; |
|
4665 } |
|
4666 |
|
4667 return NS_OK; |
|
4668 } |
|
4669 |
|
4670 NS_IMETHODIMP |
|
4671 XULDocument::ParserObserver::OnStopRequest(nsIRequest *request, |
|
4672 nsISupports* aContext, |
|
4673 nsresult aStatus) |
|
4674 { |
|
4675 nsresult rv = NS_OK; |
|
4676 |
|
4677 if (NS_FAILED(aStatus)) { |
|
4678 // If an overlay load fails, we need to nudge the prototype |
|
4679 // walk along. |
|
4680 nsCOMPtr<nsIChannel> aChannel = do_QueryInterface(request); |
|
4681 if (aChannel) { |
|
4682 nsCOMPtr<nsIURI> uri; |
|
4683 aChannel->GetOriginalURI(getter_AddRefs(uri)); |
|
4684 if (uri) { |
|
4685 mDocument->ReportMissingOverlay(uri); |
|
4686 } |
|
4687 } |
|
4688 |
|
4689 rv = mDocument->ResumeWalk(); |
|
4690 } |
|
4691 |
|
4692 // Drop the reference to the document to break cycle between the |
|
4693 // document, the parser, the content sink, and the parser |
|
4694 // observer. |
|
4695 mDocument = nullptr; |
|
4696 |
|
4697 return rv; |
|
4698 } |
|
4699 |
|
4700 already_AddRefed<nsPIWindowRoot> |
|
4701 XULDocument::GetWindowRoot() |
|
4702 { |
|
4703 nsCOMPtr<nsIInterfaceRequestor> ir(mDocumentContainer); |
|
4704 nsCOMPtr<nsIDOMWindow> window(do_GetInterface(ir)); |
|
4705 nsCOMPtr<nsPIDOMWindow> piWin(do_QueryInterface(window)); |
|
4706 return piWin ? piWin->GetTopWindowRoot() : nullptr; |
|
4707 } |
|
4708 |
|
4709 bool |
|
4710 XULDocument::IsDocumentRightToLeft() |
|
4711 { |
|
4712 // setting the localedir attribute on the root element forces a |
|
4713 // specific direction for the document. |
|
4714 Element* element = GetRootElement(); |
|
4715 if (element) { |
|
4716 static nsIContent::AttrValuesArray strings[] = |
|
4717 {&nsGkAtoms::ltr, &nsGkAtoms::rtl, nullptr}; |
|
4718 switch (element->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::localedir, |
|
4719 strings, eCaseMatters)) { |
|
4720 case 0: return false; |
|
4721 case 1: return true; |
|
4722 default: break; // otherwise, not a valid value, so fall through |
|
4723 } |
|
4724 } |
|
4725 |
|
4726 // otherwise, get the locale from the chrome registry and |
|
4727 // look up the intl.uidirection.<locale> preference |
|
4728 nsCOMPtr<nsIXULChromeRegistry> reg = |
|
4729 mozilla::services::GetXULChromeRegistryService(); |
|
4730 if (!reg) |
|
4731 return false; |
|
4732 |
|
4733 nsAutoCString package; |
|
4734 bool isChrome; |
|
4735 if (NS_SUCCEEDED(mDocumentURI->SchemeIs("chrome", &isChrome)) && |
|
4736 isChrome) { |
|
4737 mDocumentURI->GetHostPort(package); |
|
4738 } |
|
4739 else { |
|
4740 // use the 'global' package for about and resource uris. |
|
4741 // otherwise, just default to left-to-right. |
|
4742 bool isAbout, isResource; |
|
4743 if (NS_SUCCEEDED(mDocumentURI->SchemeIs("about", &isAbout)) && |
|
4744 isAbout) { |
|
4745 package.AssignLiteral("global"); |
|
4746 } |
|
4747 else if (NS_SUCCEEDED(mDocumentURI->SchemeIs("resource", &isResource)) && |
|
4748 isResource) { |
|
4749 package.AssignLiteral("global"); |
|
4750 } |
|
4751 else { |
|
4752 return false; |
|
4753 } |
|
4754 } |
|
4755 |
|
4756 bool isRTL = false; |
|
4757 reg->IsLocaleRTL(package, &isRTL); |
|
4758 return isRTL; |
|
4759 } |
|
4760 |
|
4761 void |
|
4762 XULDocument::ResetDocumentDirection() |
|
4763 { |
|
4764 DocumentStatesChanged(NS_DOCUMENT_STATE_RTL_LOCALE); |
|
4765 } |
|
4766 |
|
4767 void |
|
4768 XULDocument::DirectionChanged(const char* aPrefName, void* aData) |
|
4769 { |
|
4770 // Reset the direction and restyle the document if necessary. |
|
4771 XULDocument* doc = (XULDocument *)aData; |
|
4772 if (doc) { |
|
4773 doc->ResetDocumentDirection(); |
|
4774 } |
|
4775 } |
|
4776 |
|
4777 int |
|
4778 XULDocument::GetDocumentLWTheme() |
|
4779 { |
|
4780 if (mDocLWTheme == Doc_Theme_Uninitialized) { |
|
4781 mDocLWTheme = Doc_Theme_None; // No lightweight theme by default |
|
4782 |
|
4783 Element* element = GetRootElement(); |
|
4784 nsAutoString hasLWTheme; |
|
4785 if (element && |
|
4786 element->GetAttr(kNameSpaceID_None, nsGkAtoms::lwtheme, hasLWTheme) && |
|
4787 !(hasLWTheme.IsEmpty()) && |
|
4788 hasLWTheme.EqualsLiteral("true")) { |
|
4789 mDocLWTheme = Doc_Theme_Neutral; |
|
4790 nsAutoString lwTheme; |
|
4791 element->GetAttr(kNameSpaceID_None, nsGkAtoms::lwthemetextcolor, lwTheme); |
|
4792 if (!(lwTheme.IsEmpty())) { |
|
4793 if (lwTheme.EqualsLiteral("dark")) |
|
4794 mDocLWTheme = Doc_Theme_Dark; |
|
4795 else if (lwTheme.EqualsLiteral("bright")) |
|
4796 mDocLWTheme = Doc_Theme_Bright; |
|
4797 } |
|
4798 } |
|
4799 } |
|
4800 return mDocLWTheme; |
|
4801 } |
|
4802 |
|
4803 NS_IMETHODIMP |
|
4804 XULDocument::GetBoxObjectFor(nsIDOMElement* aElement, nsIBoxObject** aResult) |
|
4805 { |
|
4806 ErrorResult rv; |
|
4807 nsCOMPtr<Element> el = do_QueryInterface(aElement); |
|
4808 *aResult = GetBoxObjectFor(el, rv).take(); |
|
4809 return rv.ErrorCode(); |
|
4810 } |
|
4811 |
|
4812 JSObject* |
|
4813 XULDocument::WrapNode(JSContext *aCx) |
|
4814 { |
|
4815 return XULDocumentBinding::Wrap(aCx, this); |
|
4816 } |
|
4817 |
|
4818 } // namespace dom |
|
4819 } // namespace mozilla |