Bug 459443. Make sure to detach our editor even if we don't have a session history...
[mozilla-central.git] / docshell / base / nsDocShell.cpp
blobc84769add8650ce362642f21c2c5ae4767a56c5c
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: ft=cpp tw=78 sw=4 et ts=4 sts=4 cin
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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.
16 * The Original Code is the Mozilla browser.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications, Inc.
20 * Portions created by the Initial Developer are Copyright (C) 1999
21 * the Initial Developer. All Rights Reserved.
23 * Contributor(s):
24 * Travis Bogard <travis@netscape.com>
25 * Pierre Phaneuf <pp@ludusdesign.com>
26 * Peter Annema <disttsc@bart.nl>
27 * Dan Rosen <dr@netscape.com>
28 * Mats Palmgren <mats.palmgren@bredband.net>
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.
42 * ***** END LICENSE BLOCK ***** */
44 #ifdef MOZ_LOGGING
45 // so we can get logging even in release builds (but only for some things)
46 #define FORCE_PR_LOG 1
47 #endif
49 #include "nsIBrowserDOMWindow.h"
50 #include "nsIComponentManager.h"
51 #include "nsIContent.h"
52 #include "nsIDocument.h"
53 #include "nsIDOMDocument.h"
54 #include "nsIDOMNSDocument.h"
55 #include "nsIDOMElement.h"
56 #include "nsIDOMStorage.h"
57 #include "nsPIDOMStorage.h"
58 #include "nsIDocumentViewer.h"
59 #include "nsIDocumentLoaderFactory.h"
60 #include "nsCURILoader.h"
61 #include "nsURILoader.h"
62 #include "nsDocShellCID.h"
63 #include "nsLayoutCID.h"
64 #include "nsDOMCID.h"
65 #include "nsIDOMScriptObjectFactory.h"
66 #include "nsNetUtil.h"
67 #include "nsRect.h"
68 #include "prprf.h"
69 #include "nsIMarkupDocumentViewer.h"
70 #include "nsXPIDLString.h"
71 #include "nsReadableUtils.h"
72 #include "nsIDOMEventTarget.h"
73 #include "nsIDOMChromeWindow.h"
74 #include "nsIDOMWindowInternal.h"
75 #include "nsIWebBrowserChrome.h"
76 #include "nsPoint.h"
77 #include "nsGfxCIID.h"
78 #include "nsIObserverService.h"
79 #include "nsIPrompt.h"
80 #include "nsIAuthPrompt.h"
81 #include "nsIAuthPrompt2.h"
82 #include "nsTextFormatter.h"
83 #include "nsIChannelEventSink.h"
84 #include "nsIUploadChannel.h"
85 #include "nsISecurityEventSink.h"
86 #include "nsIScriptSecurityManager.h"
87 #include "nsIJSContextStack.h"
88 #include "nsIScriptObjectPrincipal.h"
89 #include "nsDocumentCharsetInfoCID.h"
90 #include "nsICanvasFrame.h"
91 #include "nsIScrollableFrame.h"
92 #include "nsContentPolicyUtils.h" // NS_CheckContentLoadPolicy(...)
93 #include "nsICategoryManager.h"
94 #include "nsXPCOMCID.h"
95 #include "nsISeekableStream.h"
96 #include "nsAutoPtr.h"
97 #include "nsIPrefService.h"
98 #include "nsIPrefBranch.h"
99 #include "nsIPrefBranch2.h"
100 #include "nsIWritablePropertyBag2.h"
101 #include "nsIAppShell.h"
102 #include "nsWidgetsCID.h"
103 #include "nsDOMJSUtils.h"
104 #include "nsIInterfaceRequestorUtils.h"
105 #include "nsIView.h"
106 #include "nsIViewManager.h"
107 #include "nsIScrollableView.h"
108 #include "nsIScriptChannel.h"
109 #include "nsIURIClassifier.h"
110 #include "nsIOfflineCacheUpdate.h"
111 #include "nsCPrefetchService.h"
113 // we want to explore making the document own the load group
114 // so we can associate the document URI with the load group.
115 // until this point, we have an evil hack:
116 #include "nsIHttpChannelInternal.h"
119 // Local Includes
120 #include "nsDocShell.h"
121 #include "nsDocShellLoadInfo.h"
122 #include "nsCDefaultURIFixup.h"
123 #include "nsDocShellEnumerator.h"
124 #include "nsSHistory.h"
125 #include "nsDocShellEditorData.h"
127 // Helper Classes
128 #include "nsDOMError.h"
129 #include "nsEscape.h"
131 // Interfaces Needed
132 #include "nsIUploadChannel.h"
133 #include "nsIProgressEventSink.h"
134 #include "nsIWebProgress.h"
135 #include "nsILayoutHistoryState.h"
136 #include "nsITimer.h"
137 #include "nsISHistoryInternal.h"
138 #include "nsIPrincipal.h"
139 #include "nsIFileURL.h"
140 #include "nsIHistoryEntry.h"
141 #include "nsISHistoryListener.h"
142 #include "nsIWindowWatcher.h"
143 #include "nsIPromptFactory.h"
144 #include "nsIObserver.h"
145 #include "nsINestedURI.h"
146 #include "nsITransportSecurityInfo.h"
147 #include "nsINSSErrorsService.h"
148 #include "nsIApplicationCache.h"
149 #include "nsIApplicationCacheChannel.h"
150 #include "nsIApplicationCacheContainer.h"
151 #include "nsIPermissionManager.h"
153 // Editor-related
154 #include "nsIEditingSession.h"
156 #include "nsPIDOMWindow.h"
157 #include "nsIDOMDocument.h"
158 #include "nsICachingChannel.h"
159 #include "nsICacheVisitor.h"
160 #include "nsICacheEntryDescriptor.h"
161 #include "nsIMultiPartChannel.h"
162 #include "nsIWyciwygChannel.h"
164 // The following are for bug #13871: Prevent frameset spoofing
165 #include "nsIHTMLDocument.h"
167 // For reporting errors with the console service.
168 // These can go away if error reporting is propagated up past nsDocShell.
169 #include "nsIConsoleService.h"
170 #include "nsIScriptError.h"
172 // used to dispatch urls to default protocol handlers
173 #include "nsCExternalHandlerService.h"
174 #include "nsIExternalProtocolService.h"
176 #include "nsIFocusController.h"
178 #include "nsITextToSubURI.h"
180 #include "nsIJARChannel.h"
182 #include "prlog.h"
183 #include "prmem.h"
185 #include "nsISelectionDisplay.h"
187 #include "nsIGlobalHistory2.h"
188 #include "nsIGlobalHistory3.h"
190 #ifdef DEBUG_DOCSHELL_FOCUS
191 #include "nsIEventStateManager.h"
192 #endif
194 #include "nsIFrame.h"
196 // for embedding
197 #include "nsIWebBrowserChromeFocus.h"
199 #include "nsPluginError.h"
201 static NS_DEFINE_CID(kDOMScriptObjectFactoryCID,
202 NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
203 static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
205 #if defined(DEBUG_bryner) || defined(DEBUG_chb)
206 //#define DEBUG_DOCSHELL_FOCUS
207 #define DEBUG_PAGE_CACHE
208 #endif
210 #include "nsContentErrors.h"
211 #include "nsIFocusEventSuppressor.h"
213 // Number of documents currently loading
214 static PRInt32 gNumberOfDocumentsLoading = 0;
216 // Global count of existing docshells.
217 static PRInt32 gDocShellCount = 0;
219 // Global reference to the URI fixup service.
220 nsIURIFixup *nsDocShell::sURIFixup = 0;
222 // True means we validate window targets to prevent frameset
223 // spoofing. Initialize this to a non-bolean value so we know to check
224 // the pref on the creation of the first docshell.
225 static PRBool gValidateOrigin = (PRBool)0xffffffff;
227 // Hint for native dispatch of events on how long to delay after
228 // all documents have loaded in milliseconds before favoring normal
229 // native event dispatch priorites over performance
230 #define NS_EVENT_STARVATION_DELAY_HINT 2000
232 // This is needed for displaying an error message
233 // when navigation is attempted on a document when printing
234 // The value arbitrary as long as it doesn't conflict with
235 // any of the other values in the errors in DisplayLoadError
236 #define NS_ERROR_DOCUMENT_IS_PRINTMODE NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_GENERAL,2001)
238 #ifdef PR_LOGGING
239 #ifdef DEBUG
240 static PRLogModuleInfo* gDocShellLog;
241 #endif
242 static PRLogModuleInfo* gDocShellLeakLog;
243 #endif
245 const char kBrandBundleURL[] = "chrome://branding/locale/brand.properties";
246 const char kAppstringsBundleURL[] = "chrome://global/locale/appstrings.properties";
248 static void
249 FavorPerformanceHint(PRBool perfOverStarvation, PRUint32 starvationDelay)
251 nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
252 if (appShell)
253 appShell->FavorPerformanceHint(perfOverStarvation, starvationDelay);
256 //*****************************************************************************
257 //*** nsDocShellFocusController
258 //*****************************************************************************
260 class nsDocShellFocusController
263 public:
264 static nsDocShellFocusController* GetInstance() { return &mDocShellFocusControllerSingleton; }
265 virtual ~nsDocShellFocusController(){}
267 void Focus(nsIDocShell* aDS);
268 void ClosingDown(nsIDocShell* aDS);
270 protected:
271 nsDocShellFocusController(){}
273 nsIDocShell* mFocusedDocShell; // very weak reference
275 private:
276 static nsDocShellFocusController mDocShellFocusControllerSingleton;
279 nsDocShellFocusController nsDocShellFocusController::mDocShellFocusControllerSingleton;
281 //*****************************************************************************
282 //*** nsDocShell: Object Management
283 //*****************************************************************************
285 nsDocShell::nsDocShell():
286 nsDocLoader(),
287 mAllowSubframes(PR_TRUE),
288 mAllowPlugins(PR_TRUE),
289 mAllowJavascript(PR_TRUE),
290 mAllowMetaRedirects(PR_TRUE),
291 mAllowImages(PR_TRUE),
292 mFocusDocFirst(PR_FALSE),
293 mHasFocus(PR_FALSE),
294 mCreatingDocument(PR_FALSE),
295 mUseErrorPages(PR_FALSE),
296 mObserveErrorPages(PR_TRUE),
297 mAllowAuth(PR_TRUE),
298 mAllowKeywordFixup(PR_FALSE),
299 mIsOffScreenBrowser(PR_FALSE),
300 mFiredUnloadEvent(PR_FALSE),
301 mEODForCurrentDocument(PR_FALSE),
302 mURIResultedInDocument(PR_FALSE),
303 mIsBeingDestroyed(PR_FALSE),
304 mIsExecutingOnLoadHandler(PR_FALSE),
305 mIsPrintingOrPP(PR_FALSE),
306 mSavingOldViewer(PR_FALSE),
307 mAppType(nsIDocShell::APP_TYPE_UNKNOWN),
308 mChildOffset(0),
309 mBusyFlags(BUSY_FLAGS_NONE),
310 mMarginWidth(0),
311 mMarginHeight(0),
312 mItemType(typeContent),
313 mDefaultScrollbarPref(Scrollbar_Auto, Scrollbar_Auto),
314 mPreviousTransIndex(-1),
315 mLoadedTransIndex(-1),
316 mTreeOwner(nsnull),
317 mChromeEventHandler(nsnull)
318 #ifdef DEBUG
319 , mInEnsureScriptEnv(PR_FALSE)
320 #endif
322 if (gDocShellCount++ == 0) {
323 NS_ASSERTION(sURIFixup == nsnull,
324 "Huh, sURIFixup not null in first nsDocShell ctor!");
326 CallGetService(NS_URIFIXUP_CONTRACTID, &sURIFixup);
329 #ifdef PR_LOGGING
330 #ifdef DEBUG
331 if (! gDocShellLog)
332 gDocShellLog = PR_NewLogModule("nsDocShell");
333 #endif
334 if (nsnull == gDocShellLeakLog)
335 gDocShellLeakLog = PR_NewLogModule("nsDocShellLeak");
336 if (gDocShellLeakLog)
337 PR_LOG(gDocShellLeakLog, PR_LOG_DEBUG, ("DOCSHELL %p created\n", this));
338 #endif
341 nsDocShell::~nsDocShell()
343 nsDocShellFocusController* dsfc = nsDocShellFocusController::GetInstance();
344 if (dsfc) {
345 dsfc->ClosingDown(this);
347 Destroy();
349 if (--gDocShellCount == 0) {
350 NS_IF_RELEASE(sURIFixup);
353 #ifdef PR_LOGGING
354 if (gDocShellLeakLog)
355 PR_LOG(gDocShellLeakLog, PR_LOG_DEBUG, ("DOCSHELL %p destroyed\n", this));
356 #endif
359 nsresult
360 nsDocShell::Init()
362 nsresult rv = nsDocLoader::Init();
363 NS_ENSURE_SUCCESS(rv, rv);
365 NS_ASSERTION(mLoadGroup, "Something went wrong!");
367 mContentListener = new nsDSURIContentListener(this);
368 NS_ENSURE_TRUE(mContentListener, NS_ERROR_OUT_OF_MEMORY);
370 rv = mContentListener->Init();
371 NS_ENSURE_SUCCESS(rv, rv);
373 if (!mStorages.Init())
374 return NS_ERROR_OUT_OF_MEMORY;
376 // We want to hold a strong ref to the loadgroup, so it better hold a weak
377 // ref to us... use an InterfaceRequestorProxy to do this.
378 nsCOMPtr<InterfaceRequestorProxy> proxy =
379 new InterfaceRequestorProxy(static_cast<nsIInterfaceRequestor*>
380 (this));
381 NS_ENSURE_TRUE(proxy, NS_ERROR_OUT_OF_MEMORY);
382 mLoadGroup->SetNotificationCallbacks(proxy);
384 rv = nsDocLoader::AddDocLoaderAsChildOfRoot(this);
385 NS_ENSURE_SUCCESS(rv, rv);
387 // Add as |this| a progress listener to itself. A little weird, but
388 // simpler than reproducing all the listener-notification logic in
389 // overrides of the various methods via which nsDocLoader can be
390 // notified. Note that this holds an nsWeakPtr to ourselves, so it's ok.
391 return AddProgressListener(this, nsIWebProgress::NOTIFY_STATE_DOCUMENT |
392 nsIWebProgress::NOTIFY_STATE_NETWORK);
396 void
397 nsDocShell::DestroyChildren()
399 nsCOMPtr<nsIDocShellTreeItem> shell;
400 PRInt32 n = mChildList.Count();
401 for (PRInt32 i = 0; i < n; i++) {
402 shell = do_QueryInterface(ChildAt(i));
403 NS_ASSERTION(shell, "docshell has null child");
405 if (shell) {
406 shell->SetTreeOwner(nsnull);
410 nsDocLoader::DestroyChildren();
413 //*****************************************************************************
414 // nsDocShell::nsISupports
415 //*****************************************************************************
417 NS_IMPL_ADDREF_INHERITED(nsDocShell, nsDocLoader)
418 NS_IMPL_RELEASE_INHERITED(nsDocShell, nsDocLoader)
420 NS_INTERFACE_MAP_BEGIN(nsDocShell)
421 NS_INTERFACE_MAP_ENTRY(nsIDocShell)
422 NS_INTERFACE_MAP_ENTRY(nsIDocShellTreeItem)
423 NS_INTERFACE_MAP_ENTRY(nsIDocShellTreeNode)
424 NS_INTERFACE_MAP_ENTRY(nsIDocShellHistory)
425 NS_INTERFACE_MAP_ENTRY(nsIWebNavigation)
426 NS_INTERFACE_MAP_ENTRY(nsIBaseWindow)
427 NS_INTERFACE_MAP_ENTRY(nsIScrollable)
428 NS_INTERFACE_MAP_ENTRY(nsITextScroll)
429 NS_INTERFACE_MAP_ENTRY(nsIDocCharset)
430 NS_INTERFACE_MAP_ENTRY(nsIScriptGlobalObjectOwner)
431 NS_INTERFACE_MAP_ENTRY(nsIRefreshURI)
432 NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
433 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
434 NS_INTERFACE_MAP_ENTRY(nsIContentViewerContainer)
435 NS_INTERFACE_MAP_ENTRY(nsIEditorDocShell)
436 NS_INTERFACE_MAP_ENTRY(nsIWebPageDescriptor)
437 NS_INTERFACE_MAP_ENTRY(nsIAuthPromptProvider)
438 NS_INTERFACE_MAP_ENTRY(nsIObserver)
439 NS_INTERFACE_MAP_ENTRY(nsILoadContext)
440 NS_INTERFACE_MAP_ENTRY(nsIDocShell_MOZILLA_1_9_1)
441 NS_INTERFACE_MAP_END_INHERITING(nsDocLoader)
443 ///*****************************************************************************
444 // nsDocShell::nsIInterfaceRequestor
445 //*****************************************************************************
446 NS_IMETHODIMP nsDocShell::GetInterface(const nsIID & aIID, void **aSink)
448 NS_PRECONDITION(aSink, "null out param");
450 *aSink = nsnull;
452 if (aIID.Equals(NS_GET_IID(nsIURIContentListener))) {
453 *aSink = mContentListener;
455 else if (aIID.Equals(NS_GET_IID(nsIScriptGlobalObject)) &&
456 NS_SUCCEEDED(EnsureScriptEnvironment())) {
457 *aSink = mScriptGlobal;
459 else if ((aIID.Equals(NS_GET_IID(nsIDOMWindowInternal)) ||
460 aIID.Equals(NS_GET_IID(nsPIDOMWindow)) ||
461 aIID.Equals(NS_GET_IID(nsIDOMWindow))) &&
462 NS_SUCCEEDED(EnsureScriptEnvironment())) {
463 return mScriptGlobal->QueryInterface(aIID, aSink);
465 else if (aIID.Equals(NS_GET_IID(nsIDOMDocument)) &&
466 NS_SUCCEEDED(EnsureContentViewer())) {
467 mContentViewer->GetDOMDocument((nsIDOMDocument **) aSink);
468 return *aSink ? NS_OK : NS_NOINTERFACE;
470 else if (aIID.Equals(NS_GET_IID(nsIApplicationCacheContainer))) {
471 *aSink = nsnull;
473 // Return application cache associated with this docshell, if any
475 nsCOMPtr<nsIContentViewer> contentViewer;
476 GetContentViewer(getter_AddRefs(contentViewer));
477 if (!contentViewer)
478 return NS_ERROR_NO_INTERFACE;
480 nsCOMPtr<nsIDOMDocument> domDoc;
481 contentViewer->GetDOMDocument(getter_AddRefs(domDoc));
482 NS_ASSERTION(domDoc, "Should have a document.");
483 if (!domDoc)
484 return NS_ERROR_NO_INTERFACE;
486 #if defined(PR_LOGGING) && defined(DEBUG)
487 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
488 ("nsDocShell[%p]: returning app cache container %p",
489 this, domDoc.get()));
490 #endif
491 return domDoc->QueryInterface(aIID, aSink);
493 else if (aIID.Equals(NS_GET_IID(nsIPrompt)) &&
494 NS_SUCCEEDED(EnsureScriptEnvironment())) {
495 nsresult rv;
496 nsCOMPtr<nsIWindowWatcher> wwatch =
497 do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
498 NS_ENSURE_SUCCESS(rv, rv);
500 nsCOMPtr<nsIDOMWindow> window(do_QueryInterface(mScriptGlobal));
502 // Get the an auth prompter for our window so that the parenting
503 // of the dialogs works as it should when using tabs.
505 nsIPrompt *prompt;
506 rv = wwatch->GetNewPrompter(window, &prompt);
507 NS_ENSURE_SUCCESS(rv, rv);
509 *aSink = prompt;
510 return NS_OK;
512 else if (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) ||
513 aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) {
514 return NS_SUCCEEDED(
515 GetAuthPrompt(PROMPT_NORMAL, aIID, aSink)) ?
516 NS_OK : NS_NOINTERFACE;
518 else if (aIID.Equals(NS_GET_IID(nsISHistory))) {
519 nsCOMPtr<nsISHistory> shistory;
520 nsresult
521 rv =
522 GetSessionHistory(getter_AddRefs(shistory));
523 if (NS_SUCCEEDED(rv) && shistory) {
524 *aSink = shistory;
525 NS_ADDREF((nsISupports *) * aSink);
526 return NS_OK;
528 return NS_NOINTERFACE;
530 else if (aIID.Equals(NS_GET_IID(nsIWebBrowserFind))) {
531 nsresult rv = EnsureFind();
532 if (NS_FAILED(rv)) return rv;
534 *aSink = mFind;
535 NS_ADDREF((nsISupports*)*aSink);
536 return NS_OK;
538 else if (aIID.Equals(NS_GET_IID(nsIEditingSession)) && NS_SUCCEEDED(EnsureEditorData())) {
539 nsCOMPtr<nsIEditingSession> editingSession;
540 mEditorData->GetEditingSession(getter_AddRefs(editingSession));
541 if (editingSession)
543 *aSink = editingSession;
544 NS_ADDREF((nsISupports *)*aSink);
545 return NS_OK;
548 return NS_NOINTERFACE;
550 else if (aIID.Equals(NS_GET_IID(nsIClipboardDragDropHookList))
551 && NS_SUCCEEDED(EnsureTransferableHookData())) {
552 *aSink = mTransferableHookData;
553 NS_ADDREF((nsISupports *)*aSink);
554 return NS_OK;
556 else if (aIID.Equals(NS_GET_IID(nsISelectionDisplay))) {
557 nsCOMPtr<nsIPresShell> shell;
558 nsresult rv = GetPresShell(getter_AddRefs(shell));
559 if (NS_SUCCEEDED(rv) && shell)
560 return shell->QueryInterface(aIID,aSink);
562 else if (aIID.Equals(NS_GET_IID(nsIDocShellTreeOwner))) {
563 nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
564 nsresult rv = GetTreeOwner(getter_AddRefs(treeOwner));
565 if (NS_SUCCEEDED(rv) && treeOwner)
566 return treeOwner->QueryInterface(aIID, aSink);
568 else {
569 return nsDocLoader::GetInterface(aIID, aSink);
572 NS_IF_ADDREF(((nsISupports *) * aSink));
573 return *aSink ? NS_OK : NS_NOINTERFACE;
576 PRUint32
577 nsDocShell::
578 ConvertDocShellLoadInfoToLoadType(nsDocShellInfoLoadType aDocShellLoadType)
580 PRUint32 loadType = LOAD_NORMAL;
582 switch (aDocShellLoadType) {
583 case nsIDocShellLoadInfo::loadNormal:
584 loadType = LOAD_NORMAL;
585 break;
586 case nsIDocShellLoadInfo::loadNormalReplace:
587 loadType = LOAD_NORMAL_REPLACE;
588 break;
589 case nsIDocShellLoadInfo::loadNormalExternal:
590 loadType = LOAD_NORMAL_EXTERNAL;
591 break;
592 case nsIDocShellLoadInfo::loadHistory:
593 loadType = LOAD_HISTORY;
594 break;
595 case nsIDocShellLoadInfo::loadNormalBypassCache:
596 loadType = LOAD_NORMAL_BYPASS_CACHE;
597 break;
598 case nsIDocShellLoadInfo::loadNormalBypassProxy:
599 loadType = LOAD_NORMAL_BYPASS_PROXY;
600 break;
601 case nsIDocShellLoadInfo::loadNormalBypassProxyAndCache:
602 loadType = LOAD_NORMAL_BYPASS_PROXY_AND_CACHE;
603 break;
604 case nsIDocShellLoadInfo::loadReloadNormal:
605 loadType = LOAD_RELOAD_NORMAL;
606 break;
607 case nsIDocShellLoadInfo::loadReloadCharsetChange:
608 loadType = LOAD_RELOAD_CHARSET_CHANGE;
609 break;
610 case nsIDocShellLoadInfo::loadReloadBypassCache:
611 loadType = LOAD_RELOAD_BYPASS_CACHE;
612 break;
613 case nsIDocShellLoadInfo::loadReloadBypassProxy:
614 loadType = LOAD_RELOAD_BYPASS_PROXY;
615 break;
616 case nsIDocShellLoadInfo::loadReloadBypassProxyAndCache:
617 loadType = LOAD_RELOAD_BYPASS_PROXY_AND_CACHE;
618 break;
619 case nsIDocShellLoadInfo::loadLink:
620 loadType = LOAD_LINK;
621 break;
622 case nsIDocShellLoadInfo::loadRefresh:
623 loadType = LOAD_REFRESH;
624 break;
625 case nsIDocShellLoadInfo::loadBypassHistory:
626 loadType = LOAD_BYPASS_HISTORY;
627 break;
628 case nsIDocShellLoadInfo::loadStopContent:
629 loadType = LOAD_STOP_CONTENT;
630 break;
631 case nsIDocShellLoadInfo::loadStopContentAndReplace:
632 loadType = LOAD_STOP_CONTENT_AND_REPLACE;
633 break;
634 default:
635 NS_NOTREACHED("Unexpected nsDocShellInfoLoadType value");
638 return loadType;
642 nsDocShellInfoLoadType
643 nsDocShell::ConvertLoadTypeToDocShellLoadInfo(PRUint32 aLoadType)
645 nsDocShellInfoLoadType docShellLoadType = nsIDocShellLoadInfo::loadNormal;
646 switch (aLoadType) {
647 case LOAD_NORMAL:
648 docShellLoadType = nsIDocShellLoadInfo::loadNormal;
649 break;
650 case LOAD_NORMAL_REPLACE:
651 docShellLoadType = nsIDocShellLoadInfo::loadNormalReplace;
652 break;
653 case LOAD_NORMAL_EXTERNAL:
654 docShellLoadType = nsIDocShellLoadInfo::loadNormalExternal;
655 break;
656 case LOAD_NORMAL_BYPASS_CACHE:
657 docShellLoadType = nsIDocShellLoadInfo::loadNormalBypassCache;
658 break;
659 case LOAD_NORMAL_BYPASS_PROXY:
660 docShellLoadType = nsIDocShellLoadInfo::loadNormalBypassProxy;
661 break;
662 case LOAD_NORMAL_BYPASS_PROXY_AND_CACHE:
663 docShellLoadType = nsIDocShellLoadInfo::loadNormalBypassProxyAndCache;
664 break;
665 case LOAD_HISTORY:
666 docShellLoadType = nsIDocShellLoadInfo::loadHistory;
667 break;
668 case LOAD_RELOAD_NORMAL:
669 docShellLoadType = nsIDocShellLoadInfo::loadReloadNormal;
670 break;
671 case LOAD_RELOAD_CHARSET_CHANGE:
672 docShellLoadType = nsIDocShellLoadInfo::loadReloadCharsetChange;
673 break;
674 case LOAD_RELOAD_BYPASS_CACHE:
675 docShellLoadType = nsIDocShellLoadInfo::loadReloadBypassCache;
676 break;
677 case LOAD_RELOAD_BYPASS_PROXY:
678 docShellLoadType = nsIDocShellLoadInfo::loadReloadBypassProxy;
679 break;
680 case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
681 docShellLoadType = nsIDocShellLoadInfo::loadReloadBypassProxyAndCache;
682 break;
683 case LOAD_LINK:
684 docShellLoadType = nsIDocShellLoadInfo::loadLink;
685 break;
686 case LOAD_REFRESH:
687 docShellLoadType = nsIDocShellLoadInfo::loadRefresh;
688 break;
689 case LOAD_BYPASS_HISTORY:
690 case LOAD_ERROR_PAGE:
691 docShellLoadType = nsIDocShellLoadInfo::loadBypassHistory;
692 break;
693 case LOAD_STOP_CONTENT:
694 docShellLoadType = nsIDocShellLoadInfo::loadStopContent;
695 break;
696 case LOAD_STOP_CONTENT_AND_REPLACE:
697 docShellLoadType = nsIDocShellLoadInfo::loadStopContentAndReplace;
698 break;
699 default:
700 NS_NOTREACHED("Unexpected load type value");
703 return docShellLoadType;
706 //*****************************************************************************
707 // nsDocShell::nsIDocShell
708 //*****************************************************************************
709 NS_IMETHODIMP
710 nsDocShell::LoadURI(nsIURI * aURI,
711 nsIDocShellLoadInfo * aLoadInfo,
712 PRUint32 aLoadFlags,
713 PRBool aFirstParty)
715 NS_PRECONDITION(aLoadInfo || (aLoadFlags & EXTRA_LOAD_FLAGS) == 0,
716 "Unexpected flags");
717 NS_PRECONDITION((aLoadFlags & 0xf) == 0, "Should not have these flags set");
719 // Note: we allow loads to get through here even if mFiredUnloadEvent is
720 // true; that case will get handled in LoadInternal or LoadHistoryEntry.
721 if (IsPrintingOrPP()) {
722 return NS_OK; // JS may not handle returning of an error code
724 nsresult rv;
725 nsCOMPtr<nsIURI> referrer;
726 nsCOMPtr<nsIInputStream> postStream;
727 nsCOMPtr<nsIInputStream> headersStream;
728 nsCOMPtr<nsISupports> owner;
729 PRBool inheritOwner = PR_FALSE;
730 PRBool sendReferrer = PR_TRUE;
731 nsCOMPtr<nsISHEntry> shEntry;
732 nsXPIDLString target;
733 PRUint32 loadType = MAKE_LOAD_TYPE(LOAD_NORMAL, aLoadFlags);
735 NS_ENSURE_ARG(aURI);
737 // Extract the info from the DocShellLoadInfo struct...
738 if (aLoadInfo) {
739 aLoadInfo->GetReferrer(getter_AddRefs(referrer));
741 nsDocShellInfoLoadType lt = nsIDocShellLoadInfo::loadNormal;
742 aLoadInfo->GetLoadType(&lt);
743 // Get the appropriate loadType from nsIDocShellLoadInfo type
744 loadType = ConvertDocShellLoadInfoToLoadType(lt);
746 aLoadInfo->GetOwner(getter_AddRefs(owner));
747 aLoadInfo->GetInheritOwner(&inheritOwner);
748 aLoadInfo->GetSHEntry(getter_AddRefs(shEntry));
749 aLoadInfo->GetTarget(getter_Copies(target));
750 aLoadInfo->GetPostDataStream(getter_AddRefs(postStream));
751 aLoadInfo->GetHeadersStream(getter_AddRefs(headersStream));
752 aLoadInfo->GetSendReferrer(&sendReferrer);
755 #if defined(PR_LOGGING) && defined(DEBUG)
756 if (PR_LOG_TEST(gDocShellLog, PR_LOG_DEBUG)) {
757 nsCAutoString uristr;
758 aURI->GetAsciiSpec(uristr);
759 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
760 ("nsDocShell[%p]: loading %s with flags 0x%08x",
761 this, uristr.get(), aLoadFlags));
763 #endif
765 if (!shEntry &&
766 !LOAD_TYPE_HAS_FLAGS(loadType, LOAD_FLAGS_REPLACE_HISTORY)) {
767 // First verify if this is a subframe.
768 nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
769 GetSameTypeParent(getter_AddRefs(parentAsItem));
770 nsCOMPtr<nsIDocShell> parentDS(do_QueryInterface(parentAsItem));
771 PRUint32 parentLoadType;
773 if (parentDS && parentDS != static_cast<nsIDocShell *>(this)) {
774 /* OK. It is a subframe. Checkout the
775 * parent's loadtype. If the parent was loaded thro' a history
776 * mechanism, then get the SH entry for the child from the parent.
777 * This is done to restore frameset navigation while going back/forward.
778 * If the parent was loaded through any other loadType, set the
779 * child's loadType too accordingly, so that session history does not
780 * get confused.
783 // Get the parent's load type
784 parentDS->GetLoadType(&parentLoadType);
786 nsCOMPtr<nsIDocShellHistory> parent(do_QueryInterface(parentAsItem));
787 if (parent) {
788 // Get the ShEntry for the child from the parent
789 parent->GetChildSHEntry(mChildOffset, getter_AddRefs(shEntry));
790 // Make some decisions on the child frame's loadType based on the
791 // parent's loadType.
792 if (mCurrentURI == nsnull) {
793 // This is a newly created frame. Check for exception cases first.
794 // By default the subframe will inherit the parent's loadType.
795 if (shEntry && (parentLoadType == LOAD_NORMAL ||
796 parentLoadType == LOAD_LINK ||
797 parentLoadType == LOAD_NORMAL_EXTERNAL)) {
798 // The parent was loaded normally. In this case, this *brand new* child really shouldn't
799 // have a SHEntry. If it does, it could be because the parent is replacing an
800 // existing frame with a new frame, in the onLoadHandler. We don't want this
801 // url to get into session history. Clear off shEntry, and set laod type to
802 // LOAD_BYPASS_HISTORY.
803 PRBool inOnLoadHandler=PR_FALSE;
804 parentDS->GetIsExecutingOnLoadHandler(&inOnLoadHandler);
805 if (inOnLoadHandler) {
806 loadType = LOAD_NORMAL_REPLACE;
807 shEntry = nsnull;
810 else if (parentLoadType == LOAD_REFRESH) {
811 // Clear shEntry. For refresh loads, we have to load
812 // what comes thro' the pipe, not what's in history.
813 shEntry = nsnull;
815 else if ((parentLoadType == LOAD_BYPASS_HISTORY) ||
816 (parentLoadType == LOAD_ERROR_PAGE) ||
817 (shEntry &&
818 ((parentLoadType & LOAD_CMD_HISTORY) ||
819 (parentLoadType == LOAD_RELOAD_NORMAL) ||
820 (parentLoadType == LOAD_RELOAD_CHARSET_CHANGE)))) {
821 // If the parent url, bypassed history or was loaded from
822 // history, pass on the parent's loadType to the new child
823 // frame too, so that the child frame will also
824 // avoid getting into history.
825 loadType = parentLoadType;
828 else {
829 // This is a pre-existing subframe. If the load was not originally initiated
830 // by session history, (if (!shEntry) condition succeeded) and mCurrentURI is not null,
831 // it is possible that a parent's onLoadHandler or even self's onLoadHandler is loading
832 // a new page in this child. Check parent's and self's busy flag and if it is set,
833 // we don't want this onLoadHandler load to get in to session history.
834 PRUint32 parentBusy = BUSY_FLAGS_NONE;
835 PRUint32 selfBusy = BUSY_FLAGS_NONE;
836 parentDS->GetBusyFlags(&parentBusy);
837 GetBusyFlags(&selfBusy);
838 if (((parentBusy & BUSY_FLAGS_BUSY) ||
839 (selfBusy & BUSY_FLAGS_BUSY)) &&
840 shEntry) {
841 loadType = LOAD_NORMAL_REPLACE;
842 shEntry = nsnull;
845 } // parent
846 } //parentDS
847 else {
848 // This is the root docshell. If we got here while
849 // executing an onLoad Handler,this load will not go
850 // into session history.
851 PRBool inOnLoadHandler=PR_FALSE;
852 GetIsExecutingOnLoadHandler(&inOnLoadHandler);
853 if (inOnLoadHandler) {
854 loadType = LOAD_NORMAL_REPLACE;
857 } // !shEntry
859 if (shEntry) {
860 #ifdef DEBUG
861 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
862 ("nsDocShell[%p]: loading from session history", this));
863 #endif
865 rv = LoadHistoryEntry(shEntry, loadType);
867 // Perform the load...
868 else {
869 // We need an owner (a referring principal). 4 possibilities:
870 // (1) If the system principal was passed in and we're a typeContent
871 // docshell, inherit the principal from the current document
872 // instead.
873 // (2) In all other cases when the principal passed in is not null,
874 // use that principal.
875 // (3) If the caller has allowed inheriting from the current document,
876 // or if we're being called from system code (eg chrome JS or pure
877 // C++) then inheritOwner should be true and InternalLoad will get
878 // an owner from the current document. If none of these things are
879 // true, then
880 // (4) we pass a null owner into the channel, and an owner will be
881 // created later from the channel's internal data.
883 // NOTE: This all only works because the only thing the owner is used
884 // for in InternalLoad is data:, javascript:, and about:blank
885 // URIs. For other URIs this would all be dead wrong!
886 nsCOMPtr<nsIScriptSecurityManager> secMan =
887 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
888 NS_ENSURE_SUCCESS(rv, rv);
890 if (owner && mItemType != typeChrome) {
891 nsCOMPtr<nsIPrincipal> ownerPrincipal = do_QueryInterface(owner);
892 PRBool isSystem;
893 rv = secMan->IsSystemPrincipal(ownerPrincipal, &isSystem);
894 NS_ENSURE_SUCCESS(rv, rv);
896 if (isSystem) {
897 owner = nsnull;
898 inheritOwner = PR_TRUE;
901 if (!owner && !inheritOwner) {
902 // See if there's system or chrome JS code running
903 rv = secMan->SubjectPrincipalIsSystem(&inheritOwner);
904 if (NS_FAILED(rv)) {
905 // Set it back to false
906 inheritOwner = PR_FALSE;
910 PRUint32 flags = 0;
912 if (inheritOwner)
913 flags |= INTERNAL_LOAD_FLAGS_INHERIT_OWNER;
915 if (!sendReferrer)
916 flags |= INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER;
918 if (aLoadFlags & LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP)
919 flags |= INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
921 if (aLoadFlags & LOAD_FLAGS_FIRST_LOAD)
922 flags |= INTERNAL_LOAD_FLAGS_FIRST_LOAD;
924 if (aLoadFlags & LOAD_FLAGS_BYPASS_CLASSIFIER)
925 flags |= INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER;
927 rv = InternalLoad(aURI,
928 referrer,
929 owner,
930 flags,
931 target.get(),
932 nsnull, // No type hint
933 postStream,
934 headersStream,
935 loadType,
936 nsnull, // No SHEntry
937 aFirstParty,
938 nsnull, // No nsIDocShell
939 nsnull); // No nsIRequest
942 return rv;
945 NS_IMETHODIMP
946 nsDocShell::LoadStream(nsIInputStream *aStream, nsIURI * aURI,
947 const nsACString &aContentType,
948 const nsACString &aContentCharset,
949 nsIDocShellLoadInfo * aLoadInfo)
951 NS_ENSURE_ARG(aStream);
953 mAllowKeywordFixup = PR_FALSE;
955 // if the caller doesn't pass in a URI we need to create a dummy URI. necko
956 // currently requires a URI in various places during the load. Some consumers
957 // do as well.
958 nsCOMPtr<nsIURI> uri = aURI;
959 if (!uri) {
960 // HACK ALERT
961 nsresult rv = NS_OK;
962 uri = do_CreateInstance(NS_SIMPLEURI_CONTRACTID, &rv);
963 if (NS_FAILED(rv))
964 return rv;
965 // Make sure that the URI spec "looks" like a protocol and path...
966 // For now, just use a bogus protocol called "internal"
967 rv = uri->SetSpec(NS_LITERAL_CSTRING("internal:load-stream"));
968 if (NS_FAILED(rv))
969 return rv;
972 PRUint32 loadType = LOAD_NORMAL;
973 if (aLoadInfo) {
974 nsDocShellInfoLoadType lt = nsIDocShellLoadInfo::loadNormal;
975 (void) aLoadInfo->GetLoadType(&lt);
976 // Get the appropriate LoadType from nsIDocShellLoadInfo type
977 loadType = ConvertDocShellLoadInfoToLoadType(lt);
980 NS_ENSURE_SUCCESS(Stop(nsIWebNavigation::STOP_NETWORK), NS_ERROR_FAILURE);
982 mLoadType = loadType;
984 // build up a channel for this stream.
985 nsCOMPtr<nsIChannel> channel;
986 NS_ENSURE_SUCCESS(NS_NewInputStreamChannel
987 (getter_AddRefs(channel), uri, aStream,
988 aContentType, aContentCharset),
989 NS_ERROR_FAILURE);
991 nsCOMPtr<nsIURILoader>
992 uriLoader(do_GetService(NS_URI_LOADER_CONTRACTID));
993 NS_ENSURE_TRUE(uriLoader, NS_ERROR_FAILURE);
995 NS_ENSURE_SUCCESS(DoChannelLoad(channel, uriLoader, PR_FALSE),
996 NS_ERROR_FAILURE);
997 return NS_OK;
1000 NS_IMETHODIMP
1001 nsDocShell::CreateLoadInfo(nsIDocShellLoadInfo ** aLoadInfo)
1003 nsDocShellLoadInfo *loadInfo = new nsDocShellLoadInfo();
1004 NS_ENSURE_TRUE(loadInfo, NS_ERROR_OUT_OF_MEMORY);
1005 nsCOMPtr<nsIDocShellLoadInfo> localRef(loadInfo);
1007 *aLoadInfo = localRef;
1008 NS_ADDREF(*aLoadInfo);
1009 return NS_OK;
1014 * Reset state to a new content model within the current document and the document
1015 * viewer. Called by the document before initiating an out of band document.write().
1017 NS_IMETHODIMP
1018 nsDocShell::PrepareForNewContentModel()
1020 mEODForCurrentDocument = PR_FALSE;
1021 return NS_OK;
1025 NS_IMETHODIMP
1026 nsDocShell::FirePageHideNotification(PRBool aIsUnload)
1028 if (mContentViewer && !mFiredUnloadEvent) {
1029 // Keep an explicit reference since calling PageHide could release
1030 // mContentViewer
1031 nsCOMPtr<nsIContentViewer> kungFuDeathGrip(mContentViewer);
1032 mFiredUnloadEvent = PR_TRUE;
1034 mContentViewer->PageHide(aIsUnload);
1036 nsAutoTArray<nsCOMPtr<nsIDocShell>, 8> kids;
1037 PRInt32 i, n = mChildList.Count();
1038 kids.SetCapacity(n);
1039 for (i = 0; i < n; i++) {
1040 kids.AppendElement(do_QueryInterface(ChildAt(i)));
1043 n = kids.Length();
1044 for (i = 0; i < n; ++i) {
1045 if (kids[i]) {
1046 kids[i]->FirePageHideNotification(aIsUnload);
1049 // Now make sure our editor, if any, is detached before we go
1050 // any farther.
1051 DetachEditorFromWindow();
1054 return NS_OK;
1058 // Bug 13871: Prevent frameset spoofing
1060 // This routine answers: 'Is origin's document from same domain as
1061 // target's document?'
1063 // file: uris are considered the same domain for the purpose of
1064 // frame navigation regardless of script accessibility (bug 420425)
1066 /* static */
1067 PRBool
1068 nsDocShell::ValidateOrigin(nsIDocShellTreeItem* aOriginTreeItem,
1069 nsIDocShellTreeItem* aTargetTreeItem)
1071 nsCOMPtr<nsIScriptSecurityManager> securityManager =
1072 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
1073 NS_ENSURE_TRUE(securityManager, PR_FALSE);
1075 nsCOMPtr<nsIPrincipal> subjectPrincipal;
1076 nsresult rv =
1077 securityManager->GetSubjectPrincipal(getter_AddRefs(subjectPrincipal));
1078 NS_ENSURE_SUCCESS(rv, PR_FALSE);
1080 if (subjectPrincipal) {
1081 // We're called from JS, check if UniversalBrowserWrite is
1082 // enabled.
1083 PRBool ubwEnabled = PR_FALSE;
1084 rv = securityManager->IsCapabilityEnabled("UniversalBrowserWrite",
1085 &ubwEnabled);
1086 NS_ENSURE_SUCCESS(rv, PR_FALSE);
1088 if (ubwEnabled) {
1089 return PR_TRUE;
1093 // Get origin document principal
1094 nsCOMPtr<nsIDOMDocument> originDOMDocument =
1095 do_GetInterface(aOriginTreeItem);
1096 nsCOMPtr<nsIDocument> originDocument(do_QueryInterface(originDOMDocument));
1097 NS_ENSURE_TRUE(originDocument, PR_FALSE);
1099 // Get target principal
1100 nsCOMPtr<nsIDOMDocument> targetDOMDocument =
1101 do_GetInterface(aTargetTreeItem);
1102 nsCOMPtr<nsIDocument> targetDocument(do_QueryInterface(targetDOMDocument));
1103 NS_ENSURE_TRUE(targetDocument, PR_FALSE);
1105 PRBool equal;
1106 rv = originDocument->NodePrincipal()->
1107 Equals(targetDocument->NodePrincipal(), &equal);
1108 if (NS_SUCCEEDED(rv) && equal) {
1109 return PR_TRUE;
1112 // Not strictly equal, special case if both are file: uris
1113 PRBool originIsFile = PR_FALSE;
1114 PRBool targetIsFile = PR_FALSE;
1115 nsCOMPtr<nsIURI> originURI;
1116 nsCOMPtr<nsIURI> targetURI;
1117 nsCOMPtr<nsIURI> innerOriginURI;
1118 nsCOMPtr<nsIURI> innerTargetURI;
1120 rv = originDocument->NodePrincipal()->GetURI(getter_AddRefs(originURI));
1121 if (NS_SUCCEEDED(rv) && originURI)
1122 innerOriginURI = NS_GetInnermostURI(originURI);
1124 rv = targetDocument->NodePrincipal()->GetURI(getter_AddRefs(targetURI));
1125 if (NS_SUCCEEDED(rv) && targetURI)
1126 innerTargetURI = NS_GetInnermostURI(targetURI);
1128 return innerOriginURI && innerTargetURI &&
1129 NS_SUCCEEDED(innerOriginURI->SchemeIs("file", &originIsFile)) &&
1130 NS_SUCCEEDED(innerTargetURI->SchemeIs("file", &targetIsFile)) &&
1131 originIsFile && targetIsFile;
1134 NS_IMETHODIMP
1135 nsDocShell::GetEldestPresContext(nsPresContext** aPresContext)
1137 nsresult rv = NS_OK;
1139 NS_ENSURE_ARG_POINTER(aPresContext);
1140 *aPresContext = nsnull;
1142 nsCOMPtr<nsIContentViewer> viewer = mContentViewer;
1143 while (viewer) {
1144 nsCOMPtr<nsIContentViewer> prevViewer;
1145 viewer->GetPreviousViewer(getter_AddRefs(prevViewer));
1146 if (prevViewer)
1147 viewer = prevViewer;
1148 else {
1149 nsCOMPtr<nsIDocumentViewer> docv(do_QueryInterface(viewer));
1150 if (docv)
1151 rv = docv->GetPresContext(aPresContext);
1152 break;
1156 return rv;
1159 NS_IMETHODIMP
1160 nsDocShell::GetPresContext(nsPresContext ** aPresContext)
1162 NS_ENSURE_ARG_POINTER(aPresContext);
1163 *aPresContext = nsnull;
1165 if (!mContentViewer)
1166 return NS_OK;
1168 nsCOMPtr<nsIDocumentViewer> docv(do_QueryInterface(mContentViewer));
1169 NS_ENSURE_TRUE(docv, NS_ERROR_NO_INTERFACE);
1171 return docv->GetPresContext(aPresContext);
1174 NS_IMETHODIMP
1175 nsDocShell::GetPresShell(nsIPresShell ** aPresShell)
1177 nsresult rv = NS_OK;
1179 NS_ENSURE_ARG_POINTER(aPresShell);
1180 *aPresShell = nsnull;
1182 nsCOMPtr<nsPresContext> presContext;
1183 (void) GetPresContext(getter_AddRefs(presContext));
1185 if (presContext) {
1186 NS_IF_ADDREF(*aPresShell = presContext->GetPresShell());
1189 return rv;
1192 NS_IMETHODIMP
1193 nsDocShell::GetEldestPresShell(nsIPresShell** aPresShell)
1195 nsresult rv = NS_OK;
1197 NS_ENSURE_ARG_POINTER(aPresShell);
1198 *aPresShell = nsnull;
1200 nsCOMPtr<nsPresContext> presContext;
1201 (void) GetEldestPresContext(getter_AddRefs(presContext));
1203 if (presContext) {
1204 NS_IF_ADDREF(*aPresShell = presContext->GetPresShell());
1207 return rv;
1210 NS_IMETHODIMP
1211 nsDocShell::GetContentViewer(nsIContentViewer ** aContentViewer)
1213 NS_ENSURE_ARG_POINTER(aContentViewer);
1215 *aContentViewer = mContentViewer;
1216 NS_IF_ADDREF(*aContentViewer);
1217 return NS_OK;
1220 NS_IMETHODIMP
1221 nsDocShell::SetChromeEventHandler(nsIDOMEventTarget* aChromeEventHandler)
1223 nsCOMPtr<nsPIDOMEventTarget> piTarget =
1224 do_QueryInterface(aChromeEventHandler);
1225 // Weak reference. Don't addref.
1226 mChromeEventHandler = piTarget;
1228 nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(mScriptGlobal));
1229 if (win) {
1230 win->SetChromeEventHandler(piTarget);
1233 return NS_OK;
1236 NS_IMETHODIMP
1237 nsDocShell::GetChromeEventHandler(nsIDOMEventTarget** aChromeEventHandler)
1239 NS_ENSURE_ARG_POINTER(aChromeEventHandler);
1240 nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(mChromeEventHandler);
1241 target.swap(*aChromeEventHandler);
1242 return NS_OK;
1245 /* [noscript] void setCurrentURI (in nsIURI uri); */
1246 NS_IMETHODIMP
1247 nsDocShell::SetCurrentURI(nsIURI *aURI)
1249 SetCurrentURI(aURI, nsnull, PR_TRUE);
1250 return NS_OK;
1253 PRBool
1254 nsDocShell::SetCurrentURI(nsIURI *aURI, nsIRequest *aRequest,
1255 PRBool aFireOnLocationChange)
1257 #ifdef PR_LOGGING
1258 if (gDocShellLeakLog && PR_LOG_TEST(gDocShellLeakLog, PR_LOG_DEBUG)) {
1259 nsCAutoString spec;
1260 if (aURI)
1261 aURI->GetSpec(spec);
1262 PR_LogPrint("DOCSHELL %p SetCurrentURI %s\n", this, spec.get());
1264 #endif
1266 // We don't want to send a location change when we're displaying an error
1267 // page, and we don't want to change our idea of "current URI" either
1268 if (mLoadType == LOAD_ERROR_PAGE) {
1269 return PR_FALSE;
1272 mCurrentURI = NS_TryToMakeImmutable(aURI);
1274 PRBool isRoot = PR_FALSE; // Is this the root docshell
1275 PRBool isSubFrame = PR_FALSE; // Is this a subframe navigation?
1277 nsCOMPtr<nsIDocShellTreeItem> root;
1279 GetSameTypeRootTreeItem(getter_AddRefs(root));
1280 if (root.get() == static_cast<nsIDocShellTreeItem *>(this))
1282 // This is the root docshell
1283 isRoot = PR_TRUE;
1285 if (mLSHE) {
1286 mLSHE->GetIsSubFrame(&isSubFrame);
1289 if (!isSubFrame && !isRoot) {
1291 * We don't want to send OnLocationChange notifications when
1292 * a subframe is being loaded for the first time, while
1293 * visiting a frameset page
1295 return PR_FALSE;
1298 if (aFireOnLocationChange) {
1299 FireOnLocationChange(this, aRequest, aURI);
1301 return !aFireOnLocationChange;
1304 NS_IMETHODIMP
1305 nsDocShell::GetCharset(char** aCharset)
1307 NS_ENSURE_ARG_POINTER(aCharset);
1308 *aCharset = nsnull;
1310 nsCOMPtr<nsIPresShell> presShell;
1311 GetPresShell(getter_AddRefs(presShell));
1312 NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
1313 nsIDocument *doc = presShell->GetDocument();
1314 NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
1315 *aCharset = ToNewCString(doc->GetDocumentCharacterSet());
1316 if (!*aCharset) {
1317 return NS_ERROR_OUT_OF_MEMORY;
1320 return NS_OK;
1323 NS_IMETHODIMP
1324 nsDocShell::SetCharset(const char* aCharset)
1326 // set the default charset
1327 nsCOMPtr<nsIContentViewer> viewer;
1328 GetContentViewer(getter_AddRefs(viewer));
1329 if (viewer) {
1330 nsCOMPtr<nsIMarkupDocumentViewer> muDV(do_QueryInterface(viewer));
1331 if (muDV) {
1332 NS_ENSURE_SUCCESS(muDV->SetDefaultCharacterSet(nsDependentCString(aCharset)),
1333 NS_ERROR_FAILURE);
1337 // set the charset override
1338 nsCOMPtr<nsIDocumentCharsetInfo> dcInfo;
1339 GetDocumentCharsetInfo(getter_AddRefs(dcInfo));
1340 if (dcInfo) {
1341 nsCOMPtr<nsIAtom> csAtom;
1342 csAtom = do_GetAtom(aCharset);
1343 dcInfo->SetForcedCharset(csAtom);
1346 return NS_OK;
1349 NS_IMETHODIMP
1350 nsDocShell::GetDocumentCharsetInfo(nsIDocumentCharsetInfo **
1351 aDocumentCharsetInfo)
1353 NS_ENSURE_ARG_POINTER(aDocumentCharsetInfo);
1355 // if the mDocumentCharsetInfo does not exist already, we create it now
1356 if (!mDocumentCharsetInfo) {
1357 mDocumentCharsetInfo = do_CreateInstance(NS_DOCUMENTCHARSETINFO_CONTRACTID);
1358 if (!mDocumentCharsetInfo)
1359 return NS_ERROR_FAILURE;
1362 *aDocumentCharsetInfo = mDocumentCharsetInfo;
1363 NS_IF_ADDREF(*aDocumentCharsetInfo);
1364 return NS_OK;
1367 NS_IMETHODIMP
1368 nsDocShell::SetDocumentCharsetInfo(nsIDocumentCharsetInfo *
1369 aDocumentCharsetInfo)
1371 mDocumentCharsetInfo = aDocumentCharsetInfo;
1372 return NS_OK;
1375 NS_IMETHODIMP
1376 nsDocShell::GetChannelIsUnsafe(PRBool *aUnsafe)
1378 *aUnsafe = PR_FALSE;
1380 nsCOMPtr<nsIChannel> channel;
1381 GetCurrentDocumentChannel(getter_AddRefs(channel));
1382 if (!channel) {
1383 return NS_OK;
1386 nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(channel);
1387 if (!jarChannel) {
1388 return NS_OK;
1391 return jarChannel->GetIsUnsafe(aUnsafe);
1394 NS_IMETHODIMP
1395 nsDocShell::GetAllowPlugins(PRBool * aAllowPlugins)
1397 NS_ENSURE_ARG_POINTER(aAllowPlugins);
1399 *aAllowPlugins = mAllowPlugins;
1400 if (!mAllowPlugins) {
1401 return NS_OK;
1404 PRBool unsafe;
1405 *aAllowPlugins = NS_SUCCEEDED(GetChannelIsUnsafe(&unsafe)) && !unsafe;
1406 return NS_OK;
1409 NS_IMETHODIMP
1410 nsDocShell::SetAllowPlugins(PRBool aAllowPlugins)
1412 mAllowPlugins = aAllowPlugins;
1413 //XXX should enable or disable a plugin host
1414 return NS_OK;
1417 NS_IMETHODIMP
1418 nsDocShell::GetAllowJavascript(PRBool * aAllowJavascript)
1420 NS_ENSURE_ARG_POINTER(aAllowJavascript);
1422 *aAllowJavascript = mAllowJavascript;
1423 if (!mAllowJavascript) {
1424 return NS_OK;
1427 PRBool unsafe;
1428 *aAllowJavascript = NS_SUCCEEDED(GetChannelIsUnsafe(&unsafe)) && !unsafe;
1429 return NS_OK;
1432 NS_IMETHODIMP
1433 nsDocShell::SetAllowJavascript(PRBool aAllowJavascript)
1435 mAllowJavascript = aAllowJavascript;
1436 return NS_OK;
1439 NS_IMETHODIMP nsDocShell::GetAllowMetaRedirects(PRBool * aReturn)
1441 NS_ENSURE_ARG_POINTER(aReturn);
1443 *aReturn = mAllowMetaRedirects;
1444 if (!mAllowMetaRedirects) {
1445 return NS_OK;
1448 PRBool unsafe;
1449 *aReturn = NS_SUCCEEDED(GetChannelIsUnsafe(&unsafe)) && !unsafe;
1450 return NS_OK;
1453 NS_IMETHODIMP nsDocShell::SetAllowMetaRedirects(PRBool aValue)
1455 mAllowMetaRedirects = aValue;
1456 return NS_OK;
1459 NS_IMETHODIMP nsDocShell::GetAllowSubframes(PRBool * aAllowSubframes)
1461 NS_ENSURE_ARG_POINTER(aAllowSubframes);
1463 *aAllowSubframes = mAllowSubframes;
1464 return NS_OK;
1467 NS_IMETHODIMP nsDocShell::SetAllowSubframes(PRBool aAllowSubframes)
1469 mAllowSubframes = aAllowSubframes;
1470 return NS_OK;
1473 NS_IMETHODIMP nsDocShell::GetAllowImages(PRBool * aAllowImages)
1475 NS_ENSURE_ARG_POINTER(aAllowImages);
1477 *aAllowImages = mAllowImages;
1478 return NS_OK;
1481 NS_IMETHODIMP nsDocShell::SetAllowImages(PRBool aAllowImages)
1483 mAllowImages = aAllowImages;
1484 return NS_OK;
1487 NS_IMETHODIMP
1488 nsDocShell::GetDocShellEnumerator(PRInt32 aItemType, PRInt32 aDirection, nsISimpleEnumerator **outEnum)
1490 NS_ENSURE_ARG_POINTER(outEnum);
1491 *outEnum = nsnull;
1493 nsRefPtr<nsDocShellEnumerator> docShellEnum;
1494 if (aDirection == ENUMERATE_FORWARDS)
1495 docShellEnum = new nsDocShellForwardsEnumerator;
1496 else
1497 docShellEnum = new nsDocShellBackwardsEnumerator;
1499 if (!docShellEnum) return NS_ERROR_OUT_OF_MEMORY;
1501 nsresult rv = docShellEnum->SetEnumDocShellType(aItemType);
1502 if (NS_FAILED(rv)) return rv;
1504 rv = docShellEnum->SetEnumerationRootItem((nsIDocShellTreeItem *)this);
1505 if (NS_FAILED(rv)) return rv;
1507 rv = docShellEnum->First();
1508 if (NS_FAILED(rv)) return rv;
1510 rv = docShellEnum->QueryInterface(NS_GET_IID(nsISimpleEnumerator), (void **)outEnum);
1512 return rv;
1515 NS_IMETHODIMP
1516 nsDocShell::GetAppType(PRUint32 * aAppType)
1518 *aAppType = mAppType;
1519 return NS_OK;
1522 NS_IMETHODIMP
1523 nsDocShell::SetAppType(PRUint32 aAppType)
1525 mAppType = aAppType;
1526 return NS_OK;
1530 NS_IMETHODIMP
1531 nsDocShell::GetAllowAuth(PRBool * aAllowAuth)
1533 *aAllowAuth = mAllowAuth;
1534 return NS_OK;
1537 NS_IMETHODIMP
1538 nsDocShell::SetAllowAuth(PRBool aAllowAuth)
1540 mAllowAuth = aAllowAuth;
1541 return NS_OK;
1544 NS_IMETHODIMP
1545 nsDocShell::GetZoom(float *zoom)
1547 NS_ENSURE_ARG_POINTER(zoom);
1548 *zoom = 1.0f;
1549 return NS_OK;
1552 NS_IMETHODIMP
1553 nsDocShell::SetZoom(float zoom)
1555 return NS_ERROR_NOT_IMPLEMENTED;
1558 NS_IMETHODIMP
1559 nsDocShell::GetMarginWidth(PRInt32 * aWidth)
1561 NS_ENSURE_ARG_POINTER(aWidth);
1563 *aWidth = mMarginWidth;
1564 return NS_OK;
1567 NS_IMETHODIMP
1568 nsDocShell::SetMarginWidth(PRInt32 aWidth)
1570 mMarginWidth = aWidth;
1571 return NS_OK;
1574 NS_IMETHODIMP
1575 nsDocShell::GetMarginHeight(PRInt32 * aHeight)
1577 NS_ENSURE_ARG_POINTER(aHeight);
1579 *aHeight = mMarginHeight;
1580 return NS_OK;
1583 NS_IMETHODIMP
1584 nsDocShell::SetMarginHeight(PRInt32 aHeight)
1586 mMarginHeight = aHeight;
1587 return NS_OK;
1590 NS_IMETHODIMP
1591 nsDocShell::GetBusyFlags(PRUint32 * aBusyFlags)
1593 NS_ENSURE_ARG_POINTER(aBusyFlags);
1595 *aBusyFlags = mBusyFlags;
1596 return NS_OK;
1599 NS_IMETHODIMP
1600 nsDocShell::TabToTreeOwner(PRBool aForward, PRBool* aTookFocus)
1602 NS_ENSURE_ARG_POINTER(aTookFocus);
1604 nsCOMPtr<nsIWebBrowserChromeFocus> chromeFocus = do_GetInterface(mTreeOwner);
1605 if (chromeFocus) {
1606 if (aForward)
1607 *aTookFocus = NS_SUCCEEDED(chromeFocus->FocusNextElement());
1608 else
1609 *aTookFocus = NS_SUCCEEDED(chromeFocus->FocusPrevElement());
1610 } else
1611 *aTookFocus = PR_FALSE;
1613 return NS_OK;
1616 NS_IMETHODIMP
1617 nsDocShell::GetSecurityUI(nsISecureBrowserUI **aSecurityUI)
1619 NS_IF_ADDREF(*aSecurityUI = mSecurityUI);
1620 return NS_OK;
1623 NS_IMETHODIMP
1624 nsDocShell::SetSecurityUI(nsISecureBrowserUI *aSecurityUI)
1626 mSecurityUI = aSecurityUI;
1627 return NS_OK;
1630 NS_IMETHODIMP
1631 nsDocShell::GetUseErrorPages(PRBool *aUseErrorPages)
1633 *aUseErrorPages = mUseErrorPages;
1634 return NS_OK;
1637 NS_IMETHODIMP
1638 nsDocShell::SetUseErrorPages(PRBool aUseErrorPages)
1640 // If mUseErrorPages is set explicitly, stop observing the pref.
1641 if (mObserveErrorPages) {
1642 nsCOMPtr<nsIPrefBranch2> prefs(do_QueryInterface(mPrefs));
1643 if (prefs) {
1644 prefs->RemoveObserver("browser.xul.error_pages.enabled", this);
1645 mObserveErrorPages = PR_FALSE;
1648 mUseErrorPages = aUseErrorPages;
1649 return NS_OK;
1652 NS_IMETHODIMP
1653 nsDocShell::GetPreviousTransIndex(PRInt32 *aPreviousTransIndex)
1655 *aPreviousTransIndex = mPreviousTransIndex;
1656 return NS_OK;
1659 NS_IMETHODIMP
1660 nsDocShell::GetLoadedTransIndex(PRInt32 *aLoadedTransIndex)
1662 *aLoadedTransIndex = mLoadedTransIndex;
1663 return NS_OK;
1666 NS_IMETHODIMP
1667 nsDocShell::HistoryPurged(PRInt32 aNumEntries)
1669 // These indices are used for fastback cache eviction, to determine
1670 // which session history entries are candidates for content viewer
1671 // eviction. We need to adjust by the number of entries that we
1672 // just purged from history, so that we look at the right session history
1673 // entries during eviction.
1674 mPreviousTransIndex = PR_MAX(-1, mPreviousTransIndex - aNumEntries);
1675 mLoadedTransIndex = PR_MAX(0, mLoadedTransIndex - aNumEntries);
1677 PRInt32 count = mChildList.Count();
1678 for (PRInt32 i = 0; i < count; ++i) {
1679 nsCOMPtr<nsIDocShell> shell = do_QueryInterface(ChildAt(i));
1680 if (shell) {
1681 shell->HistoryPurged(aNumEntries);
1685 return NS_OK;
1688 NS_IMETHODIMP
1689 nsDocShell::GetSessionStorageForPrincipal(nsIPrincipal* aPrincipal,
1690 PRBool aCreate,
1691 nsIDOMStorage** aStorage)
1693 NS_ENSURE_ARG_POINTER(aStorage);
1694 *aStorage = nsnull;
1696 if (!aPrincipal)
1697 return NS_OK;
1699 nsCOMPtr<nsIURI> codebaseURI;
1700 nsresult rv = aPrincipal->GetDomain(getter_AddRefs(codebaseURI));
1701 NS_ENSURE_SUCCESS(rv, rv);
1702 if (!codebaseURI) {
1703 rv = aPrincipal->GetURI(getter_AddRefs(codebaseURI));
1704 NS_ENSURE_SUCCESS(rv, rv);
1707 if (!codebaseURI)
1708 return NS_OK;
1710 rv = GetSessionStorageForURI(codebaseURI, aCreate, aStorage);
1711 NS_ENSURE_SUCCESS(rv, rv);
1713 nsCOMPtr<nsPIDOMStorage> piStorage = do_QueryInterface(*aStorage);
1714 if (piStorage) {
1715 PRBool canAccess = piStorage->CanAccess(aPrincipal);
1716 NS_ASSERTION(canAccess,
1717 "GetSessionStorageForPrincipal got a storage "
1718 "that could not be accessed!");
1719 if (!canAccess) {
1720 NS_RELEASE(*aStorage);
1721 return NS_ERROR_DOM_SECURITY_ERR;
1725 return NS_OK;
1728 NS_IMETHODIMP
1729 nsDocShell::GetSessionStorageForURI(nsIURI* aURI,
1730 nsIDOMStorage** aStorage)
1732 return GetSessionStorageForURI(aURI, PR_TRUE, aStorage);
1735 nsresult
1736 nsDocShell::GetSessionStorageForURI(nsIURI* aURI,
1737 PRBool aCreate,
1738 nsIDOMStorage** aStorage)
1740 NS_ENSURE_ARG(aURI);
1741 NS_ENSURE_ARG_POINTER(aStorage);
1743 *aStorage = nsnull;
1745 nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(aURI);
1746 NS_ASSERTION(innerURI, "Failed to get innermost URI");
1747 if (!innerURI)
1748 return NS_ERROR_FAILURE;
1750 nsCOMPtr<nsIDocShellTreeItem> topItem;
1751 nsresult rv = GetSameTypeRootTreeItem(getter_AddRefs(topItem));
1752 if (NS_FAILED(rv))
1753 return rv;
1755 if (!topItem)
1756 return NS_ERROR_FAILURE;
1758 nsDocShell* topDocShell = static_cast<nsDocShell*>(topItem.get());
1759 if (topDocShell != this)
1760 return topDocShell->GetSessionStorageForURI(aURI, aCreate, aStorage);
1762 nsCAutoString currentDomain;
1763 rv = innerURI->GetAsciiHost(currentDomain);
1764 NS_ENSURE_SUCCESS(rv, rv);
1766 if (currentDomain.IsEmpty())
1767 return NS_OK;
1769 if (!mStorages.Get(currentDomain, aStorage) && aCreate) {
1770 nsCOMPtr<nsIDOMStorage> newstorage =
1771 do_CreateInstance("@mozilla.org/dom/storage;1");
1772 if (!newstorage)
1773 return NS_ERROR_OUT_OF_MEMORY;
1775 nsCOMPtr<nsPIDOMStorage> pistorage = do_QueryInterface(newstorage);
1776 if (!pistorage)
1777 return NS_ERROR_FAILURE;
1778 pistorage->Init(NS_ConvertUTF8toUTF16(currentDomain), PR_FALSE);
1780 if (!mStorages.Put(currentDomain, newstorage))
1781 return NS_ERROR_OUT_OF_MEMORY;
1783 newstorage.swap(*aStorage);
1786 return NS_OK;
1789 nsresult
1790 nsDocShell::AddSessionStorage(const nsACString& aDomain,
1791 nsIDOMStorage* aStorage)
1793 NS_ENSURE_ARG_POINTER(aStorage);
1795 if (aDomain.IsEmpty())
1796 return NS_OK;
1798 nsCOMPtr<nsIDocShellTreeItem> topItem;
1799 nsresult rv = GetSameTypeRootTreeItem(getter_AddRefs(topItem));
1800 if (NS_FAILED(rv))
1801 return rv;
1803 if (topItem) {
1804 nsCOMPtr<nsIDocShell> topDocShell = do_QueryInterface(topItem);
1805 if (topDocShell == this) {
1806 // Do not replace an existing session storage.
1807 if (mStorages.GetWeak(aDomain))
1808 return NS_ERROR_NOT_AVAILABLE;
1810 if (!mStorages.Put(aDomain, aStorage))
1811 return NS_ERROR_OUT_OF_MEMORY;
1813 else {
1814 return topDocShell->AddSessionStorage(aDomain, aStorage);
1818 return NS_OK;
1821 NS_IMETHODIMP
1822 nsDocShell::GetCurrentDocumentChannel(nsIChannel** aResult)
1824 *aResult = nsnull;
1825 if (!mContentViewer)
1826 return NS_OK;
1828 nsCOMPtr<nsIDOMDocument> domDoc;
1829 nsresult rv = mContentViewer->GetDOMDocument(getter_AddRefs(domDoc));
1830 if (NS_FAILED(rv))
1831 return rv;
1833 nsCOMPtr<nsIDocument> doc(do_QueryInterface(domDoc));
1834 if (doc) {
1835 *aResult = doc->GetChannel();
1836 NS_IF_ADDREF(*aResult);
1839 return NS_OK;
1842 //*****************************************************************************
1843 // nsDocShell::nsIDocShellTreeItem
1844 //*****************************************************************************
1846 NS_IMETHODIMP
1847 nsDocShell::GetName(PRUnichar ** aName)
1849 NS_ENSURE_ARG_POINTER(aName);
1850 *aName = ToNewUnicode(mName);
1851 return NS_OK;
1854 NS_IMETHODIMP
1855 nsDocShell::SetName(const PRUnichar * aName)
1857 mName = aName; // this does a copy of aName
1858 return NS_OK;
1861 NS_IMETHODIMP
1862 nsDocShell::NameEquals(const PRUnichar *aName, PRBool *_retval)
1864 NS_ENSURE_ARG_POINTER(aName);
1865 NS_ENSURE_ARG_POINTER(_retval);
1866 *_retval = mName.Equals(aName);
1867 return NS_OK;
1870 NS_IMETHODIMP
1871 nsDocShell::GetItemType(PRInt32 * aItemType)
1873 NS_ENSURE_ARG_POINTER(aItemType);
1875 *aItemType = mItemType;
1876 return NS_OK;
1879 NS_IMETHODIMP
1880 nsDocShell::SetItemType(PRInt32 aItemType)
1882 NS_ENSURE_ARG((aItemType == typeChrome) || (typeContent == aItemType));
1884 // Only allow setting the type on root docshells. Those would be the ones
1885 // that have the docloader service as mParent or have no mParent at all.
1886 nsCOMPtr<nsIDocumentLoader> docLoaderService =
1887 do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID);
1888 NS_ENSURE_TRUE(docLoaderService, NS_ERROR_UNEXPECTED);
1890 NS_ENSURE_STATE(!mParent || mParent == docLoaderService);
1892 mItemType = aItemType;
1894 // disable auth prompting for anything but content
1895 mAllowAuth = mItemType == typeContent;
1897 return NS_OK;
1900 NS_IMETHODIMP
1901 nsDocShell::GetParent(nsIDocShellTreeItem ** aParent)
1903 if (!mParent) {
1904 *aParent = nsnull;
1905 } else {
1906 CallQueryInterface(mParent, aParent);
1908 // Note that in the case when the parent is not an nsIDocShellTreeItem we
1909 // don't want to throw; we just want to return null.
1910 return NS_OK;
1913 nsresult
1914 nsDocShell::SetDocLoaderParent(nsDocLoader * aParent)
1916 nsDocLoader::SetDocLoaderParent(aParent);
1918 // Curse ambiguous nsISupports inheritance!
1919 nsISupports* parent = GetAsSupports(aParent);
1921 // If parent is another docshell, we inherit all their flags for
1922 // allowing plugins, scripting etc.
1923 nsCOMPtr<nsIDocShell> parentAsDocShell(do_QueryInterface(parent));
1924 if (parentAsDocShell)
1926 PRBool value;
1927 if (NS_SUCCEEDED(parentAsDocShell->GetAllowPlugins(&value)))
1929 SetAllowPlugins(value);
1931 if (NS_SUCCEEDED(parentAsDocShell->GetAllowJavascript(&value)))
1933 SetAllowJavascript(value);
1935 if (NS_SUCCEEDED(parentAsDocShell->GetAllowMetaRedirects(&value)))
1937 SetAllowMetaRedirects(value);
1939 if (NS_SUCCEEDED(parentAsDocShell->GetAllowSubframes(&value)))
1941 SetAllowSubframes(value);
1943 if (NS_SUCCEEDED(parentAsDocShell->GetAllowImages(&value)))
1945 SetAllowImages(value);
1949 nsCOMPtr<nsIURIContentListener> parentURIListener(do_GetInterface(parent));
1950 if (parentURIListener)
1951 mContentListener->SetParentContentListener(parentURIListener);
1952 return NS_OK;
1955 NS_IMETHODIMP
1956 nsDocShell::GetSameTypeParent(nsIDocShellTreeItem ** aParent)
1958 NS_ENSURE_ARG_POINTER(aParent);
1959 *aParent = nsnull;
1961 nsCOMPtr<nsIDocShellTreeItem> parent =
1962 do_QueryInterface(GetAsSupports(mParent));
1963 if (!parent)
1964 return NS_OK;
1966 PRInt32 parentType;
1967 NS_ENSURE_SUCCESS(parent->GetItemType(&parentType), NS_ERROR_FAILURE);
1969 if (parentType == mItemType) {
1970 parent.swap(*aParent);
1972 return NS_OK;
1975 NS_IMETHODIMP
1976 nsDocShell::GetRootTreeItem(nsIDocShellTreeItem ** aRootTreeItem)
1978 NS_ENSURE_ARG_POINTER(aRootTreeItem);
1979 *aRootTreeItem = static_cast<nsIDocShellTreeItem *>(this);
1981 nsCOMPtr<nsIDocShellTreeItem> parent;
1982 NS_ENSURE_SUCCESS(GetParent(getter_AddRefs(parent)), NS_ERROR_FAILURE);
1983 while (parent) {
1984 *aRootTreeItem = parent;
1985 NS_ENSURE_SUCCESS((*aRootTreeItem)->GetParent(getter_AddRefs(parent)),
1986 NS_ERROR_FAILURE);
1988 NS_ADDREF(*aRootTreeItem);
1989 return NS_OK;
1992 NS_IMETHODIMP
1993 nsDocShell::GetSameTypeRootTreeItem(nsIDocShellTreeItem ** aRootTreeItem)
1995 NS_ENSURE_ARG_POINTER(aRootTreeItem);
1996 *aRootTreeItem = static_cast<nsIDocShellTreeItem *>(this);
1998 nsCOMPtr<nsIDocShellTreeItem> parent;
1999 NS_ENSURE_SUCCESS(GetSameTypeParent(getter_AddRefs(parent)),
2000 NS_ERROR_FAILURE);
2001 while (parent) {
2002 *aRootTreeItem = parent;
2003 NS_ENSURE_SUCCESS((*aRootTreeItem)->
2004 GetSameTypeParent(getter_AddRefs(parent)),
2005 NS_ERROR_FAILURE);
2007 NS_ADDREF(*aRootTreeItem);
2008 return NS_OK;
2011 /* static */
2012 PRBool
2013 nsDocShell::CanAccessItem(nsIDocShellTreeItem* aTargetItem,
2014 nsIDocShellTreeItem* aAccessingItem,
2015 PRBool aConsiderOpener)
2017 NS_PRECONDITION(aTargetItem, "Must have target item!");
2019 if (!gValidateOrigin || !aAccessingItem) {
2020 // Good to go
2021 return PR_TRUE;
2024 // XXXbz should we care if aAccessingItem or the document therein is
2025 // chrome? Should those get extra privileges?
2027 // For historical context, see:
2029 // Bug 13871: Prevent frameset spoofing
2030 // Bug 103638: Targets with same name in different windows open in wrong
2031 // window with javascript
2032 // Bug 408052: Adopt "ancestor" frame navigation policy
2034 // Now do a security check
2036 // Allow navigation if
2037 // 1) aAccessingItem can script aTargetItem or one of its ancestors in
2038 // the frame hierarchy or
2039 // 2) aTargetItem is a top-level frame and aAccessingItem is its descendant
2040 // 3) aTargetItem is a top-level frame and aAccessingItem can target
2041 // its opener per rule (1) or (2).
2043 if (aTargetItem == aAccessingItem) {
2044 // A frame is allowed to navigate itself.
2045 return PR_TRUE;
2048 nsCOMPtr<nsIDocShellTreeItem> accessingRoot;
2049 aAccessingItem->GetSameTypeRootTreeItem(getter_AddRefs(accessingRoot));
2051 if (aTargetItem == accessingRoot) {
2052 // A frame can navigate its root.
2053 return PR_TRUE;
2056 // Check if aAccessingItem can navigate one of aTargetItem's ancestors.
2057 nsCOMPtr<nsIDocShellTreeItem> target = aTargetItem;
2058 do {
2059 if (ValidateOrigin(aAccessingItem, target)) {
2060 return PR_TRUE;
2063 nsCOMPtr<nsIDocShellTreeItem> parent;
2064 target->GetSameTypeParent(getter_AddRefs(parent));
2065 parent.swap(target);
2066 } while (target);
2068 nsCOMPtr<nsIDocShellTreeItem> targetRoot;
2069 aTargetItem->GetSameTypeRootTreeItem(getter_AddRefs(targetRoot));
2071 if (aTargetItem != targetRoot) {
2072 // target is a subframe, not in accessor's frame hierarchy, and all its
2073 // ancestors have origins different from that of the accessor. Don't
2074 // allow access.
2075 return PR_FALSE;
2078 if (!aConsiderOpener) {
2079 // All done here
2080 return PR_FALSE;
2083 nsCOMPtr<nsIDOMWindow> targetWindow(do_GetInterface(aTargetItem));
2084 nsCOMPtr<nsIDOMWindowInternal> targetInternal(do_QueryInterface(targetWindow));
2085 if (!targetInternal) {
2086 NS_ERROR("This should not happen, really");
2087 return PR_FALSE;
2090 nsCOMPtr<nsIDOMWindowInternal> targetOpener;
2091 targetInternal->GetOpener(getter_AddRefs(targetOpener));
2092 nsCOMPtr<nsIWebNavigation> openerWebNav(do_GetInterface(targetOpener));
2093 nsCOMPtr<nsIDocShellTreeItem> openerItem(do_QueryInterface(openerWebNav));
2095 if (!openerItem) {
2096 return PR_FALSE;
2099 return CanAccessItem(openerItem, aAccessingItem, PR_FALSE);
2102 static PRBool
2103 ItemIsActive(nsIDocShellTreeItem *aItem)
2105 nsCOMPtr<nsIDOMWindow> tmp(do_GetInterface(aItem));
2106 nsCOMPtr<nsIDOMWindowInternal> window(do_QueryInterface(tmp));
2108 if (window) {
2109 PRBool isClosed;
2111 if (NS_SUCCEEDED(window->GetClosed(&isClosed)) && !isClosed) {
2112 return PR_TRUE;
2116 return PR_FALSE;
2119 NS_IMETHODIMP
2120 nsDocShell::FindItemWithName(const PRUnichar * aName,
2121 nsISupports * aRequestor,
2122 nsIDocShellTreeItem * aOriginalRequestor,
2123 nsIDocShellTreeItem ** _retval)
2125 NS_ENSURE_ARG(aName);
2126 NS_ENSURE_ARG_POINTER(_retval);
2128 // If we don't find one, we return NS_OK and a null result
2129 *_retval = nsnull;
2131 if (!*aName)
2132 return NS_OK;
2134 if (!aRequestor)
2136 nsCOMPtr<nsIDocShellTreeItem> foundItem;
2138 // This is the entry point into the target-finding algorithm. Check
2139 // for special names. This should only be done once, hence the check
2140 // for a null aRequestor.
2142 nsDependentString name(aName);
2143 if (name.LowerCaseEqualsLiteral("_self")) {
2144 foundItem = this;
2146 else if (name.LowerCaseEqualsLiteral("_blank"))
2148 // Just return null. Caller must handle creating a new window with
2149 // a blank name himself.
2150 return NS_OK;
2152 else if (name.LowerCaseEqualsLiteral("_parent"))
2154 GetSameTypeParent(getter_AddRefs(foundItem));
2155 if(!foundItem)
2156 foundItem = this;
2158 else if (name.LowerCaseEqualsLiteral("_top"))
2160 GetSameTypeRootTreeItem(getter_AddRefs(foundItem));
2161 NS_ASSERTION(foundItem, "Must have this; worst case it's us!");
2163 // _main is an IE target which should be case-insensitive but isn't
2164 // see bug 217886 for details
2165 else if (name.LowerCaseEqualsLiteral("_content") ||
2166 name.EqualsLiteral("_main"))
2168 // Must pass our same type root as requestor to the
2169 // treeowner to make sure things work right.
2170 nsCOMPtr<nsIDocShellTreeItem> root;
2171 GetSameTypeRootTreeItem(getter_AddRefs(root));
2172 if (mTreeOwner) {
2173 NS_ASSERTION(root, "Must have this; worst case it's us!");
2174 mTreeOwner->FindItemWithName(aName, root, aOriginalRequestor,
2175 getter_AddRefs(foundItem));
2177 #ifdef DEBUG
2178 else {
2179 NS_ERROR("Someone isn't setting up the tree owner. "
2180 "You might like to try that. "
2181 "Things will.....you know, work.");
2182 // Note: _content should always exist. If we don't have one
2183 // hanging off the treeowner, just create a named window....
2184 // so don't return here, in case we did that and can now find
2185 // it.
2186 // XXXbz should we be using |root| instead of creating
2187 // a new window?
2189 #endif
2192 if (foundItem && !CanAccessItem(foundItem, aOriginalRequestor)) {
2193 foundItem = nsnull;
2196 if (foundItem) {
2197 // We return foundItem here even if it's not an active
2198 // item since all the names we've dealt with so far are
2199 // special cases that we won't bother looking for further.
2201 foundItem.swap(*_retval);
2202 return NS_OK;
2206 // Keep looking
2208 // First we check our name.
2209 if (mName.Equals(aName) && ItemIsActive(this) &&
2210 CanAccessItem(this, aOriginalRequestor)) {
2211 NS_ADDREF(*_retval = this);
2212 return NS_OK;
2215 // This QI may fail, but the places where we want to compare, comparing
2216 // against nsnull serves the same purpose.
2217 nsCOMPtr<nsIDocShellTreeItem> reqAsTreeItem(do_QueryInterface(aRequestor));
2219 // Second we check our children making sure not to ask a child if
2220 // it is the aRequestor.
2221 #ifdef DEBUG
2222 nsresult rv =
2223 #endif
2224 FindChildWithName(aName, PR_TRUE, PR_TRUE, reqAsTreeItem,
2225 aOriginalRequestor, _retval);
2226 NS_ASSERTION(NS_SUCCEEDED(rv),
2227 "FindChildWithName should not be failing here.");
2228 if (*_retval)
2229 return NS_OK;
2231 // Third if we have a parent and it isn't the requestor then we
2232 // should ask it to do the search. If it is the requestor we
2233 // should just stop here and let the parent do the rest. If we
2234 // don't have a parent, then we should ask the
2235 // docShellTreeOwner to do the search.
2236 nsCOMPtr<nsIDocShellTreeItem> parentAsTreeItem =
2237 do_QueryInterface(GetAsSupports(mParent));
2238 if (parentAsTreeItem) {
2239 if (parentAsTreeItem == reqAsTreeItem)
2240 return NS_OK;
2242 PRInt32 parentType;
2243 parentAsTreeItem->GetItemType(&parentType);
2244 if (parentType == mItemType) {
2245 return parentAsTreeItem->
2246 FindItemWithName(aName,
2247 static_cast<nsIDocShellTreeItem*>
2248 (this),
2249 aOriginalRequestor,
2250 _retval);
2254 // If the parent is null or not of the same type fall through and ask tree
2255 // owner.
2257 // This may fail, but comparing against null serves the same purpose
2258 nsCOMPtr<nsIDocShellTreeOwner>
2259 reqAsTreeOwner(do_QueryInterface(aRequestor));
2261 if (mTreeOwner && mTreeOwner != reqAsTreeOwner) {
2262 return mTreeOwner->
2263 FindItemWithName(aName, this, aOriginalRequestor, _retval);
2266 return NS_OK;
2269 NS_IMETHODIMP
2270 nsDocShell::GetTreeOwner(nsIDocShellTreeOwner ** aTreeOwner)
2272 NS_ENSURE_ARG_POINTER(aTreeOwner);
2274 *aTreeOwner = mTreeOwner;
2275 NS_IF_ADDREF(*aTreeOwner);
2276 return NS_OK;
2279 #ifdef DEBUG_DOCSHELL_FOCUS
2280 static void
2281 PrintDocTree(nsIDocShellTreeItem * aParentNode, int aLevel)
2283 for (PRInt32 i=0;i<aLevel;i++) printf(" ");
2285 PRInt32 childWebshellCount;
2286 aParentNode->GetChildCount(&childWebshellCount);
2287 nsCOMPtr<nsIDocShell> parentAsDocShell(do_QueryInterface(aParentNode));
2288 PRInt32 type;
2289 aParentNode->GetItemType(&type);
2290 nsCOMPtr<nsIPresShell> presShell;
2291 parentAsDocShell->GetPresShell(getter_AddRefs(presShell));
2292 nsCOMPtr<nsPresContext> presContext;
2293 parentAsDocShell->GetPresContext(getter_AddRefs(presContext));
2294 nsIDocument *doc = presShell->GetDocument();
2296 nsCOMPtr<nsIDOMWindowInternal> domwin(doc->GetWindow());
2298 nsCOMPtr<nsIWidget> widget;
2299 nsIViewManager* vm = presShell->GetViewManager();
2300 if (vm) {
2301 vm->GetWidget(getter_AddRefs(widget));
2303 nsIContent* rootContent = doc->GetRootContent();
2305 printf("DS %p Ty %s Doc %p DW %p EM %p CN %p\n",
2306 (void*)parentAsDocShell.get(),
2307 type==nsIDocShellTreeItem::typeChrome?"Chr":"Con",
2308 (void*)doc, (void*)domwin.get(),
2309 (void*)presContext->EventStateManager(), (void*)rootContent);
2311 if (childWebshellCount > 0) {
2312 for (PRInt32 i=0;i<childWebshellCount;i++) {
2313 nsCOMPtr<nsIDocShellTreeItem> child;
2314 aParentNode->GetChildAt(i, getter_AddRefs(child));
2315 PrintDocTree(child, aLevel+1);
2320 static void
2321 PrintDocTree(nsIDocShellTreeItem * aParentNode)
2323 NS_ASSERTION(aParentNode, "Pointer is null!");
2325 nsCOMPtr<nsIDocShellTreeItem> parentItem;
2326 aParentNode->GetParent(getter_AddRefs(parentItem));
2327 while (parentItem) {
2328 nsCOMPtr<nsIDocShellTreeItem>tmp;
2329 parentItem->GetParent(getter_AddRefs(tmp));
2330 if (!tmp) {
2331 break;
2333 parentItem = tmp;
2336 if (!parentItem) {
2337 parentItem = aParentNode;
2340 PrintDocTree(parentItem, 0);
2342 #endif
2344 NS_IMETHODIMP
2345 nsDocShell::SetTreeOwner(nsIDocShellTreeOwner * aTreeOwner)
2347 #ifdef DEBUG_DOCSHELL_FOCUS
2348 nsCOMPtr<nsIDocShellTreeItem> item(do_QueryInterface(aTreeOwner));
2349 if (item) {
2350 PrintDocTree(item);
2352 #endif
2354 // Don't automatically set the progress based on the tree owner for frames
2355 if (!IsFrame()) {
2356 nsCOMPtr<nsIWebProgress> webProgress =
2357 do_QueryInterface(GetAsSupports(this));
2359 if (webProgress) {
2360 nsCOMPtr<nsIWebProgressListener>
2361 oldListener(do_QueryInterface(mTreeOwner));
2362 nsCOMPtr<nsIWebProgressListener>
2363 newListener(do_QueryInterface(aTreeOwner));
2365 if (oldListener) {
2366 webProgress->RemoveProgressListener(oldListener);
2369 if (newListener) {
2370 webProgress->AddProgressListener(newListener,
2371 nsIWebProgress::NOTIFY_ALL);
2376 mTreeOwner = aTreeOwner; // Weak reference per API
2378 PRInt32 i, n = mChildList.Count();
2379 for (i = 0; i < n; i++) {
2380 nsCOMPtr<nsIDocShellTreeItem> child = do_QueryInterface(ChildAt(i));
2381 NS_ENSURE_TRUE(child, NS_ERROR_FAILURE);
2382 PRInt32 childType = ~mItemType; // Set it to not us in case the get fails
2383 child->GetItemType(&childType); // We don't care if this fails, if it does we won't set the owner
2384 if (childType == mItemType)
2385 child->SetTreeOwner(aTreeOwner);
2388 return NS_OK;
2391 NS_IMETHODIMP
2392 nsDocShell::SetChildOffset(PRUint32 aChildOffset)
2394 mChildOffset = aChildOffset;
2395 return NS_OK;
2398 NS_IMETHODIMP
2399 nsDocShell::GetIsInUnload(PRBool* aIsInUnload)
2401 *aIsInUnload = mFiredUnloadEvent;
2402 return NS_OK;
2405 //*****************************************************************************
2406 // nsDocShell::nsIDocShellTreeNode
2407 //*****************************************************************************
2409 NS_IMETHODIMP
2410 nsDocShell::GetChildCount(PRInt32 * aChildCount)
2412 NS_ENSURE_ARG_POINTER(aChildCount);
2413 *aChildCount = mChildList.Count();
2414 return NS_OK;
2419 NS_IMETHODIMP
2420 nsDocShell::AddChild(nsIDocShellTreeItem * aChild)
2422 NS_ENSURE_ARG_POINTER(aChild);
2424 nsRefPtr<nsDocLoader> childAsDocLoader = GetAsDocLoader(aChild);
2425 NS_ENSURE_TRUE(childAsDocLoader, NS_ERROR_UNEXPECTED);
2427 // Make sure we're not creating a loop in the docshell tree
2428 nsDocLoader* ancestor = this;
2429 do {
2430 if (childAsDocLoader == ancestor) {
2431 return NS_ERROR_ILLEGAL_VALUE;
2433 ancestor = ancestor->GetParent();
2434 } while (ancestor);
2436 // Make sure to remove the child from its current parent.
2437 nsDocLoader* childsParent = childAsDocLoader->GetParent();
2438 if (childsParent) {
2439 childsParent->RemoveChildLoader(childAsDocLoader);
2442 // Make sure to clear the treeowner in case this child is a different type
2443 // from us.
2444 aChild->SetTreeOwner(nsnull);
2446 nsresult res = AddChildLoader(childAsDocLoader);
2447 NS_ENSURE_SUCCESS(res, res);
2448 NS_ASSERTION(mChildList.Count() > 0,
2449 "child list must not be empty after a successful add");
2451 // Set the child's index in the parent's children list
2452 // XXX What if the parent had different types of children?
2453 // XXX in that case docshell hierarchy and SH hierarchy won't match.
2455 nsCOMPtr<nsIDocShell> childDocShell = do_QueryInterface(aChild);
2456 if (childDocShell) {
2457 // If there are frameloaders in the finalization list, reduce
2458 // the offset so that the SH hierarchy is more likely to match the
2459 // docshell hierarchy
2460 nsCOMPtr<nsIDOMDocument> domDoc =
2461 do_GetInterface(GetAsSupports(this));
2462 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
2463 PRUint32 offset = mChildList.Count() - 1;
2464 if (doc) {
2465 PRUint32 oldChildCount = offset; // Current child count - 1
2466 for (PRUint32 i = 0; i < oldChildCount; ++i) {
2467 nsCOMPtr<nsIDocShell> child = do_QueryInterface(ChildAt(i));
2468 if (doc->FrameLoaderScheduledToBeFinalized(child)) {
2469 --offset;
2474 childDocShell->SetChildOffset(offset);
2478 /* Set the child's global history if the parent has one */
2479 if (mGlobalHistory) {
2480 nsCOMPtr<nsIDocShellHistory>
2481 dsHistoryChild(do_QueryInterface(aChild));
2482 if (dsHistoryChild)
2483 dsHistoryChild->SetUseGlobalHistory(PR_TRUE);
2487 PRInt32 childType = ~mItemType; // Set it to not us in case the get fails
2488 aChild->GetItemType(&childType);
2489 if (childType != mItemType)
2490 return NS_OK;
2491 // Everything below here is only done when the child is the same type.
2494 aChild->SetTreeOwner(mTreeOwner);
2496 nsCOMPtr<nsIDocShell> childAsDocShell(do_QueryInterface(aChild));
2497 if (!childAsDocShell)
2498 return NS_OK;
2500 // charset, style-disabling, and zoom will be inherited in SetupNewViewer()
2502 // Now take this document's charset and set the parentCharset field of the
2503 // child's DocumentCharsetInfo to it. We'll later use that field, in the
2504 // loading process, for the charset choosing algorithm.
2505 // If we fail, at any point, we just return NS_OK.
2506 // This code has some performance impact. But this will be reduced when
2507 // the current charset will finally be stored as an Atom, avoiding the
2508 // alias resolution extra look-up.
2510 // we are NOT going to propagate the charset is this Chrome's docshell
2511 if (mItemType == nsIDocShellTreeItem::typeChrome)
2512 return NS_OK;
2514 // get the child's docCSInfo object
2515 nsCOMPtr<nsIDocumentCharsetInfo> dcInfo = NULL;
2516 res = childAsDocShell->GetDocumentCharsetInfo(getter_AddRefs(dcInfo));
2517 if (NS_FAILED(res) || (!dcInfo))
2518 return NS_OK;
2520 // get the parent's current charset
2521 nsCOMPtr<nsIDocumentViewer> docv(do_QueryInterface(mContentViewer));
2522 if (!docv)
2523 return NS_OK;
2524 nsCOMPtr<nsIDocument> doc;
2525 res = docv->GetDocument(getter_AddRefs(doc));
2526 if (NS_FAILED(res) || (!doc))
2527 return NS_OK;
2528 const nsACString &parentCS = doc->GetDocumentCharacterSet();
2530 PRBool isWyciwyg = PR_FALSE;
2532 if (mCurrentURI) {
2533 // Check if the url is wyciwyg
2534 mCurrentURI->SchemeIs("wyciwyg", &isWyciwyg);
2537 if (!isWyciwyg) {
2538 // If this docshell is loaded from a wyciwyg: URI, don't
2539 // advertise our charset since it does not in any way reflect
2540 // the actual source charset, which is what we're trying to
2541 // expose here.
2543 // set the child's parentCharset
2544 nsCOMPtr<nsIAtom> parentCSAtom(do_GetAtom(parentCS));
2545 res = dcInfo->SetParentCharset(parentCSAtom);
2546 if (NS_FAILED(res))
2547 return NS_OK;
2549 PRInt32 charsetSource = doc->GetDocumentCharacterSetSource();
2551 // set the child's parentCharset
2552 res = dcInfo->SetParentCharsetSource(charsetSource);
2553 if (NS_FAILED(res))
2554 return NS_OK;
2557 // printf("### 1 >>> Adding child. Parent CS = %s. ItemType = %d.\n", NS_LossyConvertUTF16toASCII(parentCS).get(), mItemType);
2559 return NS_OK;
2562 NS_IMETHODIMP
2563 nsDocShell::RemoveChild(nsIDocShellTreeItem * aChild)
2565 NS_ENSURE_ARG_POINTER(aChild);
2567 nsRefPtr<nsDocLoader> childAsDocLoader = GetAsDocLoader(aChild);
2568 NS_ENSURE_TRUE(childAsDocLoader, NS_ERROR_UNEXPECTED);
2570 nsresult rv = RemoveChildLoader(childAsDocLoader);
2571 NS_ENSURE_SUCCESS(rv, rv);
2573 aChild->SetTreeOwner(nsnull);
2575 return nsDocLoader::AddDocLoaderAsChildOfRoot(childAsDocLoader);
2578 NS_IMETHODIMP
2579 nsDocShell::GetChildAt(PRInt32 aIndex, nsIDocShellTreeItem ** aChild)
2581 NS_ENSURE_ARG_POINTER(aChild);
2583 #ifdef DEBUG
2584 if (aIndex < 0) {
2585 NS_WARNING("Negative index passed to GetChildAt");
2587 else if (aIndex >= mChildList.Count()) {
2588 NS_WARNING("Too large an index passed to GetChildAt");
2590 #endif
2592 nsIDocumentLoader* child = SafeChildAt(aIndex);
2593 NS_ENSURE_TRUE(child, NS_ERROR_UNEXPECTED);
2595 return CallQueryInterface(child, aChild);
2598 NS_IMETHODIMP
2599 nsDocShell::FindChildWithName(const PRUnichar * aName,
2600 PRBool aRecurse, PRBool aSameType,
2601 nsIDocShellTreeItem * aRequestor,
2602 nsIDocShellTreeItem * aOriginalRequestor,
2603 nsIDocShellTreeItem ** _retval)
2605 NS_ENSURE_ARG(aName);
2606 NS_ENSURE_ARG_POINTER(_retval);
2608 *_retval = nsnull; // if we don't find one, we return NS_OK and a null result
2610 if (!*aName)
2611 return NS_OK;
2613 nsXPIDLString childName;
2614 PRInt32 i, n = mChildList.Count();
2615 for (i = 0; i < n; i++) {
2616 nsCOMPtr<nsIDocShellTreeItem> child = do_QueryInterface(ChildAt(i));
2617 NS_ENSURE_TRUE(child, NS_ERROR_FAILURE);
2618 PRInt32 childType;
2619 child->GetItemType(&childType);
2621 if (aSameType && (childType != mItemType))
2622 continue;
2624 PRBool childNameEquals = PR_FALSE;
2625 child->NameEquals(aName, &childNameEquals);
2626 if (childNameEquals && ItemIsActive(child) &&
2627 CanAccessItem(child, aOriginalRequestor)) {
2628 child.swap(*_retval);
2629 break;
2632 if (childType != mItemType) //Only ask it to check children if it is same type
2633 continue;
2635 if (aRecurse && (aRequestor != child)) // Only ask the child if it isn't the requestor
2637 // See if child contains the shell with the given name
2638 #ifdef DEBUG
2639 nsresult rv =
2640 #endif
2641 child->FindChildWithName(aName, PR_TRUE,
2642 aSameType,
2643 static_cast<nsIDocShellTreeItem*>
2644 (this),
2645 aOriginalRequestor,
2646 _retval);
2647 NS_ASSERTION(NS_SUCCEEDED(rv),
2648 "FindChildWithName should not fail here");
2649 if (*_retval) // found it
2650 return NS_OK;
2653 return NS_OK;
2656 //*****************************************************************************
2657 // nsDocShell::nsIDocShellHistory
2658 //*****************************************************************************
2659 NS_IMETHODIMP
2660 nsDocShell::GetChildSHEntry(PRInt32 aChildOffset, nsISHEntry ** aResult)
2662 nsresult rv = NS_OK;
2664 NS_ENSURE_ARG_POINTER(aResult);
2665 *aResult = nsnull;
2668 // A nsISHEntry for a child is *only* available when the parent is in
2669 // the progress of loading a document too...
2671 if (mLSHE) {
2672 /* Before looking for the subframe's url, check
2673 * the expiration status of the parent. If the parent
2674 * has expired from cache, then subframes will not be
2675 * loaded from history in certain situations.
2677 PRBool parentExpired=PR_FALSE;
2678 mLSHE->GetExpirationStatus(&parentExpired);
2680 /* Get the parent's Load Type so that it can be set on the child too.
2681 * By default give a loadHistory value
2683 PRUint32 loadType = nsIDocShellLoadInfo::loadHistory;
2684 mLSHE->GetLoadType(&loadType);
2685 // If the user did a shift-reload on this frameset page,
2686 // we don't want to load the subframes from history.
2687 if (loadType == nsIDocShellLoadInfo::loadReloadBypassCache ||
2688 loadType == nsIDocShellLoadInfo::loadReloadBypassProxy ||
2689 loadType == nsIDocShellLoadInfo::loadReloadBypassProxyAndCache ||
2690 loadType == nsIDocShellLoadInfo::loadRefresh)
2691 return rv;
2693 /* If the user pressed reload and the parent frame has expired
2694 * from cache, we do not want to load the child frame from history.
2696 if (parentExpired && (loadType == nsIDocShellLoadInfo::loadReloadNormal)) {
2697 // The parent has expired. Return null.
2698 *aResult = nsnull;
2699 return rv;
2702 nsCOMPtr<nsISHContainer> container(do_QueryInterface(mLSHE));
2703 if (container) {
2704 // Get the child subframe from session history.
2705 rv = container->GetChildAt(aChildOffset, aResult);
2706 if (*aResult)
2707 (*aResult)->SetLoadType(loadType);
2710 return rv;
2713 NS_IMETHODIMP
2714 nsDocShell::AddChildSHEntry(nsISHEntry * aCloneRef, nsISHEntry * aNewEntry,
2715 PRInt32 aChildOffset)
2717 nsresult rv;
2719 if (mLSHE) {
2720 /* You get here if you are currently building a
2721 * hierarchy ie.,you just visited a frameset page
2723 nsCOMPtr<nsISHContainer> container(do_QueryInterface(mLSHE, &rv));
2724 if (container) {
2725 rv = container->AddChild(aNewEntry, aChildOffset);
2728 else if (!aCloneRef) {
2729 /* This is an initial load in some subframe. Just append it if we can */
2730 nsCOMPtr<nsISHContainer> container(do_QueryInterface(mOSHE, &rv));
2731 if (container) {
2732 rv = container->AddChild(aNewEntry, aChildOffset);
2735 else if (mSessionHistory) {
2736 /* You are currently in the rootDocShell.
2737 * You will get here when a subframe has a new url
2738 * to load and you have walked up the tree all the
2739 * way to the top to clone the current SHEntry hierarchy
2740 * and replace the subframe where a new url was loaded with
2741 * a new entry.
2743 PRInt32 index = -1;
2744 nsCOMPtr<nsIHistoryEntry> currentHE;
2745 mSessionHistory->GetIndex(&index);
2746 if (index < 0)
2747 return NS_ERROR_FAILURE;
2749 rv = mSessionHistory->GetEntryAtIndex(index, PR_FALSE,
2750 getter_AddRefs(currentHE));
2751 NS_ENSURE_TRUE(currentHE, NS_ERROR_FAILURE);
2753 nsCOMPtr<nsISHEntry> currentEntry(do_QueryInterface(currentHE));
2754 if (currentEntry) {
2755 PRUint32 cloneID = 0;
2756 nsCOMPtr<nsISHEntry> nextEntry;
2757 aCloneRef->GetID(&cloneID);
2758 rv = CloneAndReplace(currentEntry, this, cloneID, aNewEntry,
2759 getter_AddRefs(nextEntry));
2761 if (NS_SUCCEEDED(rv)) {
2762 nsCOMPtr<nsISHistoryInternal>
2763 shPrivate(do_QueryInterface(mSessionHistory));
2764 NS_ENSURE_TRUE(shPrivate, NS_ERROR_FAILURE);
2765 rv = shPrivate->AddEntry(nextEntry, PR_TRUE);
2769 else {
2770 /* Just pass this along */
2771 nsCOMPtr<nsIDocShellHistory> parent =
2772 do_QueryInterface(GetAsSupports(mParent), &rv);
2773 if (parent) {
2774 rv = parent->AddChildSHEntry(aCloneRef, aNewEntry, aChildOffset);
2777 return rv;
2780 nsresult
2781 nsDocShell::DoAddChildSHEntry(nsISHEntry* aNewEntry, PRInt32 aChildOffset)
2783 /* You will get here when you are in a subframe and
2784 * a new url has been loaded on you.
2785 * The mOSHE in this subframe will be the previous url's
2786 * mOSHE. This mOSHE will be used as the identification
2787 * for this subframe in the CloneAndReplace function.
2790 // In this case, we will end up calling AddEntry, which increases the
2791 // current index by 1
2792 nsCOMPtr<nsISHistory> rootSH;
2793 GetRootSessionHistory(getter_AddRefs(rootSH));
2794 if (rootSH) {
2795 rootSH->GetIndex(&mPreviousTransIndex);
2798 nsresult rv;
2799 nsCOMPtr<nsIDocShellHistory> parent =
2800 do_QueryInterface(GetAsSupports(mParent), &rv);
2801 if (parent) {
2802 rv = parent->AddChildSHEntry(mOSHE, aNewEntry, aChildOffset);
2806 if (rootSH) {
2807 rootSH->GetIndex(&mLoadedTransIndex);
2808 #ifdef DEBUG_PAGE_CACHE
2809 printf("Previous index: %d, Loaded index: %d\n\n", mPreviousTransIndex,
2810 mLoadedTransIndex);
2811 #endif
2814 return rv;
2817 NS_IMETHODIMP
2818 nsDocShell::SetUseGlobalHistory(PRBool aUseGlobalHistory)
2820 nsresult rv;
2822 if (!aUseGlobalHistory) {
2823 mGlobalHistory = nsnull;
2824 return NS_OK;
2827 if (mGlobalHistory) {
2828 return NS_OK;
2831 mGlobalHistory = do_GetService(NS_GLOBALHISTORY2_CONTRACTID, &rv);
2832 return rv;
2835 NS_IMETHODIMP
2836 nsDocShell::GetUseGlobalHistory(PRBool *aUseGlobalHistory)
2838 *aUseGlobalHistory = (mGlobalHistory != nsnull);
2839 return NS_OK;
2842 //-------------------------------------
2843 //-- Helper Method for Print discovery
2844 //-------------------------------------
2845 PRBool
2846 nsDocShell::IsPrintingOrPP(PRBool aDisplayErrorDialog)
2848 if (mIsPrintingOrPP && aDisplayErrorDialog) {
2849 DisplayLoadError(NS_ERROR_DOCUMENT_IS_PRINTMODE, nsnull, nsnull);
2852 return mIsPrintingOrPP;
2855 PRBool
2856 nsDocShell::IsNavigationAllowed(PRBool aDisplayPrintErrorDialog)
2858 return !IsPrintingOrPP(aDisplayPrintErrorDialog) && !mFiredUnloadEvent;
2861 //*****************************************************************************
2862 // nsDocShell::nsIWebNavigation
2863 //*****************************************************************************
2865 NS_IMETHODIMP
2866 nsDocShell::GetCanGoBack(PRBool * aCanGoBack)
2868 if (!IsNavigationAllowed(PR_FALSE)) {
2869 *aCanGoBack = PR_FALSE;
2870 return NS_OK; // JS may not handle returning of an error code
2872 nsresult rv;
2873 nsCOMPtr<nsISHistory> rootSH;
2874 rv = GetRootSessionHistory(getter_AddRefs(rootSH));
2875 nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
2876 NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
2877 rv = webnav->GetCanGoBack(aCanGoBack);
2878 return rv;
2882 NS_IMETHODIMP
2883 nsDocShell::GetCanGoForward(PRBool * aCanGoForward)
2885 if (!IsNavigationAllowed(PR_FALSE)) {
2886 *aCanGoForward = PR_FALSE;
2887 return NS_OK; // JS may not handle returning of an error code
2889 nsresult rv;
2890 nsCOMPtr<nsISHistory> rootSH;
2891 rv = GetRootSessionHistory(getter_AddRefs(rootSH));
2892 nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
2893 NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
2894 rv = webnav->GetCanGoForward(aCanGoForward);
2895 return rv;
2899 NS_IMETHODIMP
2900 nsDocShell::GoBack()
2902 if (!IsNavigationAllowed()) {
2903 return NS_OK; // JS may not handle returning of an error code
2905 nsresult rv;
2906 nsCOMPtr<nsISHistory> rootSH;
2907 rv = GetRootSessionHistory(getter_AddRefs(rootSH));
2908 nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
2909 NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
2910 rv = webnav->GoBack();
2911 return rv;
2915 NS_IMETHODIMP
2916 nsDocShell::GoForward()
2918 if (!IsNavigationAllowed()) {
2919 return NS_OK; // JS may not handle returning of an error code
2921 nsresult rv;
2922 nsCOMPtr<nsISHistory> rootSH;
2923 rv = GetRootSessionHistory(getter_AddRefs(rootSH));
2924 nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
2925 NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
2926 rv = webnav->GoForward();
2927 return rv;
2931 NS_IMETHODIMP nsDocShell::GotoIndex(PRInt32 aIndex)
2933 if (!IsNavigationAllowed()) {
2934 return NS_OK; // JS may not handle returning of an error code
2936 nsresult rv;
2937 nsCOMPtr<nsISHistory> rootSH;
2938 rv = GetRootSessionHistory(getter_AddRefs(rootSH));
2939 nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
2940 NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
2941 rv = webnav->GotoIndex(aIndex);
2942 return rv;
2947 NS_IMETHODIMP
2948 nsDocShell::LoadURI(const PRUnichar * aURI,
2949 PRUint32 aLoadFlags,
2950 nsIURI * aReferringURI,
2951 nsIInputStream * aPostStream,
2952 nsIInputStream * aHeaderStream)
2954 NS_ASSERTION((aLoadFlags & 0xf) == 0, "Unexpected flags");
2956 if (!IsNavigationAllowed()) {
2957 return NS_OK; // JS may not handle returning of an error code
2959 nsCOMPtr<nsIURI> uri;
2960 nsresult rv = NS_OK;
2962 // Create a URI from our string; if that succeeds, we want to
2963 // change aLoadFlags to not include the ALLOW_THIRD_PARTY_FIXUP
2964 // flag.
2966 NS_ConvertUTF16toUTF8 uriString(aURI);
2967 // Cleanup the empty spaces that might be on each end.
2968 uriString.Trim(" ");
2969 // Eliminate embedded newlines, which single-line text fields now allow:
2970 uriString.StripChars("\r\n");
2971 NS_ENSURE_TRUE(!uriString.IsEmpty(), NS_ERROR_FAILURE);
2973 rv = NS_NewURI(getter_AddRefs(uri), uriString);
2974 if (uri) {
2975 aLoadFlags &= ~LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
2978 if (sURIFixup) {
2979 // Call the fixup object. This will clobber the rv from NS_NewURI
2980 // above, but that's fine with us. Note that we need to do this even
2981 // if NS_NewURI returned a URI, because fixup handles nested URIs, etc
2982 // (things like view-source:mozilla.org for example).
2983 PRUint32 fixupFlags = 0;
2984 if (aLoadFlags & LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) {
2985 fixupFlags |= nsIURIFixup::FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP;
2987 rv = sURIFixup->CreateFixupURI(uriString, fixupFlags,
2988 getter_AddRefs(uri));
2990 // else no fixup service so just use the URI we created and see
2991 // what happens
2993 if (NS_ERROR_MALFORMED_URI == rv) {
2994 DisplayLoadError(rv, uri, aURI);
2997 if (NS_FAILED(rv) || !uri)
2998 return NS_ERROR_FAILURE;
3000 PopupControlState popupState;
3001 if (aLoadFlags & LOAD_FLAGS_ALLOW_POPUPS) {
3002 popupState = openAllowed;
3003 aLoadFlags &= ~LOAD_FLAGS_ALLOW_POPUPS;
3004 } else {
3005 popupState = openOverridden;
3007 nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(mScriptGlobal));
3008 nsAutoPopupStatePusher statePusher(win, popupState);
3010 // Don't pass certain flags that aren't needed and end up confusing
3011 // ConvertLoadTypeToDocShellLoadInfo. We do need to ensure that they are
3012 // passed to LoadURI though, since it uses them.
3013 PRUint32 extraFlags = (aLoadFlags & EXTRA_LOAD_FLAGS);
3014 aLoadFlags &= ~EXTRA_LOAD_FLAGS;
3016 nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
3017 rv = CreateLoadInfo(getter_AddRefs(loadInfo));
3018 if (NS_FAILED(rv)) return rv;
3020 PRUint32 loadType = MAKE_LOAD_TYPE(LOAD_NORMAL, aLoadFlags);
3021 loadInfo->SetLoadType(ConvertLoadTypeToDocShellLoadInfo(loadType));
3022 loadInfo->SetPostDataStream(aPostStream);
3023 loadInfo->SetReferrer(aReferringURI);
3024 loadInfo->SetHeadersStream(aHeaderStream);
3026 rv = LoadURI(uri, loadInfo, extraFlags, PR_TRUE);
3028 return rv;
3031 NS_IMETHODIMP
3032 nsDocShell::DisplayLoadError(nsresult aError, nsIURI *aURI,
3033 const PRUnichar *aURL,
3034 nsIChannel* aFailedChannel)
3036 // Get prompt and string bundle servcies
3037 nsCOMPtr<nsIPrompt> prompter;
3038 nsCOMPtr<nsIStringBundle> stringBundle;
3039 GetPromptAndStringBundle(getter_AddRefs(prompter),
3040 getter_AddRefs(stringBundle));
3042 NS_ENSURE_TRUE(stringBundle, NS_ERROR_FAILURE);
3043 NS_ENSURE_TRUE(prompter, NS_ERROR_FAILURE);
3045 nsAutoString error;
3046 const PRUint32 kMaxFormatStrArgs = 3;
3047 nsAutoString formatStrs[kMaxFormatStrArgs];
3048 PRUint32 formatStrCount = 0;
3049 PRBool addHostPort = PR_FALSE;
3050 nsresult rv = NS_OK;
3051 nsAutoString messageStr;
3052 nsCAutoString cssClass;
3053 nsCAutoString errorPage;
3055 errorPage.AssignLiteral("neterror");
3057 // Turn the error code into a human readable error message.
3058 if (NS_ERROR_UNKNOWN_PROTOCOL == aError) {
3059 NS_ENSURE_ARG_POINTER(aURI);
3060 // extract the scheme
3061 nsCAutoString scheme;
3062 aURI->GetScheme(scheme);
3063 CopyASCIItoUTF16(scheme, formatStrs[0]);
3064 formatStrCount = 1;
3065 error.AssignLiteral("protocolNotFound");
3067 else if (NS_ERROR_FILE_NOT_FOUND == aError) {
3068 NS_ENSURE_ARG_POINTER(aURI);
3069 error.AssignLiteral("fileNotFound");
3071 else if (NS_ERROR_UNKNOWN_HOST == aError) {
3072 NS_ENSURE_ARG_POINTER(aURI);
3073 // Get the host
3074 nsCAutoString host;
3075 nsCOMPtr<nsIURI> innermostURI = NS_GetInnermostURI(aURI);
3076 innermostURI->GetHost(host);
3077 CopyUTF8toUTF16(host, formatStrs[0]);
3078 formatStrCount = 1;
3079 error.AssignLiteral("dnsNotFound");
3081 else if(NS_ERROR_CONNECTION_REFUSED == aError) {
3082 NS_ENSURE_ARG_POINTER(aURI);
3083 addHostPort = PR_TRUE;
3084 error.AssignLiteral("connectionFailure");
3086 else if(NS_ERROR_NET_INTERRUPT == aError) {
3087 NS_ENSURE_ARG_POINTER(aURI);
3088 addHostPort = PR_TRUE;
3089 error.AssignLiteral("netInterrupt");
3091 else if (NS_ERROR_NET_TIMEOUT == aError) {
3092 NS_ENSURE_ARG_POINTER(aURI);
3093 // Get the host
3094 nsCAutoString host;
3095 aURI->GetHost(host);
3096 CopyUTF8toUTF16(host, formatStrs[0]);
3097 formatStrCount = 1;
3098 error.AssignLiteral("netTimeout");
3100 else if (NS_ERROR_GET_MODULE(aError) == NS_ERROR_MODULE_SECURITY) {
3101 nsCOMPtr<nsINSSErrorsService> nsserr =
3102 do_GetService(NS_NSS_ERRORS_SERVICE_CONTRACTID);
3104 PRUint32 errorClass;
3105 if (!nsserr ||
3106 NS_FAILED(nsserr->GetErrorClass(aError, &errorClass))) {
3107 errorClass = nsINSSErrorsService::ERROR_CLASS_SSL_PROTOCOL;
3110 nsCOMPtr<nsISupports> securityInfo;
3111 nsCOMPtr<nsITransportSecurityInfo> tsi;
3112 if (aFailedChannel)
3113 aFailedChannel->GetSecurityInfo(getter_AddRefs(securityInfo));
3114 tsi = do_QueryInterface(securityInfo);
3115 if (tsi) {
3116 // Usually we should have aFailedChannel and get a detailed message
3117 tsi->GetErrorMessage(getter_Copies(messageStr));
3119 else {
3120 // No channel, let's obtain the generic error message
3121 if (nsserr) {
3122 nsserr->GetErrorMessage(aError, messageStr);
3125 if (!messageStr.IsEmpty()) {
3126 if (errorClass == nsINSSErrorsService::ERROR_CLASS_BAD_CERT) {
3127 error.AssignLiteral("nssBadCert");
3128 PRBool expert = PR_FALSE;
3129 mPrefs->GetBoolPref("browser.xul.error_pages.expert_bad_cert",
3130 &expert);
3131 if (expert) {
3132 cssClass.AssignLiteral("expertBadCert");
3135 // See if an alternate cert error page is registered
3136 nsXPIDLCString alternateErrorPage;
3137 mPrefs->GetCharPref("security.alternate_certificate_error_page",
3138 getter_Copies(alternateErrorPage));
3139 if (alternateErrorPage)
3140 errorPage.Assign(alternateErrorPage);
3141 } else {
3142 error.AssignLiteral("nssFailure2");
3145 } else if (NS_ERROR_PHISHING_URI == aError || NS_ERROR_MALWARE_URI == aError) {
3146 nsCAutoString host;
3147 aURI->GetHost(host);
3148 CopyUTF8toUTF16(host, formatStrs[0]);
3149 formatStrCount = 1;
3151 // Malware and phishing detectors may want to use an alternate error
3152 // page, but if the pref's not set, we'll fall back on the standard page
3153 nsXPIDLCString alternateErrorPage;
3154 mPrefs->GetCharPref("urlclassifier.alternate_error_page",
3155 getter_Copies(alternateErrorPage));
3156 if (alternateErrorPage)
3157 errorPage.Assign(alternateErrorPage);
3159 if (NS_ERROR_PHISHING_URI == aError)
3160 error.AssignLiteral("phishingBlocked");
3161 else
3162 error.AssignLiteral("malwareBlocked");
3163 cssClass.AssignLiteral("blacklist");
3165 else {
3166 // Errors requiring simple formatting
3167 switch (aError) {
3168 case NS_ERROR_MALFORMED_URI:
3169 // URI is malformed
3170 error.AssignLiteral("malformedURI");
3171 break;
3172 case NS_ERROR_REDIRECT_LOOP:
3173 // Doc failed to load because the server generated too many redirects
3174 error.AssignLiteral("redirectLoop");
3175 break;
3176 case NS_ERROR_UNKNOWN_SOCKET_TYPE:
3177 // Doc failed to load because PSM is not installed
3178 error.AssignLiteral("unknownSocketType");
3179 break;
3180 case NS_ERROR_NET_RESET:
3181 // Doc failed to load because the server kept reseting the connection
3182 // before we could read any data from it
3183 error.AssignLiteral("netReset");
3184 break;
3185 case NS_ERROR_DOCUMENT_NOT_CACHED:
3186 // Doc failed to load because we are offline and the cache does not
3187 // contain a copy of the document.
3188 error.AssignLiteral("netOffline");
3189 break;
3190 case NS_ERROR_DOCUMENT_IS_PRINTMODE:
3191 // Doc navigation attempted while Printing or Print Preview
3192 error.AssignLiteral("isprinting");
3193 break;
3194 case NS_ERROR_PORT_ACCESS_NOT_ALLOWED:
3195 // Port blocked for security reasons
3196 addHostPort = PR_TRUE;
3197 error.AssignLiteral("deniedPortAccess");
3198 break;
3199 case NS_ERROR_UNKNOWN_PROXY_HOST:
3200 // Proxy hostname could not be resolved.
3201 error.AssignLiteral("proxyResolveFailure");
3202 break;
3203 case NS_ERROR_PROXY_CONNECTION_REFUSED:
3204 // Proxy connection was refused.
3205 error.AssignLiteral("proxyConnectFailure");
3206 break;
3207 case NS_ERROR_INVALID_CONTENT_ENCODING:
3208 // Bad Content Encoding.
3209 error.AssignLiteral("contentEncodingError");
3210 break;
3211 case NS_ERROR_UNSAFE_CONTENT_TYPE:
3212 // Channel refused to load from an unrecognized content type.
3213 error.AssignLiteral("unsafeContentType");
3214 break;
3218 // Test if the error should be displayed
3219 if (error.IsEmpty()) {
3220 return NS_OK;
3223 // Test if the error needs to be formatted
3224 if (!messageStr.IsEmpty()) {
3225 // already obtained message
3227 else {
3228 if (addHostPort) {
3229 // Build up the host:port string.
3230 nsCAutoString hostport;
3231 if (aURI) {
3232 aURI->GetHostPort(hostport);
3233 } else {
3234 hostport.AssignLiteral("?");
3236 CopyUTF8toUTF16(hostport, formatStrs[formatStrCount++]);
3239 nsCAutoString spec;
3240 rv = NS_ERROR_NOT_AVAILABLE;
3241 if (aURI) {
3242 // displaying "file://" is aesthetically unpleasing and could even be
3243 // confusing to the user
3244 PRBool isFileURI = PR_FALSE;
3245 rv = aURI->SchemeIs("file", &isFileURI);
3246 if (NS_SUCCEEDED(rv) && isFileURI)
3247 aURI->GetPath(spec);
3248 else
3249 aURI->GetSpec(spec);
3251 nsCAutoString charset;
3252 // unescape and convert from origin charset
3253 aURI->GetOriginCharset(charset);
3254 nsCOMPtr<nsITextToSubURI> textToSubURI(
3255 do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv));
3256 if (NS_SUCCEEDED(rv)) {
3257 rv = textToSubURI->UnEscapeURIForUI(charset, spec, formatStrs[formatStrCount]);
3259 } else {
3260 spec.AssignLiteral("?");
3262 if (NS_FAILED(rv))
3263 CopyUTF8toUTF16(spec, formatStrs[formatStrCount]);
3264 rv = NS_OK;
3265 ++formatStrCount;
3267 const PRUnichar *strs[kMaxFormatStrArgs];
3268 for (PRUint32 i = 0; i < formatStrCount; i++) {
3269 strs[i] = formatStrs[i].get();
3271 nsXPIDLString str;
3272 rv = stringBundle->FormatStringFromName(
3273 error.get(),
3274 strs, formatStrCount, getter_Copies(str));
3275 NS_ENSURE_SUCCESS(rv, rv);
3276 messageStr.Assign(str.get());
3279 // Display the error as a page or an alert prompt
3280 NS_ENSURE_FALSE(messageStr.IsEmpty(), NS_ERROR_FAILURE);
3281 // Note: For now, display an alert instead of an error page if we have no
3282 // URI object. Missing URI objects are handled badly by session history.
3283 if (mUseErrorPages && aURI && aFailedChannel) {
3284 // Display an error page
3285 LoadErrorPage(aURI, aURL, errorPage.get(), error.get(),
3286 messageStr.get(), cssClass.get(), aFailedChannel);
3288 else
3290 // The prompter reqires that our private window has a document (or it
3291 // asserts). Satisfy that assertion now since GetDocument will force
3292 // creation of one if it hasn't already been created.
3293 nsCOMPtr<nsPIDOMWindow> pwin(do_QueryInterface(mScriptGlobal));
3294 if (pwin) {
3295 nsCOMPtr<nsIDOMDocument> doc;
3296 pwin->GetDocument(getter_AddRefs(doc));
3299 // Display a message box
3300 prompter->Alert(nsnull, messageStr.get());
3303 return NS_OK;
3307 NS_IMETHODIMP
3308 nsDocShell::LoadErrorPage(nsIURI *aURI, const PRUnichar *aURL,
3309 const char *aErrorPage,
3310 const PRUnichar *aErrorType,
3311 const PRUnichar *aDescription,
3312 const char *aCSSClass,
3313 nsIChannel* aFailedChannel)
3315 #if defined(PR_LOGGING) && defined(DEBUG)
3316 if (PR_LOG_TEST(gDocShellLog, PR_LOG_DEBUG)) {
3317 nsCAutoString spec;
3318 aURI->GetSpec(spec);
3320 nsCAutoString chanName;
3321 if (aFailedChannel)
3322 aFailedChannel->GetName(chanName);
3323 else
3324 chanName.AssignLiteral("<no channel>");
3326 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
3327 ("nsDocShell[%p]::LoadErrorPage(\"%s\", \"%s\", {...}, [%s])\n", this,
3328 spec.get(), NS_ConvertUTF16toUTF8(aURL).get(), chanName.get()));
3330 #endif
3331 // Create an shistory entry for the old load, if we have a channel
3332 if (aFailedChannel) {
3333 mURIResultedInDocument = PR_TRUE;
3334 OnLoadingSite(aFailedChannel, PR_TRUE, PR_FALSE);
3335 } else if (aURI) {
3336 mURIResultedInDocument = PR_TRUE;
3337 OnNewURI(aURI, nsnull, nsnull, mLoadType, PR_TRUE, PR_FALSE);
3339 // Be sure to have a correct mLSHE, it may have been cleared by
3340 // EndPageLoad. See bug 302115.
3341 if (mSessionHistory && !mLSHE) {
3342 PRInt32 idx;
3343 mSessionHistory->GetRequestedIndex(&idx);
3344 if (idx == -1)
3345 mSessionHistory->GetIndex(&idx);
3347 nsCOMPtr<nsIHistoryEntry> entry;
3348 mSessionHistory->GetEntryAtIndex(idx, PR_FALSE,
3349 getter_AddRefs(entry));
3350 mLSHE = do_QueryInterface(entry);
3353 nsCAutoString url;
3354 nsCAutoString charset;
3355 if (aURI)
3357 // Set our current URI
3358 SetCurrentURI(aURI);
3360 nsresult rv = aURI->GetSpec(url);
3361 rv |= aURI->GetOriginCharset(charset);
3362 NS_ENSURE_SUCCESS(rv, rv);
3364 else if (aURL)
3366 CopyUTF16toUTF8(aURL, url);
3368 else
3370 return NS_ERROR_INVALID_POINTER;
3373 // Create a URL to pass all the error information through to the page.
3375 char *escapedUrl = nsEscape(url.get(), url_Path);
3376 char *escapedCharset = nsEscape(charset.get(), url_Path);
3377 char *escapedError = nsEscape(NS_ConvertUTF16toUTF8(aErrorType).get(), url_Path);
3378 char *escapedDescription = nsEscape(NS_ConvertUTF16toUTF8(aDescription).get(), url_Path);
3379 char *escapedCSSClass = nsEscape(aCSSClass, url_Path);
3381 nsCString errorPageUrl("about:");
3382 errorPageUrl.AppendASCII(aErrorPage);
3383 errorPageUrl.AppendLiteral("?e=");
3385 errorPageUrl.AppendASCII(escapedError);
3386 errorPageUrl.AppendLiteral("&u=");
3387 errorPageUrl.AppendASCII(escapedUrl);
3388 if (escapedCSSClass && escapedCSSClass[0]) {
3389 errorPageUrl.AppendASCII("&s=");
3390 errorPageUrl.AppendASCII(escapedCSSClass);
3392 errorPageUrl.AppendLiteral("&c=");
3393 errorPageUrl.AppendASCII(escapedCharset);
3394 errorPageUrl.AppendLiteral("&d=");
3395 errorPageUrl.AppendASCII(escapedDescription);
3397 nsMemory::Free(escapedDescription);
3398 nsMemory::Free(escapedError);
3399 nsMemory::Free(escapedUrl);
3400 nsMemory::Free(escapedCharset);
3401 nsMemory::Free(escapedCSSClass);
3403 nsCOMPtr<nsIURI> errorPageURI;
3404 nsresult rv = NS_NewURI(getter_AddRefs(errorPageURI), errorPageUrl);
3405 NS_ENSURE_SUCCESS(rv, rv);
3407 return InternalLoad(errorPageURI, nsnull, nsnull,
3408 INTERNAL_LOAD_FLAGS_INHERIT_OWNER, nsnull, nsnull,
3409 nsnull, nsnull, LOAD_ERROR_PAGE,
3410 nsnull, PR_TRUE, nsnull, nsnull);
3414 NS_IMETHODIMP
3415 nsDocShell::Reload(PRUint32 aReloadFlags)
3417 if (!IsNavigationAllowed()) {
3418 return NS_OK; // JS may not handle returning of an error code
3420 nsresult rv;
3421 NS_ASSERTION(((aReloadFlags & 0xf) == 0),
3422 "Reload command not updated to use load flags!");
3423 NS_ASSERTION((aReloadFlags & EXTRA_LOAD_FLAGS) == 0,
3424 "Don't pass these flags to Reload");
3426 PRUint32 loadType = MAKE_LOAD_TYPE(LOAD_RELOAD_NORMAL, aReloadFlags);
3427 NS_ENSURE_TRUE(IsValidLoadType(loadType), NS_ERROR_INVALID_ARG);
3429 // Send notifications to the HistoryListener if any, about the impending reload
3430 nsCOMPtr<nsISHistory> rootSH;
3431 rv = GetRootSessionHistory(getter_AddRefs(rootSH));
3432 nsCOMPtr<nsISHistoryInternal> shistInt(do_QueryInterface(rootSH));
3433 PRBool canReload = PR_TRUE;
3434 if (rootSH) {
3435 nsCOMPtr<nsISHistoryListener> listener;
3436 shistInt->GetListener(getter_AddRefs(listener));
3437 if (listener) {
3438 listener->OnHistoryReload(mCurrentURI, aReloadFlags, &canReload);
3442 if (!canReload)
3443 return NS_OK;
3445 /* If you change this part of code, make sure bug 45297 does not re-occur */
3446 if (mOSHE) {
3447 rv = LoadHistoryEntry(mOSHE, loadType);
3449 else if (mLSHE) { // In case a reload happened before the current load is done
3450 rv = LoadHistoryEntry(mLSHE, loadType);
3452 else {
3453 nsCOMPtr<nsIDOMDocument> domDoc(do_GetInterface(GetAsSupports(this)));
3454 nsCOMPtr<nsIDocument> doc(do_QueryInterface(domDoc));
3456 nsIPrincipal* principal = nsnull;
3457 nsAutoString contentTypeHint;
3458 if (doc) {
3459 principal = doc->NodePrincipal();
3460 doc->GetContentType(contentTypeHint);
3463 rv = InternalLoad(mCurrentURI,
3464 mReferrerURI,
3465 principal,
3466 INTERNAL_LOAD_FLAGS_NONE, // Do not inherit owner from document
3467 nsnull, // No window target
3468 NS_LossyConvertUTF16toASCII(contentTypeHint).get(),
3469 nsnull, // No post data
3470 nsnull, // No headers data
3471 loadType, // Load type
3472 nsnull, // No SHEntry
3473 PR_TRUE,
3474 nsnull, // No nsIDocShell
3475 nsnull); // No nsIRequest
3479 return rv;
3482 NS_IMETHODIMP
3483 nsDocShell::Stop(PRUint32 aStopFlags)
3485 // Revoke any pending event related to content viewer restoration
3486 mRestorePresentationEvent.Revoke();
3488 if (nsIWebNavigation::STOP_CONTENT & aStopFlags) {
3489 // Stop the document loading
3490 if (mContentViewer)
3491 mContentViewer->Stop();
3494 if (nsIWebNavigation::STOP_NETWORK & aStopFlags) {
3495 // Suspend any timers that were set for this loader. We'll clear
3496 // them out for good in CreateContentViewer.
3497 if (mRefreshURIList) {
3498 SuspendRefreshURIs();
3499 mSavedRefreshURIList.swap(mRefreshURIList);
3500 mRefreshURIList = nsnull;
3503 if (mClassifier) {
3504 mClassifier->Cancel();
3505 mClassifier = nsnull;
3508 // XXXbz We could also pass |this| to nsIURILoader::Stop. That will
3509 // just call Stop() on us as an nsIDocumentLoader... We need fewer
3510 // redundant apis!
3511 Stop();
3514 PRInt32 n;
3515 PRInt32 count = mChildList.Count();
3516 for (n = 0; n < count; n++) {
3517 nsCOMPtr<nsIWebNavigation> shellAsNav(do_QueryInterface(ChildAt(n)));
3518 if (shellAsNav)
3519 shellAsNav->Stop(aStopFlags);
3522 return NS_OK;
3526 NS_IMETHODIMP nsDocShell::SetDocument(nsIDOMDocument* aDocument,
3527 const PRUnichar* aContentType)
3529 //XXX First Checkin
3530 NS_ERROR("Not Yet Implemented");
3531 return NS_ERROR_FAILURE;
3535 NS_IMETHODIMP
3536 nsDocShell::GetDocument(nsIDOMDocument ** aDocument)
3538 NS_ENSURE_ARG_POINTER(aDocument);
3539 NS_ENSURE_SUCCESS(EnsureContentViewer(), NS_ERROR_FAILURE);
3541 return mContentViewer->GetDOMDocument(aDocument);
3544 NS_IMETHODIMP
3545 nsDocShell::GetCurrentURI(nsIURI ** aURI)
3547 NS_ENSURE_ARG_POINTER(aURI);
3549 if (mCurrentURI) {
3550 return NS_EnsureSafeToReturn(mCurrentURI, aURI);
3553 *aURI = nsnull;
3554 return NS_OK;
3557 NS_IMETHODIMP
3558 nsDocShell::GetReferringURI(nsIURI ** aURI)
3560 NS_ENSURE_ARG_POINTER(aURI);
3562 *aURI = mReferrerURI;
3563 NS_IF_ADDREF(*aURI);
3565 return NS_OK;
3568 NS_IMETHODIMP
3569 nsDocShell::SetSessionHistory(nsISHistory * aSessionHistory)
3572 NS_ENSURE_TRUE(aSessionHistory, NS_ERROR_FAILURE);
3573 // make sure that we are the root docshell and
3574 // set a handle to root docshell in SH.
3576 nsCOMPtr<nsIDocShellTreeItem> root;
3577 /* Get the root docshell. If *this* is the root docshell
3578 * then save a handle to *this* in SH. SH needs it to do
3579 * traversions thro' its entries
3581 GetSameTypeRootTreeItem(getter_AddRefs(root));
3582 NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
3583 if (root.get() == static_cast<nsIDocShellTreeItem *>(this)) {
3584 mSessionHistory = aSessionHistory;
3585 nsCOMPtr<nsISHistoryInternal>
3586 shPrivate(do_QueryInterface(mSessionHistory));
3587 NS_ENSURE_TRUE(shPrivate, NS_ERROR_FAILURE);
3588 shPrivate->SetRootDocShell(this);
3589 return NS_OK;
3591 return NS_ERROR_FAILURE;
3596 NS_IMETHODIMP
3597 nsDocShell::GetSessionHistory(nsISHistory ** aSessionHistory)
3599 NS_ENSURE_ARG_POINTER(aSessionHistory);
3600 *aSessionHistory = mSessionHistory;
3601 NS_IF_ADDREF(*aSessionHistory);
3602 return NS_OK;
3605 //*****************************************************************************
3606 // nsDocShell::nsIWebPageDescriptor
3607 //*****************************************************************************
3608 NS_IMETHODIMP
3609 nsDocShell::LoadPage(nsISupports *aPageDescriptor, PRUint32 aDisplayType)
3611 nsCOMPtr<nsISHEntry> shEntryIn(do_QueryInterface(aPageDescriptor));
3613 // Currently, the opaque 'page descriptor' is an nsISHEntry...
3614 if (!shEntryIn) {
3615 return NS_ERROR_INVALID_POINTER;
3618 // Now clone shEntryIn, since we might end up modifying it later on, and we
3619 // want a page descriptor to be reusable.
3620 nsCOMPtr<nsISHEntry> shEntry;
3621 nsresult rv = shEntryIn->Clone(getter_AddRefs(shEntry));
3622 NS_ENSURE_SUCCESS(rv, rv);
3625 // load the page as view-source
3627 if (nsIWebPageDescriptor::DISPLAY_AS_SOURCE == aDisplayType) {
3628 nsCOMPtr<nsIURI> oldUri, newUri;
3629 nsCString spec, newSpec;
3631 // Create a new view-source URI and replace the original.
3632 rv = shEntry->GetURI(getter_AddRefs(oldUri));
3633 if (NS_FAILED(rv))
3634 return rv;
3636 oldUri->GetSpec(spec);
3637 newSpec.AppendLiteral("view-source:");
3638 newSpec.Append(spec);
3640 rv = NS_NewURI(getter_AddRefs(newUri), newSpec);
3641 if (NS_FAILED(rv)) {
3642 return rv;
3644 shEntry->SetURI(newUri);
3647 rv = LoadHistoryEntry(shEntry, LOAD_HISTORY);
3648 return rv;
3651 NS_IMETHODIMP
3652 nsDocShell::GetCurrentDescriptor(nsISupports **aPageDescriptor)
3654 NS_PRECONDITION(aPageDescriptor, "Null out param?");
3656 *aPageDescriptor = nsnull;
3658 nsISHEntry* src = mOSHE ? mOSHE : mLSHE;
3659 if (src) {
3660 nsCOMPtr<nsISHEntry> dest;
3662 nsresult rv = src->Clone(getter_AddRefs(dest));
3663 if (NS_FAILED(rv)) {
3664 return rv;
3667 // null out inappropriate cloned attributes...
3668 dest->SetParent(nsnull);
3669 dest->SetIsSubFrame(PR_FALSE);
3671 return CallQueryInterface(dest, aPageDescriptor);
3674 return NS_ERROR_NOT_AVAILABLE;
3678 //*****************************************************************************
3679 // nsDocShell::nsIBaseWindow
3680 //*****************************************************************************
3682 NS_IMETHODIMP
3683 nsDocShell::InitWindow(nativeWindow parentNativeWindow,
3684 nsIWidget * parentWidget, PRInt32 x, PRInt32 y,
3685 PRInt32 cx, PRInt32 cy)
3687 NS_ENSURE_ARG(parentWidget); // DocShells must get a widget for a parent
3689 SetParentWidget(parentWidget);
3690 SetPositionAndSize(x, y, cx, cy, PR_FALSE);
3692 return NS_OK;
3695 NS_IMETHODIMP
3696 nsDocShell::Create()
3698 NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome,
3699 "Unexpected item type in docshell");
3701 nsresult rv = NS_ERROR_FAILURE;
3702 mPrefs = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
3703 NS_ENSURE_SUCCESS(rv, rv);
3705 PRBool tmpbool;
3707 rv = mPrefs->GetBoolPref("browser.frames.enabled", &tmpbool);
3708 if (NS_SUCCEEDED(rv))
3709 mAllowSubframes = tmpbool;
3711 if (gValidateOrigin == (PRBool)0xffffffff) {
3712 // Check pref to see if we should prevent frameset spoofing
3713 rv = mPrefs->GetBoolPref("browser.frame.validate_origin", &tmpbool);
3714 if (NS_SUCCEEDED(rv)) {
3715 gValidateOrigin = tmpbool;
3716 } else {
3717 gValidateOrigin = PR_TRUE;
3721 // Should we use XUL error pages instead of alerts if possible?
3722 rv = mPrefs->GetBoolPref("browser.xul.error_pages.enabled", &tmpbool);
3723 if (NS_SUCCEEDED(rv))
3724 mUseErrorPages = tmpbool;
3726 nsCOMPtr<nsIPrefBranch2> prefs(do_QueryInterface(mPrefs, &rv));
3727 if (NS_SUCCEEDED(rv) && mObserveErrorPages) {
3728 prefs->AddObserver("browser.xul.error_pages.enabled", this, PR_FALSE);
3731 nsCOMPtr<nsIObserverService> serv = do_GetService(NS_OBSERVERSERVICE_CONTRACTID);
3732 if (serv) {
3733 const char* msg = mItemType == typeContent ?
3734 NS_WEBNAVIGATION_CREATE : NS_CHROME_WEBNAVIGATION_CREATE;
3735 serv->NotifyObservers(GetAsSupports(this), msg, nsnull);
3738 return NS_OK;
3741 NS_IMETHODIMP
3742 nsDocShell::Destroy()
3744 NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome,
3745 "Unexpected item type in docshell");
3747 if (!mIsBeingDestroyed) {
3748 nsCOMPtr<nsIObserverService> serv =
3749 do_GetService(NS_OBSERVERSERVICE_CONTRACTID);
3750 if (serv) {
3751 const char* msg = mItemType == typeContent ?
3752 NS_WEBNAVIGATION_DESTROY : NS_CHROME_WEBNAVIGATION_DESTROY;
3753 serv->NotifyObservers(GetAsSupports(this), msg, nsnull);
3757 mIsBeingDestroyed = PR_TRUE;
3759 // Remove our pref observers
3760 if (mObserveErrorPages) {
3761 nsCOMPtr<nsIPrefBranch2> prefs(do_QueryInterface(mPrefs));
3762 if (prefs) {
3763 prefs->RemoveObserver("browser.xul.error_pages.enabled", this);
3764 mObserveErrorPages = PR_FALSE;
3768 // Make sure to blow away our mLoadingURI just in case. No loads
3769 // from inside this pagehide.
3770 mLoadingURI = nsnull;
3772 // Fire unload event before we blow anything away.
3773 (void) FirePageHideNotification(PR_TRUE);
3775 // Clear pointers to any detached nsEditorData that's lying
3776 // around in shistory entries. Breaks cycle. See bug 430921.
3777 if (mOSHE)
3778 mOSHE->SetEditorData(nsnull);
3779 if (mLSHE)
3780 mLSHE->SetEditorData(nsnull);
3782 // Note: mContentListener can be null if Init() failed and we're being
3783 // called from the destructor.
3784 if (mContentListener) {
3785 mContentListener->DropDocShellreference();
3786 mContentListener->SetParentContentListener(nsnull);
3787 // Note that we do NOT set mContentListener to null here; that
3788 // way if someone tries to do a load in us after this point
3789 // the nsDSURIContentListener will block it. All of which
3790 // means that we should do this before calling Stop(), of
3791 // course.
3794 // Stop any URLs that are currently being loaded...
3795 Stop(nsIWebNavigation::STOP_ALL);
3797 mEditorData = nsnull;
3799 mTransferableHookData = nsnull;
3801 // Save the state of the current document, before destroying the window.
3802 // This is needed to capture the state of a frameset when the new document
3803 // causes the frameset to be destroyed...
3804 PersistLayoutHistoryState();
3806 // Remove this docshell from its parent's child list
3807 nsCOMPtr<nsIDocShellTreeItem> docShellParentAsItem =
3808 do_QueryInterface(GetAsSupports(mParent));
3809 if (docShellParentAsItem)
3810 docShellParentAsItem->RemoveChild(this);
3812 nsCOMPtr<nsIFocusEventSuppressorService> suppressor;
3813 if (mContentViewer) {
3814 suppressor =
3815 do_GetService(NS_NSIFOCUSEVENTSUPPRESSORSERVICE_CONTRACTID);
3816 NS_ENSURE_STATE(suppressor);
3817 suppressor->Suppress();
3818 mContentViewer->Close(nsnull);
3819 mContentViewer->Destroy();
3820 mContentViewer = nsnull;
3823 nsDocLoader::Destroy();
3825 mParentWidget = nsnull;
3826 mCurrentURI = nsnull;
3828 if (mScriptGlobal) {
3829 nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(mScriptGlobal));
3830 win->SetDocShell(nsnull);
3832 mScriptGlobal = nsnull;
3835 mSessionHistory = nsnull;
3836 SetTreeOwner(nsnull);
3838 // required to break ref cycle
3839 mSecurityUI = nsnull;
3841 // Cancel any timers that were set for this docshell; this is needed
3842 // to break the cycle between us and the timers.
3843 CancelRefreshURITimers();
3844 if (suppressor) {
3845 suppressor->Unsuppress();
3847 return NS_OK;
3850 NS_IMETHODIMP
3851 nsDocShell::SetPosition(PRInt32 x, PRInt32 y)
3853 mBounds.x = x;
3854 mBounds.y = y;
3856 if (mContentViewer)
3857 NS_ENSURE_SUCCESS(mContentViewer->Move(x, y), NS_ERROR_FAILURE);
3859 return NS_OK;
3862 NS_IMETHODIMP
3863 nsDocShell::GetPosition(PRInt32 * aX, PRInt32 * aY)
3865 PRInt32 dummyHolder;
3866 return GetPositionAndSize(aX, aY, &dummyHolder, &dummyHolder);
3869 NS_IMETHODIMP
3870 nsDocShell::SetSize(PRInt32 aCX, PRInt32 aCY, PRBool aRepaint)
3872 PRInt32 x = 0, y = 0;
3873 GetPosition(&x, &y);
3874 return SetPositionAndSize(x, y, aCX, aCY, aRepaint);
3877 NS_IMETHODIMP
3878 nsDocShell::GetSize(PRInt32 * aCX, PRInt32 * aCY)
3880 PRInt32 dummyHolder;
3881 return GetPositionAndSize(&dummyHolder, &dummyHolder, aCX, aCY);
3884 NS_IMETHODIMP
3885 nsDocShell::SetPositionAndSize(PRInt32 x, PRInt32 y, PRInt32 cx,
3886 PRInt32 cy, PRBool fRepaint)
3888 mBounds.x = x;
3889 mBounds.y = y;
3890 mBounds.width = cx;
3891 mBounds.height = cy;
3893 // Hold strong ref, since SetBounds can make us null out mContentViewer
3894 nsCOMPtr<nsIContentViewer> viewer = mContentViewer;
3895 if (viewer) {
3896 //XXX Border figured in here or is that handled elsewhere?
3897 NS_ENSURE_SUCCESS(viewer->SetBounds(mBounds), NS_ERROR_FAILURE);
3900 return NS_OK;
3903 NS_IMETHODIMP
3904 nsDocShell::GetPositionAndSize(PRInt32 * x, PRInt32 * y, PRInt32 * cx,
3905 PRInt32 * cy)
3907 // We should really consider just getting this information from
3908 // our window instead of duplicating the storage and code...
3909 nsCOMPtr<nsIDOMDocument> document(do_GetInterface(GetAsSupports(mParent)));
3910 nsCOMPtr<nsIDocument> doc(do_QueryInterface(document));
3911 if (doc) {
3912 doc->FlushPendingNotifications(Flush_Layout);
3915 DoGetPositionAndSize(x, y, cx, cy);
3916 return NS_OK;
3919 void
3920 nsDocShell::DoGetPositionAndSize(PRInt32 * x, PRInt32 * y, PRInt32 * cx,
3921 PRInt32 * cy)
3923 if (x)
3924 *x = mBounds.x;
3925 if (y)
3926 *y = mBounds.y;
3927 if (cx)
3928 *cx = mBounds.width;
3929 if (cy)
3930 *cy = mBounds.height;
3933 NS_IMETHODIMP
3934 nsDocShell::Repaint(PRBool aForce)
3936 nsCOMPtr<nsPresContext> context;
3937 GetPresContext(getter_AddRefs(context));
3938 NS_ENSURE_TRUE(context, NS_ERROR_FAILURE);
3940 nsIViewManager* viewManager = context->GetViewManager();
3941 NS_ENSURE_TRUE(viewManager, NS_ERROR_FAILURE);
3943 // what about aForce ?
3944 NS_ENSURE_SUCCESS(viewManager->UpdateAllViews(0), NS_ERROR_FAILURE);
3945 return NS_OK;
3948 NS_IMETHODIMP
3949 nsDocShell::GetParentWidget(nsIWidget ** parentWidget)
3951 NS_ENSURE_ARG_POINTER(parentWidget);
3953 *parentWidget = mParentWidget;
3954 NS_IF_ADDREF(*parentWidget);
3956 return NS_OK;
3959 NS_IMETHODIMP
3960 nsDocShell::SetParentWidget(nsIWidget * aParentWidget)
3962 mParentWidget = aParentWidget;
3964 return NS_OK;
3967 NS_IMETHODIMP
3968 nsDocShell::GetParentNativeWindow(nativeWindow * parentNativeWindow)
3970 NS_ENSURE_ARG_POINTER(parentNativeWindow);
3972 if (mParentWidget)
3973 *parentNativeWindow = mParentWidget->GetNativeData(NS_NATIVE_WIDGET);
3974 else
3975 *parentNativeWindow = nsnull;
3977 return NS_OK;
3980 NS_IMETHODIMP
3981 nsDocShell::SetParentNativeWindow(nativeWindow parentNativeWindow)
3983 return NS_ERROR_NOT_IMPLEMENTED;
3986 NS_IMETHODIMP
3987 nsDocShell::GetVisibility(PRBool * aVisibility)
3989 NS_ENSURE_ARG_POINTER(aVisibility);
3991 *aVisibility = PR_FALSE;
3993 if (!mContentViewer)
3994 return NS_OK;
3996 nsCOMPtr<nsIPresShell> presShell;
3997 GetPresShell(getter_AddRefs(presShell));
3998 if (!presShell)
3999 return NS_OK;
4001 // get the view manager
4002 nsIViewManager* vm = presShell->GetViewManager();
4003 NS_ENSURE_TRUE(vm, NS_ERROR_FAILURE);
4005 // get the root view
4006 nsIView *view = nsnull; // views are not ref counted
4007 NS_ENSURE_SUCCESS(vm->GetRootView(view), NS_ERROR_FAILURE);
4008 NS_ENSURE_TRUE(view, NS_ERROR_FAILURE);
4010 // if our root view is hidden, we are not visible
4011 if (view->GetVisibility() == nsViewVisibility_kHide)
4012 return NS_OK;
4014 // otherwise, we must walk up the document and view trees checking
4015 // for a hidden view, unless we're an off screen browser, which
4016 // would make this test meaningless.
4018 nsCOMPtr<nsIDocShellTreeItem> treeItem = this;
4019 nsCOMPtr<nsIDocShellTreeItem> parentItem;
4020 treeItem->GetParent(getter_AddRefs(parentItem));
4021 while (parentItem) {
4022 nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(treeItem));
4023 docShell->GetPresShell(getter_AddRefs(presShell));
4025 nsCOMPtr<nsIDocShell> parentDS = do_QueryInterface(parentItem);
4026 nsCOMPtr<nsIPresShell> pPresShell;
4027 parentDS->GetPresShell(getter_AddRefs(pPresShell));
4029 // Null-check for crash in bug 267804
4030 if (!pPresShell) {
4031 NS_NOTREACHED("parent docshell has null pres shell");
4032 return NS_OK;
4035 nsIContent *shellContent =
4036 pPresShell->GetDocument()->FindContentForSubDocument(presShell->GetDocument());
4037 NS_ASSERTION(shellContent, "subshell not in the map");
4039 nsIFrame* frame = pPresShell->GetPrimaryFrameFor(shellContent);
4040 PRBool isDocShellOffScreen = PR_FALSE;
4041 docShell->GetIsOffScreenBrowser(&isDocShellOffScreen);
4042 if (frame && !frame->AreAncestorViewsVisible() && !isDocShellOffScreen)
4043 return NS_OK;
4045 treeItem = parentItem;
4046 treeItem->GetParent(getter_AddRefs(parentItem));
4049 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_QueryInterface(mTreeOwner));
4050 if (!treeOwnerAsWin) {
4051 *aVisibility = PR_TRUE;
4052 return NS_OK;
4055 // Check with the tree owner as well to give embedders a chance to
4056 // expose visibility as well.
4057 return treeOwnerAsWin->GetVisibility(aVisibility);
4060 NS_IMETHODIMP
4061 nsDocShell::SetIsOffScreenBrowser(PRBool aIsOffScreen)
4063 mIsOffScreenBrowser = aIsOffScreen;
4064 return NS_OK;
4067 NS_IMETHODIMP
4068 nsDocShell::GetIsOffScreenBrowser(PRBool *aIsOffScreen)
4070 *aIsOffScreen = mIsOffScreenBrowser;
4071 return NS_OK;
4074 NS_IMETHODIMP
4075 nsDocShell::SetVisibility(PRBool aVisibility)
4077 if (!mContentViewer)
4078 return NS_OK;
4079 if (aVisibility) {
4080 mContentViewer->Show();
4082 else {
4083 mContentViewer->Hide();
4086 return NS_OK;
4089 NS_IMETHODIMP
4090 nsDocShell::GetEnabled(PRBool *aEnabled)
4092 NS_ENSURE_ARG_POINTER(aEnabled);
4093 *aEnabled = PR_TRUE;
4094 return NS_ERROR_NOT_IMPLEMENTED;
4097 NS_IMETHODIMP
4098 nsDocShell::SetEnabled(PRBool aEnabled)
4100 return NS_ERROR_NOT_IMPLEMENTED;
4103 NS_IMETHODIMP
4104 nsDocShell::GetBlurSuppression(PRBool *aBlurSuppression)
4106 NS_ENSURE_ARG_POINTER(aBlurSuppression);
4107 *aBlurSuppression = PR_FALSE;
4108 return NS_ERROR_NOT_IMPLEMENTED;
4111 NS_IMETHODIMP
4112 nsDocShell::SetBlurSuppression(PRBool aBlurSuppression)
4114 return NS_ERROR_NOT_IMPLEMENTED;
4117 NS_IMETHODIMP
4118 nsDocShell::GetMainWidget(nsIWidget ** aMainWidget)
4120 // We don't create our own widget, so simply return the parent one.
4121 return GetParentWidget(aMainWidget);
4124 NS_IMETHODIMP
4125 nsDocShell::SetFocus()
4127 #ifdef DEBUG_DOCSHELL_FOCUS
4128 printf("nsDocShell::SetFocus %p\n", (void*)this);
4129 #endif
4131 // Tell itself (and the DocShellFocusController) who has focus
4132 // this way focus gets removed from the currently focused DocShell
4134 SetHasFocus(PR_TRUE);
4136 return NS_OK;
4139 NS_IMETHODIMP
4140 nsDocShell::GetTitle(PRUnichar ** aTitle)
4142 NS_ENSURE_ARG_POINTER(aTitle);
4144 *aTitle = ToNewUnicode(mTitle);
4145 return NS_OK;
4148 NS_IMETHODIMP
4149 nsDocShell::SetTitle(const PRUnichar * aTitle)
4151 // Store local title
4152 mTitle = aTitle;
4154 nsCOMPtr<nsIDocShellTreeItem> parent;
4155 GetSameTypeParent(getter_AddRefs(parent));
4157 // When title is set on the top object it should then be passed to the
4158 // tree owner.
4159 if (!parent) {
4160 nsCOMPtr<nsIBaseWindow>
4161 treeOwnerAsWin(do_QueryInterface(mTreeOwner));
4162 if (treeOwnerAsWin)
4163 treeOwnerAsWin->SetTitle(aTitle);
4166 if (mGlobalHistory && mCurrentURI && mLoadType != LOAD_ERROR_PAGE) {
4167 mGlobalHistory->SetPageTitle(mCurrentURI, nsDependentString(aTitle));
4171 // Update SessionHistory with the document's title. If the
4172 // page was loaded from history or the page bypassed history,
4173 // there is no need to update the title. There is no need to
4174 // go to mSessionHistory to update the title. Setting it in mOSHE
4175 // would suffice.
4176 if (mOSHE && (mLoadType != LOAD_BYPASS_HISTORY) &&
4177 (mLoadType != LOAD_HISTORY) && (mLoadType != LOAD_ERROR_PAGE)) {
4178 mOSHE->SetTitle(mTitle);
4182 return NS_OK;
4185 //*****************************************************************************
4186 // nsDocShell::nsIScrollable
4187 //*****************************************************************************
4189 NS_IMETHODIMP
4190 nsDocShell::GetCurScrollPos(PRInt32 scrollOrientation, PRInt32 * curPos)
4192 NS_ENSURE_ARG_POINTER(curPos);
4194 nsIScrollableView* scrollView;
4195 NS_ENSURE_SUCCESS(GetRootScrollableView(&scrollView),
4196 NS_ERROR_FAILURE);
4197 if (!scrollView) {
4198 return NS_ERROR_FAILURE;
4201 nscoord x, y;
4202 NS_ENSURE_SUCCESS(scrollView->GetScrollPosition(x, y), NS_ERROR_FAILURE);
4204 switch (scrollOrientation) {
4205 case ScrollOrientation_X:
4206 *curPos = x;
4207 return NS_OK;
4209 case ScrollOrientation_Y:
4210 *curPos = y;
4211 return NS_OK;
4213 default:
4214 NS_ENSURE_TRUE(PR_FALSE, NS_ERROR_INVALID_ARG);
4216 return NS_ERROR_FAILURE;
4219 NS_IMETHODIMP
4220 nsDocShell::SetCurScrollPos(PRInt32 scrollOrientation, PRInt32 curPos)
4222 nsIScrollableView* scrollView;
4223 NS_ENSURE_SUCCESS(GetRootScrollableView(&scrollView),
4224 NS_ERROR_FAILURE);
4225 if (!scrollView) {
4226 return NS_ERROR_FAILURE;
4229 PRInt32 other;
4230 PRInt32 x;
4231 PRInt32 y;
4233 GetCurScrollPos(scrollOrientation, &other);
4235 switch (scrollOrientation) {
4236 case ScrollOrientation_X:
4237 x = curPos;
4238 y = other;
4239 break;
4241 case ScrollOrientation_Y:
4242 x = other;
4243 y = curPos;
4244 break;
4246 default:
4247 NS_ENSURE_TRUE(PR_FALSE, NS_ERROR_INVALID_ARG);
4248 x = 0;
4249 y = 0; // fix compiler warning, not actually executed
4252 NS_ENSURE_SUCCESS(scrollView->ScrollTo(x, y, 0),
4253 NS_ERROR_FAILURE);
4254 return NS_OK;
4257 NS_IMETHODIMP
4258 nsDocShell::SetCurScrollPosEx(PRInt32 curHorizontalPos, PRInt32 curVerticalPos)
4260 nsIScrollableView* scrollView;
4261 NS_ENSURE_SUCCESS(GetRootScrollableView(&scrollView),
4262 NS_ERROR_FAILURE);
4263 if (!scrollView) {
4264 return NS_ERROR_FAILURE;
4267 NS_ENSURE_SUCCESS(scrollView->ScrollTo(curHorizontalPos, curVerticalPos, 0),
4268 NS_ERROR_FAILURE);
4269 return NS_OK;
4272 // XXX This is wrong
4273 NS_IMETHODIMP
4274 nsDocShell::GetScrollRange(PRInt32 scrollOrientation,
4275 PRInt32 * minPos, PRInt32 * maxPos)
4277 NS_ENSURE_ARG_POINTER(minPos && maxPos);
4279 nsIScrollableView* scrollView;
4280 NS_ENSURE_SUCCESS(GetRootScrollableView(&scrollView),
4281 NS_ERROR_FAILURE);
4282 if (!scrollView) {
4283 return NS_ERROR_FAILURE;
4286 PRInt32 cx;
4287 PRInt32 cy;
4289 NS_ENSURE_SUCCESS(scrollView->GetContainerSize(&cx, &cy), NS_ERROR_FAILURE);
4290 *minPos = 0;
4292 switch (scrollOrientation) {
4293 case ScrollOrientation_X:
4294 *maxPos = cx;
4295 return NS_OK;
4297 case ScrollOrientation_Y:
4298 *maxPos = cy;
4299 return NS_OK;
4301 default:
4302 NS_ENSURE_TRUE(PR_FALSE, NS_ERROR_INVALID_ARG);
4305 return NS_ERROR_FAILURE;
4308 NS_IMETHODIMP
4309 nsDocShell::SetScrollRange(PRInt32 scrollOrientation,
4310 PRInt32 minPos, PRInt32 maxPos)
4312 //XXX First Check
4314 Retrieves or Sets the valid ranges for the thumb. When maxPos is set to
4315 something less than the current thumb position, curPos is set = to maxPos.
4317 @return NS_OK - Setting or Getting completed successfully.
4318 NS_ERROR_INVALID_ARG - returned when curPos is not within the
4319 minPos and maxPos.
4321 return NS_ERROR_FAILURE;
4324 NS_IMETHODIMP
4325 nsDocShell::SetScrollRangeEx(PRInt32 minHorizontalPos,
4326 PRInt32 maxHorizontalPos, PRInt32 minVerticalPos,
4327 PRInt32 maxVerticalPos)
4329 //XXX First Check
4331 Retrieves or Sets the valid ranges for the thumb. When maxPos is set to
4332 something less than the current thumb position, curPos is set = to maxPos.
4334 @return NS_OK - Setting or Getting completed successfully.
4335 NS_ERROR_INVALID_ARG - returned when curPos is not within the
4336 minPos and maxPos.
4338 return NS_ERROR_FAILURE;
4341 // This returns setting for all documents in this webshell
4342 NS_IMETHODIMP
4343 nsDocShell::GetDefaultScrollbarPreferences(PRInt32 scrollOrientation,
4344 PRInt32 * scrollbarPref)
4346 NS_ENSURE_ARG_POINTER(scrollbarPref);
4347 switch (scrollOrientation) {
4348 case ScrollOrientation_X:
4349 *scrollbarPref = mDefaultScrollbarPref.x;
4350 return NS_OK;
4352 case ScrollOrientation_Y:
4353 *scrollbarPref = mDefaultScrollbarPref.y;
4354 return NS_OK;
4356 default:
4357 NS_ENSURE_TRUE(PR_FALSE, NS_ERROR_INVALID_ARG);
4359 return NS_ERROR_FAILURE;
4362 // Set scrolling preference for all documents in this shell
4364 // There are three possible values stored in the shell:
4365 // 1) nsIScrollable::Scrollbar_Never = no scrollbar
4366 // 2) nsIScrollable::Scrollbar_Auto = scrollbar appears if the document
4367 // being displayed would normally have scrollbar
4368 // 3) nsIScrollable::Scrollbar_Always = scrollbar always appears
4370 // One important client is nsHTMLFrameInnerFrame::CreateWebShell()
4371 NS_IMETHODIMP
4372 nsDocShell::SetDefaultScrollbarPreferences(PRInt32 scrollOrientation,
4373 PRInt32 scrollbarPref)
4375 switch (scrollOrientation) {
4376 case ScrollOrientation_X:
4377 mDefaultScrollbarPref.x = scrollbarPref;
4378 return NS_OK;
4380 case ScrollOrientation_Y:
4381 mDefaultScrollbarPref.y = scrollbarPref;
4382 return NS_OK;
4384 default:
4385 NS_ENSURE_TRUE(PR_FALSE, NS_ERROR_INVALID_ARG);
4387 return NS_ERROR_FAILURE;
4390 NS_IMETHODIMP
4391 nsDocShell::GetScrollbarVisibility(PRBool * verticalVisible,
4392 PRBool * horizontalVisible)
4394 nsIScrollableView* scrollView;
4395 NS_ENSURE_SUCCESS(GetRootScrollableView(&scrollView),
4396 NS_ERROR_FAILURE);
4397 if (!scrollView)
4398 return NS_ERROR_FAILURE;
4400 // We should now call nsLayoutUtils::GetScrollableFrameFor,
4401 // but we can't because of stupid linkage!
4402 nsIFrame* scrollFrame =
4403 static_cast<nsIFrame*>(scrollView->View()->GetParent()->GetClientData());
4404 if (!scrollFrame)
4405 return NS_ERROR_FAILURE;
4406 nsIScrollableFrame* scrollable = do_QueryFrame(scrollFrame);
4407 if (!scrollable)
4408 return NS_ERROR_FAILURE;
4410 nsMargin scrollbars = scrollable->GetActualScrollbarSizes();
4411 if (verticalVisible)
4412 *verticalVisible = scrollbars.left != 0 || scrollbars.right != 0;
4413 if (horizontalVisible)
4414 *horizontalVisible = scrollbars.top != 0 || scrollbars.bottom != 0;
4416 return NS_OK;
4419 //*****************************************************************************
4420 // nsDocShell::nsITextScroll
4421 //*****************************************************************************
4423 NS_IMETHODIMP
4424 nsDocShell::ScrollByLines(PRInt32 numLines)
4426 nsIScrollableView* scrollView;
4428 NS_ENSURE_SUCCESS(GetRootScrollableView(&scrollView),
4429 NS_ERROR_FAILURE);
4430 if (!scrollView) {
4431 return NS_ERROR_FAILURE;
4434 NS_ENSURE_SUCCESS(scrollView->ScrollByLines(0, numLines), NS_ERROR_FAILURE);
4436 return NS_OK;
4439 NS_IMETHODIMP
4440 nsDocShell::ScrollByPages(PRInt32 numPages)
4442 nsIScrollableView* scrollView;
4444 NS_ENSURE_SUCCESS(GetRootScrollableView(&scrollView),
4445 NS_ERROR_FAILURE);
4446 if (!scrollView) {
4447 return NS_ERROR_FAILURE;
4450 NS_ENSURE_SUCCESS(scrollView->ScrollByPages(0, numPages), NS_ERROR_FAILURE);
4452 return NS_OK;
4455 //*****************************************************************************
4456 // nsDocShell::nsIScriptGlobalObjectOwner
4457 //*****************************************************************************
4459 nsIScriptGlobalObject*
4460 nsDocShell::GetScriptGlobalObject()
4462 NS_ENSURE_SUCCESS(EnsureScriptEnvironment(), nsnull);
4464 return mScriptGlobal;
4467 //*****************************************************************************
4468 // nsDocShell::nsIRefreshURI
4469 //*****************************************************************************
4471 NS_IMETHODIMP
4472 nsDocShell::RefreshURI(nsIURI * aURI, PRInt32 aDelay, PRBool aRepeat,
4473 PRBool aMetaRefresh)
4475 NS_ENSURE_ARG(aURI);
4477 /* Check if Meta refresh/redirects are permitted. Some
4478 * embedded applications may not want to do this.
4479 * Must do this before sending out NOTIFY_REFRESH events
4480 * because listeners may have side effects (e.g. displaying a
4481 * button to manually trigger the refresh later).
4483 PRBool allowRedirects = PR_TRUE;
4484 GetAllowMetaRedirects(&allowRedirects);
4485 if (!allowRedirects)
4486 return NS_OK;
4488 // If any web progress listeners are listening for NOTIFY_REFRESH events,
4489 // give them a chance to block this refresh.
4490 PRBool sameURI;
4491 nsresult rv = aURI->Equals(mCurrentURI, &sameURI);
4492 if (NS_FAILED(rv))
4493 sameURI = PR_FALSE;
4494 if (!RefreshAttempted(this, aURI, aDelay, sameURI))
4495 return NS_OK;
4497 nsRefreshTimer *refreshTimer = new nsRefreshTimer();
4498 NS_ENSURE_TRUE(refreshTimer, NS_ERROR_OUT_OF_MEMORY);
4499 PRUint32 busyFlags = 0;
4500 GetBusyFlags(&busyFlags);
4502 nsCOMPtr<nsISupports> dataRef = refreshTimer; // Get the ref count to 1
4504 refreshTimer->mDocShell = this;
4505 refreshTimer->mURI = aURI;
4506 refreshTimer->mDelay = aDelay;
4507 refreshTimer->mRepeat = aRepeat;
4508 refreshTimer->mMetaRefresh = aMetaRefresh;
4510 if (!mRefreshURIList) {
4511 NS_ENSURE_SUCCESS(NS_NewISupportsArray(getter_AddRefs(mRefreshURIList)),
4512 NS_ERROR_FAILURE);
4515 if (busyFlags & BUSY_FLAGS_BUSY) {
4516 // We are busy loading another page. Don't create the
4517 // timer right now. Instead queue up the request and trigger the
4518 // timer in EndPageLoad().
4519 mRefreshURIList->AppendElement(refreshTimer);
4521 else {
4522 // There is no page loading going on right now. Create the
4523 // timer and fire it right away.
4524 nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1");
4525 NS_ENSURE_TRUE(timer, NS_ERROR_FAILURE);
4527 mRefreshURIList->AppendElement(timer); // owning timer ref
4528 timer->InitWithCallback(refreshTimer, aDelay, nsITimer::TYPE_ONE_SHOT);
4530 return NS_OK;
4533 nsresult
4534 nsDocShell::ForceRefreshURIFromTimer(nsIURI * aURI,
4535 PRInt32 aDelay,
4536 PRBool aMetaRefresh,
4537 nsITimer* aTimer)
4539 NS_PRECONDITION(aTimer, "Must have a timer here");
4541 // Remove aTimer from mRefreshURIList if needed
4542 if (mRefreshURIList) {
4543 PRUint32 n = 0;
4544 mRefreshURIList->Count(&n);
4546 for (PRUint32 i = 0; i < n; ++i) {
4547 nsCOMPtr<nsITimer> timer = do_QueryElementAt(mRefreshURIList, i);
4548 if (timer == aTimer) {
4549 mRefreshURIList->RemoveElementAt(i);
4550 break;
4555 return ForceRefreshURI(aURI, aDelay, aMetaRefresh);
4558 NS_IMETHODIMP
4559 nsDocShell::ForceRefreshURI(nsIURI * aURI,
4560 PRInt32 aDelay,
4561 PRBool aMetaRefresh)
4563 NS_ENSURE_ARG(aURI);
4565 nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
4566 CreateLoadInfo(getter_AddRefs(loadInfo));
4567 NS_ENSURE_TRUE(loadInfo, NS_ERROR_OUT_OF_MEMORY);
4569 /* We do need to pass in a referrer, but we don't want it to
4570 * be sent to the server.
4572 loadInfo->SetSendReferrer(PR_FALSE);
4574 /* for most refreshes the current URI is an appropriate
4575 * internal referrer
4577 loadInfo->SetReferrer(mCurrentURI);
4579 /* Check if this META refresh causes a redirection
4580 * to another site.
4582 PRBool equalUri = PR_FALSE;
4583 nsresult rv = aURI->Equals(mCurrentURI, &equalUri);
4584 if (NS_SUCCEEDED(rv) && (!equalUri) && aMetaRefresh) {
4586 /* It is a META refresh based redirection. Now check if it happened
4587 within the threshold time we have in mind(15000 ms as defined by
4588 REFRESH_REDIRECT_TIMER). If so, pass a REPLACE flag to LoadURI().
4590 if (aDelay <= REFRESH_REDIRECT_TIMER) {
4591 loadInfo->SetLoadType(nsIDocShellLoadInfo::loadNormalReplace);
4593 /* for redirects we mimic HTTP, which passes the
4594 * original referrer
4596 nsCOMPtr<nsIURI> internalReferrer;
4597 GetReferringURI(getter_AddRefs(internalReferrer));
4598 if (internalReferrer) {
4599 loadInfo->SetReferrer(internalReferrer);
4602 else
4603 loadInfo->SetLoadType(nsIDocShellLoadInfo::loadRefresh);
4605 * LoadURI(...) will cancel all refresh timers... This causes the
4606 * Timer and its refreshData instance to be released...
4608 LoadURI(aURI, loadInfo, nsIWebNavigation::LOAD_FLAGS_NONE, PR_TRUE);
4609 return NS_OK;
4611 else
4612 loadInfo->SetLoadType(nsIDocShellLoadInfo::loadRefresh);
4614 LoadURI(aURI, loadInfo, nsIWebNavigation::LOAD_FLAGS_NONE, PR_TRUE);
4616 return NS_OK;
4619 nsresult
4620 nsDocShell::SetupRefreshURIFromHeader(nsIURI * aBaseURI,
4621 const nsACString & aHeader)
4623 // Refresh headers are parsed with the following format in mind
4624 // <META HTTP-EQUIV=REFRESH CONTENT="5; URL=http://uri">
4625 // By the time we are here, the following is true:
4626 // header = "REFRESH"
4627 // content = "5; URL=http://uri" // note the URL attribute is
4628 // optional, if it is absent, the currently loaded url is used.
4629 // Also note that the seconds and URL separator can be either
4630 // a ';' or a ','. The ',' separator should be illegal but CNN
4631 // is using it.
4633 // We need to handle the following strings, where
4634 // - X is a set of digits
4635 // - URI is either a relative or absolute URI
4637 // Note that URI should start with "url=" but we allow omission
4639 // "" || ";" || ","
4640 // empty string. use the currently loaded URI
4641 // and refresh immediately.
4642 // "X" || "X;" || "X,"
4643 // Refresh the currently loaded URI in X seconds.
4644 // "X; URI" || "X, URI"
4645 // Refresh using URI as the destination in X seconds.
4646 // "URI" || "; URI" || ", URI"
4647 // Refresh immediately using URI as the destination.
4649 // Currently, anything immediately following the URI, if
4650 // separated by any char in the set "'\"\t\r\n " will be
4651 // ignored. So "10; url=go.html ; foo=bar" will work,
4652 // and so will "10; url='go.html'; foo=bar". However,
4653 // "10; url=go.html; foo=bar" will result in the uri
4654 // "go.html;" since ';' and ',' are valid uri characters.
4656 // Note that we need to remove any tokens wrapping the URI.
4657 // These tokens currently include spaces, double and single
4658 // quotes.
4660 // when done, seconds is 0 or the given number of seconds
4661 // uriAttrib is empty or the URI specified
4662 nsCAutoString uriAttrib;
4663 PRInt32 seconds = 0;
4664 PRBool specifiesSeconds = PR_FALSE;
4666 nsACString::const_iterator iter, tokenStart, doneIterating;
4668 aHeader.BeginReading(iter);
4669 aHeader.EndReading(doneIterating);
4671 // skip leading whitespace
4672 while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter))
4673 ++iter;
4675 tokenStart = iter;
4677 // skip leading + and -
4678 if (iter != doneIterating && (*iter == '-' || *iter == '+'))
4679 ++iter;
4681 // parse number
4682 while (iter != doneIterating && (*iter >= '0' && *iter <= '9')) {
4683 seconds = seconds * 10 + (*iter - '0');
4684 specifiesSeconds = PR_TRUE;
4685 ++iter;
4688 if (iter != doneIterating) {
4689 // if we started with a '-', number is negative
4690 if (*tokenStart == '-')
4691 seconds = -seconds;
4693 // skip to next ';' or ','
4694 nsACString::const_iterator iterAfterDigit = iter;
4695 while (iter != doneIterating && !(*iter == ';' || *iter == ','))
4697 if (specifiesSeconds)
4699 // Non-whitespace characters here mean that the string is
4700 // malformed but tolerate sites that specify a decimal point,
4701 // even though meta refresh only works on whole seconds.
4702 if (iter == iterAfterDigit &&
4703 !nsCRT::IsAsciiSpace(*iter) && *iter != '.')
4705 // The characters between the seconds and the next
4706 // section are just garbage!
4707 // e.g. content="2a0z+,URL=http://www.mozilla.org/"
4708 // Just ignore this redirect.
4709 return NS_ERROR_FAILURE;
4711 else if (nsCRT::IsAsciiSpace(*iter))
4713 // We've had at least one whitespace so tolerate the mistake
4714 // and drop through.
4715 // e.g. content="10 foo"
4716 ++iter;
4717 break;
4720 ++iter;
4723 // skip any remaining whitespace
4724 while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter))
4725 ++iter;
4727 // skip ';' or ','
4728 if (iter != doneIterating && (*iter == ';' || *iter == ',')) {
4729 ++iter;
4732 // skip whitespace
4733 while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter))
4734 ++iter;
4737 // possible start of URI
4738 tokenStart = iter;
4740 // skip "url = " to real start of URI
4741 if (iter != doneIterating && (*iter == 'u' || *iter == 'U')) {
4742 ++iter;
4743 if (iter != doneIterating && (*iter == 'r' || *iter == 'R')) {
4744 ++iter;
4745 if (iter != doneIterating && (*iter == 'l' || *iter == 'L')) {
4746 ++iter;
4748 // skip whitespace
4749 while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter))
4750 ++iter;
4752 if (iter != doneIterating && *iter == '=') {
4753 ++iter;
4755 // skip whitespace
4756 while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter))
4757 ++iter;
4759 // found real start of URI
4760 tokenStart = iter;
4766 // skip a leading '"' or '\''.
4768 PRBool isQuotedURI = PR_FALSE;
4769 if (tokenStart != doneIterating && (*tokenStart == '"' || *tokenStart == '\''))
4771 isQuotedURI = PR_TRUE;
4772 ++tokenStart;
4775 // set iter to start of URI
4776 iter = tokenStart;
4778 // tokenStart here points to the beginning of URI
4780 // grab the rest of the URI
4781 while (iter != doneIterating)
4783 if (isQuotedURI && (*iter == '"' || *iter == '\''))
4784 break;
4785 ++iter;
4788 // move iter one back if the last character is a '"' or '\''
4789 if (iter != tokenStart && isQuotedURI) {
4790 --iter;
4791 if (!(*iter == '"' || *iter == '\''))
4792 ++iter;
4795 // URI is whatever's contained from tokenStart to iter.
4796 // note: if tokenStart == doneIterating, so is iter.
4798 nsresult rv = NS_OK;
4800 nsCOMPtr<nsIURI> uri;
4801 PRBool specifiesURI = PR_FALSE;
4802 if (tokenStart == iter) {
4803 uri = aBaseURI;
4805 else {
4806 uriAttrib = Substring(tokenStart, iter);
4807 // NS_NewURI takes care of any whitespace surrounding the URL
4808 rv = NS_NewURI(getter_AddRefs(uri), uriAttrib, nsnull, aBaseURI);
4809 specifiesURI = PR_TRUE;
4812 // No URI or seconds were specified
4813 if (!specifiesSeconds && !specifiesURI)
4815 // Do nothing because the alternative is to spin around in a refresh
4816 // loop forever!
4817 return NS_ERROR_FAILURE;
4820 if (NS_SUCCEEDED(rv)) {
4821 nsCOMPtr<nsIScriptSecurityManager>
4822 securityManager(do_GetService
4823 (NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv));
4824 if (NS_SUCCEEDED(rv)) {
4825 rv = securityManager->
4826 CheckLoadURI(aBaseURI, uri,
4827 nsIScriptSecurityManager::
4828 LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT);
4829 if (NS_SUCCEEDED(rv)) {
4830 // Since we can't travel back in time yet, just pretend
4831 // negative numbers do nothing at all.
4832 if (seconds < 0)
4833 return NS_ERROR_FAILURE;
4835 rv = RefreshURI(uri, seconds * 1000, PR_FALSE, PR_TRUE);
4839 return rv;
4842 NS_IMETHODIMP nsDocShell::SetupRefreshURI(nsIChannel * aChannel)
4844 nsresult rv;
4845 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel, &rv));
4846 if (NS_SUCCEEDED(rv)) {
4847 nsCAutoString refreshHeader;
4848 rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("refresh"),
4849 refreshHeader);
4851 if (!refreshHeader.IsEmpty()) {
4852 SetupReferrerFromChannel(aChannel);
4853 rv = SetupRefreshURIFromHeader(mCurrentURI, refreshHeader);
4854 if (NS_SUCCEEDED(rv)) {
4855 return NS_REFRESHURI_HEADER_FOUND;
4859 return rv;
4862 static void
4863 DoCancelRefreshURITimers(nsISupportsArray* aTimerList)
4865 if (!aTimerList)
4866 return;
4868 PRUint32 n=0;
4869 aTimerList->Count(&n);
4871 while (n) {
4872 nsCOMPtr<nsITimer> timer(do_QueryElementAt(aTimerList, --n));
4874 aTimerList->RemoveElementAt(n); // bye bye owning timer ref
4876 if (timer)
4877 timer->Cancel();
4881 NS_IMETHODIMP
4882 nsDocShell::CancelRefreshURITimers()
4884 DoCancelRefreshURITimers(mRefreshURIList);
4885 DoCancelRefreshURITimers(mSavedRefreshURIList);
4886 mRefreshURIList = nsnull;
4887 mSavedRefreshURIList = nsnull;
4889 return NS_OK;
4892 NS_IMETHODIMP
4893 nsDocShell::GetRefreshPending(PRBool* _retval)
4895 if (!mRefreshURIList) {
4896 *_retval = PR_FALSE;
4897 return NS_OK;
4900 PRUint32 count;
4901 nsresult rv = mRefreshURIList->Count(&count);
4902 if (NS_SUCCEEDED(rv))
4903 *_retval = (count != 0);
4904 return rv;
4907 NS_IMETHODIMP
4908 nsDocShell::SuspendRefreshURIs()
4910 if (mRefreshURIList) {
4911 PRUint32 n = 0;
4912 mRefreshURIList->Count(&n);
4914 for (PRUint32 i = 0; i < n; ++i) {
4915 nsCOMPtr<nsITimer> timer = do_QueryElementAt(mRefreshURIList, i);
4916 if (!timer)
4917 continue; // this must be a nsRefreshURI already
4919 // Replace this timer object with a nsRefreshTimer object.
4920 nsCOMPtr<nsITimerCallback> callback;
4921 timer->GetCallback(getter_AddRefs(callback));
4923 timer->Cancel();
4925 nsCOMPtr<nsITimerCallback> rt = do_QueryInterface(callback);
4926 NS_ASSERTION(rt, "RefreshURIList timer callbacks should only be RefreshTimer objects");
4928 mRefreshURIList->ReplaceElementAt(rt, i);
4932 // Suspend refresh URIs for our child shells as well.
4933 PRInt32 n = mChildList.Count();
4935 for (PRInt32 i = 0; i < n; ++i) {
4936 nsCOMPtr<nsIDocShell> shell = do_QueryInterface(ChildAt(i));
4937 if (shell)
4938 shell->SuspendRefreshURIs();
4941 return NS_OK;
4944 NS_IMETHODIMP
4945 nsDocShell::ResumeRefreshURIs()
4947 RefreshURIFromQueue();
4949 // Resume refresh URIs for our child shells as well.
4950 PRInt32 n = mChildList.Count();
4952 for (PRInt32 i = 0; i < n; ++i) {
4953 nsCOMPtr<nsIDocShell> shell = do_QueryInterface(ChildAt(i));
4954 if (shell)
4955 shell->ResumeRefreshURIs();
4958 return NS_OK;
4961 nsresult
4962 nsDocShell::RefreshURIFromQueue()
4964 if (!mRefreshURIList)
4965 return NS_OK;
4966 PRUint32 n = 0;
4967 mRefreshURIList->Count(&n);
4969 while (n) {
4970 nsCOMPtr<nsISupports> element;
4971 mRefreshURIList->GetElementAt(--n, getter_AddRefs(element));
4972 nsCOMPtr<nsITimerCallback> refreshInfo(do_QueryInterface(element));
4974 if (refreshInfo) {
4975 // This is the nsRefreshTimer object, waiting to be
4976 // setup in a timer object and fired.
4977 // Create the timer and trigger it.
4978 PRUint32 delay = static_cast<nsRefreshTimer*>(static_cast<nsITimerCallback*>(refreshInfo))->GetDelay();
4979 nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1");
4980 if (timer) {
4981 // Replace the nsRefreshTimer element in the queue with
4982 // its corresponding timer object, so that in case another
4983 // load comes through before the timer can go off, the timer will
4984 // get cancelled in CancelRefreshURITimer()
4985 mRefreshURIList->ReplaceElementAt(timer, n);
4986 timer->InitWithCallback(refreshInfo, delay, nsITimer::TYPE_ONE_SHOT);
4989 } // while
4991 return NS_OK;
4994 //*****************************************************************************
4995 // nsDocShell::nsIContentViewerContainer
4996 //*****************************************************************************
4998 NS_IMETHODIMP
4999 nsDocShell::Embed(nsIContentViewer * aContentViewer,
5000 const char *aCommand, nsISupports * aExtraInfo)
5002 // Save the LayoutHistoryState of the previous document, before
5003 // setting up new document
5004 PersistLayoutHistoryState();
5006 nsresult rv = SetupNewViewer(aContentViewer);
5008 // If we are loading a wyciwyg url from history, change the base URI for
5009 // the document to the original http url that created the document.write().
5010 // This makes sure that all relative urls in a document.written page loaded
5011 // via history work properly.
5012 if (mCurrentURI &&
5013 (mLoadType & LOAD_CMD_HISTORY ||
5014 mLoadType == LOAD_RELOAD_NORMAL ||
5015 mLoadType == LOAD_RELOAD_CHARSET_CHANGE)){
5016 PRBool isWyciwyg = PR_FALSE;
5017 // Check if the url is wyciwyg
5018 rv = mCurrentURI->SchemeIs("wyciwyg", &isWyciwyg);
5019 if (isWyciwyg && NS_SUCCEEDED(rv))
5020 SetBaseUrlForWyciwyg(aContentViewer);
5022 // XXX What if SetupNewViewer fails?
5023 if (mLSHE) {
5024 // Restore the editing state, if it's stored in session history.
5025 if (mLSHE->HasDetachedEditor()) {
5026 ReattachEditorToWindow(mLSHE);
5028 SetHistoryEntry(&mOSHE, mLSHE);
5031 PRBool updateHistory = PR_TRUE;
5033 // Determine if this type of load should update history
5034 switch (mLoadType) {
5035 case LOAD_NORMAL_REPLACE:
5036 case LOAD_STOP_CONTENT_AND_REPLACE:
5037 case LOAD_RELOAD_BYPASS_CACHE:
5038 case LOAD_RELOAD_BYPASS_PROXY:
5039 case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
5040 updateHistory = PR_FALSE;
5041 break;
5042 default:
5043 break;
5046 if (!updateHistory)
5047 SetLayoutHistoryState(nsnull);
5049 return NS_OK;
5052 /* void setIsPrinting (in boolean aIsPrinting); */
5053 NS_IMETHODIMP
5054 nsDocShell::SetIsPrinting(PRBool aIsPrinting)
5056 mIsPrintingOrPP = aIsPrinting;
5057 return NS_OK;
5060 //*****************************************************************************
5061 // nsDocShell::nsIWebProgressListener
5062 //*****************************************************************************
5064 NS_IMETHODIMP
5065 nsDocShell::OnProgressChange(nsIWebProgress * aProgress,
5066 nsIRequest * aRequest,
5067 PRInt32 aCurSelfProgress,
5068 PRInt32 aMaxSelfProgress,
5069 PRInt32 aCurTotalProgress,
5070 PRInt32 aMaxTotalProgress)
5072 return NS_OK;
5075 NS_IMETHODIMP
5076 nsDocShell::OnStateChange(nsIWebProgress * aProgress, nsIRequest * aRequest,
5077 PRUint32 aStateFlags, nsresult aStatus)
5079 nsresult rv;
5081 // Update the busy cursor
5082 if ((~aStateFlags & (STATE_START | STATE_IS_NETWORK)) == 0) {
5083 nsCOMPtr<nsIWyciwygChannel> wcwgChannel(do_QueryInterface(aRequest));
5084 nsCOMPtr<nsIWebProgress> webProgress =
5085 do_QueryInterface(GetAsSupports(this));
5087 // Was the wyciwyg document loaded on this docshell?
5088 if (wcwgChannel && !mLSHE && (mItemType == typeContent) && aProgress == webProgress.get()) {
5089 nsCOMPtr<nsIURI> uri;
5090 wcwgChannel->GetURI(getter_AddRefs(uri));
5092 PRBool equalUri = PR_TRUE;
5093 // Store the wyciwyg url in session history, only if it is
5094 // being loaded fresh for the first time. We don't want
5095 // multiple entries for successive loads
5096 if (mCurrentURI &&
5097 NS_SUCCEEDED(uri->Equals(mCurrentURI, &equalUri)) &&
5098 !equalUri) {
5099 // This is a document.write(). Get the made-up url
5100 // from the channel and store it in session history.
5101 rv = AddToSessionHistory(uri, wcwgChannel, nsnull,
5102 getter_AddRefs(mLSHE));
5103 SetCurrentURI(uri, aRequest, PR_TRUE);
5104 // Save history state of the previous page
5105 rv = PersistLayoutHistoryState();
5106 if (mOSHE)
5107 SetHistoryEntry(&mOSHE, mLSHE);
5111 // Page has begun to load
5112 mBusyFlags = BUSY_FLAGS_BUSY | BUSY_FLAGS_BEFORE_PAGE_LOAD;
5113 nsCOMPtr<nsIWidget> mainWidget;
5114 GetMainWidget(getter_AddRefs(mainWidget));
5115 if (mainWidget) {
5116 mainWidget->SetCursor(eCursor_spinning);
5119 else if ((~aStateFlags & (STATE_TRANSFERRING | STATE_IS_DOCUMENT)) == 0) {
5120 // Page is loading
5121 mBusyFlags = BUSY_FLAGS_BUSY | BUSY_FLAGS_PAGE_LOADING;
5123 else if ((aStateFlags & STATE_STOP) && (aStateFlags & STATE_IS_NETWORK)) {
5124 // Page has finished loading
5125 mBusyFlags = BUSY_FLAGS_NONE;
5126 nsCOMPtr<nsIWidget> mainWidget;
5127 GetMainWidget(getter_AddRefs(mainWidget));
5128 if (mainWidget) {
5129 mainWidget->SetCursor(eCursor_standard);
5132 if ((~aStateFlags & (STATE_IS_DOCUMENT | STATE_STOP)) == 0) {
5133 nsCOMPtr<nsIWebProgress> webProgress =
5134 do_QueryInterface(GetAsSupports(this));
5135 // Is the document stop notification for this document?
5136 if (aProgress == webProgress.get()) {
5137 nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
5138 EndPageLoad(aProgress, channel, aStatus);
5141 // note that redirect state changes will go through here as well, but it
5142 // is better to handle those in OnRedirectStateChange where more
5143 // information is available.
5144 return NS_OK;
5147 NS_IMETHODIMP
5148 nsDocShell::OnLocationChange(nsIWebProgress * aProgress,
5149 nsIRequest * aRequest, nsIURI * aURI)
5151 NS_NOTREACHED("notification excluded in AddProgressListener(...)");
5152 return NS_OK;
5155 void
5156 nsDocShell::OnRedirectStateChange(nsIChannel* aOldChannel,
5157 nsIChannel* aNewChannel,
5158 PRUint32 aRedirectFlags,
5159 PRUint32 aStateFlags)
5161 NS_ASSERTION(aStateFlags & STATE_REDIRECTING,
5162 "Calling OnRedirectStateChange when there is no redirect");
5163 if (!(aStateFlags & STATE_IS_DOCUMENT))
5164 return; // not a toplevel document
5166 // If this load is being checked by the URI classifier, we need to
5167 // query the classifier again for the new URI.
5168 if (mClassifier) {
5169 mClassifier->OnRedirect(aOldChannel, aNewChannel);
5172 nsCOMPtr<nsIGlobalHistory3> history3(do_QueryInterface(mGlobalHistory));
5173 nsresult result = NS_ERROR_NOT_IMPLEMENTED;
5174 if (history3) {
5175 // notify global history of this redirect
5176 result = history3->AddDocumentRedirect(aOldChannel, aNewChannel,
5177 aRedirectFlags, !IsFrame());
5180 if (result == NS_ERROR_NOT_IMPLEMENTED) {
5181 // when there is no GlobalHistory3, or it doesn't implement
5182 // AddToplevelRedirect, we fall back to GlobalHistory2. Just notify
5183 // that the redirecting page was a rePdirect so it will be link colored
5184 // but not visible.
5185 nsCOMPtr<nsIURI> oldURI;
5186 aOldChannel->GetURI(getter_AddRefs(oldURI));
5187 if (! oldURI)
5188 return; // nothing to tell anybody about
5189 AddToGlobalHistory(oldURI, PR_TRUE, aOldChannel);
5192 // check if the new load should go through the application cache.
5193 nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
5194 do_QueryInterface(aNewChannel);
5195 if (appCacheChannel) {
5196 nsCOMPtr<nsIURI> newURI;
5197 aNewChannel->GetURI(getter_AddRefs(newURI));
5198 appCacheChannel->SetChooseApplicationCache(ShouldCheckAppCache(newURI));
5202 NS_IMETHODIMP
5203 nsDocShell::OnStatusChange(nsIWebProgress * aWebProgress,
5204 nsIRequest * aRequest,
5205 nsresult aStatus, const PRUnichar * aMessage)
5207 NS_NOTREACHED("notification excluded in AddProgressListener(...)");
5208 return NS_OK;
5211 NS_IMETHODIMP
5212 nsDocShell::OnSecurityChange(nsIWebProgress * aWebProgress,
5213 nsIRequest * aRequest, PRUint32 state)
5215 NS_NOTREACHED("notification excluded in AddProgressListener(...)");
5216 return NS_OK;
5220 nsresult
5221 nsDocShell::EndPageLoad(nsIWebProgress * aProgress,
5222 nsIChannel * aChannel, nsresult aStatus)
5225 // one of many safeguards that prevent death and destruction if
5226 // someone is so very very rude as to bring this window down
5227 // during this load handler.
5229 nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
5231 // We're done with the URI classifier for this channel
5232 mClassifier = nsnull;
5235 // Notify the ContentViewer that the Document has finished loading...
5237 // This will cause any OnLoad(...) handlers to fire, if it is a HTML
5238 // document...
5240 if (!mEODForCurrentDocument && mContentViewer) {
5241 mIsExecutingOnLoadHandler = PR_TRUE;
5242 mContentViewer->LoadComplete(aStatus);
5243 mIsExecutingOnLoadHandler = PR_FALSE;
5245 mEODForCurrentDocument = PR_TRUE;
5247 // If all documents have completed their loading
5248 // favor native event dispatch priorities
5249 // over performance
5250 if (--gNumberOfDocumentsLoading == 0) {
5251 // Hint to use normal native event dispatch priorities
5252 FavorPerformanceHint(PR_FALSE, NS_EVENT_STARVATION_DELAY_HINT);
5255 /* Check if the httpChannel has any cache-control related response headers,
5256 * like no-store, no-cache. If so, update SHEntry so that
5257 * when a user goes back/forward to this page, we appropriately do
5258 * form value restoration or load from server.
5260 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
5261 if (!httpChannel) // HttpChannel could be hiding underneath a Multipart channel.
5262 GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
5264 if (httpChannel) {
5265 // figure out if SH should be saving layout state.
5266 PRBool discardLayoutState = ShouldDiscardLayoutState(httpChannel);
5267 if (mLSHE && discardLayoutState && (mLoadType & LOAD_CMD_NORMAL) &&
5268 (mLoadType != LOAD_BYPASS_HISTORY) && (mLoadType != LOAD_ERROR_PAGE))
5269 mLSHE->SetSaveLayoutStateFlag(PR_FALSE);
5272 // Clear mLSHE after calling the onLoadHandlers. This way, if the
5273 // onLoadHandler tries to load something different in
5274 // itself or one of its children, we can deal with it appropriately.
5275 if (mLSHE) {
5276 mLSHE->SetLoadType(nsIDocShellLoadInfo::loadHistory);
5278 // Clear the mLSHE reference to indicate document loading is done one
5279 // way or another.
5280 SetHistoryEntry(&mLSHE, nsnull);
5282 // if there's a refresh header in the channel, this method
5283 // will set it up for us.
5284 RefreshURIFromQueue();
5286 return NS_OK;
5290 //*****************************************************************************
5291 // nsDocShell: Content Viewer Management
5292 //*****************************************************************************
5294 NS_IMETHODIMP
5295 nsDocShell::EnsureContentViewer()
5297 if (mContentViewer)
5298 return NS_OK;
5299 if (mIsBeingDestroyed)
5300 return NS_ERROR_FAILURE;
5302 nsIPrincipal* principal = nsnull;
5304 nsCOMPtr<nsPIDOMWindow> piDOMWindow(do_QueryInterface(mScriptGlobal));
5305 if (piDOMWindow) {
5306 principal = piDOMWindow->GetOpenerScriptPrincipal();
5309 if (!principal) {
5310 principal = GetInheritedPrincipal(PR_FALSE);
5313 nsresult rv = CreateAboutBlankContentViewer(principal);
5315 if (NS_SUCCEEDED(rv)) {
5316 nsCOMPtr<nsIDOMDocument> domDoc;
5317 mContentViewer->GetDOMDocument(getter_AddRefs(domDoc));
5318 nsCOMPtr<nsIDocument> doc(do_QueryInterface(domDoc));
5319 NS_ASSERTION(doc,
5320 "Should have doc if CreateAboutBlankContentViewer "
5321 "succeeded!");
5323 doc->SetIsInitialDocument(PR_TRUE);
5326 return rv;
5329 nsresult
5330 nsDocShell::CreateAboutBlankContentViewer(nsIPrincipal* aPrincipal)
5332 nsCOMPtr<nsIDocument> blankDoc;
5333 nsCOMPtr<nsIContentViewer> viewer;
5334 nsresult rv = NS_ERROR_FAILURE;
5336 /* mCreatingDocument should never be true at this point. However, it's
5337 a theoretical possibility. We want to know about it and make it stop,
5338 and this sounds like a job for an assertion. */
5339 NS_ASSERTION(!mCreatingDocument, "infinite(?) loop creating document averted");
5340 if (mCreatingDocument)
5341 return NS_ERROR_FAILURE;
5343 mCreatingDocument = PR_TRUE;
5345 // mContentViewer->PermitUnload may release |this| docshell.
5346 nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
5348 if (mContentViewer) {
5349 // We've got a content viewer already. Make sure the user
5350 // permits us to discard the current document and replace it
5351 // with about:blank. And also ensure we fire the unload events
5352 // in the current document.
5354 PRBool okToUnload;
5355 rv = mContentViewer->PermitUnload(&okToUnload);
5357 if (NS_SUCCEEDED(rv) && !okToUnload) {
5358 // The user chose not to unload the page, interrupt the load.
5359 return NS_ERROR_FAILURE;
5362 mSavingOldViewer = CanSavePresentation(LOAD_NORMAL, nsnull, nsnull);
5364 // Make sure to blow away our mLoadingURI just in case. No loads
5365 // from inside this pagehide.
5366 mLoadingURI = nsnull;
5368 // Notify the current document that it is about to be unloaded!!
5370 // It is important to fire the unload() notification *before* any state
5371 // is changed within the DocShell - otherwise, javascript will get the
5372 // wrong information :-(
5374 (void) FirePageHideNotification(!mSavingOldViewer);
5377 // Now make sure we don't think we're in the middle of firing unload after
5378 // this point. This will make us fire unload when the about:blank document
5379 // unloads... but that's ok, more or less. Would be nice if it fired load
5380 // too, of course.
5381 mFiredUnloadEvent = PR_FALSE;
5383 // one helper factory, please
5384 nsCOMPtr<nsICategoryManager> catMan(do_GetService(NS_CATEGORYMANAGER_CONTRACTID));
5385 if (!catMan)
5386 return NS_ERROR_FAILURE;
5388 nsXPIDLCString contractId;
5389 rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", "text/html", getter_Copies(contractId));
5390 if (NS_FAILED(rv))
5391 return rv;
5393 nsCOMPtr<nsIDocumentLoaderFactory> docFactory(do_GetService(contractId));
5394 if (docFactory) {
5395 // generate (about:blank) document to load
5396 docFactory->CreateBlankDocument(mLoadGroup, aPrincipal,
5397 getter_AddRefs(blankDoc));
5398 if (blankDoc) {
5399 blankDoc->SetContainer(static_cast<nsIDocShell *>(this));
5401 // create a content viewer for us and the new document
5402 docFactory->CreateInstanceForDocument(NS_ISUPPORTS_CAST(nsIDocShell *, this),
5403 blankDoc, "view", getter_AddRefs(viewer));
5405 // hook 'em up
5406 if (viewer) {
5407 viewer->SetContainer(static_cast<nsIContentViewerContainer *>(this));
5408 nsCOMPtr<nsIDOMDocument> domdoc(do_QueryInterface(blankDoc));
5409 Embed(viewer, "", 0);
5410 viewer->SetDOMDocument(domdoc);
5412 SetCurrentURI(blankDoc->GetDocumentURI(), nsnull, PR_TRUE);
5413 rv = NS_OK;
5417 mCreatingDocument = PR_FALSE;
5419 // The transient about:blank viewer doesn't have a session history entry.
5420 SetHistoryEntry(&mOSHE, nsnull);
5422 return rv;
5425 PRBool
5426 nsDocShell::CanSavePresentation(PRUint32 aLoadType,
5427 nsIRequest *aNewRequest,
5428 nsIDocument *aNewDocument)
5430 if (!mOSHE)
5431 return PR_FALSE; // no entry to save into
5433 // Only save presentation for "normal" loads and link loads. Anything else
5434 // probably wants to refetch the page, so caching the old presentation
5435 // would be incorrect.
5436 if (aLoadType != LOAD_NORMAL &&
5437 aLoadType != LOAD_HISTORY &&
5438 aLoadType != LOAD_LINK &&
5439 aLoadType != LOAD_STOP_CONTENT &&
5440 aLoadType != LOAD_STOP_CONTENT_AND_REPLACE &&
5441 aLoadType != LOAD_ERROR_PAGE)
5442 return PR_FALSE;
5444 // If the session history entry has the saveLayoutState flag set to false,
5445 // then we should not cache the presentation.
5446 PRBool canSaveState;
5447 mOSHE->GetSaveLayoutStateFlag(&canSaveState);
5448 if (canSaveState == PR_FALSE)
5449 return PR_FALSE;
5451 // If the document is not done loading, don't cache it.
5452 nsCOMPtr<nsPIDOMWindow> pWin = do_QueryInterface(mScriptGlobal);
5453 if (!pWin || pWin->IsLoading())
5454 return PR_FALSE;
5456 if (pWin->WouldReuseInnerWindow(aNewDocument))
5457 return PR_FALSE;
5459 // Avoid doing the work of saving the presentation state in the case where
5460 // the content viewer cache is disabled.
5461 if (nsSHistory::GetMaxTotalViewers() == 0)
5462 return PR_FALSE;
5464 // Don't cache the content viewer if we're in a subframe and the subframe
5465 // pref is disabled.
5466 PRBool cacheFrames = PR_FALSE;
5467 mPrefs->GetBoolPref("browser.sessionhistory.cache_subframes",
5468 &cacheFrames);
5469 if (!cacheFrames) {
5470 nsCOMPtr<nsIDocShellTreeItem> root;
5471 GetSameTypeParent(getter_AddRefs(root));
5472 if (root && root != this) {
5473 return PR_FALSE; // this is a subframe load
5477 // If the document does not want its presentation cached, then don't.
5478 nsCOMPtr<nsIDocument> doc = do_QueryInterface(pWin->GetExtantDocument());
5479 if (!doc || !doc->CanSavePresentation(aNewRequest))
5480 return PR_FALSE;
5482 return PR_TRUE;
5485 void
5486 nsDocShell::ReattachEditorToWindow(nsISHEntry *aSHEntry)
5488 NS_ASSERTION(!mEditorData,
5489 "Why reattach an editor when we already have one?");
5490 NS_ASSERTION(aSHEntry && aSHEntry->HasDetachedEditor(),
5491 "Reattaching when there's not a detached editor.");
5493 if (mEditorData || !aSHEntry)
5494 return;
5496 mEditorData = aSHEntry->ForgetEditorData();
5497 if (mEditorData) {
5498 nsresult res = mEditorData->ReattachToWindow(this);
5499 NS_ASSERTION(NS_SUCCEEDED(res), "Failed to reattach editing session");
5503 void
5504 nsDocShell::DetachEditorFromWindow(nsISHEntry *aSHEntry)
5506 if (!mEditorData)
5507 return;
5509 NS_ASSERTION(!aSHEntry || !aSHEntry->HasDetachedEditor(),
5510 "Detaching editor when it's already detached.");
5512 nsresult res = mEditorData->DetachFromWindow();
5513 NS_ASSERTION(NS_SUCCEEDED(res), "Failed to detach editor");
5515 if (NS_SUCCEEDED(res)) {
5516 // Make aSHEntry hold the owning ref to the editor data.
5517 if (aSHEntry)
5518 aSHEntry->SetEditorData(mEditorData.forget());
5519 else
5520 mEditorData = nsnull;
5523 #ifdef DEBUG
5525 PRBool isEditable;
5526 GetEditable(&isEditable);
5527 NS_ASSERTION(!isEditable,
5528 "Window is still editable after detaching editor.");
5530 #endif // DEBUG
5534 void
5535 nsDocShell::DetachEditorFromWindow()
5537 DetachEditorFromWindow(mOSHE);
5540 nsresult
5541 nsDocShell::CaptureState()
5543 if (!mOSHE || mOSHE == mLSHE) {
5544 // No entry to save into, or we're replacing the existing entry.
5545 return NS_ERROR_FAILURE;
5548 nsCOMPtr<nsPIDOMWindow> privWin = do_QueryInterface(mScriptGlobal);
5549 if (!privWin)
5550 return NS_ERROR_FAILURE;
5552 nsCOMPtr<nsISupports> windowState;
5553 nsresult rv = privWin->SaveWindowState(getter_AddRefs(windowState));
5554 NS_ENSURE_SUCCESS(rv, rv);
5556 #ifdef DEBUG_PAGE_CACHE
5557 nsCOMPtr<nsIURI> uri;
5558 mOSHE->GetURI(getter_AddRefs(uri));
5559 nsCAutoString spec;
5560 if (uri)
5561 uri->GetSpec(spec);
5562 printf("Saving presentation into session history\n");
5563 printf(" SH URI: %s\n", spec.get());
5564 #endif
5566 rv = mOSHE->SetWindowState(windowState);
5567 NS_ENSURE_SUCCESS(rv, rv);
5569 // Suspend refresh URIs and save off the timer queue
5570 rv = mOSHE->SetRefreshURIList(mSavedRefreshURIList);
5571 NS_ENSURE_SUCCESS(rv, rv);
5573 // Capture the current content viewer bounds.
5574 nsCOMPtr<nsIPresShell> shell;
5575 nsDocShell::GetPresShell(getter_AddRefs(shell));
5576 if (shell) {
5577 nsIViewManager *vm = shell->GetViewManager();
5578 if (vm) {
5579 nsIView *rootView = nsnull;
5580 vm->GetRootView(rootView);
5581 if (rootView) {
5582 nsIWidget *widget = rootView->GetWidget();
5583 if (widget) {
5584 nsIntRect bounds(0, 0, 0, 0);
5585 widget->GetBounds(bounds);
5586 rv = mOSHE->SetViewerBounds(bounds);
5592 // Capture the docshell hierarchy.
5593 mOSHE->ClearChildShells();
5595 PRInt32 childCount = mChildList.Count();
5596 for (PRInt32 i = 0; i < childCount; ++i) {
5597 nsCOMPtr<nsIDocShellTreeItem> childShell = do_QueryInterface(ChildAt(i));
5598 NS_ASSERTION(childShell, "null child shell");
5600 mOSHE->AddChildShell(childShell);
5603 return NS_OK;
5606 NS_IMETHODIMP
5607 nsDocShell::RestorePresentationEvent::Run()
5609 if (mDocShell && NS_FAILED(mDocShell->RestoreFromHistory()))
5610 NS_WARNING("RestoreFromHistory failed");
5611 return NS_OK;
5614 NS_IMETHODIMP
5615 nsDocShell::BeginRestore(nsIContentViewer *aContentViewer, PRBool aTop)
5617 nsresult rv;
5618 if (!aContentViewer) {
5619 rv = EnsureContentViewer();
5620 NS_ENSURE_SUCCESS(rv, rv);
5622 aContentViewer = mContentViewer;
5625 // Dispatch events for restoring the presentation. We try to simulate
5626 // the progress notifications loading the document would cause, so we add
5627 // the document's channel to the loadgroup to initiate stateChange
5628 // notifications.
5630 nsCOMPtr<nsIDOMDocument> domDoc;
5631 aContentViewer->GetDOMDocument(getter_AddRefs(domDoc));
5632 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
5633 if (doc) {
5634 nsIChannel *channel = doc->GetChannel();
5635 if (channel) {
5636 mEODForCurrentDocument = PR_FALSE;
5637 mIsRestoringDocument = PR_TRUE;
5638 mLoadGroup->AddRequest(channel, nsnull);
5639 mIsRestoringDocument = PR_FALSE;
5643 if (!aTop) {
5644 // This point corresponds to us having gotten OnStartRequest or
5645 // STATE_START, so do the same thing that CreateContentViewer does at
5646 // this point to ensure that unload/pagehide events for this document
5647 // will fire when it's unloaded again.
5648 mFiredUnloadEvent = PR_FALSE;
5650 // For non-top frames, there is no notion of making sure that the
5651 // previous document is in the domwindow when STATE_START notifications
5652 // happen. We can just call BeginRestore for all of the child shells
5653 // now.
5654 rv = BeginRestoreChildren();
5655 NS_ENSURE_SUCCESS(rv, rv);
5658 return NS_OK;
5661 nsresult
5662 nsDocShell::BeginRestoreChildren()
5664 PRInt32 n = mChildList.Count();
5665 for (PRInt32 i = 0; i < n; ++i) {
5666 nsCOMPtr<nsIDocShell> child = do_QueryInterface(ChildAt(i));
5667 if (child) {
5668 nsresult rv = child->BeginRestore(nsnull, PR_FALSE);
5669 NS_ENSURE_SUCCESS(rv, rv);
5672 return NS_OK;
5675 NS_IMETHODIMP
5676 nsDocShell::FinishRestore()
5678 // First we call finishRestore() on our children. In the simulated load,
5679 // all of the child frames finish loading before the main document.
5681 PRInt32 n = mChildList.Count();
5682 for (PRInt32 i = 0; i < n; ++i) {
5683 nsCOMPtr<nsIDocShell> child = do_QueryInterface(ChildAt(i));
5684 if (child) {
5685 child->FinishRestore();
5689 if (mOSHE && mOSHE->HasDetachedEditor()) {
5690 ReattachEditorToWindow(mOSHE);
5693 if (mContentViewer) {
5694 nsCOMPtr<nsIDOMDocument> domDoc;
5695 mContentViewer->GetDOMDocument(getter_AddRefs(domDoc));
5697 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
5698 if (doc) {
5699 // Finally, we remove the request from the loadgroup. This will
5700 // cause onStateChange(STATE_STOP) to fire, which will fire the
5701 // pageshow event to the chrome.
5703 nsIChannel *channel = doc->GetChannel();
5704 if (channel) {
5705 mIsRestoringDocument = PR_TRUE;
5706 mLoadGroup->RemoveRequest(channel, nsnull, NS_OK);
5707 mIsRestoringDocument = PR_FALSE;
5712 return NS_OK;
5715 NS_IMETHODIMP
5716 nsDocShell::GetRestoringDocument(PRBool *aRestoring)
5718 *aRestoring = mIsRestoringDocument;
5719 return NS_OK;
5722 nsresult
5723 nsDocShell::RestorePresentation(nsISHEntry *aSHEntry, PRBool *aRestoring)
5725 NS_ASSERTION(mLoadType & LOAD_CMD_HISTORY,
5726 "RestorePresentation should only be called for history loads");
5728 nsCOMPtr<nsIContentViewer> viewer;
5729 aSHEntry->GetContentViewer(getter_AddRefs(viewer));
5731 #ifdef DEBUG_PAGE_CACHE
5732 nsCOMPtr<nsIURI> uri;
5733 aSHEntry->GetURI(getter_AddRefs(uri));
5735 nsCAutoString spec;
5736 if (uri)
5737 uri->GetSpec(spec);
5738 #endif
5740 *aRestoring = PR_FALSE;
5742 if (!viewer) {
5743 #ifdef DEBUG_PAGE_CACHE
5744 printf("no saved presentation for uri: %s\n", spec.get());
5745 #endif
5746 return NS_OK;
5749 // We need to make sure the content viewer's container is this docshell.
5750 // In subframe navigation, it's possible for the docshell that the
5751 // content viewer was originally loaded into to be replaced with a
5752 // different one. We don't currently support restoring the presentation
5753 // in that case.
5755 nsCOMPtr<nsISupports> container;
5756 viewer->GetContainer(getter_AddRefs(container));
5757 if (!::SameCOMIdentity(container, GetAsSupports(this))) {
5758 #ifdef DEBUG_PAGE_CACHE
5759 printf("No valid container, clearing presentation\n");
5760 #endif
5761 aSHEntry->SetContentViewer(nsnull);
5762 return NS_ERROR_FAILURE;
5765 NS_ASSERTION(mContentViewer != viewer, "Restoring existing presentation");
5767 #ifdef DEBUG_PAGE_CACHE
5768 printf("restoring presentation from session history: %s\n", spec.get());
5769 #endif
5771 SetHistoryEntry(&mLSHE, aSHEntry);
5773 // Add the request to our load group. We do this before swapping out
5774 // the content viewers so that consumers of STATE_START can access
5775 // the old document. We only deal with the toplevel load at this time --
5776 // to be consistent with normal document loading, subframes cannot start
5777 // loading until after data arrives, which is after STATE_START completes.
5779 BeginRestore(viewer, PR_TRUE);
5781 // Post an event that will remove the request after we've returned
5782 // to the event loop. This mimics the way it is called by nsIChannel
5783 // implementations.
5785 // Revoke any pending restore (just in case)
5786 NS_ASSERTION(!mRestorePresentationEvent.IsPending(),
5787 "should only have one RestorePresentationEvent");
5788 mRestorePresentationEvent.Revoke();
5790 nsRefPtr<RestorePresentationEvent> evt = new RestorePresentationEvent(this);
5791 nsresult rv = NS_DispatchToCurrentThread(evt);
5792 if (NS_SUCCEEDED(rv)) {
5793 mRestorePresentationEvent = evt.get();
5794 // The rest of the restore processing will happen on our event
5795 // callback.
5796 *aRestoring = PR_TRUE;
5799 return rv;
5802 nsresult
5803 nsDocShell::RestoreFromHistory()
5805 mRestorePresentationEvent.Forget();
5807 // This section of code follows the same ordering as CreateContentViewer.
5808 if (!mLSHE)
5809 return NS_ERROR_FAILURE;
5811 nsCOMPtr<nsIContentViewer> viewer;
5812 mLSHE->GetContentViewer(getter_AddRefs(viewer));
5813 if (!viewer)
5814 return NS_ERROR_FAILURE;
5816 if (mSavingOldViewer) {
5817 // We determined that it was safe to cache the document presentation
5818 // at the time we initiated the new load. We need to check whether
5819 // it's still safe to do so, since there may have been DOM mutations
5820 // or new requests initiated.
5821 nsCOMPtr<nsIDOMDocument> domDoc;
5822 viewer->GetDOMDocument(getter_AddRefs(domDoc));
5823 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
5824 nsIRequest *request = nsnull;
5825 if (doc)
5826 request = doc->GetChannel();
5827 mSavingOldViewer = CanSavePresentation(mLoadType, request, doc);
5830 nsCOMPtr<nsIMarkupDocumentViewer> oldMUDV(do_QueryInterface(mContentViewer));
5831 nsCOMPtr<nsIMarkupDocumentViewer> newMUDV(do_QueryInterface(viewer));
5832 float textZoom = 1.0f;
5833 float pageZoom = 1.0f;
5834 if (oldMUDV && newMUDV) {
5835 oldMUDV->GetTextZoom(&textZoom);
5836 oldMUDV->GetFullZoom(&pageZoom);
5839 // Protect against mLSHE going away via a load triggered from
5840 // pagehide or unload.
5841 nsCOMPtr<nsISHEntry> origLSHE = mLSHE;
5843 // Make sure to blow away our mLoadingURI just in case. No loads
5844 // from inside this pagehide.
5845 mLoadingURI = nsnull;
5847 // Notify the old content viewer that it's being hidden.
5848 FirePageHideNotification(!mSavingOldViewer);
5850 // If mLSHE was changed as a result of the pagehide event, then
5851 // something else was loaded. Don't finish restoring.
5852 if (mLSHE != origLSHE)
5853 return NS_OK;
5855 // Set mFiredUnloadEvent = PR_FALSE so that the unload handler for the
5856 // *new* document will fire.
5857 mFiredUnloadEvent = PR_FALSE;
5859 mURIResultedInDocument = PR_TRUE;
5860 nsCOMPtr<nsISHistory> rootSH;
5861 GetRootSessionHistory(getter_AddRefs(rootSH));
5862 if (rootSH) {
5863 nsCOMPtr<nsISHistoryInternal> hist = do_QueryInterface(rootSH);
5864 rootSH->GetIndex(&mPreviousTransIndex);
5865 hist->UpdateIndex();
5866 rootSH->GetIndex(&mLoadedTransIndex);
5867 #ifdef DEBUG_PAGE_CACHE
5868 printf("Previous index: %d, Loaded index: %d\n\n", mPreviousTransIndex,
5869 mLoadedTransIndex);
5870 #endif
5873 // Rather than call Embed(), we will retrieve the viewer from the session
5874 // history entry and swap it in.
5875 // XXX can we refactor this so that we can just call Embed()?
5876 PersistLayoutHistoryState();
5877 nsresult rv;
5878 if (mContentViewer) {
5879 if (mSavingOldViewer && NS_FAILED(CaptureState())) {
5880 if (mOSHE) {
5881 mOSHE->SyncPresentationState();
5883 mSavingOldViewer = PR_FALSE;
5887 mSavedRefreshURIList = nsnull;
5889 // In cases where we use a transient about:blank viewer between loads,
5890 // we never show the transient viewer, so _its_ previous viewer is never
5891 // unhooked from the view hierarchy. Destroy any such previous viewer now,
5892 // before we grab the root view sibling, so that we don't grab a view
5893 // that's about to go away.
5895 if (mContentViewer) {
5896 nsCOMPtr<nsIContentViewer> previousViewer;
5897 mContentViewer->GetPreviousViewer(getter_AddRefs(previousViewer));
5898 if (previousViewer) {
5899 mContentViewer->SetPreviousViewer(nsnull);
5900 previousViewer->Destroy();
5904 // Save off the root view's parent and sibling so that we can insert the
5905 // new content viewer's root view at the same position. Also save the
5906 // bounds of the root view's widget.
5908 nsIView *rootViewSibling = nsnull, *rootViewParent = nsnull;
5909 nsIntRect newBounds(0, 0, 0, 0);
5911 nsCOMPtr<nsIPresShell> oldPresShell;
5912 nsDocShell::GetPresShell(getter_AddRefs(oldPresShell));
5913 if (oldPresShell) {
5914 nsIViewManager *vm = oldPresShell->GetViewManager();
5915 if (vm) {
5916 nsIView *oldRootView = nsnull;
5917 vm->GetRootView(oldRootView);
5919 if (oldRootView) {
5920 rootViewSibling = oldRootView->GetNextSibling();
5921 rootViewParent = oldRootView->GetParent();
5923 nsIWidget *widget = oldRootView->GetWidget();
5924 if (widget) {
5925 widget->GetBounds(newBounds);
5931 // Transfer ownership to mContentViewer. By ensuring that either the
5932 // docshell or the session history, but not both, have references to the
5933 // content viewer, we prevent the viewer from being torn down after
5934 // Destroy() is called.
5936 if (mContentViewer) {
5937 mContentViewer->Close(mSavingOldViewer ? mOSHE.get() : nsnull);
5938 viewer->SetPreviousViewer(mContentViewer);
5941 mContentViewer.swap(viewer);
5942 viewer = nsnull; // force a release to complete ownership transfer
5944 // Grab all of the related presentation from the SHEntry now.
5945 // Clearing the viewer from the SHEntry will clear all of this state.
5946 nsCOMPtr<nsISupports> windowState;
5947 mLSHE->GetWindowState(getter_AddRefs(windowState));
5948 mLSHE->SetWindowState(nsnull);
5950 PRBool sticky;
5951 mLSHE->GetSticky(&sticky);
5953 nsCOMPtr<nsIDOMDocument> domDoc;
5954 mContentViewer->GetDOMDocument(getter_AddRefs(domDoc));
5956 nsCOMArray<nsIDocShellTreeItem> childShells;
5957 PRInt32 i = 0;
5958 nsCOMPtr<nsIDocShellTreeItem> child;
5959 while (NS_SUCCEEDED(mLSHE->ChildShellAt(i++, getter_AddRefs(child))) &&
5960 child) {
5961 childShells.AppendObject(child);
5964 // get the previous content viewer size
5965 nsIntRect oldBounds(0, 0, 0, 0);
5966 mLSHE->GetViewerBounds(oldBounds);
5968 // Restore the refresh URI list. The refresh timers will be restarted
5969 // when EndPageLoad() is called.
5970 nsCOMPtr<nsISupportsArray> refreshURIList;
5971 mLSHE->GetRefreshURIList(getter_AddRefs(refreshURIList));
5973 // Reattach to the window object.
5974 rv = mContentViewer->Open(windowState, mLSHE);
5976 // Now remove it from the cached presentation.
5977 mLSHE->SetContentViewer(nsnull);
5978 mEODForCurrentDocument = PR_FALSE;
5980 #ifdef DEBUG
5982 nsCOMPtr<nsISupportsArray> refreshURIs;
5983 mLSHE->GetRefreshURIList(getter_AddRefs(refreshURIs));
5984 nsCOMPtr<nsIDocShellTreeItem> childShell;
5985 mLSHE->ChildShellAt(0, getter_AddRefs(childShell));
5986 NS_ASSERTION(!refreshURIs && !childShell,
5987 "SHEntry should have cleared presentation state");
5989 #endif
5991 // Restore the sticky state of the viewer. The viewer has set this state
5992 // on the history entry in Destroy() just before marking itself non-sticky,
5993 // to avoid teardown of the presentation.
5994 mContentViewer->SetSticky(sticky);
5996 // Now that we have switched documents, forget all of our children.
5997 DestroyChildren();
5998 NS_ENSURE_SUCCESS(rv, rv);
6000 // mLSHE is now our currently-loaded document.
6001 SetHistoryEntry(&mOSHE, mLSHE);
6003 // XXX special wyciwyg handling in Embed()?
6005 // We aren't going to restore any items from the LayoutHistoryState,
6006 // but we don't want them to stay around in case the page is reloaded.
6007 SetLayoutHistoryState(nsnull);
6009 // This is the end of our Embed() replacement
6011 mSavingOldViewer = PR_FALSE;
6012 mEODForCurrentDocument = PR_FALSE;
6014 // Tell the event loop to favor plevents over user events, see comments
6015 // in CreateContentViewer.
6016 if (++gNumberOfDocumentsLoading == 1)
6017 FavorPerformanceHint(PR_TRUE, NS_EVENT_STARVATION_DELAY_HINT);
6020 if (oldMUDV && newMUDV) {
6021 newMUDV->SetTextZoom(textZoom);
6022 newMUDV->SetFullZoom(pageZoom);
6025 nsCOMPtr<nsIDocument> document = do_QueryInterface(domDoc);
6026 if (document) {
6027 // Use the uri from the mLSHE we had when we entered this function
6028 // (which need not match the document's URI if anchors are involved),
6029 // since that's the history entry we're loading. Note that if we use
6030 // origLSHE we don't have to worry about whether the entry in question
6031 // is still mLSHE or whether it's now mOSHE.
6032 nsCOMPtr<nsIURI> uri;
6033 origLSHE->GetURI(getter_AddRefs(uri));
6034 SetCurrentURI(uri, document->GetChannel(), PR_TRUE);
6037 // This is the end of our CreateContentViewer() replacement.
6038 // Now we simulate a load. First, we restore the state of the javascript
6039 // window object.
6040 nsCOMPtr<nsPIDOMWindow> privWin =
6041 do_GetInterface(static_cast<nsIInterfaceRequestor*>(this));
6042 NS_ASSERTION(privWin, "could not get nsPIDOMWindow interface");
6044 rv = privWin->RestoreWindowState(windowState);
6045 NS_ENSURE_SUCCESS(rv, rv);
6047 // Now, dispatch a title change event which would happen as the
6048 // <head> is parsed.
6049 document->NotifyPossibleTitleChange(PR_FALSE);
6051 // Now we simulate appending child docshells for subframes.
6052 for (i = 0; i < childShells.Count(); ++i) {
6053 nsIDocShellTreeItem *childItem = childShells.ObjectAt(i);
6054 nsCOMPtr<nsIDocShell> childShell = do_QueryInterface(childItem);
6056 // Make sure to not clobber the state of the child. Since AddChild
6057 // always clobbers it, save it off first.
6058 PRBool allowPlugins;
6059 childShell->GetAllowPlugins(&allowPlugins);
6061 PRBool allowJavascript;
6062 childShell->GetAllowJavascript(&allowJavascript);
6064 PRBool allowRedirects;
6065 childShell->GetAllowMetaRedirects(&allowRedirects);
6067 PRBool allowSubframes;
6068 childShell->GetAllowSubframes(&allowSubframes);
6070 PRBool allowImages;
6071 childShell->GetAllowImages(&allowImages);
6073 AddChild(childItem);
6075 childShell->SetAllowPlugins(allowPlugins);
6076 childShell->SetAllowJavascript(allowJavascript);
6077 childShell->SetAllowMetaRedirects(allowRedirects);
6078 childShell->SetAllowSubframes(allowSubframes);
6079 childShell->SetAllowImages(allowImages);
6081 rv = childShell->BeginRestore(nsnull, PR_FALSE);
6082 NS_ENSURE_SUCCESS(rv, rv);
6085 nsCOMPtr<nsIPresShell> shell;
6086 nsDocShell::GetPresShell(getter_AddRefs(shell));
6088 nsIViewManager *newVM = shell ? shell->GetViewManager() : nsnull;
6089 nsIView *newRootView = nsnull;
6090 if (newVM)
6091 newVM->GetRootView(newRootView);
6093 // Insert the new root view at the correct location in the view tree.
6094 if (rootViewParent) {
6095 nsIViewManager *parentVM = rootViewParent->GetViewManager();
6097 if (parentVM && newRootView) {
6098 // InsertChild(parent, child, sib, PR_TRUE) inserts the child after
6099 // sib in content order, which is before sib in view order. BUT
6100 // when sib is null it inserts at the end of the the document
6101 // order, i.e., first in view order. But when oldRootSibling is
6102 // null, the old root as at the end of the view list --- last in
6103 // content order --- and we want to call InsertChild(parent, child,
6104 // nsnull, PR_FALSE) in that case.
6105 parentVM->InsertChild(rootViewParent, newRootView,
6106 rootViewSibling,
6107 rootViewSibling ? PR_TRUE : PR_FALSE);
6109 NS_ASSERTION(newRootView->GetNextSibling() == rootViewSibling,
6110 "error in InsertChild");
6114 // Now that all of the child docshells have been put into place, we can
6115 // restart the timers for the window and all of the child frames.
6116 privWin->ResumeTimeouts();
6118 // Restore the refresh URI list. The refresh timers will be restarted
6119 // when EndPageLoad() is called.
6120 mRefreshURIList = refreshURIList;
6122 // Meta-refresh timers have been restarted for this shell, but not
6123 // for our children. Walk the child shells and restart their timers.
6124 PRInt32 n = mChildList.Count();
6125 for (i = 0; i < n; ++i) {
6126 nsCOMPtr<nsIDocShell> child = do_QueryInterface(ChildAt(i));
6127 if (child)
6128 child->ResumeRefreshURIs();
6131 // Make sure this presentation is the same size as the previous
6132 // presentation. If this is not the same size we showed it at last time,
6133 // then we need to resize the widget.
6135 // XXXbryner This interacts poorly with Firefox's infobar. If the old
6136 // presentation had the infobar visible, then we will resize the new
6137 // presentation to that smaller size. However, firing the locationchanged
6138 // event will hide the infobar, which will immediately resize the window
6139 // back to the larger size. A future optimization might be to restore
6140 // the presentation at the "wrong" size, then fire the locationchanged
6141 // event and check whether the docshell's new size is the same as the
6142 // cached viewer size (skipping the resize if they are equal).
6144 if (newRootView) {
6145 nsIWidget *widget = newRootView->GetWidget();
6146 if (widget && !newBounds.IsEmpty() && newBounds != oldBounds) {
6147 #ifdef DEBUG_PAGE_CACHE
6148 printf("resize widget(%d, %d, %d, %d)\n", newBounds.x,
6149 newBounds.y, newBounds.width, newBounds.height);
6150 #endif
6152 widget->Resize(newBounds.x, newBounds.y, newBounds.width,
6153 newBounds.height, PR_FALSE);
6157 // Simulate the completion of the load.
6158 nsDocShell::FinishRestore();
6160 // Restart plugins, and paint the content.
6161 if (shell)
6162 shell->Thaw();
6164 return privWin->FireDelayedDOMEvents();
6167 NS_IMETHODIMP
6168 nsDocShell::CreateContentViewer(const char *aContentType,
6169 nsIRequest * request,
6170 nsIStreamListener ** aContentHandler)
6172 *aContentHandler = nsnull;
6174 // Can we check the content type of the current content viewer
6175 // and reuse it without destroying it and re-creating it?
6177 NS_ASSERTION(mLoadGroup, "Someone ignored return from Init()?");
6179 // Instantiate the content viewer object
6180 nsCOMPtr<nsIContentViewer> viewer;
6181 nsresult rv = NewContentViewerObj(aContentType, request, mLoadGroup,
6182 aContentHandler, getter_AddRefs(viewer));
6184 if (NS_FAILED(rv))
6185 return NS_ERROR_FAILURE;
6187 // Notify the current document that it is about to be unloaded!!
6189 // It is important to fire the unload() notification *before* any state
6190 // is changed within the DocShell - otherwise, javascript will get the
6191 // wrong information :-(
6194 if (mSavingOldViewer) {
6195 // We determined that it was safe to cache the document presentation
6196 // at the time we initiated the new load. We need to check whether
6197 // it's still safe to do so, since there may have been DOM mutations
6198 // or new requests initiated.
6199 nsCOMPtr<nsIDOMDocument> domDoc;
6200 viewer->GetDOMDocument(getter_AddRefs(domDoc));
6201 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
6202 mSavingOldViewer = CanSavePresentation(mLoadType, request, doc);
6205 NS_ASSERTION(!mLoadingURI, "Re-entering unload?");
6207 nsCOMPtr<nsIChannel> aOpenedChannel = do_QueryInterface(request);
6208 if (aOpenedChannel) {
6209 aOpenedChannel->GetURI(getter_AddRefs(mLoadingURI));
6211 FirePageHideNotification(!mSavingOldViewer);
6212 mLoadingURI = nsnull;
6214 // Set mFiredUnloadEvent = PR_FALSE so that the unload handler for the
6215 // *new* document will fire.
6216 mFiredUnloadEvent = PR_FALSE;
6218 // we've created a new document so go ahead and call
6219 // OnLoadingSite(), but don't fire OnLocationChange()
6220 // notifications before we've called Embed(). See bug 284993.
6221 mURIResultedInDocument = PR_TRUE;
6223 PRBool onLocationChangeNeeded = OnLoadingSite(aOpenedChannel, PR_FALSE);
6225 // let's try resetting the load group if we need to...
6226 nsCOMPtr<nsILoadGroup> currentLoadGroup;
6227 NS_ENSURE_SUCCESS(aOpenedChannel->
6228 GetLoadGroup(getter_AddRefs(currentLoadGroup)),
6229 NS_ERROR_FAILURE);
6231 if (currentLoadGroup != mLoadGroup) {
6232 nsLoadFlags loadFlags = 0;
6234 //Cancel any URIs that are currently loading...
6235 /// XXX: Need to do this eventually Stop();
6237 // Retarget the document to this loadgroup...
6239 /* First attach the channel to the right loadgroup
6240 * and then remove from the old loadgroup. This
6241 * puts the notifications in the right order and
6242 * we don't null-out mLSHE in OnStateChange() for
6243 * all redirected urls
6245 aOpenedChannel->SetLoadGroup(mLoadGroup);
6247 // Mark the channel as being a document URI...
6248 aOpenedChannel->GetLoadFlags(&loadFlags);
6249 loadFlags |= nsIChannel::LOAD_DOCUMENT_URI;
6251 aOpenedChannel->SetLoadFlags(loadFlags);
6253 mLoadGroup->AddRequest(request, nsnull);
6254 if (currentLoadGroup)
6255 currentLoadGroup->RemoveRequest(request, nsnull,
6256 NS_BINDING_RETARGETED);
6258 // Update the notification callbacks, so that progress and
6259 // status information are sent to the right docshell...
6260 aOpenedChannel->SetNotificationCallbacks(this);
6263 NS_ENSURE_SUCCESS(Embed(viewer, "", (nsISupports *) nsnull),
6264 NS_ERROR_FAILURE);
6266 mSavedRefreshURIList = nsnull;
6267 mSavingOldViewer = PR_FALSE;
6268 mEODForCurrentDocument = PR_FALSE;
6270 // if this document is part of a multipart document,
6271 // the ID can be used to distinguish it from the other parts.
6272 nsCOMPtr<nsIMultiPartChannel> multiPartChannel(do_QueryInterface(request));
6273 if (multiPartChannel) {
6274 nsCOMPtr<nsIPresShell> shell;
6275 rv = GetPresShell(getter_AddRefs(shell));
6276 if (NS_SUCCEEDED(rv) && shell) {
6277 nsIDocument *doc = shell->GetDocument();
6278 if (doc) {
6279 PRUint32 partID;
6280 multiPartChannel->GetPartID(&partID);
6281 doc->SetPartID(partID);
6286 // Give hint to native plevent dispatch mechanism. If a document
6287 // is loading the native plevent dispatch mechanism should favor
6288 // performance over normal native event dispatch priorities.
6289 if (++gNumberOfDocumentsLoading == 1) {
6290 // Hint to favor performance for the plevent notification mechanism.
6291 // We want the pages to load as fast as possible even if its means
6292 // native messages might be starved.
6293 FavorPerformanceHint(PR_TRUE, NS_EVENT_STARVATION_DELAY_HINT);
6296 if (onLocationChangeNeeded) {
6297 FireOnLocationChange(this, request, mCurrentURI);
6300 return NS_OK;
6303 nsresult
6304 nsDocShell::NewContentViewerObj(const char *aContentType,
6305 nsIRequest * request, nsILoadGroup * aLoadGroup,
6306 nsIStreamListener ** aContentHandler,
6307 nsIContentViewer ** aViewer)
6309 nsCOMPtr<nsIChannel> aOpenedChannel = do_QueryInterface(request);
6311 nsresult rv;
6312 nsCOMPtr<nsICategoryManager> catMan(do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv));
6313 if (NS_FAILED(rv))
6314 return rv;
6316 nsXPIDLCString contractId;
6317 rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", aContentType, getter_Copies(contractId));
6319 // Create an instance of the document-loader-factory
6320 nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory;
6321 if (NS_SUCCEEDED(rv))
6322 docLoaderFactory = do_GetService(contractId.get());
6324 if (!docLoaderFactory) {
6325 return NS_ERROR_FAILURE;
6328 // Now create an instance of the content viewer
6329 // nsLayoutDLF makes the determination if it should be a "view-source" instead of "view"
6330 NS_ENSURE_SUCCESS(docLoaderFactory->CreateInstance("view",
6331 aOpenedChannel,
6332 aLoadGroup, aContentType,
6333 static_cast<nsIContentViewerContainer*>(this),
6334 nsnull,
6335 aContentHandler,
6336 aViewer),
6337 NS_ERROR_FAILURE);
6339 (*aViewer)->SetContainer(static_cast<nsIContentViewerContainer *>(this));
6340 return NS_OK;
6343 NS_IMETHODIMP
6344 nsDocShell::SetupNewViewer(nsIContentViewer * aNewViewer)
6347 // Copy content viewer state from previous or parent content viewer.
6349 // The following logic is mirrored in nsHTMLDocument::StartDocumentLoad!
6351 // Do NOT to maintain a reference to the old content viewer outside
6352 // of this "copying" block, or it will not be destroyed until the end of
6353 // this routine and all <SCRIPT>s and event handlers fail! (bug 20315)
6355 // In this block of code, if we get an error result, we return it
6356 // but if we get a null pointer, that's perfectly legal for parent
6357 // and parentContentViewer.
6360 PRInt32 x = 0;
6361 PRInt32 y = 0;
6362 PRInt32 cx = 0;
6363 PRInt32 cy = 0;
6365 // This will get the size from the current content viewer or from the
6366 // Init settings
6367 DoGetPositionAndSize(&x, &y, &cx, &cy);
6369 nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
6370 NS_ENSURE_SUCCESS(GetSameTypeParent(getter_AddRefs(parentAsItem)),
6371 NS_ERROR_FAILURE);
6372 nsCOMPtr<nsIDocShell> parent(do_QueryInterface(parentAsItem));
6374 nsCAutoString defaultCharset;
6375 nsCAutoString forceCharset;
6376 nsCAutoString hintCharset;
6377 PRInt32 hintCharsetSource;
6378 nsCAutoString prevDocCharset;
6379 float textZoom;
6380 float pageZoom;
6381 PRBool styleDisabled;
6382 // |newMUDV| also serves as a flag to set the data from the above vars
6383 nsCOMPtr<nsIMarkupDocumentViewer> newMUDV;
6385 if (mContentViewer || parent) {
6386 nsCOMPtr<nsIMarkupDocumentViewer> oldMUDV;
6387 if (mContentViewer) {
6388 // Get any interesting state from old content viewer
6389 // XXX: it would be far better to just reuse the document viewer ,
6390 // since we know we're just displaying the same document as before
6391 oldMUDV = do_QueryInterface(mContentViewer);
6393 // Tell the old content viewer to hibernate in session history when
6394 // it is destroyed.
6396 if (mSavingOldViewer && NS_FAILED(CaptureState())) {
6397 if (mOSHE) {
6398 mOSHE->SyncPresentationState();
6400 mSavingOldViewer = PR_FALSE;
6403 else {
6404 // No old content viewer, so get state from parent's content viewer
6405 nsCOMPtr<nsIContentViewer> parentContentViewer;
6406 parent->GetContentViewer(getter_AddRefs(parentContentViewer));
6407 oldMUDV = do_QueryInterface(parentContentViewer);
6410 if (oldMUDV) {
6411 nsresult rv;
6413 newMUDV = do_QueryInterface(aNewViewer,&rv);
6414 if (newMUDV) {
6415 NS_ENSURE_SUCCESS(oldMUDV->
6416 GetDefaultCharacterSet(defaultCharset),
6417 NS_ERROR_FAILURE);
6418 NS_ENSURE_SUCCESS(oldMUDV->
6419 GetForceCharacterSet(forceCharset),
6420 NS_ERROR_FAILURE);
6421 NS_ENSURE_SUCCESS(oldMUDV->
6422 GetHintCharacterSet(hintCharset),
6423 NS_ERROR_FAILURE);
6424 NS_ENSURE_SUCCESS(oldMUDV->
6425 GetHintCharacterSetSource(&hintCharsetSource),
6426 NS_ERROR_FAILURE);
6427 NS_ENSURE_SUCCESS(oldMUDV->
6428 GetTextZoom(&textZoom),
6429 NS_ERROR_FAILURE);
6430 NS_ENSURE_SUCCESS(oldMUDV->
6431 GetFullZoom(&pageZoom),
6432 NS_ERROR_FAILURE);
6433 NS_ENSURE_SUCCESS(oldMUDV->
6434 GetAuthorStyleDisabled(&styleDisabled),
6435 NS_ERROR_FAILURE);
6436 NS_ENSURE_SUCCESS(oldMUDV->
6437 GetPrevDocCharacterSet(prevDocCharset),
6438 NS_ERROR_FAILURE);
6443 // It is necessary to obtain the focus controller to utilize its ability
6444 // to suppress focus. This is necessary to fix Win32-only bugs related to
6445 // a loss of focus when mContentViewer is set to null. The internal window
6446 // is destroyed, and the OS focuses the parent window. This call ends up
6447 // notifying the focus controller that the outer window should focus
6448 // and this hoses us on any link traversal.
6450 // Please do not touch any of the focus controller code here without
6451 // testing bugs #28580 and 50509. These are immensely important bugs,
6452 // so PLEASE take care not to regress them if you decide to alter this
6453 // code later -- hyatt
6454 nsIFocusController *focusController = nsnull;
6455 if (mScriptGlobal) {
6456 nsCOMPtr<nsPIDOMWindow> ourWindow = do_QueryInterface(mScriptGlobal);
6457 focusController = ourWindow->GetRootFocusController();
6458 if (focusController) {
6459 // Suppress the command dispatcher.
6460 focusController->SetSuppressFocus(PR_TRUE,
6461 "Win32-Only Link Traversal Issue");
6462 // Remove focus from the element that has it
6463 nsCOMPtr<nsIDOMWindowInternal> focusedWindow;
6464 focusController->GetFocusedWindow(getter_AddRefs(focusedWindow));
6466 // We want to null out the last focused element if the document containing
6467 // it is going away. If the last focused element is in a descendent
6468 // window of our domwindow, its document will be destroyed when we
6469 // destroy our children. So, check for this case and null out the
6470 // last focused element. See bug 70484.
6472 PRBool isSubWindow = PR_FALSE;
6473 nsCOMPtr<nsIDOMWindow> curwin;
6474 if (focusedWindow)
6475 focusedWindow->GetParent(getter_AddRefs(curwin));
6476 while (curwin) {
6477 if (curwin == ourWindow) {
6478 isSubWindow = PR_TRUE;
6479 break;
6482 // don't use nsCOMPtr here to avoid extra addref
6483 // when assigning to curwin
6484 nsIDOMWindow* temp;
6485 curwin->GetParent(&temp);
6486 if (curwin == temp) {
6487 NS_RELEASE(temp);
6488 break;
6490 curwin = dont_AddRef(temp);
6493 if (ourWindow == focusedWindow || isSubWindow)
6494 focusController->ResetElementFocus();
6498 nscolor bgcolor = NS_RGBA(0, 0, 0, 0);
6499 PRBool bgSet = PR_FALSE;
6501 // Ensure that the content viewer is destroyed *after* the GC - bug 71515
6502 nsCOMPtr<nsIContentViewer> kungfuDeathGrip = mContentViewer;
6503 if (mContentViewer) {
6504 // Stop any activity that may be happening in the old document before
6505 // releasing it...
6506 mContentViewer->Stop();
6508 // Try to extract the default background color from the old
6509 // view manager, so we can use it for the next document.
6510 nsCOMPtr<nsIDocumentViewer> docviewer =
6511 do_QueryInterface(mContentViewer);
6513 if (docviewer) {
6514 nsCOMPtr<nsIPresShell> shell;
6515 docviewer->GetPresShell(getter_AddRefs(shell));
6517 if (shell) {
6518 nsIViewManager* vm = shell->GetViewManager();
6520 if (vm) {
6521 vm->GetDefaultBackgroundColor(&bgcolor);
6522 // If the background color is not known, don't propagate it.
6523 bgSet = NS_GET_A(bgcolor) != 0;
6528 mContentViewer->Close(mSavingOldViewer ? mOSHE.get() : nsnull);
6529 aNewViewer->SetPreviousViewer(mContentViewer);
6531 mContentViewer = nsnull;
6534 mContentViewer = aNewViewer;
6536 nsCOMPtr<nsIWidget> widget;
6537 NS_ENSURE_SUCCESS(GetMainWidget(getter_AddRefs(widget)), NS_ERROR_FAILURE);
6539 nsIntRect bounds(x, y, cx, cy);
6541 if (NS_FAILED(mContentViewer->Init(widget, bounds))) {
6542 mContentViewer = nsnull;
6543 NS_ERROR("ContentViewer Initialization failed");
6544 return NS_ERROR_FAILURE;
6547 // If we have old state to copy, set the old state onto the new content
6548 // viewer
6549 if (newMUDV) {
6550 NS_ENSURE_SUCCESS(newMUDV->SetDefaultCharacterSet(defaultCharset),
6551 NS_ERROR_FAILURE);
6552 NS_ENSURE_SUCCESS(newMUDV->SetForceCharacterSet(forceCharset),
6553 NS_ERROR_FAILURE);
6554 NS_ENSURE_SUCCESS(newMUDV->SetHintCharacterSet(hintCharset),
6555 NS_ERROR_FAILURE);
6556 NS_ENSURE_SUCCESS(newMUDV->
6557 SetHintCharacterSetSource(hintCharsetSource),
6558 NS_ERROR_FAILURE);
6559 NS_ENSURE_SUCCESS(newMUDV->SetPrevDocCharacterSet(prevDocCharset),
6560 NS_ERROR_FAILURE);
6561 NS_ENSURE_SUCCESS(newMUDV->SetTextZoom(textZoom),
6562 NS_ERROR_FAILURE);
6563 NS_ENSURE_SUCCESS(newMUDV->SetFullZoom(pageZoom),
6564 NS_ERROR_FAILURE);
6565 NS_ENSURE_SUCCESS(newMUDV->SetAuthorStyleDisabled(styleDisabled),
6566 NS_ERROR_FAILURE);
6569 // End copying block (Don't mess with the old content/document viewer
6570 // beyond here!!)
6572 // See the book I wrote above regarding why the focus controller is
6573 // being used here. -- hyatt
6575 /* Note it's important that focus suppression be turned off no earlier
6576 because in cases where the docshell is lazily creating an about:blank
6577 document, mContentViewer->Init finally puts a reference to that
6578 document into the DOM window, which prevents an infinite recursion
6579 attempting to lazily create the document as focus is unsuppressed
6580 (bug 110856). */
6581 if (focusController)
6582 focusController->SetSuppressFocus(PR_FALSE,
6583 "Win32-Only Link Traversal Issue");
6585 if (bgSet && widget) {
6586 // Stuff the bgcolor from the last view manager into the new
6587 // view manager. This improves page load continuity.
6588 nsCOMPtr<nsIDocumentViewer> docviewer =
6589 do_QueryInterface(mContentViewer);
6591 if (docviewer) {
6592 nsCOMPtr<nsIPresShell> shell;
6593 docviewer->GetPresShell(getter_AddRefs(shell));
6595 if (shell) {
6596 nsIViewManager* vm = shell->GetViewManager();
6598 if (vm) {
6599 vm->SetDefaultBackgroundColor(bgcolor);
6605 // XXX: It looks like the LayoutState gets restored again in Embed()
6606 // right after the call to SetupNewViewer(...)
6608 // We don't show the mContentViewer yet, since we want to draw the old page
6609 // until we have enough of the new page to show. Just return with the new
6610 // viewer still set to hidden.
6612 // Now that we have switched documents, forget all of our children
6613 DestroyChildren();
6615 return NS_OK;
6619 nsresult
6620 nsDocShell::CheckLoadingPermissions()
6622 // This method checks whether the caller may load content into
6623 // this docshell. Even though we've done our best to hide windows
6624 // from code that doesn't have the right to access them, it's
6625 // still possible for an evil site to open a window and access
6626 // frames in the new window through window.frames[] (which is
6627 // allAccess for historic reasons), so we still need to do this
6628 // check on load.
6629 nsresult rv = NS_OK, sameOrigin = NS_OK;
6631 if (!gValidateOrigin || !IsFrame()) {
6632 // Origin validation was turned off, or we're not a frame.
6633 // Permit all loads.
6635 return rv;
6638 // We're a frame. Check that the caller has write permission to
6639 // the parent before allowing it to load anything into this
6640 // docshell.
6642 nsCOMPtr<nsIScriptSecurityManager> securityManager =
6643 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
6644 NS_ENSURE_SUCCESS(rv, rv);
6646 PRBool ubwEnabled = PR_FALSE;
6647 rv = securityManager->IsCapabilityEnabled("UniversalBrowserWrite",
6648 &ubwEnabled);
6649 if (NS_FAILED(rv) || ubwEnabled) {
6650 return rv;
6653 nsCOMPtr<nsIPrincipal> subjPrincipal;
6654 rv = securityManager->GetSubjectPrincipal(getter_AddRefs(subjPrincipal));
6655 NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && subjPrincipal, rv);
6657 // Check if the caller is from the same origin as this docshell,
6658 // or any of it's ancestors.
6659 nsCOMPtr<nsIDocShellTreeItem> item(this);
6660 do {
6661 nsCOMPtr<nsIScriptGlobalObject> sgo(do_GetInterface(item));
6662 nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(sgo));
6664 nsIPrincipal *p;
6665 if (!sop || !(p = sop->GetPrincipal())) {
6666 return NS_ERROR_UNEXPECTED;
6669 // Compare origins
6670 PRBool equal;
6671 sameOrigin = subjPrincipal->Equals(p, &equal);
6672 if (NS_SUCCEEDED(sameOrigin)) {
6673 if (equal) {
6674 // Same origin, permit load
6676 return sameOrigin;
6679 sameOrigin = NS_ERROR_DOM_PROP_ACCESS_DENIED;
6682 nsCOMPtr<nsIDocShellTreeItem> tmp;
6683 item->GetSameTypeParent(getter_AddRefs(tmp));
6684 item.swap(tmp);
6685 } while (item);
6687 return sameOrigin;
6690 //*****************************************************************************
6691 // nsDocShell: Site Loading
6692 //*****************************************************************************
6693 class InternalLoadEvent : public nsRunnable
6695 public:
6696 InternalLoadEvent(nsDocShell* aDocShell, nsIURI * aURI, nsIURI * aReferrer,
6697 nsISupports * aOwner, PRUint32 aFlags,
6698 const char* aTypeHint, nsIInputStream * aPostData,
6699 nsIInputStream * aHeadersData, PRUint32 aLoadType,
6700 nsISHEntry * aSHEntry, PRBool aFirstParty) :
6701 mDocShell(aDocShell),
6702 mURI(aURI),
6703 mReferrer(aReferrer),
6704 mOwner(aOwner),
6705 mFlags(aFlags),
6706 mPostData(aPostData),
6707 mHeadersData(aHeadersData),
6708 mLoadType(aLoadType),
6709 mSHEntry(aSHEntry),
6710 mFirstParty(aFirstParty)
6712 // Make sure to keep null things null as needed
6713 if (aTypeHint) {
6714 mTypeHint = aTypeHint;
6718 NS_IMETHOD Run() {
6719 return mDocShell->InternalLoad(mURI, mReferrer, mOwner, mFlags,
6720 nsnull, mTypeHint.get(),
6721 mPostData, mHeadersData, mLoadType,
6722 mSHEntry, mFirstParty, nsnull, nsnull);
6725 private:
6726 nsRefPtr<nsDocShell> mDocShell;
6727 nsCOMPtr<nsIURI> mURI;
6728 nsCOMPtr<nsIURI> mReferrer;
6729 nsCOMPtr<nsISupports> mOwner;
6730 PRUint32 mFlags;
6732 // Use IDL strings so .get() returns null by default
6733 nsXPIDLString mWindowTarget;
6734 nsXPIDLCString mTypeHint;
6736 nsCOMPtr<nsIInputStream> mPostData;
6737 nsCOMPtr<nsIInputStream> mHeadersData;
6738 PRUint32 mLoadType;
6739 nsCOMPtr<nsISHEntry> mSHEntry;
6740 PRBool mFirstParty;
6743 NS_IMETHODIMP
6744 nsDocShell::InternalLoad(nsIURI * aURI,
6745 nsIURI * aReferrer,
6746 nsISupports * aOwner,
6747 PRUint32 aFlags,
6748 const PRUnichar *aWindowTarget,
6749 const char* aTypeHint,
6750 nsIInputStream * aPostData,
6751 nsIInputStream * aHeadersData,
6752 PRUint32 aLoadType,
6753 nsISHEntry * aSHEntry,
6754 PRBool aFirstParty,
6755 nsIDocShell** aDocShell,
6756 nsIRequest** aRequest)
6758 nsresult rv = NS_OK;
6760 #ifdef PR_LOGGING
6761 if (gDocShellLeakLog && PR_LOG_TEST(gDocShellLeakLog, PR_LOG_DEBUG)) {
6762 nsCAutoString spec;
6763 if (aURI)
6764 aURI->GetSpec(spec);
6765 PR_LogPrint("DOCSHELL %p InternalLoad %s\n", this, spec.get());
6767 #endif
6769 // Initialize aDocShell/aRequest
6770 if (aDocShell) {
6771 *aDocShell = nsnull;
6773 if (aRequest) {
6774 *aRequest = nsnull;
6777 if (!aURI) {
6778 return NS_ERROR_NULL_POINTER;
6781 NS_ENSURE_TRUE(IsValidLoadType(aLoadType), NS_ERROR_INVALID_ARG);
6783 NS_ENSURE_TRUE(!mIsBeingDestroyed, NS_ERROR_NOT_AVAILABLE);
6785 // wyciwyg urls can only be loaded through history. Any normal load of
6786 // wyciwyg through docshell is illegal. Disallow such loads.
6787 if (aLoadType & LOAD_CMD_NORMAL) {
6788 PRBool isWyciwyg = PR_FALSE;
6789 rv = aURI->SchemeIs("wyciwyg", &isWyciwyg);
6790 if ((isWyciwyg && NS_SUCCEEDED(rv)) || NS_FAILED(rv))
6791 return NS_ERROR_FAILURE;
6794 PRBool bIsJavascript = PR_FALSE;
6795 if (NS_FAILED(aURI->SchemeIs("javascript", &bIsJavascript))) {
6796 bIsJavascript = PR_FALSE;
6800 // First, notify any nsIContentPolicy listeners about the document load.
6801 // Only abort the load if a content policy listener explicitly vetos it!
6803 nsCOMPtr<nsIDOMElement> requestingElement;
6804 // Use nsPIDOMWindow since we _want_ to cross the chrome boundary if needed
6805 nsCOMPtr<nsPIDOMWindow> privateWin(do_QueryInterface(mScriptGlobal));
6806 if (privateWin)
6807 requestingElement = privateWin->GetFrameElementInternal();
6809 PRInt16 shouldLoad = nsIContentPolicy::ACCEPT;
6810 PRUint32 contentType;
6811 if (IsFrame()) {
6812 NS_ASSERTION(requestingElement, "A frame but no DOM element!?");
6813 contentType = nsIContentPolicy::TYPE_SUBDOCUMENT;
6814 } else {
6815 contentType = nsIContentPolicy::TYPE_DOCUMENT;
6818 nsISupports* context = requestingElement;
6819 if (!context) {
6820 context = mScriptGlobal;
6823 // XXXbz would be nice to know the loading principal here... but we don't
6824 nsCOMPtr<nsIPrincipal> loadingPrincipal;
6825 if (aReferrer) {
6826 nsCOMPtr<nsIScriptSecurityManager> secMan =
6827 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
6828 NS_ENSURE_SUCCESS(rv, rv);
6830 rv = secMan->GetCodebasePrincipal(aReferrer,
6831 getter_AddRefs(loadingPrincipal));
6834 rv = NS_CheckContentLoadPolicy(contentType,
6835 aURI,
6836 loadingPrincipal,
6837 context,
6838 EmptyCString(), //mime guess
6839 nsnull, //extra
6840 &shouldLoad);
6842 if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
6843 if (NS_SUCCEEDED(rv) && shouldLoad == nsIContentPolicy::REJECT_TYPE) {
6844 return NS_ERROR_CONTENT_BLOCKED_SHOW_ALT;
6847 return NS_ERROR_CONTENT_BLOCKED;
6850 nsCOMPtr<nsISupports> owner(aOwner);
6852 // Get an owner from the current document if necessary. Note that we only
6853 // do this for URIs that inherit a security context and local file URIs;
6854 // in particular we do NOT do this for about:blank. This way, random
6855 // about:blank loads that have no owner (which basically means they were
6856 // done by someone from chrome manually messing with our nsIWebNavigation
6857 // or by C++ setting document.location) don't get a funky principal. If
6858 // callers want something interesting to happen with the about:blank
6859 // principal in this case, they should pass an owner in.
6862 PRBool inherits;
6863 // One more twist: Don't inherit the owner for external loads.
6864 if (aLoadType != LOAD_NORMAL_EXTERNAL && !owner &&
6865 (aFlags & INTERNAL_LOAD_FLAGS_INHERIT_OWNER) &&
6866 ((NS_SUCCEEDED(URIInheritsSecurityContext(aURI, &inherits)) &&
6867 inherits) || URIIsLocalFile(aURI))) {
6869 // Don't allow loads that would inherit our security context
6870 // if this document came from an unsafe channel.
6871 nsCOMPtr<nsIDocShellTreeItem> treeItem = this;
6872 do {
6873 nsCOMPtr<nsIDocShell> itemDocShell =
6874 do_QueryInterface(treeItem);
6875 PRBool isUnsafe;
6876 if (itemDocShell &&
6877 NS_SUCCEEDED(itemDocShell->GetChannelIsUnsafe(&isUnsafe)) &&
6878 isUnsafe) {
6879 return NS_ERROR_DOM_SECURITY_ERR;
6882 nsCOMPtr<nsIDocShellTreeItem> parent;
6883 treeItem->GetSameTypeParent(getter_AddRefs(parent));
6884 parent.swap(treeItem);
6885 } while (treeItem);
6887 owner = GetInheritedPrincipal(PR_TRUE);
6892 // Resolve the window target before going any further...
6893 // If the load has been targeted to another DocShell, then transfer the
6894 // load to it...
6896 if (aWindowTarget && *aWindowTarget) {
6897 // We've already done our owner-inheriting. Mask out that bit, so we
6898 // don't try inheriting an owner from the target window if we came up
6899 // with a null owner above.
6900 aFlags = aFlags & ~INTERNAL_LOAD_FLAGS_INHERIT_OWNER;
6902 // Locate the target DocShell.
6903 // This may involve creating a new toplevel window - if necessary.
6905 nsCOMPtr<nsIDocShellTreeItem> targetItem;
6906 FindItemWithName(aWindowTarget, nsnull, this,
6907 getter_AddRefs(targetItem));
6909 nsCOMPtr<nsIDocShell> targetDocShell = do_QueryInterface(targetItem);
6911 PRBool isNewWindow = PR_FALSE;
6912 if (!targetDocShell) {
6913 nsCOMPtr<nsIDOMWindowInternal> win =
6914 do_GetInterface(GetAsSupports(this));
6915 NS_ENSURE_TRUE(win, NS_ERROR_NOT_AVAILABLE);
6917 nsDependentString name(aWindowTarget);
6918 nsCOMPtr<nsIDOMWindow> newWin;
6919 rv = win->Open(EmptyString(), // URL to load
6920 name, // window name
6921 EmptyString(), // Features
6922 getter_AddRefs(newWin));
6924 // In some cases the Open call doesn't actually result in a new
6925 // window being opened. We can detect these cases by examining the
6926 // document in |newWin|, if any.
6927 nsCOMPtr<nsPIDOMWindow> piNewWin = do_QueryInterface(newWin);
6928 if (piNewWin) {
6929 nsCOMPtr<nsIDocument> newDoc =
6930 do_QueryInterface(piNewWin->GetExtantDocument());
6931 if (!newDoc || newDoc->IsInitialDocument()) {
6932 isNewWindow = PR_TRUE;
6933 aFlags |= INTERNAL_LOAD_FLAGS_FIRST_LOAD;
6937 nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(newWin);
6938 targetDocShell = do_QueryInterface(webNav);
6942 // Transfer the load to the target DocShell... Pass nsnull as the
6943 // window target name from to prevent recursive retargeting!
6945 if (NS_SUCCEEDED(rv) && targetDocShell) {
6946 rv = targetDocShell->InternalLoad(aURI,
6947 aReferrer,
6948 owner,
6949 aFlags,
6950 nsnull, // No window target
6951 aTypeHint,
6952 aPostData,
6953 aHeadersData,
6954 aLoadType,
6955 aSHEntry,
6956 aFirstParty,
6957 aDocShell,
6958 aRequest);
6959 if (rv == NS_ERROR_NO_CONTENT) {
6960 // XXXbz except we never reach this code!
6961 if (isNewWindow) {
6963 // At this point, a new window has been created, but the
6964 // URI did not have any data associated with it...
6966 // So, the best we can do, is to tear down the new window
6967 // that was just created!
6969 nsCOMPtr<nsIDOMWindowInternal> domWin =
6970 do_GetInterface(targetDocShell);
6971 if (domWin) {
6972 domWin->Close();
6976 // NS_ERROR_NO_CONTENT should not be returned to the
6977 // caller... This is an internal error code indicating that
6978 // the URI had no data associated with it - probably a
6979 // helper-app style protocol (ie. mailto://)
6981 rv = NS_OK;
6983 else if (isNewWindow) {
6984 // XXX: Once new windows are created hidden, the new
6985 // window will need to be made visible... For now,
6986 // do nothing.
6990 // Else we ran out of memory, or were a popup and got blocked,
6991 // or something.
6993 return rv;
6997 // Load is being targetted at this docshell so return an error if the
6998 // docshell is in the process of being destroyed.
7000 if (mIsBeingDestroyed) {
7001 return NS_ERROR_FAILURE;
7004 rv = CheckLoadingPermissions();
7005 if (NS_FAILED(rv)) {
7006 return rv;
7009 // If this docshell is owned by a frameloader, make sure to cancel
7010 // possible frameloader initialization before loading a new page.
7011 nsCOMPtr<nsIDocShellTreeItem> parent;
7012 GetParent(getter_AddRefs(parent));
7013 if (parent) {
7014 nsCOMPtr<nsIDOMDocument> domDoc = do_GetInterface(parent);
7015 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
7016 if (doc) {
7017 doc->TryCancelFrameLoaderInitialization(this);
7021 if (mFiredUnloadEvent) {
7022 if (IsOKToLoadURI(aURI)) {
7023 NS_PRECONDITION(!aWindowTarget || !*aWindowTarget,
7024 "Shouldn't have a window target here!");
7026 // If this is a replace load, make whatever load triggered
7027 // the unload event also a replace load, so we don't
7028 // create extra history entries.
7029 if (LOAD_TYPE_HAS_FLAGS(aLoadType, LOAD_FLAGS_REPLACE_HISTORY)) {
7030 mLoadType = LOAD_NORMAL_REPLACE;
7033 // Do this asynchronously
7034 nsCOMPtr<nsIRunnable> ev =
7035 new InternalLoadEvent(this, aURI, aReferrer, aOwner, aFlags,
7036 aTypeHint, aPostData, aHeadersData,
7037 aLoadType, aSHEntry, aFirstParty);
7038 return NS_DispatchToCurrentThread(ev);
7041 // Just ignore this load attempt
7042 return NS_OK;
7045 // Before going any further vet loads initiated by external programs.
7046 if (aLoadType == LOAD_NORMAL_EXTERNAL) {
7047 // Disallow external chrome: loads targetted at content windows
7048 PRBool isChrome = PR_FALSE;
7049 if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &isChrome)) && isChrome) {
7050 NS_WARNING("blocked external chrome: url -- use '-chrome' option");
7051 return NS_ERROR_FAILURE;
7054 // clear the decks to prevent context bleed-through (bug 298255)
7055 rv = CreateAboutBlankContentViewer(nsnull);
7056 if (NS_FAILED(rv))
7057 return NS_ERROR_FAILURE;
7059 // reset loadType so we don't have to add lots of tests for
7060 // LOAD_NORMAL_EXTERNAL after this point
7061 aLoadType = LOAD_NORMAL;
7064 mAllowKeywordFixup =
7065 (aFlags & INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) != 0;
7066 mURIResultedInDocument = PR_FALSE; // reset the clock...
7069 // First:
7070 // Check to see if the new URI is an anchor in the existing document.
7071 // Skip this check if we're doing some sort of abnormal load, if the
7072 // new load is a non-history load and has postdata, or if we're doing
7073 // a history load and the page identifiers of mOSHE and aSHEntry
7074 // don't match.
7076 PRBool allowScroll = PR_TRUE;
7077 if (!aSHEntry) {
7078 allowScroll = (aPostData == nsnull);
7079 } else if (mOSHE) {
7080 PRUint32 ourPageIdent;
7081 mOSHE->GetPageIdentifier(&ourPageIdent);
7082 PRUint32 otherPageIdent;
7083 aSHEntry->GetPageIdentifier(&otherPageIdent);
7084 allowScroll = (ourPageIdent == otherPageIdent);
7085 #ifdef DEBUG
7086 if (allowScroll) {
7087 nsCOMPtr<nsIInputStream> currentPostData;
7088 mOSHE->GetPostData(getter_AddRefs(currentPostData));
7089 NS_ASSERTION(currentPostData == aPostData,
7090 "Different POST data for entries for the same page?");
7092 #endif
7095 if ((aLoadType == LOAD_NORMAL ||
7096 aLoadType == LOAD_STOP_CONTENT ||
7097 LOAD_TYPE_HAS_FLAGS(aLoadType, LOAD_FLAGS_REPLACE_HISTORY) ||
7098 aLoadType == LOAD_HISTORY ||
7099 aLoadType == LOAD_LINK) && allowScroll) {
7100 PRBool wasAnchor = PR_FALSE;
7101 nscoord cx, cy;
7102 NS_ENSURE_SUCCESS(ScrollIfAnchor(aURI, &wasAnchor, aLoadType, &cx, &cy), NS_ERROR_FAILURE);
7103 if (wasAnchor) {
7104 mLoadType = aLoadType;
7105 mURIResultedInDocument = PR_TRUE;
7107 /* we need to assign mLSHE to aSHEntry right here, so that on History loads,
7108 * SetCurrentURI() called from OnNewURI() will send proper
7109 * onLocationChange() notifications to the browser to update
7110 * back/forward buttons.
7112 SetHistoryEntry(&mLSHE, aSHEntry);
7114 /* This is a anchor traversal with in the same page.
7115 * call OnNewURI() so that, this traversal will be
7116 * recorded in session and global history.
7118 nsCOMPtr<nsISupports> owner;
7119 if (mOSHE) {
7120 mOSHE->GetOwner(getter_AddRefs(owner));
7122 OnNewURI(aURI, nsnull, owner, mLoadType, PR_TRUE);
7123 nsCOMPtr<nsIInputStream> postData;
7124 PRUint32 pageIdent = PR_UINT32_MAX;
7125 nsCOMPtr<nsISupports> cacheKey;
7127 if (mOSHE) {
7128 /* save current position of scroller(s) (bug 59774) */
7129 mOSHE->SetScrollPosition(cx, cy);
7130 // Get the postdata and page ident from the current page, if
7131 // the new load is being done via normal means. Note that
7132 // "normal means" can be checked for just by checking for
7133 // LOAD_CMD_NORMAL, given the loadType and allowScroll check
7134 // above -- it filters out some LOAD_CMD_NORMAL cases that we
7135 // wouldn't want here.
7136 if (aLoadType & LOAD_CMD_NORMAL) {
7137 mOSHE->GetPostData(getter_AddRefs(postData));
7138 mOSHE->GetPageIdentifier(&pageIdent);
7139 mOSHE->GetCacheKey(getter_AddRefs(cacheKey));
7143 /* Assign mOSHE to mLSHE. This will either be a new entry created
7144 * by OnNewURI() for normal loads or aSHEntry for history loads.
7146 if (mLSHE) {
7147 SetHistoryEntry(&mOSHE, mLSHE);
7148 // Save the postData obtained from the previous page
7149 // in to the session history entry created for the
7150 // anchor page, so that any history load of the anchor
7151 // page will restore the appropriate postData.
7152 if (postData)
7153 mOSHE->SetPostData(postData);
7155 // Make sure we won't just repost without hitting the
7156 // cache first
7157 if (cacheKey)
7158 mOSHE->SetCacheKey(cacheKey);
7160 // Propagate our page ident to the new mOSHE so that
7161 // we'll know it just differed by a scroll on the page.
7162 if (pageIdent != PR_UINT32_MAX)
7163 mOSHE->SetPageIdentifier(pageIdent);
7166 /* restore previous position of scroller(s), if we're moving
7167 * back in history (bug 59774)
7169 if (mOSHE && (aLoadType == LOAD_HISTORY || aLoadType == LOAD_RELOAD_NORMAL))
7171 nscoord bx, by;
7172 mOSHE->GetScrollPosition(&bx, &by);
7173 SetCurScrollPosEx(bx, by);
7176 /* Clear out mLSHE so that further anchor visits get
7177 * recorded in SH and SH won't misbehave.
7179 SetHistoryEntry(&mLSHE, nsnull);
7180 /* Set the title for the SH entry for this target url. so that
7181 * SH menus in go/back/forward buttons won't be empty for this.
7183 if (mSessionHistory) {
7184 PRInt32 index = -1;
7185 mSessionHistory->GetIndex(&index);
7186 nsCOMPtr<nsIHistoryEntry> hEntry;
7187 mSessionHistory->GetEntryAtIndex(index, PR_FALSE,
7188 getter_AddRefs(hEntry));
7189 NS_ENSURE_TRUE(hEntry, NS_ERROR_FAILURE);
7190 nsCOMPtr<nsISHEntry> shEntry(do_QueryInterface(hEntry));
7191 if (shEntry)
7192 shEntry->SetTitle(mTitle);
7195 return NS_OK;
7199 // mContentViewer->PermitUnload can destroy |this| docShell, which
7200 // causes the next call of CanSavePresentation to crash.
7201 // Hold onto |this| until we return, to prevent a crash from happening.
7202 // (bug#331040)
7203 nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
7205 // Check if the page doesn't want to be unloaded. The javascript:
7206 // protocol handler deals with this for javascript: URLs.
7207 if (!bIsJavascript && mContentViewer) {
7208 PRBool okToUnload;
7209 rv = mContentViewer->PermitUnload(&okToUnload);
7211 if (NS_SUCCEEDED(rv) && !okToUnload) {
7212 // The user chose not to unload the page, interrupt the
7213 // load.
7214 return NS_OK;
7218 // Check for saving the presentation here, before calling Stop().
7219 // This is necessary so that we can catch any pending requests.
7220 // Since the new request has not been created yet, we pass null for the
7221 // new request parameter.
7222 // Also pass nsnull for the document, since it doesn't affect the return
7223 // value for our purposes here.
7224 PRBool savePresentation = CanSavePresentation(aLoadType, nsnull, nsnull);
7226 // Don't stop current network activity for javascript: URL's since
7227 // they might not result in any data, and thus nothing should be
7228 // stopped in those cases. In the case where they do result in
7229 // data, the javascript: URL channel takes care of stopping
7230 // current network activity.
7231 if (!bIsJavascript) {
7232 // Stop any current network activity.
7233 // Also stop content if this is a zombie doc. otherwise
7234 // the onload will be delayed by other loads initiated in the
7235 // background by the first document that
7236 // didn't fully load before the next load was initiated.
7237 // If not a zombie, don't stop content until data
7238 // starts arriving from the new URI...
7240 nsCOMPtr<nsIContentViewer> zombieViewer;
7241 if (mContentViewer) {
7242 mContentViewer->GetPreviousViewer(getter_AddRefs(zombieViewer));
7245 if (zombieViewer ||
7246 LOAD_TYPE_HAS_FLAGS(aLoadType, LOAD_FLAGS_STOP_CONTENT)) {
7247 rv = Stop(nsIWebNavigation::STOP_ALL);
7248 } else {
7249 rv = Stop(nsIWebNavigation::STOP_NETWORK);
7252 if (NS_FAILED(rv))
7253 return rv;
7256 mLoadType = aLoadType;
7258 // mLSHE should be assigned to aSHEntry, only after Stop() has
7259 // been called. But when loading an error page, do not clear the
7260 // mLSHE for the real page.
7261 if (mLoadType != LOAD_ERROR_PAGE)
7262 SetHistoryEntry(&mLSHE, aSHEntry);
7264 mSavingOldViewer = savePresentation;
7266 // If we have a saved content viewer in history, restore and show it now.
7267 if (aSHEntry && (mLoadType & LOAD_CMD_HISTORY)) {
7268 // It's possible that the previous viewer of mContentViewer is the
7269 // viewer that will end up in aSHEntry when it gets closed. If that's
7270 // the case, we need to go ahead and force it into its shentry so we
7271 // can restore it.
7272 if (mContentViewer) {
7273 nsCOMPtr<nsIContentViewer> prevViewer;
7274 mContentViewer->GetPreviousViewer(getter_AddRefs(prevViewer));
7275 if (prevViewer) {
7276 #ifdef DEBUG
7277 nsCOMPtr<nsIContentViewer> prevPrevViewer;
7278 prevViewer->GetPreviousViewer(getter_AddRefs(prevPrevViewer));
7279 NS_ASSERTION(!prevPrevViewer, "Should never have viewer chain here");
7280 #endif
7281 nsCOMPtr<nsISHEntry> viewerEntry;
7282 prevViewer->GetHistoryEntry(getter_AddRefs(viewerEntry));
7283 if (viewerEntry == aSHEntry) {
7284 // Make sure this viewer ends up in the right place
7285 mContentViewer->SetPreviousViewer(nsnull);
7286 prevViewer->Destroy();
7290 nsCOMPtr<nsISHEntry> oldEntry = mOSHE;
7291 PRBool restoring;
7292 rv = RestorePresentation(aSHEntry, &restoring);
7293 if (restoring)
7294 return rv;
7296 // We failed to restore the presentation, so clean up.
7297 // Both the old and new history entries could potentially be in
7298 // an inconsistent state.
7299 if (NS_FAILED(rv)) {
7300 if (oldEntry)
7301 oldEntry->SyncPresentationState();
7303 aSHEntry->SyncPresentationState();
7307 nsCOMPtr<nsIRequest> req;
7308 rv = DoURILoad(aURI, aReferrer,
7309 !(aFlags & INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER),
7310 owner, aTypeHint, aPostData, aHeadersData, aFirstParty,
7311 aDocShell, getter_AddRefs(req),
7312 (aFlags & INTERNAL_LOAD_FLAGS_FIRST_LOAD) != 0,
7313 (aFlags & INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER) != 0);
7314 if (req && aRequest)
7315 NS_ADDREF(*aRequest = req);
7317 if (NS_FAILED(rv)) {
7318 nsCOMPtr<nsIChannel> chan(do_QueryInterface(req));
7319 DisplayLoadError(rv, aURI, nsnull, chan);
7322 return rv;
7325 nsIPrincipal*
7326 nsDocShell::GetInheritedPrincipal(PRBool aConsiderCurrentDocument)
7328 nsCOMPtr<nsIDocument> document;
7330 if (aConsiderCurrentDocument && mContentViewer) {
7331 nsCOMPtr<nsIDocumentViewer>
7332 docViewer(do_QueryInterface(mContentViewer));
7333 if (!docViewer)
7334 return nsnull;
7335 docViewer->GetDocument(getter_AddRefs(document));
7338 if (!document) {
7339 nsCOMPtr<nsIDocShellTreeItem> parentItem;
7340 GetSameTypeParent(getter_AddRefs(parentItem));
7341 if (parentItem) {
7342 nsCOMPtr<nsIDOMDocument> parentDomDoc(do_GetInterface(parentItem));
7343 document = do_QueryInterface(parentDomDoc);
7347 if (!document) {
7348 if (!aConsiderCurrentDocument) {
7349 return nsnull;
7352 // Make sure we end up with _something_ as the principal no matter
7353 // what.
7354 EnsureContentViewer(); // If this fails, we'll just get a null
7355 // docViewer and bail.
7357 nsCOMPtr<nsIDocumentViewer>
7358 docViewer(do_QueryInterface(mContentViewer));
7359 if (!docViewer)
7360 return nsnull;
7361 docViewer->GetDocument(getter_AddRefs(document));
7364 //-- Get the document's principal
7365 if (document) {
7366 return document->NodePrincipal();
7369 return nsnull;
7372 PRBool
7373 nsDocShell::ShouldCheckAppCache(nsIURI *aURI)
7375 nsCOMPtr<nsIOfflineCacheUpdateService> offlineService =
7376 do_GetService(NS_OFFLINECACHEUPDATESERVICE_CONTRACTID);
7377 if (!offlineService) {
7378 return PR_FALSE;
7381 PRBool allowed;
7382 nsresult rv = offlineService->OfflineAppAllowedForURI(aURI,
7383 nsnull,
7384 &allowed);
7385 return NS_SUCCEEDED(rv) && allowed;
7388 nsresult
7389 nsDocShell::DoURILoad(nsIURI * aURI,
7390 nsIURI * aReferrerURI,
7391 PRBool aSendReferrer,
7392 nsISupports * aOwner,
7393 const char * aTypeHint,
7394 nsIInputStream * aPostData,
7395 nsIInputStream * aHeadersData,
7396 PRBool aFirstParty,
7397 nsIDocShell ** aDocShell,
7398 nsIRequest ** aRequest,
7399 PRBool aIsNewWindowTarget,
7400 PRBool aBypassClassifier)
7402 nsresult rv;
7403 nsCOMPtr<nsIURILoader> uriLoader;
7405 uriLoader = do_GetService(NS_URI_LOADER_CONTRACTID, &rv);
7406 if (NS_FAILED(rv)) return rv;
7408 nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL;
7409 if (aFirstParty) {
7410 // tag first party URL loads
7411 loadFlags |= nsIChannel::LOAD_INITIAL_DOCUMENT_URI;
7414 if (mLoadType == LOAD_ERROR_PAGE) {
7415 // Error pages are LOAD_BACKGROUND
7416 loadFlags |= nsIChannel::LOAD_BACKGROUND;
7419 // open a channel for the url
7420 nsCOMPtr<nsIChannel> channel;
7422 rv = NS_NewChannel(getter_AddRefs(channel),
7423 aURI,
7424 nsnull,
7425 nsnull,
7426 static_cast<nsIInterfaceRequestor *>(this),
7427 loadFlags);
7428 if (NS_FAILED(rv)) {
7429 if (rv == NS_ERROR_UNKNOWN_PROTOCOL) {
7430 // This is a uri with a protocol scheme we don't know how
7431 // to handle. Embedders might still be interested in
7432 // handling the load, though, so we fire a notification
7433 // before throwing the load away.
7434 PRBool abort = PR_FALSE;
7435 nsresult rv2 = mContentListener->OnStartURIOpen(aURI, &abort);
7436 if (NS_SUCCEEDED(rv2) && abort) {
7437 // Hey, they're handling the load for us! How convenient!
7438 return NS_OK;
7442 return rv;
7445 nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
7446 do_QueryInterface(channel);
7447 if (appCacheChannel) {
7448 // Any document load should not inherit application cache.
7449 appCacheChannel->SetInheritApplicationCache(PR_FALSE);
7451 // Loads with the correct permissions should check for a matching
7452 // application cache.
7453 appCacheChannel->SetChooseApplicationCache(ShouldCheckAppCache(aURI));
7456 // Make sure to give the caller a channel if we managed to create one
7457 // This is important for correct error page/session history interaction
7458 if (aRequest)
7459 NS_ADDREF(*aRequest = channel);
7461 channel->SetOriginalURI(aURI);
7462 if (aTypeHint && *aTypeHint) {
7463 channel->SetContentType(nsDependentCString(aTypeHint));
7464 mContentTypeHint = aTypeHint;
7466 else {
7467 mContentTypeHint.Truncate();
7470 //hack
7471 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
7472 nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal(do_QueryInterface(channel));
7473 if (httpChannelInternal) {
7474 if (aFirstParty) {
7475 httpChannelInternal->SetDocumentURI(aURI);
7476 } else {
7477 httpChannelInternal->SetDocumentURI(aReferrerURI);
7481 nsCOMPtr<nsIWritablePropertyBag2> props(do_QueryInterface(channel));
7482 if (props)
7484 // save true referrer for those who need it (e.g. xpinstall whitelisting)
7485 // Currently only http and ftp channels support this.
7486 props->SetPropertyAsInterface(NS_LITERAL_STRING("docshell.internalReferrer"),
7487 aReferrerURI);
7491 // If this is a HTTP channel, then set up the HTTP specific information
7492 // (ie. POST data, referrer, ...)
7494 if (httpChannel) {
7495 nsCOMPtr<nsICachingChannel> cacheChannel(do_QueryInterface(httpChannel));
7496 /* Get the cache Key from SH */
7497 nsCOMPtr<nsISupports> cacheKey;
7498 if (mLSHE) {
7499 mLSHE->GetCacheKey(getter_AddRefs(cacheKey));
7501 else if (mOSHE) // for reload cases
7502 mOSHE->GetCacheKey(getter_AddRefs(cacheKey));
7504 // figure out if we need to set the post data stream on the channel...
7505 // right now, this is only done for http channels.....
7506 if (aPostData) {
7507 // XXX it's a bit of a hack to rewind the postdata stream here but
7508 // it has to be done in case the post data is being reused multiple
7509 // times.
7510 nsCOMPtr<nsISeekableStream>
7511 postDataSeekable(do_QueryInterface(aPostData));
7512 if (postDataSeekable) {
7513 rv = postDataSeekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
7514 NS_ENSURE_SUCCESS(rv, rv);
7517 nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
7518 NS_ASSERTION(uploadChannel, "http must support nsIUploadChannel");
7520 // we really need to have a content type associated with this stream!!
7521 uploadChannel->SetUploadStream(aPostData, EmptyCString(), -1);
7522 /* If there is a valid postdata *and* it is a History Load,
7523 * set up the cache key on the channel, to retrieve the
7524 * data *only* from the cache. If it is a normal reload, the
7525 * cache is free to go to the server for updated postdata.
7527 if (cacheChannel && cacheKey) {
7528 if (mLoadType == LOAD_HISTORY || mLoadType == LOAD_RELOAD_CHARSET_CHANGE) {
7529 cacheChannel->SetCacheKey(cacheKey);
7530 PRUint32 loadFlags;
7531 if (NS_SUCCEEDED(channel->GetLoadFlags(&loadFlags)))
7532 channel->SetLoadFlags(loadFlags | nsICachingChannel::LOAD_ONLY_FROM_CACHE);
7534 else if (mLoadType == LOAD_RELOAD_NORMAL)
7535 cacheChannel->SetCacheKey(cacheKey);
7538 else {
7539 /* If there is no postdata, set the cache key on the channel, and
7540 * do not set the LOAD_ONLY_FROM_CACHE flag, so that the channel
7541 * will be free to get it from net if it is not found in cache.
7542 * New cache may use it creatively on CGI pages with GET
7543 * method and even on those that say "no-cache"
7545 if (mLoadType == LOAD_HISTORY || mLoadType == LOAD_RELOAD_NORMAL
7546 || mLoadType == LOAD_RELOAD_CHARSET_CHANGE) {
7547 if (cacheChannel && cacheKey)
7548 cacheChannel->SetCacheKey(cacheKey);
7551 if (aHeadersData) {
7552 rv = AddHeadersToChannel(aHeadersData, httpChannel);
7554 // Set the referrer explicitly
7555 if (aReferrerURI && aSendReferrer) {
7556 // Referrer is currenly only set for link clicks here.
7557 httpChannel->SetReferrer(aReferrerURI);
7561 // Set the owner of the channel, but only for channels that can't
7562 // provide their own security context.
7564 // XXX: Is seems wrong that the owner is ignored - even if one is
7565 // supplied) unless the URI is javascript or data or about:blank.
7566 // XXX: If this is ever changed, check all callers for what owners they're
7567 // passing in. In particular, see the code and comments in LoadURI
7568 // where we fall back on inheriting the owner if called
7569 // from chrome. That would be very wrong if this code changed
7570 // anything but channels that can't provide their own security context!
7572 // (Currently chrome URIs set the owner when they are created!
7573 // So setting a NULL owner would be bad!)
7575 // If this code ever changes, change nsObjectLoadingContent::LoadObject
7576 // accordingly.
7577 PRBool inherit;
7578 // We expect URIInheritsSecurityContext to return success for an
7579 // about:blank URI, so don't call IsAboutBlank() if this call fails.
7580 rv = URIInheritsSecurityContext(aURI, &inherit);
7581 if (NS_SUCCEEDED(rv) && (inherit || IsAboutBlank(aURI))) {
7582 channel->SetOwner(aOwner);
7586 // file: uri special-casing
7588 // If this is a file: load opened from another file: then it may need
7589 // to inherit the owner from the referrer so they can script each other.
7590 // If we don't set the owner explicitly then each file: gets an owner
7591 // based on its own codebase later.
7593 nsCOMPtr<nsIPrincipal> ownerPrincipal(do_QueryInterface(aOwner));
7594 if (URIIsLocalFile(aURI) && ownerPrincipal &&
7595 NS_SUCCEEDED(ownerPrincipal->CheckMayLoad(aURI, PR_FALSE))) {
7596 // One more check here. CheckMayLoad will always return true for the
7597 // system principal, but we do NOT want to inherit in that case.
7598 PRBool isSystem;
7599 nsCOMPtr<nsIScriptSecurityManager> secMan =
7600 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
7601 if (secMan &&
7602 NS_SUCCEEDED(secMan->IsSystemPrincipal(ownerPrincipal,
7603 &isSystem)) &&
7604 !isSystem) {
7605 channel->SetOwner(aOwner);
7609 nsCOMPtr<nsIScriptChannel> scriptChannel = do_QueryInterface(channel);
7610 if (scriptChannel) {
7611 // Allow execution against our context if the principals match
7612 scriptChannel->
7613 SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL);
7616 if (aIsNewWindowTarget) {
7617 nsCOMPtr<nsIWritablePropertyBag2> props = do_QueryInterface(channel);
7618 if (props) {
7619 props->SetPropertyAsBool(
7620 NS_LITERAL_STRING("docshell.newWindowTarget"),
7621 PR_TRUE);
7625 rv = DoChannelLoad(channel, uriLoader, aBypassClassifier);
7628 // If the channel load failed, we failed and nsIWebProgress just ain't
7629 // gonna happen.
7631 if (NS_SUCCEEDED(rv)) {
7632 if (aDocShell) {
7633 *aDocShell = this;
7634 NS_ADDREF(*aDocShell);
7638 return rv;
7641 static NS_METHOD
7642 AppendSegmentToString(nsIInputStream *in,
7643 void *closure,
7644 const char *fromRawSegment,
7645 PRUint32 toOffset,
7646 PRUint32 count,
7647 PRUint32 *writeCount)
7649 // aFromSegment now contains aCount bytes of data.
7651 nsCAutoString *buf = static_cast<nsCAutoString *>(closure);
7652 buf->Append(fromRawSegment, count);
7654 // Indicate that we have consumed all of aFromSegment
7655 *writeCount = count;
7656 return NS_OK;
7659 NS_IMETHODIMP
7660 nsDocShell::AddHeadersToChannel(nsIInputStream *aHeadersData,
7661 nsIChannel *aGenericChannel)
7663 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aGenericChannel);
7664 NS_ENSURE_STATE(httpChannel);
7666 PRUint32 numRead;
7667 nsCAutoString headersString;
7668 nsresult rv = aHeadersData->ReadSegments(AppendSegmentToString,
7669 &headersString,
7670 PR_UINT32_MAX,
7671 &numRead);
7672 NS_ENSURE_SUCCESS(rv, rv);
7674 // used during the manipulation of the String from the InputStream
7675 nsCAutoString headerName;
7676 nsCAutoString headerValue;
7677 PRInt32 crlf;
7678 PRInt32 colon;
7681 // Iterate over the headersString: for each "\r\n" delimited chunk,
7682 // add the value as a header to the nsIHttpChannel
7685 static const char kWhitespace[] = "\b\t\r\n ";
7686 while (PR_TRUE) {
7687 crlf = headersString.Find("\r\n");
7688 if (crlf == kNotFound)
7689 return NS_OK;
7691 const nsCSubstring &oneHeader = StringHead(headersString, crlf);
7693 colon = oneHeader.FindChar(':');
7694 if (colon == kNotFound)
7695 return NS_ERROR_UNEXPECTED;
7697 headerName = StringHead(oneHeader, colon);
7698 headerValue = Substring(oneHeader, colon + 1);
7700 headerName.Trim(kWhitespace);
7701 headerValue.Trim(kWhitespace);
7703 headersString.Cut(0, crlf + 2);
7706 // FINALLY: we can set the header!
7709 rv = httpChannel->SetRequestHeader(headerName, headerValue, PR_TRUE);
7710 NS_ENSURE_SUCCESS(rv, rv);
7713 NS_NOTREACHED("oops");
7714 return NS_ERROR_UNEXPECTED;
7717 nsresult nsDocShell::DoChannelLoad(nsIChannel * aChannel,
7718 nsIURILoader * aURILoader,
7719 PRBool aBypassClassifier)
7721 nsresult rv;
7722 // Mark the channel as being a document URI and allow content sniffing...
7723 nsLoadFlags loadFlags = 0;
7724 (void) aChannel->GetLoadFlags(&loadFlags);
7725 loadFlags |= nsIChannel::LOAD_DOCUMENT_URI |
7726 nsIChannel::LOAD_CALL_CONTENT_SNIFFERS;
7728 // Load attributes depend on load type...
7729 switch (mLoadType) {
7730 case LOAD_HISTORY:
7731 loadFlags |= nsIRequest::VALIDATE_NEVER;
7732 break;
7734 case LOAD_RELOAD_CHARSET_CHANGE:
7735 loadFlags |= nsIRequest::LOAD_FROM_CACHE;
7736 break;
7738 case LOAD_RELOAD_NORMAL:
7739 case LOAD_REFRESH:
7740 loadFlags |= nsIRequest::VALIDATE_ALWAYS;
7741 break;
7743 case LOAD_NORMAL_BYPASS_CACHE:
7744 case LOAD_NORMAL_BYPASS_PROXY:
7745 case LOAD_NORMAL_BYPASS_PROXY_AND_CACHE:
7746 case LOAD_RELOAD_BYPASS_CACHE:
7747 case LOAD_RELOAD_BYPASS_PROXY:
7748 case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
7749 loadFlags |= nsIRequest::LOAD_BYPASS_CACHE;
7750 break;
7752 case LOAD_NORMAL:
7753 case LOAD_LINK:
7754 // Set cache checking flags
7755 PRInt32 prefSetting;
7756 if (NS_SUCCEEDED
7757 (mPrefs->
7758 GetIntPref("browser.cache.check_doc_frequency",
7759 &prefSetting))) {
7760 switch (prefSetting) {
7761 case 0:
7762 loadFlags |= nsIRequest::VALIDATE_ONCE_PER_SESSION;
7763 break;
7764 case 1:
7765 loadFlags |= nsIRequest::VALIDATE_ALWAYS;
7766 break;
7767 case 2:
7768 loadFlags |= nsIRequest::VALIDATE_NEVER;
7769 break;
7772 break;
7775 (void) aChannel->SetLoadFlags(loadFlags);
7777 rv = aURILoader->OpenURI(aChannel,
7778 (mLoadType == LOAD_LINK),
7779 this);
7780 NS_ENSURE_SUCCESS(rv, rv);
7782 if (!aBypassClassifier) {
7783 rv = CheckClassifier(aChannel);
7784 if (NS_FAILED(rv)) {
7785 aChannel->Cancel(rv);
7786 return rv;
7790 return NS_OK;
7793 nsresult
7794 nsDocShell::CheckClassifier(nsIChannel *aChannel)
7796 nsRefPtr<nsClassifierCallback> classifier = new nsClassifierCallback();
7797 if (!classifier) return NS_ERROR_OUT_OF_MEMORY;
7799 nsresult rv = classifier->Start(aChannel, PR_FALSE);
7800 NS_ENSURE_SUCCESS(rv, rv);
7802 mClassifier = classifier;
7804 return NS_OK;
7807 NS_IMETHODIMP
7808 nsDocShell::ScrollIfAnchor(nsIURI * aURI, PRBool * aWasAnchor,
7809 PRUint32 aLoadType, nscoord *cx, nscoord *cy)
7811 NS_ASSERTION(aURI, "null uri arg");
7812 NS_ASSERTION(aWasAnchor, "null anchor arg");
7814 if (aURI == nsnull || aWasAnchor == nsnull) {
7815 return NS_ERROR_FAILURE;
7818 *aWasAnchor = PR_FALSE;
7820 if (!mCurrentURI) {
7821 return NS_OK;
7824 nsCOMPtr<nsIPresShell> shell;
7825 nsresult rv = GetPresShell(getter_AddRefs(shell));
7826 if (NS_FAILED(rv) || !shell) {
7827 // If we failed to get the shell, or if there is no shell,
7828 // nothing left to do here.
7830 return rv;
7833 // NOTE: we assume URIs are absolute for comparison purposes
7835 nsCAutoString currentSpec;
7836 NS_ENSURE_SUCCESS(mCurrentURI->GetSpec(currentSpec),
7837 NS_ERROR_FAILURE);
7839 nsCAutoString newSpec;
7840 NS_ENSURE_SUCCESS(aURI->GetSpec(newSpec), NS_ERROR_FAILURE);
7842 // Search for hash marks in the current URI and the new URI and
7843 // take a copy of everything to the left of the hash for
7844 // comparison.
7846 const char kHash = '#';
7848 // Split the new URI into a left and right part
7849 // (assume we're parsing it out right
7850 nsACString::const_iterator urlStart, urlEnd, refStart, refEnd;
7851 newSpec.BeginReading(urlStart);
7852 newSpec.EndReading(refEnd);
7854 PRInt32 hashNew = newSpec.FindChar(kHash);
7855 if (hashNew == 0) {
7856 return NS_OK; // Strange URI
7859 if (hashNew > 0) {
7860 // found it
7861 urlEnd = urlStart;
7862 urlEnd.advance(hashNew);
7864 refStart = urlEnd;
7865 ++refStart; // advanced past '#'
7868 else {
7869 // no hash at all
7870 urlEnd = refStart = refEnd;
7872 const nsACString& sNewLeft = Substring(urlStart, urlEnd);
7873 const nsACString& sNewRef = Substring(refStart, refEnd);
7875 // Split the current URI in a left and right part
7876 nsACString::const_iterator currentLeftStart, currentLeftEnd;
7877 currentSpec.BeginReading(currentLeftStart);
7879 PRInt32 hashCurrent = currentSpec.FindChar(kHash);
7880 if (hashCurrent == 0) {
7881 return NS_OK; // Strange URI
7884 if (hashCurrent > 0) {
7885 currentLeftEnd = currentLeftStart;
7886 currentLeftEnd.advance(hashCurrent);
7888 else {
7889 currentSpec.EndReading(currentLeftEnd);
7892 // If we have no new anchor, we do not want to scroll, unless there is a
7893 // current anchor and we are doing a history load. So return if we have no
7894 // new anchor, and there is no current anchor or the load is not a history
7895 // load.
7896 NS_ASSERTION(hashNew != 0 && hashCurrent != 0,
7897 "What happened to the early returns above?");
7898 if (hashNew == kNotFound &&
7899 (hashCurrent == kNotFound || aLoadType != LOAD_HISTORY)) {
7900 return NS_OK;
7903 // Compare the URIs.
7905 // NOTE: this is a case sensitive comparison because some parts of the
7906 // URI are case sensitive, and some are not. i.e. the domain name
7907 // is case insensitive but the the paths are not.
7909 // This means that comparing "http://www.ABC.com/" to "http://www.abc.com/"
7910 // will fail this test.
7912 if (!Substring(currentLeftStart, currentLeftEnd).Equals(sNewLeft)) {
7913 return NS_OK; // URIs not the same
7916 // Now we know we are dealing with an anchor
7917 *aWasAnchor = PR_TRUE;
7919 // Both the new and current URIs refer to the same page. We can now
7920 // browse to the hash stored in the new URI.
7922 // But first let's capture positions of scroller(s) that can
7923 // (and usually will) be modified by GoToAnchor() call.
7925 GetCurScrollPos(ScrollOrientation_X, cx);
7926 GetCurScrollPos(ScrollOrientation_Y, cy);
7928 if (!sNewRef.IsEmpty()) {
7929 // anchor is there, but if it's a load from history,
7930 // we don't have any anchor jumping to do
7931 PRBool scroll = aLoadType != LOAD_HISTORY &&
7932 aLoadType != LOAD_RELOAD_NORMAL;
7934 char *str = ToNewCString(sNewRef);
7935 if (!str) {
7936 return NS_ERROR_OUT_OF_MEMORY;
7939 // nsUnescape modifies the string that is passed into it.
7940 nsUnescape(str);
7942 // We assume that the bytes are in UTF-8, as it says in the
7943 // spec:
7944 // http://www.w3.org/TR/html4/appendix/notes.html#h-B.2.1
7946 // We try the UTF-8 string first, and then try the document's
7947 // charset (see below). If the string is not UTF-8,
7948 // conversion will fail and give us an empty Unicode string.
7949 // In that case, we should just fall through to using the
7950 // page's charset.
7951 rv = NS_ERROR_FAILURE;
7952 NS_ConvertUTF8toUTF16 uStr(str);
7953 if (!uStr.IsEmpty()) {
7954 rv = shell->GoToAnchor(NS_ConvertUTF8toUTF16(str), scroll);
7956 nsMemory::Free(str);
7958 // Above will fail if the anchor name is not UTF-8. Need to
7959 // convert from document charset to unicode.
7960 if (NS_FAILED(rv)) {
7962 // Get a document charset
7963 NS_ENSURE_TRUE(mContentViewer, NS_ERROR_FAILURE);
7964 nsCOMPtr<nsIDocumentViewer>
7965 docv(do_QueryInterface(mContentViewer));
7966 NS_ENSURE_TRUE(docv, NS_ERROR_FAILURE);
7967 nsCOMPtr<nsIDocument> doc;
7968 rv = docv->GetDocument(getter_AddRefs(doc));
7969 NS_ENSURE_SUCCESS(rv, rv);
7970 const nsACString &aCharset = doc->GetDocumentCharacterSet();
7972 nsCOMPtr<nsITextToSubURI> textToSubURI =
7973 do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
7974 NS_ENSURE_SUCCESS(rv, rv);
7976 // Unescape and convert to unicode
7977 nsXPIDLString uStr;
7979 rv = textToSubURI->UnEscapeAndConvert(PromiseFlatCString(aCharset).get(),
7980 PromiseFlatCString(sNewRef).get(),
7981 getter_Copies(uStr));
7982 NS_ENSURE_SUCCESS(rv, rv);
7984 // Ignore return value of GoToAnchor, since it will return an error
7985 // if there is no such anchor in the document, which is actually a
7986 // success condition for us (we want to update the session history
7987 // with the new URI no matter whether we actually scrolled
7988 // somewhere).
7989 shell->GoToAnchor(uStr, scroll);
7992 else {
7994 // Tell the shell it's at an anchor, without scrolling.
7995 shell->GoToAnchor(EmptyString(), PR_FALSE);
7997 // An empty anchor was found, but if it's a load from history,
7998 // we don't have to jump to the top of the page. Scrollbar
7999 // position will be restored by the caller, based on positions
8000 // stored in session history.
8001 if (aLoadType == LOAD_HISTORY || aLoadType == LOAD_RELOAD_NORMAL)
8002 return rv;
8003 //An empty anchor. Scroll to the top of the page.
8004 rv = SetCurScrollPosEx(0, 0);
8007 return rv;
8010 void
8011 nsDocShell::SetupReferrerFromChannel(nsIChannel * aChannel)
8013 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
8014 if (httpChannel) {
8015 nsCOMPtr<nsIURI> referrer;
8016 nsresult rv = httpChannel->GetReferrer(getter_AddRefs(referrer));
8017 if (NS_SUCCEEDED(rv)) {
8018 SetReferrerURI(referrer);
8023 PRBool
8024 nsDocShell::OnNewURI(nsIURI * aURI, nsIChannel * aChannel, nsISupports* aOwner,
8025 PRUint32 aLoadType, PRBool aFireOnLocationChange,
8026 PRBool aAddToGlobalHistory)
8028 NS_PRECONDITION(aURI, "uri is null");
8029 NS_PRECONDITION(!aChannel || !aOwner, "Shouldn't have both set");
8031 #if defined(PR_LOGGING) && defined(DEBUG)
8032 if (PR_LOG_TEST(gDocShellLog, PR_LOG_DEBUG)) {
8033 nsCAutoString spec;
8034 aURI->GetSpec(spec);
8036 nsCAutoString chanName;
8037 if (aChannel)
8038 aChannel->GetName(chanName);
8039 else
8040 chanName.AssignLiteral("<no channel>");
8042 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
8043 ("nsDocShell[%p]::OnNewURI(\"%s\", [%s], 0x%x)\n", this, spec.get(),
8044 chanName.get(), aLoadType));
8046 #endif
8048 PRBool updateHistory = PR_TRUE;
8049 PRBool equalUri = PR_FALSE;
8050 PRBool shAvailable = PR_TRUE;
8052 // Get the post data from the channel
8053 nsCOMPtr<nsIInputStream> inputStream;
8054 if (aChannel) {
8055 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
8057 // Check if the HTTPChannel is hiding under a multiPartChannel
8058 if (!httpChannel) {
8059 GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
8062 if (httpChannel) {
8063 nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
8064 if (uploadChannel) {
8065 uploadChannel->GetUploadStream(getter_AddRefs(inputStream));
8069 /* Create SH Entry (mLSHE) only if there is a SessionHistory object (mSessionHistory) in
8070 * the current frame or in the root docshell
8072 nsCOMPtr<nsISHistory> rootSH = mSessionHistory;
8073 if (!rootSH) {
8074 // Get the handle to SH from the root docshell
8075 GetRootSessionHistory(getter_AddRefs(rootSH));
8076 if (!rootSH)
8077 shAvailable = PR_FALSE;
8078 } // rootSH
8081 // Determine if this type of load should update history.
8082 if (aLoadType == LOAD_BYPASS_HISTORY ||
8083 aLoadType == LOAD_ERROR_PAGE ||
8084 aLoadType & LOAD_CMD_HISTORY ||
8085 aLoadType & LOAD_CMD_RELOAD)
8086 updateHistory = PR_FALSE;
8088 // Check if the url to be loaded is the same as the one already loaded.
8089 if (mCurrentURI)
8090 aURI->Equals(mCurrentURI, &equalUri);
8092 #ifdef DEBUG
8093 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
8094 (" shAvailable=%i updateHistory=%i equalURI=%i\n",
8095 shAvailable, updateHistory, equalUri));
8096 #endif
8098 /* If the url to be loaded is the same as the one already there,
8099 * and the original loadType is LOAD_NORMAL, LOAD_LINK, or
8100 * LOAD_STOP_CONTENT, set loadType to LOAD_NORMAL_REPLACE so that
8101 * AddToSessionHistory() won't mess with the current SHEntry and
8102 * if this page has any frame children, it also will be handled
8103 * properly. see bug 83684
8105 * XXX Hopefully changing the loadType at this time will not hurt
8106 * anywhere. The other way to take care of sequentially repeating
8107 * frameset pages is to add new methods to nsIDocShellTreeItem.
8108 * Hopefully I don't have to do that.
8110 if (equalUri &&
8111 (mLoadType == LOAD_NORMAL ||
8112 mLoadType == LOAD_LINK ||
8113 mLoadType == LOAD_STOP_CONTENT) &&
8114 !inputStream)
8116 mLoadType = LOAD_NORMAL_REPLACE;
8119 // If this is a refresh to the currently loaded url, we don't
8120 // have to update session or global history.
8121 if (mLoadType == LOAD_REFRESH && !inputStream && equalUri) {
8122 SetHistoryEntry(&mLSHE, mOSHE);
8126 /* If the user pressed shift-reload, cache will create a new cache key
8127 * for the page. Save the new cacheKey in Session History.
8128 * see bug 90098
8130 if (aChannel &&
8131 (aLoadType == LOAD_RELOAD_BYPASS_CACHE ||
8132 aLoadType == LOAD_RELOAD_BYPASS_PROXY ||
8133 aLoadType == LOAD_RELOAD_BYPASS_PROXY_AND_CACHE)) {
8134 NS_ASSERTION(!updateHistory,
8135 "We shouldn't be updating history for forced reloads!");
8137 nsCOMPtr<nsICachingChannel> cacheChannel(do_QueryInterface(aChannel));
8138 nsCOMPtr<nsISupports> cacheKey;
8139 // Get the Cache Key and store it in SH.
8140 if (cacheChannel)
8141 cacheChannel->GetCacheKey(getter_AddRefs(cacheKey));
8142 // If we already have a loading history entry, store the new cache key
8143 // in it. Otherwise, since we're doing a reload and won't be updating
8144 // our history entry, store the cache key in our current history entry.
8145 if (mLSHE)
8146 mLSHE->SetCacheKey(cacheKey);
8147 else if (mOSHE)
8148 mOSHE->SetCacheKey(cacheKey);
8151 if (updateHistory && shAvailable) {
8152 // Update session history if necessary...
8153 if (!mLSHE && (mItemType == typeContent) && mURIResultedInDocument) {
8154 /* This is a fresh page getting loaded for the first time
8155 *.Create a Entry for it and add it to SH, if this is the
8156 * rootDocShell
8158 (void) AddToSessionHistory(aURI, aChannel, aOwner,
8159 getter_AddRefs(mLSHE));
8162 // Update Global history
8163 if (aAddToGlobalHistory) {
8164 // Get the referrer uri from the channel
8165 AddToGlobalHistory(aURI, PR_FALSE, aChannel);
8169 // If this was a history load, update the index in
8170 // SH.
8171 if (rootSH && (mLoadType & LOAD_CMD_HISTORY)) {
8172 nsCOMPtr<nsISHistoryInternal> shInternal(do_QueryInterface(rootSH));
8173 if (shInternal) {
8174 rootSH->GetIndex(&mPreviousTransIndex);
8175 shInternal->UpdateIndex();
8176 rootSH->GetIndex(&mLoadedTransIndex);
8177 #ifdef DEBUG_PAGE_CACHE
8178 printf("Previous index: %d, Loaded index: %d\n\n",
8179 mPreviousTransIndex, mLoadedTransIndex);
8180 #endif
8183 PRBool onLocationChangeNeeded = SetCurrentURI(aURI, aChannel,
8184 aFireOnLocationChange);
8185 // Make sure to store the referrer from the channel, if any
8186 SetupReferrerFromChannel(aChannel);
8187 return onLocationChangeNeeded;
8190 PRBool
8191 nsDocShell::OnLoadingSite(nsIChannel * aChannel, PRBool aFireOnLocationChange,
8192 PRBool aAddToGlobalHistory)
8194 nsCOMPtr<nsIURI> uri;
8195 // If this a redirect, use the final url (uri)
8196 // else use the original url
8198 // Note that this should match what documents do (see nsDocument::Reset).
8199 NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
8200 NS_ENSURE_TRUE(uri, PR_FALSE);
8202 return OnNewURI(uri, aChannel, nsnull, mLoadType, aFireOnLocationChange,
8203 aAddToGlobalHistory);
8207 void
8208 nsDocShell::SetReferrerURI(nsIURI * aURI)
8210 mReferrerURI = aURI; // This assigment addrefs
8213 //*****************************************************************************
8214 // nsDocShell: Session History
8215 //*****************************************************************************
8216 PRBool
8217 nsDocShell::ShouldAddToSessionHistory(nsIURI * aURI)
8219 // I believe none of the about: urls should go in the history. But then
8220 // that could just be me... If the intent is only deny about:blank then we
8221 // should just do a spec compare, rather than two gets of the scheme and
8222 // then the path. -Gagan
8223 nsresult rv;
8224 nsCAutoString buf;
8226 rv = aURI->GetScheme(buf);
8227 if (NS_FAILED(rv))
8228 return PR_FALSE;
8230 if (buf.Equals("about")) {
8231 rv = aURI->GetPath(buf);
8232 if (NS_FAILED(rv))
8233 return PR_FALSE;
8235 if (buf.Equals("blank")) {
8236 return PR_FALSE;
8239 return PR_TRUE;
8242 nsresult
8243 nsDocShell::AddToSessionHistory(nsIURI * aURI, nsIChannel * aChannel,
8244 nsISupports* aOwner, nsISHEntry ** aNewEntry)
8246 NS_PRECONDITION(aURI, "uri is null");
8247 NS_PRECONDITION(!aChannel || !aOwner, "Shouldn't have both set");
8249 #if defined(PR_LOGGING) && defined(DEBUG)
8250 if (PR_LOG_TEST(gDocShellLog, PR_LOG_DEBUG)) {
8251 nsCAutoString spec;
8252 aURI->GetSpec(spec);
8254 nsCAutoString chanName;
8255 if (aChannel)
8256 aChannel->GetName(chanName);
8257 else
8258 chanName.AssignLiteral("<no channel>");
8260 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
8261 ("nsDocShell[%p]::AddToSessionHistory(\"%s\", [%s])\n", this, spec.get(),
8262 chanName.get()));
8264 #endif
8266 nsresult rv = NS_OK;
8267 nsCOMPtr<nsISHEntry> entry;
8268 PRBool shouldPersist;
8270 shouldPersist = ShouldAddToSessionHistory(aURI);
8272 // Get a handle to the root docshell
8273 nsCOMPtr<nsIDocShellTreeItem> root;
8274 GetSameTypeRootTreeItem(getter_AddRefs(root));
8276 * If this is a LOAD_FLAGS_REPLACE_HISTORY in a subframe, we use
8277 * the existing SH entry in the page and replace the url and
8278 * other vitalities.
8280 if (LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY) &&
8281 root != static_cast<nsIDocShellTreeItem *>(this)) {
8282 // This is a subframe
8283 entry = mOSHE;
8284 nsCOMPtr<nsISHContainer> shContainer(do_QueryInterface(entry));
8285 if (shContainer) {
8286 PRInt32 childCount = 0;
8287 shContainer->GetChildCount(&childCount);
8288 // Remove all children of this entry
8289 for (PRInt32 i = childCount - 1; i >= 0; i--) {
8290 nsCOMPtr<nsISHEntry> child;
8291 shContainer->GetChildAt(i, getter_AddRefs(child));
8292 shContainer->RemoveChild(child);
8293 } // for
8294 } // shContainer
8297 // Create a new entry if necessary.
8298 if (!entry) {
8299 entry = do_CreateInstance(NS_SHENTRY_CONTRACTID);
8301 if (!entry) {
8302 return NS_ERROR_OUT_OF_MEMORY;
8306 // Get the post data & referrer
8307 nsCOMPtr<nsIInputStream> inputStream;
8308 nsCOMPtr<nsIURI> referrerURI;
8309 nsCOMPtr<nsISupports> cacheKey;
8310 nsCOMPtr<nsISupports> cacheToken;
8311 nsCOMPtr<nsISupports> owner = aOwner;
8312 PRBool expired = PR_FALSE;
8313 PRBool discardLayoutState = PR_FALSE;
8314 if (aChannel) {
8315 nsCOMPtr<nsICachingChannel>
8316 cacheChannel(do_QueryInterface(aChannel));
8317 /* If there is a caching channel, get the Cache Key and store it
8318 * in SH.
8320 if (cacheChannel) {
8321 cacheChannel->GetCacheKey(getter_AddRefs(cacheKey));
8322 cacheChannel->GetCacheToken(getter_AddRefs(cacheToken));
8324 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
8326 // Check if the httpChannel is hiding under a multipartChannel
8327 if (!httpChannel) {
8328 GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
8330 if (httpChannel) {
8331 nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
8332 if (uploadChannel) {
8333 uploadChannel->GetUploadStream(getter_AddRefs(inputStream));
8335 httpChannel->GetReferrer(getter_AddRefs(referrerURI));
8337 discardLayoutState = ShouldDiscardLayoutState(httpChannel);
8339 aChannel->GetOwner(getter_AddRefs(owner));
8342 //Title is set in nsDocShell::SetTitle()
8343 entry->Create(aURI, // uri
8344 EmptyString(), // Title
8345 inputStream, // Post data stream
8346 nsnull, // LayoutHistory state
8347 cacheKey, // CacheKey
8348 mContentTypeHint, // Content-type
8349 owner); // Channel or provided owner
8350 entry->SetReferrerURI(referrerURI);
8351 /* If cache got a 'no-store', ask SH not to store
8352 * HistoryLayoutState. By default, SH will set this
8353 * flag to PR_TRUE and save HistoryLayoutState.
8355 if (discardLayoutState) {
8356 entry->SetSaveLayoutStateFlag(PR_FALSE);
8358 if (cacheToken) {
8359 // Check if the page has expired from cache
8360 nsCOMPtr<nsICacheEntryInfo> cacheEntryInfo(do_QueryInterface(cacheToken));
8361 if (cacheEntryInfo) {
8362 PRUint32 expTime;
8363 cacheEntryInfo->GetExpirationTime(&expTime);
8364 PRUint32 now = PRTimeToSeconds(PR_Now());
8365 if (expTime <= now)
8366 expired = PR_TRUE;
8370 if (expired == PR_TRUE)
8371 entry->SetExpirationStatus(PR_TRUE);
8374 if (root == static_cast<nsIDocShellTreeItem *>(this) && mSessionHistory) {
8375 // This is the root docshell
8376 if (LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY)) {
8377 // Replace current entry in session history.
8378 PRInt32 index = 0;
8379 mSessionHistory->GetIndex(&index);
8380 nsCOMPtr<nsISHistoryInternal> shPrivate(do_QueryInterface(mSessionHistory));
8381 // Replace the current entry with the new entry
8382 if (shPrivate)
8383 rv = shPrivate->ReplaceEntry(index, entry);
8385 else {
8386 // Add to session history
8387 nsCOMPtr<nsISHistoryInternal>
8388 shPrivate(do_QueryInterface(mSessionHistory));
8389 NS_ENSURE_TRUE(shPrivate, NS_ERROR_FAILURE);
8390 mSessionHistory->GetIndex(&mPreviousTransIndex);
8391 rv = shPrivate->AddEntry(entry, shouldPersist);
8392 mSessionHistory->GetIndex(&mLoadedTransIndex);
8393 #ifdef DEBUG_PAGE_CACHE
8394 printf("Previous index: %d, Loaded index: %d\n\n",
8395 mPreviousTransIndex, mLoadedTransIndex);
8396 #endif
8399 else {
8400 // This is a subframe.
8401 if (!mOSHE || !LOAD_TYPE_HAS_FLAGS(mLoadType,
8402 LOAD_FLAGS_REPLACE_HISTORY))
8403 rv = DoAddChildSHEntry(entry, mChildOffset);
8406 // Return the new SH entry...
8407 if (aNewEntry) {
8408 *aNewEntry = nsnull;
8409 if (NS_SUCCEEDED(rv)) {
8410 *aNewEntry = entry;
8411 NS_ADDREF(*aNewEntry);
8415 return rv;
8419 NS_IMETHODIMP
8420 nsDocShell::LoadHistoryEntry(nsISHEntry * aEntry, PRUint32 aLoadType)
8422 if (!IsNavigationAllowed()) {
8423 return NS_OK;
8426 nsCOMPtr<nsIURI> uri;
8427 nsCOMPtr<nsIInputStream> postData;
8428 nsCOMPtr<nsIURI> referrerURI;
8429 nsCAutoString contentType;
8430 nsCOMPtr<nsISupports> owner;
8432 NS_ENSURE_TRUE(aEntry, NS_ERROR_FAILURE);
8434 NS_ENSURE_SUCCESS(aEntry->GetURI(getter_AddRefs(uri)), NS_ERROR_FAILURE);
8435 NS_ENSURE_SUCCESS(aEntry->GetReferrerURI(getter_AddRefs(referrerURI)),
8436 NS_ERROR_FAILURE);
8437 NS_ENSURE_SUCCESS(aEntry->GetPostData(getter_AddRefs(postData)),
8438 NS_ERROR_FAILURE);
8439 NS_ENSURE_SUCCESS(aEntry->GetContentType(contentType), NS_ERROR_FAILURE);
8440 NS_ENSURE_SUCCESS(aEntry->GetOwner(getter_AddRefs(owner)),
8441 NS_ERROR_FAILURE);
8443 // Calling CreateAboutBlankContentViewer can set mOSHE to null, and if
8444 // that's the only thing holding a ref to aEntry that will cause aEntry to
8445 // die while we're loading it. So hold a strong ref to aEntry here, just
8446 // in case.
8447 nsCOMPtr<nsISHEntry> kungFuDeathGrip(aEntry);
8448 PRBool isJS;
8449 nsresult rv = uri->SchemeIs("javascript", &isJS);
8450 if (NS_FAILED(rv) || isJS) {
8451 // We're loading a URL that will execute script from inside asyncOpen.
8452 // Replace the current document with about:blank now to prevent
8453 // anything from the current document from leaking into any JavaScript
8454 // code in the URL.
8455 nsCOMPtr<nsIPrincipal> prin = do_QueryInterface(owner);
8456 rv = CreateAboutBlankContentViewer(prin);
8458 if (NS_FAILED(rv)) {
8459 // The creation of the intermittent about:blank content
8460 // viewer failed for some reason (potentially because the
8461 // user prevented it). Interrupt the history load.
8462 return NS_OK;
8465 if (!owner) {
8466 // Ensure that we have an owner. Otherwise javascript: URIs will
8467 // pick it up from the about:blank page we just loaded, and we
8468 // don't really want even that in this case.
8469 owner = do_CreateInstance("@mozilla.org/nullprincipal;1");
8470 NS_ENSURE_TRUE(owner, NS_ERROR_OUT_OF_MEMORY);
8474 /* If there is a valid postdata *and* the user pressed
8475 * reload or shift-reload, take user's permission before we
8476 * repost the data to the server.
8478 if ((aLoadType & LOAD_CMD_RELOAD) && postData) {
8479 PRBool repost;
8480 rv = ConfirmRepost(&repost);
8481 if (NS_FAILED(rv)) return rv;
8483 // If the user pressed cancel in the dialog, return. We're done here.
8484 if (!repost)
8485 return NS_BINDING_ABORTED;
8488 rv = InternalLoad(uri,
8489 referrerURI,
8490 owner,
8491 INTERNAL_LOAD_FLAGS_NONE, // Do not inherit owner from document (security-critical!)
8492 nsnull, // No window target
8493 contentType.get(), // Type hint
8494 postData, // Post data stream
8495 nsnull, // No headers stream
8496 aLoadType, // Load type
8497 aEntry, // SHEntry
8498 PR_TRUE,
8499 nsnull, // No nsIDocShell
8500 nsnull); // No nsIRequest
8501 return rv;
8504 NS_IMETHODIMP nsDocShell::GetShouldSaveLayoutState(PRBool* aShould)
8506 *aShould = PR_FALSE;
8507 if (mOSHE) {
8508 // Don't capture historystate and save it in history
8509 // if the page asked not to do so.
8510 mOSHE->GetSaveLayoutStateFlag(aShould);
8513 return NS_OK;
8516 NS_IMETHODIMP nsDocShell::PersistLayoutHistoryState()
8518 nsresult rv = NS_OK;
8520 if (mOSHE) {
8521 nsCOMPtr<nsIPresShell> shell;
8522 rv = GetPresShell(getter_AddRefs(shell));
8523 if (NS_SUCCEEDED(rv) && shell) {
8524 nsCOMPtr<nsILayoutHistoryState> layoutState;
8525 rv = shell->CaptureHistoryState(getter_AddRefs(layoutState),
8526 PR_TRUE);
8530 return rv;
8533 /* static */ nsresult
8534 nsDocShell::WalkHistoryEntries(nsISHEntry *aRootEntry,
8535 nsDocShell *aRootShell,
8536 WalkHistoryEntriesFunc aCallback,
8537 void *aData)
8539 NS_ENSURE_TRUE(aRootEntry, NS_ERROR_FAILURE);
8541 nsCOMPtr<nsISHContainer> container(do_QueryInterface(aRootEntry));
8542 if (!container)
8543 return NS_ERROR_FAILURE;
8545 PRInt32 childCount;
8546 container->GetChildCount(&childCount);
8547 for (PRInt32 i = 0; i < childCount; i++) {
8548 nsCOMPtr<nsISHEntry> childEntry;
8549 container->GetChildAt(i, getter_AddRefs(childEntry));
8550 if (!childEntry) {
8551 // childEntry can be null for valid reasons, for example if the
8552 // docshell at index i never loaded anything useful.
8553 continue;
8556 nsDocShell *childShell = nsnull;
8557 if (aRootShell) {
8558 // Walk the children of aRootShell and see if one of them
8559 // has srcChild as a SHEntry.
8561 PRInt32 childCount = aRootShell->mChildList.Count();
8562 for (PRInt32 j = 0; j < childCount; ++j) {
8563 nsDocShell *child =
8564 static_cast<nsDocShell*>(aRootShell->ChildAt(j));
8566 if (child->HasHistoryEntry(childEntry)) {
8567 childShell = child;
8568 break;
8572 nsresult rv = aCallback(childEntry, childShell, i, aData);
8573 NS_ENSURE_SUCCESS(rv, rv);
8576 return NS_OK;
8579 // callback data for WalkHistoryEntries
8580 struct NS_STACK_CLASS CloneAndReplaceData
8582 CloneAndReplaceData(PRUint32 aCloneID, nsISHEntry *aReplaceEntry,
8583 nsISHEntry *aDestTreeParent)
8584 : cloneID(aCloneID),
8585 replaceEntry(aReplaceEntry),
8586 destTreeParent(aDestTreeParent) { }
8588 PRUint32 cloneID;
8589 nsISHEntry *replaceEntry;
8590 nsISHEntry *destTreeParent;
8591 nsCOMPtr<nsISHEntry> resultEntry;
8594 /* static */ nsresult
8595 nsDocShell::CloneAndReplaceChild(nsISHEntry *aEntry, nsDocShell *aShell,
8596 PRInt32 aEntryIndex, void *aData)
8598 nsresult result = NS_OK;
8599 nsCOMPtr<nsISHEntry> dest;
8601 CloneAndReplaceData *data = static_cast<CloneAndReplaceData*>(aData);
8602 PRUint32 cloneID = data->cloneID;
8603 nsISHEntry *replaceEntry = data->replaceEntry;
8605 PRUint32 srcID;
8606 aEntry->GetID(&srcID);
8608 if (srcID == cloneID) {
8609 // Just replace the entry, and don't walk the children.
8610 dest = replaceEntry;
8611 dest->SetIsSubFrame(PR_TRUE);
8612 } else {
8613 // Clone the SHEntry...
8614 result = aEntry->Clone(getter_AddRefs(dest));
8615 if (NS_FAILED(result))
8616 return result;
8618 // This entry is for a subframe navigation
8619 dest->SetIsSubFrame(PR_TRUE);
8621 // Walk the children
8622 CloneAndReplaceData childData(cloneID, replaceEntry, dest);
8623 result = WalkHistoryEntries(aEntry, aShell,
8624 CloneAndReplaceChild, &childData);
8625 if (NS_FAILED(result))
8626 return result;
8628 if (aShell)
8629 aShell->SwapHistoryEntries(aEntry, dest);
8632 nsCOMPtr<nsISHContainer> container =
8633 do_QueryInterface(data->destTreeParent);
8634 if (container)
8635 container->AddChild(dest, aEntryIndex);
8637 data->resultEntry = dest;
8638 return result;
8641 /* static */ nsresult
8642 nsDocShell::CloneAndReplace(nsISHEntry *aSrcEntry,
8643 nsDocShell *aSrcShell,
8644 PRUint32 aCloneID,
8645 nsISHEntry *aReplaceEntry,
8646 nsISHEntry **aResultEntry)
8648 NS_ENSURE_ARG_POINTER(aResultEntry);
8649 NS_ENSURE_TRUE(aReplaceEntry, NS_ERROR_FAILURE);
8651 CloneAndReplaceData data(aCloneID, aReplaceEntry, nsnull);
8652 nsresult rv = CloneAndReplaceChild(aSrcEntry, aSrcShell, 0, &data);
8654 data.resultEntry.swap(*aResultEntry);
8655 return rv;
8659 void
8660 nsDocShell::SwapHistoryEntries(nsISHEntry *aOldEntry, nsISHEntry *aNewEntry)
8662 if (aOldEntry == mOSHE)
8663 mOSHE = aNewEntry;
8665 if (aOldEntry == mLSHE)
8666 mLSHE = aNewEntry;
8670 struct SwapEntriesData
8672 nsDocShell *ignoreShell; // constant; the shell to ignore
8673 nsISHEntry *destTreeRoot; // constant; the root of the dest tree
8674 nsISHEntry *destTreeParent; // constant; the node under destTreeRoot
8675 // whose children will correspond to aEntry
8679 nsresult
8680 nsDocShell::SetChildHistoryEntry(nsISHEntry *aEntry, nsDocShell *aShell,
8681 PRInt32 aEntryIndex, void *aData)
8683 SwapEntriesData *data = static_cast<SwapEntriesData*>(aData);
8684 nsDocShell *ignoreShell = data->ignoreShell;
8686 if (!aShell || aShell == ignoreShell)
8687 return NS_OK;
8689 nsISHEntry *destTreeRoot = data->destTreeRoot;
8691 nsCOMPtr<nsISHEntry> destEntry;
8692 nsCOMPtr<nsISHContainer> container =
8693 do_QueryInterface(data->destTreeParent);
8695 if (container) {
8696 // aEntry is a clone of some child of destTreeParent, but since the
8697 // trees aren't necessarily in sync, we'll have to locate it.
8698 // Note that we could set aShell's entry to null if we don't find a
8699 // corresponding entry under destTreeParent.
8701 PRUint32 targetID, id;
8702 aEntry->GetID(&targetID);
8704 // First look at the given index, since this is the common case.
8705 nsCOMPtr<nsISHEntry> entry;
8706 container->GetChildAt(aEntryIndex, getter_AddRefs(entry));
8707 if (entry && NS_SUCCEEDED(entry->GetID(&id)) && id == targetID) {
8708 destEntry.swap(entry);
8709 } else {
8710 PRInt32 childCount;
8711 container->GetChildCount(&childCount);
8712 for (PRInt32 i = 0; i < childCount; ++i) {
8713 container->GetChildAt(i, getter_AddRefs(entry));
8714 if (!entry)
8715 continue;
8717 entry->GetID(&id);
8718 if (id == targetID) {
8719 destEntry.swap(entry);
8720 break;
8724 } else {
8725 destEntry = destTreeRoot;
8728 aShell->SwapHistoryEntries(aEntry, destEntry);
8730 // Now handle the children of aEntry.
8731 SwapEntriesData childData = { ignoreShell, destTreeRoot, destEntry };
8732 return WalkHistoryEntries(aEntry, aShell,
8733 SetChildHistoryEntry, &childData);
8737 static nsISHEntry*
8738 GetRootSHEntry(nsISHEntry *aEntry)
8740 nsCOMPtr<nsISHEntry> rootEntry = aEntry;
8741 nsISHEntry *result = nsnull;
8742 while (rootEntry) {
8743 result = rootEntry;
8744 result->GetParent(getter_AddRefs(rootEntry));
8747 return result;
8751 void
8752 nsDocShell::SetHistoryEntry(nsCOMPtr<nsISHEntry> *aPtr, nsISHEntry *aEntry)
8754 // We need to sync up the docshell and session history trees for
8755 // subframe navigation. If the load was in a subframe, we forward up to
8756 // the root docshell, which will then recursively sync up all docshells
8757 // to their corresponding entries in the new session history tree.
8758 // If we don't do this, then we can cache a content viewer on the wrong
8759 // cloned entry, and subsequently restore it at the wrong time.
8761 nsISHEntry *newRootEntry = GetRootSHEntry(aEntry);
8762 if (newRootEntry) {
8763 // newRootEntry is now the new root entry.
8764 // Find the old root entry as well.
8766 // Need a strong ref. on |oldRootEntry| so it isn't destroyed when
8767 // SetChildHistoryEntry() does SwapHistoryEntries() (bug 304639).
8768 nsCOMPtr<nsISHEntry> oldRootEntry = GetRootSHEntry(*aPtr);
8769 if (oldRootEntry) {
8770 nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
8771 GetSameTypeParent(getter_AddRefs(parentAsItem));
8772 nsCOMPtr<nsIDocShell> rootShell = do_QueryInterface(parentAsItem);
8773 if (rootShell) { // if we're the root just set it, nothing to swap
8774 SwapEntriesData data = { this, newRootEntry };
8775 nsIDocShell *rootIDocShell =
8776 static_cast<nsIDocShell*>(rootShell);
8777 nsDocShell *rootDocShell = static_cast<nsDocShell*>
8778 (rootIDocShell);
8780 #ifdef NS_DEBUG
8781 nsresult rv =
8782 #endif
8783 SetChildHistoryEntry(oldRootEntry, rootDocShell,
8784 0, &data);
8785 NS_ASSERTION(NS_SUCCEEDED(rv), "SetChildHistoryEntry failed");
8790 *aPtr = aEntry;
8794 nsresult
8795 nsDocShell::GetRootSessionHistory(nsISHistory ** aReturn)
8797 nsresult rv;
8799 nsCOMPtr<nsIDocShellTreeItem> root;
8800 //Get the root docshell
8801 rv = GetSameTypeRootTreeItem(getter_AddRefs(root));
8802 // QI to nsIWebNavigation
8803 nsCOMPtr<nsIWebNavigation> rootAsWebnav(do_QueryInterface(root));
8804 if (rootAsWebnav) {
8805 // Get the handle to SH from the root docshell
8806 rv = rootAsWebnav->GetSessionHistory(aReturn);
8808 return rv;
8811 nsresult
8812 nsDocShell::GetHttpChannel(nsIChannel * aChannel, nsIHttpChannel ** aReturn)
8814 NS_ENSURE_ARG_POINTER(aReturn);
8815 if (!aChannel)
8816 return NS_ERROR_FAILURE;
8818 nsCOMPtr<nsIMultiPartChannel> multiPartChannel(do_QueryInterface(aChannel));
8819 if (multiPartChannel) {
8820 nsCOMPtr<nsIChannel> baseChannel;
8821 multiPartChannel->GetBaseChannel(getter_AddRefs(baseChannel));
8822 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(baseChannel));
8823 *aReturn = httpChannel;
8824 NS_IF_ADDREF(*aReturn);
8826 return NS_OK;
8829 PRBool
8830 nsDocShell::ShouldDiscardLayoutState(nsIHttpChannel * aChannel)
8832 // By default layout State will be saved.
8833 if (!aChannel)
8834 return PR_FALSE;
8836 // figure out if SH should be saving layout state
8837 nsCOMPtr<nsISupports> securityInfo;
8838 PRBool noStore = PR_FALSE, noCache = PR_FALSE;
8839 aChannel->GetSecurityInfo(getter_AddRefs(securityInfo));
8840 aChannel->IsNoStoreResponse(&noStore);
8841 aChannel->IsNoCacheResponse(&noCache);
8843 return (noStore || (noCache && securityInfo));
8846 //*****************************************************************************
8847 // nsDocShell: nsIEditorDocShell
8848 //*****************************************************************************
8850 NS_IMETHODIMP nsDocShell::GetEditor(nsIEditor * *aEditor)
8852 NS_ENSURE_ARG_POINTER(aEditor);
8854 if (!mEditorData) {
8855 *aEditor = nsnull;
8856 return NS_OK;
8859 return mEditorData->GetEditor(aEditor);
8862 NS_IMETHODIMP nsDocShell::SetEditor(nsIEditor * aEditor)
8864 nsresult rv = EnsureEditorData();
8865 if (NS_FAILED(rv)) return rv;
8867 return mEditorData->SetEditor(aEditor);
8871 NS_IMETHODIMP nsDocShell::GetEditable(PRBool *aEditable)
8873 NS_ENSURE_ARG_POINTER(aEditable);
8874 *aEditable = mEditorData && mEditorData->GetEditable();
8875 return NS_OK;
8879 NS_IMETHODIMP nsDocShell::GetHasEditingSession(PRBool *aHasEditingSession)
8881 NS_ENSURE_ARG_POINTER(aHasEditingSession);
8883 if (mEditorData)
8885 nsCOMPtr<nsIEditingSession> editingSession;
8886 mEditorData->GetEditingSession(getter_AddRefs(editingSession));
8887 *aHasEditingSession = (editingSession.get() != nsnull);
8889 else
8891 *aHasEditingSession = PR_FALSE;
8894 return NS_OK;
8897 NS_IMETHODIMP nsDocShell::MakeEditable(PRBool inWaitForUriLoad)
8899 nsresult rv = EnsureEditorData();
8900 if (NS_FAILED(rv)) return rv;
8902 return mEditorData->MakeEditable(inWaitForUriLoad);
8905 nsresult
8906 nsDocShell::AddToGlobalHistory(nsIURI * aURI, PRBool aRedirect,
8907 nsIChannel * aChannel)
8909 if (mItemType != typeContent || !mGlobalHistory)
8910 return NS_OK;
8912 // If this is a POST request, we do not want to include this in global
8913 // history, so return early.
8914 nsCOMPtr<nsIHttpChannel> hchan(do_QueryInterface(aChannel));
8915 if (hchan) {
8916 nsCAutoString type;
8917 nsresult rv = hchan->GetRequestMethod(type);
8918 if (NS_SUCCEEDED(rv) && type.EqualsLiteral("POST"))
8919 return NS_OK;
8922 PRBool visited;
8923 nsresult rv = mGlobalHistory->IsVisited(aURI, &visited);
8924 if (NS_FAILED(rv))
8925 return rv;
8927 nsCOMPtr<nsIURI> referrer;
8928 if (aChannel)
8929 NS_GetReferrerFromChannel(aChannel, getter_AddRefs(referrer));
8931 rv = mGlobalHistory->AddURI(aURI, aRedirect, !IsFrame(), referrer);
8932 if (NS_FAILED(rv))
8933 return rv;
8935 if (!visited) {
8936 nsCOMPtr<nsIObserverService> obsService =
8937 do_GetService("@mozilla.org/observer-service;1");
8938 if (obsService) {
8939 obsService->NotifyObservers(aURI, NS_LINK_VISITED_EVENT_TOPIC, nsnull);
8943 return NS_OK;
8946 //*****************************************************************************
8947 // nsDocShell: Helper Routines
8948 //*****************************************************************************
8950 NS_IMETHODIMP
8951 nsDocShell::SetLoadType(PRUint32 aLoadType)
8953 mLoadType = aLoadType;
8954 return NS_OK;
8957 NS_IMETHODIMP
8958 nsDocShell::GetLoadType(PRUint32 * aLoadType)
8960 *aLoadType = mLoadType;
8961 return NS_OK;
8964 nsresult
8965 nsDocShell::ConfirmRepost(PRBool * aRepost)
8967 nsresult rv;
8968 nsCOMPtr<nsIPrompt> prompter;
8969 CallGetInterface(this, static_cast<nsIPrompt**>(getter_AddRefs(prompter)));
8971 nsCOMPtr<nsIStringBundleService>
8972 stringBundleService(do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv));
8973 NS_ENSURE_SUCCESS(rv, rv);
8975 nsCOMPtr<nsIStringBundle> appBundle;
8976 rv = stringBundleService->CreateBundle(kAppstringsBundleURL,
8977 getter_AddRefs(appBundle));
8978 NS_ENSURE_SUCCESS(rv, rv);
8980 nsCOMPtr<nsIStringBundle> brandBundle;
8981 rv = stringBundleService->CreateBundle(kBrandBundleURL, getter_AddRefs(brandBundle));
8982 NS_ENSURE_SUCCESS(rv, rv);
8984 NS_ASSERTION(prompter && brandBundle && appBundle,
8985 "Unable to set up repost prompter.");
8987 nsXPIDLString brandName;
8988 rv = brandBundle->GetStringFromName(NS_LITERAL_STRING("brandShortName").get(),
8989 getter_Copies(brandName));
8991 nsXPIDLString msgString, button0Title;
8992 if (NS_FAILED(rv)) { // No brand, use the generic version.
8993 rv = appBundle->GetStringFromName(NS_LITERAL_STRING("confirmRepostPrompt").get(),
8994 getter_Copies(msgString));
8996 else {
8997 // Brand available - if the app has an override file with formatting, the app name will
8998 // be included. Without an override, the prompt will look like the generic version.
8999 const PRUnichar *formatStrings[] = { brandName.get() };
9000 rv = appBundle->FormatStringFromName(NS_LITERAL_STRING("confirmRepostPrompt").get(),
9001 formatStrings, NS_ARRAY_LENGTH(formatStrings),
9002 getter_Copies(msgString));
9004 if (NS_FAILED(rv)) return rv;
9006 rv = appBundle->GetStringFromName(NS_LITERAL_STRING("resendButton.label").get(),
9007 getter_Copies(button0Title));
9008 if (NS_FAILED(rv)) return rv;
9010 PRInt32 buttonPressed;
9011 rv = prompter->
9012 ConfirmEx(nsnull, msgString.get(),
9013 (nsIPrompt::BUTTON_POS_0 * nsIPrompt::BUTTON_TITLE_IS_STRING) +
9014 (nsIPrompt::BUTTON_POS_1 * nsIPrompt::BUTTON_TITLE_CANCEL),
9015 button0Title.get(), nsnull, nsnull, nsnull, nsnull, &buttonPressed);
9016 if (NS_FAILED(rv)) return rv;
9018 *aRepost = (buttonPressed == 0);
9019 return NS_OK;
9022 NS_IMETHODIMP
9023 nsDocShell::GetPromptAndStringBundle(nsIPrompt ** aPrompt,
9024 nsIStringBundle ** aStringBundle)
9026 NS_ENSURE_SUCCESS(GetInterface(NS_GET_IID(nsIPrompt), (void **) aPrompt),
9027 NS_ERROR_FAILURE);
9029 nsCOMPtr<nsIStringBundleService>
9030 stringBundleService(do_GetService(NS_STRINGBUNDLE_CONTRACTID));
9031 NS_ENSURE_TRUE(stringBundleService, NS_ERROR_FAILURE);
9033 NS_ENSURE_SUCCESS(stringBundleService->
9034 CreateBundle(kAppstringsBundleURL,
9035 aStringBundle),
9036 NS_ERROR_FAILURE);
9038 return NS_OK;
9041 NS_IMETHODIMP
9042 nsDocShell::GetChildOffset(nsIDOMNode * aChild, nsIDOMNode * aParent,
9043 PRInt32 * aOffset)
9045 NS_ENSURE_ARG_POINTER(aChild || aParent);
9047 nsCOMPtr<nsIDOMNodeList> childNodes;
9048 NS_ENSURE_SUCCESS(aParent->GetChildNodes(getter_AddRefs(childNodes)),
9049 NS_ERROR_FAILURE);
9050 NS_ENSURE_TRUE(childNodes, NS_ERROR_FAILURE);
9052 PRInt32 i = 0;
9054 for (; PR_TRUE; i++) {
9055 nsCOMPtr<nsIDOMNode> childNode;
9056 NS_ENSURE_SUCCESS(childNodes->Item(i, getter_AddRefs(childNode)),
9057 NS_ERROR_FAILURE);
9058 NS_ENSURE_TRUE(childNode, NS_ERROR_FAILURE);
9060 if (childNode.get() == aChild) {
9061 *aOffset = i;
9062 return NS_OK;
9066 return NS_ERROR_FAILURE;
9069 NS_IMETHODIMP
9070 nsDocShell::GetRootScrollableView(nsIScrollableView ** aOutScrollView)
9072 NS_ENSURE_ARG_POINTER(aOutScrollView);
9074 nsCOMPtr<nsIPresShell> shell;
9075 NS_ENSURE_SUCCESS(GetPresShell(getter_AddRefs(shell)), NS_ERROR_FAILURE);
9076 NS_ENSURE_TRUE(shell, NS_ERROR_NULL_POINTER);
9078 NS_ENSURE_SUCCESS(shell->GetViewManager()->GetRootScrollableView(aOutScrollView),
9079 NS_ERROR_FAILURE);
9081 if (*aOutScrollView == nsnull) {
9082 return NS_ERROR_FAILURE;
9084 return NS_OK;
9087 #ifdef DEBUG
9088 class nsDebugAutoBoolTrueSetter
9090 public:
9091 nsDebugAutoBoolTrueSetter(PRBool *aBool)
9092 : mBool(aBool)
9094 *mBool = PR_TRUE;
9097 ~nsDebugAutoBoolTrueSetter()
9099 *mBool = PR_FALSE;
9101 protected:
9102 PRBool *mBool;
9104 #endif
9106 NS_IMETHODIMP
9107 nsDocShell::EnsureScriptEnvironment()
9109 if (mScriptGlobal)
9110 return NS_OK;
9112 if (mIsBeingDestroyed) {
9113 return NS_ERROR_NOT_AVAILABLE;
9116 #ifdef DEBUG
9117 NS_ASSERTION(!mInEnsureScriptEnv,
9118 "Infinite loop! Calling EnsureScriptEnvironment() from "
9119 "within EnsureScriptEnvironment()!");
9121 // Yeah, this isn't re-entrant safe, but that's ok since if we
9122 // re-enter this method, we'll infinitely loop...
9123 nsDebugAutoBoolTrueSetter boolSetter(&mInEnsureScriptEnv);
9124 #endif
9126 nsCOMPtr<nsIDOMScriptObjectFactory> factory =
9127 do_GetService(kDOMScriptObjectFactoryCID);
9128 NS_ENSURE_TRUE(factory, NS_ERROR_FAILURE);
9130 nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(mTreeOwner));
9131 NS_ENSURE_TRUE(browserChrome, NS_ERROR_NOT_AVAILABLE);
9133 PRUint32 chromeFlags;
9134 browserChrome->GetChromeFlags(&chromeFlags);
9136 PRBool isModalContentWindow =
9137 (chromeFlags & nsIWebBrowserChrome::CHROME_MODAL) &&
9138 !(chromeFlags & nsIWebBrowserChrome::CHROME_OPENAS_CHROME);
9140 // If our window is modal and we're not opened as chrome, make
9141 // this window a modal content window.
9142 factory->NewScriptGlobalObject(mItemType == typeChrome,
9143 isModalContentWindow,
9144 getter_AddRefs(mScriptGlobal));
9145 NS_ENSURE_TRUE(mScriptGlobal, NS_ERROR_FAILURE);
9147 nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(mScriptGlobal));
9148 win->SetDocShell(static_cast<nsIDocShell *>(this));
9150 // Ensure the script object is set to run javascript - other languages
9151 // setup on demand.
9152 // XXXmarkh - should this be setup to run the default language for this doc?
9153 nsresult rv;
9154 rv = mScriptGlobal->EnsureScriptEnvironment(nsIProgrammingLanguage::JAVASCRIPT);
9155 NS_ENSURE_SUCCESS(rv, rv);
9157 return NS_OK;
9161 NS_IMETHODIMP
9162 nsDocShell::EnsureEditorData()
9164 PRBool openDocHasDetachedEditor = mOSHE && mOSHE->HasDetachedEditor();
9165 if (!mEditorData && !mIsBeingDestroyed && !openDocHasDetachedEditor) {
9166 // We shouldn't recreate the editor data if it already exists, or
9167 // we're shutting down, or we already have a detached editor data
9168 // stored in the session history. We should only have one editordata
9169 // per docshell.
9170 mEditorData = new nsDocShellEditorData(this);
9173 return mEditorData ? NS_OK : NS_ERROR_NOT_AVAILABLE;
9176 nsresult
9177 nsDocShell::EnsureTransferableHookData()
9179 if (!mTransferableHookData) {
9180 mTransferableHookData = new nsTransferableHookData();
9181 if (!mTransferableHookData) return NS_ERROR_OUT_OF_MEMORY;
9184 return NS_OK;
9188 NS_IMETHODIMP nsDocShell::EnsureFind()
9190 nsresult rv;
9191 if (!mFind)
9193 mFind = do_CreateInstance("@mozilla.org/embedcomp/find;1", &rv);
9194 if (NS_FAILED(rv)) return rv;
9197 // we promise that the nsIWebBrowserFind that we return has been set
9198 // up to point to the focused, or content window, so we have to
9199 // set that up each time.
9201 nsIScriptGlobalObject* scriptGO = GetScriptGlobalObject();
9202 NS_ENSURE_TRUE(scriptGO, NS_ERROR_UNEXPECTED);
9204 // default to our window
9205 nsCOMPtr<nsIDOMWindow> rootWindow = do_QueryInterface(scriptGO);
9206 nsCOMPtr<nsIDOMWindow> windowToSearch = rootWindow;
9208 // if we can, search the focused window
9209 nsCOMPtr<nsPIDOMWindow> ourWindow = do_QueryInterface(scriptGO);
9210 nsIFocusController *focusController = nsnull;
9211 if (ourWindow)
9212 focusController = ourWindow->GetRootFocusController();
9213 if (focusController)
9215 nsCOMPtr<nsIDOMWindowInternal> focusedWindow;
9216 focusController->GetFocusedWindow(getter_AddRefs(focusedWindow));
9217 if (focusedWindow)
9218 windowToSearch = focusedWindow;
9221 nsCOMPtr<nsIWebBrowserFindInFrames> findInFrames = do_QueryInterface(mFind);
9222 if (!findInFrames) return NS_ERROR_NO_INTERFACE;
9224 rv = findInFrames->SetRootSearchFrame(rootWindow);
9225 if (NS_FAILED(rv)) return rv;
9226 rv = findInFrames->SetCurrentSearchFrame(windowToSearch);
9227 if (NS_FAILED(rv)) return rv;
9229 return NS_OK;
9232 PRBool
9233 nsDocShell::IsFrame()
9235 nsCOMPtr<nsIDocShellTreeItem> parent =
9236 do_QueryInterface(GetAsSupports(mParent));
9237 if (parent) {
9238 PRInt32 parentType = ~mItemType; // Not us
9239 parent->GetItemType(&parentType);
9240 if (parentType == mItemType) // This is a frame
9241 return PR_TRUE;
9244 return PR_FALSE;
9247 NS_IMETHODIMP
9248 nsDocShell::GetHasFocus(PRBool *aHasFocus)
9250 *aHasFocus = mHasFocus;
9251 return NS_OK;
9254 NS_IMETHODIMP
9255 nsDocShell::SetHasFocus(PRBool aHasFocus)
9257 #ifdef DEBUG_DOCSHELL_FOCUS
9258 printf(">>>>>>>>>> nsDocShell::SetHasFocus: %p %s\n", (void*)this,
9259 aHasFocus?"Yes":"No");
9260 #endif
9262 mHasFocus = aHasFocus;
9264 nsDocShellFocusController* dsfc = nsDocShellFocusController::GetInstance();
9265 if (dsfc && aHasFocus) {
9266 dsfc->Focus(this);
9269 if (!aHasFocus) {
9270 // We may be in a situation where the focus outline was shown
9271 // on this document because the user tabbed into it, but the focus
9272 // is now switching to another document via a click. In this case,
9273 // we need to make sure the focus outline is removed from this document.
9274 SetCanvasHasFocus(PR_FALSE);
9277 return NS_OK;
9280 // Find an nsICanvasFrame under aFrame. Only search the principal
9281 // child lists. aFrame must be non-null.
9282 static nsICanvasFrame* FindCanvasFrame(nsIFrame* aFrame)
9284 nsICanvasFrame* canvasFrame = do_QueryFrame(aFrame);
9285 if (canvasFrame) {
9286 return canvasFrame;
9289 nsIFrame* kid = aFrame->GetFirstChild(nsnull);
9290 while (kid) {
9291 canvasFrame = FindCanvasFrame(kid);
9292 if (canvasFrame) {
9293 return canvasFrame;
9295 kid = kid->GetNextSibling();
9298 return nsnull;
9301 //-------------------------------------------------------
9302 // Tells the HTMLFrame/CanvasFrame that is now has focus
9303 NS_IMETHODIMP
9304 nsDocShell::SetCanvasHasFocus(PRBool aCanvasHasFocus)
9306 if (mEditorData && mEditorData->GetEditable())
9307 return NS_ERROR_NOT_AVAILABLE;
9309 nsCOMPtr<nsIPresShell> presShell;
9310 GetPresShell(getter_AddRefs(presShell));
9311 if (!presShell) return NS_ERROR_FAILURE;
9313 nsIDocument *doc = presShell->GetDocument();
9314 if (!doc) return NS_ERROR_FAILURE;
9316 nsIContent *rootContent = doc->GetRootContent();
9317 if (rootContent) {
9318 nsIFrame* frame = presShell->GetPrimaryFrameFor(rootContent);
9319 if (frame) {
9320 frame = frame->GetParent();
9321 if (frame) {
9322 nsICanvasFrame* canvasFrame = do_QueryFrame(frame);
9323 if (canvasFrame) {
9324 return canvasFrame->SetHasFocus(aCanvasHasFocus);
9328 } else {
9329 // Look for the frame the hard way
9330 nsIFrame* frame = presShell->GetRootFrame();
9331 if (frame) {
9332 nsICanvasFrame* canvasFrame = FindCanvasFrame(frame);
9333 if (canvasFrame) {
9334 return canvasFrame->SetHasFocus(aCanvasHasFocus);
9339 return NS_ERROR_FAILURE;
9342 NS_IMETHODIMP
9343 nsDocShell::GetCanvasHasFocus(PRBool *aCanvasHasFocus)
9345 return NS_ERROR_FAILURE;
9348 /* boolean IsBeingDestroyed (); */
9349 NS_IMETHODIMP
9350 nsDocShell::IsBeingDestroyed(PRBool *aDoomed)
9352 NS_ENSURE_ARG(aDoomed);
9353 *aDoomed = mIsBeingDestroyed;
9354 return NS_OK;
9358 NS_IMETHODIMP
9359 nsDocShell::GetIsExecutingOnLoadHandler(PRBool *aResult)
9361 NS_ENSURE_ARG(aResult);
9362 *aResult = mIsExecutingOnLoadHandler;
9363 return NS_OK;
9366 NS_IMETHODIMP
9367 nsDocShell::GetLayoutHistoryState(nsILayoutHistoryState **aLayoutHistoryState)
9369 if (mOSHE)
9370 mOSHE->GetLayoutHistoryState(aLayoutHistoryState);
9371 return NS_OK;
9374 NS_IMETHODIMP
9375 nsDocShell::SetLayoutHistoryState(nsILayoutHistoryState *aLayoutHistoryState)
9377 if (mOSHE)
9378 mOSHE->SetLayoutHistoryState(aLayoutHistoryState);
9379 return NS_OK;
9382 //*****************************************************************************
9383 //*** nsRefreshTimer: Object Management
9384 //*****************************************************************************
9386 nsRefreshTimer::nsRefreshTimer()
9387 : mDelay(0), mRepeat(PR_FALSE), mMetaRefresh(PR_FALSE)
9391 nsRefreshTimer::~nsRefreshTimer()
9395 //*****************************************************************************
9396 // nsRefreshTimer::nsISupports
9397 //*****************************************************************************
9399 NS_IMPL_THREADSAFE_ADDREF(nsRefreshTimer)
9400 NS_IMPL_THREADSAFE_RELEASE(nsRefreshTimer)
9402 NS_INTERFACE_MAP_BEGIN(nsRefreshTimer)
9403 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITimerCallback)
9404 NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
9405 NS_INTERFACE_MAP_END_THREADSAFE
9407 ///*****************************************************************************
9408 // nsRefreshTimer::nsITimerCallback
9409 //******************************************************************************
9410 NS_IMETHODIMP
9411 nsRefreshTimer::Notify(nsITimer * aTimer)
9413 NS_ASSERTION(mDocShell, "DocShell is somehow null");
9415 if (mDocShell && aTimer) {
9416 // Get the delay count to determine load type
9417 PRUint32 delay = 0;
9418 aTimer->GetDelay(&delay);
9419 mDocShell->ForceRefreshURIFromTimer(mURI, delay, mMetaRefresh, aTimer);
9421 return NS_OK;
9424 //*****************************************************************************
9425 //*** nsDocShellFocusController: Object Management
9426 //*****************************************************************************
9427 void
9428 nsDocShellFocusController::Focus(nsIDocShell* aDocShell)
9430 #ifdef DEBUG_DOCSHELL_FOCUS
9431 printf("****** nsDocShellFocusController Focus To: %p Blur To: %p\n",
9432 (void*)aDocShell, (void*)mFocusedDocShell);
9433 #endif
9435 if (aDocShell != mFocusedDocShell) {
9436 if (mFocusedDocShell) {
9437 mFocusedDocShell->SetHasFocus(PR_FALSE);
9439 mFocusedDocShell = aDocShell;
9444 //--------------------------------------------------
9445 // This is need for when the document with focus goes away
9446 void
9447 nsDocShellFocusController::ClosingDown(nsIDocShell* aDocShell)
9449 if (aDocShell == mFocusedDocShell) {
9450 mFocusedDocShell = nsnull;
9454 //*****************************************************************************
9455 // nsDocShell::InterfaceRequestorProxy
9456 //*****************************************************************************
9457 nsDocShell::InterfaceRequestorProxy::InterfaceRequestorProxy(nsIInterfaceRequestor* p)
9459 if (p) {
9460 mWeakPtr = do_GetWeakReference(p);
9464 nsDocShell::InterfaceRequestorProxy::~InterfaceRequestorProxy()
9466 mWeakPtr = nsnull;
9469 NS_IMPL_THREADSAFE_ISUPPORTS1(nsDocShell::InterfaceRequestorProxy, nsIInterfaceRequestor)
9471 NS_IMETHODIMP
9472 nsDocShell::InterfaceRequestorProxy::GetInterface(const nsIID & aIID, void **aSink)
9474 NS_ENSURE_ARG_POINTER(aSink);
9475 nsCOMPtr<nsIInterfaceRequestor> ifReq = do_QueryReferent(mWeakPtr);
9476 if (ifReq) {
9477 return ifReq->GetInterface(aIID, aSink);
9479 *aSink = nsnull;
9480 return NS_NOINTERFACE;
9483 nsresult
9484 nsDocShell::SetBaseUrlForWyciwyg(nsIContentViewer * aContentViewer)
9486 if (!aContentViewer)
9487 return NS_ERROR_FAILURE;
9489 nsCOMPtr<nsIURI> baseURI;
9490 nsCOMPtr<nsIDocument> document;
9491 nsresult rv = NS_ERROR_NOT_AVAILABLE;
9493 if (sURIFixup)
9494 rv = sURIFixup->CreateExposableURI(mCurrentURI,
9495 getter_AddRefs(baseURI));
9497 // Get the current document and set the base uri
9498 if (baseURI) {
9499 nsCOMPtr<nsIDocumentViewer> docViewer(do_QueryInterface(aContentViewer));
9500 if (docViewer) {
9501 rv = docViewer->GetDocument(getter_AddRefs(document));
9502 if (document)
9503 rv = document->SetBaseURI(baseURI);
9506 return rv;
9509 //*****************************************************************************
9510 // nsDocShell::nsIAuthPromptProvider
9511 //*****************************************************************************
9513 NS_IMETHODIMP
9514 nsDocShell::GetAuthPrompt(PRUint32 aPromptReason, const nsIID& iid,
9515 void** aResult)
9517 // a priority prompt request will override a false mAllowAuth setting
9518 PRBool priorityPrompt = (aPromptReason == PROMPT_PROXY);
9520 if (!mAllowAuth && !priorityPrompt)
9521 return NS_ERROR_NOT_AVAILABLE;
9523 // we're either allowing auth, or it's a proxy request
9524 nsresult rv;
9525 nsCOMPtr<nsIPromptFactory> wwatch =
9526 do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
9527 NS_ENSURE_SUCCESS(rv, rv);
9529 rv = EnsureScriptEnvironment();
9530 NS_ENSURE_SUCCESS(rv, rv);
9532 nsCOMPtr<nsIDOMWindow> window(do_QueryInterface(mScriptGlobal));
9534 // Get the an auth prompter for our window so that the parenting
9535 // of the dialogs works as it should when using tabs.
9537 return wwatch->GetPrompt(window, iid,
9538 reinterpret_cast<void**>(aResult));
9541 //*****************************************************************************
9542 // nsDocShell::nsIObserver
9543 //*****************************************************************************
9545 NS_IMETHODIMP
9546 nsDocShell::Observe(nsISupports *aSubject, const char *aTopic,
9547 const PRUnichar *aData)
9549 nsresult rv = NS_OK;
9550 if (mObserveErrorPages &&
9551 !nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) &&
9552 !nsCRT::strcmp(aData,
9553 NS_LITERAL_STRING("browser.xul.error_pages.enabled").get())) {
9555 nsCOMPtr<nsIPrefBranch> prefs(do_QueryInterface(aSubject, &rv));
9556 NS_ENSURE_SUCCESS(rv, rv);
9558 PRBool tmpbool;
9559 rv = prefs->GetBoolPref("browser.xul.error_pages.enabled", &tmpbool);
9560 if (NS_SUCCEEDED(rv))
9561 mUseErrorPages = tmpbool;
9563 } else {
9564 rv = NS_ERROR_UNEXPECTED;
9566 return rv;
9569 //*****************************************************************************
9570 // nsDocShell::nsILoadContext
9571 //*****************************************************************************
9572 NS_IMETHODIMP
9573 nsDocShell::GetAssociatedWindow(nsIDOMWindow** aWindow)
9575 return CallGetInterface(this, aWindow);
9578 NS_IMETHODIMP
9579 nsDocShell::GetTopWindow(nsIDOMWindow** aWindow)
9581 nsresult rv;
9582 nsCOMPtr<nsIDOMWindow> win = do_GetInterface(GetAsSupports(this), &rv);
9583 NS_ENSURE_SUCCESS(rv, rv);
9584 return win->GetTop(aWindow);
9587 NS_IMETHODIMP
9588 nsDocShell::IsAppOfType(PRUint32 aAppType, PRBool *aIsOfType)
9590 nsCOMPtr<nsIDocShell> shell = this;
9591 while (shell) {
9592 PRUint32 type;
9593 shell->GetAppType(&type);
9594 if (type == aAppType) {
9595 *aIsOfType = PR_TRUE;
9596 return NS_OK;
9598 nsCOMPtr<nsIDocShellTreeItem> item = do_QueryInterface(shell);
9599 nsCOMPtr<nsIDocShellTreeItem> parent;
9600 item->GetParent(getter_AddRefs(parent));
9601 shell = do_QueryInterface(parent);
9604 *aIsOfType = PR_FALSE;
9605 return NS_OK;
9608 NS_IMETHODIMP
9609 nsDocShell::GetIsContent(PRBool *aIsContent)
9611 *aIsContent = (mItemType == typeContent);
9612 return NS_OK;
9615 /* static */
9616 nsresult
9617 nsDocShell::URIInheritsSecurityContext(nsIURI* aURI, PRBool* aResult)
9619 // Note: about:blank URIs do NOT inherit the security context from the
9620 // current document, which is what this function tests for...
9621 return NS_URIChainHasFlags(aURI,
9622 nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT,
9623 aResult);
9626 /* static */
9627 PRBool
9628 nsDocShell::URIIsLocalFile(nsIURI *aURI)
9630 PRBool isFile;
9631 nsCOMPtr<nsINetUtil> util = do_GetIOService();
9633 return util && NS_SUCCEEDED(util->ProtocolHasFlags(aURI,
9634 nsIProtocolHandler::URI_IS_LOCAL_FILE,
9635 &isFile)) &&
9636 isFile;
9639 /* static */
9640 PRBool
9641 nsDocShell::IsAboutBlank(nsIURI* aURI)
9643 NS_PRECONDITION(aURI, "Must have URI");
9645 // GetSpec can be expensive for some URIs, so check the scheme first.
9646 PRBool isAbout = PR_FALSE;
9647 if (NS_FAILED(aURI->SchemeIs("about", &isAbout)) || !isAbout) {
9648 return PR_FALSE;
9651 nsCAutoString str;
9652 aURI->GetSpec(str);
9653 return str.EqualsLiteral("about:blank");
9656 PRBool
9657 nsDocShell::IsOKToLoadURI(nsIURI* aURI)
9659 NS_PRECONDITION(aURI, "Must have a URI!");
9661 if (!mFiredUnloadEvent) {
9662 return PR_TRUE;
9665 if (!mLoadingURI) {
9666 return PR_FALSE;
9669 nsCOMPtr<nsIScriptSecurityManager> secMan =
9670 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
9671 return
9672 secMan &&
9673 NS_SUCCEEDED(secMan->CheckSameOriginURI(aURI, mLoadingURI, PR_FALSE));
9676 //*****************************************************************************
9677 // nsClassifierCallback
9678 //*****************************************************************************
9680 NS_IMPL_ISUPPORTS5(nsClassifierCallback,
9681 nsIChannelClassifier,
9682 nsIURIClassifierCallback,
9683 nsIRunnable,
9684 nsIChannelEventSink,
9685 nsIInterfaceRequestor)
9687 NS_IMETHODIMP
9688 nsClassifierCallback::Run()
9690 if (!mChannel) {
9691 return NS_OK;
9694 NS_ASSERTION(!mSuspendedChannel,
9695 "nsClassifierCallback::Run() called while a "
9696 "channel is still suspended.");
9698 nsCOMPtr<nsIChannel> channel;
9699 channel.swap(mChannel);
9701 // Don't bother to run the classifier on a load that has already failed.
9702 // (this might happen after a redirect)
9703 PRUint32 status;
9704 channel->GetStatus(&status);
9705 if (NS_FAILED(status))
9706 return NS_OK;
9708 // Don't bother to run the classifier on a cached load that was
9709 // previously classified.
9710 if (HasBeenClassified(channel)) {
9711 return NS_OK;
9714 nsCOMPtr<nsIURI> uri;
9715 nsresult rv = channel->GetURI(getter_AddRefs(uri));
9716 NS_ENSURE_SUCCESS(rv, rv);
9718 // Don't bother checking certain types of URIs.
9719 PRBool hasFlags;
9720 rv = NS_URIChainHasFlags(uri,
9721 nsIProtocolHandler::URI_DANGEROUS_TO_LOAD,
9722 &hasFlags);
9723 NS_ENSURE_SUCCESS(rv, rv);
9724 if (hasFlags) return NS_OK;
9726 rv = NS_URIChainHasFlags(uri,
9727 nsIProtocolHandler::URI_IS_LOCAL_FILE,
9728 &hasFlags);
9729 NS_ENSURE_SUCCESS(rv, rv);
9730 if (hasFlags) return NS_OK;
9732 rv = NS_URIChainHasFlags(uri,
9733 nsIProtocolHandler::URI_IS_UI_RESOURCE,
9734 &hasFlags);
9735 NS_ENSURE_SUCCESS(rv, rv);
9736 if (hasFlags) return NS_OK;
9738 rv = NS_URIChainHasFlags(uri,
9739 nsIProtocolHandler::URI_IS_LOCAL_RESOURCE,
9740 &hasFlags);
9741 NS_ENSURE_SUCCESS(rv, rv);
9742 if (hasFlags) return NS_OK;
9744 nsCOMPtr<nsIURIClassifier> uriClassifier =
9745 do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID, &rv);
9746 if (rv == NS_ERROR_FACTORY_NOT_REGISTERED ||
9747 rv == NS_ERROR_NOT_AVAILABLE) {
9748 // no URI classifier, ignore this failure.
9749 return NS_OK;
9751 NS_ENSURE_SUCCESS(rv, rv);
9753 PRBool expectCallback;
9754 rv = uriClassifier->Classify(uri, this, &expectCallback);
9755 if (NS_FAILED(rv)) return rv;
9757 if (expectCallback) {
9758 // Suspend the channel, it will be resumed when we get the classifier
9759 // callback.
9760 rv = channel->Suspend();
9761 if (NS_FAILED(rv)) {
9762 // Some channels (including nsJSChannel) fail on Suspend. This
9763 // shouldn't be fatal, but will prevent malware from being
9764 // blocked on these channels.
9765 return NS_OK;
9768 mSuspendedChannel = channel;
9769 #ifdef DEBUG
9770 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
9771 ("nsClassifierCallback[%p]: suspended channel %p",
9772 this, mSuspendedChannel.get()));
9773 #endif
9776 return NS_OK;
9779 // Note in the cache entry that this URL was classified, so that future
9780 // cached loads don't need to be checked.
9781 void
9782 nsClassifierCallback::MarkEntryClassified(nsresult status)
9784 nsCOMPtr<nsICachingChannel> cachingChannel =
9785 do_QueryInterface(mSuspendedChannel);
9786 if (!cachingChannel) {
9787 return;
9790 nsCOMPtr<nsISupports> cacheToken;
9791 cachingChannel->GetCacheToken(getter_AddRefs(cacheToken));
9792 if (!cacheToken) {
9793 return;
9796 nsCOMPtr<nsICacheEntryDescriptor> cacheEntry =
9797 do_QueryInterface(cacheToken);
9798 if (!cacheEntry) {
9799 return;
9802 cacheEntry->SetMetaDataElement("docshell:classified",
9803 NS_SUCCEEDED(status) ? "1" : nsnull);
9806 PRBool
9807 nsClassifierCallback::HasBeenClassified(nsIChannel *aChannel)
9809 nsCOMPtr<nsICachingChannel> cachingChannel =
9810 do_QueryInterface(aChannel);
9811 if (!cachingChannel) {
9812 return PR_FALSE;
9815 // Only check the tag if we are loading from the cache without
9816 // validation.
9817 PRBool fromCache;
9818 if (NS_FAILED(cachingChannel->IsFromCache(&fromCache)) || !fromCache) {
9819 return PR_FALSE;
9822 nsCOMPtr<nsISupports> cacheToken;
9823 cachingChannel->GetCacheToken(getter_AddRefs(cacheToken));
9824 if (!cacheToken) {
9825 return PR_FALSE;
9828 nsCOMPtr<nsICacheEntryDescriptor> cacheEntry =
9829 do_QueryInterface(cacheToken);
9830 if (!cacheEntry) {
9831 return PR_FALSE;
9834 nsXPIDLCString tag;
9835 cacheEntry->GetMetaDataElement("docshell:classified", getter_Copies(tag));
9836 return tag.EqualsLiteral("1");
9839 NS_IMETHODIMP
9840 nsClassifierCallback::OnClassifyComplete(nsresult aErrorCode)
9842 if (mSuspendedChannel) {
9843 MarkEntryClassified(aErrorCode);
9845 if (NS_FAILED(aErrorCode)) {
9846 #ifdef DEBUG
9847 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
9848 ("nsClassifierCallback[%p]: cancelling channel %p with error code: %d",
9849 this, mSuspendedChannel.get(), aErrorCode));
9850 #endif
9851 mSuspendedChannel->Cancel(aErrorCode);
9853 #ifdef DEBUG
9854 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
9855 ("nsClassifierCallback[%p]: resuming channel %p from OnClassifyComplete",
9856 this, mSuspendedChannel.get()));
9857 #endif
9858 mSuspendedChannel->Resume();
9859 mSuspendedChannel = nsnull;
9862 return NS_OK;
9865 NS_IMETHODIMP
9866 nsClassifierCallback::Start(nsIChannel *aChannel, PRBool aInstallListener)
9868 mChannel = aChannel;
9870 if (aInstallListener) {
9871 nsresult rv = aChannel->GetNotificationCallbacks
9872 (getter_AddRefs(mNotificationCallbacks));
9873 NS_ENSURE_SUCCESS(rv, rv);
9875 rv = aChannel->SetNotificationCallbacks
9876 (static_cast<nsIInterfaceRequestor*>(this));
9877 NS_ENSURE_SUCCESS(rv, rv);
9880 return Run();
9883 NS_IMETHODIMP
9884 nsClassifierCallback::OnRedirect(nsIChannel *aOldChannel,
9885 nsIChannel *aNewChannel)
9887 mChannel = aNewChannel;
9889 // we call the Run() from the main loop to give the channel a
9890 // chance to AsyncOpen() before we suspend it.
9891 NS_DispatchToCurrentThread(this);
9893 return NS_OK;
9896 NS_IMETHODIMP
9897 nsClassifierCallback::Cancel()
9899 if (mSuspendedChannel) {
9900 #ifdef DEBUG
9901 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
9902 ("nsClassifierCallback[%p]: resuming channel %p from Cancel()",
9903 this, mSuspendedChannel.get()));
9904 #endif
9905 mSuspendedChannel->Resume();
9906 mSuspendedChannel = nsnull;
9909 if (mChannel) {
9910 mChannel = nsnull;
9913 return NS_OK;
9916 NS_IMETHODIMP
9917 nsClassifierCallback::OnChannelRedirect(nsIChannel *aOldChannel,
9918 nsIChannel *aNewChannel,
9919 PRUint32 aFlags)
9921 nsresult rv = OnRedirect(aOldChannel, aNewChannel);
9922 NS_ENSURE_SUCCESS(rv, rv);
9924 if (mNotificationCallbacks) {
9925 nsCOMPtr<nsIChannelEventSink> sink =
9926 do_GetInterface(mNotificationCallbacks);
9927 if (sink) {
9928 return sink->OnChannelRedirect(aOldChannel, aNewChannel, aFlags);
9932 return NS_OK;
9935 NS_IMETHODIMP
9936 nsClassifierCallback::GetInterface(const nsIID &aIID, void **aResult)
9938 if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
9939 NS_ADDREF_THIS();
9940 *aResult = static_cast<nsIChannelEventSink *>(this);
9941 return NS_OK;
9942 } else if (mNotificationCallbacks) {
9943 return mNotificationCallbacks->GetInterface(aIID, aResult);
9944 } else {
9945 return NS_ERROR_NO_INTERFACE;