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