Bug 451040 ? Passwords Manager Empty after convert to MozStorage. r=gavin
[wine-gecko.git] / docshell / base / nsDocShell.cpp
blobde9238a594042e3d257dab93819410779c21342a
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"
111 // we want to explore making the document own the load group
112 // so we can associate the document URI with the load group.
113 // until this point, we have an evil hack:
114 #include "nsIHttpChannelInternal.h"
117 // Local Includes
118 #include "nsDocShell.h"
119 #include "nsDocShellLoadInfo.h"
120 #include "nsCDefaultURIFixup.h"
121 #include "nsDocShellEnumerator.h"
122 #include "nsSHistory.h"
123 #include "nsDocShellEditorData.h"
125 // Helper Classes
126 #include "nsDOMError.h"
127 #include "nsEscape.h"
129 // Interfaces Needed
130 #include "nsIUploadChannel.h"
131 #include "nsIProgressEventSink.h"
132 #include "nsIWebProgress.h"
133 #include "nsILayoutHistoryState.h"
134 #include "nsITimer.h"
135 #include "nsISHistoryInternal.h"
136 #include "nsIPrincipal.h"
137 #include "nsIFileURL.h"
138 #include "nsIHistoryEntry.h"
139 #include "nsISHistoryListener.h"
140 #include "nsIWindowWatcher.h"
141 #include "nsIPromptFactory.h"
142 #include "nsIObserver.h"
143 #include "nsINestedURI.h"
144 #include "nsITransportSecurityInfo.h"
145 #include "nsINSSErrorsService.h"
147 // Editor-related
148 #include "nsIEditingSession.h"
150 #include "nsPIDOMWindow.h"
151 #include "nsIDOMDocument.h"
152 #include "nsICachingChannel.h"
153 #include "nsICacheVisitor.h"
154 #include "nsICacheEntryDescriptor.h"
155 #include "nsIMultiPartChannel.h"
156 #include "nsIWyciwygChannel.h"
158 // The following are for bug #13871: Prevent frameset spoofing
159 #include "nsIHTMLDocument.h"
161 // For reporting errors with the console service.
162 // These can go away if error reporting is propagated up past nsDocShell.
163 #include "nsIConsoleService.h"
164 #include "nsIScriptError.h"
166 // used to dispatch urls to default protocol handlers
167 #include "nsCExternalHandlerService.h"
168 #include "nsIExternalProtocolService.h"
170 #include "nsIFocusController.h"
172 #include "nsITextToSubURI.h"
174 #include "nsIJARChannel.h"
176 #include "prlog.h"
177 #include "prmem.h"
179 #include "nsISelectionDisplay.h"
181 #include "nsIGlobalHistory2.h"
182 #include "nsIGlobalHistory3.h"
184 #ifdef DEBUG_DOCSHELL_FOCUS
185 #include "nsIEventStateManager.h"
186 #endif
188 #include "nsIFrame.h"
190 // for embedding
191 #include "nsIWebBrowserChromeFocus.h"
193 #include "nsPluginError.h"
195 static NS_DEFINE_IID(kDeviceContextCID, NS_DEVICE_CONTEXT_CID);
196 static NS_DEFINE_CID(kDOMScriptObjectFactoryCID,
197 NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
198 static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
200 #if defined(DEBUG_bryner) || defined(DEBUG_chb)
201 //#define DEBUG_DOCSHELL_FOCUS
202 #define DEBUG_PAGE_CACHE
203 #endif
205 #include "nsContentErrors.h"
206 #include "nsIFocusEventSuppressor.h"
208 // Number of documents currently loading
209 static PRInt32 gNumberOfDocumentsLoading = 0;
211 // Global count of existing docshells.
212 static PRInt32 gDocShellCount = 0;
214 // Global reference to the URI fixup service.
215 nsIURIFixup *nsDocShell::sURIFixup = 0;
217 // True means we validate window targets to prevent frameset
218 // spoofing. Initialize this to a non-bolean value so we know to check
219 // the pref on the creation of the first docshell.
220 static PRBool gValidateOrigin = (PRBool)0xffffffff;
222 // Hint for native dispatch of events on how long to delay after
223 // all documents have loaded in milliseconds before favoring normal
224 // native event dispatch priorites over performance
225 #define NS_EVENT_STARVATION_DELAY_HINT 2000
227 // This is needed for displaying an error message
228 // when navigation is attempted on a document when printing
229 // The value arbitrary as long as it doesn't conflict with
230 // any of the other values in the errors in DisplayLoadError
231 #define NS_ERROR_DOCUMENT_IS_PRINTMODE NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_GENERAL,2001)
233 #ifdef PR_LOGGING
234 #ifdef DEBUG
235 static PRLogModuleInfo* gDocShellLog;
236 #endif
237 static PRLogModuleInfo* gDocShellLeakLog;
238 #endif
240 const char kBrandBundleURL[] = "chrome://branding/locale/brand.properties";
241 const char kAppstringsBundleURL[] = "chrome://global/locale/appstrings.properties";
243 static void
244 FavorPerformanceHint(PRBool perfOverStarvation, PRUint32 starvationDelay)
246 nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
247 if (appShell)
248 appShell->FavorPerformanceHint(perfOverStarvation, starvationDelay);
251 //*****************************************************************************
252 //*** nsDocShellFocusController
253 //*****************************************************************************
255 class nsDocShellFocusController
258 public:
259 static nsDocShellFocusController* GetInstance() { return &mDocShellFocusControllerSingleton; }
260 virtual ~nsDocShellFocusController(){}
262 void Focus(nsIDocShell* aDS);
263 void ClosingDown(nsIDocShell* aDS);
265 protected:
266 nsDocShellFocusController(){}
268 nsIDocShell* mFocusedDocShell; // very weak reference
270 private:
271 static nsDocShellFocusController mDocShellFocusControllerSingleton;
274 nsDocShellFocusController nsDocShellFocusController::mDocShellFocusControllerSingleton;
276 //*****************************************************************************
277 //*** nsDocShell: Object Management
278 //*****************************************************************************
280 nsDocShell::nsDocShell():
281 nsDocLoader(),
282 mAllowSubframes(PR_TRUE),
283 mAllowPlugins(PR_TRUE),
284 mAllowJavascript(PR_TRUE),
285 mAllowMetaRedirects(PR_TRUE),
286 mAllowImages(PR_TRUE),
287 mFocusDocFirst(PR_FALSE),
288 mHasFocus(PR_FALSE),
289 mCreatingDocument(PR_FALSE),
290 mUseErrorPages(PR_FALSE),
291 mObserveErrorPages(PR_TRUE),
292 mAllowAuth(PR_TRUE),
293 mAllowKeywordFixup(PR_FALSE),
294 mFiredUnloadEvent(PR_FALSE),
295 mEODForCurrentDocument(PR_FALSE),
296 mURIResultedInDocument(PR_FALSE),
297 mIsBeingDestroyed(PR_FALSE),
298 mIsExecutingOnLoadHandler(PR_FALSE),
299 mIsPrintingOrPP(PR_FALSE),
300 mSavingOldViewer(PR_FALSE),
301 mAppType(nsIDocShell::APP_TYPE_UNKNOWN),
302 mChildOffset(0),
303 mBusyFlags(BUSY_FLAGS_NONE),
304 mMarginWidth(0),
305 mMarginHeight(0),
306 mItemType(typeContent),
307 mDefaultScrollbarPref(Scrollbar_Auto, Scrollbar_Auto),
308 mPreviousTransIndex(-1),
309 mLoadedTransIndex(-1),
310 mTreeOwner(nsnull),
311 mChromeEventHandler(nsnull)
312 #ifdef DEBUG
313 , mInEnsureScriptEnv(PR_FALSE)
314 #endif
316 if (gDocShellCount++ == 0) {
317 NS_ASSERTION(sURIFixup == nsnull,
318 "Huh, sURIFixup not null in first nsDocShell ctor!");
320 CallGetService(NS_URIFIXUP_CONTRACTID, &sURIFixup);
323 #ifdef PR_LOGGING
324 #ifdef DEBUG
325 if (! gDocShellLog)
326 gDocShellLog = PR_NewLogModule("nsDocShell");
327 #endif
328 if (nsnull == gDocShellLeakLog)
329 gDocShellLeakLog = PR_NewLogModule("nsDocShellLeak");
330 if (gDocShellLeakLog)
331 PR_LOG(gDocShellLeakLog, PR_LOG_DEBUG, ("DOCSHELL %p created\n", this));
332 #endif
335 nsDocShell::~nsDocShell()
337 nsDocShellFocusController* dsfc = nsDocShellFocusController::GetInstance();
338 if (dsfc) {
339 dsfc->ClosingDown(this);
341 Destroy();
343 if (--gDocShellCount == 0) {
344 NS_IF_RELEASE(sURIFixup);
347 #ifdef PR_LOGGING
348 if (gDocShellLeakLog)
349 PR_LOG(gDocShellLeakLog, PR_LOG_DEBUG, ("DOCSHELL %p destroyed\n", this));
350 #endif
353 nsresult
354 nsDocShell::Init()
356 nsresult rv = nsDocLoader::Init();
357 NS_ENSURE_SUCCESS(rv, rv);
359 NS_ASSERTION(mLoadGroup, "Something went wrong!");
361 mContentListener = new nsDSURIContentListener(this);
362 NS_ENSURE_TRUE(mContentListener, NS_ERROR_OUT_OF_MEMORY);
364 rv = mContentListener->Init();
365 NS_ENSURE_SUCCESS(rv, rv);
367 if (!mStorages.Init())
368 return NS_ERROR_OUT_OF_MEMORY;
370 // We want to hold a strong ref to the loadgroup, so it better hold a weak
371 // ref to us... use an InterfaceRequestorProxy to do this.
372 nsCOMPtr<InterfaceRequestorProxy> proxy =
373 new InterfaceRequestorProxy(static_cast<nsIInterfaceRequestor*>
374 (this));
375 NS_ENSURE_TRUE(proxy, NS_ERROR_OUT_OF_MEMORY);
376 mLoadGroup->SetNotificationCallbacks(proxy);
378 rv = nsDocLoader::AddDocLoaderAsChildOfRoot(this);
379 NS_ENSURE_SUCCESS(rv, rv);
381 // Add as |this| a progress listener to itself. A little weird, but
382 // simpler than reproducing all the listener-notification logic in
383 // overrides of the various methods via which nsDocLoader can be
384 // notified. Note that this holds an nsWeakPtr to ourselves, so it's ok.
385 return AddProgressListener(this, nsIWebProgress::NOTIFY_STATE_DOCUMENT |
386 nsIWebProgress::NOTIFY_STATE_NETWORK);
390 void
391 nsDocShell::DestroyChildren()
393 nsCOMPtr<nsIDocShellTreeItem> shell;
394 PRInt32 n = mChildList.Count();
395 for (PRInt32 i = 0; i < n; i++) {
396 shell = do_QueryInterface(ChildAt(i));
397 NS_ASSERTION(shell, "docshell has null child");
399 if (shell) {
400 shell->SetTreeOwner(nsnull);
404 nsDocLoader::DestroyChildren();
407 //*****************************************************************************
408 // nsDocShell::nsISupports
409 //*****************************************************************************
411 NS_IMPL_ADDREF_INHERITED(nsDocShell, nsDocLoader)
412 NS_IMPL_RELEASE_INHERITED(nsDocShell, nsDocLoader)
414 NS_INTERFACE_MAP_BEGIN(nsDocShell)
415 NS_INTERFACE_MAP_ENTRY(nsIDocShell)
416 NS_INTERFACE_MAP_ENTRY(nsIDocShellTreeItem)
417 NS_INTERFACE_MAP_ENTRY(nsIDocShellTreeNode)
418 NS_INTERFACE_MAP_ENTRY(nsIDocShellHistory)
419 NS_INTERFACE_MAP_ENTRY(nsIWebNavigation)
420 NS_INTERFACE_MAP_ENTRY(nsIBaseWindow)
421 NS_INTERFACE_MAP_ENTRY(nsIScrollable)
422 NS_INTERFACE_MAP_ENTRY(nsITextScroll)
423 NS_INTERFACE_MAP_ENTRY(nsIDocCharset)
424 NS_INTERFACE_MAP_ENTRY(nsIScriptGlobalObjectOwner)
425 NS_INTERFACE_MAP_ENTRY(nsIRefreshURI)
426 NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
427 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
428 NS_INTERFACE_MAP_ENTRY(nsIContentViewerContainer)
429 NS_INTERFACE_MAP_ENTRY(nsIEditorDocShell)
430 NS_INTERFACE_MAP_ENTRY(nsIWebPageDescriptor)
431 NS_INTERFACE_MAP_ENTRY(nsIAuthPromptProvider)
432 NS_INTERFACE_MAP_ENTRY(nsIObserver)
433 NS_INTERFACE_MAP_END_INHERITING(nsDocLoader)
435 ///*****************************************************************************
436 // nsDocShell::nsIInterfaceRequestor
437 //*****************************************************************************
438 NS_IMETHODIMP nsDocShell::GetInterface(const nsIID & aIID, void **aSink)
440 NS_PRECONDITION(aSink, "null out param");
442 *aSink = nsnull;
444 if (aIID.Equals(NS_GET_IID(nsIURIContentListener))) {
445 *aSink = mContentListener;
447 else if (aIID.Equals(NS_GET_IID(nsIScriptGlobalObject)) &&
448 NS_SUCCEEDED(EnsureScriptEnvironment())) {
449 *aSink = mScriptGlobal;
451 else if ((aIID.Equals(NS_GET_IID(nsIDOMWindowInternal)) ||
452 aIID.Equals(NS_GET_IID(nsPIDOMWindow)) ||
453 aIID.Equals(NS_GET_IID(nsIDOMWindow))) &&
454 NS_SUCCEEDED(EnsureScriptEnvironment())) {
455 return mScriptGlobal->QueryInterface(aIID, aSink);
457 else if (aIID.Equals(NS_GET_IID(nsIDOMDocument)) &&
458 NS_SUCCEEDED(EnsureContentViewer())) {
459 mContentViewer->GetDOMDocument((nsIDOMDocument **) aSink);
460 return *aSink ? NS_OK : NS_NOINTERFACE;
462 else if (aIID.Equals(NS_GET_IID(nsIPrompt)) &&
463 NS_SUCCEEDED(EnsureScriptEnvironment())) {
464 nsresult rv;
465 nsCOMPtr<nsIWindowWatcher> wwatch =
466 do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
467 NS_ENSURE_SUCCESS(rv, rv);
469 nsCOMPtr<nsIDOMWindow> window(do_QueryInterface(mScriptGlobal));
471 // Get the an auth prompter for our window so that the parenting
472 // of the dialogs works as it should when using tabs.
474 nsIPrompt *prompt;
475 rv = wwatch->GetNewPrompter(window, &prompt);
476 NS_ENSURE_SUCCESS(rv, rv);
478 *aSink = prompt;
479 return NS_OK;
481 else if (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) ||
482 aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) {
483 return NS_SUCCEEDED(
484 GetAuthPrompt(PROMPT_NORMAL, aIID, aSink)) ?
485 NS_OK : NS_NOINTERFACE;
487 else if (aIID.Equals(NS_GET_IID(nsISHistory))) {
488 nsCOMPtr<nsISHistory> shistory;
489 nsresult
490 rv =
491 GetSessionHistory(getter_AddRefs(shistory));
492 if (NS_SUCCEEDED(rv) && shistory) {
493 *aSink = shistory;
494 NS_ADDREF((nsISupports *) * aSink);
495 return NS_OK;
497 return NS_NOINTERFACE;
499 else if (aIID.Equals(NS_GET_IID(nsIWebBrowserFind))) {
500 nsresult rv = EnsureFind();
501 if (NS_FAILED(rv)) return rv;
503 *aSink = mFind;
504 NS_ADDREF((nsISupports*)*aSink);
505 return NS_OK;
507 else if (aIID.Equals(NS_GET_IID(nsIEditingSession)) && NS_SUCCEEDED(EnsureEditorData())) {
508 nsCOMPtr<nsIEditingSession> editingSession;
509 mEditorData->GetEditingSession(getter_AddRefs(editingSession));
510 if (editingSession)
512 *aSink = editingSession;
513 NS_ADDREF((nsISupports *)*aSink);
514 return NS_OK;
517 return NS_NOINTERFACE;
519 else if (aIID.Equals(NS_GET_IID(nsIClipboardDragDropHookList))
520 && NS_SUCCEEDED(EnsureTransferableHookData())) {
521 *aSink = mTransferableHookData;
522 NS_ADDREF((nsISupports *)*aSink);
523 return NS_OK;
525 else if (aIID.Equals(NS_GET_IID(nsISelectionDisplay))) {
526 nsCOMPtr<nsIPresShell> shell;
527 nsresult rv = GetPresShell(getter_AddRefs(shell));
528 if (NS_SUCCEEDED(rv) && shell)
529 return shell->QueryInterface(aIID,aSink);
531 else if (aIID.Equals(NS_GET_IID(nsIDocShellTreeOwner))) {
532 nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
533 nsresult rv = GetTreeOwner(getter_AddRefs(treeOwner));
534 if (NS_SUCCEEDED(rv) && treeOwner)
535 return treeOwner->QueryInterface(aIID, aSink);
537 else {
538 return nsDocLoader::GetInterface(aIID, aSink);
541 NS_IF_ADDREF(((nsISupports *) * aSink));
542 return *aSink ? NS_OK : NS_NOINTERFACE;
545 PRUint32
546 nsDocShell::
547 ConvertDocShellLoadInfoToLoadType(nsDocShellInfoLoadType aDocShellLoadType)
549 PRUint32 loadType = LOAD_NORMAL;
551 switch (aDocShellLoadType) {
552 case nsIDocShellLoadInfo::loadNormal:
553 loadType = LOAD_NORMAL;
554 break;
555 case nsIDocShellLoadInfo::loadNormalReplace:
556 loadType = LOAD_NORMAL_REPLACE;
557 break;
558 case nsIDocShellLoadInfo::loadNormalExternal:
559 loadType = LOAD_NORMAL_EXTERNAL;
560 break;
561 case nsIDocShellLoadInfo::loadHistory:
562 loadType = LOAD_HISTORY;
563 break;
564 case nsIDocShellLoadInfo::loadNormalBypassCache:
565 loadType = LOAD_NORMAL_BYPASS_CACHE;
566 break;
567 case nsIDocShellLoadInfo::loadNormalBypassProxy:
568 loadType = LOAD_NORMAL_BYPASS_PROXY;
569 break;
570 case nsIDocShellLoadInfo::loadNormalBypassProxyAndCache:
571 loadType = LOAD_NORMAL_BYPASS_PROXY_AND_CACHE;
572 break;
573 case nsIDocShellLoadInfo::loadReloadNormal:
574 loadType = LOAD_RELOAD_NORMAL;
575 break;
576 case nsIDocShellLoadInfo::loadReloadCharsetChange:
577 loadType = LOAD_RELOAD_CHARSET_CHANGE;
578 break;
579 case nsIDocShellLoadInfo::loadReloadBypassCache:
580 loadType = LOAD_RELOAD_BYPASS_CACHE;
581 break;
582 case nsIDocShellLoadInfo::loadReloadBypassProxy:
583 loadType = LOAD_RELOAD_BYPASS_PROXY;
584 break;
585 case nsIDocShellLoadInfo::loadReloadBypassProxyAndCache:
586 loadType = LOAD_RELOAD_BYPASS_PROXY_AND_CACHE;
587 break;
588 case nsIDocShellLoadInfo::loadLink:
589 loadType = LOAD_LINK;
590 break;
591 case nsIDocShellLoadInfo::loadRefresh:
592 loadType = LOAD_REFRESH;
593 break;
594 case nsIDocShellLoadInfo::loadBypassHistory:
595 loadType = LOAD_BYPASS_HISTORY;
596 break;
597 case nsIDocShellLoadInfo::loadStopContent:
598 loadType = LOAD_STOP_CONTENT;
599 break;
600 case nsIDocShellLoadInfo::loadStopContentAndReplace:
601 loadType = LOAD_STOP_CONTENT_AND_REPLACE;
602 break;
603 default:
604 NS_NOTREACHED("Unexpected nsDocShellInfoLoadType value");
607 return loadType;
611 nsDocShellInfoLoadType
612 nsDocShell::ConvertLoadTypeToDocShellLoadInfo(PRUint32 aLoadType)
614 nsDocShellInfoLoadType docShellLoadType = nsIDocShellLoadInfo::loadNormal;
615 switch (aLoadType) {
616 case LOAD_NORMAL:
617 docShellLoadType = nsIDocShellLoadInfo::loadNormal;
618 break;
619 case LOAD_NORMAL_REPLACE:
620 docShellLoadType = nsIDocShellLoadInfo::loadNormalReplace;
621 break;
622 case LOAD_NORMAL_EXTERNAL:
623 docShellLoadType = nsIDocShellLoadInfo::loadNormalExternal;
624 break;
625 case LOAD_NORMAL_BYPASS_CACHE:
626 docShellLoadType = nsIDocShellLoadInfo::loadNormalBypassCache;
627 break;
628 case LOAD_NORMAL_BYPASS_PROXY:
629 docShellLoadType = nsIDocShellLoadInfo::loadNormalBypassProxy;
630 break;
631 case LOAD_NORMAL_BYPASS_PROXY_AND_CACHE:
632 docShellLoadType = nsIDocShellLoadInfo::loadNormalBypassProxyAndCache;
633 break;
634 case LOAD_HISTORY:
635 docShellLoadType = nsIDocShellLoadInfo::loadHistory;
636 break;
637 case LOAD_RELOAD_NORMAL:
638 docShellLoadType = nsIDocShellLoadInfo::loadReloadNormal;
639 break;
640 case LOAD_RELOAD_CHARSET_CHANGE:
641 docShellLoadType = nsIDocShellLoadInfo::loadReloadCharsetChange;
642 break;
643 case LOAD_RELOAD_BYPASS_CACHE:
644 docShellLoadType = nsIDocShellLoadInfo::loadReloadBypassCache;
645 break;
646 case LOAD_RELOAD_BYPASS_PROXY:
647 docShellLoadType = nsIDocShellLoadInfo::loadReloadBypassProxy;
648 break;
649 case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
650 docShellLoadType = nsIDocShellLoadInfo::loadReloadBypassProxyAndCache;
651 break;
652 case LOAD_LINK:
653 docShellLoadType = nsIDocShellLoadInfo::loadLink;
654 break;
655 case LOAD_REFRESH:
656 docShellLoadType = nsIDocShellLoadInfo::loadRefresh;
657 break;
658 case LOAD_BYPASS_HISTORY:
659 case LOAD_ERROR_PAGE:
660 docShellLoadType = nsIDocShellLoadInfo::loadBypassHistory;
661 break;
662 case LOAD_STOP_CONTENT:
663 docShellLoadType = nsIDocShellLoadInfo::loadStopContent;
664 break;
665 case LOAD_STOP_CONTENT_AND_REPLACE:
666 docShellLoadType = nsIDocShellLoadInfo::loadStopContentAndReplace;
667 break;
668 default:
669 NS_NOTREACHED("Unexpected load type value");
672 return docShellLoadType;
675 //*****************************************************************************
676 // nsDocShell::nsIDocShell
677 //*****************************************************************************
678 NS_IMETHODIMP
679 nsDocShell::LoadURI(nsIURI * aURI,
680 nsIDocShellLoadInfo * aLoadInfo,
681 PRUint32 aLoadFlags,
682 PRBool aFirstParty)
684 // Note: we allow loads to get through here even if mFiredUnloadEvent is
685 // true; that case will get handled in LoadInternal or LoadHistoryEntry.
686 if (IsPrintingOrPP()) {
687 return NS_OK; // JS may not handle returning of an error code
689 nsresult rv;
690 nsCOMPtr<nsIURI> referrer;
691 nsCOMPtr<nsIInputStream> postStream;
692 nsCOMPtr<nsIInputStream> headersStream;
693 nsCOMPtr<nsISupports> owner;
694 PRBool inheritOwner = PR_FALSE;
695 PRBool sendReferrer = PR_TRUE;
696 nsCOMPtr<nsISHEntry> shEntry;
697 nsXPIDLString target;
698 PRUint32 loadType = MAKE_LOAD_TYPE(LOAD_NORMAL, aLoadFlags);
700 NS_ENSURE_ARG(aURI);
702 // Extract the info from the DocShellLoadInfo struct...
703 if (aLoadInfo) {
704 aLoadInfo->GetReferrer(getter_AddRefs(referrer));
706 nsDocShellInfoLoadType lt = nsIDocShellLoadInfo::loadNormal;
707 aLoadInfo->GetLoadType(&lt);
708 // Get the appropriate loadType from nsIDocShellLoadInfo type
709 loadType = ConvertDocShellLoadInfoToLoadType(lt);
711 aLoadInfo->GetOwner(getter_AddRefs(owner));
712 aLoadInfo->GetInheritOwner(&inheritOwner);
713 aLoadInfo->GetSHEntry(getter_AddRefs(shEntry));
714 aLoadInfo->GetTarget(getter_Copies(target));
715 aLoadInfo->GetPostDataStream(getter_AddRefs(postStream));
716 aLoadInfo->GetHeadersStream(getter_AddRefs(headersStream));
717 aLoadInfo->GetSendReferrer(&sendReferrer);
720 #if defined(PR_LOGGING) && defined(DEBUG)
721 if (PR_LOG_TEST(gDocShellLog, PR_LOG_DEBUG)) {
722 nsCAutoString uristr;
723 aURI->GetAsciiSpec(uristr);
724 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
725 ("nsDocShell[%p]: loading %s with flags 0x%08x",
726 this, uristr.get(), aLoadFlags));
728 #endif
730 if (!shEntry &&
731 !LOAD_TYPE_HAS_FLAGS(loadType, LOAD_FLAGS_REPLACE_HISTORY)) {
732 // First verify if this is a subframe.
733 nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
734 GetSameTypeParent(getter_AddRefs(parentAsItem));
735 nsCOMPtr<nsIDocShell> parentDS(do_QueryInterface(parentAsItem));
736 PRUint32 parentLoadType;
738 if (parentDS && parentDS != static_cast<nsIDocShell *>(this)) {
739 /* OK. It is a subframe. Checkout the
740 * parent's loadtype. If the parent was loaded thro' a history
741 * mechanism, then get the SH entry for the child from the parent.
742 * This is done to restore frameset navigation while going back/forward.
743 * If the parent was loaded through any other loadType, set the
744 * child's loadType too accordingly, so that session history does not
745 * get confused.
748 // Get the parent's load type
749 parentDS->GetLoadType(&parentLoadType);
751 nsCOMPtr<nsIDocShellHistory> parent(do_QueryInterface(parentAsItem));
752 if (parent) {
753 // Get the ShEntry for the child from the parent
754 parent->GetChildSHEntry(mChildOffset, getter_AddRefs(shEntry));
755 // Make some decisions on the child frame's loadType based on the
756 // parent's loadType.
757 if (mCurrentURI == nsnull) {
758 // This is a newly created frame. Check for exception cases first.
759 // By default the subframe will inherit the parent's loadType.
760 if (shEntry && (parentLoadType == LOAD_NORMAL ||
761 parentLoadType == LOAD_LINK ||
762 parentLoadType == LOAD_NORMAL_EXTERNAL)) {
763 // The parent was loaded normally. In this case, this *brand new* child really shouldn't
764 // have a SHEntry. If it does, it could be because the parent is replacing an
765 // existing frame with a new frame, in the onLoadHandler. We don't want this
766 // url to get into session history. Clear off shEntry, and set laod type to
767 // LOAD_BYPASS_HISTORY.
768 PRBool inOnLoadHandler=PR_FALSE;
769 parentDS->GetIsExecutingOnLoadHandler(&inOnLoadHandler);
770 if (inOnLoadHandler) {
771 loadType = LOAD_NORMAL_REPLACE;
772 shEntry = nsnull;
775 else if (parentLoadType == LOAD_REFRESH) {
776 // Clear shEntry. For refresh loads, we have to load
777 // what comes thro' the pipe, not what's in history.
778 shEntry = nsnull;
780 else if ((parentLoadType == LOAD_BYPASS_HISTORY) ||
781 (parentLoadType == LOAD_ERROR_PAGE) ||
782 (shEntry &&
783 ((parentLoadType & LOAD_CMD_HISTORY) ||
784 (parentLoadType == LOAD_RELOAD_NORMAL) ||
785 (parentLoadType == LOAD_RELOAD_CHARSET_CHANGE)))) {
786 // If the parent url, bypassed history or was loaded from
787 // history, pass on the parent's loadType to the new child
788 // frame too, so that the child frame will also
789 // avoid getting into history.
790 loadType = parentLoadType;
793 else {
794 // This is a pre-existing subframe. If the load was not originally initiated
795 // by session history, (if (!shEntry) condition succeeded) and mCurrentURI is not null,
796 // it is possible that a parent's onLoadHandler or even self's onLoadHandler is loading
797 // a new page in this child. Check parent's and self's busy flag and if it is set,
798 // we don't want this onLoadHandler load to get in to session history.
799 PRUint32 parentBusy = BUSY_FLAGS_NONE;
800 PRUint32 selfBusy = BUSY_FLAGS_NONE;
801 parentDS->GetBusyFlags(&parentBusy);
802 GetBusyFlags(&selfBusy);
803 if (((parentBusy & BUSY_FLAGS_BUSY) ||
804 (selfBusy & BUSY_FLAGS_BUSY)) &&
805 shEntry) {
806 loadType = LOAD_NORMAL_REPLACE;
807 shEntry = nsnull;
810 } // parent
811 } //parentDS
812 else {
813 // This is the root docshell. If we got here while
814 // executing an onLoad Handler,this load will not go
815 // into session history.
816 PRBool inOnLoadHandler=PR_FALSE;
817 GetIsExecutingOnLoadHandler(&inOnLoadHandler);
818 if (inOnLoadHandler) {
819 loadType = LOAD_NORMAL_REPLACE;
822 } // !shEntry
824 if (shEntry) {
825 #ifdef DEBUG
826 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
827 ("nsDocShell[%p]: loading from session history", this));
828 #endif
830 rv = LoadHistoryEntry(shEntry, loadType);
832 // Perform the load...
833 else {
834 // We need an owner (a referring principal). 4 possibilities:
835 // (1) If the system principal was passed in and we're a typeContent
836 // docshell, inherit the principal from the current document
837 // instead.
838 // (2) In all other cases when the principal passed in is not null,
839 // use that principal.
840 // (3) If the caller has allowed inheriting from the current document,
841 // or if we're being called from system code (eg chrome JS or pure
842 // C++) then inheritOwner should be true and InternalLoad will get
843 // an owner from the current document. If none of these things are
844 // true, then
845 // (4) we pass a null owner into the channel, and an owner will be
846 // created later from the channel's internal data.
848 // NOTE: This all only works because the only thing the owner is used
849 // for in InternalLoad is data:, javascript:, and about:blank
850 // URIs. For other URIs this would all be dead wrong!
851 nsCOMPtr<nsIScriptSecurityManager> secMan =
852 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
853 NS_ENSURE_SUCCESS(rv, rv);
855 if (owner && mItemType != typeChrome) {
856 nsCOMPtr<nsIPrincipal> ownerPrincipal = do_QueryInterface(owner);
857 PRBool isSystem;
858 rv = secMan->IsSystemPrincipal(ownerPrincipal, &isSystem);
859 NS_ENSURE_SUCCESS(rv, rv);
861 if (isSystem) {
862 owner = nsnull;
863 inheritOwner = PR_TRUE;
866 if (!owner && !inheritOwner) {
867 // See if there's system or chrome JS code running
868 rv = secMan->SubjectPrincipalIsSystem(&inheritOwner);
869 if (NS_FAILED(rv)) {
870 // Set it back to false
871 inheritOwner = PR_FALSE;
875 PRUint32 flags = 0;
877 if (inheritOwner)
878 flags |= INTERNAL_LOAD_FLAGS_INHERIT_OWNER;
880 if (!sendReferrer)
881 flags |= INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER;
883 if (aLoadFlags & LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP)
884 flags |= INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
886 if (aLoadFlags & LOAD_FLAGS_FIRST_LOAD)
887 flags |= INTERNAL_LOAD_FLAGS_FIRST_LOAD;
889 if (aLoadFlags & LOAD_FLAGS_BYPASS_CLASSIFIER)
890 flags |= INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER;
892 rv = InternalLoad(aURI,
893 referrer,
894 owner,
895 flags,
896 target.get(),
897 nsnull, // No type hint
898 postStream,
899 headersStream,
900 loadType,
901 nsnull, // No SHEntry
902 aFirstParty,
903 nsnull, // No nsIDocShell
904 nsnull); // No nsIRequest
907 return rv;
910 NS_IMETHODIMP
911 nsDocShell::LoadStream(nsIInputStream *aStream, nsIURI * aURI,
912 const nsACString &aContentType,
913 const nsACString &aContentCharset,
914 nsIDocShellLoadInfo * aLoadInfo)
916 NS_ENSURE_ARG(aStream);
918 mAllowKeywordFixup = PR_FALSE;
920 // if the caller doesn't pass in a URI we need to create a dummy URI. necko
921 // currently requires a URI in various places during the load. Some consumers
922 // do as well.
923 nsCOMPtr<nsIURI> uri = aURI;
924 if (!uri) {
925 // HACK ALERT
926 nsresult rv = NS_OK;
927 uri = do_CreateInstance(NS_SIMPLEURI_CONTRACTID, &rv);
928 if (NS_FAILED(rv))
929 return rv;
930 // Make sure that the URI spec "looks" like a protocol and path...
931 // For now, just use a bogus protocol called "internal"
932 rv = uri->SetSpec(NS_LITERAL_CSTRING("internal:load-stream"));
933 if (NS_FAILED(rv))
934 return rv;
937 PRUint32 loadType = LOAD_NORMAL;
938 if (aLoadInfo) {
939 nsDocShellInfoLoadType lt = nsIDocShellLoadInfo::loadNormal;
940 (void) aLoadInfo->GetLoadType(&lt);
941 // Get the appropriate LoadType from nsIDocShellLoadInfo type
942 loadType = ConvertDocShellLoadInfoToLoadType(lt);
945 NS_ENSURE_SUCCESS(Stop(nsIWebNavigation::STOP_NETWORK), NS_ERROR_FAILURE);
947 mLoadType = loadType;
949 // build up a channel for this stream.
950 nsCOMPtr<nsIChannel> channel;
951 NS_ENSURE_SUCCESS(NS_NewInputStreamChannel
952 (getter_AddRefs(channel), uri, aStream,
953 aContentType, aContentCharset),
954 NS_ERROR_FAILURE);
956 nsCOMPtr<nsIURILoader>
957 uriLoader(do_GetService(NS_URI_LOADER_CONTRACTID));
958 NS_ENSURE_TRUE(uriLoader, NS_ERROR_FAILURE);
960 NS_ENSURE_SUCCESS(DoChannelLoad(channel, uriLoader, PR_FALSE),
961 NS_ERROR_FAILURE);
962 return NS_OK;
965 NS_IMETHODIMP
966 nsDocShell::CreateLoadInfo(nsIDocShellLoadInfo ** aLoadInfo)
968 nsDocShellLoadInfo *loadInfo = new nsDocShellLoadInfo();
969 NS_ENSURE_TRUE(loadInfo, NS_ERROR_OUT_OF_MEMORY);
970 nsCOMPtr<nsIDocShellLoadInfo> localRef(loadInfo);
972 *aLoadInfo = localRef;
973 NS_ADDREF(*aLoadInfo);
974 return NS_OK;
979 * Reset state to a new content model within the current document and the document
980 * viewer. Called by the document before initiating an out of band document.write().
982 NS_IMETHODIMP
983 nsDocShell::PrepareForNewContentModel()
985 mEODForCurrentDocument = PR_FALSE;
986 return NS_OK;
990 NS_IMETHODIMP
991 nsDocShell::FirePageHideNotification(PRBool aIsUnload)
993 if (mContentViewer && !mFiredUnloadEvent) {
994 // Keep an explicit reference since calling PageHide could release
995 // mContentViewer
996 nsCOMPtr<nsIContentViewer> kungFuDeathGrip(mContentViewer);
997 mFiredUnloadEvent = PR_TRUE;
999 mContentViewer->PageHide(aIsUnload);
1001 nsAutoTArray<nsCOMPtr<nsIDocShell>, 8> kids;
1002 PRInt32 i, n = mChildList.Count();
1003 kids.SetCapacity(n);
1004 for (i = 0; i < n; i++) {
1005 kids.AppendElement(do_QueryInterface(ChildAt(i)));
1008 n = kids.Length();
1009 for (i = 0; i < n; ++i) {
1010 if (kids[i]) {
1011 kids[i]->FirePageHideNotification(aIsUnload);
1014 // Now make sure our editor, if any, is detached before we go
1015 // any farther.
1016 DetachEditorFromWindow();
1019 return NS_OK;
1023 // Bug 13871: Prevent frameset spoofing
1025 // This routine answers: 'Is origin's document from same domain as
1026 // target's document?'
1028 // file: uris are considered the same domain for the purpose of
1029 // frame navigation regardless of script accessibility (bug 420425)
1031 /* static */
1032 PRBool
1033 nsDocShell::ValidateOrigin(nsIDocShellTreeItem* aOriginTreeItem,
1034 nsIDocShellTreeItem* aTargetTreeItem)
1036 nsCOMPtr<nsIScriptSecurityManager> securityManager =
1037 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
1038 NS_ENSURE_TRUE(securityManager, PR_FALSE);
1040 nsCOMPtr<nsIPrincipal> subjectPrincipal;
1041 nsresult rv =
1042 securityManager->GetSubjectPrincipal(getter_AddRefs(subjectPrincipal));
1043 NS_ENSURE_SUCCESS(rv, PR_FALSE);
1045 if (subjectPrincipal) {
1046 // We're called from JS, check if UniversalBrowserWrite is
1047 // enabled.
1048 PRBool ubwEnabled = PR_FALSE;
1049 rv = securityManager->IsCapabilityEnabled("UniversalBrowserWrite",
1050 &ubwEnabled);
1051 NS_ENSURE_SUCCESS(rv, PR_FALSE);
1053 if (ubwEnabled) {
1054 return PR_TRUE;
1058 // Get origin document principal
1059 nsCOMPtr<nsIDOMDocument> originDOMDocument =
1060 do_GetInterface(aOriginTreeItem);
1061 nsCOMPtr<nsIDocument> originDocument(do_QueryInterface(originDOMDocument));
1062 NS_ENSURE_TRUE(originDocument, PR_FALSE);
1064 // Get target principal
1065 nsCOMPtr<nsIDOMDocument> targetDOMDocument =
1066 do_GetInterface(aTargetTreeItem);
1067 nsCOMPtr<nsIDocument> targetDocument(do_QueryInterface(targetDOMDocument));
1068 NS_ENSURE_TRUE(targetDocument, PR_FALSE);
1070 PRBool equal;
1071 rv = originDocument->NodePrincipal()->
1072 Equals(targetDocument->NodePrincipal(), &equal);
1073 if (NS_SUCCEEDED(rv) && equal) {
1074 return PR_TRUE;
1077 // Not strictly equal, special case if both are file: uris
1078 PRBool originIsFile = PR_FALSE;
1079 PRBool targetIsFile = PR_FALSE;
1080 nsCOMPtr<nsIURI> originURI;
1081 nsCOMPtr<nsIURI> targetURI;
1082 nsCOMPtr<nsIURI> innerOriginURI;
1083 nsCOMPtr<nsIURI> innerTargetURI;
1085 rv = originDocument->NodePrincipal()->GetURI(getter_AddRefs(originURI));
1086 if (NS_SUCCEEDED(rv) && originURI)
1087 innerOriginURI = NS_GetInnermostURI(originURI);
1089 rv = targetDocument->NodePrincipal()->GetURI(getter_AddRefs(targetURI));
1090 if (NS_SUCCEEDED(rv) && targetURI)
1091 innerTargetURI = NS_GetInnermostURI(targetURI);
1093 return innerOriginURI && innerTargetURI &&
1094 NS_SUCCEEDED(innerOriginURI->SchemeIs("file", &originIsFile)) &&
1095 NS_SUCCEEDED(innerTargetURI->SchemeIs("file", &targetIsFile)) &&
1096 originIsFile && targetIsFile;
1099 NS_IMETHODIMP
1100 nsDocShell::GetEldestPresContext(nsPresContext** aPresContext)
1102 nsresult rv = NS_OK;
1104 NS_ENSURE_ARG_POINTER(aPresContext);
1105 *aPresContext = nsnull;
1107 nsCOMPtr<nsIContentViewer> viewer = mContentViewer;
1108 while (viewer) {
1109 nsCOMPtr<nsIContentViewer> prevViewer;
1110 viewer->GetPreviousViewer(getter_AddRefs(prevViewer));
1111 if (prevViewer)
1112 viewer = prevViewer;
1113 else {
1114 nsCOMPtr<nsIDocumentViewer> docv(do_QueryInterface(viewer));
1115 if (docv)
1116 rv = docv->GetPresContext(aPresContext);
1117 break;
1121 return rv;
1124 NS_IMETHODIMP
1125 nsDocShell::GetPresContext(nsPresContext ** aPresContext)
1127 NS_ENSURE_ARG_POINTER(aPresContext);
1128 *aPresContext = nsnull;
1130 if (!mContentViewer)
1131 return NS_OK;
1133 nsCOMPtr<nsIDocumentViewer> docv(do_QueryInterface(mContentViewer));
1134 NS_ENSURE_TRUE(docv, NS_ERROR_NO_INTERFACE);
1136 return docv->GetPresContext(aPresContext);
1139 NS_IMETHODIMP
1140 nsDocShell::GetPresShell(nsIPresShell ** aPresShell)
1142 nsresult rv = NS_OK;
1144 NS_ENSURE_ARG_POINTER(aPresShell);
1145 *aPresShell = nsnull;
1147 nsCOMPtr<nsPresContext> presContext;
1148 (void) GetPresContext(getter_AddRefs(presContext));
1150 if (presContext) {
1151 NS_IF_ADDREF(*aPresShell = presContext->GetPresShell());
1154 return rv;
1157 NS_IMETHODIMP
1158 nsDocShell::GetEldestPresShell(nsIPresShell** aPresShell)
1160 nsresult rv = NS_OK;
1162 NS_ENSURE_ARG_POINTER(aPresShell);
1163 *aPresShell = nsnull;
1165 nsCOMPtr<nsPresContext> presContext;
1166 (void) GetEldestPresContext(getter_AddRefs(presContext));
1168 if (presContext) {
1169 NS_IF_ADDREF(*aPresShell = presContext->GetPresShell());
1172 return rv;
1175 NS_IMETHODIMP
1176 nsDocShell::GetContentViewer(nsIContentViewer ** aContentViewer)
1178 NS_ENSURE_ARG_POINTER(aContentViewer);
1180 *aContentViewer = mContentViewer;
1181 NS_IF_ADDREF(*aContentViewer);
1182 return NS_OK;
1185 NS_IMETHODIMP
1186 nsDocShell::SetChromeEventHandler(nsIDOMEventTarget* aChromeEventHandler)
1188 nsCOMPtr<nsPIDOMEventTarget> piTarget =
1189 do_QueryInterface(aChromeEventHandler);
1190 // Weak reference. Don't addref.
1191 mChromeEventHandler = piTarget;
1193 nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(mScriptGlobal));
1194 if (win) {
1195 win->SetChromeEventHandler(piTarget);
1198 return NS_OK;
1201 NS_IMETHODIMP
1202 nsDocShell::GetChromeEventHandler(nsIDOMEventTarget** aChromeEventHandler)
1204 NS_ENSURE_ARG_POINTER(aChromeEventHandler);
1205 nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(mChromeEventHandler);
1206 target.swap(*aChromeEventHandler);
1207 return NS_OK;
1210 /* [noscript] void setCurrentURI (in nsIURI uri); */
1211 NS_IMETHODIMP
1212 nsDocShell::SetCurrentURI(nsIURI *aURI)
1214 SetCurrentURI(aURI, nsnull, PR_TRUE);
1215 return NS_OK;
1218 PRBool
1219 nsDocShell::SetCurrentURI(nsIURI *aURI, nsIRequest *aRequest,
1220 PRBool aFireOnLocationChange)
1222 #ifdef PR_LOGGING
1223 if (gDocShellLeakLog && PR_LOG_TEST(gDocShellLeakLog, PR_LOG_DEBUG)) {
1224 nsCAutoString spec;
1225 if (aURI)
1226 aURI->GetSpec(spec);
1227 PR_LogPrint("DOCSHELL %p SetCurrentURI %s\n", this, spec.get());
1229 #endif
1231 // We don't want to send a location change when we're displaying an error
1232 // page, and we don't want to change our idea of "current URI" either
1233 if (mLoadType == LOAD_ERROR_PAGE) {
1234 return PR_FALSE;
1237 mCurrentURI = NS_TryToMakeImmutable(aURI);
1239 PRBool isRoot = PR_FALSE; // Is this the root docshell
1240 PRBool isSubFrame = PR_FALSE; // Is this a subframe navigation?
1242 nsCOMPtr<nsIDocShellTreeItem> root;
1244 GetSameTypeRootTreeItem(getter_AddRefs(root));
1245 if (root.get() == static_cast<nsIDocShellTreeItem *>(this))
1247 // This is the root docshell
1248 isRoot = PR_TRUE;
1250 if (mLSHE) {
1251 mLSHE->GetIsSubFrame(&isSubFrame);
1254 if (!isSubFrame && !isRoot) {
1256 * We don't want to send OnLocationChange notifications when
1257 * a subframe is being loaded for the first time, while
1258 * visiting a frameset page
1260 return PR_FALSE;
1263 if (aFireOnLocationChange) {
1264 FireOnLocationChange(this, aRequest, aURI);
1266 return !aFireOnLocationChange;
1269 NS_IMETHODIMP
1270 nsDocShell::GetCharset(char** aCharset)
1272 NS_ENSURE_ARG_POINTER(aCharset);
1273 *aCharset = nsnull;
1275 nsCOMPtr<nsIPresShell> presShell;
1276 GetPresShell(getter_AddRefs(presShell));
1277 NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
1278 nsIDocument *doc = presShell->GetDocument();
1279 NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
1280 *aCharset = ToNewCString(doc->GetDocumentCharacterSet());
1281 if (!*aCharset) {
1282 return NS_ERROR_OUT_OF_MEMORY;
1285 return NS_OK;
1288 NS_IMETHODIMP
1289 nsDocShell::SetCharset(const char* aCharset)
1291 // set the default charset
1292 nsCOMPtr<nsIContentViewer> viewer;
1293 GetContentViewer(getter_AddRefs(viewer));
1294 if (viewer) {
1295 nsCOMPtr<nsIMarkupDocumentViewer> muDV(do_QueryInterface(viewer));
1296 if (muDV) {
1297 NS_ENSURE_SUCCESS(muDV->SetDefaultCharacterSet(nsDependentCString(aCharset)),
1298 NS_ERROR_FAILURE);
1302 // set the charset override
1303 nsCOMPtr<nsIDocumentCharsetInfo> dcInfo;
1304 GetDocumentCharsetInfo(getter_AddRefs(dcInfo));
1305 if (dcInfo) {
1306 nsCOMPtr<nsIAtom> csAtom;
1307 csAtom = do_GetAtom(aCharset);
1308 dcInfo->SetForcedCharset(csAtom);
1311 return NS_OK;
1314 NS_IMETHODIMP
1315 nsDocShell::GetDocumentCharsetInfo(nsIDocumentCharsetInfo **
1316 aDocumentCharsetInfo)
1318 NS_ENSURE_ARG_POINTER(aDocumentCharsetInfo);
1320 // if the mDocumentCharsetInfo does not exist already, we create it now
1321 if (!mDocumentCharsetInfo) {
1322 mDocumentCharsetInfo = do_CreateInstance(NS_DOCUMENTCHARSETINFO_CONTRACTID);
1323 if (!mDocumentCharsetInfo)
1324 return NS_ERROR_FAILURE;
1327 *aDocumentCharsetInfo = mDocumentCharsetInfo;
1328 NS_IF_ADDREF(*aDocumentCharsetInfo);
1329 return NS_OK;
1332 NS_IMETHODIMP
1333 nsDocShell::SetDocumentCharsetInfo(nsIDocumentCharsetInfo *
1334 aDocumentCharsetInfo)
1336 mDocumentCharsetInfo = aDocumentCharsetInfo;
1337 return NS_OK;
1340 NS_IMETHODIMP
1341 nsDocShell::GetChannelIsUnsafe(PRBool *aUnsafe)
1343 *aUnsafe = PR_FALSE;
1345 nsCOMPtr<nsIChannel> channel;
1346 GetCurrentDocumentChannel(getter_AddRefs(channel));
1347 if (!channel) {
1348 return NS_OK;
1351 nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(channel);
1352 if (!jarChannel) {
1353 return NS_OK;
1356 return jarChannel->GetIsUnsafe(aUnsafe);
1359 NS_IMETHODIMP
1360 nsDocShell::GetAllowPlugins(PRBool * aAllowPlugins)
1362 NS_ENSURE_ARG_POINTER(aAllowPlugins);
1364 *aAllowPlugins = mAllowPlugins;
1365 if (!mAllowPlugins) {
1366 return NS_OK;
1369 PRBool unsafe;
1370 *aAllowPlugins = NS_SUCCEEDED(GetChannelIsUnsafe(&unsafe)) && !unsafe;
1371 return NS_OK;
1374 NS_IMETHODIMP
1375 nsDocShell::SetAllowPlugins(PRBool aAllowPlugins)
1377 mAllowPlugins = aAllowPlugins;
1378 //XXX should enable or disable a plugin host
1379 return NS_OK;
1382 NS_IMETHODIMP
1383 nsDocShell::GetAllowJavascript(PRBool * aAllowJavascript)
1385 NS_ENSURE_ARG_POINTER(aAllowJavascript);
1387 *aAllowJavascript = mAllowJavascript;
1388 if (!mAllowJavascript) {
1389 return NS_OK;
1392 PRBool unsafe;
1393 *aAllowJavascript = NS_SUCCEEDED(GetChannelIsUnsafe(&unsafe)) && !unsafe;
1394 return NS_OK;
1397 NS_IMETHODIMP
1398 nsDocShell::SetAllowJavascript(PRBool aAllowJavascript)
1400 mAllowJavascript = aAllowJavascript;
1401 return NS_OK;
1404 NS_IMETHODIMP nsDocShell::GetAllowMetaRedirects(PRBool * aReturn)
1406 NS_ENSURE_ARG_POINTER(aReturn);
1408 *aReturn = mAllowMetaRedirects;
1409 if (!mAllowMetaRedirects) {
1410 return NS_OK;
1413 PRBool unsafe;
1414 *aReturn = NS_SUCCEEDED(GetChannelIsUnsafe(&unsafe)) && !unsafe;
1415 return NS_OK;
1418 NS_IMETHODIMP nsDocShell::SetAllowMetaRedirects(PRBool aValue)
1420 mAllowMetaRedirects = aValue;
1421 return NS_OK;
1424 NS_IMETHODIMP nsDocShell::GetAllowSubframes(PRBool * aAllowSubframes)
1426 NS_ENSURE_ARG_POINTER(aAllowSubframes);
1428 *aAllowSubframes = mAllowSubframes;
1429 return NS_OK;
1432 NS_IMETHODIMP nsDocShell::SetAllowSubframes(PRBool aAllowSubframes)
1434 mAllowSubframes = aAllowSubframes;
1435 return NS_OK;
1438 NS_IMETHODIMP nsDocShell::GetAllowImages(PRBool * aAllowImages)
1440 NS_ENSURE_ARG_POINTER(aAllowImages);
1442 *aAllowImages = mAllowImages;
1443 return NS_OK;
1446 NS_IMETHODIMP nsDocShell::SetAllowImages(PRBool aAllowImages)
1448 mAllowImages = aAllowImages;
1449 return NS_OK;
1452 NS_IMETHODIMP
1453 nsDocShell::GetDocShellEnumerator(PRInt32 aItemType, PRInt32 aDirection, nsISimpleEnumerator **outEnum)
1455 NS_ENSURE_ARG_POINTER(outEnum);
1456 *outEnum = nsnull;
1458 nsRefPtr<nsDocShellEnumerator> docShellEnum;
1459 if (aDirection == ENUMERATE_FORWARDS)
1460 docShellEnum = new nsDocShellForwardsEnumerator;
1461 else
1462 docShellEnum = new nsDocShellBackwardsEnumerator;
1464 if (!docShellEnum) return NS_ERROR_OUT_OF_MEMORY;
1466 nsresult rv = docShellEnum->SetEnumDocShellType(aItemType);
1467 if (NS_FAILED(rv)) return rv;
1469 rv = docShellEnum->SetEnumerationRootItem((nsIDocShellTreeItem *)this);
1470 if (NS_FAILED(rv)) return rv;
1472 rv = docShellEnum->First();
1473 if (NS_FAILED(rv)) return rv;
1475 rv = docShellEnum->QueryInterface(NS_GET_IID(nsISimpleEnumerator), (void **)outEnum);
1477 return rv;
1480 NS_IMETHODIMP
1481 nsDocShell::GetAppType(PRUint32 * aAppType)
1483 *aAppType = mAppType;
1484 return NS_OK;
1487 NS_IMETHODIMP
1488 nsDocShell::SetAppType(PRUint32 aAppType)
1490 mAppType = aAppType;
1491 return NS_OK;
1495 NS_IMETHODIMP
1496 nsDocShell::GetAllowAuth(PRBool * aAllowAuth)
1498 *aAllowAuth = mAllowAuth;
1499 return NS_OK;
1502 NS_IMETHODIMP
1503 nsDocShell::SetAllowAuth(PRBool aAllowAuth)
1505 mAllowAuth = aAllowAuth;
1506 return NS_OK;
1509 NS_IMETHODIMP
1510 nsDocShell::GetZoom(float *zoom)
1512 NS_ENSURE_ARG_POINTER(zoom);
1513 *zoom = 1.0f;
1514 return NS_OK;
1517 NS_IMETHODIMP
1518 nsDocShell::SetZoom(float zoom)
1520 return NS_ERROR_NOT_IMPLEMENTED;
1523 NS_IMETHODIMP
1524 nsDocShell::GetMarginWidth(PRInt32 * aWidth)
1526 NS_ENSURE_ARG_POINTER(aWidth);
1528 *aWidth = mMarginWidth;
1529 return NS_OK;
1532 NS_IMETHODIMP
1533 nsDocShell::SetMarginWidth(PRInt32 aWidth)
1535 mMarginWidth = aWidth;
1536 return NS_OK;
1539 NS_IMETHODIMP
1540 nsDocShell::GetMarginHeight(PRInt32 * aHeight)
1542 NS_ENSURE_ARG_POINTER(aHeight);
1544 *aHeight = mMarginHeight;
1545 return NS_OK;
1548 NS_IMETHODIMP
1549 nsDocShell::SetMarginHeight(PRInt32 aHeight)
1551 mMarginHeight = aHeight;
1552 return NS_OK;
1555 NS_IMETHODIMP
1556 nsDocShell::GetBusyFlags(PRUint32 * aBusyFlags)
1558 NS_ENSURE_ARG_POINTER(aBusyFlags);
1560 *aBusyFlags = mBusyFlags;
1561 return NS_OK;
1564 NS_IMETHODIMP
1565 nsDocShell::TabToTreeOwner(PRBool aForward, PRBool* aTookFocus)
1567 NS_ENSURE_ARG_POINTER(aTookFocus);
1569 nsCOMPtr<nsIWebBrowserChromeFocus> chromeFocus = do_GetInterface(mTreeOwner);
1570 if (chromeFocus) {
1571 if (aForward)
1572 *aTookFocus = NS_SUCCEEDED(chromeFocus->FocusNextElement());
1573 else
1574 *aTookFocus = NS_SUCCEEDED(chromeFocus->FocusPrevElement());
1575 } else
1576 *aTookFocus = PR_FALSE;
1578 return NS_OK;
1581 NS_IMETHODIMP
1582 nsDocShell::GetSecurityUI(nsISecureBrowserUI **aSecurityUI)
1584 NS_IF_ADDREF(*aSecurityUI = mSecurityUI);
1585 return NS_OK;
1588 NS_IMETHODIMP
1589 nsDocShell::SetSecurityUI(nsISecureBrowserUI *aSecurityUI)
1591 mSecurityUI = aSecurityUI;
1592 return NS_OK;
1595 NS_IMETHODIMP
1596 nsDocShell::GetUseErrorPages(PRBool *aUseErrorPages)
1598 *aUseErrorPages = mUseErrorPages;
1599 return NS_OK;
1602 NS_IMETHODIMP
1603 nsDocShell::SetUseErrorPages(PRBool aUseErrorPages)
1605 // If mUseErrorPages is set explicitly, stop observing the pref.
1606 if (mObserveErrorPages) {
1607 nsCOMPtr<nsIPrefBranch2> prefs(do_QueryInterface(mPrefs));
1608 if (prefs) {
1609 prefs->RemoveObserver("browser.xul.error_pages.enabled", this);
1610 mObserveErrorPages = PR_FALSE;
1613 mUseErrorPages = aUseErrorPages;
1614 return NS_OK;
1617 NS_IMETHODIMP
1618 nsDocShell::GetPreviousTransIndex(PRInt32 *aPreviousTransIndex)
1620 *aPreviousTransIndex = mPreviousTransIndex;
1621 return NS_OK;
1624 NS_IMETHODIMP
1625 nsDocShell::GetLoadedTransIndex(PRInt32 *aLoadedTransIndex)
1627 *aLoadedTransIndex = mLoadedTransIndex;
1628 return NS_OK;
1631 NS_IMETHODIMP
1632 nsDocShell::HistoryPurged(PRInt32 aNumEntries)
1634 // These indices are used for fastback cache eviction, to determine
1635 // which session history entries are candidates for content viewer
1636 // eviction. We need to adjust by the number of entries that we
1637 // just purged from history, so that we look at the right session history
1638 // entries during eviction.
1639 mPreviousTransIndex = PR_MAX(-1, mPreviousTransIndex - aNumEntries);
1640 mLoadedTransIndex = PR_MAX(0, mLoadedTransIndex - aNumEntries);
1642 PRInt32 count = mChildList.Count();
1643 for (PRInt32 i = 0; i < count; ++i) {
1644 nsCOMPtr<nsIDocShell> shell = do_QueryInterface(ChildAt(i));
1645 if (shell) {
1646 shell->HistoryPurged(aNumEntries);
1650 return NS_OK;
1653 NS_IMETHODIMP
1654 nsDocShell::GetSessionStorageForURI(nsIURI* aURI,
1655 nsIDOMStorage** aStorage)
1657 NS_ENSURE_ARG_POINTER(aStorage);
1659 *aStorage = nsnull;
1661 nsCOMPtr<nsIDocShellTreeItem> topItem;
1662 nsresult rv = GetSameTypeRootTreeItem(getter_AddRefs(topItem));
1663 if (NS_FAILED(rv))
1664 return rv;
1666 if (!topItem)
1667 return NS_ERROR_FAILURE;
1669 nsCOMPtr<nsIDocShell> topDocShell = do_QueryInterface(topItem);
1670 if (topDocShell != this)
1671 return topDocShell->GetSessionStorageForURI(aURI, aStorage);
1673 nsCAutoString currentDomain;
1674 rv = aURI->GetAsciiHost(currentDomain);
1675 NS_ENSURE_SUCCESS(rv, rv);
1677 if (currentDomain.IsEmpty())
1678 return NS_OK;
1680 if (!mStorages.Get(currentDomain, aStorage)) {
1681 nsCOMPtr<nsIDOMStorage> newstorage =
1682 do_CreateInstance("@mozilla.org/dom/storage;1");
1683 if (!newstorage)
1684 return NS_ERROR_OUT_OF_MEMORY;
1686 nsCOMPtr<nsPIDOMStorage> pistorage = do_QueryInterface(newstorage);
1687 if (!pistorage)
1688 return NS_ERROR_FAILURE;
1689 pistorage->Init(aURI, NS_ConvertUTF8toUTF16(currentDomain), PR_FALSE);
1691 if (!mStorages.Put(currentDomain, newstorage))
1692 return NS_ERROR_OUT_OF_MEMORY;
1694 *aStorage = newstorage;
1695 NS_ADDREF(*aStorage);
1698 return NS_OK;
1701 nsresult
1702 nsDocShell::AddSessionStorage(const nsACString& aDomain,
1703 nsIDOMStorage* aStorage)
1705 NS_ENSURE_ARG_POINTER(aStorage);
1707 if (aDomain.IsEmpty())
1708 return NS_OK;
1710 nsCOMPtr<nsIDocShellTreeItem> topItem;
1711 nsresult rv = GetSameTypeRootTreeItem(getter_AddRefs(topItem));
1712 if (NS_FAILED(rv))
1713 return rv;
1715 if (topItem) {
1716 nsCOMPtr<nsIDocShell> topDocShell = do_QueryInterface(topItem);
1717 if (topDocShell == this) {
1718 if (!mStorages.Put(aDomain, aStorage))
1719 return NS_ERROR_OUT_OF_MEMORY;
1721 else {
1722 return topDocShell->AddSessionStorage(aDomain, aStorage);
1726 return NS_OK;
1729 NS_IMETHODIMP
1730 nsDocShell::GetCurrentDocumentChannel(nsIChannel** aResult)
1732 *aResult = nsnull;
1733 if (!mContentViewer)
1734 return NS_OK;
1736 nsCOMPtr<nsIDOMDocument> domDoc;
1737 nsresult rv = mContentViewer->GetDOMDocument(getter_AddRefs(domDoc));
1738 if (NS_FAILED(rv))
1739 return rv;
1741 nsCOMPtr<nsIDocument> doc(do_QueryInterface(domDoc));
1742 if (doc) {
1743 *aResult = doc->GetChannel();
1744 NS_IF_ADDREF(*aResult);
1747 return NS_OK;
1750 //*****************************************************************************
1751 // nsDocShell::nsIDocShellTreeItem
1752 //*****************************************************************************
1754 NS_IMETHODIMP
1755 nsDocShell::GetName(PRUnichar ** aName)
1757 NS_ENSURE_ARG_POINTER(aName);
1758 *aName = ToNewUnicode(mName);
1759 return NS_OK;
1762 NS_IMETHODIMP
1763 nsDocShell::SetName(const PRUnichar * aName)
1765 mName = aName; // this does a copy of aName
1766 return NS_OK;
1769 NS_IMETHODIMP
1770 nsDocShell::NameEquals(const PRUnichar *aName, PRBool *_retval)
1772 NS_ENSURE_ARG_POINTER(aName);
1773 NS_ENSURE_ARG_POINTER(_retval);
1774 *_retval = mName.Equals(aName);
1775 return NS_OK;
1778 NS_IMETHODIMP
1779 nsDocShell::GetItemType(PRInt32 * aItemType)
1781 NS_ENSURE_ARG_POINTER(aItemType);
1783 *aItemType = mItemType;
1784 return NS_OK;
1787 NS_IMETHODIMP
1788 nsDocShell::SetItemType(PRInt32 aItemType)
1790 NS_ENSURE_ARG((aItemType == typeChrome) || (typeContent == aItemType));
1792 // Only allow setting the type on root docshells. Those would be the ones
1793 // that have the docloader service as mParent or have no mParent at all.
1794 nsCOMPtr<nsIDocumentLoader> docLoaderService =
1795 do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID);
1796 NS_ENSURE_TRUE(docLoaderService, NS_ERROR_UNEXPECTED);
1798 NS_ENSURE_STATE(!mParent || mParent == docLoaderService);
1800 mItemType = aItemType;
1802 // disable auth prompting for anything but content
1803 mAllowAuth = mItemType == typeContent;
1805 return NS_OK;
1808 NS_IMETHODIMP
1809 nsDocShell::GetParent(nsIDocShellTreeItem ** aParent)
1811 if (!mParent) {
1812 *aParent = nsnull;
1813 } else {
1814 CallQueryInterface(mParent, aParent);
1816 // Note that in the case when the parent is not an nsIDocShellTreeItem we
1817 // don't want to throw; we just want to return null.
1818 return NS_OK;
1821 nsresult
1822 nsDocShell::SetDocLoaderParent(nsDocLoader * aParent)
1824 nsDocLoader::SetDocLoaderParent(aParent);
1826 // Curse ambiguous nsISupports inheritance!
1827 nsISupports* parent = GetAsSupports(aParent);
1829 // If parent is another docshell, we inherit all their flags for
1830 // allowing plugins, scripting etc.
1831 nsCOMPtr<nsIDocShell> parentAsDocShell(do_QueryInterface(parent));
1832 if (parentAsDocShell)
1834 PRBool value;
1835 if (NS_SUCCEEDED(parentAsDocShell->GetAllowPlugins(&value)))
1837 SetAllowPlugins(value);
1839 if (NS_SUCCEEDED(parentAsDocShell->GetAllowJavascript(&value)))
1841 SetAllowJavascript(value);
1843 if (NS_SUCCEEDED(parentAsDocShell->GetAllowMetaRedirects(&value)))
1845 SetAllowMetaRedirects(value);
1847 if (NS_SUCCEEDED(parentAsDocShell->GetAllowSubframes(&value)))
1849 SetAllowSubframes(value);
1851 if (NS_SUCCEEDED(parentAsDocShell->GetAllowImages(&value)))
1853 SetAllowImages(value);
1857 nsCOMPtr<nsIURIContentListener> parentURIListener(do_GetInterface(parent));
1858 if (parentURIListener)
1859 mContentListener->SetParentContentListener(parentURIListener);
1860 return NS_OK;
1863 NS_IMETHODIMP
1864 nsDocShell::GetSameTypeParent(nsIDocShellTreeItem ** aParent)
1866 NS_ENSURE_ARG_POINTER(aParent);
1867 *aParent = nsnull;
1869 nsCOMPtr<nsIDocShellTreeItem> parent =
1870 do_QueryInterface(GetAsSupports(mParent));
1871 if (!parent)
1872 return NS_OK;
1874 PRInt32 parentType;
1875 NS_ENSURE_SUCCESS(parent->GetItemType(&parentType), NS_ERROR_FAILURE);
1877 if (parentType == mItemType) {
1878 parent.swap(*aParent);
1880 return NS_OK;
1883 NS_IMETHODIMP
1884 nsDocShell::GetRootTreeItem(nsIDocShellTreeItem ** aRootTreeItem)
1886 NS_ENSURE_ARG_POINTER(aRootTreeItem);
1887 *aRootTreeItem = static_cast<nsIDocShellTreeItem *>(this);
1889 nsCOMPtr<nsIDocShellTreeItem> parent;
1890 NS_ENSURE_SUCCESS(GetParent(getter_AddRefs(parent)), NS_ERROR_FAILURE);
1891 while (parent) {
1892 *aRootTreeItem = parent;
1893 NS_ENSURE_SUCCESS((*aRootTreeItem)->GetParent(getter_AddRefs(parent)),
1894 NS_ERROR_FAILURE);
1896 NS_ADDREF(*aRootTreeItem);
1897 return NS_OK;
1900 NS_IMETHODIMP
1901 nsDocShell::GetSameTypeRootTreeItem(nsIDocShellTreeItem ** aRootTreeItem)
1903 NS_ENSURE_ARG_POINTER(aRootTreeItem);
1904 *aRootTreeItem = static_cast<nsIDocShellTreeItem *>(this);
1906 nsCOMPtr<nsIDocShellTreeItem> parent;
1907 NS_ENSURE_SUCCESS(GetSameTypeParent(getter_AddRefs(parent)),
1908 NS_ERROR_FAILURE);
1909 while (parent) {
1910 *aRootTreeItem = parent;
1911 NS_ENSURE_SUCCESS((*aRootTreeItem)->
1912 GetSameTypeParent(getter_AddRefs(parent)),
1913 NS_ERROR_FAILURE);
1915 NS_ADDREF(*aRootTreeItem);
1916 return NS_OK;
1919 /* static */
1920 PRBool
1921 nsDocShell::CanAccessItem(nsIDocShellTreeItem* aTargetItem,
1922 nsIDocShellTreeItem* aAccessingItem,
1923 PRBool aConsiderOpener)
1925 NS_PRECONDITION(aTargetItem, "Must have target item!");
1927 if (!gValidateOrigin || !aAccessingItem) {
1928 // Good to go
1929 return PR_TRUE;
1932 // XXXbz should we care if aAccessingItem or the document therein is
1933 // chrome? Should those get extra privileges?
1935 // For historical context, see:
1937 // Bug 13871: Prevent frameset spoofing
1938 // Bug 103638: Targets with same name in different windows open in wrong
1939 // window with javascript
1940 // Bug 408052: Adopt "ancestor" frame navigation policy
1942 // Now do a security check
1944 // Allow navigation if
1945 // 1) aAccessingItem can script aTargetItem or one of its ancestors in
1946 // the frame hierarchy or
1947 // 2) aTargetItem is a top-level frame and aAccessingItem is its descendant
1948 // 3) aTargetItem is a top-level frame and aAccessingItem can target
1949 // its opener per rule (1) or (2).
1951 if (aTargetItem == aAccessingItem) {
1952 // A frame is allowed to navigate itself.
1953 return PR_TRUE;
1956 nsCOMPtr<nsIDocShellTreeItem> accessingRoot;
1957 aAccessingItem->GetSameTypeRootTreeItem(getter_AddRefs(accessingRoot));
1959 if (aTargetItem == accessingRoot) {
1960 // A frame can navigate its root.
1961 return PR_TRUE;
1964 // Check if aAccessingItem can navigate one of aTargetItem's ancestors.
1965 nsCOMPtr<nsIDocShellTreeItem> target = aTargetItem;
1966 do {
1967 if (ValidateOrigin(aAccessingItem, target)) {
1968 return PR_TRUE;
1971 nsCOMPtr<nsIDocShellTreeItem> parent;
1972 target->GetSameTypeParent(getter_AddRefs(parent));
1973 parent.swap(target);
1974 } while (target);
1976 nsCOMPtr<nsIDocShellTreeItem> targetRoot;
1977 aTargetItem->GetSameTypeRootTreeItem(getter_AddRefs(targetRoot));
1979 if (aTargetItem != targetRoot) {
1980 // target is a subframe, not in accessor's frame hierarchy, and all its
1981 // ancestors have origins different from that of the accessor. Don't
1982 // allow access.
1983 return PR_FALSE;
1986 if (!aConsiderOpener) {
1987 // All done here
1988 return PR_FALSE;
1991 nsCOMPtr<nsIDOMWindow> targetWindow(do_GetInterface(aTargetItem));
1992 nsCOMPtr<nsIDOMWindowInternal> targetInternal(do_QueryInterface(targetWindow));
1993 if (!targetInternal) {
1994 NS_ERROR("This should not happen, really");
1995 return PR_FALSE;
1998 nsCOMPtr<nsIDOMWindowInternal> targetOpener;
1999 targetInternal->GetOpener(getter_AddRefs(targetOpener));
2000 nsCOMPtr<nsIWebNavigation> openerWebNav(do_GetInterface(targetOpener));
2001 nsCOMPtr<nsIDocShellTreeItem> openerItem(do_QueryInterface(openerWebNav));
2003 if (!openerItem) {
2004 return PR_FALSE;
2007 return CanAccessItem(openerItem, aAccessingItem, PR_FALSE);
2010 static PRBool
2011 ItemIsActive(nsIDocShellTreeItem *aItem)
2013 nsCOMPtr<nsIDOMWindow> tmp(do_GetInterface(aItem));
2014 nsCOMPtr<nsIDOMWindowInternal> window(do_QueryInterface(tmp));
2016 if (window) {
2017 PRBool isClosed;
2019 if (NS_SUCCEEDED(window->GetClosed(&isClosed)) && !isClosed) {
2020 return PR_TRUE;
2024 return PR_FALSE;
2027 NS_IMETHODIMP
2028 nsDocShell::FindItemWithName(const PRUnichar * aName,
2029 nsISupports * aRequestor,
2030 nsIDocShellTreeItem * aOriginalRequestor,
2031 nsIDocShellTreeItem ** _retval)
2033 NS_ENSURE_ARG(aName);
2034 NS_ENSURE_ARG_POINTER(_retval);
2036 // If we don't find one, we return NS_OK and a null result
2037 *_retval = nsnull;
2039 if (!*aName)
2040 return NS_OK;
2042 if (!aRequestor)
2044 nsCOMPtr<nsIDocShellTreeItem> foundItem;
2046 // This is the entry point into the target-finding algorithm. Check
2047 // for special names. This should only be done once, hence the check
2048 // for a null aRequestor.
2050 nsDependentString name(aName);
2051 if (name.LowerCaseEqualsLiteral("_self")) {
2052 foundItem = this;
2054 else if (name.LowerCaseEqualsLiteral("_blank"))
2056 // Just return null. Caller must handle creating a new window with
2057 // a blank name himself.
2058 return NS_OK;
2060 else if (name.LowerCaseEqualsLiteral("_parent"))
2062 GetSameTypeParent(getter_AddRefs(foundItem));
2063 if(!foundItem)
2064 foundItem = this;
2066 else if (name.LowerCaseEqualsLiteral("_top"))
2068 GetSameTypeRootTreeItem(getter_AddRefs(foundItem));
2069 NS_ASSERTION(foundItem, "Must have this; worst case it's us!");
2071 // _main is an IE target which should be case-insensitive but isn't
2072 // see bug 217886 for details
2073 else if (name.LowerCaseEqualsLiteral("_content") ||
2074 name.EqualsLiteral("_main"))
2076 // Must pass our same type root as requestor to the
2077 // treeowner to make sure things work right.
2078 nsCOMPtr<nsIDocShellTreeItem> root;
2079 GetSameTypeRootTreeItem(getter_AddRefs(root));
2080 if (mTreeOwner) {
2081 NS_ASSERTION(root, "Must have this; worst case it's us!");
2082 mTreeOwner->FindItemWithName(aName, root, aOriginalRequestor,
2083 getter_AddRefs(foundItem));
2085 #ifdef DEBUG
2086 else {
2087 NS_ERROR("Someone isn't setting up the tree owner. "
2088 "You might like to try that. "
2089 "Things will.....you know, work.");
2090 // Note: _content should always exist. If we don't have one
2091 // hanging off the treeowner, just create a named window....
2092 // so don't return here, in case we did that and can now find
2093 // it.
2094 // XXXbz should we be using |root| instead of creating
2095 // a new window?
2097 #endif
2100 if (foundItem && !CanAccessItem(foundItem, aOriginalRequestor)) {
2101 foundItem = nsnull;
2104 if (foundItem) {
2105 // We return foundItem here even if it's not an active
2106 // item since all the names we've dealt with so far are
2107 // special cases that we won't bother looking for further.
2109 foundItem.swap(*_retval);
2110 return NS_OK;
2114 // Keep looking
2116 // First we check our name.
2117 if (mName.Equals(aName) && ItemIsActive(this) &&
2118 CanAccessItem(this, aOriginalRequestor)) {
2119 NS_ADDREF(*_retval = this);
2120 return NS_OK;
2123 // This QI may fail, but the places where we want to compare, comparing
2124 // against nsnull serves the same purpose.
2125 nsCOMPtr<nsIDocShellTreeItem> reqAsTreeItem(do_QueryInterface(aRequestor));
2127 // Second we check our children making sure not to ask a child if
2128 // it is the aRequestor.
2129 #ifdef DEBUG
2130 nsresult rv =
2131 #endif
2132 FindChildWithName(aName, PR_TRUE, PR_TRUE, reqAsTreeItem,
2133 aOriginalRequestor, _retval);
2134 NS_ASSERTION(NS_SUCCEEDED(rv),
2135 "FindChildWithName should not be failing here.");
2136 if (*_retval)
2137 return NS_OK;
2139 // Third if we have a parent and it isn't the requestor then we
2140 // should ask it to do the search. If it is the requestor we
2141 // should just stop here and let the parent do the rest. If we
2142 // don't have a parent, then we should ask the
2143 // docShellTreeOwner to do the search.
2144 nsCOMPtr<nsIDocShellTreeItem> parentAsTreeItem =
2145 do_QueryInterface(GetAsSupports(mParent));
2146 if (parentAsTreeItem) {
2147 if (parentAsTreeItem == reqAsTreeItem)
2148 return NS_OK;
2150 PRInt32 parentType;
2151 parentAsTreeItem->GetItemType(&parentType);
2152 if (parentType == mItemType) {
2153 return parentAsTreeItem->
2154 FindItemWithName(aName,
2155 static_cast<nsIDocShellTreeItem*>
2156 (this),
2157 aOriginalRequestor,
2158 _retval);
2162 // If the parent is null or not of the same type fall through and ask tree
2163 // owner.
2165 // This may fail, but comparing against null serves the same purpose
2166 nsCOMPtr<nsIDocShellTreeOwner>
2167 reqAsTreeOwner(do_QueryInterface(aRequestor));
2169 if (mTreeOwner && mTreeOwner != reqAsTreeOwner) {
2170 return mTreeOwner->
2171 FindItemWithName(aName, this, aOriginalRequestor, _retval);
2174 return NS_OK;
2177 NS_IMETHODIMP
2178 nsDocShell::GetTreeOwner(nsIDocShellTreeOwner ** aTreeOwner)
2180 NS_ENSURE_ARG_POINTER(aTreeOwner);
2182 *aTreeOwner = mTreeOwner;
2183 NS_IF_ADDREF(*aTreeOwner);
2184 return NS_OK;
2187 #ifdef DEBUG_DOCSHELL_FOCUS
2188 static void
2189 PrintDocTree(nsIDocShellTreeItem * aParentNode, int aLevel)
2191 for (PRInt32 i=0;i<aLevel;i++) printf(" ");
2193 PRInt32 childWebshellCount;
2194 aParentNode->GetChildCount(&childWebshellCount);
2195 nsCOMPtr<nsIDocShell> parentAsDocShell(do_QueryInterface(aParentNode));
2196 PRInt32 type;
2197 aParentNode->GetItemType(&type);
2198 nsCOMPtr<nsIPresShell> presShell;
2199 parentAsDocShell->GetPresShell(getter_AddRefs(presShell));
2200 nsCOMPtr<nsPresContext> presContext;
2201 parentAsDocShell->GetPresContext(getter_AddRefs(presContext));
2202 nsIDocument *doc = presShell->GetDocument();
2204 nsCOMPtr<nsIDOMWindowInternal> domwin(doc->GetWindow());
2206 nsCOMPtr<nsIWidget> widget;
2207 nsIViewManager* vm = presShell->GetViewManager();
2208 if (vm) {
2209 vm->GetWidget(getter_AddRefs(widget));
2211 nsIContent* rootContent = doc->GetRootContent();
2213 printf("DS %p Ty %s Doc %p DW %p EM %p CN %p\n",
2214 (void*)parentAsDocShell.get(),
2215 type==nsIDocShellTreeItem::typeChrome?"Chr":"Con",
2216 (void*)doc, (void*)domwin.get(),
2217 (void*)presContext->EventStateManager(), (void*)rootContent);
2219 if (childWebshellCount > 0) {
2220 for (PRInt32 i=0;i<childWebshellCount;i++) {
2221 nsCOMPtr<nsIDocShellTreeItem> child;
2222 aParentNode->GetChildAt(i, getter_AddRefs(child));
2223 PrintDocTree(child, aLevel+1);
2228 static void
2229 PrintDocTree(nsIDocShellTreeItem * aParentNode)
2231 NS_ASSERTION(aParentNode, "Pointer is null!");
2233 nsCOMPtr<nsIDocShellTreeItem> parentItem;
2234 aParentNode->GetParent(getter_AddRefs(parentItem));
2235 while (parentItem) {
2236 nsCOMPtr<nsIDocShellTreeItem>tmp;
2237 parentItem->GetParent(getter_AddRefs(tmp));
2238 if (!tmp) {
2239 break;
2241 parentItem = tmp;
2244 if (!parentItem) {
2245 parentItem = aParentNode;
2248 PrintDocTree(parentItem, 0);
2250 #endif
2252 NS_IMETHODIMP
2253 nsDocShell::SetTreeOwner(nsIDocShellTreeOwner * aTreeOwner)
2255 #ifdef DEBUG_DOCSHELL_FOCUS
2256 nsCOMPtr<nsIDocShellTreeItem> item(do_QueryInterface(aTreeOwner));
2257 if (item) {
2258 PrintDocTree(item);
2260 #endif
2262 // Don't automatically set the progress based on the tree owner for frames
2263 if (!IsFrame()) {
2264 nsCOMPtr<nsIWebProgress> webProgress =
2265 do_QueryInterface(GetAsSupports(this));
2267 if (webProgress) {
2268 nsCOMPtr<nsIWebProgressListener>
2269 oldListener(do_QueryInterface(mTreeOwner));
2270 nsCOMPtr<nsIWebProgressListener>
2271 newListener(do_QueryInterface(aTreeOwner));
2273 if (oldListener) {
2274 webProgress->RemoveProgressListener(oldListener);
2277 if (newListener) {
2278 webProgress->AddProgressListener(newListener,
2279 nsIWebProgress::NOTIFY_ALL);
2284 mTreeOwner = aTreeOwner; // Weak reference per API
2286 PRInt32 i, n = mChildList.Count();
2287 for (i = 0; i < n; i++) {
2288 nsCOMPtr<nsIDocShellTreeItem> child = do_QueryInterface(ChildAt(i));
2289 NS_ENSURE_TRUE(child, NS_ERROR_FAILURE);
2290 PRInt32 childType = ~mItemType; // Set it to not us in case the get fails
2291 child->GetItemType(&childType); // We don't care if this fails, if it does we won't set the owner
2292 if (childType == mItemType)
2293 child->SetTreeOwner(aTreeOwner);
2296 return NS_OK;
2299 NS_IMETHODIMP
2300 nsDocShell::SetChildOffset(PRUint32 aChildOffset)
2302 mChildOffset = aChildOffset;
2303 return NS_OK;
2306 NS_IMETHODIMP
2307 nsDocShell::GetIsInUnload(PRBool* aIsInUnload)
2309 *aIsInUnload = mFiredUnloadEvent;
2310 return NS_OK;
2313 //*****************************************************************************
2314 // nsDocShell::nsIDocShellTreeNode
2315 //*****************************************************************************
2317 NS_IMETHODIMP
2318 nsDocShell::GetChildCount(PRInt32 * aChildCount)
2320 NS_ENSURE_ARG_POINTER(aChildCount);
2321 *aChildCount = mChildList.Count();
2322 return NS_OK;
2327 NS_IMETHODIMP
2328 nsDocShell::AddChild(nsIDocShellTreeItem * aChild)
2330 NS_ENSURE_ARG_POINTER(aChild);
2332 nsRefPtr<nsDocLoader> childAsDocLoader = GetAsDocLoader(aChild);
2333 NS_ENSURE_TRUE(childAsDocLoader, NS_ERROR_UNEXPECTED);
2335 // Make sure we're not creating a loop in the docshell tree
2336 nsDocLoader* ancestor = this;
2337 do {
2338 if (childAsDocLoader == ancestor) {
2339 return NS_ERROR_ILLEGAL_VALUE;
2341 ancestor = ancestor->GetParent();
2342 } while (ancestor);
2344 // Make sure to remove the child from its current parent.
2345 nsDocLoader* childsParent = childAsDocLoader->GetParent();
2346 if (childsParent) {
2347 childsParent->RemoveChildLoader(childAsDocLoader);
2350 // Make sure to clear the treeowner in case this child is a different type
2351 // from us.
2352 aChild->SetTreeOwner(nsnull);
2354 nsresult res = AddChildLoader(childAsDocLoader);
2355 NS_ENSURE_SUCCESS(res, res);
2356 NS_ASSERTION(mChildList.Count() > 0,
2357 "child list must not be empty after a successful add");
2359 // Set the child's index in the parent's children list
2360 // XXX What if the parent had different types of children?
2361 // XXX in that case docshell hierarchy and SH hierarchy won't match.
2363 nsCOMPtr<nsIDocShell> childDocShell = do_QueryInterface(aChild);
2364 if (childDocShell) {
2365 // If there are frameloaders in the finalization list, reduce
2366 // the offset so that the SH hierarchy is more likely to match the
2367 // docshell hierarchy
2368 nsCOMPtr<nsIDOMDocument> domDoc =
2369 do_GetInterface(GetAsSupports(this));
2370 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
2371 PRUint32 offset = mChildList.Count() - 1;
2372 if (doc) {
2373 PRUint32 oldChildCount = offset; // Current child count - 1
2374 for (PRUint32 i = 0; i < oldChildCount; ++i) {
2375 nsCOMPtr<nsIDocShell> child = do_QueryInterface(ChildAt(i));
2376 if (doc->FrameLoaderScheduledToBeFinalized(child)) {
2377 --offset;
2382 childDocShell->SetChildOffset(offset);
2386 /* Set the child's global history if the parent has one */
2387 if (mGlobalHistory) {
2388 nsCOMPtr<nsIDocShellHistory>
2389 dsHistoryChild(do_QueryInterface(aChild));
2390 if (dsHistoryChild)
2391 dsHistoryChild->SetUseGlobalHistory(PR_TRUE);
2395 PRInt32 childType = ~mItemType; // Set it to not us in case the get fails
2396 aChild->GetItemType(&childType);
2397 if (childType != mItemType)
2398 return NS_OK;
2399 // Everything below here is only done when the child is the same type.
2402 aChild->SetTreeOwner(mTreeOwner);
2404 nsCOMPtr<nsIDocShell> childAsDocShell(do_QueryInterface(aChild));
2405 if (!childAsDocShell)
2406 return NS_OK;
2408 // charset, style-disabling, and zoom will be inherited in SetupNewViewer()
2410 // Now take this document's charset and set the parentCharset field of the
2411 // child's DocumentCharsetInfo to it. We'll later use that field, in the
2412 // loading process, for the charset choosing algorithm.
2413 // If we fail, at any point, we just return NS_OK.
2414 // This code has some performance impact. But this will be reduced when
2415 // the current charset will finally be stored as an Atom, avoiding the
2416 // alias resolution extra look-up.
2418 // we are NOT going to propagate the charset is this Chrome's docshell
2419 if (mItemType == nsIDocShellTreeItem::typeChrome)
2420 return NS_OK;
2422 // get the child's docCSInfo object
2423 nsCOMPtr<nsIDocumentCharsetInfo> dcInfo = NULL;
2424 res = childAsDocShell->GetDocumentCharsetInfo(getter_AddRefs(dcInfo));
2425 if (NS_FAILED(res) || (!dcInfo))
2426 return NS_OK;
2428 // get the parent's current charset
2429 nsCOMPtr<nsIDocumentViewer> docv(do_QueryInterface(mContentViewer));
2430 if (!docv)
2431 return NS_OK;
2432 nsCOMPtr<nsIDocument> doc;
2433 res = docv->GetDocument(getter_AddRefs(doc));
2434 if (NS_FAILED(res) || (!doc))
2435 return NS_OK;
2436 const nsACString &parentCS = doc->GetDocumentCharacterSet();
2438 PRBool isWyciwyg = PR_FALSE;
2440 if (mCurrentURI) {
2441 // Check if the url is wyciwyg
2442 mCurrentURI->SchemeIs("wyciwyg", &isWyciwyg);
2445 if (!isWyciwyg) {
2446 // If this docshell is loaded from a wyciwyg: URI, don't
2447 // advertise our charset since it does not in any way reflect
2448 // the actual source charset, which is what we're trying to
2449 // expose here.
2451 // set the child's parentCharset
2452 nsCOMPtr<nsIAtom> parentCSAtom(do_GetAtom(parentCS));
2453 res = dcInfo->SetParentCharset(parentCSAtom);
2454 if (NS_FAILED(res))
2455 return NS_OK;
2457 PRInt32 charsetSource = doc->GetDocumentCharacterSetSource();
2459 // set the child's parentCharset
2460 res = dcInfo->SetParentCharsetSource(charsetSource);
2461 if (NS_FAILED(res))
2462 return NS_OK;
2465 // printf("### 1 >>> Adding child. Parent CS = %s. ItemType = %d.\n", NS_LossyConvertUTF16toASCII(parentCS).get(), mItemType);
2467 return NS_OK;
2470 NS_IMETHODIMP
2471 nsDocShell::RemoveChild(nsIDocShellTreeItem * aChild)
2473 NS_ENSURE_ARG_POINTER(aChild);
2475 nsRefPtr<nsDocLoader> childAsDocLoader = GetAsDocLoader(aChild);
2476 NS_ENSURE_TRUE(childAsDocLoader, NS_ERROR_UNEXPECTED);
2478 nsresult rv = RemoveChildLoader(childAsDocLoader);
2479 NS_ENSURE_SUCCESS(rv, rv);
2481 aChild->SetTreeOwner(nsnull);
2483 return nsDocLoader::AddDocLoaderAsChildOfRoot(childAsDocLoader);
2486 NS_IMETHODIMP
2487 nsDocShell::GetChildAt(PRInt32 aIndex, nsIDocShellTreeItem ** aChild)
2489 NS_ENSURE_ARG_POINTER(aChild);
2491 #ifdef DEBUG
2492 if (aIndex < 0) {
2493 NS_WARNING("Negative index passed to GetChildAt");
2495 else if (aIndex >= mChildList.Count()) {
2496 NS_WARNING("Too large an index passed to GetChildAt");
2498 #endif
2500 nsIDocumentLoader* child = SafeChildAt(aIndex);
2501 NS_ENSURE_TRUE(child, NS_ERROR_UNEXPECTED);
2503 return CallQueryInterface(child, aChild);
2506 NS_IMETHODIMP
2507 nsDocShell::FindChildWithName(const PRUnichar * aName,
2508 PRBool aRecurse, PRBool aSameType,
2509 nsIDocShellTreeItem * aRequestor,
2510 nsIDocShellTreeItem * aOriginalRequestor,
2511 nsIDocShellTreeItem ** _retval)
2513 NS_ENSURE_ARG(aName);
2514 NS_ENSURE_ARG_POINTER(_retval);
2516 *_retval = nsnull; // if we don't find one, we return NS_OK and a null result
2518 if (!*aName)
2519 return NS_OK;
2521 nsXPIDLString childName;
2522 PRInt32 i, n = mChildList.Count();
2523 for (i = 0; i < n; i++) {
2524 nsCOMPtr<nsIDocShellTreeItem> child = do_QueryInterface(ChildAt(i));
2525 NS_ENSURE_TRUE(child, NS_ERROR_FAILURE);
2526 PRInt32 childType;
2527 child->GetItemType(&childType);
2529 if (aSameType && (childType != mItemType))
2530 continue;
2532 PRBool childNameEquals = PR_FALSE;
2533 child->NameEquals(aName, &childNameEquals);
2534 if (childNameEquals && ItemIsActive(child) &&
2535 CanAccessItem(child, aOriginalRequestor)) {
2536 child.swap(*_retval);
2537 break;
2540 if (childType != mItemType) //Only ask it to check children if it is same type
2541 continue;
2543 if (aRecurse && (aRequestor != child)) // Only ask the child if it isn't the requestor
2545 // See if child contains the shell with the given name
2546 #ifdef DEBUG
2547 nsresult rv =
2548 #endif
2549 child->FindChildWithName(aName, PR_TRUE,
2550 aSameType,
2551 static_cast<nsIDocShellTreeItem*>
2552 (this),
2553 aOriginalRequestor,
2554 _retval);
2555 NS_ASSERTION(NS_SUCCEEDED(rv),
2556 "FindChildWithName should not fail here");
2557 if (*_retval) // found it
2558 return NS_OK;
2561 return NS_OK;
2564 //*****************************************************************************
2565 // nsDocShell::nsIDocShellHistory
2566 //*****************************************************************************
2567 NS_IMETHODIMP
2568 nsDocShell::GetChildSHEntry(PRInt32 aChildOffset, nsISHEntry ** aResult)
2570 nsresult rv = NS_OK;
2572 NS_ENSURE_ARG_POINTER(aResult);
2573 *aResult = nsnull;
2576 // A nsISHEntry for a child is *only* available when the parent is in
2577 // the progress of loading a document too...
2579 if (mLSHE) {
2580 /* Before looking for the subframe's url, check
2581 * the expiration status of the parent. If the parent
2582 * has expired from cache, then subframes will not be
2583 * loaded from history in certain situations.
2585 PRBool parentExpired=PR_FALSE;
2586 mLSHE->GetExpirationStatus(&parentExpired);
2588 /* Get the parent's Load Type so that it can be set on the child too.
2589 * By default give a loadHistory value
2591 PRUint32 loadType = nsIDocShellLoadInfo::loadHistory;
2592 mLSHE->GetLoadType(&loadType);
2593 // If the user did a shift-reload on this frameset page,
2594 // we don't want to load the subframes from history.
2595 if (loadType == nsIDocShellLoadInfo::loadReloadBypassCache ||
2596 loadType == nsIDocShellLoadInfo::loadReloadBypassProxy ||
2597 loadType == nsIDocShellLoadInfo::loadReloadBypassProxyAndCache ||
2598 loadType == nsIDocShellLoadInfo::loadRefresh)
2599 return rv;
2601 /* If the user pressed reload and the parent frame has expired
2602 * from cache, we do not want to load the child frame from history.
2604 if (parentExpired && (loadType == nsIDocShellLoadInfo::loadReloadNormal)) {
2605 // The parent has expired. Return null.
2606 *aResult = nsnull;
2607 return rv;
2610 nsCOMPtr<nsISHContainer> container(do_QueryInterface(mLSHE));
2611 if (container) {
2612 // Get the child subframe from session history.
2613 rv = container->GetChildAt(aChildOffset, aResult);
2614 if (*aResult)
2615 (*aResult)->SetLoadType(loadType);
2618 return rv;
2621 NS_IMETHODIMP
2622 nsDocShell::AddChildSHEntry(nsISHEntry * aCloneRef, nsISHEntry * aNewEntry,
2623 PRInt32 aChildOffset)
2625 nsresult rv;
2627 if (mLSHE) {
2628 /* You get here if you are currently building a
2629 * hierarchy ie.,you just visited a frameset page
2631 nsCOMPtr<nsISHContainer> container(do_QueryInterface(mLSHE, &rv));
2632 if (container) {
2633 rv = container->AddChild(aNewEntry, aChildOffset);
2636 else if (!aCloneRef) {
2637 /* This is an initial load in some subframe. Just append it if we can */
2638 nsCOMPtr<nsISHContainer> container(do_QueryInterface(mOSHE, &rv));
2639 if (container) {
2640 rv = container->AddChild(aNewEntry, aChildOffset);
2643 else if (mSessionHistory) {
2644 /* You are currently in the rootDocShell.
2645 * You will get here when a subframe has a new url
2646 * to load and you have walked up the tree all the
2647 * way to the top to clone the current SHEntry hierarchy
2648 * and replace the subframe where a new url was loaded with
2649 * a new entry.
2651 PRInt32 index = -1;
2652 nsCOMPtr<nsIHistoryEntry> currentHE;
2653 mSessionHistory->GetIndex(&index);
2654 if (index < 0)
2655 return NS_ERROR_FAILURE;
2657 rv = mSessionHistory->GetEntryAtIndex(index, PR_FALSE,
2658 getter_AddRefs(currentHE));
2659 NS_ENSURE_TRUE(currentHE, NS_ERROR_FAILURE);
2661 nsCOMPtr<nsISHEntry> currentEntry(do_QueryInterface(currentHE));
2662 if (currentEntry) {
2663 PRUint32 cloneID = 0;
2664 nsCOMPtr<nsISHEntry> nextEntry;
2665 aCloneRef->GetID(&cloneID);
2666 rv = CloneAndReplace(currentEntry, this, cloneID, aNewEntry,
2667 getter_AddRefs(nextEntry));
2669 if (NS_SUCCEEDED(rv)) {
2670 nsCOMPtr<nsISHistoryInternal>
2671 shPrivate(do_QueryInterface(mSessionHistory));
2672 NS_ENSURE_TRUE(shPrivate, NS_ERROR_FAILURE);
2673 rv = shPrivate->AddEntry(nextEntry, PR_TRUE);
2677 else {
2678 /* Just pass this along */
2679 nsCOMPtr<nsIDocShellHistory> parent =
2680 do_QueryInterface(GetAsSupports(mParent), &rv);
2681 if (parent) {
2682 rv = parent->AddChildSHEntry(aCloneRef, aNewEntry, aChildOffset);
2685 return rv;
2688 nsresult
2689 nsDocShell::DoAddChildSHEntry(nsISHEntry* aNewEntry, PRInt32 aChildOffset)
2691 /* You will get here when you are in a subframe and
2692 * a new url has been loaded on you.
2693 * The mOSHE in this subframe will be the previous url's
2694 * mOSHE. This mOSHE will be used as the identification
2695 * for this subframe in the CloneAndReplace function.
2698 // In this case, we will end up calling AddEntry, which increases the
2699 // current index by 1
2700 nsCOMPtr<nsISHistory> rootSH;
2701 GetRootSessionHistory(getter_AddRefs(rootSH));
2702 if (rootSH) {
2703 rootSH->GetIndex(&mPreviousTransIndex);
2706 nsresult rv;
2707 nsCOMPtr<nsIDocShellHistory> parent =
2708 do_QueryInterface(GetAsSupports(mParent), &rv);
2709 if (parent) {
2710 rv = parent->AddChildSHEntry(mOSHE, aNewEntry, aChildOffset);
2714 if (rootSH) {
2715 rootSH->GetIndex(&mLoadedTransIndex);
2716 #ifdef DEBUG_PAGE_CACHE
2717 printf("Previous index: %d, Loaded index: %d\n\n", mPreviousTransIndex,
2718 mLoadedTransIndex);
2719 #endif
2722 return rv;
2725 NS_IMETHODIMP
2726 nsDocShell::SetUseGlobalHistory(PRBool aUseGlobalHistory)
2728 nsresult rv;
2730 if (!aUseGlobalHistory) {
2731 mGlobalHistory = nsnull;
2732 return NS_OK;
2735 if (mGlobalHistory) {
2736 return NS_OK;
2739 mGlobalHistory = do_GetService(NS_GLOBALHISTORY2_CONTRACTID, &rv);
2740 return rv;
2743 NS_IMETHODIMP
2744 nsDocShell::GetUseGlobalHistory(PRBool *aUseGlobalHistory)
2746 *aUseGlobalHistory = (mGlobalHistory != nsnull);
2747 return NS_OK;
2750 //-------------------------------------
2751 //-- Helper Method for Print discovery
2752 //-------------------------------------
2753 PRBool
2754 nsDocShell::IsPrintingOrPP(PRBool aDisplayErrorDialog)
2756 if (mIsPrintingOrPP && aDisplayErrorDialog) {
2757 DisplayLoadError(NS_ERROR_DOCUMENT_IS_PRINTMODE, nsnull, nsnull);
2760 return mIsPrintingOrPP;
2763 PRBool
2764 nsDocShell::IsNavigationAllowed(PRBool aDisplayPrintErrorDialog)
2766 return !IsPrintingOrPP(aDisplayPrintErrorDialog) && !mFiredUnloadEvent;
2769 //*****************************************************************************
2770 // nsDocShell::nsIWebNavigation
2771 //*****************************************************************************
2773 NS_IMETHODIMP
2774 nsDocShell::GetCanGoBack(PRBool * aCanGoBack)
2776 if (!IsNavigationAllowed(PR_FALSE)) {
2777 *aCanGoBack = PR_FALSE;
2778 return NS_OK; // JS may not handle returning of an error code
2780 nsresult rv;
2781 nsCOMPtr<nsISHistory> rootSH;
2782 rv = GetRootSessionHistory(getter_AddRefs(rootSH));
2783 nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
2784 NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
2785 rv = webnav->GetCanGoBack(aCanGoBack);
2786 return rv;
2790 NS_IMETHODIMP
2791 nsDocShell::GetCanGoForward(PRBool * aCanGoForward)
2793 if (!IsNavigationAllowed(PR_FALSE)) {
2794 *aCanGoForward = PR_FALSE;
2795 return NS_OK; // JS may not handle returning of an error code
2797 nsresult rv;
2798 nsCOMPtr<nsISHistory> rootSH;
2799 rv = GetRootSessionHistory(getter_AddRefs(rootSH));
2800 nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
2801 NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
2802 rv = webnav->GetCanGoForward(aCanGoForward);
2803 return rv;
2807 NS_IMETHODIMP
2808 nsDocShell::GoBack()
2810 if (!IsNavigationAllowed()) {
2811 return NS_OK; // JS may not handle returning of an error code
2813 nsresult rv;
2814 nsCOMPtr<nsISHistory> rootSH;
2815 rv = GetRootSessionHistory(getter_AddRefs(rootSH));
2816 nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
2817 NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
2818 rv = webnav->GoBack();
2819 return rv;
2823 NS_IMETHODIMP
2824 nsDocShell::GoForward()
2826 if (!IsNavigationAllowed()) {
2827 return NS_OK; // JS may not handle returning of an error code
2829 nsresult rv;
2830 nsCOMPtr<nsISHistory> rootSH;
2831 rv = GetRootSessionHistory(getter_AddRefs(rootSH));
2832 nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
2833 NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
2834 rv = webnav->GoForward();
2835 return rv;
2839 NS_IMETHODIMP nsDocShell::GotoIndex(PRInt32 aIndex)
2841 if (!IsNavigationAllowed()) {
2842 return NS_OK; // JS may not handle returning of an error code
2844 nsresult rv;
2845 nsCOMPtr<nsISHistory> rootSH;
2846 rv = GetRootSessionHistory(getter_AddRefs(rootSH));
2847 nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
2848 NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
2849 rv = webnav->GotoIndex(aIndex);
2850 return rv;
2855 NS_IMETHODIMP
2856 nsDocShell::LoadURI(const PRUnichar * aURI,
2857 PRUint32 aLoadFlags,
2858 nsIURI * aReferringURI,
2859 nsIInputStream * aPostStream,
2860 nsIInputStream * aHeaderStream)
2862 if (!IsNavigationAllowed()) {
2863 return NS_OK; // JS may not handle returning of an error code
2865 nsCOMPtr<nsIURI> uri;
2866 nsresult rv = NS_OK;
2868 // Create a URI from our string; if that succeeds, we want to
2869 // change aLoadFlags to not include the ALLOW_THIRD_PARTY_FIXUP
2870 // flag.
2872 NS_ConvertUTF16toUTF8 uriString(aURI);
2873 // Cleanup the empty spaces that might be on each end.
2874 uriString.Trim(" ");
2875 // Eliminate embedded newlines, which single-line text fields now allow:
2876 uriString.StripChars("\r\n");
2877 NS_ENSURE_TRUE(!uriString.IsEmpty(), NS_ERROR_FAILURE);
2879 rv = NS_NewURI(getter_AddRefs(uri), uriString);
2880 if (uri) {
2881 aLoadFlags &= ~LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
2884 if (sURIFixup) {
2885 // Call the fixup object. This will clobber the rv from NS_NewURI
2886 // above, but that's fine with us. Note that we need to do this even
2887 // if NS_NewURI returned a URI, because fixup handles nested URIs, etc
2888 // (things like view-source:mozilla.org for example).
2889 PRUint32 fixupFlags = 0;
2890 if (aLoadFlags & LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) {
2891 fixupFlags |= nsIURIFixup::FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP;
2893 rv = sURIFixup->CreateFixupURI(uriString, fixupFlags,
2894 getter_AddRefs(uri));
2896 // else no fixup service so just use the URI we created and see
2897 // what happens
2899 if (NS_ERROR_MALFORMED_URI == rv) {
2900 DisplayLoadError(rv, uri, aURI);
2903 if (NS_FAILED(rv) || !uri)
2904 return NS_ERROR_FAILURE;
2906 PopupControlState popupState;
2907 if (aLoadFlags & LOAD_FLAGS_ALLOW_POPUPS) {
2908 popupState = openAllowed;
2909 aLoadFlags &= ~LOAD_FLAGS_ALLOW_POPUPS;
2910 } else {
2911 popupState = openOverridden;
2913 nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(mScriptGlobal));
2914 nsAutoPopupStatePusher statePusher(win, popupState);
2916 // Don't pass certain flags that aren't needed and end up confusing
2917 // ConvertLoadTypeToDocShellLoadInfo. We do need to ensure that they are
2918 // passed to LoadURI though, since it uses them.
2919 PRUint32 extraFlags = (aLoadFlags & EXTRA_LOAD_FLAGS);
2920 aLoadFlags &= ~EXTRA_LOAD_FLAGS;
2922 nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
2923 rv = CreateLoadInfo(getter_AddRefs(loadInfo));
2924 if (NS_FAILED(rv)) return rv;
2926 PRUint32 loadType = MAKE_LOAD_TYPE(LOAD_NORMAL, aLoadFlags);
2927 loadInfo->SetLoadType(ConvertLoadTypeToDocShellLoadInfo(loadType));
2928 loadInfo->SetPostDataStream(aPostStream);
2929 loadInfo->SetReferrer(aReferringURI);
2930 loadInfo->SetHeadersStream(aHeaderStream);
2932 rv = LoadURI(uri, loadInfo, extraFlags, PR_TRUE);
2934 return rv;
2937 NS_IMETHODIMP
2938 nsDocShell::DisplayLoadError(nsresult aError, nsIURI *aURI,
2939 const PRUnichar *aURL,
2940 nsIChannel* aFailedChannel)
2942 // Get prompt and string bundle servcies
2943 nsCOMPtr<nsIPrompt> prompter;
2944 nsCOMPtr<nsIStringBundle> stringBundle;
2945 GetPromptAndStringBundle(getter_AddRefs(prompter),
2946 getter_AddRefs(stringBundle));
2948 NS_ENSURE_TRUE(stringBundle, NS_ERROR_FAILURE);
2949 NS_ENSURE_TRUE(prompter, NS_ERROR_FAILURE);
2951 nsAutoString error;
2952 const PRUint32 kMaxFormatStrArgs = 3;
2953 nsAutoString formatStrs[kMaxFormatStrArgs];
2954 PRUint32 formatStrCount = 0;
2955 PRBool addHostPort = PR_FALSE;
2956 nsresult rv = NS_OK;
2957 nsAutoString messageStr;
2958 nsCAutoString cssClass;
2959 nsCAutoString errorPage;
2961 errorPage.AssignLiteral("neterror");
2963 // Turn the error code into a human readable error message.
2964 if (NS_ERROR_UNKNOWN_PROTOCOL == aError) {
2965 NS_ENSURE_ARG_POINTER(aURI);
2966 // extract the scheme
2967 nsCAutoString scheme;
2968 aURI->GetScheme(scheme);
2969 CopyASCIItoUTF16(scheme, formatStrs[0]);
2970 formatStrCount = 1;
2971 error.AssignLiteral("protocolNotFound");
2973 else if (NS_ERROR_FILE_NOT_FOUND == aError) {
2974 NS_ENSURE_ARG_POINTER(aURI);
2975 error.AssignLiteral("fileNotFound");
2977 else if (NS_ERROR_UNKNOWN_HOST == aError) {
2978 NS_ENSURE_ARG_POINTER(aURI);
2979 // Get the host
2980 nsCAutoString host;
2981 nsCOMPtr<nsIURI> innermostURI = NS_GetInnermostURI(aURI);
2982 innermostURI->GetHost(host);
2983 CopyUTF8toUTF16(host, formatStrs[0]);
2984 formatStrCount = 1;
2985 error.AssignLiteral("dnsNotFound");
2987 else if(NS_ERROR_CONNECTION_REFUSED == aError) {
2988 NS_ENSURE_ARG_POINTER(aURI);
2989 addHostPort = PR_TRUE;
2990 error.AssignLiteral("connectionFailure");
2992 else if(NS_ERROR_NET_INTERRUPT == aError) {
2993 NS_ENSURE_ARG_POINTER(aURI);
2994 addHostPort = PR_TRUE;
2995 error.AssignLiteral("netInterrupt");
2997 else if (NS_ERROR_NET_TIMEOUT == aError) {
2998 NS_ENSURE_ARG_POINTER(aURI);
2999 // Get the host
3000 nsCAutoString host;
3001 aURI->GetHost(host);
3002 CopyUTF8toUTF16(host, formatStrs[0]);
3003 formatStrCount = 1;
3004 error.AssignLiteral("netTimeout");
3006 else if (NS_ERROR_GET_MODULE(aError) == NS_ERROR_MODULE_SECURITY) {
3007 nsCOMPtr<nsINSSErrorsService> nsserr =
3008 do_GetService(NS_NSS_ERRORS_SERVICE_CONTRACTID);
3010 PRUint32 errorClass;
3011 if (!nsserr ||
3012 NS_FAILED(nsserr->GetErrorClass(aError, &errorClass))) {
3013 errorClass = nsINSSErrorsService::ERROR_CLASS_SSL_PROTOCOL;
3016 nsCOMPtr<nsISupports> securityInfo;
3017 nsCOMPtr<nsITransportSecurityInfo> tsi;
3018 if (aFailedChannel)
3019 aFailedChannel->GetSecurityInfo(getter_AddRefs(securityInfo));
3020 tsi = do_QueryInterface(securityInfo);
3021 if (tsi) {
3022 // Usually we should have aFailedChannel and get a detailed message
3023 tsi->GetErrorMessage(getter_Copies(messageStr));
3025 else {
3026 // No channel, let's obtain the generic error message
3027 if (nsserr) {
3028 nsserr->GetErrorMessage(aError, messageStr);
3031 if (!messageStr.IsEmpty()) {
3032 if (errorClass == nsINSSErrorsService::ERROR_CLASS_BAD_CERT) {
3033 error.AssignLiteral("nssBadCert");
3034 PRBool expert = PR_FALSE;
3035 mPrefs->GetBoolPref("browser.xul.error_pages.expert_bad_cert",
3036 &expert);
3037 if (expert) {
3038 cssClass.AssignLiteral("expertBadCert");
3040 } else {
3041 error.AssignLiteral("nssFailure2");
3044 } else if (NS_ERROR_PHISHING_URI == aError || NS_ERROR_MALWARE_URI == aError) {
3045 nsCAutoString host;
3046 aURI->GetHost(host);
3047 CopyUTF8toUTF16(host, formatStrs[0]);
3048 formatStrCount = 1;
3050 // Malware and phishing detectors may want to use an alternate error
3051 // page, but if the pref's not set, we'll fall back on the standard page
3052 nsXPIDLCString alternateErrorPage;
3053 mPrefs->GetCharPref("urlclassifier.alternate_error_page",
3054 getter_Copies(alternateErrorPage));
3055 if (alternateErrorPage)
3056 errorPage.Assign(alternateErrorPage);
3058 if (NS_ERROR_PHISHING_URI == aError)
3059 error.AssignLiteral("phishingBlocked");
3060 else
3061 error.AssignLiteral("malwareBlocked");
3062 cssClass.AssignLiteral("blacklist");
3064 else {
3065 // Errors requiring simple formatting
3066 switch (aError) {
3067 case NS_ERROR_MALFORMED_URI:
3068 // URI is malformed
3069 error.AssignLiteral("malformedURI");
3070 break;
3071 case NS_ERROR_REDIRECT_LOOP:
3072 // Doc failed to load because the server generated too many redirects
3073 error.AssignLiteral("redirectLoop");
3074 break;
3075 case NS_ERROR_UNKNOWN_SOCKET_TYPE:
3076 // Doc failed to load because PSM is not installed
3077 error.AssignLiteral("unknownSocketType");
3078 break;
3079 case NS_ERROR_NET_RESET:
3080 // Doc failed to load because the server kept reseting the connection
3081 // before we could read any data from it
3082 error.AssignLiteral("netReset");
3083 break;
3084 case NS_ERROR_DOCUMENT_NOT_CACHED:
3085 // Doc failed to load because we are offline and the cache does not
3086 // contain a copy of the document.
3087 error.AssignLiteral("netOffline");
3088 break;
3089 case NS_ERROR_DOCUMENT_IS_PRINTMODE:
3090 // Doc navigation attempted while Printing or Print Preview
3091 error.AssignLiteral("isprinting");
3092 break;
3093 case NS_ERROR_PORT_ACCESS_NOT_ALLOWED:
3094 // Port blocked for security reasons
3095 addHostPort = PR_TRUE;
3096 error.AssignLiteral("deniedPortAccess");
3097 break;
3098 case NS_ERROR_UNKNOWN_PROXY_HOST:
3099 // Proxy hostname could not be resolved.
3100 error.AssignLiteral("proxyResolveFailure");
3101 break;
3102 case NS_ERROR_PROXY_CONNECTION_REFUSED:
3103 // Proxy connection was refused.
3104 error.AssignLiteral("proxyConnectFailure");
3105 break;
3106 case NS_ERROR_INVALID_CONTENT_ENCODING:
3107 // Bad Content Encoding.
3108 error.AssignLiteral("contentEncodingError");
3109 break;
3110 case NS_ERROR_UNSAFE_CONTENT_TYPE:
3111 // Channel refused to load from an unrecognized content type.
3112 error.AssignLiteral("unsafeContentType");
3113 break;
3117 // Test if the error should be displayed
3118 if (error.IsEmpty()) {
3119 return NS_OK;
3122 // Test if the error needs to be formatted
3123 if (!messageStr.IsEmpty()) {
3124 // already obtained message
3126 else {
3127 if (addHostPort) {
3128 // Build up the host:port string.
3129 nsCAutoString hostport;
3130 if (aURI) {
3131 aURI->GetHostPort(hostport);
3132 } else {
3133 hostport.AssignLiteral("?");
3135 CopyUTF8toUTF16(hostport, formatStrs[formatStrCount++]);
3138 nsCAutoString spec;
3139 rv = NS_ERROR_NOT_AVAILABLE;
3140 if (aURI) {
3141 // displaying "file://" is aesthetically unpleasing and could even be
3142 // confusing to the user
3143 PRBool isFileURI = PR_FALSE;
3144 rv = aURI->SchemeIs("file", &isFileURI);
3145 if (NS_SUCCEEDED(rv) && isFileURI)
3146 aURI->GetPath(spec);
3147 else
3148 aURI->GetSpec(spec);
3150 nsCAutoString charset;
3151 // unescape and convert from origin charset
3152 aURI->GetOriginCharset(charset);
3153 nsCOMPtr<nsITextToSubURI> textToSubURI(
3154 do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv));
3155 if (NS_SUCCEEDED(rv)) {
3156 rv = textToSubURI->UnEscapeURIForUI(charset, spec, formatStrs[formatStrCount]);
3158 } else {
3159 spec.AssignLiteral("?");
3161 if (NS_FAILED(rv))
3162 CopyUTF8toUTF16(spec, formatStrs[formatStrCount]);
3163 rv = NS_OK;
3164 ++formatStrCount;
3166 const PRUnichar *strs[kMaxFormatStrArgs];
3167 for (PRUint32 i = 0; i < formatStrCount; i++) {
3168 strs[i] = formatStrs[i].get();
3170 nsXPIDLString str;
3171 rv = stringBundle->FormatStringFromName(
3172 error.get(),
3173 strs, formatStrCount, getter_Copies(str));
3174 NS_ENSURE_SUCCESS(rv, rv);
3175 messageStr.Assign(str.get());
3178 // Display the error as a page or an alert prompt
3179 NS_ENSURE_FALSE(messageStr.IsEmpty(), NS_ERROR_FAILURE);
3180 // Note: For now, display an alert instead of an error page if we have no
3181 // URI object. Missing URI objects are handled badly by session history.
3182 if (mUseErrorPages && aURI && aFailedChannel) {
3183 // Display an error page
3184 LoadErrorPage(aURI, aURL, errorPage.get(), error.get(),
3185 messageStr.get(), cssClass.get(), aFailedChannel);
3187 else
3189 // The prompter reqires that our private window has a document (or it
3190 // asserts). Satisfy that assertion now since GetDocument will force
3191 // creation of one if it hasn't already been created.
3192 nsCOMPtr<nsPIDOMWindow> pwin(do_QueryInterface(mScriptGlobal));
3193 if (pwin) {
3194 nsCOMPtr<nsIDOMDocument> doc;
3195 pwin->GetDocument(getter_AddRefs(doc));
3198 // Display a message box
3199 prompter->Alert(nsnull, messageStr.get());
3202 return NS_OK;
3206 NS_IMETHODIMP
3207 nsDocShell::LoadErrorPage(nsIURI *aURI, const PRUnichar *aURL,
3208 const char *aErrorPage,
3209 const PRUnichar *aErrorType,
3210 const PRUnichar *aDescription,
3211 const char *aCSSClass,
3212 nsIChannel* aFailedChannel)
3214 #if defined(PR_LOGGING) && defined(DEBUG)
3215 if (PR_LOG_TEST(gDocShellLog, PR_LOG_DEBUG)) {
3216 nsCAutoString spec;
3217 aURI->GetSpec(spec);
3219 nsCAutoString chanName;
3220 if (aFailedChannel)
3221 aFailedChannel->GetName(chanName);
3222 else
3223 chanName.AssignLiteral("<no channel>");
3225 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
3226 ("nsDocShell[%p]::LoadErrorPage(\"%s\", \"%s\", {...}, [%s])\n", this,
3227 spec.get(), NS_ConvertUTF16toUTF8(aURL).get(), chanName.get()));
3229 #endif
3230 // Create an shistory entry for the old load, if we have a channel
3231 if (aFailedChannel) {
3232 mURIResultedInDocument = PR_TRUE;
3233 OnLoadingSite(aFailedChannel, PR_TRUE, PR_FALSE);
3234 } else if (aURI) {
3235 mURIResultedInDocument = PR_TRUE;
3236 OnNewURI(aURI, nsnull, mLoadType, PR_TRUE, PR_FALSE);
3238 // Be sure to have a correct mLSHE, it may have been cleared by
3239 // EndPageLoad. See bug 302115.
3240 if (mSessionHistory && !mLSHE) {
3241 PRInt32 idx;
3242 mSessionHistory->GetRequestedIndex(&idx);
3243 if (idx == -1)
3244 mSessionHistory->GetIndex(&idx);
3246 nsCOMPtr<nsIHistoryEntry> entry;
3247 mSessionHistory->GetEntryAtIndex(idx, PR_FALSE,
3248 getter_AddRefs(entry));
3249 mLSHE = do_QueryInterface(entry);
3252 nsCAutoString url;
3253 nsCAutoString charset;
3254 if (aURI)
3256 // Set our current URI
3257 SetCurrentURI(aURI);
3259 nsresult rv = aURI->GetSpec(url);
3260 rv |= aURI->GetOriginCharset(charset);
3261 NS_ENSURE_SUCCESS(rv, rv);
3263 else if (aURL)
3265 CopyUTF16toUTF8(aURL, url);
3267 else
3269 return NS_ERROR_INVALID_POINTER;
3272 // Create a URL to pass all the error information through to the page.
3274 char *escapedUrl = nsEscape(url.get(), url_Path);
3275 char *escapedCharset = nsEscape(charset.get(), url_Path);
3276 char *escapedError = nsEscape(NS_ConvertUTF16toUTF8(aErrorType).get(), url_Path);
3277 char *escapedDescription = nsEscape(NS_ConvertUTF16toUTF8(aDescription).get(), url_Path);
3278 char *escapedCSSClass = nsEscape(aCSSClass, url_Path);
3280 nsCString errorPageUrl("about:");
3281 errorPageUrl.AppendASCII(aErrorPage);
3282 errorPageUrl.AppendLiteral("?e=");
3284 errorPageUrl.AppendASCII(escapedError);
3285 errorPageUrl.AppendLiteral("&u=");
3286 errorPageUrl.AppendASCII(escapedUrl);
3287 if (escapedCSSClass && escapedCSSClass[0]) {
3288 errorPageUrl.AppendASCII("&s=");
3289 errorPageUrl.AppendASCII(escapedCSSClass);
3291 errorPageUrl.AppendLiteral("&c=");
3292 errorPageUrl.AppendASCII(escapedCharset);
3293 errorPageUrl.AppendLiteral("&d=");
3294 errorPageUrl.AppendASCII(escapedDescription);
3296 nsMemory::Free(escapedDescription);
3297 nsMemory::Free(escapedError);
3298 nsMemory::Free(escapedUrl);
3299 nsMemory::Free(escapedCharset);
3300 nsMemory::Free(escapedCSSClass);
3302 nsCOMPtr<nsIURI> errorPageURI;
3303 nsresult rv = NS_NewURI(getter_AddRefs(errorPageURI), errorPageUrl);
3304 NS_ENSURE_SUCCESS(rv, rv);
3306 return InternalLoad(errorPageURI, nsnull, nsnull, PR_TRUE, nsnull, nsnull,
3307 nsnull, nsnull, LOAD_ERROR_PAGE,
3308 nsnull, PR_TRUE, nsnull, nsnull);
3312 NS_IMETHODIMP
3313 nsDocShell::Reload(PRUint32 aReloadFlags)
3315 if (!IsNavigationAllowed()) {
3316 return NS_OK; // JS may not handle returning of an error code
3318 nsresult rv;
3319 NS_ASSERTION(((aReloadFlags & 0xf) == 0),
3320 "Reload command not updated to use load flags!");
3322 PRUint32 loadType = MAKE_LOAD_TYPE(LOAD_RELOAD_NORMAL, aReloadFlags);
3323 NS_ENSURE_TRUE(IsValidLoadType(loadType), NS_ERROR_INVALID_ARG);
3325 // Send notifications to the HistoryListener if any, about the impending reload
3326 nsCOMPtr<nsISHistory> rootSH;
3327 rv = GetRootSessionHistory(getter_AddRefs(rootSH));
3328 nsCOMPtr<nsISHistoryInternal> shistInt(do_QueryInterface(rootSH));
3329 PRBool canReload = PR_TRUE;
3330 if (rootSH) {
3331 nsCOMPtr<nsISHistoryListener> listener;
3332 shistInt->GetListener(getter_AddRefs(listener));
3333 if (listener) {
3334 listener->OnHistoryReload(mCurrentURI, aReloadFlags, &canReload);
3338 if (!canReload)
3339 return NS_OK;
3341 /* If you change this part of code, make sure bug 45297 does not re-occur */
3342 if (mOSHE) {
3343 rv = LoadHistoryEntry(mOSHE, loadType);
3345 else if (mLSHE) { // In case a reload happened before the current load is done
3346 rv = LoadHistoryEntry(mLSHE, loadType);
3348 else {
3349 nsCOMPtr<nsIDOMDocument> domDoc(do_GetInterface(GetAsSupports(this)));
3350 nsCOMPtr<nsIDocument> doc(do_QueryInterface(domDoc));
3352 nsIPrincipal* principal = nsnull;
3353 nsAutoString contentTypeHint;
3354 if (doc) {
3355 principal = doc->NodePrincipal();
3356 doc->GetContentType(contentTypeHint);
3359 rv = InternalLoad(mCurrentURI,
3360 mReferrerURI,
3361 principal,
3362 INTERNAL_LOAD_FLAGS_NONE, // Do not inherit owner from document
3363 nsnull, // No window target
3364 NS_LossyConvertUTF16toASCII(contentTypeHint).get(),
3365 nsnull, // No post data
3366 nsnull, // No headers data
3367 loadType, // Load type
3368 nsnull, // No SHEntry
3369 PR_TRUE,
3370 nsnull, // No nsIDocShell
3371 nsnull); // No nsIRequest
3375 return rv;
3378 NS_IMETHODIMP
3379 nsDocShell::Stop(PRUint32 aStopFlags)
3381 // Revoke any pending event related to content viewer restoration
3382 mRestorePresentationEvent.Revoke();
3384 if (nsIWebNavigation::STOP_CONTENT & aStopFlags) {
3385 // Stop the document loading
3386 if (mContentViewer)
3387 mContentViewer->Stop();
3390 if (nsIWebNavigation::STOP_NETWORK & aStopFlags) {
3391 // Suspend any timers that were set for this loader. We'll clear
3392 // them out for good in CreateContentViewer.
3393 if (mRefreshURIList) {
3394 SuspendRefreshURIs();
3395 mSavedRefreshURIList.swap(mRefreshURIList);
3396 mRefreshURIList = nsnull;
3399 if (mClassifier) {
3400 mClassifier->Cancel();
3401 mClassifier = nsnull;
3404 // XXXbz We could also pass |this| to nsIURILoader::Stop. That will
3405 // just call Stop() on us as an nsIDocumentLoader... We need fewer
3406 // redundant apis!
3407 Stop();
3410 PRInt32 n;
3411 PRInt32 count = mChildList.Count();
3412 for (n = 0; n < count; n++) {
3413 nsCOMPtr<nsIWebNavigation> shellAsNav(do_QueryInterface(ChildAt(n)));
3414 if (shellAsNav)
3415 shellAsNav->Stop(aStopFlags);
3418 return NS_OK;
3422 NS_IMETHODIMP nsDocShell::SetDocument(nsIDOMDocument* aDocument,
3423 const PRUnichar* aContentType)
3425 //XXX First Checkin
3426 NS_ERROR("Not Yet Implemented");
3427 return NS_ERROR_FAILURE;
3431 NS_IMETHODIMP
3432 nsDocShell::GetDocument(nsIDOMDocument ** aDocument)
3434 NS_ENSURE_ARG_POINTER(aDocument);
3435 NS_ENSURE_SUCCESS(EnsureContentViewer(), NS_ERROR_FAILURE);
3437 return mContentViewer->GetDOMDocument(aDocument);
3440 NS_IMETHODIMP
3441 nsDocShell::GetCurrentURI(nsIURI ** aURI)
3443 NS_ENSURE_ARG_POINTER(aURI);
3445 if (mCurrentURI) {
3446 return NS_EnsureSafeToReturn(mCurrentURI, aURI);
3449 *aURI = nsnull;
3450 return NS_OK;
3453 NS_IMETHODIMP
3454 nsDocShell::GetReferringURI(nsIURI ** aURI)
3456 NS_ENSURE_ARG_POINTER(aURI);
3458 *aURI = mReferrerURI;
3459 NS_IF_ADDREF(*aURI);
3461 return NS_OK;
3464 NS_IMETHODIMP
3465 nsDocShell::SetSessionHistory(nsISHistory * aSessionHistory)
3468 NS_ENSURE_TRUE(aSessionHistory, NS_ERROR_FAILURE);
3469 // make sure that we are the root docshell and
3470 // set a handle to root docshell in SH.
3472 nsCOMPtr<nsIDocShellTreeItem> root;
3473 /* Get the root docshell. If *this* is the root docshell
3474 * then save a handle to *this* in SH. SH needs it to do
3475 * traversions thro' its entries
3477 GetSameTypeRootTreeItem(getter_AddRefs(root));
3478 NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
3479 if (root.get() == static_cast<nsIDocShellTreeItem *>(this)) {
3480 mSessionHistory = aSessionHistory;
3481 nsCOMPtr<nsISHistoryInternal>
3482 shPrivate(do_QueryInterface(mSessionHistory));
3483 NS_ENSURE_TRUE(shPrivate, NS_ERROR_FAILURE);
3484 shPrivate->SetRootDocShell(this);
3485 return NS_OK;
3487 return NS_ERROR_FAILURE;
3492 NS_IMETHODIMP
3493 nsDocShell::GetSessionHistory(nsISHistory ** aSessionHistory)
3495 NS_ENSURE_ARG_POINTER(aSessionHistory);
3496 *aSessionHistory = mSessionHistory;
3497 NS_IF_ADDREF(*aSessionHistory);
3498 return NS_OK;
3501 //*****************************************************************************
3502 // nsDocShell::nsIWebPageDescriptor
3503 //*****************************************************************************
3504 NS_IMETHODIMP
3505 nsDocShell::LoadPage(nsISupports *aPageDescriptor, PRUint32 aDisplayType)
3507 nsCOMPtr<nsISHEntry> shEntryIn(do_QueryInterface(aPageDescriptor));
3509 // Currently, the opaque 'page descriptor' is an nsISHEntry...
3510 if (!shEntryIn) {
3511 return NS_ERROR_INVALID_POINTER;
3514 // Now clone shEntryIn, since we might end up modifying it later on, and we
3515 // want a page descriptor to be reusable.
3516 nsCOMPtr<nsISHEntry> shEntry;
3517 nsresult rv = shEntryIn->Clone(getter_AddRefs(shEntry));
3518 NS_ENSURE_SUCCESS(rv, rv);
3521 // load the page as view-source
3523 if (nsIWebPageDescriptor::DISPLAY_AS_SOURCE == aDisplayType) {
3524 nsCOMPtr<nsIURI> oldUri, newUri;
3525 nsCString spec, newSpec;
3527 // Create a new view-source URI and replace the original.
3528 rv = shEntry->GetURI(getter_AddRefs(oldUri));
3529 if (NS_FAILED(rv))
3530 return rv;
3532 oldUri->GetSpec(spec);
3533 newSpec.AppendLiteral("view-source:");
3534 newSpec.Append(spec);
3536 rv = NS_NewURI(getter_AddRefs(newUri), newSpec);
3537 if (NS_FAILED(rv)) {
3538 return rv;
3540 shEntry->SetURI(newUri);
3543 rv = LoadHistoryEntry(shEntry, LOAD_HISTORY);
3544 return rv;
3547 NS_IMETHODIMP
3548 nsDocShell::GetCurrentDescriptor(nsISupports **aPageDescriptor)
3550 NS_PRECONDITION(aPageDescriptor, "Null out param?");
3552 *aPageDescriptor = nsnull;
3554 nsISHEntry* src = mOSHE ? mOSHE : mLSHE;
3555 if (src) {
3556 nsCOMPtr<nsISHEntry> dest;
3558 nsresult rv = src->Clone(getter_AddRefs(dest));
3559 if (NS_FAILED(rv)) {
3560 return rv;
3563 // null out inappropriate cloned attributes...
3564 dest->SetParent(nsnull);
3565 dest->SetIsSubFrame(PR_FALSE);
3567 return CallQueryInterface(dest, aPageDescriptor);
3570 return NS_ERROR_NOT_AVAILABLE;
3574 //*****************************************************************************
3575 // nsDocShell::nsIBaseWindow
3576 //*****************************************************************************
3578 NS_IMETHODIMP
3579 nsDocShell::InitWindow(nativeWindow parentNativeWindow,
3580 nsIWidget * parentWidget, PRInt32 x, PRInt32 y,
3581 PRInt32 cx, PRInt32 cy)
3583 NS_ENSURE_ARG(parentWidget); // DocShells must get a widget for a parent
3585 SetParentWidget(parentWidget);
3586 SetPositionAndSize(x, y, cx, cy, PR_FALSE);
3588 return NS_OK;
3591 NS_IMETHODIMP
3592 nsDocShell::Create()
3594 NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome,
3595 "Unexpected item type in docshell");
3597 nsresult rv = NS_ERROR_FAILURE;
3598 mPrefs = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
3599 NS_ENSURE_SUCCESS(rv, rv);
3601 PRBool tmpbool;
3603 rv = mPrefs->GetBoolPref("browser.frames.enabled", &tmpbool);
3604 if (NS_SUCCEEDED(rv))
3605 mAllowSubframes = tmpbool;
3607 if (gValidateOrigin == (PRBool)0xffffffff) {
3608 // Check pref to see if we should prevent frameset spoofing
3609 rv = mPrefs->GetBoolPref("browser.frame.validate_origin", &tmpbool);
3610 if (NS_SUCCEEDED(rv)) {
3611 gValidateOrigin = tmpbool;
3612 } else {
3613 gValidateOrigin = PR_TRUE;
3617 // Should we use XUL error pages instead of alerts if possible?
3618 rv = mPrefs->GetBoolPref("browser.xul.error_pages.enabled", &tmpbool);
3619 if (NS_SUCCEEDED(rv))
3620 mUseErrorPages = tmpbool;
3622 nsCOMPtr<nsIPrefBranch2> prefs(do_QueryInterface(mPrefs, &rv));
3623 if (NS_SUCCEEDED(rv) && mObserveErrorPages) {
3624 prefs->AddObserver("browser.xul.error_pages.enabled", this, PR_FALSE);
3627 nsCOMPtr<nsIObserverService> serv = do_GetService(NS_OBSERVERSERVICE_CONTRACTID);
3628 if (serv) {
3629 const char* msg = mItemType == typeContent ?
3630 NS_WEBNAVIGATION_CREATE : NS_CHROME_WEBNAVIGATION_CREATE;
3631 serv->NotifyObservers(GetAsSupports(this), msg, nsnull);
3634 return NS_OK;
3637 NS_IMETHODIMP
3638 nsDocShell::Destroy()
3640 NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome,
3641 "Unexpected item type in docshell");
3643 if (!mIsBeingDestroyed) {
3644 nsCOMPtr<nsIObserverService> serv =
3645 do_GetService(NS_OBSERVERSERVICE_CONTRACTID);
3646 if (serv) {
3647 const char* msg = mItemType == typeContent ?
3648 NS_WEBNAVIGATION_DESTROY : NS_CHROME_WEBNAVIGATION_DESTROY;
3649 serv->NotifyObservers(GetAsSupports(this), msg, nsnull);
3653 mIsBeingDestroyed = PR_TRUE;
3655 // Remove our pref observers
3656 if (mObserveErrorPages) {
3657 nsCOMPtr<nsIPrefBranch2> prefs(do_QueryInterface(mPrefs));
3658 if (prefs) {
3659 prefs->RemoveObserver("browser.xul.error_pages.enabled", this);
3660 mObserveErrorPages = PR_FALSE;
3664 // Make sure to blow away our mLoadingURI just in case. No loads
3665 // from inside this pagehide.
3666 mLoadingURI = nsnull;
3668 // Fire unload event before we blow anything away.
3669 (void) FirePageHideNotification(PR_TRUE);
3671 // Clear pointers to any detached nsEditorData that's lying
3672 // around in shistory entries. Breaks cycle. See bug 430921.
3673 if (mOSHE)
3674 mOSHE->SetEditorData(nsnull);
3675 if (mLSHE)
3676 mLSHE->SetEditorData(nsnull);
3678 // Note: mContentListener can be null if Init() failed and we're being
3679 // called from the destructor.
3680 if (mContentListener) {
3681 mContentListener->DropDocShellreference();
3682 mContentListener->SetParentContentListener(nsnull);
3683 // Note that we do NOT set mContentListener to null here; that
3684 // way if someone tries to do a load in us after this point
3685 // the nsDSURIContentListener will block it. All of which
3686 // means that we should do this before calling Stop(), of
3687 // course.
3690 // Stop any URLs that are currently being loaded...
3691 Stop(nsIWebNavigation::STOP_ALL);
3693 mEditorData = nsnull;
3695 mTransferableHookData = nsnull;
3697 // Save the state of the current document, before destroying the window.
3698 // This is needed to capture the state of a frameset when the new document
3699 // causes the frameset to be destroyed...
3700 PersistLayoutHistoryState();
3702 // Remove this docshell from its parent's child list
3703 nsCOMPtr<nsIDocShellTreeItem> docShellParentAsItem =
3704 do_QueryInterface(GetAsSupports(mParent));
3705 if (docShellParentAsItem)
3706 docShellParentAsItem->RemoveChild(this);
3708 nsCOMPtr<nsIFocusEventSuppressorService> suppressor;
3709 if (mContentViewer) {
3710 suppressor =
3711 do_GetService(NS_NSIFOCUSEVENTSUPPRESSORSERVICE_CONTRACTID);
3712 NS_ENSURE_STATE(suppressor);
3713 suppressor->Suppress();
3714 mContentViewer->Close(nsnull);
3715 mContentViewer->Destroy();
3716 mContentViewer = nsnull;
3719 nsDocLoader::Destroy();
3721 mParentWidget = nsnull;
3722 mCurrentURI = nsnull;
3724 if (mScriptGlobal) {
3725 nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(mScriptGlobal));
3726 win->SetDocShell(nsnull);
3728 mScriptGlobal = nsnull;
3731 mSessionHistory = nsnull;
3732 SetTreeOwner(nsnull);
3734 // required to break ref cycle
3735 mSecurityUI = nsnull;
3737 // Cancel any timers that were set for this docshell; this is needed
3738 // to break the cycle between us and the timers.
3739 CancelRefreshURITimers();
3740 if (suppressor) {
3741 suppressor->Unsuppress();
3743 return NS_OK;
3746 NS_IMETHODIMP
3747 nsDocShell::SetPosition(PRInt32 x, PRInt32 y)
3749 mBounds.x = x;
3750 mBounds.y = y;
3752 if (mContentViewer)
3753 NS_ENSURE_SUCCESS(mContentViewer->Move(x, y), NS_ERROR_FAILURE);
3755 return NS_OK;
3758 NS_IMETHODIMP
3759 nsDocShell::GetPosition(PRInt32 * aX, PRInt32 * aY)
3761 PRInt32 dummyHolder;
3762 return GetPositionAndSize(aX, aY, &dummyHolder, &dummyHolder);
3765 NS_IMETHODIMP
3766 nsDocShell::SetSize(PRInt32 aCX, PRInt32 aCY, PRBool aRepaint)
3768 PRInt32 x = 0, y = 0;
3769 GetPosition(&x, &y);
3770 return SetPositionAndSize(x, y, aCX, aCY, aRepaint);
3773 NS_IMETHODIMP
3774 nsDocShell::GetSize(PRInt32 * aCX, PRInt32 * aCY)
3776 PRInt32 dummyHolder;
3777 return GetPositionAndSize(&dummyHolder, &dummyHolder, aCX, aCY);
3780 NS_IMETHODIMP
3781 nsDocShell::SetPositionAndSize(PRInt32 x, PRInt32 y, PRInt32 cx,
3782 PRInt32 cy, PRBool fRepaint)
3784 mBounds.x = x;
3785 mBounds.y = y;
3786 mBounds.width = cx;
3787 mBounds.height = cy;
3789 // Hold strong ref, since SetBounds can make us null out mContentViewer
3790 nsCOMPtr<nsIContentViewer> viewer = mContentViewer;
3791 if (viewer) {
3792 //XXX Border figured in here or is that handled elsewhere?
3793 NS_ENSURE_SUCCESS(viewer->SetBounds(mBounds), NS_ERROR_FAILURE);
3796 return NS_OK;
3799 NS_IMETHODIMP
3800 nsDocShell::GetPositionAndSize(PRInt32 * x, PRInt32 * y, PRInt32 * cx,
3801 PRInt32 * cy)
3803 // We should really consider just getting this information from
3804 // our window instead of duplicating the storage and code...
3805 nsCOMPtr<nsIDOMDocument> document(do_GetInterface(GetAsSupports(mParent)));
3806 nsCOMPtr<nsIDocument> doc(do_QueryInterface(document));
3807 if (doc) {
3808 doc->FlushPendingNotifications(Flush_Layout);
3811 DoGetPositionAndSize(x, y, cx, cy);
3812 return NS_OK;
3815 void
3816 nsDocShell::DoGetPositionAndSize(PRInt32 * x, PRInt32 * y, PRInt32 * cx,
3817 PRInt32 * cy)
3819 if (x)
3820 *x = mBounds.x;
3821 if (y)
3822 *y = mBounds.y;
3823 if (cx)
3824 *cx = mBounds.width;
3825 if (cy)
3826 *cy = mBounds.height;
3829 NS_IMETHODIMP
3830 nsDocShell::Repaint(PRBool aForce)
3832 nsCOMPtr<nsPresContext> context;
3833 GetPresContext(getter_AddRefs(context));
3834 NS_ENSURE_TRUE(context, NS_ERROR_FAILURE);
3836 nsIViewManager* viewManager = context->GetViewManager();
3837 NS_ENSURE_TRUE(viewManager, NS_ERROR_FAILURE);
3839 // what about aForce ?
3840 NS_ENSURE_SUCCESS(viewManager->UpdateAllViews(0), NS_ERROR_FAILURE);
3841 return NS_OK;
3844 NS_IMETHODIMP
3845 nsDocShell::GetParentWidget(nsIWidget ** parentWidget)
3847 NS_ENSURE_ARG_POINTER(parentWidget);
3849 *parentWidget = mParentWidget;
3850 NS_IF_ADDREF(*parentWidget);
3852 return NS_OK;
3855 NS_IMETHODIMP
3856 nsDocShell::SetParentWidget(nsIWidget * aParentWidget)
3858 mParentWidget = aParentWidget;
3860 return NS_OK;
3863 NS_IMETHODIMP
3864 nsDocShell::GetParentNativeWindow(nativeWindow * parentNativeWindow)
3866 NS_ENSURE_ARG_POINTER(parentNativeWindow);
3868 if (mParentWidget)
3869 *parentNativeWindow = mParentWidget->GetNativeData(NS_NATIVE_WIDGET);
3870 else
3871 *parentNativeWindow = nsnull;
3873 return NS_OK;
3876 NS_IMETHODIMP
3877 nsDocShell::SetParentNativeWindow(nativeWindow parentNativeWindow)
3879 return NS_ERROR_NOT_IMPLEMENTED;
3882 NS_IMETHODIMP
3883 nsDocShell::GetVisibility(PRBool * aVisibility)
3885 NS_ENSURE_ARG_POINTER(aVisibility);
3886 if (!mContentViewer) {
3887 *aVisibility = PR_FALSE;
3888 return NS_OK;
3891 // get the pres shell
3892 nsCOMPtr<nsIPresShell> presShell;
3893 NS_ENSURE_SUCCESS(GetPresShell(getter_AddRefs(presShell)),
3894 NS_ERROR_FAILURE);
3895 NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
3897 // get the view manager
3898 nsIViewManager* vm = presShell->GetViewManager();
3899 NS_ENSURE_TRUE(vm, NS_ERROR_FAILURE);
3901 // get the root view
3902 nsIView *view = nsnull; // views are not ref counted
3903 NS_ENSURE_SUCCESS(vm->GetRootView(view), NS_ERROR_FAILURE);
3904 NS_ENSURE_TRUE(view, NS_ERROR_FAILURE);
3906 // if our root view is hidden, we are not visible
3907 if (view->GetVisibility() == nsViewVisibility_kHide) {
3908 *aVisibility = PR_FALSE;
3909 return NS_OK;
3912 // otherwise, we must walk up the document and view trees checking
3913 // for a hidden view.
3915 nsCOMPtr<nsIDocShellTreeItem> treeItem = this;
3916 nsCOMPtr<nsIDocShellTreeItem> parentItem;
3917 treeItem->GetParent(getter_AddRefs(parentItem));
3918 while (parentItem) {
3919 nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(treeItem));
3920 docShell->GetPresShell(getter_AddRefs(presShell));
3922 nsCOMPtr<nsIDocShell> parentDS = do_QueryInterface(parentItem);
3923 nsCOMPtr<nsIPresShell> pPresShell;
3924 parentDS->GetPresShell(getter_AddRefs(pPresShell));
3926 // Null-check for crash in bug 267804
3927 if (!pPresShell) {
3928 NS_NOTREACHED("docshell has null pres shell");
3929 *aVisibility = PR_FALSE;
3930 return NS_OK;
3933 nsIContent *shellContent =
3934 pPresShell->GetDocument()->FindContentForSubDocument(presShell->GetDocument());
3935 NS_ASSERTION(shellContent, "subshell not in the map");
3937 nsIFrame* frame = pPresShell->GetPrimaryFrameFor(shellContent);
3938 if (frame && !frame->AreAncestorViewsVisible()) {
3939 *aVisibility = PR_FALSE;
3940 return NS_OK;
3943 treeItem = parentItem;
3944 treeItem->GetParent(getter_AddRefs(parentItem));
3947 nsCOMPtr<nsIBaseWindow>
3948 treeOwnerAsWin(do_QueryInterface(mTreeOwner));
3949 if (!treeOwnerAsWin) {
3950 *aVisibility = PR_TRUE;
3951 return NS_OK;
3954 // Check with the tree owner as well to give embedders a chance to
3955 // expose visibility as well.
3956 return treeOwnerAsWin->GetVisibility(aVisibility);
3959 NS_IMETHODIMP
3960 nsDocShell::SetVisibility(PRBool aVisibility)
3962 if (!mContentViewer)
3963 return NS_OK;
3964 if (aVisibility) {
3965 mContentViewer->Show();
3967 else {
3968 mContentViewer->Hide();
3971 return NS_OK;
3974 NS_IMETHODIMP
3975 nsDocShell::GetEnabled(PRBool *aEnabled)
3977 NS_ENSURE_ARG_POINTER(aEnabled);
3978 *aEnabled = PR_TRUE;
3979 return NS_ERROR_NOT_IMPLEMENTED;
3982 NS_IMETHODIMP
3983 nsDocShell::SetEnabled(PRBool aEnabled)
3985 return NS_ERROR_NOT_IMPLEMENTED;
3988 NS_IMETHODIMP
3989 nsDocShell::GetBlurSuppression(PRBool *aBlurSuppression)
3991 NS_ENSURE_ARG_POINTER(aBlurSuppression);
3992 *aBlurSuppression = PR_FALSE;
3993 return NS_ERROR_NOT_IMPLEMENTED;
3996 NS_IMETHODIMP
3997 nsDocShell::SetBlurSuppression(PRBool aBlurSuppression)
3999 return NS_ERROR_NOT_IMPLEMENTED;
4002 NS_IMETHODIMP
4003 nsDocShell::GetMainWidget(nsIWidget ** aMainWidget)
4005 // We don't create our own widget, so simply return the parent one.
4006 return GetParentWidget(aMainWidget);
4009 NS_IMETHODIMP
4010 nsDocShell::SetFocus()
4012 #ifdef DEBUG_DOCSHELL_FOCUS
4013 printf("nsDocShell::SetFocus %p\n", (void*)this);
4014 #endif
4016 // Tell itself (and the DocShellFocusController) who has focus
4017 // this way focus gets removed from the currently focused DocShell
4019 SetHasFocus(PR_TRUE);
4021 return NS_OK;
4024 NS_IMETHODIMP
4025 nsDocShell::GetTitle(PRUnichar ** aTitle)
4027 NS_ENSURE_ARG_POINTER(aTitle);
4029 *aTitle = ToNewUnicode(mTitle);
4030 return NS_OK;
4033 NS_IMETHODIMP
4034 nsDocShell::SetTitle(const PRUnichar * aTitle)
4036 // Store local title
4037 mTitle = aTitle;
4039 nsCOMPtr<nsIDocShellTreeItem> parent;
4040 GetSameTypeParent(getter_AddRefs(parent));
4042 // When title is set on the top object it should then be passed to the
4043 // tree owner.
4044 if (!parent) {
4045 nsCOMPtr<nsIBaseWindow>
4046 treeOwnerAsWin(do_QueryInterface(mTreeOwner));
4047 if (treeOwnerAsWin)
4048 treeOwnerAsWin->SetTitle(aTitle);
4051 if (mGlobalHistory && mCurrentURI && mLoadType != LOAD_ERROR_PAGE) {
4052 mGlobalHistory->SetPageTitle(mCurrentURI, nsDependentString(aTitle));
4056 // Update SessionHistory with the document's title. If the
4057 // page was loaded from history or the page bypassed history,
4058 // there is no need to update the title. There is no need to
4059 // go to mSessionHistory to update the title. Setting it in mOSHE
4060 // would suffice.
4061 if (mOSHE && (mLoadType != LOAD_BYPASS_HISTORY) &&
4062 (mLoadType != LOAD_HISTORY) && (mLoadType != LOAD_ERROR_PAGE)) {
4063 mOSHE->SetTitle(mTitle);
4067 return NS_OK;
4070 //*****************************************************************************
4071 // nsDocShell::nsIScrollable
4072 //*****************************************************************************
4074 NS_IMETHODIMP
4075 nsDocShell::GetCurScrollPos(PRInt32 scrollOrientation, PRInt32 * curPos)
4077 NS_ENSURE_ARG_POINTER(curPos);
4079 nsIScrollableView* scrollView;
4080 NS_ENSURE_SUCCESS(GetRootScrollableView(&scrollView),
4081 NS_ERROR_FAILURE);
4082 if (!scrollView) {
4083 return NS_ERROR_FAILURE;
4086 nscoord x, y;
4087 NS_ENSURE_SUCCESS(scrollView->GetScrollPosition(x, y), NS_ERROR_FAILURE);
4089 switch (scrollOrientation) {
4090 case ScrollOrientation_X:
4091 *curPos = x;
4092 return NS_OK;
4094 case ScrollOrientation_Y:
4095 *curPos = y;
4096 return NS_OK;
4098 default:
4099 NS_ENSURE_TRUE(PR_FALSE, NS_ERROR_INVALID_ARG);
4101 return NS_ERROR_FAILURE;
4104 NS_IMETHODIMP
4105 nsDocShell::SetCurScrollPos(PRInt32 scrollOrientation, PRInt32 curPos)
4107 nsIScrollableView* scrollView;
4108 NS_ENSURE_SUCCESS(GetRootScrollableView(&scrollView),
4109 NS_ERROR_FAILURE);
4110 if (!scrollView) {
4111 return NS_ERROR_FAILURE;
4114 PRInt32 other;
4115 PRInt32 x;
4116 PRInt32 y;
4118 GetCurScrollPos(scrollOrientation, &other);
4120 switch (scrollOrientation) {
4121 case ScrollOrientation_X:
4122 x = curPos;
4123 y = other;
4124 break;
4126 case ScrollOrientation_Y:
4127 x = other;
4128 y = curPos;
4129 break;
4131 default:
4132 NS_ENSURE_TRUE(PR_FALSE, NS_ERROR_INVALID_ARG);
4133 x = 0;
4134 y = 0; // fix compiler warning, not actually executed
4137 NS_ENSURE_SUCCESS(scrollView->ScrollTo(x, y, NS_VMREFRESH_IMMEDIATE),
4138 NS_ERROR_FAILURE);
4139 return NS_OK;
4142 NS_IMETHODIMP
4143 nsDocShell::SetCurScrollPosEx(PRInt32 curHorizontalPos, PRInt32 curVerticalPos)
4145 nsIScrollableView* scrollView;
4146 NS_ENSURE_SUCCESS(GetRootScrollableView(&scrollView),
4147 NS_ERROR_FAILURE);
4148 if (!scrollView) {
4149 return NS_ERROR_FAILURE;
4152 NS_ENSURE_SUCCESS(scrollView->ScrollTo(curHorizontalPos, curVerticalPos,
4153 NS_VMREFRESH_IMMEDIATE),
4154 NS_ERROR_FAILURE);
4155 return NS_OK;
4158 // XXX This is wrong
4159 NS_IMETHODIMP
4160 nsDocShell::GetScrollRange(PRInt32 scrollOrientation,
4161 PRInt32 * minPos, PRInt32 * maxPos)
4163 NS_ENSURE_ARG_POINTER(minPos && maxPos);
4165 nsIScrollableView* scrollView;
4166 NS_ENSURE_SUCCESS(GetRootScrollableView(&scrollView),
4167 NS_ERROR_FAILURE);
4168 if (!scrollView) {
4169 return NS_ERROR_FAILURE;
4172 PRInt32 cx;
4173 PRInt32 cy;
4175 NS_ENSURE_SUCCESS(scrollView->GetContainerSize(&cx, &cy), NS_ERROR_FAILURE);
4176 *minPos = 0;
4178 switch (scrollOrientation) {
4179 case ScrollOrientation_X:
4180 *maxPos = cx;
4181 return NS_OK;
4183 case ScrollOrientation_Y:
4184 *maxPos = cy;
4185 return NS_OK;
4187 default:
4188 NS_ENSURE_TRUE(PR_FALSE, NS_ERROR_INVALID_ARG);
4191 return NS_ERROR_FAILURE;
4194 NS_IMETHODIMP
4195 nsDocShell::SetScrollRange(PRInt32 scrollOrientation,
4196 PRInt32 minPos, PRInt32 maxPos)
4198 //XXX First Check
4200 Retrieves or Sets the valid ranges for the thumb. When maxPos is set to
4201 something less than the current thumb position, curPos is set = to maxPos.
4203 @return NS_OK - Setting or Getting completed successfully.
4204 NS_ERROR_INVALID_ARG - returned when curPos is not within the
4205 minPos and maxPos.
4207 return NS_ERROR_FAILURE;
4210 NS_IMETHODIMP
4211 nsDocShell::SetScrollRangeEx(PRInt32 minHorizontalPos,
4212 PRInt32 maxHorizontalPos, PRInt32 minVerticalPos,
4213 PRInt32 maxVerticalPos)
4215 //XXX First Check
4217 Retrieves or Sets the valid ranges for the thumb. When maxPos is set to
4218 something less than the current thumb position, curPos is set = to maxPos.
4220 @return NS_OK - Setting or Getting completed successfully.
4221 NS_ERROR_INVALID_ARG - returned when curPos is not within the
4222 minPos and maxPos.
4224 return NS_ERROR_FAILURE;
4227 // This returns setting for all documents in this webshell
4228 NS_IMETHODIMP
4229 nsDocShell::GetDefaultScrollbarPreferences(PRInt32 scrollOrientation,
4230 PRInt32 * scrollbarPref)
4232 NS_ENSURE_ARG_POINTER(scrollbarPref);
4233 switch (scrollOrientation) {
4234 case ScrollOrientation_X:
4235 *scrollbarPref = mDefaultScrollbarPref.x;
4236 return NS_OK;
4238 case ScrollOrientation_Y:
4239 *scrollbarPref = mDefaultScrollbarPref.y;
4240 return NS_OK;
4242 default:
4243 NS_ENSURE_TRUE(PR_FALSE, NS_ERROR_INVALID_ARG);
4245 return NS_ERROR_FAILURE;
4248 // Set scrolling preference for all documents in this shell
4250 // There are three possible values stored in the shell:
4251 // 1) nsIScrollable::Scrollbar_Never = no scrollbar
4252 // 2) nsIScrollable::Scrollbar_Auto = scrollbar appears if the document
4253 // being displayed would normally have scrollbar
4254 // 3) nsIScrollable::Scrollbar_Always = scrollbar always appears
4256 // One important client is nsHTMLFrameInnerFrame::CreateWebShell()
4257 NS_IMETHODIMP
4258 nsDocShell::SetDefaultScrollbarPreferences(PRInt32 scrollOrientation,
4259 PRInt32 scrollbarPref)
4261 switch (scrollOrientation) {
4262 case ScrollOrientation_X:
4263 mDefaultScrollbarPref.x = scrollbarPref;
4264 return NS_OK;
4266 case ScrollOrientation_Y:
4267 mDefaultScrollbarPref.y = scrollbarPref;
4268 return NS_OK;
4270 default:
4271 NS_ENSURE_TRUE(PR_FALSE, NS_ERROR_INVALID_ARG);
4273 return NS_ERROR_FAILURE;
4276 NS_IMETHODIMP
4277 nsDocShell::GetScrollbarVisibility(PRBool * verticalVisible,
4278 PRBool * horizontalVisible)
4280 nsIScrollableView* scrollView;
4281 NS_ENSURE_SUCCESS(GetRootScrollableView(&scrollView),
4282 NS_ERROR_FAILURE);
4283 if (!scrollView)
4284 return NS_ERROR_FAILURE;
4286 // We should now call nsLayoutUtils::GetScrollableFrameFor,
4287 // but we can't because of stupid linkage!
4288 nsIFrame* scrollFrame =
4289 static_cast<nsIFrame*>(scrollView->View()->GetParent()->GetClientData());
4290 if (!scrollFrame)
4291 return NS_ERROR_FAILURE;
4292 nsIScrollableFrame* scrollable = nsnull;
4293 CallQueryInterface(scrollFrame, &scrollable);
4294 if (!scrollable)
4295 return NS_ERROR_FAILURE;
4297 nsMargin scrollbars = scrollable->GetActualScrollbarSizes();
4298 if (verticalVisible)
4299 *verticalVisible = scrollbars.left != 0 || scrollbars.right != 0;
4300 if (horizontalVisible)
4301 *horizontalVisible = scrollbars.top != 0 || scrollbars.bottom != 0;
4303 return NS_OK;
4306 //*****************************************************************************
4307 // nsDocShell::nsITextScroll
4308 //*****************************************************************************
4310 NS_IMETHODIMP
4311 nsDocShell::ScrollByLines(PRInt32 numLines)
4313 nsIScrollableView* scrollView;
4315 NS_ENSURE_SUCCESS(GetRootScrollableView(&scrollView),
4316 NS_ERROR_FAILURE);
4317 if (!scrollView) {
4318 return NS_ERROR_FAILURE;
4321 NS_ENSURE_SUCCESS(scrollView->ScrollByLines(0, numLines), NS_ERROR_FAILURE);
4323 return NS_OK;
4326 NS_IMETHODIMP
4327 nsDocShell::ScrollByPages(PRInt32 numPages)
4329 nsIScrollableView* scrollView;
4331 NS_ENSURE_SUCCESS(GetRootScrollableView(&scrollView),
4332 NS_ERROR_FAILURE);
4333 if (!scrollView) {
4334 return NS_ERROR_FAILURE;
4337 NS_ENSURE_SUCCESS(scrollView->ScrollByPages(0, numPages), NS_ERROR_FAILURE);
4339 return NS_OK;
4342 //*****************************************************************************
4343 // nsDocShell::nsIScriptGlobalObjectOwner
4344 //*****************************************************************************
4346 nsIScriptGlobalObject*
4347 nsDocShell::GetScriptGlobalObject()
4349 NS_ENSURE_SUCCESS(EnsureScriptEnvironment(), nsnull);
4351 return mScriptGlobal;
4354 //*****************************************************************************
4355 // nsDocShell::nsIRefreshURI
4356 //*****************************************************************************
4358 NS_IMETHODIMP
4359 nsDocShell::RefreshURI(nsIURI * aURI, PRInt32 aDelay, PRBool aRepeat,
4360 PRBool aMetaRefresh)
4362 NS_ENSURE_ARG(aURI);
4364 /* Check if Meta refresh/redirects are permitted. Some
4365 * embedded applications may not want to do this.
4366 * Must do this before sending out NOTIFY_REFRESH events
4367 * because listeners may have side effects (e.g. displaying a
4368 * button to manually trigger the refresh later).
4370 PRBool allowRedirects = PR_TRUE;
4371 GetAllowMetaRedirects(&allowRedirects);
4372 if (!allowRedirects)
4373 return NS_OK;
4375 // If any web progress listeners are listening for NOTIFY_REFRESH events,
4376 // give them a chance to block this refresh.
4377 PRBool sameURI;
4378 nsresult rv = aURI->Equals(mCurrentURI, &sameURI);
4379 if (NS_FAILED(rv))
4380 sameURI = PR_FALSE;
4381 if (!RefreshAttempted(this, aURI, aDelay, sameURI))
4382 return NS_OK;
4384 nsRefreshTimer *refreshTimer = new nsRefreshTimer();
4385 NS_ENSURE_TRUE(refreshTimer, NS_ERROR_OUT_OF_MEMORY);
4386 PRUint32 busyFlags = 0;
4387 GetBusyFlags(&busyFlags);
4389 nsCOMPtr<nsISupports> dataRef = refreshTimer; // Get the ref count to 1
4391 refreshTimer->mDocShell = this;
4392 refreshTimer->mURI = aURI;
4393 refreshTimer->mDelay = aDelay;
4394 refreshTimer->mRepeat = aRepeat;
4395 refreshTimer->mMetaRefresh = aMetaRefresh;
4397 if (!mRefreshURIList) {
4398 NS_ENSURE_SUCCESS(NS_NewISupportsArray(getter_AddRefs(mRefreshURIList)),
4399 NS_ERROR_FAILURE);
4402 if (busyFlags & BUSY_FLAGS_BUSY) {
4403 // We are busy loading another page. Don't create the
4404 // timer right now. Instead queue up the request and trigger the
4405 // timer in EndPageLoad().
4406 mRefreshURIList->AppendElement(refreshTimer);
4408 else {
4409 // There is no page loading going on right now. Create the
4410 // timer and fire it right away.
4411 nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1");
4412 NS_ENSURE_TRUE(timer, NS_ERROR_FAILURE);
4414 mRefreshURIList->AppendElement(timer); // owning timer ref
4415 timer->InitWithCallback(refreshTimer, aDelay, nsITimer::TYPE_ONE_SHOT);
4417 return NS_OK;
4420 NS_IMETHODIMP
4421 nsDocShell::ForceRefreshURI(nsIURI * aURI,
4422 PRInt32 aDelay,
4423 PRBool aMetaRefresh)
4425 NS_ENSURE_ARG(aURI);
4427 nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
4428 CreateLoadInfo(getter_AddRefs(loadInfo));
4429 NS_ENSURE_TRUE(loadInfo, NS_ERROR_OUT_OF_MEMORY);
4431 /* We do need to pass in a referrer, but we don't want it to
4432 * be sent to the server.
4434 loadInfo->SetSendReferrer(PR_FALSE);
4436 /* for most refreshes the current URI is an appropriate
4437 * internal referrer
4439 loadInfo->SetReferrer(mCurrentURI);
4441 /* Check if this META refresh causes a redirection
4442 * to another site.
4444 PRBool equalUri = PR_FALSE;
4445 nsresult rv = aURI->Equals(mCurrentURI, &equalUri);
4446 if (NS_SUCCEEDED(rv) && (!equalUri) && aMetaRefresh) {
4448 /* It is a META refresh based redirection. Now check if it happened
4449 within the threshold time we have in mind(15000 ms as defined by
4450 REFRESH_REDIRECT_TIMER). If so, pass a REPLACE flag to LoadURI().
4452 if (aDelay <= REFRESH_REDIRECT_TIMER) {
4453 loadInfo->SetLoadType(nsIDocShellLoadInfo::loadNormalReplace);
4455 /* for redirects we mimic HTTP, which passes the
4456 * original referrer
4458 nsCOMPtr<nsIURI> internalReferrer;
4459 GetReferringURI(getter_AddRefs(internalReferrer));
4460 if (internalReferrer) {
4461 loadInfo->SetReferrer(internalReferrer);
4464 else
4465 loadInfo->SetLoadType(nsIDocShellLoadInfo::loadRefresh);
4467 * LoadURI(...) will cancel all refresh timers... This causes the
4468 * Timer and its refreshData instance to be released...
4470 LoadURI(aURI, loadInfo, nsIWebNavigation::LOAD_FLAGS_NONE, PR_TRUE);
4471 return NS_OK;
4473 else
4474 loadInfo->SetLoadType(nsIDocShellLoadInfo::loadRefresh);
4476 LoadURI(aURI, loadInfo, nsIWebNavigation::LOAD_FLAGS_NONE, PR_TRUE);
4478 return NS_OK;
4481 nsresult
4482 nsDocShell::SetupRefreshURIFromHeader(nsIURI * aBaseURI,
4483 const nsACString & aHeader)
4485 // Refresh headers are parsed with the following format in mind
4486 // <META HTTP-EQUIV=REFRESH CONTENT="5; URL=http://uri">
4487 // By the time we are here, the following is true:
4488 // header = "REFRESH"
4489 // content = "5; URL=http://uri" // note the URL attribute is
4490 // optional, if it is absent, the currently loaded url is used.
4491 // Also note that the seconds and URL separator can be either
4492 // a ';' or a ','. The ',' separator should be illegal but CNN
4493 // is using it.
4495 // We need to handle the following strings, where
4496 // - X is a set of digits
4497 // - URI is either a relative or absolute URI
4499 // Note that URI should start with "url=" but we allow omission
4501 // "" || ";" || ","
4502 // empty string. use the currently loaded URI
4503 // and refresh immediately.
4504 // "X" || "X;" || "X,"
4505 // Refresh the currently loaded URI in X seconds.
4506 // "X; URI" || "X, URI"
4507 // Refresh using URI as the destination in X seconds.
4508 // "URI" || "; URI" || ", URI"
4509 // Refresh immediately using URI as the destination.
4511 // Currently, anything immediately following the URI, if
4512 // separated by any char in the set "'\"\t\r\n " will be
4513 // ignored. So "10; url=go.html ; foo=bar" will work,
4514 // and so will "10; url='go.html'; foo=bar". However,
4515 // "10; url=go.html; foo=bar" will result in the uri
4516 // "go.html;" since ';' and ',' are valid uri characters.
4518 // Note that we need to remove any tokens wrapping the URI.
4519 // These tokens currently include spaces, double and single
4520 // quotes.
4522 // when done, seconds is 0 or the given number of seconds
4523 // uriAttrib is empty or the URI specified
4524 nsCAutoString uriAttrib;
4525 PRInt32 seconds = 0;
4526 PRBool specifiesSeconds = PR_FALSE;
4528 nsACString::const_iterator iter, tokenStart, doneIterating;
4530 aHeader.BeginReading(iter);
4531 aHeader.EndReading(doneIterating);
4533 // skip leading whitespace
4534 while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter))
4535 ++iter;
4537 tokenStart = iter;
4539 // skip leading + and -
4540 if (iter != doneIterating && (*iter == '-' || *iter == '+'))
4541 ++iter;
4543 // parse number
4544 while (iter != doneIterating && (*iter >= '0' && *iter <= '9')) {
4545 seconds = seconds * 10 + (*iter - '0');
4546 specifiesSeconds = PR_TRUE;
4547 ++iter;
4550 if (iter != doneIterating) {
4551 // if we started with a '-', number is negative
4552 if (*tokenStart == '-')
4553 seconds = -seconds;
4555 // skip to next ';' or ','
4556 nsACString::const_iterator iterAfterDigit = iter;
4557 while (iter != doneIterating && !(*iter == ';' || *iter == ','))
4559 if (specifiesSeconds)
4561 // Non-whitespace characters here mean that the string is
4562 // malformed but tolerate sites that specify a decimal point,
4563 // even though meta refresh only works on whole seconds.
4564 if (iter == iterAfterDigit &&
4565 !nsCRT::IsAsciiSpace(*iter) && *iter != '.')
4567 // The characters between the seconds and the next
4568 // section are just garbage!
4569 // e.g. content="2a0z+,URL=http://www.mozilla.org/"
4570 // Just ignore this redirect.
4571 return NS_ERROR_FAILURE;
4573 else if (nsCRT::IsAsciiSpace(*iter))
4575 // We've had at least one whitespace so tolerate the mistake
4576 // and drop through.
4577 // e.g. content="10 foo"
4578 ++iter;
4579 break;
4582 ++iter;
4585 // skip any remaining whitespace
4586 while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter))
4587 ++iter;
4589 // skip ';' or ','
4590 if (iter != doneIterating && (*iter == ';' || *iter == ',')) {
4591 ++iter;
4594 // skip whitespace
4595 while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter))
4596 ++iter;
4599 // possible start of URI
4600 tokenStart = iter;
4602 // skip "url = " to real start of URI
4603 if (iter != doneIterating && (*iter == 'u' || *iter == 'U')) {
4604 ++iter;
4605 if (iter != doneIterating && (*iter == 'r' || *iter == 'R')) {
4606 ++iter;
4607 if (iter != doneIterating && (*iter == 'l' || *iter == 'L')) {
4608 ++iter;
4610 // skip whitespace
4611 while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter))
4612 ++iter;
4614 if (iter != doneIterating && *iter == '=') {
4615 ++iter;
4617 // skip whitespace
4618 while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter))
4619 ++iter;
4621 // found real start of URI
4622 tokenStart = iter;
4628 // skip a leading '"' or '\''.
4630 PRBool isQuotedURI = PR_FALSE;
4631 if (tokenStart != doneIterating && (*tokenStart == '"' || *tokenStart == '\''))
4633 isQuotedURI = PR_TRUE;
4634 ++tokenStart;
4637 // set iter to start of URI
4638 iter = tokenStart;
4640 // tokenStart here points to the beginning of URI
4642 // grab the rest of the URI
4643 while (iter != doneIterating)
4645 if (isQuotedURI && (*iter == '"' || *iter == '\''))
4646 break;
4647 ++iter;
4650 // move iter one back if the last character is a '"' or '\''
4651 if (iter != tokenStart && isQuotedURI) {
4652 --iter;
4653 if (!(*iter == '"' || *iter == '\''))
4654 ++iter;
4657 // URI is whatever's contained from tokenStart to iter.
4658 // note: if tokenStart == doneIterating, so is iter.
4660 nsresult rv = NS_OK;
4662 nsCOMPtr<nsIURI> uri;
4663 PRBool specifiesURI = PR_FALSE;
4664 if (tokenStart == iter) {
4665 uri = aBaseURI;
4667 else {
4668 uriAttrib = Substring(tokenStart, iter);
4669 // NS_NewURI takes care of any whitespace surrounding the URL
4670 rv = NS_NewURI(getter_AddRefs(uri), uriAttrib, nsnull, aBaseURI);
4671 specifiesURI = PR_TRUE;
4674 // No URI or seconds were specified
4675 if (!specifiesSeconds && !specifiesURI)
4677 // Do nothing because the alternative is to spin around in a refresh
4678 // loop forever!
4679 return NS_ERROR_FAILURE;
4682 if (NS_SUCCEEDED(rv)) {
4683 nsCOMPtr<nsIScriptSecurityManager>
4684 securityManager(do_GetService
4685 (NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv));
4686 if (NS_SUCCEEDED(rv)) {
4687 rv = securityManager->
4688 CheckLoadURI(aBaseURI, uri,
4689 nsIScriptSecurityManager::
4690 LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT);
4691 if (NS_SUCCEEDED(rv)) {
4692 // Since we can't travel back in time yet, just pretend
4693 // negative numbers do nothing at all.
4694 if (seconds < 0)
4695 return NS_ERROR_FAILURE;
4697 rv = RefreshURI(uri, seconds * 1000, PR_FALSE, PR_TRUE);
4701 return rv;
4704 NS_IMETHODIMP nsDocShell::SetupRefreshURI(nsIChannel * aChannel)
4706 nsresult rv;
4707 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel, &rv));
4708 if (NS_SUCCEEDED(rv)) {
4709 nsCAutoString refreshHeader;
4710 rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("refresh"),
4711 refreshHeader);
4713 if (!refreshHeader.IsEmpty()) {
4714 SetupReferrerFromChannel(aChannel);
4715 rv = SetupRefreshURIFromHeader(mCurrentURI, refreshHeader);
4716 if (NS_SUCCEEDED(rv)) {
4717 return NS_REFRESHURI_HEADER_FOUND;
4721 return rv;
4724 static void
4725 DoCancelRefreshURITimers(nsISupportsArray* aTimerList)
4727 if (!aTimerList)
4728 return;
4730 PRUint32 n=0;
4731 aTimerList->Count(&n);
4733 while (n) {
4734 nsCOMPtr<nsITimer> timer(do_QueryElementAt(aTimerList, --n));
4736 aTimerList->RemoveElementAt(n); // bye bye owning timer ref
4738 if (timer)
4739 timer->Cancel();
4743 NS_IMETHODIMP
4744 nsDocShell::CancelRefreshURITimers()
4746 DoCancelRefreshURITimers(mRefreshURIList);
4747 DoCancelRefreshURITimers(mSavedRefreshURIList);
4748 mRefreshURIList = nsnull;
4749 mSavedRefreshURIList = nsnull;
4751 return NS_OK;
4754 NS_IMETHODIMP
4755 nsDocShell::GetRefreshPending(PRBool* _retval)
4757 if (!mRefreshURIList) {
4758 *_retval = PR_FALSE;
4759 return NS_OK;
4762 PRUint32 count;
4763 nsresult rv = mRefreshURIList->Count(&count);
4764 if (NS_SUCCEEDED(rv))
4765 *_retval = (count != 0);
4766 return rv;
4769 NS_IMETHODIMP
4770 nsDocShell::SuspendRefreshURIs()
4772 if (mRefreshURIList) {
4773 PRUint32 n = 0;
4774 mRefreshURIList->Count(&n);
4776 for (PRUint32 i = 0; i < n; ++i) {
4777 nsCOMPtr<nsITimer> timer = do_QueryElementAt(mRefreshURIList, i);
4778 if (!timer)
4779 continue; // this must be a nsRefreshURI already
4781 // Replace this timer object with a nsRefreshTimer object.
4782 nsCOMPtr<nsITimerCallback> callback;
4783 timer->GetCallback(getter_AddRefs(callback));
4785 timer->Cancel();
4787 nsCOMPtr<nsITimerCallback> rt = do_QueryInterface(callback);
4788 NS_ASSERTION(rt, "RefreshURIList timer callbacks should only be RefreshTimer objects");
4790 mRefreshURIList->ReplaceElementAt(rt, i);
4794 // Suspend refresh URIs for our child shells as well.
4795 PRInt32 n = mChildList.Count();
4797 for (PRInt32 i = 0; i < n; ++i) {
4798 nsCOMPtr<nsIDocShell> shell = do_QueryInterface(ChildAt(i));
4799 if (shell)
4800 shell->SuspendRefreshURIs();
4803 return NS_OK;
4806 NS_IMETHODIMP
4807 nsDocShell::ResumeRefreshURIs()
4809 RefreshURIFromQueue();
4811 // Resume refresh URIs for our child shells as well.
4812 PRInt32 n = mChildList.Count();
4814 for (PRInt32 i = 0; i < n; ++i) {
4815 nsCOMPtr<nsIDocShell> shell = do_QueryInterface(ChildAt(i));
4816 if (shell)
4817 shell->ResumeRefreshURIs();
4820 return NS_OK;
4823 nsresult
4824 nsDocShell::RefreshURIFromQueue()
4826 if (!mRefreshURIList)
4827 return NS_OK;
4828 PRUint32 n = 0;
4829 mRefreshURIList->Count(&n);
4831 while (n) {
4832 nsCOMPtr<nsISupports> element;
4833 mRefreshURIList->GetElementAt(--n, getter_AddRefs(element));
4834 nsCOMPtr<nsITimerCallback> refreshInfo(do_QueryInterface(element));
4836 if (refreshInfo) {
4837 // This is the nsRefreshTimer object, waiting to be
4838 // setup in a timer object and fired.
4839 // Create the timer and trigger it.
4840 PRUint32 delay = static_cast<nsRefreshTimer*>(static_cast<nsITimerCallback*>(refreshInfo))->GetDelay();
4841 nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1");
4842 if (timer) {
4843 // Replace the nsRefreshTimer element in the queue with
4844 // its corresponding timer object, so that in case another
4845 // load comes through before the timer can go off, the timer will
4846 // get cancelled in CancelRefreshURITimer()
4847 mRefreshURIList->ReplaceElementAt(timer, n);
4848 timer->InitWithCallback(refreshInfo, delay, nsITimer::TYPE_ONE_SHOT);
4851 } // while
4853 return NS_OK;
4856 //*****************************************************************************
4857 // nsDocShell::nsIContentViewerContainer
4858 //*****************************************************************************
4860 NS_IMETHODIMP
4861 nsDocShell::Embed(nsIContentViewer * aContentViewer,
4862 const char *aCommand, nsISupports * aExtraInfo)
4864 // Save the LayoutHistoryState of the previous document, before
4865 // setting up new document
4866 PersistLayoutHistoryState();
4868 nsresult rv = SetupNewViewer(aContentViewer);
4870 // If we are loading a wyciwyg url from history, change the base URI for
4871 // the document to the original http url that created the document.write().
4872 // This makes sure that all relative urls in a document.written page loaded
4873 // via history work properly.
4874 if (mCurrentURI &&
4875 (mLoadType & LOAD_CMD_HISTORY ||
4876 mLoadType == LOAD_RELOAD_NORMAL ||
4877 mLoadType == LOAD_RELOAD_CHARSET_CHANGE)){
4878 PRBool isWyciwyg = PR_FALSE;
4879 // Check if the url is wyciwyg
4880 rv = mCurrentURI->SchemeIs("wyciwyg", &isWyciwyg);
4881 if (isWyciwyg && NS_SUCCEEDED(rv))
4882 SetBaseUrlForWyciwyg(aContentViewer);
4884 // XXX What if SetupNewViewer fails?
4885 if (mLSHE) {
4886 // Restore the editing state, if it's stored in session history.
4887 if (mLSHE->HasDetachedEditor()) {
4888 ReattachEditorToWindow(mLSHE);
4890 SetHistoryEntry(&mOSHE, mLSHE);
4893 PRBool updateHistory = PR_TRUE;
4895 // Determine if this type of load should update history
4896 switch (mLoadType) {
4897 case LOAD_NORMAL_REPLACE:
4898 case LOAD_STOP_CONTENT_AND_REPLACE:
4899 case LOAD_RELOAD_BYPASS_CACHE:
4900 case LOAD_RELOAD_BYPASS_PROXY:
4901 case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
4902 updateHistory = PR_FALSE;
4903 break;
4904 default:
4905 break;
4908 if (!updateHistory)
4909 SetLayoutHistoryState(nsnull);
4911 return NS_OK;
4914 /* void setIsPrinting (in boolean aIsPrinting); */
4915 NS_IMETHODIMP
4916 nsDocShell::SetIsPrinting(PRBool aIsPrinting)
4918 mIsPrintingOrPP = aIsPrinting;
4919 return NS_OK;
4922 //*****************************************************************************
4923 // nsDocShell::nsIWebProgressListener
4924 //*****************************************************************************
4926 NS_IMETHODIMP
4927 nsDocShell::OnProgressChange(nsIWebProgress * aProgress,
4928 nsIRequest * aRequest,
4929 PRInt32 aCurSelfProgress,
4930 PRInt32 aMaxSelfProgress,
4931 PRInt32 aCurTotalProgress,
4932 PRInt32 aMaxTotalProgress)
4934 return NS_OK;
4937 NS_IMETHODIMP
4938 nsDocShell::OnStateChange(nsIWebProgress * aProgress, nsIRequest * aRequest,
4939 PRUint32 aStateFlags, nsresult aStatus)
4941 nsresult rv;
4943 // Update the busy cursor
4944 if ((~aStateFlags & (STATE_START | STATE_IS_NETWORK)) == 0) {
4945 nsCOMPtr<nsIWyciwygChannel> wcwgChannel(do_QueryInterface(aRequest));
4946 nsCOMPtr<nsIWebProgress> webProgress =
4947 do_QueryInterface(GetAsSupports(this));
4949 // Was the wyciwyg document loaded on this docshell?
4950 if (wcwgChannel && !mLSHE && (mItemType == typeContent) && aProgress == webProgress.get()) {
4951 nsCOMPtr<nsIURI> uri;
4952 wcwgChannel->GetURI(getter_AddRefs(uri));
4954 PRBool equalUri = PR_TRUE;
4955 // Store the wyciwyg url in session history, only if it is
4956 // being loaded fresh for the first time. We don't want
4957 // multiple entries for successive loads
4958 if (mCurrentURI &&
4959 NS_SUCCEEDED(uri->Equals(mCurrentURI, &equalUri)) &&
4960 !equalUri) {
4961 // This is a document.write(). Get the made-up url
4962 // from the channel and store it in session history.
4963 rv = AddToSessionHistory(uri, wcwgChannel, getter_AddRefs(mLSHE));
4964 SetCurrentURI(uri, aRequest, PR_TRUE);
4965 // Save history state of the previous page
4966 rv = PersistLayoutHistoryState();
4967 if (mOSHE)
4968 SetHistoryEntry(&mOSHE, mLSHE);
4972 // Page has begun to load
4973 mBusyFlags = BUSY_FLAGS_BUSY | BUSY_FLAGS_BEFORE_PAGE_LOAD;
4974 nsCOMPtr<nsIWidget> mainWidget;
4975 GetMainWidget(getter_AddRefs(mainWidget));
4976 if (mainWidget) {
4977 mainWidget->SetCursor(eCursor_spinning);
4980 else if ((~aStateFlags & (STATE_TRANSFERRING | STATE_IS_DOCUMENT)) == 0) {
4981 // Page is loading
4982 mBusyFlags = BUSY_FLAGS_BUSY | BUSY_FLAGS_PAGE_LOADING;
4984 else if ((aStateFlags & STATE_STOP) && (aStateFlags & STATE_IS_NETWORK)) {
4985 // Page has finished loading
4986 mBusyFlags = BUSY_FLAGS_NONE;
4987 nsCOMPtr<nsIWidget> mainWidget;
4988 GetMainWidget(getter_AddRefs(mainWidget));
4989 if (mainWidget) {
4990 mainWidget->SetCursor(eCursor_standard);
4993 if ((~aStateFlags & (STATE_IS_DOCUMENT | STATE_STOP)) == 0) {
4994 nsCOMPtr<nsIWebProgress> webProgress =
4995 do_QueryInterface(GetAsSupports(this));
4996 // Is the document stop notification for this document?
4997 if (aProgress == webProgress.get()) {
4998 nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
4999 EndPageLoad(aProgress, channel, aStatus);
5002 // note that redirect state changes will go through here as well, but it
5003 // is better to handle those in OnRedirectStateChange where more
5004 // information is available.
5005 return NS_OK;
5008 NS_IMETHODIMP
5009 nsDocShell::OnLocationChange(nsIWebProgress * aProgress,
5010 nsIRequest * aRequest, nsIURI * aURI)
5012 NS_NOTREACHED("notification excluded in AddProgressListener(...)");
5013 return NS_OK;
5016 void
5017 nsDocShell::OnRedirectStateChange(nsIChannel* aOldChannel,
5018 nsIChannel* aNewChannel,
5019 PRUint32 aRedirectFlags,
5020 PRUint32 aStateFlags)
5022 NS_ASSERTION(aStateFlags & STATE_REDIRECTING,
5023 "Calling OnRedirectStateChange when there is no redirect");
5024 if (!(aStateFlags & STATE_IS_DOCUMENT))
5025 return; // not a toplevel document
5027 // If this load is being checked by the URI classifier, we need to
5028 // query the classifier again for the new URI.
5029 if (mClassifier) {
5030 mClassifier->OnRedirect(aOldChannel, aNewChannel);
5033 nsCOMPtr<nsIGlobalHistory3> history3(do_QueryInterface(mGlobalHistory));
5034 nsresult result = NS_ERROR_NOT_IMPLEMENTED;
5035 if (history3) {
5036 // notify global history of this redirect
5037 result = history3->AddDocumentRedirect(aOldChannel, aNewChannel,
5038 aRedirectFlags, !IsFrame());
5041 if (result == NS_ERROR_NOT_IMPLEMENTED) {
5042 // when there is no GlobalHistory3, or it doesn't implement
5043 // AddToplevelRedirect, we fall back to GlobalHistory2. Just notify
5044 // that the redirecting page was a redirect so it will be link colored
5045 // but not visible.
5046 nsCOMPtr<nsIURI> oldURI;
5047 aOldChannel->GetURI(getter_AddRefs(oldURI));
5048 if (! oldURI)
5049 return; // nothing to tell anybody about
5050 AddToGlobalHistory(oldURI, PR_TRUE, aOldChannel);
5054 NS_IMETHODIMP
5055 nsDocShell::OnStatusChange(nsIWebProgress * aWebProgress,
5056 nsIRequest * aRequest,
5057 nsresult aStatus, const PRUnichar * aMessage)
5059 NS_NOTREACHED("notification excluded in AddProgressListener(...)");
5060 return NS_OK;
5063 NS_IMETHODIMP
5064 nsDocShell::OnSecurityChange(nsIWebProgress * aWebProgress,
5065 nsIRequest * aRequest, PRUint32 state)
5067 NS_NOTREACHED("notification excluded in AddProgressListener(...)");
5068 return NS_OK;
5072 nsresult
5073 nsDocShell::EndPageLoad(nsIWebProgress * aProgress,
5074 nsIChannel * aChannel, nsresult aStatus)
5077 // one of many safeguards that prevent death and destruction if
5078 // someone is so very very rude as to bring this window down
5079 // during this load handler.
5081 nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
5083 // We're done with the URI classifier for this channel
5084 mClassifier = nsnull;
5087 // Notify the ContentViewer that the Document has finished loading...
5089 // This will cause any OnLoad(...) handlers to fire, if it is a HTML
5090 // document...
5092 if (!mEODForCurrentDocument && mContentViewer) {
5093 mIsExecutingOnLoadHandler = PR_TRUE;
5094 mContentViewer->LoadComplete(aStatus);
5095 mIsExecutingOnLoadHandler = PR_FALSE;
5097 mEODForCurrentDocument = PR_TRUE;
5099 // If all documents have completed their loading
5100 // favor native event dispatch priorities
5101 // over performance
5102 if (--gNumberOfDocumentsLoading == 0) {
5103 // Hint to use normal native event dispatch priorities
5104 FavorPerformanceHint(PR_FALSE, NS_EVENT_STARVATION_DELAY_HINT);
5107 /* Check if the httpChannel has any cache-control related response headers,
5108 * like no-store, no-cache. If so, update SHEntry so that
5109 * when a user goes back/forward to this page, we appropriately do
5110 * form value restoration or load from server.
5112 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
5113 if (!httpChannel) // HttpChannel could be hiding underneath a Multipart channel.
5114 GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
5116 if (httpChannel) {
5117 // figure out if SH should be saving layout state.
5118 PRBool discardLayoutState = ShouldDiscardLayoutState(httpChannel);
5119 if (mLSHE && discardLayoutState && (mLoadType & LOAD_CMD_NORMAL) &&
5120 (mLoadType != LOAD_BYPASS_HISTORY) && (mLoadType != LOAD_ERROR_PAGE))
5121 mLSHE->SetSaveLayoutStateFlag(PR_FALSE);
5124 // Clear mLSHE after calling the onLoadHandlers. This way, if the
5125 // onLoadHandler tries to load something different in
5126 // itself or one of its children, we can deal with it appropriately.
5127 if (mLSHE) {
5128 mLSHE->SetLoadType(nsIDocShellLoadInfo::loadHistory);
5130 // Clear the mLSHE reference to indicate document loading is done one
5131 // way or another.
5132 SetHistoryEntry(&mLSHE, nsnull);
5134 // if there's a refresh header in the channel, this method
5135 // will set it up for us.
5136 RefreshURIFromQueue();
5138 return NS_OK;
5142 //*****************************************************************************
5143 // nsDocShell: Content Viewer Management
5144 //*****************************************************************************
5146 NS_IMETHODIMP
5147 nsDocShell::EnsureContentViewer()
5149 if (mContentViewer)
5150 return NS_OK;
5151 if (mIsBeingDestroyed)
5152 return NS_ERROR_FAILURE;
5154 nsIPrincipal* principal = nsnull;
5156 nsCOMPtr<nsPIDOMWindow> piDOMWindow(do_QueryInterface(mScriptGlobal));
5157 if (piDOMWindow) {
5158 principal = piDOMWindow->GetOpenerScriptPrincipal();
5161 if (!principal) {
5162 principal = GetInheritedPrincipal(PR_FALSE);
5165 nsresult rv = CreateAboutBlankContentViewer(principal);
5167 if (NS_SUCCEEDED(rv)) {
5168 nsCOMPtr<nsIDOMDocument> domDoc;
5169 mContentViewer->GetDOMDocument(getter_AddRefs(domDoc));
5170 nsCOMPtr<nsIDocument> doc(do_QueryInterface(domDoc));
5171 NS_ASSERTION(doc,
5172 "Should have doc if CreateAboutBlankContentViewer "
5173 "succeeded!");
5175 doc->SetIsInitialDocument(PR_TRUE);
5178 return rv;
5181 nsresult
5182 nsDocShell::CreateAboutBlankContentViewer(nsIPrincipal* aPrincipal)
5184 nsCOMPtr<nsIDocument> blankDoc;
5185 nsCOMPtr<nsIContentViewer> viewer;
5186 nsresult rv = NS_ERROR_FAILURE;
5188 /* mCreatingDocument should never be true at this point. However, it's
5189 a theoretical possibility. We want to know about it and make it stop,
5190 and this sounds like a job for an assertion. */
5191 NS_ASSERTION(!mCreatingDocument, "infinite(?) loop creating document averted");
5192 if (mCreatingDocument)
5193 return NS_ERROR_FAILURE;
5195 mCreatingDocument = PR_TRUE;
5197 // mContentViewer->PermitUnload may release |this| docshell.
5198 nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
5200 if (mContentViewer) {
5201 // We've got a content viewer already. Make sure the user
5202 // permits us to discard the current document and replace it
5203 // with about:blank. And also ensure we fire the unload events
5204 // in the current document.
5206 PRBool okToUnload;
5207 rv = mContentViewer->PermitUnload(&okToUnload);
5209 if (NS_SUCCEEDED(rv) && !okToUnload) {
5210 // The user chose not to unload the page, interrupt the load.
5211 return NS_ERROR_FAILURE;
5214 mSavingOldViewer = CanSavePresentation(LOAD_NORMAL, nsnull, nsnull);
5216 // Make sure to blow away our mLoadingURI just in case. No loads
5217 // from inside this pagehide.
5218 mLoadingURI = nsnull;
5220 // Notify the current document that it is about to be unloaded!!
5222 // It is important to fire the unload() notification *before* any state
5223 // is changed within the DocShell - otherwise, javascript will get the
5224 // wrong information :-(
5226 (void) FirePageHideNotification(!mSavingOldViewer);
5229 // Now make sure we don't think we're in the middle of firing unload after
5230 // this point. This will make us fire unload when the about:blank document
5231 // unloads... but that's ok, more or less. Would be nice if it fired load
5232 // too, of course.
5233 mFiredUnloadEvent = PR_FALSE;
5235 // one helper factory, please
5236 nsCOMPtr<nsICategoryManager> catMan(do_GetService(NS_CATEGORYMANAGER_CONTRACTID));
5237 if (!catMan)
5238 return NS_ERROR_FAILURE;
5240 nsXPIDLCString contractId;
5241 rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", "text/html", getter_Copies(contractId));
5242 if (NS_FAILED(rv))
5243 return rv;
5245 nsCOMPtr<nsIDocumentLoaderFactory> docFactory(do_GetService(contractId));
5246 if (docFactory) {
5247 // generate (about:blank) document to load
5248 docFactory->CreateBlankDocument(mLoadGroup, aPrincipal,
5249 getter_AddRefs(blankDoc));
5250 if (blankDoc) {
5251 blankDoc->SetContainer(static_cast<nsIDocShell *>(this));
5253 // create a content viewer for us and the new document
5254 docFactory->CreateInstanceForDocument(NS_ISUPPORTS_CAST(nsIDocShell *, this),
5255 blankDoc, "view", getter_AddRefs(viewer));
5257 // hook 'em up
5258 if (viewer) {
5259 viewer->SetContainer(static_cast<nsIContentViewerContainer *>(this));
5260 nsCOMPtr<nsIDOMDocument> domdoc(do_QueryInterface(blankDoc));
5261 Embed(viewer, "", 0);
5262 viewer->SetDOMDocument(domdoc);
5264 SetCurrentURI(blankDoc->GetDocumentURI(), nsnull, PR_TRUE);
5265 rv = NS_OK;
5269 mCreatingDocument = PR_FALSE;
5271 // The transient about:blank viewer doesn't have a session history entry.
5272 SetHistoryEntry(&mOSHE, nsnull);
5274 return rv;
5277 PRBool
5278 nsDocShell::CanSavePresentation(PRUint32 aLoadType,
5279 nsIRequest *aNewRequest,
5280 nsIDocument *aNewDocument)
5282 if (!mOSHE)
5283 return PR_FALSE; // no entry to save into
5285 // Only save presentation for "normal" loads and link loads. Anything else
5286 // probably wants to refetch the page, so caching the old presentation
5287 // would be incorrect.
5288 if (aLoadType != LOAD_NORMAL &&
5289 aLoadType != LOAD_HISTORY &&
5290 aLoadType != LOAD_LINK &&
5291 aLoadType != LOAD_STOP_CONTENT &&
5292 aLoadType != LOAD_STOP_CONTENT_AND_REPLACE &&
5293 aLoadType != LOAD_ERROR_PAGE)
5294 return PR_FALSE;
5296 // If the session history entry has the saveLayoutState flag set to false,
5297 // then we should not cache the presentation.
5298 PRBool canSaveState;
5299 mOSHE->GetSaveLayoutStateFlag(&canSaveState);
5300 if (canSaveState == PR_FALSE)
5301 return PR_FALSE;
5303 // If the document is not done loading, don't cache it.
5304 nsCOMPtr<nsPIDOMWindow> pWin = do_QueryInterface(mScriptGlobal);
5305 if (!pWin || pWin->IsLoading())
5306 return PR_FALSE;
5308 if (pWin->WouldReuseInnerWindow(aNewDocument))
5309 return PR_FALSE;
5311 // Avoid doing the work of saving the presentation state in the case where
5312 // the content viewer cache is disabled.
5313 if (nsSHistory::GetMaxTotalViewers() == 0)
5314 return PR_FALSE;
5316 // Don't cache the content viewer if we're in a subframe and the subframe
5317 // pref is disabled.
5318 PRBool cacheFrames = PR_FALSE;
5319 mPrefs->GetBoolPref("browser.sessionhistory.cache_subframes",
5320 &cacheFrames);
5321 if (!cacheFrames) {
5322 nsCOMPtr<nsIDocShellTreeItem> root;
5323 GetSameTypeParent(getter_AddRefs(root));
5324 if (root && root != this) {
5325 return PR_FALSE; // this is a subframe load
5329 // If the document does not want its presentation cached, then don't.
5330 nsCOMPtr<nsIDocument> doc = do_QueryInterface(pWin->GetExtantDocument());
5331 if (!doc || !doc->CanSavePresentation(aNewRequest))
5332 return PR_FALSE;
5334 return PR_TRUE;
5337 void
5338 nsDocShell::ReattachEditorToWindow(nsISHEntry *aSHEntry)
5340 NS_ASSERTION(!mEditorData,
5341 "Why reattach an editor when we already have one?");
5342 NS_ASSERTION(aSHEntry && aSHEntry->HasDetachedEditor(),
5343 "Reattaching when there's not a detached editor.");
5345 if (mEditorData || !aSHEntry)
5346 return;
5348 mEditorData = aSHEntry->ForgetEditorData();
5349 if (mEditorData) {
5350 nsresult res = mEditorData->ReattachToWindow(this);
5351 NS_ASSERTION(NS_SUCCEEDED(res), "Failed to reattach editing session");
5355 void
5356 nsDocShell::DetachEditorFromWindow(nsISHEntry *aSHEntry)
5358 if (!mEditorData)
5359 return;
5361 NS_ASSERTION(!aSHEntry || !aSHEntry->HasDetachedEditor(),
5362 "Detaching editor when it's already detached.");
5364 nsresult res = mEditorData->DetachFromWindow();
5365 NS_ASSERTION(NS_SUCCEEDED(res), "Failed to detach editor");
5367 if (NS_SUCCEEDED(res)) {
5368 // Make aSHEntry hold the owning ref to the editor data.
5369 if (aSHEntry)
5370 aSHEntry->SetEditorData(mEditorData.forget());
5371 else
5372 mEditorData = nsnull;
5375 #ifdef DEBUG
5377 PRBool isEditable;
5378 GetEditable(&isEditable);
5379 NS_ASSERTION(!isEditable,
5380 "Window is still editable after detaching editor.");
5382 #endif // DEBUG
5386 void
5387 nsDocShell::DetachEditorFromWindow()
5389 if (mOSHE)
5390 DetachEditorFromWindow(mOSHE);
5393 nsresult
5394 nsDocShell::CaptureState()
5396 if (!mOSHE || mOSHE == mLSHE) {
5397 // No entry to save into, or we're replacing the existing entry.
5398 return NS_ERROR_FAILURE;
5401 nsCOMPtr<nsPIDOMWindow> privWin = do_QueryInterface(mScriptGlobal);
5402 if (!privWin)
5403 return NS_ERROR_FAILURE;
5405 nsCOMPtr<nsISupports> windowState;
5406 nsresult rv = privWin->SaveWindowState(getter_AddRefs(windowState));
5407 NS_ENSURE_SUCCESS(rv, rv);
5409 #ifdef DEBUG_PAGE_CACHE
5410 nsCOMPtr<nsIURI> uri;
5411 mOSHE->GetURI(getter_AddRefs(uri));
5412 nsCAutoString spec;
5413 if (uri)
5414 uri->GetSpec(spec);
5415 printf("Saving presentation into session history\n");
5416 printf(" SH URI: %s\n", spec.get());
5417 #endif
5419 rv = mOSHE->SetWindowState(windowState);
5420 NS_ENSURE_SUCCESS(rv, rv);
5422 // Suspend refresh URIs and save off the timer queue
5423 rv = mOSHE->SetRefreshURIList(mSavedRefreshURIList);
5424 NS_ENSURE_SUCCESS(rv, rv);
5426 // Capture the current content viewer bounds.
5427 nsCOMPtr<nsIPresShell> shell;
5428 nsDocShell::GetPresShell(getter_AddRefs(shell));
5429 if (shell) {
5430 nsIViewManager *vm = shell->GetViewManager();
5431 if (vm) {
5432 nsIView *rootView = nsnull;
5433 vm->GetRootView(rootView);
5434 if (rootView) {
5435 nsIWidget *widget = rootView->GetWidget();
5436 if (widget) {
5437 nsRect bounds(0, 0, 0, 0);
5438 widget->GetBounds(bounds);
5439 rv = mOSHE->SetViewerBounds(bounds);
5445 // Capture the docshell hierarchy.
5446 mOSHE->ClearChildShells();
5448 PRInt32 childCount = mChildList.Count();
5449 for (PRInt32 i = 0; i < childCount; ++i) {
5450 nsCOMPtr<nsIDocShellTreeItem> childShell = do_QueryInterface(ChildAt(i));
5451 NS_ASSERTION(childShell, "null child shell");
5453 mOSHE->AddChildShell(childShell);
5456 return NS_OK;
5459 NS_IMETHODIMP
5460 nsDocShell::RestorePresentationEvent::Run()
5462 if (mDocShell && NS_FAILED(mDocShell->RestoreFromHistory()))
5463 NS_WARNING("RestoreFromHistory failed");
5464 return NS_OK;
5467 NS_IMETHODIMP
5468 nsDocShell::BeginRestore(nsIContentViewer *aContentViewer, PRBool aTop)
5470 nsresult rv;
5471 if (!aContentViewer) {
5472 rv = EnsureContentViewer();
5473 NS_ENSURE_SUCCESS(rv, rv);
5475 aContentViewer = mContentViewer;
5478 // Dispatch events for restoring the presentation. We try to simulate
5479 // the progress notifications loading the document would cause, so we add
5480 // the document's channel to the loadgroup to initiate stateChange
5481 // notifications.
5483 nsCOMPtr<nsIDOMDocument> domDoc;
5484 aContentViewer->GetDOMDocument(getter_AddRefs(domDoc));
5485 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
5486 if (doc) {
5487 nsIChannel *channel = doc->GetChannel();
5488 if (channel) {
5489 mEODForCurrentDocument = PR_FALSE;
5490 mIsRestoringDocument = PR_TRUE;
5491 mLoadGroup->AddRequest(channel, nsnull);
5492 mIsRestoringDocument = PR_FALSE;
5496 if (!aTop) {
5497 // This point corresponds to us having gotten OnStartRequest or
5498 // STATE_START, so do the same thing that CreateContentViewer does at
5499 // this point to ensure that unload/pagehide events for this document
5500 // will fire when it's unloaded again.
5501 mFiredUnloadEvent = PR_FALSE;
5503 // For non-top frames, there is no notion of making sure that the
5504 // previous document is in the domwindow when STATE_START notifications
5505 // happen. We can just call BeginRestore for all of the child shells
5506 // now.
5507 rv = BeginRestoreChildren();
5508 NS_ENSURE_SUCCESS(rv, rv);
5511 return NS_OK;
5514 nsresult
5515 nsDocShell::BeginRestoreChildren()
5517 PRInt32 n = mChildList.Count();
5518 for (PRInt32 i = 0; i < n; ++i) {
5519 nsCOMPtr<nsIDocShell> child = do_QueryInterface(ChildAt(i));
5520 if (child) {
5521 nsresult rv = child->BeginRestore(nsnull, PR_FALSE);
5522 NS_ENSURE_SUCCESS(rv, rv);
5525 return NS_OK;
5528 NS_IMETHODIMP
5529 nsDocShell::FinishRestore()
5531 // First we call finishRestore() on our children. In the simulated load,
5532 // all of the child frames finish loading before the main document.
5534 PRInt32 n = mChildList.Count();
5535 for (PRInt32 i = 0; i < n; ++i) {
5536 nsCOMPtr<nsIDocShell> child = do_QueryInterface(ChildAt(i));
5537 if (child) {
5538 child->FinishRestore();
5542 if (mOSHE && mOSHE->HasDetachedEditor()) {
5543 ReattachEditorToWindow(mOSHE);
5546 if (mContentViewer) {
5547 nsCOMPtr<nsIDOMDocument> domDoc;
5548 mContentViewer->GetDOMDocument(getter_AddRefs(domDoc));
5550 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
5551 if (doc) {
5552 // Finally, we remove the request from the loadgroup. This will
5553 // cause onStateChange(STATE_STOP) to fire, which will fire the
5554 // pageshow event to the chrome.
5556 nsIChannel *channel = doc->GetChannel();
5557 if (channel) {
5558 mIsRestoringDocument = PR_TRUE;
5559 mLoadGroup->RemoveRequest(channel, nsnull, NS_OK);
5560 mIsRestoringDocument = PR_FALSE;
5565 return NS_OK;
5568 NS_IMETHODIMP
5569 nsDocShell::GetRestoringDocument(PRBool *aRestoring)
5571 *aRestoring = mIsRestoringDocument;
5572 return NS_OK;
5575 nsresult
5576 nsDocShell::RestorePresentation(nsISHEntry *aSHEntry, PRBool *aRestoring)
5578 NS_ASSERTION(mLoadType & LOAD_CMD_HISTORY,
5579 "RestorePresentation should only be called for history loads");
5581 nsCOMPtr<nsIContentViewer> viewer;
5582 aSHEntry->GetContentViewer(getter_AddRefs(viewer));
5584 #ifdef DEBUG_PAGE_CACHE
5585 nsCOMPtr<nsIURI> uri;
5586 aSHEntry->GetURI(getter_AddRefs(uri));
5588 nsCAutoString spec;
5589 if (uri)
5590 uri->GetSpec(spec);
5591 #endif
5593 *aRestoring = PR_FALSE;
5595 if (!viewer) {
5596 #ifdef DEBUG_PAGE_CACHE
5597 printf("no saved presentation for uri: %s\n", spec.get());
5598 #endif
5599 return NS_OK;
5602 // We need to make sure the content viewer's container is this docshell.
5603 // In subframe navigation, it's possible for the docshell that the
5604 // content viewer was originally loaded into to be replaced with a
5605 // different one. We don't currently support restoring the presentation
5606 // in that case.
5608 nsCOMPtr<nsISupports> container;
5609 viewer->GetContainer(getter_AddRefs(container));
5610 if (!::SameCOMIdentity(container, GetAsSupports(this))) {
5611 #ifdef DEBUG_PAGE_CACHE
5612 printf("No valid container, clearing presentation\n");
5613 #endif
5614 aSHEntry->SetContentViewer(nsnull);
5615 return NS_ERROR_FAILURE;
5618 NS_ASSERTION(mContentViewer != viewer, "Restoring existing presentation");
5620 #ifdef DEBUG_PAGE_CACHE
5621 printf("restoring presentation from session history: %s\n", spec.get());
5622 #endif
5624 SetHistoryEntry(&mLSHE, aSHEntry);
5626 // Add the request to our load group. We do this before swapping out
5627 // the content viewers so that consumers of STATE_START can access
5628 // the old document. We only deal with the toplevel load at this time --
5629 // to be consistent with normal document loading, subframes cannot start
5630 // loading until after data arrives, which is after STATE_START completes.
5632 BeginRestore(viewer, PR_TRUE);
5634 // Post an event that will remove the request after we've returned
5635 // to the event loop. This mimics the way it is called by nsIChannel
5636 // implementations.
5638 // Revoke any pending restore (just in case)
5639 NS_ASSERTION(!mRestorePresentationEvent.IsPending(),
5640 "should only have one RestorePresentationEvent");
5641 mRestorePresentationEvent.Revoke();
5643 nsRefPtr<RestorePresentationEvent> evt = new RestorePresentationEvent(this);
5644 nsresult rv = NS_DispatchToCurrentThread(evt);
5645 if (NS_SUCCEEDED(rv)) {
5646 mRestorePresentationEvent = evt.get();
5647 // The rest of the restore processing will happen on our event
5648 // callback.
5649 *aRestoring = PR_TRUE;
5652 return rv;
5655 nsresult
5656 nsDocShell::RestoreFromHistory()
5658 mRestorePresentationEvent.Forget();
5660 // This section of code follows the same ordering as CreateContentViewer.
5661 if (!mLSHE)
5662 return NS_ERROR_FAILURE;
5664 nsCOMPtr<nsIContentViewer> viewer;
5665 mLSHE->GetContentViewer(getter_AddRefs(viewer));
5666 if (!viewer)
5667 return NS_ERROR_FAILURE;
5669 if (mSavingOldViewer) {
5670 // We determined that it was safe to cache the document presentation
5671 // at the time we initiated the new load. We need to check whether
5672 // it's still safe to do so, since there may have been DOM mutations
5673 // or new requests initiated.
5674 nsCOMPtr<nsIDOMDocument> domDoc;
5675 viewer->GetDOMDocument(getter_AddRefs(domDoc));
5676 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
5677 nsIRequest *request = nsnull;
5678 if (doc)
5679 request = doc->GetChannel();
5680 mSavingOldViewer = CanSavePresentation(mLoadType, request, doc);
5683 nsCOMPtr<nsIMarkupDocumentViewer> oldMUDV(do_QueryInterface(mContentViewer));
5684 nsCOMPtr<nsIMarkupDocumentViewer> newMUDV(do_QueryInterface(viewer));
5685 float textZoom = 1.0f;
5686 float pageZoom = 1.0f;
5687 if (oldMUDV && newMUDV) {
5688 oldMUDV->GetTextZoom(&textZoom);
5689 oldMUDV->GetFullZoom(&pageZoom);
5692 // Protect against mLSHE going away via a load triggered from
5693 // pagehide or unload.
5694 nsCOMPtr<nsISHEntry> origLSHE = mLSHE;
5696 // Make sure to blow away our mLoadingURI just in case. No loads
5697 // from inside this pagehide.
5698 mLoadingURI = nsnull;
5700 // Notify the old content viewer that it's being hidden.
5701 FirePageHideNotification(!mSavingOldViewer);
5703 // If mLSHE was changed as a result of the pagehide event, then
5704 // something else was loaded. Don't finish restoring.
5705 if (mLSHE != origLSHE)
5706 return NS_OK;
5708 // Set mFiredUnloadEvent = PR_FALSE so that the unload handler for the
5709 // *new* document will fire.
5710 mFiredUnloadEvent = PR_FALSE;
5712 mURIResultedInDocument = PR_TRUE;
5713 nsCOMPtr<nsISHistory> rootSH;
5714 GetRootSessionHistory(getter_AddRefs(rootSH));
5715 if (rootSH) {
5716 nsCOMPtr<nsISHistoryInternal> hist = do_QueryInterface(rootSH);
5717 rootSH->GetIndex(&mPreviousTransIndex);
5718 hist->UpdateIndex();
5719 rootSH->GetIndex(&mLoadedTransIndex);
5720 #ifdef DEBUG_PAGE_CACHE
5721 printf("Previous index: %d, Loaded index: %d\n\n", mPreviousTransIndex,
5722 mLoadedTransIndex);
5723 #endif
5726 // Rather than call Embed(), we will retrieve the viewer from the session
5727 // history entry and swap it in.
5728 // XXX can we refactor this so that we can just call Embed()?
5729 PersistLayoutHistoryState();
5730 nsresult rv;
5731 if (mContentViewer) {
5732 if (mSavingOldViewer && NS_FAILED(CaptureState())) {
5733 if (mOSHE) {
5734 mOSHE->SyncPresentationState();
5736 mSavingOldViewer = PR_FALSE;
5740 mSavedRefreshURIList = nsnull;
5742 // In cases where we use a transient about:blank viewer between loads,
5743 // we never show the transient viewer, so _its_ previous viewer is never
5744 // unhooked from the view hierarchy. Destroy any such previous viewer now,
5745 // before we grab the root view sibling, so that we don't grab a view
5746 // that's about to go away.
5748 if (mContentViewer) {
5749 nsCOMPtr<nsIContentViewer> previousViewer;
5750 mContentViewer->GetPreviousViewer(getter_AddRefs(previousViewer));
5751 if (previousViewer) {
5752 mContentViewer->SetPreviousViewer(nsnull);
5753 previousViewer->Destroy();
5757 // Save off the root view's parent and sibling so that we can insert the
5758 // new content viewer's root view at the same position. Also save the
5759 // bounds of the root view's widget.
5761 nsIView *rootViewSibling = nsnull, *rootViewParent = nsnull;
5762 nsRect newBounds(0, 0, 0, 0);
5764 nsCOMPtr<nsIPresShell> oldPresShell;
5765 nsDocShell::GetPresShell(getter_AddRefs(oldPresShell));
5766 if (oldPresShell) {
5767 nsIViewManager *vm = oldPresShell->GetViewManager();
5768 if (vm) {
5769 nsIView *oldRootView = nsnull;
5770 vm->GetRootView(oldRootView);
5772 if (oldRootView) {
5773 rootViewSibling = oldRootView->GetNextSibling();
5774 rootViewParent = oldRootView->GetParent();
5776 nsIWidget *widget = oldRootView->GetWidget();
5777 if (widget) {
5778 widget->GetBounds(newBounds);
5784 // Transfer ownership to mContentViewer. By ensuring that either the
5785 // docshell or the session history, but not both, have references to the
5786 // content viewer, we prevent the viewer from being torn down after
5787 // Destroy() is called.
5789 if (mContentViewer) {
5790 mContentViewer->Close(mSavingOldViewer ? mOSHE.get() : nsnull);
5791 viewer->SetPreviousViewer(mContentViewer);
5794 mContentViewer.swap(viewer);
5795 viewer = nsnull; // force a release to complete ownership transfer
5797 // Grab all of the related presentation from the SHEntry now.
5798 // Clearing the viewer from the SHEntry will clear all of this state.
5799 nsCOMPtr<nsISupports> windowState;
5800 mLSHE->GetWindowState(getter_AddRefs(windowState));
5801 mLSHE->SetWindowState(nsnull);
5803 PRBool sticky;
5804 mLSHE->GetSticky(&sticky);
5806 nsCOMPtr<nsIDOMDocument> domDoc;
5807 mContentViewer->GetDOMDocument(getter_AddRefs(domDoc));
5809 nsCOMArray<nsIDocShellTreeItem> childShells;
5810 PRInt32 i = 0;
5811 nsCOMPtr<nsIDocShellTreeItem> child;
5812 while (NS_SUCCEEDED(mLSHE->ChildShellAt(i++, getter_AddRefs(child))) &&
5813 child) {
5814 childShells.AppendObject(child);
5817 // get the previous content viewer size
5818 nsRect oldBounds(0, 0, 0, 0);
5819 mLSHE->GetViewerBounds(oldBounds);
5821 // Restore the refresh URI list. The refresh timers will be restarted
5822 // when EndPageLoad() is called.
5823 nsCOMPtr<nsISupportsArray> refreshURIList;
5824 mLSHE->GetRefreshURIList(getter_AddRefs(refreshURIList));
5826 // Reattach to the window object.
5827 rv = mContentViewer->Open(windowState, mLSHE);
5829 // Now remove it from the cached presentation.
5830 mLSHE->SetContentViewer(nsnull);
5831 mEODForCurrentDocument = PR_FALSE;
5833 #ifdef DEBUG
5835 nsCOMPtr<nsISupportsArray> refreshURIs;
5836 mLSHE->GetRefreshURIList(getter_AddRefs(refreshURIs));
5837 nsCOMPtr<nsIDocShellTreeItem> childShell;
5838 mLSHE->ChildShellAt(0, getter_AddRefs(childShell));
5839 NS_ASSERTION(!refreshURIs && !childShell,
5840 "SHEntry should have cleared presentation state");
5842 #endif
5844 // Restore the sticky state of the viewer. The viewer has set this state
5845 // on the history entry in Destroy() just before marking itself non-sticky,
5846 // to avoid teardown of the presentation.
5847 mContentViewer->SetSticky(sticky);
5849 // Now that we have switched documents, forget all of our children.
5850 DestroyChildren();
5851 NS_ENSURE_SUCCESS(rv, rv);
5853 // mLSHE is now our currently-loaded document.
5854 SetHistoryEntry(&mOSHE, mLSHE);
5856 // XXX special wyciwyg handling in Embed()?
5858 // We aren't going to restore any items from the LayoutHistoryState,
5859 // but we don't want them to stay around in case the page is reloaded.
5860 SetLayoutHistoryState(nsnull);
5862 // This is the end of our Embed() replacement
5864 mSavingOldViewer = PR_FALSE;
5865 mEODForCurrentDocument = PR_FALSE;
5867 // Tell the event loop to favor plevents over user events, see comments
5868 // in CreateContentViewer.
5869 if (++gNumberOfDocumentsLoading == 1)
5870 FavorPerformanceHint(PR_TRUE, NS_EVENT_STARVATION_DELAY_HINT);
5873 if (oldMUDV && newMUDV) {
5874 newMUDV->SetTextZoom(textZoom);
5875 newMUDV->SetFullZoom(pageZoom);
5878 nsCOMPtr<nsIDocument> document = do_QueryInterface(domDoc);
5879 if (document) {
5880 // Use the uri from the mLSHE we had when we entered this function
5881 // (which need not match the document's URI if anchors are involved),
5882 // since that's the history entry we're loading. Note that if we use
5883 // origLSHE we don't have to worry about whether the entry in question
5884 // is still mLSHE or whether it's now mOSHE.
5885 nsCOMPtr<nsIURI> uri;
5886 origLSHE->GetURI(getter_AddRefs(uri));
5887 SetCurrentURI(uri, document->GetChannel(), PR_TRUE);
5890 // This is the end of our CreateContentViewer() replacement.
5891 // Now we simulate a load. First, we restore the state of the javascript
5892 // window object.
5893 nsCOMPtr<nsPIDOMWindow> privWin =
5894 do_GetInterface(static_cast<nsIInterfaceRequestor*>(this));
5895 NS_ASSERTION(privWin, "could not get nsPIDOMWindow interface");
5897 rv = privWin->RestoreWindowState(windowState);
5898 NS_ENSURE_SUCCESS(rv, rv);
5900 // Now, dispatch a title change event which would happen as the
5901 // <head> is parsed.
5902 document->NotifyPossibleTitleChange(PR_FALSE);
5904 // Now we simulate appending child docshells for subframes.
5905 for (i = 0; i < childShells.Count(); ++i) {
5906 nsIDocShellTreeItem *childItem = childShells.ObjectAt(i);
5907 nsCOMPtr<nsIDocShell> childShell = do_QueryInterface(childItem);
5909 // Make sure to not clobber the state of the child. Since AddChild
5910 // always clobbers it, save it off first.
5911 PRBool allowPlugins;
5912 childShell->GetAllowPlugins(&allowPlugins);
5914 PRBool allowJavascript;
5915 childShell->GetAllowJavascript(&allowJavascript);
5917 PRBool allowRedirects;
5918 childShell->GetAllowMetaRedirects(&allowRedirects);
5920 PRBool allowSubframes;
5921 childShell->GetAllowSubframes(&allowSubframes);
5923 PRBool allowImages;
5924 childShell->GetAllowImages(&allowImages);
5926 AddChild(childItem);
5928 childShell->SetAllowPlugins(allowPlugins);
5929 childShell->SetAllowJavascript(allowJavascript);
5930 childShell->SetAllowMetaRedirects(allowRedirects);
5931 childShell->SetAllowSubframes(allowSubframes);
5932 childShell->SetAllowImages(allowImages);
5934 rv = childShell->BeginRestore(nsnull, PR_FALSE);
5935 NS_ENSURE_SUCCESS(rv, rv);
5938 nsCOMPtr<nsIPresShell> shell;
5939 nsDocShell::GetPresShell(getter_AddRefs(shell));
5941 nsIViewManager *newVM = shell ? shell->GetViewManager() : nsnull;
5942 nsIView *newRootView = nsnull;
5943 if (newVM)
5944 newVM->GetRootView(newRootView);
5946 // Insert the new root view at the correct location in the view tree.
5947 if (rootViewParent) {
5948 nsIViewManager *parentVM = rootViewParent->GetViewManager();
5950 if (parentVM && newRootView) {
5951 // InsertChild(parent, child, sib, PR_TRUE) inserts the child after
5952 // sib in content order, which is before sib in view order. BUT
5953 // when sib is null it inserts at the end of the the document
5954 // order, i.e., first in view order. But when oldRootSibling is
5955 // null, the old root as at the end of the view list --- last in
5956 // content order --- and we want to call InsertChild(parent, child,
5957 // nsnull, PR_FALSE) in that case.
5958 parentVM->InsertChild(rootViewParent, newRootView,
5959 rootViewSibling,
5960 rootViewSibling ? PR_TRUE : PR_FALSE);
5962 NS_ASSERTION(newRootView->GetNextSibling() == rootViewSibling,
5963 "error in InsertChild");
5967 // Now that all of the child docshells have been put into place, we can
5968 // restart the timers for the window and all of the child frames.
5969 privWin->ResumeTimeouts();
5971 // Restore the refresh URI list. The refresh timers will be restarted
5972 // when EndPageLoad() is called.
5973 mRefreshURIList = refreshURIList;
5975 // Meta-refresh timers have been restarted for this shell, but not
5976 // for our children. Walk the child shells and restart their timers.
5977 PRInt32 n = mChildList.Count();
5978 for (i = 0; i < n; ++i) {
5979 nsCOMPtr<nsIDocShell> child = do_QueryInterface(ChildAt(i));
5980 if (child)
5981 child->ResumeRefreshURIs();
5984 // Make sure this presentation is the same size as the previous
5985 // presentation. If this is not the same size we showed it at last time,
5986 // then we need to resize the widget.
5988 // XXXbryner This interacts poorly with Firefox's infobar. If the old
5989 // presentation had the infobar visible, then we will resize the new
5990 // presentation to that smaller size. However, firing the locationchanged
5991 // event will hide the infobar, which will immediately resize the window
5992 // back to the larger size. A future optimization might be to restore
5993 // the presentation at the "wrong" size, then fire the locationchanged
5994 // event and check whether the docshell's new size is the same as the
5995 // cached viewer size (skipping the resize if they are equal).
5997 if (newRootView) {
5998 nsIWidget *widget = newRootView->GetWidget();
5999 if (widget && !newBounds.IsEmpty() && newBounds != oldBounds) {
6000 #ifdef DEBUG_PAGE_CACHE
6001 printf("resize widget(%d, %d, %d, %d)\n", newBounds.x,
6002 newBounds.y, newBounds.width, newBounds.height);
6003 #endif
6005 widget->Resize(newBounds.x, newBounds.y, newBounds.width,
6006 newBounds.height, PR_FALSE);
6010 // Simulate the completion of the load.
6011 nsDocShell::FinishRestore();
6013 // Restart plugins, and paint the content.
6014 if (shell)
6015 shell->Thaw();
6017 return privWin->FireDelayedDOMEvents();
6020 NS_IMETHODIMP
6021 nsDocShell::CreateContentViewer(const char *aContentType,
6022 nsIRequest * request,
6023 nsIStreamListener ** aContentHandler)
6025 *aContentHandler = nsnull;
6027 // Can we check the content type of the current content viewer
6028 // and reuse it without destroying it and re-creating it?
6030 NS_ASSERTION(mLoadGroup, "Someone ignored return from Init()?");
6032 // Instantiate the content viewer object
6033 nsCOMPtr<nsIContentViewer> viewer;
6034 nsresult rv = NewContentViewerObj(aContentType, request, mLoadGroup,
6035 aContentHandler, getter_AddRefs(viewer));
6037 if (NS_FAILED(rv))
6038 return NS_ERROR_FAILURE;
6040 // Notify the current document that it is about to be unloaded!!
6042 // It is important to fire the unload() notification *before* any state
6043 // is changed within the DocShell - otherwise, javascript will get the
6044 // wrong information :-(
6047 if (mSavingOldViewer) {
6048 // We determined that it was safe to cache the document presentation
6049 // at the time we initiated the new load. We need to check whether
6050 // it's still safe to do so, since there may have been DOM mutations
6051 // or new requests initiated.
6052 nsCOMPtr<nsIDOMDocument> domDoc;
6053 viewer->GetDOMDocument(getter_AddRefs(domDoc));
6054 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
6055 mSavingOldViewer = CanSavePresentation(mLoadType, request, doc);
6058 NS_ASSERTION(!mLoadingURI, "Re-entering unload?");
6060 nsCOMPtr<nsIChannel> aOpenedChannel = do_QueryInterface(request);
6061 if (aOpenedChannel) {
6062 aOpenedChannel->GetURI(getter_AddRefs(mLoadingURI));
6064 FirePageHideNotification(!mSavingOldViewer);
6065 mLoadingURI = nsnull;
6067 // Set mFiredUnloadEvent = PR_FALSE so that the unload handler for the
6068 // *new* document will fire.
6069 mFiredUnloadEvent = PR_FALSE;
6071 // we've created a new document so go ahead and call
6072 // OnLoadingSite(), but don't fire OnLocationChange()
6073 // notifications before we've called Embed(). See bug 284993.
6074 mURIResultedInDocument = PR_TRUE;
6076 PRBool onLocationChangeNeeded = OnLoadingSite(aOpenedChannel, PR_FALSE);
6078 // let's try resetting the load group if we need to...
6079 nsCOMPtr<nsILoadGroup> currentLoadGroup;
6080 NS_ENSURE_SUCCESS(aOpenedChannel->
6081 GetLoadGroup(getter_AddRefs(currentLoadGroup)),
6082 NS_ERROR_FAILURE);
6084 if (currentLoadGroup != mLoadGroup) {
6085 nsLoadFlags loadFlags = 0;
6087 //Cancel any URIs that are currently loading...
6088 /// XXX: Need to do this eventually Stop();
6090 // Retarget the document to this loadgroup...
6092 /* First attach the channel to the right loadgroup
6093 * and then remove from the old loadgroup. This
6094 * puts the notifications in the right order and
6095 * we don't null-out mLSHE in OnStateChange() for
6096 * all redirected urls
6098 aOpenedChannel->SetLoadGroup(mLoadGroup);
6100 // Mark the channel as being a document URI...
6101 aOpenedChannel->GetLoadFlags(&loadFlags);
6102 loadFlags |= nsIChannel::LOAD_DOCUMENT_URI;
6104 aOpenedChannel->SetLoadFlags(loadFlags);
6106 mLoadGroup->AddRequest(request, nsnull);
6107 if (currentLoadGroup)
6108 currentLoadGroup->RemoveRequest(request, nsnull,
6109 NS_BINDING_RETARGETED);
6111 // Update the notification callbacks, so that progress and
6112 // status information are sent to the right docshell...
6113 aOpenedChannel->SetNotificationCallbacks(this);
6116 NS_ENSURE_SUCCESS(Embed(viewer, "", (nsISupports *) nsnull),
6117 NS_ERROR_FAILURE);
6119 mSavedRefreshURIList = nsnull;
6120 mSavingOldViewer = PR_FALSE;
6121 mEODForCurrentDocument = PR_FALSE;
6123 // if this document is part of a multipart document,
6124 // the ID can be used to distinguish it from the other parts.
6125 nsCOMPtr<nsIMultiPartChannel> multiPartChannel(do_QueryInterface(request));
6126 if (multiPartChannel) {
6127 nsCOMPtr<nsIPresShell> shell;
6128 rv = GetPresShell(getter_AddRefs(shell));
6129 if (NS_SUCCEEDED(rv) && shell) {
6130 nsIDocument *doc = shell->GetDocument();
6131 if (doc) {
6132 PRUint32 partID;
6133 multiPartChannel->GetPartID(&partID);
6134 doc->SetPartID(partID);
6139 // Give hint to native plevent dispatch mechanism. If a document
6140 // is loading the native plevent dispatch mechanism should favor
6141 // performance over normal native event dispatch priorities.
6142 if (++gNumberOfDocumentsLoading == 1) {
6143 // Hint to favor performance for the plevent notification mechanism.
6144 // We want the pages to load as fast as possible even if its means
6145 // native messages might be starved.
6146 FavorPerformanceHint(PR_TRUE, NS_EVENT_STARVATION_DELAY_HINT);
6149 if (onLocationChangeNeeded) {
6150 FireOnLocationChange(this, request, mCurrentURI);
6153 return NS_OK;
6156 nsresult
6157 nsDocShell::NewContentViewerObj(const char *aContentType,
6158 nsIRequest * request, nsILoadGroup * aLoadGroup,
6159 nsIStreamListener ** aContentHandler,
6160 nsIContentViewer ** aViewer)
6162 nsCOMPtr<nsIChannel> aOpenedChannel = do_QueryInterface(request);
6164 nsresult rv;
6165 nsCOMPtr<nsICategoryManager> catMan(do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv));
6166 if (NS_FAILED(rv))
6167 return rv;
6169 nsXPIDLCString contractId;
6170 rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", aContentType, getter_Copies(contractId));
6172 // Create an instance of the document-loader-factory
6173 nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory;
6174 if (NS_SUCCEEDED(rv))
6175 docLoaderFactory = do_GetService(contractId.get());
6177 if (!docLoaderFactory) {
6178 return NS_ERROR_FAILURE;
6181 // Now create an instance of the content viewer
6182 // nsLayoutDLF makes the determination if it should be a "view-source" instead of "view"
6183 NS_ENSURE_SUCCESS(docLoaderFactory->CreateInstance("view",
6184 aOpenedChannel,
6185 aLoadGroup, aContentType,
6186 static_cast<nsIContentViewerContainer*>(this),
6187 nsnull,
6188 aContentHandler,
6189 aViewer),
6190 NS_ERROR_FAILURE);
6192 (*aViewer)->SetContainer(static_cast<nsIContentViewerContainer *>(this));
6193 return NS_OK;
6196 NS_IMETHODIMP
6197 nsDocShell::SetupNewViewer(nsIContentViewer * aNewViewer)
6200 // Copy content viewer state from previous or parent content viewer.
6202 // The following logic is mirrored in nsHTMLDocument::StartDocumentLoad!
6204 // Do NOT to maintain a reference to the old content viewer outside
6205 // of this "copying" block, or it will not be destroyed until the end of
6206 // this routine and all <SCRIPT>s and event handlers fail! (bug 20315)
6208 // In this block of code, if we get an error result, we return it
6209 // but if we get a null pointer, that's perfectly legal for parent
6210 // and parentContentViewer.
6213 PRInt32 x = 0;
6214 PRInt32 y = 0;
6215 PRInt32 cx = 0;
6216 PRInt32 cy = 0;
6218 // This will get the size from the current content viewer or from the
6219 // Init settings
6220 DoGetPositionAndSize(&x, &y, &cx, &cy);
6222 nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
6223 NS_ENSURE_SUCCESS(GetSameTypeParent(getter_AddRefs(parentAsItem)),
6224 NS_ERROR_FAILURE);
6225 nsCOMPtr<nsIDocShell> parent(do_QueryInterface(parentAsItem));
6227 nsCAutoString defaultCharset;
6228 nsCAutoString forceCharset;
6229 nsCAutoString hintCharset;
6230 PRInt32 hintCharsetSource;
6231 nsCAutoString prevDocCharset;
6232 float textZoom;
6233 float pageZoom;
6234 PRBool styleDisabled;
6235 // |newMUDV| also serves as a flag to set the data from the above vars
6236 nsCOMPtr<nsIMarkupDocumentViewer> newMUDV;
6238 if (mContentViewer || parent) {
6239 nsCOMPtr<nsIMarkupDocumentViewer> oldMUDV;
6240 if (mContentViewer) {
6241 // Get any interesting state from old content viewer
6242 // XXX: it would be far better to just reuse the document viewer ,
6243 // since we know we're just displaying the same document as before
6244 oldMUDV = do_QueryInterface(mContentViewer);
6246 // Tell the old content viewer to hibernate in session history when
6247 // it is destroyed.
6249 if (mSavingOldViewer && NS_FAILED(CaptureState())) {
6250 if (mOSHE) {
6251 mOSHE->SyncPresentationState();
6253 mSavingOldViewer = PR_FALSE;
6256 else {
6257 // No old content viewer, so get state from parent's content viewer
6258 nsCOMPtr<nsIContentViewer> parentContentViewer;
6259 parent->GetContentViewer(getter_AddRefs(parentContentViewer));
6260 oldMUDV = do_QueryInterface(parentContentViewer);
6263 if (oldMUDV) {
6264 nsresult rv;
6266 newMUDV = do_QueryInterface(aNewViewer,&rv);
6267 if (newMUDV) {
6268 NS_ENSURE_SUCCESS(oldMUDV->
6269 GetDefaultCharacterSet(defaultCharset),
6270 NS_ERROR_FAILURE);
6271 NS_ENSURE_SUCCESS(oldMUDV->
6272 GetForceCharacterSet(forceCharset),
6273 NS_ERROR_FAILURE);
6274 NS_ENSURE_SUCCESS(oldMUDV->
6275 GetHintCharacterSet(hintCharset),
6276 NS_ERROR_FAILURE);
6277 NS_ENSURE_SUCCESS(oldMUDV->
6278 GetHintCharacterSetSource(&hintCharsetSource),
6279 NS_ERROR_FAILURE);
6280 NS_ENSURE_SUCCESS(oldMUDV->
6281 GetTextZoom(&textZoom),
6282 NS_ERROR_FAILURE);
6283 NS_ENSURE_SUCCESS(oldMUDV->
6284 GetFullZoom(&pageZoom),
6285 NS_ERROR_FAILURE);
6286 NS_ENSURE_SUCCESS(oldMUDV->
6287 GetAuthorStyleDisabled(&styleDisabled),
6288 NS_ERROR_FAILURE);
6289 NS_ENSURE_SUCCESS(oldMUDV->
6290 GetPrevDocCharacterSet(prevDocCharset),
6291 NS_ERROR_FAILURE);
6296 // It is necessary to obtain the focus controller to utilize its ability
6297 // to suppress focus. This is necessary to fix Win32-only bugs related to
6298 // a loss of focus when mContentViewer is set to null. The internal window
6299 // is destroyed, and the OS focuses the parent window. This call ends up
6300 // notifying the focus controller that the outer window should focus
6301 // and this hoses us on any link traversal.
6303 // Please do not touch any of the focus controller code here without
6304 // testing bugs #28580 and 50509. These are immensely important bugs,
6305 // so PLEASE take care not to regress them if you decide to alter this
6306 // code later -- hyatt
6307 nsIFocusController *focusController = nsnull;
6308 if (mScriptGlobal) {
6309 nsCOMPtr<nsPIDOMWindow> ourWindow = do_QueryInterface(mScriptGlobal);
6310 focusController = ourWindow->GetRootFocusController();
6311 if (focusController) {
6312 // Suppress the command dispatcher.
6313 focusController->SetSuppressFocus(PR_TRUE,
6314 "Win32-Only Link Traversal Issue");
6315 // Remove focus from the element that has it
6316 nsCOMPtr<nsIDOMWindowInternal> focusedWindow;
6317 focusController->GetFocusedWindow(getter_AddRefs(focusedWindow));
6319 // We want to null out the last focused element if the document containing
6320 // it is going away. If the last focused element is in a descendent
6321 // window of our domwindow, its document will be destroyed when we
6322 // destroy our children. So, check for this case and null out the
6323 // last focused element. See bug 70484.
6325 PRBool isSubWindow = PR_FALSE;
6326 nsCOMPtr<nsIDOMWindow> curwin;
6327 if (focusedWindow)
6328 focusedWindow->GetParent(getter_AddRefs(curwin));
6329 while (curwin) {
6330 if (curwin == ourWindow) {
6331 isSubWindow = PR_TRUE;
6332 break;
6335 // don't use nsCOMPtr here to avoid extra addref
6336 // when assigning to curwin
6337 nsIDOMWindow* temp;
6338 curwin->GetParent(&temp);
6339 if (curwin == temp) {
6340 NS_RELEASE(temp);
6341 break;
6343 curwin = dont_AddRef(temp);
6346 if (ourWindow == focusedWindow || isSubWindow)
6347 focusController->ResetElementFocus();
6351 nscolor bgcolor = NS_RGBA(0, 0, 0, 0);
6352 PRBool bgSet = PR_FALSE;
6354 // Ensure that the content viewer is destroyed *after* the GC - bug 71515
6355 nsCOMPtr<nsIContentViewer> kungfuDeathGrip = mContentViewer;
6356 if (mContentViewer) {
6357 // Stop any activity that may be happening in the old document before
6358 // releasing it...
6359 mContentViewer->Stop();
6361 // Try to extract the default background color from the old
6362 // view manager, so we can use it for the next document.
6363 nsCOMPtr<nsIDocumentViewer> docviewer =
6364 do_QueryInterface(mContentViewer);
6366 if (docviewer) {
6367 nsCOMPtr<nsIPresShell> shell;
6368 docviewer->GetPresShell(getter_AddRefs(shell));
6370 if (shell) {
6371 nsIViewManager* vm = shell->GetViewManager();
6373 if (vm) {
6374 vm->GetDefaultBackgroundColor(&bgcolor);
6375 // If the background color is not known, don't propagate it.
6376 bgSet = NS_GET_A(bgcolor) != 0;
6381 mContentViewer->Close(mSavingOldViewer ? mOSHE.get() : nsnull);
6382 aNewViewer->SetPreviousViewer(mContentViewer);
6384 mContentViewer = nsnull;
6387 mContentViewer = aNewViewer;
6389 nsCOMPtr<nsIWidget> widget;
6390 NS_ENSURE_SUCCESS(GetMainWidget(getter_AddRefs(widget)), NS_ERROR_FAILURE);
6392 nsCOMPtr<nsIDeviceContext> deviceContext;
6393 if (widget) {
6394 deviceContext = do_CreateInstance(kDeviceContextCID);
6395 NS_ENSURE_TRUE(deviceContext, NS_ERROR_FAILURE);
6396 deviceContext->Init(widget->GetNativeData(NS_NATIVE_WIDGET));
6399 nsRect bounds(x, y, cx, cy);
6401 if (NS_FAILED(mContentViewer->Init(widget, deviceContext, bounds))) {
6402 mContentViewer = nsnull;
6403 NS_ERROR("ContentViewer Initialization failed");
6404 return NS_ERROR_FAILURE;
6407 // If we have old state to copy, set the old state onto the new content
6408 // viewer
6409 if (newMUDV) {
6410 NS_ENSURE_SUCCESS(newMUDV->SetDefaultCharacterSet(defaultCharset),
6411 NS_ERROR_FAILURE);
6412 NS_ENSURE_SUCCESS(newMUDV->SetForceCharacterSet(forceCharset),
6413 NS_ERROR_FAILURE);
6414 NS_ENSURE_SUCCESS(newMUDV->SetHintCharacterSet(hintCharset),
6415 NS_ERROR_FAILURE);
6416 NS_ENSURE_SUCCESS(newMUDV->
6417 SetHintCharacterSetSource(hintCharsetSource),
6418 NS_ERROR_FAILURE);
6419 NS_ENSURE_SUCCESS(newMUDV->SetPrevDocCharacterSet(prevDocCharset),
6420 NS_ERROR_FAILURE);
6421 NS_ENSURE_SUCCESS(newMUDV->SetTextZoom(textZoom),
6422 NS_ERROR_FAILURE);
6423 NS_ENSURE_SUCCESS(newMUDV->SetFullZoom(pageZoom),
6424 NS_ERROR_FAILURE);
6425 NS_ENSURE_SUCCESS(newMUDV->SetAuthorStyleDisabled(styleDisabled),
6426 NS_ERROR_FAILURE);
6429 // End copying block (Don't mess with the old content/document viewer
6430 // beyond here!!)
6432 // See the book I wrote above regarding why the focus controller is
6433 // being used here. -- hyatt
6435 /* Note it's important that focus suppression be turned off no earlier
6436 because in cases where the docshell is lazily creating an about:blank
6437 document, mContentViewer->Init finally puts a reference to that
6438 document into the DOM window, which prevents an infinite recursion
6439 attempting to lazily create the document as focus is unsuppressed
6440 (bug 110856). */
6441 if (focusController)
6442 focusController->SetSuppressFocus(PR_FALSE,
6443 "Win32-Only Link Traversal Issue");
6445 if (bgSet && widget) {
6446 // Stuff the bgcolor from the last view manager into the new
6447 // view manager. This improves page load continuity.
6448 nsCOMPtr<nsIDocumentViewer> docviewer =
6449 do_QueryInterface(mContentViewer);
6451 if (docviewer) {
6452 nsCOMPtr<nsIPresShell> shell;
6453 docviewer->GetPresShell(getter_AddRefs(shell));
6455 if (shell) {
6456 nsIViewManager* vm = shell->GetViewManager();
6458 if (vm) {
6459 vm->SetDefaultBackgroundColor(bgcolor);
6465 // XXX: It looks like the LayoutState gets restored again in Embed()
6466 // right after the call to SetupNewViewer(...)
6468 // We don't show the mContentViewer yet, since we want to draw the old page
6469 // until we have enough of the new page to show. Just return with the new
6470 // viewer still set to hidden.
6472 // Now that we have switched documents, forget all of our children
6473 DestroyChildren();
6475 return NS_OK;
6479 nsresult
6480 nsDocShell::CheckLoadingPermissions()
6482 // This method checks whether the caller may load content into
6483 // this docshell. Even though we've done our best to hide windows
6484 // from code that doesn't have the right to access them, it's
6485 // still possible for an evil site to open a window and access
6486 // frames in the new window through window.frames[] (which is
6487 // allAccess for historic reasons), so we still need to do this
6488 // check on load.
6489 nsresult rv = NS_OK, sameOrigin = NS_OK;
6491 if (!gValidateOrigin || !IsFrame()) {
6492 // Origin validation was turned off, or we're not a frame.
6493 // Permit all loads.
6495 return rv;
6498 // We're a frame. Check that the caller has write permission to
6499 // the parent before allowing it to load anything into this
6500 // docshell.
6502 nsCOMPtr<nsIScriptSecurityManager> securityManager =
6503 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
6504 NS_ENSURE_SUCCESS(rv, rv);
6506 PRBool ubwEnabled = PR_FALSE;
6507 rv = securityManager->IsCapabilityEnabled("UniversalBrowserWrite",
6508 &ubwEnabled);
6509 if (NS_FAILED(rv) || ubwEnabled) {
6510 return rv;
6513 nsCOMPtr<nsIPrincipal> subjPrincipal;
6514 rv = securityManager->GetSubjectPrincipal(getter_AddRefs(subjPrincipal));
6515 NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && subjPrincipal, rv);
6517 // Check if the caller is from the same origin as this docshell,
6518 // or any of it's ancestors.
6519 nsCOMPtr<nsIDocShellTreeItem> item(this);
6520 do {
6521 nsCOMPtr<nsIScriptGlobalObject> sgo(do_GetInterface(item));
6522 nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(sgo));
6524 nsIPrincipal *p;
6525 if (!sop || !(p = sop->GetPrincipal())) {
6526 return NS_ERROR_UNEXPECTED;
6529 // Compare origins
6530 PRBool equal;
6531 sameOrigin = subjPrincipal->Equals(p, &equal);
6532 if (NS_SUCCEEDED(sameOrigin)) {
6533 if (equal) {
6534 // Same origin, permit load
6536 return sameOrigin;
6539 sameOrigin = NS_ERROR_DOM_PROP_ACCESS_DENIED;
6542 nsCOMPtr<nsIDocShellTreeItem> tmp;
6543 item->GetSameTypeParent(getter_AddRefs(tmp));
6544 item.swap(tmp);
6545 } while (item);
6547 return sameOrigin;
6550 //*****************************************************************************
6551 // nsDocShell: Site Loading
6552 //*****************************************************************************
6553 class InternalLoadEvent : public nsRunnable
6555 public:
6556 InternalLoadEvent(nsDocShell* aDocShell, nsIURI * aURI, nsIURI * aReferrer,
6557 nsISupports * aOwner, PRUint32 aFlags,
6558 const char* aTypeHint, nsIInputStream * aPostData,
6559 nsIInputStream * aHeadersData, PRUint32 aLoadType,
6560 nsISHEntry * aSHEntry, PRBool aFirstParty) :
6561 mDocShell(aDocShell),
6562 mURI(aURI),
6563 mReferrer(aReferrer),
6564 mOwner(aOwner),
6565 mFlags(aFlags),
6566 mPostData(aPostData),
6567 mHeadersData(aHeadersData),
6568 mLoadType(aLoadType),
6569 mSHEntry(aSHEntry),
6570 mFirstParty(aFirstParty)
6572 // Make sure to keep null things null as needed
6573 if (aTypeHint) {
6574 mTypeHint = aTypeHint;
6578 NS_IMETHOD Run() {
6579 return mDocShell->InternalLoad(mURI, mReferrer, mOwner, mFlags,
6580 nsnull, mTypeHint.get(),
6581 mPostData, mHeadersData, mLoadType,
6582 mSHEntry, mFirstParty, nsnull, nsnull);
6585 private:
6586 nsRefPtr<nsDocShell> mDocShell;
6587 nsCOMPtr<nsIURI> mURI;
6588 nsCOMPtr<nsIURI> mReferrer;
6589 nsCOMPtr<nsISupports> mOwner;
6590 PRUint32 mFlags;
6592 // Use IDL strings so .get() returns null by default
6593 nsXPIDLString mWindowTarget;
6594 nsXPIDLCString mTypeHint;
6596 nsCOMPtr<nsIInputStream> mPostData;
6597 nsCOMPtr<nsIInputStream> mHeadersData;
6598 PRUint32 mLoadType;
6599 nsCOMPtr<nsISHEntry> mSHEntry;
6600 PRBool mFirstParty;
6603 NS_IMETHODIMP
6604 nsDocShell::InternalLoad(nsIURI * aURI,
6605 nsIURI * aReferrer,
6606 nsISupports * aOwner,
6607 PRUint32 aFlags,
6608 const PRUnichar *aWindowTarget,
6609 const char* aTypeHint,
6610 nsIInputStream * aPostData,
6611 nsIInputStream * aHeadersData,
6612 PRUint32 aLoadType,
6613 nsISHEntry * aSHEntry,
6614 PRBool aFirstParty,
6615 nsIDocShell** aDocShell,
6616 nsIRequest** aRequest)
6618 nsresult rv = NS_OK;
6620 #ifdef PR_LOGGING
6621 if (gDocShellLeakLog && PR_LOG_TEST(gDocShellLeakLog, PR_LOG_DEBUG)) {
6622 nsCAutoString spec;
6623 if (aURI)
6624 aURI->GetSpec(spec);
6625 PR_LogPrint("DOCSHELL %p InternalLoad %s\n", this, spec.get());
6627 #endif
6629 // Initialize aDocShell/aRequest
6630 if (aDocShell) {
6631 *aDocShell = nsnull;
6633 if (aRequest) {
6634 *aRequest = nsnull;
6637 if (!aURI) {
6638 return NS_ERROR_NULL_POINTER;
6641 NS_ENSURE_TRUE(IsValidLoadType(aLoadType), NS_ERROR_INVALID_ARG);
6643 NS_ENSURE_TRUE(!mIsBeingDestroyed, NS_ERROR_NOT_AVAILABLE);
6645 // wyciwyg urls can only be loaded through history. Any normal load of
6646 // wyciwyg through docshell is illegal. Disallow such loads.
6647 if (aLoadType & LOAD_CMD_NORMAL) {
6648 PRBool isWyciwyg = PR_FALSE;
6649 rv = aURI->SchemeIs("wyciwyg", &isWyciwyg);
6650 if ((isWyciwyg && NS_SUCCEEDED(rv)) || NS_FAILED(rv))
6651 return NS_ERROR_FAILURE;
6654 PRBool bIsJavascript = PR_FALSE;
6655 if (NS_FAILED(aURI->SchemeIs("javascript", &bIsJavascript))) {
6656 bIsJavascript = PR_FALSE;
6660 // First, notify any nsIContentPolicy listeners about the document load.
6661 // Only abort the load if a content policy listener explicitly vetos it!
6663 nsCOMPtr<nsIDOMElement> requestingElement;
6664 // Use nsPIDOMWindow since we _want_ to cross the chrome boundary if needed
6665 nsCOMPtr<nsPIDOMWindow> privateWin(do_QueryInterface(mScriptGlobal));
6666 if (privateWin)
6667 requestingElement = privateWin->GetFrameElementInternal();
6669 PRInt16 shouldLoad = nsIContentPolicy::ACCEPT;
6670 PRUint32 contentType;
6671 if (IsFrame()) {
6672 NS_ASSERTION(requestingElement, "A frame but no DOM element!?");
6673 contentType = nsIContentPolicy::TYPE_SUBDOCUMENT;
6674 } else {
6675 contentType = nsIContentPolicy::TYPE_DOCUMENT;
6678 nsISupports* context = requestingElement;
6679 if (!context) {
6680 context = mScriptGlobal;
6683 // XXXbz would be nice to know the loading principal here... but we don't
6684 nsCOMPtr<nsIPrincipal> loadingPrincipal;
6685 if (aReferrer) {
6686 nsCOMPtr<nsIScriptSecurityManager> secMan =
6687 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
6688 NS_ENSURE_SUCCESS(rv, rv);
6690 rv = secMan->GetCodebasePrincipal(aReferrer,
6691 getter_AddRefs(loadingPrincipal));
6694 rv = NS_CheckContentLoadPolicy(contentType,
6695 aURI,
6696 loadingPrincipal,
6697 context,
6698 EmptyCString(), //mime guess
6699 nsnull, //extra
6700 &shouldLoad);
6702 if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
6703 if (NS_SUCCEEDED(rv) && shouldLoad == nsIContentPolicy::REJECT_TYPE) {
6704 return NS_ERROR_CONTENT_BLOCKED_SHOW_ALT;
6707 return NS_ERROR_CONTENT_BLOCKED;
6710 nsCOMPtr<nsISupports> owner(aOwner);
6712 // Get an owner from the current document if necessary. Note that we only
6713 // do this for URIs that inherit a security context and local file URIs;
6714 // in particular we do NOT do this for about:blank. This way, random
6715 // about:blank loads that have no owner (which basically means they were
6716 // done by someone from chrome manually messing with our nsIWebNavigation
6717 // or by C++ setting document.location) don't get a funky principal. If
6718 // callers want something interesting to happen with the about:blank
6719 // principal in this case, they should pass an owner in.
6722 PRBool inherits;
6723 // One more twist: Don't inherit the owner for external loads.
6724 if (aLoadType != LOAD_NORMAL_EXTERNAL && !owner &&
6725 (aFlags & INTERNAL_LOAD_FLAGS_INHERIT_OWNER) &&
6726 ((NS_SUCCEEDED(URIInheritsSecurityContext(aURI, &inherits)) &&
6727 inherits) || URIIsLocalFile(aURI))) {
6729 // Don't allow loads that would inherit our security context
6730 // if this document came from an unsafe channel.
6731 nsCOMPtr<nsIDocShellTreeItem> treeItem = this;
6732 do {
6733 nsCOMPtr<nsIDocShell> itemDocShell =
6734 do_QueryInterface(treeItem);
6735 PRBool isUnsafe;
6736 if (itemDocShell &&
6737 NS_SUCCEEDED(itemDocShell->GetChannelIsUnsafe(&isUnsafe)) &&
6738 isUnsafe) {
6739 return NS_ERROR_DOM_SECURITY_ERR;
6742 nsCOMPtr<nsIDocShellTreeItem> parent;
6743 treeItem->GetSameTypeParent(getter_AddRefs(parent));
6744 parent.swap(treeItem);
6745 } while (treeItem);
6747 owner = GetInheritedPrincipal(PR_TRUE);
6752 // Resolve the window target before going any further...
6753 // If the load has been targeted to another DocShell, then transfer the
6754 // load to it...
6756 if (aWindowTarget && *aWindowTarget) {
6757 // We've already done our owner-inheriting. Mask out that bit, so we
6758 // don't try inheriting an owner from the target window if we came up
6759 // with a null owner above.
6760 aFlags = aFlags & ~INTERNAL_LOAD_FLAGS_INHERIT_OWNER;
6762 // Locate the target DocShell.
6763 // This may involve creating a new toplevel window - if necessary.
6765 nsCOMPtr<nsIDocShellTreeItem> targetItem;
6766 FindItemWithName(aWindowTarget, nsnull, this,
6767 getter_AddRefs(targetItem));
6769 nsCOMPtr<nsIDocShell> targetDocShell = do_QueryInterface(targetItem);
6771 PRBool isNewWindow = PR_FALSE;
6772 if (!targetDocShell) {
6773 nsCOMPtr<nsIDOMWindowInternal> win =
6774 do_GetInterface(GetAsSupports(this));
6775 NS_ENSURE_TRUE(win, NS_ERROR_NOT_AVAILABLE);
6777 nsDependentString name(aWindowTarget);
6778 nsCOMPtr<nsIDOMWindow> newWin;
6779 rv = win->Open(EmptyString(), // URL to load
6780 name, // window name
6781 EmptyString(), // Features
6782 getter_AddRefs(newWin));
6784 // In some cases the Open call doesn't actually result in a new
6785 // window being opened. We can detect these cases by examining the
6786 // document in |newWin|, if any.
6787 nsCOMPtr<nsPIDOMWindow> piNewWin = do_QueryInterface(newWin);
6788 if (piNewWin) {
6789 nsCOMPtr<nsIDocument> newDoc =
6790 do_QueryInterface(piNewWin->GetExtantDocument());
6791 if (!newDoc || newDoc->IsInitialDocument()) {
6792 isNewWindow = PR_TRUE;
6793 aFlags |= INTERNAL_LOAD_FLAGS_FIRST_LOAD;
6797 nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(newWin);
6798 targetDocShell = do_QueryInterface(webNav);
6800 nsCOMPtr<nsIScriptObjectPrincipal> sop =
6801 do_QueryInterface(mScriptGlobal);
6802 nsCOMPtr<nsIURI> currentCodebase;
6804 if (sop) {
6805 nsIPrincipal *principal = sop->GetPrincipal();
6807 if (principal) {
6808 principal->GetURI(getter_AddRefs(currentCodebase));
6812 // We opened a new window for the target, clone the
6813 // session storage if the current URI's domain matches
6814 // that of the loading URI.
6815 if (targetDocShell && currentCodebase && aURI) {
6816 nsCAutoString thisDomain, newDomain;
6817 nsresult gethostrv = currentCodebase->GetAsciiHost(thisDomain);
6818 gethostrv |= aURI->GetAsciiHost(newDomain);
6819 if (NS_SUCCEEDED(gethostrv) && thisDomain.Equals(newDomain)) {
6820 nsCOMPtr<nsIDOMStorage> storage;
6821 GetSessionStorageForURI(currentCodebase,
6822 getter_AddRefs(storage));
6823 nsCOMPtr<nsPIDOMStorage> piStorage =
6824 do_QueryInterface(storage);
6825 if (piStorage) {
6826 nsCOMPtr<nsIDOMStorage> newstorage =
6827 piStorage->Clone(currentCodebase);
6828 targetDocShell->AddSessionStorage(thisDomain,
6829 newstorage);
6836 // Transfer the load to the target DocShell... Pass nsnull as the
6837 // window target name from to prevent recursive retargeting!
6839 if (NS_SUCCEEDED(rv) && targetDocShell) {
6840 rv = targetDocShell->InternalLoad(aURI,
6841 aReferrer,
6842 owner,
6843 aFlags,
6844 nsnull, // No window target
6845 aTypeHint,
6846 aPostData,
6847 aHeadersData,
6848 aLoadType,
6849 aSHEntry,
6850 aFirstParty,
6851 aDocShell,
6852 aRequest);
6853 if (rv == NS_ERROR_NO_CONTENT) {
6854 // XXXbz except we never reach this code!
6855 if (isNewWindow) {
6857 // At this point, a new window has been created, but the
6858 // URI did not have any data associated with it...
6860 // So, the best we can do, is to tear down the new window
6861 // that was just created!
6863 nsCOMPtr<nsIDOMWindowInternal> domWin =
6864 do_GetInterface(targetDocShell);
6865 if (domWin) {
6866 domWin->Close();
6870 // NS_ERROR_NO_CONTENT should not be returned to the
6871 // caller... This is an internal error code indicating that
6872 // the URI had no data associated with it - probably a
6873 // helper-app style protocol (ie. mailto://)
6875 rv = NS_OK;
6877 else if (isNewWindow) {
6878 // XXX: Once new windows are created hidden, the new
6879 // window will need to be made visible... For now,
6880 // do nothing.
6884 // Else we ran out of memory, or were a popup and got blocked,
6885 // or something.
6887 return rv;
6891 // Load is being targetted at this docshell so return an error if the
6892 // docshell is in the process of being destroyed.
6894 if (mIsBeingDestroyed) {
6895 return NS_ERROR_FAILURE;
6898 rv = CheckLoadingPermissions();
6899 if (NS_FAILED(rv)) {
6900 return rv;
6903 // If this docshell is owned by a frameloader, make sure to cancel
6904 // possible frameloader initialization before loading a new page.
6905 nsCOMPtr<nsIDocShellTreeItem> parent;
6906 GetParent(getter_AddRefs(parent));
6907 if (parent) {
6908 nsCOMPtr<nsIDOMDocument> domDoc = do_GetInterface(parent);
6909 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
6910 if (doc) {
6911 doc->TryCancelFrameLoaderInitialization(this);
6915 if (mFiredUnloadEvent) {
6916 if (IsOKToLoadURI(aURI)) {
6917 NS_PRECONDITION(!aWindowTarget || !*aWindowTarget,
6918 "Shouldn't have a window target here!");
6920 // If this is a replace load, make whatever load triggered
6921 // the unload event also a replace load, so we don't
6922 // create extra history entries.
6923 if (LOAD_TYPE_HAS_FLAGS(aLoadType, LOAD_FLAGS_REPLACE_HISTORY)) {
6924 mLoadType = LOAD_NORMAL_REPLACE;
6927 // Do this asynchronously
6928 nsCOMPtr<nsIRunnable> ev =
6929 new InternalLoadEvent(this, aURI, aReferrer, aOwner, aFlags,
6930 aTypeHint, aPostData, aHeadersData,
6931 aLoadType, aSHEntry, aFirstParty);
6932 return NS_DispatchToCurrentThread(ev);
6935 // Just ignore this load attempt
6936 return NS_OK;
6939 // Before going any further vet loads initiated by external programs.
6940 if (aLoadType == LOAD_NORMAL_EXTERNAL) {
6941 // Disallow external chrome: loads targetted at content windows
6942 PRBool isChrome = PR_FALSE;
6943 if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &isChrome)) && isChrome) {
6944 NS_WARNING("blocked external chrome: url -- use '-chrome' option");
6945 return NS_ERROR_FAILURE;
6948 // clear the decks to prevent context bleed-through (bug 298255)
6949 rv = CreateAboutBlankContentViewer(nsnull);
6950 if (NS_FAILED(rv))
6951 return NS_ERROR_FAILURE;
6953 // reset loadType so we don't have to add lots of tests for
6954 // LOAD_NORMAL_EXTERNAL after this point
6955 aLoadType = LOAD_NORMAL;
6958 mAllowKeywordFixup =
6959 (aFlags & INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) != 0;
6960 mURIResultedInDocument = PR_FALSE; // reset the clock...
6963 // First:
6964 // Check to see if the new URI is an anchor in the existing document.
6965 // Skip this check if we're doing some sort of abnormal load, if the
6966 // new load is a non-history load and has postdata, or if we're doing
6967 // a history load and the page identifiers of mOSHE and aSHEntry
6968 // don't match.
6970 PRBool allowScroll = PR_TRUE;
6971 if (!aSHEntry) {
6972 allowScroll = (aPostData == nsnull);
6973 } else if (mOSHE) {
6974 PRUint32 ourPageIdent;
6975 mOSHE->GetPageIdentifier(&ourPageIdent);
6976 PRUint32 otherPageIdent;
6977 aSHEntry->GetPageIdentifier(&otherPageIdent);
6978 allowScroll = (ourPageIdent == otherPageIdent);
6979 #ifdef DEBUG
6980 if (allowScroll) {
6981 nsCOMPtr<nsIInputStream> currentPostData;
6982 mOSHE->GetPostData(getter_AddRefs(currentPostData));
6983 NS_ASSERTION(currentPostData == aPostData,
6984 "Different POST data for entries for the same page?");
6986 #endif
6989 if ((aLoadType == LOAD_NORMAL ||
6990 aLoadType == LOAD_STOP_CONTENT ||
6991 LOAD_TYPE_HAS_FLAGS(aLoadType, LOAD_FLAGS_REPLACE_HISTORY) ||
6992 aLoadType == LOAD_HISTORY ||
6993 aLoadType == LOAD_LINK) && allowScroll) {
6994 PRBool wasAnchor = PR_FALSE;
6995 nscoord cx, cy;
6996 NS_ENSURE_SUCCESS(ScrollIfAnchor(aURI, &wasAnchor, aLoadType, &cx, &cy), NS_ERROR_FAILURE);
6997 if (wasAnchor) {
6998 mLoadType = aLoadType;
6999 mURIResultedInDocument = PR_TRUE;
7001 /* we need to assign mLSHE to aSHEntry right here, so that on History loads,
7002 * SetCurrentURI() called from OnNewURI() will send proper
7003 * onLocationChange() notifications to the browser to update
7004 * back/forward buttons.
7006 SetHistoryEntry(&mLSHE, aSHEntry);
7008 /* This is a anchor traversal with in the same page.
7009 * call OnNewURI() so that, this traversal will be
7010 * recorded in session and global history.
7012 OnNewURI(aURI, nsnull, mLoadType, PR_TRUE);
7013 nsCOMPtr<nsIInputStream> postData;
7014 PRUint32 pageIdent = PR_UINT32_MAX;
7015 nsCOMPtr<nsISupports> cacheKey;
7017 if (mOSHE) {
7018 /* save current position of scroller(s) (bug 59774) */
7019 mOSHE->SetScrollPosition(cx, cy);
7020 // Get the postdata and page ident from the current page, if
7021 // the new load is being done via normal means. Note that
7022 // "normal means" can be checked for just by checking for
7023 // LOAD_CMD_NORMAL, given the loadType and allowScroll check
7024 // above -- it filters out some LOAD_CMD_NORMAL cases that we
7025 // wouldn't want here.
7026 if (aLoadType & LOAD_CMD_NORMAL) {
7027 mOSHE->GetPostData(getter_AddRefs(postData));
7028 mOSHE->GetPageIdentifier(&pageIdent);
7029 mOSHE->GetCacheKey(getter_AddRefs(cacheKey));
7033 /* Assign mOSHE to mLSHE. This will either be a new entry created
7034 * by OnNewURI() for normal loads or aSHEntry for history loads.
7036 if (mLSHE) {
7037 SetHistoryEntry(&mOSHE, mLSHE);
7038 // Save the postData obtained from the previous page
7039 // in to the session history entry created for the
7040 // anchor page, so that any history load of the anchor
7041 // page will restore the appropriate postData.
7042 if (postData)
7043 mOSHE->SetPostData(postData);
7045 // Make sure we won't just repost without hitting the
7046 // cache first
7047 if (cacheKey)
7048 mOSHE->SetCacheKey(cacheKey);
7050 // Propagate our page ident to the new mOSHE so that
7051 // we'll know it just differed by a scroll on the page.
7052 if (pageIdent != PR_UINT32_MAX)
7053 mOSHE->SetPageIdentifier(pageIdent);
7056 /* restore previous position of scroller(s), if we're moving
7057 * back in history (bug 59774)
7059 if (mOSHE && (aLoadType == LOAD_HISTORY || aLoadType == LOAD_RELOAD_NORMAL))
7061 nscoord bx, by;
7062 mOSHE->GetScrollPosition(&bx, &by);
7063 SetCurScrollPosEx(bx, by);
7066 /* Clear out mLSHE so that further anchor visits get
7067 * recorded in SH and SH won't misbehave.
7069 SetHistoryEntry(&mLSHE, nsnull);
7070 /* Set the title for the SH entry for this target url. so that
7071 * SH menus in go/back/forward buttons won't be empty for this.
7073 if (mSessionHistory) {
7074 PRInt32 index = -1;
7075 mSessionHistory->GetIndex(&index);
7076 nsCOMPtr<nsIHistoryEntry> hEntry;
7077 mSessionHistory->GetEntryAtIndex(index, PR_FALSE,
7078 getter_AddRefs(hEntry));
7079 NS_ENSURE_TRUE(hEntry, NS_ERROR_FAILURE);
7080 nsCOMPtr<nsISHEntry> shEntry(do_QueryInterface(hEntry));
7081 if (shEntry)
7082 shEntry->SetTitle(mTitle);
7085 return NS_OK;
7089 // mContentViewer->PermitUnload can destroy |this| docShell, which
7090 // causes the next call of CanSavePresentation to crash.
7091 // Hold onto |this| until we return, to prevent a crash from happening.
7092 // (bug#331040)
7093 nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
7095 // Check if the page doesn't want to be unloaded. The javascript:
7096 // protocol handler deals with this for javascript: URLs.
7097 if (!bIsJavascript && mContentViewer) {
7098 PRBool okToUnload;
7099 rv = mContentViewer->PermitUnload(&okToUnload);
7101 if (NS_SUCCEEDED(rv) && !okToUnload) {
7102 // The user chose not to unload the page, interrupt the
7103 // load.
7104 return NS_OK;
7108 // Check for saving the presentation here, before calling Stop().
7109 // This is necessary so that we can catch any pending requests.
7110 // Since the new request has not been created yet, we pass null for the
7111 // new request parameter.
7112 // Also pass nsnull for the document, since it doesn't affect the return
7113 // value for our purposes here.
7114 PRBool savePresentation = CanSavePresentation(aLoadType, nsnull, nsnull);
7116 // Don't stop current network activity for javascript: URL's since
7117 // they might not result in any data, and thus nothing should be
7118 // stopped in those cases. In the case where they do result in
7119 // data, the javascript: URL channel takes care of stopping
7120 // current network activity.
7121 if (!bIsJavascript) {
7122 // Stop any current network activity.
7123 // Also stop content if this is a zombie doc. otherwise
7124 // the onload will be delayed by other loads initiated in the
7125 // background by the first document that
7126 // didn't fully load before the next load was initiated.
7127 // If not a zombie, don't stop content until data
7128 // starts arriving from the new URI...
7130 nsCOMPtr<nsIContentViewer> zombieViewer;
7131 if (mContentViewer) {
7132 mContentViewer->GetPreviousViewer(getter_AddRefs(zombieViewer));
7135 if (zombieViewer ||
7136 LOAD_TYPE_HAS_FLAGS(aLoadType, LOAD_FLAGS_STOP_CONTENT)) {
7137 rv = Stop(nsIWebNavigation::STOP_ALL);
7138 } else {
7139 rv = Stop(nsIWebNavigation::STOP_NETWORK);
7142 if (NS_FAILED(rv))
7143 return rv;
7146 mLoadType = aLoadType;
7148 // mLSHE should be assigned to aSHEntry, only after Stop() has
7149 // been called. But when loading an error page, do not clear the
7150 // mLSHE for the real page.
7151 if (mLoadType != LOAD_ERROR_PAGE)
7152 SetHistoryEntry(&mLSHE, aSHEntry);
7154 mSavingOldViewer = savePresentation;
7156 // If we have a saved content viewer in history, restore and show it now.
7157 if (aSHEntry && (mLoadType & LOAD_CMD_HISTORY)) {
7158 // It's possible that the previous viewer of mContentViewer is the
7159 // viewer that will end up in aSHEntry when it gets closed. If that's
7160 // the case, we need to go ahead and force it into its shentry so we
7161 // can restore it.
7162 if (mContentViewer) {
7163 nsCOMPtr<nsIContentViewer> prevViewer;
7164 mContentViewer->GetPreviousViewer(getter_AddRefs(prevViewer));
7165 if (prevViewer) {
7166 #ifdef DEBUG
7167 nsCOMPtr<nsIContentViewer> prevPrevViewer;
7168 prevViewer->GetPreviousViewer(getter_AddRefs(prevPrevViewer));
7169 NS_ASSERTION(!prevPrevViewer, "Should never have viewer chain here");
7170 #endif
7171 nsCOMPtr<nsISHEntry> viewerEntry;
7172 prevViewer->GetHistoryEntry(getter_AddRefs(viewerEntry));
7173 if (viewerEntry == aSHEntry) {
7174 // Make sure this viewer ends up in the right place
7175 mContentViewer->SetPreviousViewer(nsnull);
7176 prevViewer->Destroy();
7180 nsCOMPtr<nsISHEntry> oldEntry = mOSHE;
7181 PRBool restoring;
7182 rv = RestorePresentation(aSHEntry, &restoring);
7183 if (restoring)
7184 return rv;
7186 // We failed to restore the presentation, so clean up.
7187 // Both the old and new history entries could potentially be in
7188 // an inconsistent state.
7189 if (NS_FAILED(rv)) {
7190 if (oldEntry)
7191 oldEntry->SyncPresentationState();
7193 aSHEntry->SyncPresentationState();
7197 nsCOMPtr<nsIRequest> req;
7198 rv = DoURILoad(aURI, aReferrer,
7199 !(aFlags & INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER),
7200 owner, aTypeHint, aPostData, aHeadersData, aFirstParty,
7201 aDocShell, getter_AddRefs(req),
7202 (aFlags & INTERNAL_LOAD_FLAGS_FIRST_LOAD) != 0,
7203 (aFlags & INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER) != 0);
7204 if (req && aRequest)
7205 NS_ADDREF(*aRequest = req);
7207 if (NS_FAILED(rv)) {
7208 nsCOMPtr<nsIChannel> chan(do_QueryInterface(req));
7209 DisplayLoadError(rv, aURI, nsnull, chan);
7212 return rv;
7215 nsIPrincipal*
7216 nsDocShell::GetInheritedPrincipal(PRBool aConsiderCurrentDocument)
7218 nsCOMPtr<nsIDocument> document;
7220 if (aConsiderCurrentDocument && mContentViewer) {
7221 nsCOMPtr<nsIDocumentViewer>
7222 docViewer(do_QueryInterface(mContentViewer));
7223 if (!docViewer)
7224 return nsnull;
7225 docViewer->GetDocument(getter_AddRefs(document));
7228 if (!document) {
7229 nsCOMPtr<nsIDocShellTreeItem> parentItem;
7230 GetSameTypeParent(getter_AddRefs(parentItem));
7231 if (parentItem) {
7232 nsCOMPtr<nsIDOMDocument> parentDomDoc(do_GetInterface(parentItem));
7233 document = do_QueryInterface(parentDomDoc);
7237 if (!document) {
7238 if (!aConsiderCurrentDocument) {
7239 return nsnull;
7242 // Make sure we end up with _something_ as the principal no matter
7243 // what.
7244 EnsureContentViewer(); // If this fails, we'll just get a null
7245 // docViewer and bail.
7247 nsCOMPtr<nsIDocumentViewer>
7248 docViewer(do_QueryInterface(mContentViewer));
7249 if (!docViewer)
7250 return nsnull;
7251 docViewer->GetDocument(getter_AddRefs(document));
7254 //-- Get the document's principal
7255 if (document) {
7256 return document->NodePrincipal();
7259 return nsnull;
7262 nsresult
7263 nsDocShell::DoURILoad(nsIURI * aURI,
7264 nsIURI * aReferrerURI,
7265 PRBool aSendReferrer,
7266 nsISupports * aOwner,
7267 const char * aTypeHint,
7268 nsIInputStream * aPostData,
7269 nsIInputStream * aHeadersData,
7270 PRBool aFirstParty,
7271 nsIDocShell ** aDocShell,
7272 nsIRequest ** aRequest,
7273 PRBool aIsNewWindowTarget,
7274 PRBool aBypassClassifier)
7276 nsresult rv;
7277 nsCOMPtr<nsIURILoader> uriLoader;
7279 uriLoader = do_GetService(NS_URI_LOADER_CONTRACTID, &rv);
7280 if (NS_FAILED(rv)) return rv;
7282 nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL;
7283 if (aFirstParty) {
7284 // tag first party URL loads
7285 loadFlags |= nsIChannel::LOAD_INITIAL_DOCUMENT_URI;
7288 if (mLoadType == LOAD_ERROR_PAGE) {
7289 // Error pages are LOAD_BACKGROUND
7290 loadFlags |= nsIChannel::LOAD_BACKGROUND;
7293 // open a channel for the url
7294 nsCOMPtr<nsIChannel> channel;
7296 rv = NS_NewChannel(getter_AddRefs(channel),
7297 aURI,
7298 nsnull,
7299 nsnull,
7300 static_cast<nsIInterfaceRequestor *>(this),
7301 loadFlags);
7302 if (NS_FAILED(rv)) {
7303 if (rv == NS_ERROR_UNKNOWN_PROTOCOL) {
7304 // This is a uri with a protocol scheme we don't know how
7305 // to handle. Embedders might still be interested in
7306 // handling the load, though, so we fire a notification
7307 // before throwing the load away.
7308 PRBool abort = PR_FALSE;
7309 nsresult rv2 = mContentListener->OnStartURIOpen(aURI, &abort);
7310 if (NS_SUCCEEDED(rv2) && abort) {
7311 // Hey, they're handling the load for us! How convenient!
7312 return NS_OK;
7316 return rv;
7319 // Make sure to give the caller a channel if we managed to create one
7320 // This is important for correct error page/session history interaction
7321 if (aRequest)
7322 NS_ADDREF(*aRequest = channel);
7324 channel->SetOriginalURI(aURI);
7325 if (aTypeHint && *aTypeHint) {
7326 channel->SetContentType(nsDependentCString(aTypeHint));
7327 mContentTypeHint = aTypeHint;
7329 else {
7330 mContentTypeHint.Truncate();
7333 //hack
7334 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
7335 nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal(do_QueryInterface(channel));
7336 if (httpChannelInternal) {
7337 if (aFirstParty) {
7338 httpChannelInternal->SetDocumentURI(aURI);
7339 } else {
7340 httpChannelInternal->SetDocumentURI(aReferrerURI);
7344 nsCOMPtr<nsIWritablePropertyBag2> props(do_QueryInterface(channel));
7345 if (props)
7347 // save true referrer for those who need it (e.g. xpinstall whitelisting)
7348 // Currently only http and ftp channels support this.
7349 props->SetPropertyAsInterface(NS_LITERAL_STRING("docshell.internalReferrer"),
7350 aReferrerURI);
7354 // If this is a HTTP channel, then set up the HTTP specific information
7355 // (ie. POST data, referrer, ...)
7357 if (httpChannel) {
7358 nsCOMPtr<nsICachingChannel> cacheChannel(do_QueryInterface(httpChannel));
7359 /* Get the cache Key from SH */
7360 nsCOMPtr<nsISupports> cacheKey;
7361 if (mLSHE) {
7362 mLSHE->GetCacheKey(getter_AddRefs(cacheKey));
7364 else if (mOSHE) // for reload cases
7365 mOSHE->GetCacheKey(getter_AddRefs(cacheKey));
7367 // figure out if we need to set the post data stream on the channel...
7368 // right now, this is only done for http channels.....
7369 if (aPostData) {
7370 // XXX it's a bit of a hack to rewind the postdata stream here but
7371 // it has to be done in case the post data is being reused multiple
7372 // times.
7373 nsCOMPtr<nsISeekableStream>
7374 postDataSeekable(do_QueryInterface(aPostData));
7375 if (postDataSeekable) {
7376 rv = postDataSeekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
7377 NS_ENSURE_SUCCESS(rv, rv);
7380 nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
7381 NS_ASSERTION(uploadChannel, "http must support nsIUploadChannel");
7383 // we really need to have a content type associated with this stream!!
7384 uploadChannel->SetUploadStream(aPostData, EmptyCString(), -1);
7385 /* If there is a valid postdata *and* it is a History Load,
7386 * set up the cache key on the channel, to retrieve the
7387 * data *only* from the cache. If it is a normal reload, the
7388 * cache is free to go to the server for updated postdata.
7390 if (cacheChannel && cacheKey) {
7391 if (mLoadType == LOAD_HISTORY || mLoadType == LOAD_RELOAD_CHARSET_CHANGE) {
7392 cacheChannel->SetCacheKey(cacheKey);
7393 PRUint32 loadFlags;
7394 if (NS_SUCCEEDED(channel->GetLoadFlags(&loadFlags)))
7395 channel->SetLoadFlags(loadFlags | nsICachingChannel::LOAD_ONLY_FROM_CACHE);
7397 else if (mLoadType == LOAD_RELOAD_NORMAL)
7398 cacheChannel->SetCacheKey(cacheKey);
7401 else {
7402 /* If there is no postdata, set the cache key on the channel, and
7403 * do not set the LOAD_ONLY_FROM_CACHE flag, so that the channel
7404 * will be free to get it from net if it is not found in cache.
7405 * New cache may use it creatively on CGI pages with GET
7406 * method and even on those that say "no-cache"
7408 if (mLoadType == LOAD_HISTORY || mLoadType == LOAD_RELOAD_NORMAL
7409 || mLoadType == LOAD_RELOAD_CHARSET_CHANGE) {
7410 if (cacheChannel && cacheKey)
7411 cacheChannel->SetCacheKey(cacheKey);
7414 if (aHeadersData) {
7415 rv = AddHeadersToChannel(aHeadersData, httpChannel);
7417 // Set the referrer explicitly
7418 if (aReferrerURI && aSendReferrer) {
7419 // Referrer is currenly only set for link clicks here.
7420 httpChannel->SetReferrer(aReferrerURI);
7424 // Set the owner of the channel, but only for channels that can't
7425 // provide their own security context.
7427 // XXX: Is seems wrong that the owner is ignored - even if one is
7428 // supplied) unless the URI is javascript or data or about:blank.
7429 // XXX: If this is ever changed, check all callers for what owners they're
7430 // passing in. In particular, see the code and comments in LoadURI
7431 // where we fall back on inheriting the owner if called
7432 // from chrome. That would be very wrong if this code changed
7433 // anything but channels that can't provide their own security context!
7435 // (Currently chrome URIs set the owner when they are created!
7436 // So setting a NULL owner would be bad!)
7438 // If this code ever changes, change nsObjectLoadingContent::LoadObject
7439 // accordingly.
7440 PRBool inherit;
7441 // We expect URIInheritsSecurityContext to return success for an
7442 // about:blank URI, so don't call IsAboutBlank() if this call fails.
7443 rv = URIInheritsSecurityContext(aURI, &inherit);
7444 if (NS_SUCCEEDED(rv) && (inherit || IsAboutBlank(aURI))) {
7445 channel->SetOwner(aOwner);
7449 // file: uri special-casing
7451 // If this is a file: load opened from another file: then it may need
7452 // to inherit the owner from the referrer so they can script each other.
7453 // If we don't set the owner explicitly then each file: gets an owner
7454 // based on its own codebase later.
7456 nsCOMPtr<nsIPrincipal> ownerPrincipal(do_QueryInterface(aOwner));
7457 if (URIIsLocalFile(aURI) && ownerPrincipal &&
7458 NS_SUCCEEDED(ownerPrincipal->CheckMayLoad(aURI, PR_FALSE))) {
7459 channel->SetOwner(aOwner);
7462 nsCOMPtr<nsIScriptChannel> scriptChannel = do_QueryInterface(channel);
7463 if (scriptChannel) {
7464 // Allow execution against our context if the principals match
7465 scriptChannel->
7466 SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL);
7469 if (aIsNewWindowTarget) {
7470 nsCOMPtr<nsIWritablePropertyBag2> props = do_QueryInterface(channel);
7471 if (props) {
7472 props->SetPropertyAsBool(
7473 NS_LITERAL_STRING("docshell.newWindowTarget"),
7474 PR_TRUE);
7478 rv = DoChannelLoad(channel, uriLoader, aBypassClassifier);
7481 // If the channel load failed, we failed and nsIWebProgress just ain't
7482 // gonna happen.
7484 if (NS_SUCCEEDED(rv)) {
7485 if (aDocShell) {
7486 *aDocShell = this;
7487 NS_ADDREF(*aDocShell);
7491 return rv;
7494 static NS_METHOD
7495 AppendSegmentToString(nsIInputStream *in,
7496 void *closure,
7497 const char *fromRawSegment,
7498 PRUint32 toOffset,
7499 PRUint32 count,
7500 PRUint32 *writeCount)
7502 // aFromSegment now contains aCount bytes of data.
7504 nsCAutoString *buf = static_cast<nsCAutoString *>(closure);
7505 buf->Append(fromRawSegment, count);
7507 // Indicate that we have consumed all of aFromSegment
7508 *writeCount = count;
7509 return NS_OK;
7512 NS_IMETHODIMP
7513 nsDocShell::AddHeadersToChannel(nsIInputStream *aHeadersData,
7514 nsIChannel *aGenericChannel)
7516 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aGenericChannel);
7517 NS_ENSURE_STATE(httpChannel);
7519 PRUint32 numRead;
7520 nsCAutoString headersString;
7521 nsresult rv = aHeadersData->ReadSegments(AppendSegmentToString,
7522 &headersString,
7523 PR_UINT32_MAX,
7524 &numRead);
7525 NS_ENSURE_SUCCESS(rv, rv);
7527 // used during the manipulation of the String from the InputStream
7528 nsCAutoString headerName;
7529 nsCAutoString headerValue;
7530 PRInt32 crlf;
7531 PRInt32 colon;
7534 // Iterate over the headersString: for each "\r\n" delimited chunk,
7535 // add the value as a header to the nsIHttpChannel
7538 static const char kWhitespace[] = "\b\t\r\n ";
7539 while (PR_TRUE) {
7540 crlf = headersString.Find("\r\n");
7541 if (crlf == kNotFound)
7542 return NS_OK;
7544 const nsCSubstring &oneHeader = StringHead(headersString, crlf);
7546 colon = oneHeader.FindChar(':');
7547 if (colon == kNotFound)
7548 return NS_ERROR_UNEXPECTED;
7550 headerName = StringHead(oneHeader, colon);
7551 headerValue = Substring(oneHeader, colon + 1);
7553 headerName.Trim(kWhitespace);
7554 headerValue.Trim(kWhitespace);
7556 headersString.Cut(0, crlf + 2);
7559 // FINALLY: we can set the header!
7562 rv = httpChannel->SetRequestHeader(headerName, headerValue, PR_TRUE);
7563 NS_ENSURE_SUCCESS(rv, rv);
7566 NS_NOTREACHED("oops");
7567 return NS_ERROR_UNEXPECTED;
7570 nsresult nsDocShell::DoChannelLoad(nsIChannel * aChannel,
7571 nsIURILoader * aURILoader,
7572 PRBool aBypassClassifier)
7574 nsresult rv;
7575 // Mark the channel as being a document URI and allow content sniffing...
7576 nsLoadFlags loadFlags = 0;
7577 (void) aChannel->GetLoadFlags(&loadFlags);
7578 loadFlags |= nsIChannel::LOAD_DOCUMENT_URI |
7579 nsIChannel::LOAD_CALL_CONTENT_SNIFFERS;
7581 // Load attributes depend on load type...
7582 switch (mLoadType) {
7583 case LOAD_HISTORY:
7584 loadFlags |= nsIRequest::VALIDATE_NEVER;
7585 break;
7587 case LOAD_RELOAD_CHARSET_CHANGE:
7588 loadFlags |= nsIRequest::LOAD_FROM_CACHE;
7589 break;
7591 case LOAD_RELOAD_NORMAL:
7592 case LOAD_REFRESH:
7593 loadFlags |= nsIRequest::VALIDATE_ALWAYS;
7594 break;
7596 case LOAD_NORMAL_BYPASS_CACHE:
7597 case LOAD_NORMAL_BYPASS_PROXY:
7598 case LOAD_NORMAL_BYPASS_PROXY_AND_CACHE:
7599 case LOAD_RELOAD_BYPASS_CACHE:
7600 case LOAD_RELOAD_BYPASS_PROXY:
7601 case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
7602 loadFlags |= nsIRequest::LOAD_BYPASS_CACHE;
7603 break;
7605 case LOAD_NORMAL:
7606 case LOAD_LINK:
7607 // Set cache checking flags
7608 PRInt32 prefSetting;
7609 if (NS_SUCCEEDED
7610 (mPrefs->
7611 GetIntPref("browser.cache.check_doc_frequency",
7612 &prefSetting))) {
7613 switch (prefSetting) {
7614 case 0:
7615 loadFlags |= nsIRequest::VALIDATE_ONCE_PER_SESSION;
7616 break;
7617 case 1:
7618 loadFlags |= nsIRequest::VALIDATE_ALWAYS;
7619 break;
7620 case 2:
7621 loadFlags |= nsIRequest::VALIDATE_NEVER;
7622 break;
7625 break;
7628 (void) aChannel->SetLoadFlags(loadFlags);
7630 rv = aURILoader->OpenURI(aChannel,
7631 (mLoadType == LOAD_LINK),
7632 this);
7633 NS_ENSURE_SUCCESS(rv, rv);
7635 if (!aBypassClassifier) {
7636 rv = CheckClassifier(aChannel);
7637 if (NS_FAILED(rv)) {
7638 aChannel->Cancel(rv);
7639 return rv;
7643 return NS_OK;
7646 nsresult
7647 nsDocShell::CheckClassifier(nsIChannel *aChannel)
7649 nsRefPtr<nsClassifierCallback> classifier = new nsClassifierCallback();
7650 if (!classifier) return NS_ERROR_OUT_OF_MEMORY;
7652 nsresult rv = classifier->Start(aChannel);
7653 if (rv == NS_ERROR_FACTORY_NOT_REGISTERED ||
7654 rv == NS_ERROR_NOT_AVAILABLE) {
7655 // no URI classifier => ignored cases
7656 return NS_OK;
7658 NS_ENSURE_SUCCESS(rv, rv);
7660 mClassifier = classifier;
7662 return NS_OK;
7665 NS_IMETHODIMP
7666 nsDocShell::ScrollIfAnchor(nsIURI * aURI, PRBool * aWasAnchor,
7667 PRUint32 aLoadType, nscoord *cx, nscoord *cy)
7669 NS_ASSERTION(aURI, "null uri arg");
7670 NS_ASSERTION(aWasAnchor, "null anchor arg");
7672 if (aURI == nsnull || aWasAnchor == nsnull) {
7673 return NS_ERROR_FAILURE;
7676 *aWasAnchor = PR_FALSE;
7678 if (!mCurrentURI) {
7679 return NS_OK;
7682 nsCOMPtr<nsIPresShell> shell;
7683 nsresult rv = GetPresShell(getter_AddRefs(shell));
7684 if (NS_FAILED(rv) || !shell) {
7685 // If we failed to get the shell, or if there is no shell,
7686 // nothing left to do here.
7688 return rv;
7691 // NOTE: we assume URIs are absolute for comparison purposes
7693 nsCAutoString currentSpec;
7694 NS_ENSURE_SUCCESS(mCurrentURI->GetSpec(currentSpec),
7695 NS_ERROR_FAILURE);
7697 nsCAutoString newSpec;
7698 NS_ENSURE_SUCCESS(aURI->GetSpec(newSpec), NS_ERROR_FAILURE);
7700 // Search for hash marks in the current URI and the new URI and
7701 // take a copy of everything to the left of the hash for
7702 // comparison.
7704 const char kHash = '#';
7706 // Split the new URI into a left and right part
7707 // (assume we're parsing it out right
7708 nsACString::const_iterator urlStart, urlEnd, refStart, refEnd;
7709 newSpec.BeginReading(urlStart);
7710 newSpec.EndReading(refEnd);
7712 PRInt32 hashNew = newSpec.FindChar(kHash);
7713 if (hashNew == 0) {
7714 return NS_OK; // Strange URI
7717 if (hashNew > 0) {
7718 // found it
7719 urlEnd = urlStart;
7720 urlEnd.advance(hashNew);
7722 refStart = urlEnd;
7723 ++refStart; // advanced past '#'
7726 else {
7727 // no hash at all
7728 urlEnd = refStart = refEnd;
7730 const nsACString& sNewLeft = Substring(urlStart, urlEnd);
7731 const nsACString& sNewRef = Substring(refStart, refEnd);
7733 // Split the current URI in a left and right part
7734 nsACString::const_iterator currentLeftStart, currentLeftEnd;
7735 currentSpec.BeginReading(currentLeftStart);
7737 PRInt32 hashCurrent = currentSpec.FindChar(kHash);
7738 if (hashCurrent == 0) {
7739 return NS_OK; // Strange URI
7742 if (hashCurrent > 0) {
7743 currentLeftEnd = currentLeftStart;
7744 currentLeftEnd.advance(hashCurrent);
7746 else {
7747 currentSpec.EndReading(currentLeftEnd);
7750 // If we have no new anchor, we do not want to scroll, unless there is a
7751 // current anchor and we are doing a history load. So return if we have no
7752 // new anchor, and there is no current anchor or the load is not a history
7753 // load.
7754 NS_ASSERTION(hashNew != 0 && hashCurrent != 0,
7755 "What happened to the early returns above?");
7756 if (hashNew == kNotFound &&
7757 (hashCurrent == kNotFound || aLoadType != LOAD_HISTORY)) {
7758 return NS_OK;
7761 // Compare the URIs.
7763 // NOTE: this is a case sensitive comparison because some parts of the
7764 // URI are case sensitive, and some are not. i.e. the domain name
7765 // is case insensitive but the the paths are not.
7767 // This means that comparing "http://www.ABC.com/" to "http://www.abc.com/"
7768 // will fail this test.
7770 if (!Substring(currentLeftStart, currentLeftEnd).Equals(sNewLeft)) {
7771 return NS_OK; // URIs not the same
7774 // Now we know we are dealing with an anchor
7775 *aWasAnchor = PR_TRUE;
7777 // Both the new and current URIs refer to the same page. We can now
7778 // browse to the hash stored in the new URI.
7780 // But first let's capture positions of scroller(s) that can
7781 // (and usually will) be modified by GoToAnchor() call.
7783 GetCurScrollPos(ScrollOrientation_X, cx);
7784 GetCurScrollPos(ScrollOrientation_Y, cy);
7786 if (!sNewRef.IsEmpty()) {
7787 // anchor is there, but if it's a load from history,
7788 // we don't have any anchor jumping to do
7789 PRBool scroll = aLoadType != LOAD_HISTORY &&
7790 aLoadType != LOAD_RELOAD_NORMAL;
7792 char *str = ToNewCString(sNewRef);
7793 if (!str) {
7794 return NS_ERROR_OUT_OF_MEMORY;
7797 // nsUnescape modifies the string that is passed into it.
7798 nsUnescape(str);
7800 // We assume that the bytes are in UTF-8, as it says in the
7801 // spec:
7802 // http://www.w3.org/TR/html4/appendix/notes.html#h-B.2.1
7804 // We try the UTF-8 string first, and then try the document's
7805 // charset (see below). If the string is not UTF-8,
7806 // conversion will fail and give us an empty Unicode string.
7807 // In that case, we should just fall through to using the
7808 // page's charset.
7809 rv = NS_ERROR_FAILURE;
7810 NS_ConvertUTF8toUTF16 uStr(str);
7811 if (!uStr.IsEmpty()) {
7812 rv = shell->GoToAnchor(NS_ConvertUTF8toUTF16(str), scroll);
7814 nsMemory::Free(str);
7816 // Above will fail if the anchor name is not UTF-8. Need to
7817 // convert from document charset to unicode.
7818 if (NS_FAILED(rv)) {
7820 // Get a document charset
7821 NS_ENSURE_TRUE(mContentViewer, NS_ERROR_FAILURE);
7822 nsCOMPtr<nsIDocumentViewer>
7823 docv(do_QueryInterface(mContentViewer));
7824 NS_ENSURE_TRUE(docv, NS_ERROR_FAILURE);
7825 nsCOMPtr<nsIDocument> doc;
7826 rv = docv->GetDocument(getter_AddRefs(doc));
7827 NS_ENSURE_SUCCESS(rv, rv);
7828 const nsACString &aCharset = doc->GetDocumentCharacterSet();
7830 nsCOMPtr<nsITextToSubURI> textToSubURI =
7831 do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
7832 NS_ENSURE_SUCCESS(rv, rv);
7834 // Unescape and convert to unicode
7835 nsXPIDLString uStr;
7837 rv = textToSubURI->UnEscapeAndConvert(PromiseFlatCString(aCharset).get(),
7838 PromiseFlatCString(sNewRef).get(),
7839 getter_Copies(uStr));
7840 NS_ENSURE_SUCCESS(rv, rv);
7842 // Ignore return value of GoToAnchor, since it will return an error
7843 // if there is no such anchor in the document, which is actually a
7844 // success condition for us (we want to update the session history
7845 // with the new URI no matter whether we actually scrolled
7846 // somewhere).
7847 shell->GoToAnchor(uStr, scroll);
7850 else {
7852 // Tell the shell it's at an anchor, without scrolling.
7853 shell->GoToAnchor(EmptyString(), PR_FALSE);
7855 // An empty anchor was found, but if it's a load from history,
7856 // we don't have to jump to the top of the page. Scrollbar
7857 // position will be restored by the caller, based on positions
7858 // stored in session history.
7859 if (aLoadType == LOAD_HISTORY || aLoadType == LOAD_RELOAD_NORMAL)
7860 return rv;
7861 //An empty anchor. Scroll to the top of the page.
7862 rv = SetCurScrollPosEx(0, 0);
7865 return rv;
7868 void
7869 nsDocShell::SetupReferrerFromChannel(nsIChannel * aChannel)
7871 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
7872 if (httpChannel) {
7873 nsCOMPtr<nsIURI> referrer;
7874 nsresult rv = httpChannel->GetReferrer(getter_AddRefs(referrer));
7875 if (NS_SUCCEEDED(rv)) {
7876 SetReferrerURI(referrer);
7881 PRBool
7882 nsDocShell::OnNewURI(nsIURI * aURI, nsIChannel * aChannel,
7883 PRUint32 aLoadType, PRBool aFireOnLocationChange,
7884 PRBool aAddToGlobalHistory)
7886 NS_ASSERTION(aURI, "uri is null");
7887 #if defined(PR_LOGGING) && defined(DEBUG)
7888 if (PR_LOG_TEST(gDocShellLog, PR_LOG_DEBUG)) {
7889 nsCAutoString spec;
7890 aURI->GetSpec(spec);
7892 nsCAutoString chanName;
7893 if (aChannel)
7894 aChannel->GetName(chanName);
7895 else
7896 chanName.AssignLiteral("<no channel>");
7898 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
7899 ("nsDocShell[%p]::OnNewURI(\"%s\", [%s], 0x%x)\n", this, spec.get(),
7900 chanName.get(), aLoadType));
7902 #endif
7904 PRBool updateHistory = PR_TRUE;
7905 PRBool equalUri = PR_FALSE;
7906 PRBool shAvailable = PR_TRUE;
7908 // Get the post data from the channel
7909 nsCOMPtr<nsIInputStream> inputStream;
7910 if (aChannel) {
7911 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
7913 // Check if the HTTPChannel is hiding under a multiPartChannel
7914 if (!httpChannel) {
7915 GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
7918 if (httpChannel) {
7919 nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
7920 if (uploadChannel) {
7921 uploadChannel->GetUploadStream(getter_AddRefs(inputStream));
7925 /* Create SH Entry (mLSHE) only if there is a SessionHistory object (mSessionHistory) in
7926 * the current frame or in the root docshell
7928 nsCOMPtr<nsISHistory> rootSH = mSessionHistory;
7929 if (!rootSH) {
7930 // Get the handle to SH from the root docshell
7931 GetRootSessionHistory(getter_AddRefs(rootSH));
7932 if (!rootSH)
7933 shAvailable = PR_FALSE;
7934 } // rootSH
7937 // Determine if this type of load should update history.
7938 if (aLoadType == LOAD_BYPASS_HISTORY ||
7939 aLoadType == LOAD_ERROR_PAGE ||
7940 aLoadType & LOAD_CMD_HISTORY ||
7941 aLoadType & LOAD_CMD_RELOAD)
7942 updateHistory = PR_FALSE;
7944 // Check if the url to be loaded is the same as the one already loaded.
7945 if (mCurrentURI)
7946 aURI->Equals(mCurrentURI, &equalUri);
7948 #ifdef DEBUG
7949 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
7950 (" shAvailable=%i updateHistory=%i equalURI=%i\n",
7951 shAvailable, updateHistory, equalUri));
7952 #endif
7954 /* If the url to be loaded is the same as the one already there,
7955 * and the original loadType is LOAD_NORMAL, LOAD_LINK, or
7956 * LOAD_STOP_CONTENT, set loadType to LOAD_NORMAL_REPLACE so that
7957 * AddToSessionHistory() won't mess with the current SHEntry and
7958 * if this page has any frame children, it also will be handled
7959 * properly. see bug 83684
7961 * XXX Hopefully changing the loadType at this time will not hurt
7962 * anywhere. The other way to take care of sequentially repeating
7963 * frameset pages is to add new methods to nsIDocShellTreeItem.
7964 * Hopefully I don't have to do that.
7966 if (equalUri &&
7967 (mLoadType == LOAD_NORMAL ||
7968 mLoadType == LOAD_LINK ||
7969 mLoadType == LOAD_STOP_CONTENT) &&
7970 !inputStream)
7972 mLoadType = LOAD_NORMAL_REPLACE;
7975 // If this is a refresh to the currently loaded url, we don't
7976 // have to update session or global history.
7977 if (mLoadType == LOAD_REFRESH && !inputStream && equalUri) {
7978 SetHistoryEntry(&mLSHE, mOSHE);
7982 /* If the user pressed shift-reload, cache will create a new cache key
7983 * for the page. Save the new cacheKey in Session History.
7984 * see bug 90098
7986 if (aChannel &&
7987 (aLoadType == LOAD_RELOAD_BYPASS_CACHE ||
7988 aLoadType == LOAD_RELOAD_BYPASS_PROXY ||
7989 aLoadType == LOAD_RELOAD_BYPASS_PROXY_AND_CACHE)) {
7990 NS_ASSERTION(!updateHistory,
7991 "We shouldn't be updating history for forced reloads!");
7993 nsCOMPtr<nsICachingChannel> cacheChannel(do_QueryInterface(aChannel));
7994 nsCOMPtr<nsISupports> cacheKey;
7995 // Get the Cache Key and store it in SH.
7996 if (cacheChannel)
7997 cacheChannel->GetCacheKey(getter_AddRefs(cacheKey));
7998 // If we already have a loading history entry, store the new cache key
7999 // in it. Otherwise, since we're doing a reload and won't be updating
8000 // our history entry, store the cache key in our current history entry.
8001 if (mLSHE)
8002 mLSHE->SetCacheKey(cacheKey);
8003 else if (mOSHE)
8004 mOSHE->SetCacheKey(cacheKey);
8007 if (updateHistory && shAvailable) {
8008 // Update session history if necessary...
8009 if (!mLSHE && (mItemType == typeContent) && mURIResultedInDocument) {
8010 /* This is a fresh page getting loaded for the first time
8011 *.Create a Entry for it and add it to SH, if this is the
8012 * rootDocShell
8014 (void) AddToSessionHistory(aURI, aChannel, getter_AddRefs(mLSHE));
8017 // Update Global history
8018 if (aAddToGlobalHistory) {
8019 // Get the referrer uri from the channel
8020 AddToGlobalHistory(aURI, PR_FALSE, aChannel);
8024 // If this was a history load, update the index in
8025 // SH.
8026 if (rootSH && (mLoadType & LOAD_CMD_HISTORY)) {
8027 nsCOMPtr<nsISHistoryInternal> shInternal(do_QueryInterface(rootSH));
8028 if (shInternal) {
8029 rootSH->GetIndex(&mPreviousTransIndex);
8030 shInternal->UpdateIndex();
8031 rootSH->GetIndex(&mLoadedTransIndex);
8032 #ifdef DEBUG_PAGE_CACHE
8033 printf("Previous index: %d, Loaded index: %d\n\n",
8034 mPreviousTransIndex, mLoadedTransIndex);
8035 #endif
8038 PRBool onLocationChangeNeeded = SetCurrentURI(aURI, aChannel,
8039 aFireOnLocationChange);
8040 // Make sure to store the referrer from the channel, if any
8041 SetupReferrerFromChannel(aChannel);
8042 return onLocationChangeNeeded;
8045 PRBool
8046 nsDocShell::OnLoadingSite(nsIChannel * aChannel, PRBool aFireOnLocationChange,
8047 PRBool aAddToGlobalHistory)
8049 nsCOMPtr<nsIURI> uri;
8050 // If this a redirect, use the final url (uri)
8051 // else use the original url
8053 // Note that this should match what documents do (see nsDocument::Reset).
8054 NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
8055 NS_ENSURE_TRUE(uri, PR_FALSE);
8057 return OnNewURI(uri, aChannel, mLoadType, aFireOnLocationChange,
8058 aAddToGlobalHistory);
8062 void
8063 nsDocShell::SetReferrerURI(nsIURI * aURI)
8065 mReferrerURI = aURI; // This assigment addrefs
8068 //*****************************************************************************
8069 // nsDocShell: Session History
8070 //*****************************************************************************
8071 PRBool
8072 nsDocShell::ShouldAddToSessionHistory(nsIURI * aURI)
8074 // I believe none of the about: urls should go in the history. But then
8075 // that could just be me... If the intent is only deny about:blank then we
8076 // should just do a spec compare, rather than two gets of the scheme and
8077 // then the path. -Gagan
8078 nsresult rv;
8079 nsCAutoString buf;
8081 rv = aURI->GetScheme(buf);
8082 if (NS_FAILED(rv))
8083 return PR_FALSE;
8085 if (buf.Equals("about")) {
8086 rv = aURI->GetPath(buf);
8087 if (NS_FAILED(rv))
8088 return PR_FALSE;
8090 if (buf.Equals("blank")) {
8091 return PR_FALSE;
8094 return PR_TRUE;
8097 nsresult
8098 nsDocShell::AddToSessionHistory(nsIURI * aURI,
8099 nsIChannel * aChannel, nsISHEntry ** aNewEntry)
8101 #if defined(PR_LOGGING) && defined(DEBUG)
8102 if (PR_LOG_TEST(gDocShellLog, PR_LOG_DEBUG)) {
8103 nsCAutoString spec;
8104 aURI->GetSpec(spec);
8106 nsCAutoString chanName;
8107 if (aChannel)
8108 aChannel->GetName(chanName);
8109 else
8110 chanName.AssignLiteral("<no channel>");
8112 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
8113 ("nsDocShell[%p]::AddToSessionHistory(\"%s\", [%s])\n", this, spec.get(),
8114 chanName.get()));
8116 #endif
8118 nsresult rv = NS_OK;
8119 nsCOMPtr<nsISHEntry> entry;
8120 PRBool shouldPersist;
8122 shouldPersist = ShouldAddToSessionHistory(aURI);
8124 // Get a handle to the root docshell
8125 nsCOMPtr<nsIDocShellTreeItem> root;
8126 GetSameTypeRootTreeItem(getter_AddRefs(root));
8128 * If this is a LOAD_FLAGS_REPLACE_HISTORY in a subframe, we use
8129 * the existing SH entry in the page and replace the url and
8130 * other vitalities.
8132 if (LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY) &&
8133 root != static_cast<nsIDocShellTreeItem *>(this)) {
8134 // This is a subframe
8135 entry = mOSHE;
8136 nsCOMPtr<nsISHContainer> shContainer(do_QueryInterface(entry));
8137 if (shContainer) {
8138 PRInt32 childCount = 0;
8139 shContainer->GetChildCount(&childCount);
8140 // Remove all children of this entry
8141 for (PRInt32 i = childCount - 1; i >= 0; i--) {
8142 nsCOMPtr<nsISHEntry> child;
8143 shContainer->GetChildAt(i, getter_AddRefs(child));
8144 shContainer->RemoveChild(child);
8145 } // for
8146 } // shContainer
8149 // Create a new entry if necessary.
8150 if (!entry) {
8151 entry = do_CreateInstance(NS_SHENTRY_CONTRACTID);
8153 if (!entry) {
8154 return NS_ERROR_OUT_OF_MEMORY;
8158 // Get the post data & referrer
8159 nsCOMPtr<nsIInputStream> inputStream;
8160 nsCOMPtr<nsIURI> referrerURI;
8161 nsCOMPtr<nsISupports> cacheKey;
8162 nsCOMPtr<nsISupports> cacheToken;
8163 nsCOMPtr<nsISupports> owner;
8164 PRBool expired = PR_FALSE;
8165 PRBool discardLayoutState = PR_FALSE;
8166 if (aChannel) {
8167 nsCOMPtr<nsICachingChannel>
8168 cacheChannel(do_QueryInterface(aChannel));
8169 /* If there is a caching channel, get the Cache Key and store it
8170 * in SH.
8172 if (cacheChannel) {
8173 cacheChannel->GetCacheKey(getter_AddRefs(cacheKey));
8174 cacheChannel->GetCacheToken(getter_AddRefs(cacheToken));
8176 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
8178 // Check if the httpChannel is hiding under a multipartChannel
8179 if (!httpChannel) {
8180 GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
8182 if (httpChannel) {
8183 nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
8184 if (uploadChannel) {
8185 uploadChannel->GetUploadStream(getter_AddRefs(inputStream));
8187 httpChannel->GetReferrer(getter_AddRefs(referrerURI));
8189 discardLayoutState = ShouldDiscardLayoutState(httpChannel);
8191 aChannel->GetOwner(getter_AddRefs(owner));
8194 //Title is set in nsDocShell::SetTitle()
8195 entry->Create(aURI, // uri
8196 EmptyString(), // Title
8197 inputStream, // Post data stream
8198 nsnull, // LayoutHistory state
8199 cacheKey, // CacheKey
8200 mContentTypeHint, // Content-type
8201 owner); // Channel owner
8202 entry->SetReferrerURI(referrerURI);
8203 /* If cache got a 'no-store', ask SH not to store
8204 * HistoryLayoutState. By default, SH will set this
8205 * flag to PR_TRUE and save HistoryLayoutState.
8207 if (discardLayoutState) {
8208 entry->SetSaveLayoutStateFlag(PR_FALSE);
8210 if (cacheToken) {
8211 // Check if the page has expired from cache
8212 nsCOMPtr<nsICacheEntryInfo> cacheEntryInfo(do_QueryInterface(cacheToken));
8213 if (cacheEntryInfo) {
8214 PRUint32 expTime;
8215 cacheEntryInfo->GetExpirationTime(&expTime);
8216 PRUint32 now = PRTimeToSeconds(PR_Now());
8217 if (expTime <= now)
8218 expired = PR_TRUE;
8222 if (expired == PR_TRUE)
8223 entry->SetExpirationStatus(PR_TRUE);
8226 if (root == static_cast<nsIDocShellTreeItem *>(this) && mSessionHistory) {
8227 // This is the root docshell
8228 if (LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY)) {
8229 // Replace current entry in session history.
8230 PRInt32 index = 0;
8231 mSessionHistory->GetIndex(&index);
8232 nsCOMPtr<nsISHistoryInternal> shPrivate(do_QueryInterface(mSessionHistory));
8233 // Replace the current entry with the new entry
8234 if (shPrivate)
8235 rv = shPrivate->ReplaceEntry(index, entry);
8237 else {
8238 // Add to session history
8239 nsCOMPtr<nsISHistoryInternal>
8240 shPrivate(do_QueryInterface(mSessionHistory));
8241 NS_ENSURE_TRUE(shPrivate, NS_ERROR_FAILURE);
8242 mSessionHistory->GetIndex(&mPreviousTransIndex);
8243 rv = shPrivate->AddEntry(entry, shouldPersist);
8244 mSessionHistory->GetIndex(&mLoadedTransIndex);
8245 #ifdef DEBUG_PAGE_CACHE
8246 printf("Previous index: %d, Loaded index: %d\n\n",
8247 mPreviousTransIndex, mLoadedTransIndex);
8248 #endif
8251 else {
8252 // This is a subframe.
8253 if (!mOSHE || !LOAD_TYPE_HAS_FLAGS(mLoadType,
8254 LOAD_FLAGS_REPLACE_HISTORY))
8255 rv = DoAddChildSHEntry(entry, mChildOffset);
8258 // Return the new SH entry...
8259 if (aNewEntry) {
8260 *aNewEntry = nsnull;
8261 if (NS_SUCCEEDED(rv)) {
8262 *aNewEntry = entry;
8263 NS_ADDREF(*aNewEntry);
8267 return rv;
8271 NS_IMETHODIMP
8272 nsDocShell::LoadHistoryEntry(nsISHEntry * aEntry, PRUint32 aLoadType)
8274 if (!IsNavigationAllowed()) {
8275 return NS_OK;
8278 nsCOMPtr<nsIURI> uri;
8279 nsCOMPtr<nsIInputStream> postData;
8280 nsCOMPtr<nsIURI> referrerURI;
8281 nsCAutoString contentType;
8282 nsCOMPtr<nsISupports> owner;
8284 NS_ENSURE_TRUE(aEntry, NS_ERROR_FAILURE);
8286 NS_ENSURE_SUCCESS(aEntry->GetURI(getter_AddRefs(uri)), NS_ERROR_FAILURE);
8287 NS_ENSURE_SUCCESS(aEntry->GetReferrerURI(getter_AddRefs(referrerURI)),
8288 NS_ERROR_FAILURE);
8289 NS_ENSURE_SUCCESS(aEntry->GetPostData(getter_AddRefs(postData)),
8290 NS_ERROR_FAILURE);
8291 NS_ENSURE_SUCCESS(aEntry->GetContentType(contentType), NS_ERROR_FAILURE);
8292 NS_ENSURE_SUCCESS(aEntry->GetOwner(getter_AddRefs(owner)),
8293 NS_ERROR_FAILURE);
8295 // Calling CreateAboutBlankContentViewer can set mOSHE to null, and if
8296 // that's the only thing holding a ref to aEntry that will cause aEntry to
8297 // die while we're loading it. So hold a strong ref to aEntry here, just
8298 // in case.
8299 nsCOMPtr<nsISHEntry> kungFuDeathGrip(aEntry);
8300 PRBool isJS;
8301 nsresult rv = uri->SchemeIs("javascript", &isJS);
8302 if (NS_FAILED(rv) || isJS) {
8303 // We're loading a URL that will execute script from inside asyncOpen.
8304 // Replace the current document with about:blank now to prevent
8305 // anything from the current document from leaking into any JavaScript
8306 // code in the URL.
8307 nsCOMPtr<nsIPrincipal> prin = do_QueryInterface(owner);
8308 rv = CreateAboutBlankContentViewer(prin);
8310 if (NS_FAILED(rv)) {
8311 // The creation of the intermittent about:blank content
8312 // viewer failed for some reason (potentially because the
8313 // user prevented it). Interrupt the history load.
8314 return NS_OK;
8317 if (!owner) {
8318 // Ensure that we have an owner. Otherwise javascript: URIs will
8319 // pick it up from the about:blank page we just loaded, and we
8320 // don't really want even that in this case.
8321 owner = do_CreateInstance("@mozilla.org/nullprincipal;1");
8322 NS_ENSURE_TRUE(owner, NS_ERROR_OUT_OF_MEMORY);
8326 /* If there is a valid postdata *and* the user pressed
8327 * reload or shift-reload, take user's permission before we
8328 * repost the data to the server.
8330 if ((aLoadType & LOAD_CMD_RELOAD) && postData) {
8331 PRBool repost;
8332 rv = ConfirmRepost(&repost);
8333 if (NS_FAILED(rv)) return rv;
8335 // If the user pressed cancel in the dialog, return. We're done here.
8336 if (!repost)
8337 return NS_BINDING_ABORTED;
8340 rv = InternalLoad(uri,
8341 referrerURI,
8342 owner,
8343 INTERNAL_LOAD_FLAGS_NONE, // Do not inherit owner from document (security-critical!)
8344 nsnull, // No window target
8345 contentType.get(), // Type hint
8346 postData, // Post data stream
8347 nsnull, // No headers stream
8348 aLoadType, // Load type
8349 aEntry, // SHEntry
8350 PR_TRUE,
8351 nsnull, // No nsIDocShell
8352 nsnull); // No nsIRequest
8353 return rv;
8356 NS_IMETHODIMP nsDocShell::GetShouldSaveLayoutState(PRBool* aShould)
8358 *aShould = PR_FALSE;
8359 if (mOSHE) {
8360 // Don't capture historystate and save it in history
8361 // if the page asked not to do so.
8362 mOSHE->GetSaveLayoutStateFlag(aShould);
8365 return NS_OK;
8368 NS_IMETHODIMP nsDocShell::PersistLayoutHistoryState()
8370 nsresult rv = NS_OK;
8372 if (mOSHE) {
8373 PRBool shouldSave;
8374 GetShouldSaveLayoutState(&shouldSave);
8375 if (!shouldSave)
8376 return NS_OK;
8378 nsCOMPtr<nsIPresShell> shell;
8379 rv = GetPresShell(getter_AddRefs(shell));
8380 if (NS_SUCCEEDED(rv) && shell) {
8381 nsCOMPtr<nsILayoutHistoryState> layoutState;
8382 rv = shell->CaptureHistoryState(getter_AddRefs(layoutState),
8383 PR_TRUE);
8387 return rv;
8390 /* static */ nsresult
8391 nsDocShell::WalkHistoryEntries(nsISHEntry *aRootEntry,
8392 nsDocShell *aRootShell,
8393 WalkHistoryEntriesFunc aCallback,
8394 void *aData)
8396 NS_ENSURE_TRUE(aRootEntry, NS_ERROR_FAILURE);
8398 nsCOMPtr<nsISHContainer> container(do_QueryInterface(aRootEntry));
8399 if (!container)
8400 return NS_ERROR_FAILURE;
8402 PRInt32 childCount;
8403 container->GetChildCount(&childCount);
8404 for (PRInt32 i = 0; i < childCount; i++) {
8405 nsCOMPtr<nsISHEntry> childEntry;
8406 container->GetChildAt(i, getter_AddRefs(childEntry));
8407 if (!childEntry) {
8408 // childEntry can be null for valid reasons, for example if the
8409 // docshell at index i never loaded anything useful.
8410 continue;
8413 nsDocShell *childShell = nsnull;
8414 if (aRootShell) {
8415 // Walk the children of aRootShell and see if one of them
8416 // has srcChild as a SHEntry.
8418 PRInt32 childCount = aRootShell->mChildList.Count();
8419 for (PRInt32 j = 0; j < childCount; ++j) {
8420 nsDocShell *child =
8421 static_cast<nsDocShell*>(aRootShell->ChildAt(j));
8423 if (child->HasHistoryEntry(childEntry)) {
8424 childShell = child;
8425 break;
8429 nsresult rv = aCallback(childEntry, childShell, i, aData);
8430 NS_ENSURE_SUCCESS(rv, rv);
8433 return NS_OK;
8436 // callback data for WalkHistoryEntries
8437 struct NS_STACK_CLASS CloneAndReplaceData
8439 CloneAndReplaceData(PRUint32 aCloneID, nsISHEntry *aReplaceEntry,
8440 nsISHEntry *aDestTreeParent)
8441 : cloneID(aCloneID),
8442 replaceEntry(aReplaceEntry),
8443 destTreeParent(aDestTreeParent) { }
8445 PRUint32 cloneID;
8446 nsISHEntry *replaceEntry;
8447 nsISHEntry *destTreeParent;
8448 nsCOMPtr<nsISHEntry> resultEntry;
8451 /* static */ nsresult
8452 nsDocShell::CloneAndReplaceChild(nsISHEntry *aEntry, nsDocShell *aShell,
8453 PRInt32 aEntryIndex, void *aData)
8455 nsresult result = NS_OK;
8456 nsCOMPtr<nsISHEntry> dest;
8458 CloneAndReplaceData *data = static_cast<CloneAndReplaceData*>(aData);
8459 PRUint32 cloneID = data->cloneID;
8460 nsISHEntry *replaceEntry = data->replaceEntry;
8462 PRUint32 srcID;
8463 aEntry->GetID(&srcID);
8465 if (srcID == cloneID) {
8466 // Just replace the entry, and don't walk the children.
8467 dest = replaceEntry;
8468 dest->SetIsSubFrame(PR_TRUE);
8469 } else {
8470 // Clone the SHEntry...
8471 result = aEntry->Clone(getter_AddRefs(dest));
8472 if (NS_FAILED(result))
8473 return result;
8475 // This entry is for a subframe navigation
8476 dest->SetIsSubFrame(PR_TRUE);
8478 // Walk the children
8479 CloneAndReplaceData childData(cloneID, replaceEntry, dest);
8480 result = WalkHistoryEntries(aEntry, aShell,
8481 CloneAndReplaceChild, &childData);
8482 if (NS_FAILED(result))
8483 return result;
8485 if (aShell)
8486 aShell->SwapHistoryEntries(aEntry, dest);
8489 nsCOMPtr<nsISHContainer> container =
8490 do_QueryInterface(data->destTreeParent);
8491 if (container)
8492 container->AddChild(dest, aEntryIndex);
8494 data->resultEntry = dest;
8495 return result;
8498 /* static */ nsresult
8499 nsDocShell::CloneAndReplace(nsISHEntry *aSrcEntry,
8500 nsDocShell *aSrcShell,
8501 PRUint32 aCloneID,
8502 nsISHEntry *aReplaceEntry,
8503 nsISHEntry **aResultEntry)
8505 NS_ENSURE_ARG_POINTER(aResultEntry);
8506 NS_ENSURE_TRUE(aReplaceEntry, NS_ERROR_FAILURE);
8508 CloneAndReplaceData data(aCloneID, aReplaceEntry, nsnull);
8509 nsresult rv = CloneAndReplaceChild(aSrcEntry, aSrcShell, 0, &data);
8511 data.resultEntry.swap(*aResultEntry);
8512 return rv;
8516 void
8517 nsDocShell::SwapHistoryEntries(nsISHEntry *aOldEntry, nsISHEntry *aNewEntry)
8519 if (aOldEntry == mOSHE)
8520 mOSHE = aNewEntry;
8522 if (aOldEntry == mLSHE)
8523 mLSHE = aNewEntry;
8527 struct SwapEntriesData
8529 nsDocShell *ignoreShell; // constant; the shell to ignore
8530 nsISHEntry *destTreeRoot; // constant; the root of the dest tree
8531 nsISHEntry *destTreeParent; // constant; the node under destTreeRoot
8532 // whose children will correspond to aEntry
8536 nsresult
8537 nsDocShell::SetChildHistoryEntry(nsISHEntry *aEntry, nsDocShell *aShell,
8538 PRInt32 aEntryIndex, void *aData)
8540 SwapEntriesData *data = static_cast<SwapEntriesData*>(aData);
8541 nsDocShell *ignoreShell = data->ignoreShell;
8543 if (!aShell || aShell == ignoreShell)
8544 return NS_OK;
8546 nsISHEntry *destTreeRoot = data->destTreeRoot;
8548 nsCOMPtr<nsISHEntry> destEntry;
8549 nsCOMPtr<nsISHContainer> container =
8550 do_QueryInterface(data->destTreeParent);
8552 if (container) {
8553 // aEntry is a clone of some child of destTreeParent, but since the
8554 // trees aren't necessarily in sync, we'll have to locate it.
8555 // Note that we could set aShell's entry to null if we don't find a
8556 // corresponding entry under destTreeParent.
8558 PRUint32 targetID, id;
8559 aEntry->GetID(&targetID);
8561 // First look at the given index, since this is the common case.
8562 nsCOMPtr<nsISHEntry> entry;
8563 container->GetChildAt(aEntryIndex, getter_AddRefs(entry));
8564 if (entry && NS_SUCCEEDED(entry->GetID(&id)) && id == targetID) {
8565 destEntry.swap(entry);
8566 } else {
8567 PRInt32 childCount;
8568 container->GetChildCount(&childCount);
8569 for (PRInt32 i = 0; i < childCount; ++i) {
8570 container->GetChildAt(i, getter_AddRefs(entry));
8571 if (!entry)
8572 continue;
8574 entry->GetID(&id);
8575 if (id == targetID) {
8576 destEntry.swap(entry);
8577 break;
8581 } else {
8582 destEntry = destTreeRoot;
8585 aShell->SwapHistoryEntries(aEntry, destEntry);
8587 // Now handle the children of aEntry.
8588 SwapEntriesData childData = { ignoreShell, destTreeRoot, destEntry };
8589 return WalkHistoryEntries(aEntry, aShell,
8590 SetChildHistoryEntry, &childData);
8594 static nsISHEntry*
8595 GetRootSHEntry(nsISHEntry *aEntry)
8597 nsCOMPtr<nsISHEntry> rootEntry = aEntry;
8598 nsISHEntry *result = nsnull;
8599 while (rootEntry) {
8600 result = rootEntry;
8601 result->GetParent(getter_AddRefs(rootEntry));
8604 return result;
8608 void
8609 nsDocShell::SetHistoryEntry(nsCOMPtr<nsISHEntry> *aPtr, nsISHEntry *aEntry)
8611 // We need to sync up the docshell and session history trees for
8612 // subframe navigation. If the load was in a subframe, we forward up to
8613 // the root docshell, which will then recursively sync up all docshells
8614 // to their corresponding entries in the new session history tree.
8615 // If we don't do this, then we can cache a content viewer on the wrong
8616 // cloned entry, and subsequently restore it at the wrong time.
8618 nsISHEntry *newRootEntry = GetRootSHEntry(aEntry);
8619 if (newRootEntry) {
8620 // newRootEntry is now the new root entry.
8621 // Find the old root entry as well.
8623 // Need a strong ref. on |oldRootEntry| so it isn't destroyed when
8624 // SetChildHistoryEntry() does SwapHistoryEntries() (bug 304639).
8625 nsCOMPtr<nsISHEntry> oldRootEntry = GetRootSHEntry(*aPtr);
8626 if (oldRootEntry) {
8627 nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
8628 GetSameTypeParent(getter_AddRefs(parentAsItem));
8629 nsCOMPtr<nsIDocShell> rootShell = do_QueryInterface(parentAsItem);
8630 if (rootShell) { // if we're the root just set it, nothing to swap
8631 SwapEntriesData data = { this, newRootEntry };
8632 nsIDocShell *rootIDocShell =
8633 static_cast<nsIDocShell*>(rootShell);
8634 nsDocShell *rootDocShell = static_cast<nsDocShell*>
8635 (rootIDocShell);
8637 #ifdef NS_DEBUG
8638 nsresult rv =
8639 #endif
8640 SetChildHistoryEntry(oldRootEntry, rootDocShell,
8641 0, &data);
8642 NS_ASSERTION(NS_SUCCEEDED(rv), "SetChildHistoryEntry failed");
8647 *aPtr = aEntry;
8651 nsresult
8652 nsDocShell::GetRootSessionHistory(nsISHistory ** aReturn)
8654 nsresult rv;
8656 nsCOMPtr<nsIDocShellTreeItem> root;
8657 //Get the root docshell
8658 rv = GetSameTypeRootTreeItem(getter_AddRefs(root));
8659 // QI to nsIWebNavigation
8660 nsCOMPtr<nsIWebNavigation> rootAsWebnav(do_QueryInterface(root));
8661 if (rootAsWebnav) {
8662 // Get the handle to SH from the root docshell
8663 rv = rootAsWebnav->GetSessionHistory(aReturn);
8665 return rv;
8668 nsresult
8669 nsDocShell::GetHttpChannel(nsIChannel * aChannel, nsIHttpChannel ** aReturn)
8671 NS_ENSURE_ARG_POINTER(aReturn);
8672 if (!aChannel)
8673 return NS_ERROR_FAILURE;
8675 nsCOMPtr<nsIMultiPartChannel> multiPartChannel(do_QueryInterface(aChannel));
8676 if (multiPartChannel) {
8677 nsCOMPtr<nsIChannel> baseChannel;
8678 multiPartChannel->GetBaseChannel(getter_AddRefs(baseChannel));
8679 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(baseChannel));
8680 *aReturn = httpChannel;
8681 NS_IF_ADDREF(*aReturn);
8683 return NS_OK;
8686 PRBool
8687 nsDocShell::ShouldDiscardLayoutState(nsIHttpChannel * aChannel)
8689 // By default layout State will be saved.
8690 if (!aChannel)
8691 return PR_FALSE;
8693 // figure out if SH should be saving layout state
8694 nsCOMPtr<nsISupports> securityInfo;
8695 PRBool noStore = PR_FALSE, noCache = PR_FALSE;
8696 aChannel->GetSecurityInfo(getter_AddRefs(securityInfo));
8697 aChannel->IsNoStoreResponse(&noStore);
8698 aChannel->IsNoCacheResponse(&noCache);
8700 return (noStore || (noCache && securityInfo));
8703 //*****************************************************************************
8704 // nsDocShell: nsIEditorDocShell
8705 //*****************************************************************************
8707 NS_IMETHODIMP nsDocShell::GetEditor(nsIEditor * *aEditor)
8709 NS_ENSURE_ARG_POINTER(aEditor);
8711 if (!mEditorData) {
8712 *aEditor = nsnull;
8713 return NS_OK;
8716 return mEditorData->GetEditor(aEditor);
8719 NS_IMETHODIMP nsDocShell::SetEditor(nsIEditor * aEditor)
8721 nsresult rv = EnsureEditorData();
8722 if (NS_FAILED(rv)) return rv;
8724 return mEditorData->SetEditor(aEditor);
8728 NS_IMETHODIMP nsDocShell::GetEditable(PRBool *aEditable)
8730 NS_ENSURE_ARG_POINTER(aEditable);
8731 *aEditable = mEditorData && mEditorData->GetEditable();
8732 return NS_OK;
8736 NS_IMETHODIMP nsDocShell::GetHasEditingSession(PRBool *aHasEditingSession)
8738 NS_ENSURE_ARG_POINTER(aHasEditingSession);
8740 if (mEditorData)
8742 nsCOMPtr<nsIEditingSession> editingSession;
8743 mEditorData->GetEditingSession(getter_AddRefs(editingSession));
8744 *aHasEditingSession = (editingSession.get() != nsnull);
8746 else
8748 *aHasEditingSession = PR_FALSE;
8751 return NS_OK;
8754 NS_IMETHODIMP nsDocShell::MakeEditable(PRBool inWaitForUriLoad)
8756 nsresult rv = EnsureEditorData();
8757 if (NS_FAILED(rv)) return rv;
8759 return mEditorData->MakeEditable(inWaitForUriLoad);
8762 nsresult
8763 nsDocShell::AddToGlobalHistory(nsIURI * aURI, PRBool aRedirect,
8764 nsIChannel * aChannel)
8766 if (mItemType != typeContent || !mGlobalHistory)
8767 return NS_OK;
8769 // If this is a POST request, we do not want to include this in global
8770 // history, so return early.
8771 nsCOMPtr<nsIHttpChannel> hchan(do_QueryInterface(aChannel));
8772 if (hchan) {
8773 nsCAutoString type;
8774 nsresult rv = hchan->GetRequestMethod(type);
8775 if (NS_SUCCEEDED(rv) && type.EqualsLiteral("POST"))
8776 return NS_OK;
8779 PRBool visited;
8780 nsresult rv = mGlobalHistory->IsVisited(aURI, &visited);
8781 if (NS_FAILED(rv))
8782 return rv;
8784 nsCOMPtr<nsIURI> referrer;
8785 if (aChannel)
8786 NS_GetReferrerFromChannel(aChannel, getter_AddRefs(referrer));
8788 rv = mGlobalHistory->AddURI(aURI, aRedirect, !IsFrame(), referrer);
8789 if (NS_FAILED(rv))
8790 return rv;
8792 if (!visited) {
8793 nsCOMPtr<nsIObserverService> obsService =
8794 do_GetService("@mozilla.org/observer-service;1");
8795 if (obsService) {
8796 obsService->NotifyObservers(aURI, NS_LINK_VISITED_EVENT_TOPIC, nsnull);
8800 return NS_OK;
8803 //*****************************************************************************
8804 // nsDocShell: Helper Routines
8805 //*****************************************************************************
8807 NS_IMETHODIMP
8808 nsDocShell::SetLoadType(PRUint32 aLoadType)
8810 mLoadType = aLoadType;
8811 return NS_OK;
8814 NS_IMETHODIMP
8815 nsDocShell::GetLoadType(PRUint32 * aLoadType)
8817 *aLoadType = mLoadType;
8818 return NS_OK;
8821 nsresult
8822 nsDocShell::ConfirmRepost(PRBool * aRepost)
8824 nsresult rv;
8825 nsCOMPtr<nsIPrompt> prompter;
8826 CallGetInterface(this, static_cast<nsIPrompt**>(getter_AddRefs(prompter)));
8828 nsCOMPtr<nsIStringBundleService>
8829 stringBundleService(do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv));
8830 NS_ENSURE_SUCCESS(rv, rv);
8832 nsCOMPtr<nsIStringBundle> appBundle;
8833 rv = stringBundleService->CreateBundle(kAppstringsBundleURL,
8834 getter_AddRefs(appBundle));
8835 NS_ENSURE_SUCCESS(rv, rv);
8837 nsCOMPtr<nsIStringBundle> brandBundle;
8838 rv = stringBundleService->CreateBundle(kBrandBundleURL, getter_AddRefs(brandBundle));
8839 NS_ENSURE_SUCCESS(rv, rv);
8841 NS_ASSERTION(prompter && brandBundle && appBundle,
8842 "Unable to set up repost prompter.");
8844 nsXPIDLString brandName;
8845 rv = brandBundle->GetStringFromName(NS_LITERAL_STRING("brandShortName").get(),
8846 getter_Copies(brandName));
8848 nsXPIDLString msgString, button0Title;
8849 if (NS_FAILED(rv)) { // No brand, use the generic version.
8850 rv = appBundle->GetStringFromName(NS_LITERAL_STRING("confirmRepostPrompt").get(),
8851 getter_Copies(msgString));
8853 else {
8854 // Brand available - if the app has an override file with formatting, the app name will
8855 // be included. Without an override, the prompt will look like the generic version.
8856 const PRUnichar *formatStrings[] = { brandName.get() };
8857 rv = appBundle->FormatStringFromName(NS_LITERAL_STRING("confirmRepostPrompt").get(),
8858 formatStrings, NS_ARRAY_LENGTH(formatStrings),
8859 getter_Copies(msgString));
8861 if (NS_FAILED(rv)) return rv;
8863 rv = appBundle->GetStringFromName(NS_LITERAL_STRING("resendButton.label").get(),
8864 getter_Copies(button0Title));
8865 if (NS_FAILED(rv)) return rv;
8867 PRInt32 buttonPressed;
8868 rv = prompter->
8869 ConfirmEx(nsnull, msgString.get(),
8870 (nsIPrompt::BUTTON_POS_0 * nsIPrompt::BUTTON_TITLE_IS_STRING) +
8871 (nsIPrompt::BUTTON_POS_1 * nsIPrompt::BUTTON_TITLE_CANCEL),
8872 button0Title.get(), nsnull, nsnull, nsnull, nsnull, &buttonPressed);
8873 if (NS_FAILED(rv)) return rv;
8875 *aRepost = (buttonPressed == 0);
8876 return NS_OK;
8879 NS_IMETHODIMP
8880 nsDocShell::GetPromptAndStringBundle(nsIPrompt ** aPrompt,
8881 nsIStringBundle ** aStringBundle)
8883 NS_ENSURE_SUCCESS(GetInterface(NS_GET_IID(nsIPrompt), (void **) aPrompt),
8884 NS_ERROR_FAILURE);
8886 nsCOMPtr<nsIStringBundleService>
8887 stringBundleService(do_GetService(NS_STRINGBUNDLE_CONTRACTID));
8888 NS_ENSURE_TRUE(stringBundleService, NS_ERROR_FAILURE);
8890 NS_ENSURE_SUCCESS(stringBundleService->
8891 CreateBundle(kAppstringsBundleURL,
8892 aStringBundle),
8893 NS_ERROR_FAILURE);
8895 return NS_OK;
8898 NS_IMETHODIMP
8899 nsDocShell::GetChildOffset(nsIDOMNode * aChild, nsIDOMNode * aParent,
8900 PRInt32 * aOffset)
8902 NS_ENSURE_ARG_POINTER(aChild || aParent);
8904 nsCOMPtr<nsIDOMNodeList> childNodes;
8905 NS_ENSURE_SUCCESS(aParent->GetChildNodes(getter_AddRefs(childNodes)),
8906 NS_ERROR_FAILURE);
8907 NS_ENSURE_TRUE(childNodes, NS_ERROR_FAILURE);
8909 PRInt32 i = 0;
8911 for (; PR_TRUE; i++) {
8912 nsCOMPtr<nsIDOMNode> childNode;
8913 NS_ENSURE_SUCCESS(childNodes->Item(i, getter_AddRefs(childNode)),
8914 NS_ERROR_FAILURE);
8915 NS_ENSURE_TRUE(childNode, NS_ERROR_FAILURE);
8917 if (childNode.get() == aChild) {
8918 *aOffset = i;
8919 return NS_OK;
8923 return NS_ERROR_FAILURE;
8926 NS_IMETHODIMP
8927 nsDocShell::GetRootScrollableView(nsIScrollableView ** aOutScrollView)
8929 NS_ENSURE_ARG_POINTER(aOutScrollView);
8931 nsCOMPtr<nsIPresShell> shell;
8932 NS_ENSURE_SUCCESS(GetPresShell(getter_AddRefs(shell)), NS_ERROR_FAILURE);
8933 NS_ENSURE_TRUE(shell, NS_ERROR_NULL_POINTER);
8935 NS_ENSURE_SUCCESS(shell->GetViewManager()->GetRootScrollableView(aOutScrollView),
8936 NS_ERROR_FAILURE);
8938 if (*aOutScrollView == nsnull) {
8939 return NS_ERROR_FAILURE;
8941 return NS_OK;
8944 #ifdef DEBUG
8945 class nsDebugAutoBoolTrueSetter
8947 public:
8948 nsDebugAutoBoolTrueSetter(PRBool *aBool)
8949 : mBool(aBool)
8951 *mBool = PR_TRUE;
8954 ~nsDebugAutoBoolTrueSetter()
8956 *mBool = PR_FALSE;
8958 protected:
8959 PRBool *mBool;
8961 #endif
8963 NS_IMETHODIMP
8964 nsDocShell::EnsureScriptEnvironment()
8966 if (mScriptGlobal)
8967 return NS_OK;
8969 if (mIsBeingDestroyed) {
8970 return NS_ERROR_NOT_AVAILABLE;
8973 #ifdef DEBUG
8974 NS_ASSERTION(!mInEnsureScriptEnv,
8975 "Infinite loop! Calling EnsureScriptEnvironment() from "
8976 "within EnsureScriptEnvironment()!");
8978 // Yeah, this isn't re-entrant safe, but that's ok since if we
8979 // re-enter this method, we'll infinitely loop...
8980 nsDebugAutoBoolTrueSetter boolSetter(&mInEnsureScriptEnv);
8981 #endif
8983 nsCOMPtr<nsIDOMScriptObjectFactory> factory =
8984 do_GetService(kDOMScriptObjectFactoryCID);
8985 NS_ENSURE_TRUE(factory, NS_ERROR_FAILURE);
8987 nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(mTreeOwner));
8988 NS_ENSURE_TRUE(browserChrome, NS_ERROR_NOT_AVAILABLE);
8990 PRUint32 chromeFlags;
8991 browserChrome->GetChromeFlags(&chromeFlags);
8993 PRBool isModalContentWindow =
8994 (chromeFlags & nsIWebBrowserChrome::CHROME_MODAL) &&
8995 !(chromeFlags & nsIWebBrowserChrome::CHROME_OPENAS_CHROME);
8997 // If our window is modal and we're not opened as chrome, make
8998 // this window a modal content window.
8999 factory->NewScriptGlobalObject(mItemType == typeChrome,
9000 isModalContentWindow,
9001 getter_AddRefs(mScriptGlobal));
9002 NS_ENSURE_TRUE(mScriptGlobal, NS_ERROR_FAILURE);
9004 nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(mScriptGlobal));
9005 win->SetDocShell(static_cast<nsIDocShell *>(this));
9007 // Ensure the script object is set to run javascript - other languages
9008 // setup on demand.
9009 // XXXmarkh - should this be setup to run the default language for this doc?
9010 nsresult rv;
9011 rv = mScriptGlobal->EnsureScriptEnvironment(nsIProgrammingLanguage::JAVASCRIPT);
9012 NS_ENSURE_SUCCESS(rv, rv);
9014 return NS_OK;
9018 NS_IMETHODIMP
9019 nsDocShell::EnsureEditorData()
9021 PRBool openDocHasDetachedEditor = mOSHE && mOSHE->HasDetachedEditor();
9022 if (!mEditorData && !mIsBeingDestroyed && !openDocHasDetachedEditor) {
9023 // We shouldn't recreate the editor data if it already exists, or
9024 // we're shutting down, or we already have a detached editor data
9025 // stored in the session history. We should only have one editordata
9026 // per docshell.
9027 mEditorData = new nsDocShellEditorData(this);
9030 return mEditorData ? NS_OK : NS_ERROR_NOT_AVAILABLE;
9033 nsresult
9034 nsDocShell::EnsureTransferableHookData()
9036 if (!mTransferableHookData) {
9037 mTransferableHookData = new nsTransferableHookData();
9038 if (!mTransferableHookData) return NS_ERROR_OUT_OF_MEMORY;
9041 return NS_OK;
9045 NS_IMETHODIMP nsDocShell::EnsureFind()
9047 nsresult rv;
9048 if (!mFind)
9050 mFind = do_CreateInstance("@mozilla.org/embedcomp/find;1", &rv);
9051 if (NS_FAILED(rv)) return rv;
9054 // we promise that the nsIWebBrowserFind that we return has been set
9055 // up to point to the focused, or content window, so we have to
9056 // set that up each time.
9058 nsIScriptGlobalObject* scriptGO = GetScriptGlobalObject();
9059 NS_ENSURE_TRUE(scriptGO, NS_ERROR_UNEXPECTED);
9061 // default to our window
9062 nsCOMPtr<nsIDOMWindow> rootWindow = do_QueryInterface(scriptGO);
9063 nsCOMPtr<nsIDOMWindow> windowToSearch = rootWindow;
9065 // if we can, search the focused window
9066 nsCOMPtr<nsPIDOMWindow> ourWindow = do_QueryInterface(scriptGO);
9067 nsIFocusController *focusController = nsnull;
9068 if (ourWindow)
9069 focusController = ourWindow->GetRootFocusController();
9070 if (focusController)
9072 nsCOMPtr<nsIDOMWindowInternal> focusedWindow;
9073 focusController->GetFocusedWindow(getter_AddRefs(focusedWindow));
9074 if (focusedWindow)
9075 windowToSearch = focusedWindow;
9078 nsCOMPtr<nsIWebBrowserFindInFrames> findInFrames = do_QueryInterface(mFind);
9079 if (!findInFrames) return NS_ERROR_NO_INTERFACE;
9081 rv = findInFrames->SetRootSearchFrame(rootWindow);
9082 if (NS_FAILED(rv)) return rv;
9083 rv = findInFrames->SetCurrentSearchFrame(windowToSearch);
9084 if (NS_FAILED(rv)) return rv;
9086 return NS_OK;
9089 PRBool
9090 nsDocShell::IsFrame()
9092 nsCOMPtr<nsIDocShellTreeItem> parent =
9093 do_QueryInterface(GetAsSupports(mParent));
9094 if (parent) {
9095 PRInt32 parentType = ~mItemType; // Not us
9096 parent->GetItemType(&parentType);
9097 if (parentType == mItemType) // This is a frame
9098 return PR_TRUE;
9101 return PR_FALSE;
9104 NS_IMETHODIMP
9105 nsDocShell::GetHasFocus(PRBool *aHasFocus)
9107 *aHasFocus = mHasFocus;
9108 return NS_OK;
9111 NS_IMETHODIMP
9112 nsDocShell::SetHasFocus(PRBool aHasFocus)
9114 #ifdef DEBUG_DOCSHELL_FOCUS
9115 printf(">>>>>>>>>> nsDocShell::SetHasFocus: %p %s\n", (void*)this,
9116 aHasFocus?"Yes":"No");
9117 #endif
9119 mHasFocus = aHasFocus;
9121 nsDocShellFocusController* dsfc = nsDocShellFocusController::GetInstance();
9122 if (dsfc && aHasFocus) {
9123 dsfc->Focus(this);
9126 if (!aHasFocus) {
9127 // We may be in a situation where the focus outline was shown
9128 // on this document because the user tabbed into it, but the focus
9129 // is now switching to another document via a click. In this case,
9130 // we need to make sure the focus outline is removed from this document.
9131 SetCanvasHasFocus(PR_FALSE);
9134 return NS_OK;
9137 // Find an nsICanvasFrame under aFrame. Only search the principal
9138 // child lists. aFrame must be non-null.
9139 static nsICanvasFrame* FindCanvasFrame(nsIFrame* aFrame)
9141 nsICanvasFrame* canvasFrame;
9142 if (NS_SUCCEEDED(CallQueryInterface(aFrame, &canvasFrame))) {
9143 return canvasFrame;
9146 nsIFrame* kid = aFrame->GetFirstChild(nsnull);
9147 while (kid) {
9148 canvasFrame = FindCanvasFrame(kid);
9149 if (canvasFrame) {
9150 return canvasFrame;
9152 kid = kid->GetNextSibling();
9155 return nsnull;
9158 //-------------------------------------------------------
9159 // Tells the HTMLFrame/CanvasFrame that is now has focus
9160 NS_IMETHODIMP
9161 nsDocShell::SetCanvasHasFocus(PRBool aCanvasHasFocus)
9163 if (mEditorData && mEditorData->GetEditable())
9164 return NS_ERROR_NOT_AVAILABLE;
9166 nsCOMPtr<nsIPresShell> presShell;
9167 GetPresShell(getter_AddRefs(presShell));
9168 if (!presShell) return NS_ERROR_FAILURE;
9170 nsIDocument *doc = presShell->GetDocument();
9171 if (!doc) return NS_ERROR_FAILURE;
9173 nsIContent *rootContent = doc->GetRootContent();
9174 if (rootContent) {
9175 nsIFrame* frame = presShell->GetPrimaryFrameFor(rootContent);
9176 if (frame) {
9177 frame = frame->GetParent();
9178 if (frame) {
9179 nsICanvasFrame* canvasFrame;
9180 if (NS_SUCCEEDED(CallQueryInterface(frame, &canvasFrame))) {
9181 return canvasFrame->SetHasFocus(aCanvasHasFocus);
9185 } else {
9186 // Look for the frame the hard way
9187 nsIFrame* frame = presShell->GetRootFrame();
9188 if (frame) {
9189 nsICanvasFrame* canvasFrame = FindCanvasFrame(frame);
9190 if (canvasFrame) {
9191 return canvasFrame->SetHasFocus(aCanvasHasFocus);
9196 return NS_ERROR_FAILURE;
9199 NS_IMETHODIMP
9200 nsDocShell::GetCanvasHasFocus(PRBool *aCanvasHasFocus)
9202 return NS_ERROR_FAILURE;
9205 /* boolean IsBeingDestroyed (); */
9206 NS_IMETHODIMP
9207 nsDocShell::IsBeingDestroyed(PRBool *aDoomed)
9209 NS_ENSURE_ARG(aDoomed);
9210 *aDoomed = mIsBeingDestroyed;
9211 return NS_OK;
9215 NS_IMETHODIMP
9216 nsDocShell::GetIsExecutingOnLoadHandler(PRBool *aResult)
9218 NS_ENSURE_ARG(aResult);
9219 *aResult = mIsExecutingOnLoadHandler;
9220 return NS_OK;
9223 NS_IMETHODIMP
9224 nsDocShell::GetLayoutHistoryState(nsILayoutHistoryState **aLayoutHistoryState)
9226 if (mOSHE)
9227 mOSHE->GetLayoutHistoryState(aLayoutHistoryState);
9228 return NS_OK;
9231 NS_IMETHODIMP
9232 nsDocShell::SetLayoutHistoryState(nsILayoutHistoryState *aLayoutHistoryState)
9234 if (mOSHE)
9235 mOSHE->SetLayoutHistoryState(aLayoutHistoryState);
9236 return NS_OK;
9239 //*****************************************************************************
9240 //*** nsRefreshTimer: Object Management
9241 //*****************************************************************************
9243 nsRefreshTimer::nsRefreshTimer()
9244 : mDelay(0), mRepeat(PR_FALSE), mMetaRefresh(PR_FALSE)
9248 nsRefreshTimer::~nsRefreshTimer()
9252 //*****************************************************************************
9253 // nsRefreshTimer::nsISupports
9254 //*****************************************************************************
9256 NS_IMPL_THREADSAFE_ADDREF(nsRefreshTimer)
9257 NS_IMPL_THREADSAFE_RELEASE(nsRefreshTimer)
9259 NS_INTERFACE_MAP_BEGIN(nsRefreshTimer)
9260 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITimerCallback)
9261 NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
9262 NS_INTERFACE_MAP_END_THREADSAFE
9264 ///*****************************************************************************
9265 // nsRefreshTimer::nsITimerCallback
9266 //******************************************************************************
9267 NS_IMETHODIMP
9268 nsRefreshTimer::Notify(nsITimer * aTimer)
9270 NS_ASSERTION(mDocShell, "DocShell is somehow null");
9272 if (mDocShell && aTimer) {
9273 // Get the delay count to determine load type
9274 PRUint32 delay = 0;
9275 aTimer->GetDelay(&delay);
9276 nsCOMPtr<nsIRefreshURI> refreshURI = do_QueryInterface(mDocShell);
9277 if (refreshURI)
9278 refreshURI->ForceRefreshURI(mURI, delay, mMetaRefresh);
9280 return NS_OK;
9283 //*****************************************************************************
9284 //*** nsDocShellFocusController: Object Management
9285 //*****************************************************************************
9286 void
9287 nsDocShellFocusController::Focus(nsIDocShell* aDocShell)
9289 #ifdef DEBUG_DOCSHELL_FOCUS
9290 printf("****** nsDocShellFocusController Focus To: %p Blur To: %p\n",
9291 (void*)aDocShell, (void*)mFocusedDocShell);
9292 #endif
9294 if (aDocShell != mFocusedDocShell) {
9295 if (mFocusedDocShell) {
9296 mFocusedDocShell->SetHasFocus(PR_FALSE);
9298 mFocusedDocShell = aDocShell;
9303 //--------------------------------------------------
9304 // This is need for when the document with focus goes away
9305 void
9306 nsDocShellFocusController::ClosingDown(nsIDocShell* aDocShell)
9308 if (aDocShell == mFocusedDocShell) {
9309 mFocusedDocShell = nsnull;
9313 //*****************************************************************************
9314 // nsDocShell::InterfaceRequestorProxy
9315 //*****************************************************************************
9316 nsDocShell::InterfaceRequestorProxy::InterfaceRequestorProxy(nsIInterfaceRequestor* p)
9318 if (p) {
9319 mWeakPtr = do_GetWeakReference(p);
9323 nsDocShell::InterfaceRequestorProxy::~InterfaceRequestorProxy()
9325 mWeakPtr = nsnull;
9328 NS_IMPL_THREADSAFE_ISUPPORTS1(nsDocShell::InterfaceRequestorProxy, nsIInterfaceRequestor)
9330 NS_IMETHODIMP
9331 nsDocShell::InterfaceRequestorProxy::GetInterface(const nsIID & aIID, void **aSink)
9333 NS_ENSURE_ARG_POINTER(aSink);
9334 nsCOMPtr<nsIInterfaceRequestor> ifReq = do_QueryReferent(mWeakPtr);
9335 if (ifReq) {
9336 return ifReq->GetInterface(aIID, aSink);
9338 *aSink = nsnull;
9339 return NS_NOINTERFACE;
9342 nsresult
9343 nsDocShell::SetBaseUrlForWyciwyg(nsIContentViewer * aContentViewer)
9345 if (!aContentViewer)
9346 return NS_ERROR_FAILURE;
9348 nsCOMPtr<nsIURI> baseURI;
9349 nsCOMPtr<nsIDocument> document;
9350 nsresult rv = NS_ERROR_NOT_AVAILABLE;
9352 if (sURIFixup)
9353 rv = sURIFixup->CreateExposableURI(mCurrentURI,
9354 getter_AddRefs(baseURI));
9356 // Get the current document and set the base uri
9357 if (baseURI) {
9358 nsCOMPtr<nsIDocumentViewer> docViewer(do_QueryInterface(aContentViewer));
9359 if (docViewer) {
9360 rv = docViewer->GetDocument(getter_AddRefs(document));
9361 if (document)
9362 rv = document->SetBaseURI(baseURI);
9365 return rv;
9368 //*****************************************************************************
9369 // nsDocShell::nsIAuthPromptProvider
9370 //*****************************************************************************
9372 NS_IMETHODIMP
9373 nsDocShell::GetAuthPrompt(PRUint32 aPromptReason, const nsIID& iid,
9374 void** aResult)
9376 // a priority prompt request will override a false mAllowAuth setting
9377 PRBool priorityPrompt = (aPromptReason == PROMPT_PROXY);
9379 if (!mAllowAuth && !priorityPrompt)
9380 return NS_ERROR_NOT_AVAILABLE;
9382 // we're either allowing auth, or it's a proxy request
9383 nsresult rv;
9384 nsCOMPtr<nsIPromptFactory> wwatch =
9385 do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
9386 NS_ENSURE_SUCCESS(rv, rv);
9388 rv = EnsureScriptEnvironment();
9389 NS_ENSURE_SUCCESS(rv, rv);
9391 nsCOMPtr<nsIDOMWindow> window(do_QueryInterface(mScriptGlobal));
9393 // Get the an auth prompter for our window so that the parenting
9394 // of the dialogs works as it should when using tabs.
9396 return wwatch->GetPrompt(window, iid,
9397 reinterpret_cast<void**>(aResult));
9400 //*****************************************************************************
9401 // nsDocShell::nsIObserver
9402 //*****************************************************************************
9404 NS_IMETHODIMP
9405 nsDocShell::Observe(nsISupports *aSubject, const char *aTopic,
9406 const PRUnichar *aData)
9408 nsresult rv = NS_OK;
9409 if (mObserveErrorPages &&
9410 !nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) &&
9411 !nsCRT::strcmp(aData,
9412 NS_LITERAL_STRING("browser.xul.error_pages.enabled").get())) {
9414 nsCOMPtr<nsIPrefBranch> prefs(do_QueryInterface(aSubject, &rv));
9415 NS_ENSURE_SUCCESS(rv, rv);
9417 PRBool tmpbool;
9418 rv = prefs->GetBoolPref("browser.xul.error_pages.enabled", &tmpbool);
9419 if (NS_SUCCEEDED(rv))
9420 mUseErrorPages = tmpbool;
9422 } else {
9423 rv = NS_ERROR_UNEXPECTED;
9425 return rv;
9428 /* static */
9429 nsresult
9430 nsDocShell::URIInheritsSecurityContext(nsIURI* aURI, PRBool* aResult)
9432 // Note: about:blank URIs do NOT inherit the security context from the
9433 // current document, which is what this function tests for...
9434 return NS_URIChainHasFlags(aURI,
9435 nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT,
9436 aResult);
9439 /* static */
9440 PRBool
9441 nsDocShell::URIIsLocalFile(nsIURI *aURI)
9443 PRBool isFile;
9444 nsCOMPtr<nsINetUtil> util = do_GetIOService();
9446 return util && NS_SUCCEEDED(util->ProtocolHasFlags(aURI,
9447 nsIProtocolHandler::URI_IS_LOCAL_FILE,
9448 &isFile)) &&
9449 isFile;
9452 /* static */
9453 PRBool
9454 nsDocShell::IsAboutBlank(nsIURI* aURI)
9456 NS_PRECONDITION(aURI, "Must have URI");
9458 // GetSpec can be expensive for some URIs, so check the scheme first.
9459 PRBool isAbout = PR_FALSE;
9460 if (NS_FAILED(aURI->SchemeIs("about", &isAbout)) || !isAbout) {
9461 return PR_FALSE;
9464 nsCAutoString str;
9465 aURI->GetSpec(str);
9466 return str.EqualsLiteral("about:blank");
9469 PRBool
9470 nsDocShell::IsOKToLoadURI(nsIURI* aURI)
9472 NS_PRECONDITION(aURI, "Must have a URI!");
9474 if (!mFiredUnloadEvent) {
9475 return PR_TRUE;
9478 if (!mLoadingURI) {
9479 return PR_FALSE;
9482 nsCOMPtr<nsIScriptSecurityManager> secMan =
9483 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
9484 return
9485 secMan &&
9486 NS_SUCCEEDED(secMan->CheckSameOriginURI(aURI, mLoadingURI, PR_FALSE));
9489 //*****************************************************************************
9490 // nsClassifierCallback
9491 //*****************************************************************************
9493 NS_IMPL_ISUPPORTS3(nsClassifierCallback,
9494 nsIChannelClassifier,
9495 nsIURIClassifierCallback,
9496 nsIRunnable)
9498 NS_IMETHODIMP
9499 nsClassifierCallback::Run()
9501 if (!mChannel) {
9502 return NS_OK;
9505 NS_ASSERTION(!mSuspendedChannel,
9506 "nsClassifierCallback::Run() called while a "
9507 "channel is still suspended.");
9509 nsCOMPtr<nsIChannel> channel;
9510 channel.swap(mChannel);
9512 // Don't bother to run the classifier on a load that has already failed.
9513 // (this might happen after a redirect)
9514 PRUint32 status;
9515 channel->GetStatus(&status);
9516 if (NS_FAILED(status))
9517 return NS_OK;
9519 // Don't bother to run the classifier on a cached load that was
9520 // previously classified.
9521 if (HasBeenClassified()) {
9522 return NS_OK;
9525 nsCOMPtr<nsIURI> uri;
9526 nsresult rv = channel->GetURI(getter_AddRefs(uri));
9527 NS_ENSURE_SUCCESS(rv, rv);
9529 // Don't bother checking certain types of URIs.
9530 PRBool hasFlags;
9531 rv = NS_URIChainHasFlags(uri,
9532 nsIProtocolHandler::URI_DANGEROUS_TO_LOAD,
9533 &hasFlags);
9534 NS_ENSURE_SUCCESS(rv, rv);
9535 if (hasFlags) return NS_OK;
9537 rv = NS_URIChainHasFlags(uri,
9538 nsIProtocolHandler::URI_IS_LOCAL_FILE,
9539 &hasFlags);
9540 NS_ENSURE_SUCCESS(rv, rv);
9541 if (hasFlags) return NS_OK;
9543 rv = NS_URIChainHasFlags(uri,
9544 nsIProtocolHandler::URI_IS_UI_RESOURCE,
9545 &hasFlags);
9546 NS_ENSURE_SUCCESS(rv, rv);
9547 if (hasFlags) return NS_OK;
9549 nsCOMPtr<nsIURIClassifier> uriClassifier =
9550 do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID, &rv);
9551 if (NS_FAILED(rv)) return rv;
9553 PRBool expectCallback;
9554 rv = uriClassifier->Classify(uri, this, &expectCallback);
9555 if (NS_FAILED(rv)) return rv;
9557 if (expectCallback) {
9558 // Suspend the channel, it will be resumed when we get the classifier
9559 // callback.
9560 rv = channel->Suspend();
9561 if (NS_FAILED(rv)) {
9562 // Some channels (including nsJSChannel) fail on Suspend. This
9563 // shouldn't be fatal, but will prevent malware from being
9564 // blocked on these channels.
9565 return NS_OK;
9568 mSuspendedChannel = channel;
9569 #ifdef DEBUG
9570 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
9571 ("nsClassifierCallback[%p]: suspended channel %p",
9572 this, mSuspendedChannel.get()));
9573 #endif
9576 return NS_OK;
9579 // Note in the cache entry that this URL was classified, so that future
9580 // cached loads don't need to be checked.
9581 void
9582 nsClassifierCallback::MarkEntryClassified(nsresult status)
9584 nsCOMPtr<nsICachingChannel> cachingChannel =
9585 do_QueryInterface(mSuspendedChannel);
9586 if (!cachingChannel) {
9587 return;
9590 nsCOMPtr<nsISupports> cacheToken;
9591 cachingChannel->GetCacheToken(getter_AddRefs(cacheToken));
9592 if (!cacheToken) {
9593 return;
9596 nsCOMPtr<nsICacheEntryDescriptor> cacheEntry =
9597 do_QueryInterface(cacheToken);
9598 if (!cacheEntry) {
9599 return;
9602 cacheEntry->SetMetaDataElement("docshell:classified",
9603 NS_SUCCEEDED(status) ? "1" : nsnull);
9606 PRBool
9607 nsClassifierCallback::HasBeenClassified()
9609 nsCOMPtr<nsICachingChannel> cachingChannel =
9610 do_QueryInterface(mSuspendedChannel);
9611 if (!cachingChannel) {
9612 return PR_FALSE;
9615 // Only check the tag if we are loading from the cache without
9616 // validation.
9617 PRBool fromCache;
9618 if (NS_FAILED(cachingChannel->IsFromCache(&fromCache)) || !fromCache) {
9619 return PR_FALSE;
9622 nsCOMPtr<nsISupports> cacheToken;
9623 cachingChannel->GetCacheToken(getter_AddRefs(cacheToken));
9624 if (!cacheToken) {
9625 return PR_FALSE;
9628 nsCOMPtr<nsICacheEntryDescriptor> cacheEntry =
9629 do_QueryInterface(cacheToken);
9630 if (!cacheEntry) {
9631 return PR_FALSE;
9634 nsXPIDLCString tag;
9635 cacheEntry->GetMetaDataElement("docshell:classified", getter_Copies(tag));
9636 return tag.EqualsLiteral("1");
9639 NS_IMETHODIMP
9640 nsClassifierCallback::OnClassifyComplete(nsresult aErrorCode)
9642 if (mSuspendedChannel) {
9643 MarkEntryClassified(aErrorCode);
9645 if (NS_FAILED(aErrorCode)) {
9646 #ifdef DEBUG
9647 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
9648 ("nsClassifierCallback[%p]: cancelling channel %p with error code: %d",
9649 this, mSuspendedChannel.get(), aErrorCode));
9650 #endif
9651 mSuspendedChannel->Cancel(aErrorCode);
9653 #ifdef DEBUG
9654 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
9655 ("nsClassifierCallback[%p]: resuming channel %p from OnClassifyComplete",
9656 this, mSuspendedChannel.get()));
9657 #endif
9658 mSuspendedChannel->Resume();
9659 mSuspendedChannel = nsnull;
9662 return NS_OK;
9665 NS_IMETHODIMP
9666 nsClassifierCallback::Start(nsIChannel *aChannel)
9668 mChannel = aChannel;
9669 return Run();
9672 NS_IMETHODIMP
9673 nsClassifierCallback::OnRedirect(nsIChannel *aOldChannel,
9674 nsIChannel *aNewChannel)
9676 mChannel = aNewChannel;
9678 // we call the Run() from the main loop to give the channel a
9679 // chance to AsyncOpen() before we suspend it.
9680 NS_DispatchToCurrentThread(this);
9682 return NS_OK;
9685 NS_IMETHODIMP
9686 nsClassifierCallback::Cancel()
9688 if (mSuspendedChannel) {
9689 #ifdef DEBUG
9690 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
9691 ("nsClassifierCallback[%p]: resuming channel %p from Cancel()",
9692 this, mSuspendedChannel.get()));
9693 #endif
9694 mSuspendedChannel->Resume();
9695 mSuspendedChannel = nsnull;
9698 if (mChannel) {
9699 mChannel = nsnull;
9702 return NS_OK;