Bug 570258: Some more atom usage cleanup. r=jst
[mozilla-central.git] / docshell / base / nsDocShell.cpp
blobabf488f665f17db3a194fff9015c26debfd717c9
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 "mozilla/dom/Element.h"
53 #include "nsIDocument.h"
54 #include "nsIDOMDocument.h"
55 #include "nsIDOM3Document.h"
56 #include "nsIDOMNSDocument.h"
57 #include "nsIDOMElement.h"
58 #include "nsIDOMStorageObsolete.h"
59 #include "nsIDOMStorage.h"
60 #include "nsPIDOMStorage.h"
61 #include "nsIDocumentViewer.h"
62 #include "nsIDocumentLoaderFactory.h"
63 #include "nsCURILoader.h"
64 #include "nsURILoader.h"
65 #include "nsDocShellCID.h"
66 #include "nsLayoutCID.h"
67 #include "nsDOMCID.h"
68 #include "nsIDOMScriptObjectFactory.h"
69 #include "nsNetUtil.h"
70 #include "nsRect.h"
71 #include "prprf.h"
72 #include "nsIMarkupDocumentViewer.h"
73 #include "nsXPIDLString.h"
74 #include "nsReadableUtils.h"
75 #include "nsIDOMEventTarget.h"
76 #include "nsIDOMChromeWindow.h"
77 #include "nsIDOMWindowInternal.h"
78 #include "nsIWebBrowserChrome.h"
79 #include "nsPoint.h"
80 #include "nsGfxCIID.h"
81 #include "nsIObserverService.h"
82 #include "nsIPrompt.h"
83 #include "nsIAuthPrompt.h"
84 #include "nsIAuthPrompt2.h"
85 #include "nsTextFormatter.h"
86 #include "nsIChannelEventSink.h"
87 #include "nsIUploadChannel.h"
88 #include "nsISecurityEventSink.h"
89 #include "mozilla/FunctionTimer.h"
90 #include "nsIScriptSecurityManager.h"
91 #include "nsIJSContextStack.h"
92 #include "nsIScriptObjectPrincipal.h"
93 #include "nsDocumentCharsetInfoCID.h"
94 #include "nsIScrollableFrame.h"
95 #include "nsContentPolicyUtils.h" // NS_CheckContentLoadPolicy(...)
96 #include "nsICategoryManager.h"
97 #include "nsXPCOMCID.h"
98 #include "nsISeekableStream.h"
99 #include "nsAutoPtr.h"
100 #include "nsIPrefService.h"
101 #include "nsIPrefBranch.h"
102 #include "nsIPrefBranch2.h"
103 #include "nsIWritablePropertyBag2.h"
104 #include "nsIAppShell.h"
105 #include "nsWidgetsCID.h"
106 #include "nsDOMJSUtils.h"
107 #include "nsIInterfaceRequestorUtils.h"
108 #include "nsIView.h"
109 #include "nsIViewManager.h"
110 #include "nsIScriptChannel.h"
111 #include "nsIOfflineCacheUpdate.h"
112 #include "nsCPrefetchService.h"
113 #include "nsJSON.h"
115 // we want to explore making the document own the load group
116 // so we can associate the document URI with the load group.
117 // until this point, we have an evil hack:
118 #include "nsIHttpChannelInternal.h"
121 // Local Includes
122 #include "nsDocShell.h"
123 #include "nsDocShellLoadInfo.h"
124 #include "nsCDefaultURIFixup.h"
125 #include "nsDocShellEnumerator.h"
126 #include "nsSHistory.h"
127 #include "nsDocShellEditorData.h"
129 // Helper Classes
130 #include "nsDOMError.h"
131 #include "nsEscape.h"
133 // Interfaces Needed
134 #include "nsIUploadChannel.h"
135 #include "nsIProgressEventSink.h"
136 #include "nsIWebProgress.h"
137 #include "nsILayoutHistoryState.h"
138 #include "nsITimer.h"
139 #include "nsISHistoryInternal.h"
140 #include "nsIPrincipal.h"
141 #include "nsIFileURL.h"
142 #include "nsIHistoryEntry.h"
143 #include "nsISHistoryListener.h"
144 #include "nsIWindowWatcher.h"
145 #include "nsIPromptFactory.h"
146 #include "nsIObserver.h"
147 #include "nsINestedURI.h"
148 #include "nsITransportSecurityInfo.h"
149 #include "nsINSSErrorsService.h"
150 #include "nsIApplicationCache.h"
151 #include "nsIApplicationCacheChannel.h"
152 #include "nsIApplicationCacheContainer.h"
153 #include "nsIPermissionManager.h"
154 #include "nsStreamUtils.h"
155 #include "nsIController.h"
156 #include "nsPICommandUpdater.h"
157 #include "nsIDOMHTMLAnchorElement.h"
158 #include "nsIWebBrowserChrome2.h"
160 // Editor-related
161 #include "nsIEditingSession.h"
163 #include "nsPIDOMWindow.h"
164 #include "nsPIWindowRoot.h"
165 #include "nsIDOMDocument.h"
166 #include "nsICachingChannel.h"
167 #include "nsICacheVisitor.h"
168 #include "nsICacheEntryDescriptor.h"
169 #include "nsIMultiPartChannel.h"
170 #include "nsIWyciwygChannel.h"
172 // The following are for bug #13871: Prevent frameset spoofing
173 #include "nsIHTMLDocument.h"
175 // For reporting errors with the console service.
176 // These can go away if error reporting is propagated up past nsDocShell.
177 #include "nsIConsoleService.h"
178 #include "nsIScriptError.h"
180 // used to dispatch urls to default protocol handlers
181 #include "nsCExternalHandlerService.h"
182 #include "nsIExternalProtocolService.h"
184 #include "nsFocusManager.h"
186 #include "nsITextToSubURI.h"
188 #include "nsIJARChannel.h"
190 #include "prlog.h"
191 #include "prmem.h"
193 #include "nsISelectionDisplay.h"
195 #include "nsIGlobalHistory2.h"
196 #include "nsIGlobalHistory3.h"
198 #ifdef DEBUG_DOCSHELL_FOCUS
199 #include "nsIEventStateManager.h"
200 #endif
202 #include "nsIFrame.h"
204 // for embedding
205 #include "nsIWebBrowserChromeFocus.h"
207 #if NS_PRINT_PREVIEW
208 #include "nsIDocumentViewerPrint.h"
209 #include "nsIWebBrowserPrint.h"
210 #endif
212 #include "nsPluginError.h"
214 static NS_DEFINE_CID(kDOMScriptObjectFactoryCID,
215 NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
216 static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
218 #if defined(DEBUG_bryner) || defined(DEBUG_chb)
219 //#define DEBUG_DOCSHELL_FOCUS
220 #define DEBUG_PAGE_CACHE
221 #endif
223 #include "nsContentErrors.h"
224 #include "nsIChannelPolicy.h"
225 #include "nsIContentSecurityPolicy.h"
227 using namespace mozilla;
229 // Number of documents currently loading
230 static PRInt32 gNumberOfDocumentsLoading = 0;
232 // Global count of existing docshells.
233 static PRInt32 gDocShellCount = 0;
235 // Global reference to the URI fixup service.
236 nsIURIFixup *nsDocShell::sURIFixup = 0;
238 // True means we validate window targets to prevent frameset
239 // spoofing. Initialize this to a non-bolean value so we know to check
240 // the pref on the creation of the first docshell.
241 static PRBool gValidateOrigin = (PRBool)0xffffffff;
243 // Hint for native dispatch of events on how long to delay after
244 // all documents have loaded in milliseconds before favoring normal
245 // native event dispatch priorites over performance
246 #define NS_EVENT_STARVATION_DELAY_HINT 2000
248 // This is needed for displaying an error message
249 // when navigation is attempted on a document when printing
250 // The value arbitrary as long as it doesn't conflict with
251 // any of the other values in the errors in DisplayLoadError
252 #define NS_ERROR_DOCUMENT_IS_PRINTMODE NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_GENERAL,2001)
254 #ifdef PR_LOGGING
255 #ifdef DEBUG
256 static PRLogModuleInfo* gDocShellLog;
257 #endif
258 static PRLogModuleInfo* gDocShellLeakLog;
259 #endif
261 const char kBrandBundleURL[] = "chrome://branding/locale/brand.properties";
262 const char kAppstringsBundleURL[] = "chrome://global/locale/appstrings.properties";
264 static void
265 FavorPerformanceHint(PRBool perfOverStarvation, PRUint32 starvationDelay)
267 nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
268 if (appShell)
269 appShell->FavorPerformanceHint(perfOverStarvation, starvationDelay);
272 //*****************************************************************************
273 // <a ping> support
274 //*****************************************************************************
276 #define PREF_PINGS_ENABLED "browser.send_pings"
277 #define PREF_PINGS_MAX_PER_LINK "browser.send_pings.max_per_link"
278 #define PREF_PINGS_REQUIRE_SAME_HOST "browser.send_pings.require_same_host"
280 // Check prefs to see if pings are enabled and if so what restrictions might
281 // be applied.
283 // @param maxPerLink
284 // This parameter returns the number of pings that are allowed per link click
286 // @param requireSameHost
287 // This parameter returns PR_TRUE if pings are restricted to the same host as
288 // the document in which the click occurs. If the same host restriction is
289 // imposed, then we still allow for pings to cross over to different
290 // protocols and ports for flexibility and because it is not possible to send
291 // a ping via FTP.
293 // @returns
294 // PR_TRUE if pings are enabled and PR_FALSE otherwise.
296 static PRBool
297 PingsEnabled(PRInt32 *maxPerLink, PRBool *requireSameHost)
299 PRBool allow = PR_FALSE;
301 *maxPerLink = 1;
302 *requireSameHost = PR_TRUE;
304 nsCOMPtr<nsIPrefBranch> prefs =
305 do_GetService(NS_PREFSERVICE_CONTRACTID);
306 if (prefs) {
307 PRBool val;
308 if (NS_SUCCEEDED(prefs->GetBoolPref(PREF_PINGS_ENABLED, &val)))
309 allow = val;
310 if (allow) {
311 prefs->GetIntPref(PREF_PINGS_MAX_PER_LINK, maxPerLink);
312 prefs->GetBoolPref(PREF_PINGS_REQUIRE_SAME_HOST, requireSameHost);
316 return allow;
319 static PRBool
320 CheckPingURI(nsIURI* uri, nsIContent* content)
322 if (!uri)
323 return PR_FALSE;
325 // Check with nsIScriptSecurityManager
326 nsCOMPtr<nsIScriptSecurityManager> ssmgr =
327 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
328 NS_ENSURE_TRUE(ssmgr, PR_FALSE);
330 nsresult rv =
331 ssmgr->CheckLoadURIWithPrincipal(content->NodePrincipal(), uri,
332 nsIScriptSecurityManager::STANDARD);
333 if (NS_FAILED(rv)) {
334 return PR_FALSE;
337 // Ignore non-HTTP(S)
338 PRBool match;
339 if ((NS_FAILED(uri->SchemeIs("http", &match)) || !match) &&
340 (NS_FAILED(uri->SchemeIs("https", &match)) || !match)) {
341 return PR_FALSE;
344 // Check with contentpolicy
345 PRInt16 shouldLoad = nsIContentPolicy::ACCEPT;
346 rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_PING,
347 uri,
348 content->NodePrincipal(),
349 content,
350 EmptyCString(), // mime hint
351 nsnull, //extra
352 &shouldLoad);
353 return NS_SUCCEEDED(rv) && NS_CP_ACCEPTED(shouldLoad);
356 typedef void (* ForEachPingCallback)(void *closure, nsIContent *content,
357 nsIURI *uri, nsIIOService *ios);
359 static void
360 ForEachPing(nsIContent *content, ForEachPingCallback callback, void *closure)
362 // NOTE: Using nsIDOMNSHTMLAnchorElement2::GetPing isn't really worth it here
363 // since we'd still need to parse the resulting string. Instead, we
364 // just parse the raw attribute. It might be nice if the content node
365 // implemented an interface that exposed an enumeration of nsIURIs.
367 // Make sure we are dealing with either an <A> or <AREA> element in the HTML
368 // or XHTML namespace.
369 if (!content->IsHTML())
370 return;
371 nsIAtom *nameAtom = content->Tag();
372 if (!nameAtom->Equals(NS_LITERAL_STRING("a")) &&
373 !nameAtom->Equals(NS_LITERAL_STRING("area")))
374 return;
376 nsCOMPtr<nsIAtom> pingAtom = do_GetAtom("ping");
377 if (!pingAtom)
378 return;
380 nsAutoString value;
381 content->GetAttr(kNameSpaceID_None, pingAtom, value);
382 if (value.IsEmpty())
383 return;
385 nsCOMPtr<nsIIOService> ios = do_GetIOService();
386 if (!ios)
387 return;
389 nsIDocument *doc = content->GetOwnerDoc();
390 if (!doc)
391 return;
393 // value contains relative URIs split on spaces (U+0020)
394 const PRUnichar *start = value.BeginReading();
395 const PRUnichar *end = value.EndReading();
396 const PRUnichar *iter = start;
397 for (;;) {
398 if (iter < end && *iter != ' ') {
399 ++iter;
400 } else { // iter is pointing at either end or a space
401 while (*start == ' ' && start < iter)
402 ++start;
403 if (iter != start) {
404 nsCOMPtr<nsIURI> uri, baseURI = content->GetBaseURI();
405 ios->NewURI(NS_ConvertUTF16toUTF8(Substring(start, iter)),
406 doc->GetDocumentCharacterSet().get(),
407 baseURI, getter_AddRefs(uri));
408 if (CheckPingURI(uri, content)) {
409 callback(closure, content, uri, ios);
412 start = iter = iter + 1;
413 if (iter >= end)
414 break;
419 //----------------------------------------------------------------------
421 // We wait this many milliseconds before killing the ping channel...
422 #define PING_TIMEOUT 10000
424 static void
425 OnPingTimeout(nsITimer *timer, void *closure)
427 nsILoadGroup *loadGroup = static_cast<nsILoadGroup *>(closure);
428 loadGroup->Cancel(NS_ERROR_ABORT);
429 loadGroup->Release();
432 // Check to see if two URIs have the same host or not
433 static PRBool
434 IsSameHost(nsIURI *uri1, nsIURI *uri2)
436 nsCAutoString host1, host2;
437 uri1->GetAsciiHost(host1);
438 uri2->GetAsciiHost(host2);
439 return host1.Equals(host2);
442 class nsPingListener : public nsIStreamListener
443 , public nsIInterfaceRequestor
444 , public nsIChannelEventSink
446 public:
447 NS_DECL_ISUPPORTS
448 NS_DECL_NSIREQUESTOBSERVER
449 NS_DECL_NSISTREAMLISTENER
450 NS_DECL_NSIINTERFACEREQUESTOR
451 NS_DECL_NSICHANNELEVENTSINK
453 nsPingListener(PRBool requireSameHost, nsIContent* content)
454 : mRequireSameHost(requireSameHost),
455 mContent(content)
458 private:
459 PRBool mRequireSameHost;
460 nsCOMPtr<nsIContent> mContent;
463 NS_IMPL_ISUPPORTS4(nsPingListener, nsIStreamListener, nsIRequestObserver,
464 nsIInterfaceRequestor, nsIChannelEventSink)
466 NS_IMETHODIMP
467 nsPingListener::OnStartRequest(nsIRequest *request, nsISupports *context)
469 return NS_OK;
472 NS_IMETHODIMP
473 nsPingListener::OnDataAvailable(nsIRequest *request, nsISupports *context,
474 nsIInputStream *stream, PRUint32 offset,
475 PRUint32 count)
477 PRUint32 result;
478 return stream->ReadSegments(NS_DiscardSegment, nsnull, count, &result);
481 NS_IMETHODIMP
482 nsPingListener::OnStopRequest(nsIRequest *request, nsISupports *context,
483 nsresult status)
485 return NS_OK;
488 NS_IMETHODIMP
489 nsPingListener::GetInterface(const nsIID &iid, void **result)
491 if (iid.Equals(NS_GET_IID(nsIChannelEventSink))) {
492 NS_ADDREF_THIS();
493 *result = (nsIChannelEventSink *) this;
494 return NS_OK;
497 return NS_ERROR_NO_INTERFACE;
500 NS_IMETHODIMP
501 nsPingListener::OnChannelRedirect(nsIChannel *oldChan, nsIChannel *newChan,
502 PRUint32 flags)
504 nsCOMPtr<nsIURI> newURI;
505 newChan->GetURI(getter_AddRefs(newURI));
507 if (!CheckPingURI(newURI, mContent))
508 return NS_ERROR_ABORT;
510 if (!mRequireSameHost)
511 return NS_OK;
513 // XXXbz should this be using something more like the nsContentUtils
514 // same-origin checker?
515 nsCOMPtr<nsIURI> oldURI;
516 oldChan->GetURI(getter_AddRefs(oldURI));
517 NS_ENSURE_STATE(oldURI && newURI);
519 if (!IsSameHost(oldURI, newURI))
520 return NS_ERROR_ABORT;
522 return NS_OK;
525 struct SendPingInfo {
526 PRInt32 numPings;
527 PRInt32 maxPings;
528 PRBool requireSameHost;
529 nsIURI *referrer;
532 static void
533 SendPing(void *closure, nsIContent *content, nsIURI *uri, nsIIOService *ios)
535 SendPingInfo *info = static_cast<SendPingInfo *>(closure);
536 if (info->numPings >= info->maxPings)
537 return;
539 if (info->requireSameHost) {
540 // Make sure the referrer and the given uri share the same origin. We
541 // only require the same hostname. The scheme and port may differ.
542 if (!IsSameHost(uri, info->referrer))
543 return;
546 nsIDocument *doc = content->GetOwnerDoc();
547 if (!doc)
548 return;
550 nsCOMPtr<nsIChannel> chan;
551 ios->NewChannelFromURI(uri, getter_AddRefs(chan));
552 if (!chan)
553 return;
555 // Don't bother caching the result of this URI load.
556 chan->SetLoadFlags(nsIRequest::INHIBIT_CACHING);
558 nsCOMPtr<nsIHttpChannel> httpChan = do_QueryInterface(chan);
559 if (!httpChan)
560 return;
562 // This is needed in order for 3rd-party cookie blocking to work.
563 nsCOMPtr<nsIHttpChannelInternal> httpInternal = do_QueryInterface(httpChan);
564 if (httpInternal)
565 httpInternal->SetDocumentURI(doc->GetDocumentURI());
567 if (info->referrer)
568 httpChan->SetReferrer(info->referrer);
570 httpChan->SetRequestMethod(NS_LITERAL_CSTRING("POST"));
572 // Remove extraneous request headers (to reduce request size)
573 httpChan->SetRequestHeader(NS_LITERAL_CSTRING("accept"),
574 EmptyCString(), PR_FALSE);
575 httpChan->SetRequestHeader(NS_LITERAL_CSTRING("accept-language"),
576 EmptyCString(), PR_FALSE);
577 httpChan->SetRequestHeader(NS_LITERAL_CSTRING("accept-charset"),
578 EmptyCString(), PR_FALSE);
579 httpChan->SetRequestHeader(NS_LITERAL_CSTRING("accept-encoding"),
580 EmptyCString(), PR_FALSE);
582 nsCOMPtr<nsIUploadChannel> uploadChan = do_QueryInterface(httpChan);
583 if (!uploadChan)
584 return;
586 // To avoid sending an unnecessary Content-Type header, we encode the
587 // closing portion of the headers in the POST body.
588 NS_NAMED_LITERAL_CSTRING(uploadData, "Content-Length: 0\r\n\r\n");
590 nsCOMPtr<nsIInputStream> uploadStream;
591 NS_NewPostDataStream(getter_AddRefs(uploadStream), PR_FALSE,
592 uploadData, 0);
593 if (!uploadStream)
594 return;
596 uploadChan->SetUploadStream(uploadStream, EmptyCString(), -1);
598 // The channel needs to have a loadgroup associated with it, so that we can
599 // cancel the channel and any redirected channels it may create.
600 nsCOMPtr<nsILoadGroup> loadGroup =
601 do_CreateInstance(NS_LOADGROUP_CONTRACTID);
602 if (!loadGroup)
603 return;
604 chan->SetLoadGroup(loadGroup);
606 // Construct a listener that merely discards any response. If successful at
607 // opening the channel, then it is not necessary to hold a reference to the
608 // channel. The networking subsystem will take care of that for us.
609 nsCOMPtr<nsIStreamListener> listener =
610 new nsPingListener(info->requireSameHost, content);
611 if (!listener)
612 return;
614 // Observe redirects as well:
615 nsCOMPtr<nsIInterfaceRequestor> callbacks = do_QueryInterface(listener);
616 NS_ASSERTION(callbacks, "oops");
617 loadGroup->SetNotificationCallbacks(callbacks);
619 chan->AsyncOpen(listener, nsnull);
621 // Even if AsyncOpen failed, we still count this as a successful ping. It's
622 // possible that AsyncOpen may have failed after triggering some background
623 // process that may have written something to the network.
624 info->numPings++;
626 // Prevent ping requests from stalling and never being garbage collected...
627 nsCOMPtr<nsITimer> timer =
628 do_CreateInstance(NS_TIMER_CONTRACTID);
629 if (timer) {
630 nsresult rv = timer->InitWithFuncCallback(OnPingTimeout, loadGroup,
631 PING_TIMEOUT,
632 nsITimer::TYPE_ONE_SHOT);
633 if (NS_SUCCEEDED(rv)) {
634 // When the timer expires, the callback function will release this
635 // reference to the loadgroup.
636 static_cast<nsILoadGroup *>(loadGroup.get())->AddRef();
637 loadGroup = 0;
641 // If we failed to setup the timer, then we should just cancel the channel
642 // because we won't be able to ensure that it goes away in a timely manner.
643 if (loadGroup)
644 chan->Cancel(NS_ERROR_ABORT);
647 // Spec: http://whatwg.org/specs/web-apps/current-work/#ping
648 static void
649 DispatchPings(nsIContent *content, nsIURI *referrer)
651 SendPingInfo info;
653 if (!PingsEnabled(&info.maxPings, &info.requireSameHost))
654 return;
655 if (info.maxPings == 0)
656 return;
658 info.numPings = 0;
659 info.referrer = referrer;
661 ForEachPing(content, SendPing, &info);
664 static nsISHEntry* GetRootSHEntry(nsISHEntry *entry);
666 //*****************************************************************************
667 //*** nsDocShell: Object Management
668 //*****************************************************************************
670 // Note: operator new zeros our memory
671 nsDocShell::nsDocShell():
672 nsDocLoader(),
673 mDefaultScrollbarPref(Scrollbar_Auto, Scrollbar_Auto),
674 mTreeOwner(nsnull),
675 mChromeEventHandler(nsnull),
676 mCharsetReloadState(eCharsetReloadInit),
677 mChildOffset(0),
678 mBusyFlags(BUSY_FLAGS_NONE),
679 mAppType(nsIDocShell::APP_TYPE_UNKNOWN),
680 mMarginWidth(-1),
681 mMarginHeight(-1),
682 mItemType(typeContent),
683 mPreviousTransIndex(-1),
684 mLoadedTransIndex(-1),
685 mAllowSubframes(PR_TRUE),
686 mAllowPlugins(PR_TRUE),
687 mAllowJavascript(PR_TRUE),
688 mAllowMetaRedirects(PR_TRUE),
689 mAllowImages(PR_TRUE),
690 mAllowDNSPrefetch(PR_TRUE),
691 mCreatingDocument(PR_FALSE),
692 mUseErrorPages(PR_FALSE),
693 mObserveErrorPages(PR_TRUE),
694 mAllowAuth(PR_TRUE),
695 mAllowKeywordFixup(PR_FALSE),
696 mIsOffScreenBrowser(PR_FALSE),
697 mFiredUnloadEvent(PR_FALSE),
698 mEODForCurrentDocument(PR_FALSE),
699 mURIResultedInDocument(PR_FALSE),
700 mIsBeingDestroyed(PR_FALSE),
701 mIsExecutingOnLoadHandler(PR_FALSE),
702 mIsPrintingOrPP(PR_FALSE),
703 mSavingOldViewer(PR_FALSE)
704 #ifdef DEBUG
705 , mInEnsureScriptEnv(PR_FALSE)
706 #endif
708 if (gDocShellCount++ == 0) {
709 NS_ASSERTION(sURIFixup == nsnull,
710 "Huh, sURIFixup not null in first nsDocShell ctor!");
712 CallGetService(NS_URIFIXUP_CONTRACTID, &sURIFixup);
715 #ifdef PR_LOGGING
716 #ifdef DEBUG
717 if (! gDocShellLog)
718 gDocShellLog = PR_NewLogModule("nsDocShell");
719 #endif
720 if (nsnull == gDocShellLeakLog)
721 gDocShellLeakLog = PR_NewLogModule("nsDocShellLeak");
722 if (gDocShellLeakLog)
723 PR_LOG(gDocShellLeakLog, PR_LOG_DEBUG, ("DOCSHELL %p created\n", this));
724 #endif
726 #ifdef DEBUG
727 // We're counting the number of |nsDocShells| to help find leaks
728 ++gNumberOfDocShells;
729 #endif
730 #ifdef DEBUG
731 printf("++DOCSHELL %p == %ld\n", (void*) this, gNumberOfDocShells);
732 #endif
735 nsDocShell::~nsDocShell()
737 Destroy();
739 if (--gDocShellCount == 0) {
740 NS_IF_RELEASE(sURIFixup);
743 #ifdef PR_LOGGING
744 if (gDocShellLeakLog)
745 PR_LOG(gDocShellLeakLog, PR_LOG_DEBUG, ("DOCSHELL %p destroyed\n", this));
746 #endif
748 #ifdef DEBUG
749 // We're counting the number of |nsDocShells| to help find leaks
750 --gNumberOfDocShells;
751 #endif
752 #ifdef DEBUG
753 printf("--DOCSHELL %p == %ld\n", (void*) this, gNumberOfDocShells);
754 #endif
757 nsresult
758 nsDocShell::Init()
760 nsresult rv = nsDocLoader::Init();
761 NS_ENSURE_SUCCESS(rv, rv);
763 NS_ASSERTION(mLoadGroup, "Something went wrong!");
765 mContentListener = new nsDSURIContentListener(this);
766 NS_ENSURE_TRUE(mContentListener, NS_ERROR_OUT_OF_MEMORY);
768 rv = mContentListener->Init();
769 NS_ENSURE_SUCCESS(rv, rv);
771 if (!mStorages.Init())
772 return NS_ERROR_OUT_OF_MEMORY;
774 // We want to hold a strong ref to the loadgroup, so it better hold a weak
775 // ref to us... use an InterfaceRequestorProxy to do this.
776 nsCOMPtr<InterfaceRequestorProxy> proxy =
777 new InterfaceRequestorProxy(static_cast<nsIInterfaceRequestor*>
778 (this));
779 NS_ENSURE_TRUE(proxy, NS_ERROR_OUT_OF_MEMORY);
780 mLoadGroup->SetNotificationCallbacks(proxy);
782 rv = nsDocLoader::AddDocLoaderAsChildOfRoot(this);
783 NS_ENSURE_SUCCESS(rv, rv);
785 // Add as |this| a progress listener to itself. A little weird, but
786 // simpler than reproducing all the listener-notification logic in
787 // overrides of the various methods via which nsDocLoader can be
788 // notified. Note that this holds an nsWeakPtr to ourselves, so it's ok.
789 return AddProgressListener(this, nsIWebProgress::NOTIFY_STATE_DOCUMENT |
790 nsIWebProgress::NOTIFY_STATE_NETWORK);
794 void
795 nsDocShell::DestroyChildren()
797 nsCOMPtr<nsIDocShellTreeItem> shell;
798 PRInt32 n = mChildList.Count();
799 for (PRInt32 i = 0; i < n; i++) {
800 shell = do_QueryInterface(ChildAt(i));
801 NS_ASSERTION(shell, "docshell has null child");
803 if (shell) {
804 shell->SetTreeOwner(nsnull);
808 nsDocLoader::DestroyChildren();
811 //*****************************************************************************
812 // nsDocShell::nsISupports
813 //*****************************************************************************
815 NS_IMPL_ADDREF_INHERITED(nsDocShell, nsDocLoader)
816 NS_IMPL_RELEASE_INHERITED(nsDocShell, nsDocLoader)
818 NS_INTERFACE_MAP_BEGIN(nsDocShell)
819 NS_INTERFACE_MAP_ENTRY(nsIDocShell)
820 NS_INTERFACE_MAP_ENTRY(nsIDocShellTreeItem)
821 NS_INTERFACE_MAP_ENTRY(nsIDocShellTreeNode)
822 NS_INTERFACE_MAP_ENTRY(nsIDocShellHistory)
823 NS_INTERFACE_MAP_ENTRY(nsIWebNavigation)
824 NS_INTERFACE_MAP_ENTRY(nsIBaseWindow)
825 NS_INTERFACE_MAP_ENTRY(nsIScrollable)
826 NS_INTERFACE_MAP_ENTRY(nsITextScroll)
827 NS_INTERFACE_MAP_ENTRY(nsIDocCharset)
828 NS_INTERFACE_MAP_ENTRY(nsIScriptGlobalObjectOwner)
829 NS_INTERFACE_MAP_ENTRY(nsIRefreshURI)
830 NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
831 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
832 NS_INTERFACE_MAP_ENTRY(nsIContentViewerContainer)
833 NS_INTERFACE_MAP_ENTRY(nsIEditorDocShell)
834 NS_INTERFACE_MAP_ENTRY(nsIWebPageDescriptor)
835 NS_INTERFACE_MAP_ENTRY(nsIAuthPromptProvider)
836 NS_INTERFACE_MAP_ENTRY(nsIObserver)
837 NS_INTERFACE_MAP_ENTRY(nsILoadContext)
838 NS_INTERFACE_MAP_ENTRY(nsIWebShellServices)
839 NS_INTERFACE_MAP_ENTRY(nsILinkHandler)
840 NS_INTERFACE_MAP_ENTRY(nsIClipboardCommands)
841 NS_INTERFACE_MAP_END_INHERITING(nsDocLoader)
843 ///*****************************************************************************
844 // nsDocShell::nsIInterfaceRequestor
845 //*****************************************************************************
846 NS_IMETHODIMP nsDocShell::GetInterface(const nsIID & aIID, void **aSink)
848 NS_PRECONDITION(aSink, "null out param");
850 *aSink = nsnull;
852 if (aIID.Equals(NS_GET_IID(nsICommandManager))) {
853 NS_ENSURE_SUCCESS(EnsureCommandHandler(), NS_ERROR_FAILURE);
854 *aSink = mCommandManager;
856 else if (aIID.Equals(NS_GET_IID(nsIURIContentListener))) {
857 *aSink = mContentListener;
859 else if (aIID.Equals(NS_GET_IID(nsIScriptGlobalObject)) &&
860 NS_SUCCEEDED(EnsureScriptEnvironment())) {
861 *aSink = mScriptGlobal;
863 else if ((aIID.Equals(NS_GET_IID(nsIDOMWindowInternal)) ||
864 aIID.Equals(NS_GET_IID(nsPIDOMWindow)) ||
865 aIID.Equals(NS_GET_IID(nsIDOMWindow))) &&
866 NS_SUCCEEDED(EnsureScriptEnvironment())) {
867 return mScriptGlobal->QueryInterface(aIID, aSink);
869 else if (aIID.Equals(NS_GET_IID(nsIDOMDocument)) &&
870 NS_SUCCEEDED(EnsureContentViewer())) {
871 mContentViewer->GetDOMDocument((nsIDOMDocument **) aSink);
872 return *aSink ? NS_OK : NS_NOINTERFACE;
874 else if (aIID.Equals(NS_GET_IID(nsIDocument)) &&
875 NS_SUCCEEDED(EnsureContentViewer())) {
876 nsCOMPtr<nsIDOMDocument> domDoc;
877 mContentViewer->GetDOMDocument(getter_AddRefs(domDoc));
878 if (!domDoc)
879 return NS_NOINTERFACE;
880 return domDoc->QueryInterface(aIID, aSink);
882 else if (aIID.Equals(NS_GET_IID(nsIApplicationCacheContainer))) {
883 *aSink = nsnull;
885 // Return application cache associated with this docshell, if any
887 nsCOMPtr<nsIContentViewer> contentViewer;
888 GetContentViewer(getter_AddRefs(contentViewer));
889 if (!contentViewer)
890 return NS_ERROR_NO_INTERFACE;
892 nsCOMPtr<nsIDOMDocument> domDoc;
893 contentViewer->GetDOMDocument(getter_AddRefs(domDoc));
894 NS_ASSERTION(domDoc, "Should have a document.");
895 if (!domDoc)
896 return NS_ERROR_NO_INTERFACE;
898 #if defined(PR_LOGGING) && defined(DEBUG)
899 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
900 ("nsDocShell[%p]: returning app cache container %p",
901 this, domDoc.get()));
902 #endif
903 return domDoc->QueryInterface(aIID, aSink);
905 else if (aIID.Equals(NS_GET_IID(nsIPrompt)) &&
906 NS_SUCCEEDED(EnsureScriptEnvironment())) {
907 nsresult rv;
908 nsCOMPtr<nsIWindowWatcher> wwatch =
909 do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
910 NS_ENSURE_SUCCESS(rv, rv);
912 nsCOMPtr<nsIDOMWindow> window(do_QueryInterface(mScriptGlobal));
914 // Get the an auth prompter for our window so that the parenting
915 // of the dialogs works as it should when using tabs.
917 nsIPrompt *prompt;
918 rv = wwatch->GetNewPrompter(window, &prompt);
919 NS_ENSURE_SUCCESS(rv, rv);
921 *aSink = prompt;
922 return NS_OK;
924 else if (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) ||
925 aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) {
926 return NS_SUCCEEDED(
927 GetAuthPrompt(PROMPT_NORMAL, aIID, aSink)) ?
928 NS_OK : NS_NOINTERFACE;
930 else if (aIID.Equals(NS_GET_IID(nsISHistory))) {
931 nsCOMPtr<nsISHistory> shistory;
932 nsresult
933 rv =
934 GetSessionHistory(getter_AddRefs(shistory));
935 if (NS_SUCCEEDED(rv) && shistory) {
936 *aSink = shistory;
937 NS_ADDREF((nsISupports *) * aSink);
938 return NS_OK;
940 return NS_NOINTERFACE;
942 else if (aIID.Equals(NS_GET_IID(nsIWebBrowserFind))) {
943 nsresult rv = EnsureFind();
944 if (NS_FAILED(rv)) return rv;
946 *aSink = mFind;
947 NS_ADDREF((nsISupports*)*aSink);
948 return NS_OK;
950 else if (aIID.Equals(NS_GET_IID(nsIEditingSession)) && NS_SUCCEEDED(EnsureEditorData())) {
951 nsCOMPtr<nsIEditingSession> editingSession;
952 mEditorData->GetEditingSession(getter_AddRefs(editingSession));
953 if (editingSession)
955 *aSink = editingSession;
956 NS_ADDREF((nsISupports *)*aSink);
957 return NS_OK;
960 return NS_NOINTERFACE;
962 else if (aIID.Equals(NS_GET_IID(nsIClipboardDragDropHookList))
963 && NS_SUCCEEDED(EnsureTransferableHookData())) {
964 *aSink = mTransferableHookData;
965 NS_ADDREF((nsISupports *)*aSink);
966 return NS_OK;
968 else if (aIID.Equals(NS_GET_IID(nsISelectionDisplay))) {
969 nsCOMPtr<nsIPresShell> shell;
970 nsresult rv = GetPresShell(getter_AddRefs(shell));
971 if (NS_SUCCEEDED(rv) && shell)
972 return shell->QueryInterface(aIID,aSink);
974 else if (aIID.Equals(NS_GET_IID(nsIDocShellTreeOwner))) {
975 nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
976 nsresult rv = GetTreeOwner(getter_AddRefs(treeOwner));
977 if (NS_SUCCEEDED(rv) && treeOwner)
978 return treeOwner->QueryInterface(aIID, aSink);
980 else {
981 return nsDocLoader::GetInterface(aIID, aSink);
984 NS_IF_ADDREF(((nsISupports *) * aSink));
985 return *aSink ? NS_OK : NS_NOINTERFACE;
988 PRUint32
989 nsDocShell::
990 ConvertDocShellLoadInfoToLoadType(nsDocShellInfoLoadType aDocShellLoadType)
992 PRUint32 loadType = LOAD_NORMAL;
994 switch (aDocShellLoadType) {
995 case nsIDocShellLoadInfo::loadNormal:
996 loadType = LOAD_NORMAL;
997 break;
998 case nsIDocShellLoadInfo::loadNormalReplace:
999 loadType = LOAD_NORMAL_REPLACE;
1000 break;
1001 case nsIDocShellLoadInfo::loadNormalExternal:
1002 loadType = LOAD_NORMAL_EXTERNAL;
1003 break;
1004 case nsIDocShellLoadInfo::loadHistory:
1005 loadType = LOAD_HISTORY;
1006 break;
1007 case nsIDocShellLoadInfo::loadNormalBypassCache:
1008 loadType = LOAD_NORMAL_BYPASS_CACHE;
1009 break;
1010 case nsIDocShellLoadInfo::loadNormalBypassProxy:
1011 loadType = LOAD_NORMAL_BYPASS_PROXY;
1012 break;
1013 case nsIDocShellLoadInfo::loadNormalBypassProxyAndCache:
1014 loadType = LOAD_NORMAL_BYPASS_PROXY_AND_CACHE;
1015 break;
1016 case nsIDocShellLoadInfo::loadReloadNormal:
1017 loadType = LOAD_RELOAD_NORMAL;
1018 break;
1019 case nsIDocShellLoadInfo::loadReloadCharsetChange:
1020 loadType = LOAD_RELOAD_CHARSET_CHANGE;
1021 break;
1022 case nsIDocShellLoadInfo::loadReloadBypassCache:
1023 loadType = LOAD_RELOAD_BYPASS_CACHE;
1024 break;
1025 case nsIDocShellLoadInfo::loadReloadBypassProxy:
1026 loadType = LOAD_RELOAD_BYPASS_PROXY;
1027 break;
1028 case nsIDocShellLoadInfo::loadReloadBypassProxyAndCache:
1029 loadType = LOAD_RELOAD_BYPASS_PROXY_AND_CACHE;
1030 break;
1031 case nsIDocShellLoadInfo::loadLink:
1032 loadType = LOAD_LINK;
1033 break;
1034 case nsIDocShellLoadInfo::loadRefresh:
1035 loadType = LOAD_REFRESH;
1036 break;
1037 case nsIDocShellLoadInfo::loadBypassHistory:
1038 loadType = LOAD_BYPASS_HISTORY;
1039 break;
1040 case nsIDocShellLoadInfo::loadStopContent:
1041 loadType = LOAD_STOP_CONTENT;
1042 break;
1043 case nsIDocShellLoadInfo::loadStopContentAndReplace:
1044 loadType = LOAD_STOP_CONTENT_AND_REPLACE;
1045 break;
1046 case nsIDocShellLoadInfo::loadPushState:
1047 loadType = LOAD_PUSHSTATE;
1048 break;
1049 default:
1050 NS_NOTREACHED("Unexpected nsDocShellInfoLoadType value");
1053 return loadType;
1057 nsDocShellInfoLoadType
1058 nsDocShell::ConvertLoadTypeToDocShellLoadInfo(PRUint32 aLoadType)
1060 nsDocShellInfoLoadType docShellLoadType = nsIDocShellLoadInfo::loadNormal;
1061 switch (aLoadType) {
1062 case LOAD_NORMAL:
1063 docShellLoadType = nsIDocShellLoadInfo::loadNormal;
1064 break;
1065 case LOAD_NORMAL_REPLACE:
1066 docShellLoadType = nsIDocShellLoadInfo::loadNormalReplace;
1067 break;
1068 case LOAD_NORMAL_EXTERNAL:
1069 docShellLoadType = nsIDocShellLoadInfo::loadNormalExternal;
1070 break;
1071 case LOAD_NORMAL_BYPASS_CACHE:
1072 docShellLoadType = nsIDocShellLoadInfo::loadNormalBypassCache;
1073 break;
1074 case LOAD_NORMAL_BYPASS_PROXY:
1075 docShellLoadType = nsIDocShellLoadInfo::loadNormalBypassProxy;
1076 break;
1077 case LOAD_NORMAL_BYPASS_PROXY_AND_CACHE:
1078 docShellLoadType = nsIDocShellLoadInfo::loadNormalBypassProxyAndCache;
1079 break;
1080 case LOAD_HISTORY:
1081 docShellLoadType = nsIDocShellLoadInfo::loadHistory;
1082 break;
1083 case LOAD_RELOAD_NORMAL:
1084 docShellLoadType = nsIDocShellLoadInfo::loadReloadNormal;
1085 break;
1086 case LOAD_RELOAD_CHARSET_CHANGE:
1087 docShellLoadType = nsIDocShellLoadInfo::loadReloadCharsetChange;
1088 break;
1089 case LOAD_RELOAD_BYPASS_CACHE:
1090 docShellLoadType = nsIDocShellLoadInfo::loadReloadBypassCache;
1091 break;
1092 case LOAD_RELOAD_BYPASS_PROXY:
1093 docShellLoadType = nsIDocShellLoadInfo::loadReloadBypassProxy;
1094 break;
1095 case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
1096 docShellLoadType = nsIDocShellLoadInfo::loadReloadBypassProxyAndCache;
1097 break;
1098 case LOAD_LINK:
1099 docShellLoadType = nsIDocShellLoadInfo::loadLink;
1100 break;
1101 case LOAD_REFRESH:
1102 docShellLoadType = nsIDocShellLoadInfo::loadRefresh;
1103 break;
1104 case LOAD_BYPASS_HISTORY:
1105 case LOAD_ERROR_PAGE:
1106 docShellLoadType = nsIDocShellLoadInfo::loadBypassHistory;
1107 break;
1108 case LOAD_STOP_CONTENT:
1109 docShellLoadType = nsIDocShellLoadInfo::loadStopContent;
1110 break;
1111 case LOAD_STOP_CONTENT_AND_REPLACE:
1112 docShellLoadType = nsIDocShellLoadInfo::loadStopContentAndReplace;
1113 break;
1114 case LOAD_PUSHSTATE:
1115 docShellLoadType = nsIDocShellLoadInfo::loadPushState;
1116 break;
1117 default:
1118 NS_NOTREACHED("Unexpected load type value");
1121 return docShellLoadType;
1124 //*****************************************************************************
1125 // nsDocShell::nsIDocShell
1126 //*****************************************************************************
1127 NS_IMETHODIMP
1128 nsDocShell::LoadURI(nsIURI * aURI,
1129 nsIDocShellLoadInfo * aLoadInfo,
1130 PRUint32 aLoadFlags,
1131 PRBool aFirstParty)
1133 NS_PRECONDITION(aLoadInfo || (aLoadFlags & EXTRA_LOAD_FLAGS) == 0,
1134 "Unexpected flags");
1135 NS_PRECONDITION((aLoadFlags & 0xf) == 0, "Should not have these flags set");
1137 // Note: we allow loads to get through here even if mFiredUnloadEvent is
1138 // true; that case will get handled in LoadInternal or LoadHistoryEntry.
1139 if (IsPrintingOrPP()) {
1140 return NS_OK; // JS may not handle returning of an error code
1142 nsresult rv;
1143 nsCOMPtr<nsIURI> referrer;
1144 nsCOMPtr<nsIInputStream> postStream;
1145 nsCOMPtr<nsIInputStream> headersStream;
1146 nsCOMPtr<nsISupports> owner;
1147 PRBool inheritOwner = PR_FALSE;
1148 PRBool ownerIsExplicit = PR_FALSE;
1149 PRBool sendReferrer = PR_TRUE;
1150 nsCOMPtr<nsISHEntry> shEntry;
1151 nsXPIDLString target;
1152 PRUint32 loadType = MAKE_LOAD_TYPE(LOAD_NORMAL, aLoadFlags);
1154 NS_ENSURE_ARG(aURI);
1156 // Extract the info from the DocShellLoadInfo struct...
1157 if (aLoadInfo) {
1158 aLoadInfo->GetReferrer(getter_AddRefs(referrer));
1160 nsDocShellInfoLoadType lt = nsIDocShellLoadInfo::loadNormal;
1161 aLoadInfo->GetLoadType(&lt);
1162 // Get the appropriate loadType from nsIDocShellLoadInfo type
1163 loadType = ConvertDocShellLoadInfoToLoadType(lt);
1165 aLoadInfo->GetOwner(getter_AddRefs(owner));
1166 aLoadInfo->GetInheritOwner(&inheritOwner);
1167 aLoadInfo->GetOwnerIsExplicit(&ownerIsExplicit);
1168 aLoadInfo->GetSHEntry(getter_AddRefs(shEntry));
1169 aLoadInfo->GetTarget(getter_Copies(target));
1170 aLoadInfo->GetPostDataStream(getter_AddRefs(postStream));
1171 aLoadInfo->GetHeadersStream(getter_AddRefs(headersStream));
1172 aLoadInfo->GetSendReferrer(&sendReferrer);
1175 #if defined(PR_LOGGING) && defined(DEBUG)
1176 if (PR_LOG_TEST(gDocShellLog, PR_LOG_DEBUG)) {
1177 nsCAutoString uristr;
1178 aURI->GetAsciiSpec(uristr);
1179 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
1180 ("nsDocShell[%p]: loading %s with flags 0x%08x",
1181 this, uristr.get(), aLoadFlags));
1183 #endif
1185 if (!shEntry &&
1186 !LOAD_TYPE_HAS_FLAGS(loadType, LOAD_FLAGS_REPLACE_HISTORY)) {
1187 // First verify if this is a subframe.
1188 nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
1189 GetSameTypeParent(getter_AddRefs(parentAsItem));
1190 nsCOMPtr<nsIDocShell> parentDS(do_QueryInterface(parentAsItem));
1191 PRUint32 parentLoadType;
1193 if (parentDS && parentDS != static_cast<nsIDocShell *>(this)) {
1194 /* OK. It is a subframe. Checkout the
1195 * parent's loadtype. If the parent was loaded thro' a history
1196 * mechanism, then get the SH entry for the child from the parent.
1197 * This is done to restore frameset navigation while going back/forward.
1198 * If the parent was loaded through any other loadType, set the
1199 * child's loadType too accordingly, so that session history does not
1200 * get confused.
1203 // Get the parent's load type
1204 parentDS->GetLoadType(&parentLoadType);
1206 nsCOMPtr<nsIDocShellHistory> parent(do_QueryInterface(parentAsItem));
1207 if (parent) {
1208 // Get the ShEntry for the child from the parent
1209 parent->GetChildSHEntry(mChildOffset, getter_AddRefs(shEntry));
1210 // Make some decisions on the child frame's loadType based on the
1211 // parent's loadType.
1212 if (mCurrentURI == nsnull) {
1213 // This is a newly created frame. Check for exception cases first.
1214 // By default the subframe will inherit the parent's loadType.
1215 if (shEntry && (parentLoadType == LOAD_NORMAL ||
1216 parentLoadType == LOAD_LINK ||
1217 parentLoadType == LOAD_NORMAL_EXTERNAL)) {
1218 // The parent was loaded normally. In this case, this *brand new* child really shouldn't
1219 // have a SHEntry. If it does, it could be because the parent is replacing an
1220 // existing frame with a new frame, in the onLoadHandler. We don't want this
1221 // url to get into session history. Clear off shEntry, and set load type to
1222 // LOAD_BYPASS_HISTORY.
1223 PRBool inOnLoadHandler=PR_FALSE;
1224 parentDS->GetIsExecutingOnLoadHandler(&inOnLoadHandler);
1225 if (inOnLoadHandler) {
1226 loadType = LOAD_NORMAL_REPLACE;
1227 shEntry = nsnull;
1230 else if (parentLoadType == LOAD_REFRESH) {
1231 // Clear shEntry. For refresh loads, we have to load
1232 // what comes thro' the pipe, not what's in history.
1233 shEntry = nsnull;
1235 else if ((parentLoadType == LOAD_BYPASS_HISTORY) ||
1236 (parentLoadType == LOAD_ERROR_PAGE) ||
1237 (shEntry &&
1238 ((parentLoadType & LOAD_CMD_HISTORY) ||
1239 (parentLoadType == LOAD_RELOAD_NORMAL) ||
1240 (parentLoadType == LOAD_RELOAD_CHARSET_CHANGE)))) {
1241 // If the parent url, bypassed history or was loaded from
1242 // history, pass on the parent's loadType to the new child
1243 // frame too, so that the child frame will also
1244 // avoid getting into history.
1245 loadType = parentLoadType;
1248 else {
1249 // This is a pre-existing subframe. If the load was not originally initiated
1250 // by session history, (if (!shEntry) condition succeeded) and mCurrentURI is not null,
1251 // it is possible that a parent's onLoadHandler or even self's onLoadHandler is loading
1252 // a new page in this child. Check parent's and self's busy flag and if it is set,
1253 // we don't want this onLoadHandler load to get in to session history.
1254 PRUint32 parentBusy = BUSY_FLAGS_NONE;
1255 PRUint32 selfBusy = BUSY_FLAGS_NONE;
1256 parentDS->GetBusyFlags(&parentBusy);
1257 GetBusyFlags(&selfBusy);
1258 if (((parentBusy & BUSY_FLAGS_BUSY) ||
1259 (selfBusy & BUSY_FLAGS_BUSY)) &&
1260 shEntry) {
1261 loadType = LOAD_NORMAL_REPLACE;
1262 shEntry = nsnull;
1265 } // parent
1266 } //parentDS
1267 else {
1268 // This is the root docshell. If we got here while
1269 // executing an onLoad Handler,this load will not go
1270 // into session history.
1271 PRBool inOnLoadHandler=PR_FALSE;
1272 GetIsExecutingOnLoadHandler(&inOnLoadHandler);
1273 if (inOnLoadHandler) {
1274 loadType = LOAD_NORMAL_REPLACE;
1277 } // !shEntry
1279 if (shEntry) {
1280 #ifdef DEBUG
1281 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
1282 ("nsDocShell[%p]: loading from session history", this));
1283 #endif
1285 return LoadHistoryEntry(shEntry, loadType);
1288 // Perform the load...
1290 // We need an owner (a referring principal).
1292 // If ownerIsExplicit is not set there are 4 possibilities:
1293 // (1) If the system principal was passed in and we're a typeContent
1294 // docshell, inherit the principal from the current document
1295 // instead.
1296 // (2) In all other cases when the principal passed in is not null,
1297 // use that principal.
1298 // (3) If the caller has allowed inheriting from the current document,
1299 // or if we're being called from system code (eg chrome JS or pure
1300 // C++) then inheritOwner should be true and InternalLoad will get
1301 // an owner from the current document. If none of these things are
1302 // true, then
1303 // (4) we pass a null owner into the channel, and an owner will be
1304 // created later from the channel's internal data.
1306 // If ownerIsExplicit *is* set, there are 4 possibilities
1307 // (1) If the system principal was passed in and we're a typeContent
1308 // docshell, return an error.
1309 // (2) In all other cases when the principal passed in is not null,
1310 // use that principal.
1311 // (3) If the caller has allowed inheriting from the current document,
1312 // then inheritOwner should be true and InternalLoad will get an owner
1313 // from the current document. If none of these things are true, then
1314 // (4) we pass a null owner into the channel, and an owner will be
1315 // created later from the channel's internal data.
1317 // NOTE: This all only works because the only thing the owner is used
1318 // for in InternalLoad is data:, javascript:, and about:blank
1319 // URIs. For other URIs this would all be dead wrong!
1321 nsCOMPtr<nsIScriptSecurityManager> secMan =
1322 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
1323 NS_ENSURE_SUCCESS(rv, rv);
1325 if (owner && mItemType != typeChrome) {
1326 nsCOMPtr<nsIPrincipal> ownerPrincipal = do_QueryInterface(owner);
1327 PRBool isSystem;
1328 rv = secMan->IsSystemPrincipal(ownerPrincipal, &isSystem);
1329 NS_ENSURE_SUCCESS(rv, rv);
1331 if (isSystem) {
1332 if (ownerIsExplicit) {
1333 return NS_ERROR_DOM_SECURITY_ERR;
1335 owner = nsnull;
1336 inheritOwner = PR_TRUE;
1339 if (!owner && !inheritOwner && !ownerIsExplicit) {
1340 // See if there's system or chrome JS code running
1341 rv = secMan->SubjectPrincipalIsSystem(&inheritOwner);
1342 if (NS_FAILED(rv)) {
1343 // Set it back to false
1344 inheritOwner = PR_FALSE;
1348 PRUint32 flags = 0;
1350 if (inheritOwner)
1351 flags |= INTERNAL_LOAD_FLAGS_INHERIT_OWNER;
1353 if (!sendReferrer)
1354 flags |= INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER;
1356 if (aLoadFlags & LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP)
1357 flags |= INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
1359 if (aLoadFlags & LOAD_FLAGS_FIRST_LOAD)
1360 flags |= INTERNAL_LOAD_FLAGS_FIRST_LOAD;
1362 if (aLoadFlags & LOAD_FLAGS_BYPASS_CLASSIFIER)
1363 flags |= INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER;
1365 if (aLoadFlags & LOAD_FLAGS_FORCE_ALLOW_COOKIES)
1366 flags |= INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES;
1368 return InternalLoad(aURI,
1369 referrer,
1370 owner,
1371 flags,
1372 target.get(),
1373 nsnull, // No type hint
1374 postStream,
1375 headersStream,
1376 loadType,
1377 nsnull, // No SHEntry
1378 aFirstParty,
1379 nsnull, // No nsIDocShell
1380 nsnull); // No nsIRequest
1383 NS_IMETHODIMP
1384 nsDocShell::LoadStream(nsIInputStream *aStream, nsIURI * aURI,
1385 const nsACString &aContentType,
1386 const nsACString &aContentCharset,
1387 nsIDocShellLoadInfo * aLoadInfo)
1389 NS_ENSURE_ARG(aStream);
1391 mAllowKeywordFixup = PR_FALSE;
1393 // if the caller doesn't pass in a URI we need to create a dummy URI. necko
1394 // currently requires a URI in various places during the load. Some consumers
1395 // do as well.
1396 nsCOMPtr<nsIURI> uri = aURI;
1397 if (!uri) {
1398 // HACK ALERT
1399 nsresult rv = NS_OK;
1400 uri = do_CreateInstance(NS_SIMPLEURI_CONTRACTID, &rv);
1401 if (NS_FAILED(rv))
1402 return rv;
1403 // Make sure that the URI spec "looks" like a protocol and path...
1404 // For now, just use a bogus protocol called "internal"
1405 rv = uri->SetSpec(NS_LITERAL_CSTRING("internal:load-stream"));
1406 if (NS_FAILED(rv))
1407 return rv;
1410 PRUint32 loadType = LOAD_NORMAL;
1411 if (aLoadInfo) {
1412 nsDocShellInfoLoadType lt = nsIDocShellLoadInfo::loadNormal;
1413 (void) aLoadInfo->GetLoadType(&lt);
1414 // Get the appropriate LoadType from nsIDocShellLoadInfo type
1415 loadType = ConvertDocShellLoadInfoToLoadType(lt);
1418 NS_ENSURE_SUCCESS(Stop(nsIWebNavigation::STOP_NETWORK), NS_ERROR_FAILURE);
1420 mLoadType = loadType;
1422 // build up a channel for this stream.
1423 nsCOMPtr<nsIChannel> channel;
1424 NS_ENSURE_SUCCESS(NS_NewInputStreamChannel
1425 (getter_AddRefs(channel), uri, aStream,
1426 aContentType, aContentCharset),
1427 NS_ERROR_FAILURE);
1429 nsCOMPtr<nsIURILoader>
1430 uriLoader(do_GetService(NS_URI_LOADER_CONTRACTID));
1431 NS_ENSURE_TRUE(uriLoader, NS_ERROR_FAILURE);
1433 NS_ENSURE_SUCCESS(DoChannelLoad(channel, uriLoader, PR_FALSE),
1434 NS_ERROR_FAILURE);
1435 return NS_OK;
1438 NS_IMETHODIMP
1439 nsDocShell::CreateLoadInfo(nsIDocShellLoadInfo ** aLoadInfo)
1441 nsDocShellLoadInfo *loadInfo = new nsDocShellLoadInfo();
1442 NS_ENSURE_TRUE(loadInfo, NS_ERROR_OUT_OF_MEMORY);
1443 nsCOMPtr<nsIDocShellLoadInfo> localRef(loadInfo);
1445 *aLoadInfo = localRef;
1446 NS_ADDREF(*aLoadInfo);
1447 return NS_OK;
1452 * Reset state to a new content model within the current document and the document
1453 * viewer. Called by the document before initiating an out of band document.write().
1455 NS_IMETHODIMP
1456 nsDocShell::PrepareForNewContentModel()
1458 mEODForCurrentDocument = PR_FALSE;
1459 return NS_OK;
1463 NS_IMETHODIMP
1464 nsDocShell::FirePageHideNotification(PRBool aIsUnload)
1466 if (mContentViewer && !mFiredUnloadEvent) {
1467 // Keep an explicit reference since calling PageHide could release
1468 // mContentViewer
1469 nsCOMPtr<nsIContentViewer> kungFuDeathGrip(mContentViewer);
1470 mFiredUnloadEvent = PR_TRUE;
1472 mContentViewer->PageHide(aIsUnload);
1474 nsAutoTArray<nsCOMPtr<nsIDocShell>, 8> kids;
1475 PRInt32 i, n = mChildList.Count();
1476 kids.SetCapacity(n);
1477 for (i = 0; i < n; i++) {
1478 kids.AppendElement(do_QueryInterface(ChildAt(i)));
1481 n = kids.Length();
1482 for (i = 0; i < n; ++i) {
1483 if (kids[i]) {
1484 kids[i]->FirePageHideNotification(aIsUnload);
1487 // Now make sure our editor, if any, is detached before we go
1488 // any farther.
1489 DetachEditorFromWindow();
1492 return NS_OK;
1496 // Bug 13871: Prevent frameset spoofing
1498 // This routine answers: 'Is origin's document from same domain as
1499 // target's document?'
1501 // file: uris are considered the same domain for the purpose of
1502 // frame navigation regardless of script accessibility (bug 420425)
1504 /* static */
1505 PRBool
1506 nsDocShell::ValidateOrigin(nsIDocShellTreeItem* aOriginTreeItem,
1507 nsIDocShellTreeItem* aTargetTreeItem)
1509 nsCOMPtr<nsIScriptSecurityManager> securityManager =
1510 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
1511 NS_ENSURE_TRUE(securityManager, PR_FALSE);
1513 nsCOMPtr<nsIPrincipal> subjectPrincipal;
1514 nsresult rv =
1515 securityManager->GetSubjectPrincipal(getter_AddRefs(subjectPrincipal));
1516 NS_ENSURE_SUCCESS(rv, PR_FALSE);
1518 if (subjectPrincipal) {
1519 // We're called from JS, check if UniversalBrowserWrite is
1520 // enabled.
1521 PRBool ubwEnabled = PR_FALSE;
1522 rv = securityManager->IsCapabilityEnabled("UniversalBrowserWrite",
1523 &ubwEnabled);
1524 NS_ENSURE_SUCCESS(rv, PR_FALSE);
1526 if (ubwEnabled) {
1527 return PR_TRUE;
1531 // Get origin document principal
1532 nsCOMPtr<nsIDOMDocument> originDOMDocument =
1533 do_GetInterface(aOriginTreeItem);
1534 nsCOMPtr<nsIDocument> originDocument(do_QueryInterface(originDOMDocument));
1535 NS_ENSURE_TRUE(originDocument, PR_FALSE);
1537 // Get target principal
1538 nsCOMPtr<nsIDOMDocument> targetDOMDocument =
1539 do_GetInterface(aTargetTreeItem);
1540 nsCOMPtr<nsIDocument> targetDocument(do_QueryInterface(targetDOMDocument));
1541 NS_ENSURE_TRUE(targetDocument, PR_FALSE);
1543 PRBool equal;
1544 rv = originDocument->NodePrincipal()->
1545 Equals(targetDocument->NodePrincipal(), &equal);
1546 if (NS_SUCCEEDED(rv) && equal) {
1547 return PR_TRUE;
1550 // Not strictly equal, special case if both are file: uris
1551 PRBool originIsFile = PR_FALSE;
1552 PRBool targetIsFile = PR_FALSE;
1553 nsCOMPtr<nsIURI> originURI;
1554 nsCOMPtr<nsIURI> targetURI;
1555 nsCOMPtr<nsIURI> innerOriginURI;
1556 nsCOMPtr<nsIURI> innerTargetURI;
1558 rv = originDocument->NodePrincipal()->GetURI(getter_AddRefs(originURI));
1559 if (NS_SUCCEEDED(rv) && originURI)
1560 innerOriginURI = NS_GetInnermostURI(originURI);
1562 rv = targetDocument->NodePrincipal()->GetURI(getter_AddRefs(targetURI));
1563 if (NS_SUCCEEDED(rv) && targetURI)
1564 innerTargetURI = NS_GetInnermostURI(targetURI);
1566 return innerOriginURI && innerTargetURI &&
1567 NS_SUCCEEDED(innerOriginURI->SchemeIs("file", &originIsFile)) &&
1568 NS_SUCCEEDED(innerTargetURI->SchemeIs("file", &targetIsFile)) &&
1569 originIsFile && targetIsFile;
1572 NS_IMETHODIMP
1573 nsDocShell::GetEldestPresContext(nsPresContext** aPresContext)
1575 nsresult rv = NS_OK;
1577 NS_ENSURE_ARG_POINTER(aPresContext);
1578 *aPresContext = nsnull;
1580 nsCOMPtr<nsIContentViewer> viewer = mContentViewer;
1581 while (viewer) {
1582 nsCOMPtr<nsIContentViewer> prevViewer;
1583 viewer->GetPreviousViewer(getter_AddRefs(prevViewer));
1584 if (prevViewer)
1585 viewer = prevViewer;
1586 else {
1587 nsCOMPtr<nsIDocumentViewer> docv(do_QueryInterface(viewer));
1588 if (docv)
1589 rv = docv->GetPresContext(aPresContext);
1590 break;
1594 return rv;
1597 NS_IMETHODIMP
1598 nsDocShell::GetPresContext(nsPresContext ** aPresContext)
1600 NS_ENSURE_ARG_POINTER(aPresContext);
1601 *aPresContext = nsnull;
1603 if (!mContentViewer)
1604 return NS_OK;
1606 nsCOMPtr<nsIDocumentViewer> docv(do_QueryInterface(mContentViewer));
1607 NS_ENSURE_TRUE(docv, NS_ERROR_NO_INTERFACE);
1609 return docv->GetPresContext(aPresContext);
1612 NS_IMETHODIMP
1613 nsDocShell::GetPresShell(nsIPresShell ** aPresShell)
1615 nsresult rv = NS_OK;
1617 NS_ENSURE_ARG_POINTER(aPresShell);
1618 *aPresShell = nsnull;
1620 nsRefPtr<nsPresContext> presContext;
1621 (void) GetPresContext(getter_AddRefs(presContext));
1623 if (presContext) {
1624 NS_IF_ADDREF(*aPresShell = presContext->GetPresShell());
1627 return rv;
1630 NS_IMETHODIMP
1631 nsDocShell::GetEldestPresShell(nsIPresShell** aPresShell)
1633 nsresult rv = NS_OK;
1635 NS_ENSURE_ARG_POINTER(aPresShell);
1636 *aPresShell = nsnull;
1638 nsRefPtr<nsPresContext> presContext;
1639 (void) GetEldestPresContext(getter_AddRefs(presContext));
1641 if (presContext) {
1642 NS_IF_ADDREF(*aPresShell = presContext->GetPresShell());
1645 return rv;
1648 NS_IMETHODIMP
1649 nsDocShell::GetContentViewer(nsIContentViewer ** aContentViewer)
1651 NS_ENSURE_ARG_POINTER(aContentViewer);
1653 *aContentViewer = mContentViewer;
1654 NS_IF_ADDREF(*aContentViewer);
1655 return NS_OK;
1658 NS_IMETHODIMP
1659 nsDocShell::SetChromeEventHandler(nsIDOMEventTarget* aChromeEventHandler)
1661 nsCOMPtr<nsPIDOMEventTarget> piTarget =
1662 do_QueryInterface(aChromeEventHandler);
1663 // Weak reference. Don't addref.
1664 mChromeEventHandler = piTarget;
1666 nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(mScriptGlobal));
1667 if (win) {
1668 win->SetChromeEventHandler(piTarget);
1671 return NS_OK;
1674 NS_IMETHODIMP
1675 nsDocShell::GetChromeEventHandler(nsIDOMEventTarget** aChromeEventHandler)
1677 NS_ENSURE_ARG_POINTER(aChromeEventHandler);
1678 nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(mChromeEventHandler);
1679 target.swap(*aChromeEventHandler);
1680 return NS_OK;
1683 /* [noscript] void setCurrentURI (in nsIURI uri); */
1684 NS_IMETHODIMP
1685 nsDocShell::SetCurrentURI(nsIURI *aURI)
1687 SetCurrentURI(aURI, nsnull, PR_TRUE);
1688 return NS_OK;
1691 PRBool
1692 nsDocShell::SetCurrentURI(nsIURI *aURI, nsIRequest *aRequest,
1693 PRBool aFireOnLocationChange)
1695 #ifdef PR_LOGGING
1696 if (gDocShellLeakLog && PR_LOG_TEST(gDocShellLeakLog, PR_LOG_DEBUG)) {
1697 nsCAutoString spec;
1698 if (aURI)
1699 aURI->GetSpec(spec);
1700 PR_LogPrint("DOCSHELL %p SetCurrentURI %s\n", this, spec.get());
1702 #endif
1704 // We don't want to send a location change when we're displaying an error
1705 // page, and we don't want to change our idea of "current URI" either
1706 if (mLoadType == LOAD_ERROR_PAGE) {
1707 return PR_FALSE;
1710 mCurrentURI = NS_TryToMakeImmutable(aURI);
1712 PRBool isRoot = PR_FALSE; // Is this the root docshell
1713 PRBool isSubFrame = PR_FALSE; // Is this a subframe navigation?
1715 nsCOMPtr<nsIDocShellTreeItem> root;
1717 GetSameTypeRootTreeItem(getter_AddRefs(root));
1718 if (root.get() == static_cast<nsIDocShellTreeItem *>(this))
1720 // This is the root docshell
1721 isRoot = PR_TRUE;
1723 if (mLSHE) {
1724 mLSHE->GetIsSubFrame(&isSubFrame);
1727 if (!isSubFrame && !isRoot) {
1729 * We don't want to send OnLocationChange notifications when
1730 * a subframe is being loaded for the first time, while
1731 * visiting a frameset page
1733 return PR_FALSE;
1736 if (aFireOnLocationChange) {
1737 FireOnLocationChange(this, aRequest, aURI);
1739 return !aFireOnLocationChange;
1742 NS_IMETHODIMP
1743 nsDocShell::GetCharset(char** aCharset)
1745 NS_ENSURE_ARG_POINTER(aCharset);
1746 *aCharset = nsnull;
1748 nsCOMPtr<nsIPresShell> presShell;
1749 GetPresShell(getter_AddRefs(presShell));
1750 NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
1751 nsIDocument *doc = presShell->GetDocument();
1752 NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
1753 *aCharset = ToNewCString(doc->GetDocumentCharacterSet());
1754 if (!*aCharset) {
1755 return NS_ERROR_OUT_OF_MEMORY;
1758 return NS_OK;
1761 NS_IMETHODIMP
1762 nsDocShell::SetCharset(const char* aCharset)
1764 // set the default charset
1765 nsCOMPtr<nsIContentViewer> viewer;
1766 GetContentViewer(getter_AddRefs(viewer));
1767 if (viewer) {
1768 nsCOMPtr<nsIMarkupDocumentViewer> muDV(do_QueryInterface(viewer));
1769 if (muDV) {
1770 nsCString charset(aCharset);
1771 NS_ENSURE_SUCCESS(muDV->SetDefaultCharacterSet(charset),
1772 NS_ERROR_FAILURE);
1776 // set the charset override
1777 nsCOMPtr<nsIDocumentCharsetInfo> dcInfo;
1778 GetDocumentCharsetInfo(getter_AddRefs(dcInfo));
1779 if (dcInfo) {
1780 nsCOMPtr<nsIAtom> csAtom;
1781 csAtom = do_GetAtom(aCharset);
1782 dcInfo->SetForcedCharset(csAtom);
1785 return NS_OK;
1788 NS_IMETHODIMP
1789 nsDocShell::GetDocumentCharsetInfo(nsIDocumentCharsetInfo **
1790 aDocumentCharsetInfo)
1792 NS_ENSURE_ARG_POINTER(aDocumentCharsetInfo);
1794 // if the mDocumentCharsetInfo does not exist already, we create it now
1795 if (!mDocumentCharsetInfo) {
1796 mDocumentCharsetInfo = do_CreateInstance(NS_DOCUMENTCHARSETINFO_CONTRACTID);
1797 if (!mDocumentCharsetInfo)
1798 return NS_ERROR_FAILURE;
1801 *aDocumentCharsetInfo = mDocumentCharsetInfo;
1802 NS_IF_ADDREF(*aDocumentCharsetInfo);
1803 return NS_OK;
1806 NS_IMETHODIMP
1807 nsDocShell::SetDocumentCharsetInfo(nsIDocumentCharsetInfo *
1808 aDocumentCharsetInfo)
1810 mDocumentCharsetInfo = aDocumentCharsetInfo;
1811 return NS_OK;
1814 NS_IMETHODIMP
1815 nsDocShell::GetChannelIsUnsafe(PRBool *aUnsafe)
1817 *aUnsafe = PR_FALSE;
1819 nsIChannel* channel = GetCurrentDocChannel();
1820 if (!channel) {
1821 return NS_OK;
1824 nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(channel);
1825 if (!jarChannel) {
1826 return NS_OK;
1829 return jarChannel->GetIsUnsafe(aUnsafe);
1832 NS_IMETHODIMP
1833 nsDocShell::GetAllowPlugins(PRBool * aAllowPlugins)
1835 NS_ENSURE_ARG_POINTER(aAllowPlugins);
1837 *aAllowPlugins = mAllowPlugins;
1838 if (!mAllowPlugins) {
1839 return NS_OK;
1842 PRBool unsafe;
1843 *aAllowPlugins = NS_SUCCEEDED(GetChannelIsUnsafe(&unsafe)) && !unsafe;
1844 return NS_OK;
1847 NS_IMETHODIMP
1848 nsDocShell::SetAllowPlugins(PRBool aAllowPlugins)
1850 mAllowPlugins = aAllowPlugins;
1851 //XXX should enable or disable a plugin host
1852 return NS_OK;
1855 NS_IMETHODIMP
1856 nsDocShell::GetAllowJavascript(PRBool * aAllowJavascript)
1858 NS_ENSURE_ARG_POINTER(aAllowJavascript);
1860 *aAllowJavascript = mAllowJavascript;
1861 if (!mAllowJavascript) {
1862 return NS_OK;
1865 PRBool unsafe;
1866 *aAllowJavascript = NS_SUCCEEDED(GetChannelIsUnsafe(&unsafe)) && !unsafe;
1867 return NS_OK;
1870 NS_IMETHODIMP
1871 nsDocShell::SetAllowJavascript(PRBool aAllowJavascript)
1873 mAllowJavascript = aAllowJavascript;
1874 return NS_OK;
1877 NS_IMETHODIMP nsDocShell::GetAllowMetaRedirects(PRBool * aReturn)
1879 NS_ENSURE_ARG_POINTER(aReturn);
1881 *aReturn = mAllowMetaRedirects;
1882 if (!mAllowMetaRedirects) {
1883 return NS_OK;
1886 PRBool unsafe;
1887 *aReturn = NS_SUCCEEDED(GetChannelIsUnsafe(&unsafe)) && !unsafe;
1888 return NS_OK;
1891 NS_IMETHODIMP nsDocShell::SetAllowMetaRedirects(PRBool aValue)
1893 mAllowMetaRedirects = aValue;
1894 return NS_OK;
1897 NS_IMETHODIMP nsDocShell::GetAllowSubframes(PRBool * aAllowSubframes)
1899 NS_ENSURE_ARG_POINTER(aAllowSubframes);
1901 *aAllowSubframes = mAllowSubframes;
1902 return NS_OK;
1905 NS_IMETHODIMP nsDocShell::SetAllowSubframes(PRBool aAllowSubframes)
1907 mAllowSubframes = aAllowSubframes;
1908 return NS_OK;
1911 NS_IMETHODIMP nsDocShell::GetAllowImages(PRBool * aAllowImages)
1913 NS_ENSURE_ARG_POINTER(aAllowImages);
1915 *aAllowImages = mAllowImages;
1916 return NS_OK;
1919 NS_IMETHODIMP nsDocShell::SetAllowImages(PRBool aAllowImages)
1921 mAllowImages = aAllowImages;
1922 return NS_OK;
1925 NS_IMETHODIMP nsDocShell::GetAllowDNSPrefetch(PRBool * aAllowDNSPrefetch)
1927 *aAllowDNSPrefetch = mAllowDNSPrefetch;
1928 return NS_OK;
1931 NS_IMETHODIMP nsDocShell::SetAllowDNSPrefetch(PRBool aAllowDNSPrefetch)
1933 mAllowDNSPrefetch = aAllowDNSPrefetch;
1934 return NS_OK;
1937 NS_IMETHODIMP
1938 nsDocShell::GetDocShellEnumerator(PRInt32 aItemType, PRInt32 aDirection, nsISimpleEnumerator **outEnum)
1940 NS_ENSURE_ARG_POINTER(outEnum);
1941 *outEnum = nsnull;
1943 nsRefPtr<nsDocShellEnumerator> docShellEnum;
1944 if (aDirection == ENUMERATE_FORWARDS)
1945 docShellEnum = new nsDocShellForwardsEnumerator;
1946 else
1947 docShellEnum = new nsDocShellBackwardsEnumerator;
1949 if (!docShellEnum) return NS_ERROR_OUT_OF_MEMORY;
1951 nsresult rv = docShellEnum->SetEnumDocShellType(aItemType);
1952 if (NS_FAILED(rv)) return rv;
1954 rv = docShellEnum->SetEnumerationRootItem((nsIDocShellTreeItem *)this);
1955 if (NS_FAILED(rv)) return rv;
1957 rv = docShellEnum->First();
1958 if (NS_FAILED(rv)) return rv;
1960 rv = docShellEnum->QueryInterface(NS_GET_IID(nsISimpleEnumerator), (void **)outEnum);
1962 return rv;
1965 NS_IMETHODIMP
1966 nsDocShell::GetAppType(PRUint32 * aAppType)
1968 *aAppType = mAppType;
1969 return NS_OK;
1972 NS_IMETHODIMP
1973 nsDocShell::SetAppType(PRUint32 aAppType)
1975 mAppType = aAppType;
1976 return NS_OK;
1980 NS_IMETHODIMP
1981 nsDocShell::GetAllowAuth(PRBool * aAllowAuth)
1983 *aAllowAuth = mAllowAuth;
1984 return NS_OK;
1987 NS_IMETHODIMP
1988 nsDocShell::SetAllowAuth(PRBool aAllowAuth)
1990 mAllowAuth = aAllowAuth;
1991 return NS_OK;
1994 NS_IMETHODIMP
1995 nsDocShell::GetZoom(float *zoom)
1997 NS_ENSURE_ARG_POINTER(zoom);
1998 *zoom = 1.0f;
1999 return NS_OK;
2002 NS_IMETHODIMP
2003 nsDocShell::SetZoom(float zoom)
2005 return NS_ERROR_NOT_IMPLEMENTED;
2008 NS_IMETHODIMP
2009 nsDocShell::GetMarginWidth(PRInt32 * aWidth)
2011 NS_ENSURE_ARG_POINTER(aWidth);
2013 *aWidth = mMarginWidth;
2014 return NS_OK;
2017 NS_IMETHODIMP
2018 nsDocShell::SetMarginWidth(PRInt32 aWidth)
2020 mMarginWidth = aWidth;
2021 return NS_OK;
2024 NS_IMETHODIMP
2025 nsDocShell::GetMarginHeight(PRInt32 * aHeight)
2027 NS_ENSURE_ARG_POINTER(aHeight);
2029 *aHeight = mMarginHeight;
2030 return NS_OK;
2033 NS_IMETHODIMP
2034 nsDocShell::SetMarginHeight(PRInt32 aHeight)
2036 mMarginHeight = aHeight;
2037 return NS_OK;
2040 NS_IMETHODIMP
2041 nsDocShell::GetBusyFlags(PRUint32 * aBusyFlags)
2043 NS_ENSURE_ARG_POINTER(aBusyFlags);
2045 *aBusyFlags = mBusyFlags;
2046 return NS_OK;
2049 NS_IMETHODIMP
2050 nsDocShell::TabToTreeOwner(PRBool aForward, PRBool* aTookFocus)
2052 NS_ENSURE_ARG_POINTER(aTookFocus);
2054 nsCOMPtr<nsIWebBrowserChromeFocus> chromeFocus = do_GetInterface(mTreeOwner);
2055 if (chromeFocus) {
2056 if (aForward)
2057 *aTookFocus = NS_SUCCEEDED(chromeFocus->FocusNextElement());
2058 else
2059 *aTookFocus = NS_SUCCEEDED(chromeFocus->FocusPrevElement());
2060 } else
2061 *aTookFocus = PR_FALSE;
2063 return NS_OK;
2066 NS_IMETHODIMP
2067 nsDocShell::GetSecurityUI(nsISecureBrowserUI **aSecurityUI)
2069 NS_IF_ADDREF(*aSecurityUI = mSecurityUI);
2070 return NS_OK;
2073 NS_IMETHODIMP
2074 nsDocShell::SetSecurityUI(nsISecureBrowserUI *aSecurityUI)
2076 mSecurityUI = aSecurityUI;
2077 return NS_OK;
2080 NS_IMETHODIMP
2081 nsDocShell::GetUseErrorPages(PRBool *aUseErrorPages)
2083 *aUseErrorPages = mUseErrorPages;
2084 return NS_OK;
2087 NS_IMETHODIMP
2088 nsDocShell::SetUseErrorPages(PRBool aUseErrorPages)
2090 // If mUseErrorPages is set explicitly, stop observing the pref.
2091 if (mObserveErrorPages) {
2092 nsCOMPtr<nsIPrefBranch2> prefs(do_QueryInterface(mPrefs));
2093 if (prefs) {
2094 prefs->RemoveObserver("browser.xul.error_pages.enabled", this);
2095 mObserveErrorPages = PR_FALSE;
2098 mUseErrorPages = aUseErrorPages;
2099 return NS_OK;
2102 NS_IMETHODIMP
2103 nsDocShell::GetPreviousTransIndex(PRInt32 *aPreviousTransIndex)
2105 *aPreviousTransIndex = mPreviousTransIndex;
2106 return NS_OK;
2109 NS_IMETHODIMP
2110 nsDocShell::GetLoadedTransIndex(PRInt32 *aLoadedTransIndex)
2112 *aLoadedTransIndex = mLoadedTransIndex;
2113 return NS_OK;
2116 NS_IMETHODIMP
2117 nsDocShell::HistoryPurged(PRInt32 aNumEntries)
2119 // These indices are used for fastback cache eviction, to determine
2120 // which session history entries are candidates for content viewer
2121 // eviction. We need to adjust by the number of entries that we
2122 // just purged from history, so that we look at the right session history
2123 // entries during eviction.
2124 mPreviousTransIndex = NS_MAX(-1, mPreviousTransIndex - aNumEntries);
2125 mLoadedTransIndex = NS_MAX(0, mLoadedTransIndex - aNumEntries);
2127 PRInt32 count = mChildList.Count();
2128 for (PRInt32 i = 0; i < count; ++i) {
2129 nsCOMPtr<nsIDocShell> shell = do_QueryInterface(ChildAt(i));
2130 if (shell) {
2131 shell->HistoryPurged(aNumEntries);
2135 return NS_OK;
2138 static
2139 nsresult
2140 GetPrincipalDomain(nsIPrincipal* aPrincipal, nsACString& aDomain)
2142 aDomain.Truncate();
2144 nsCOMPtr<nsIURI> codebaseURI;
2145 nsresult rv = aPrincipal->GetDomain(getter_AddRefs(codebaseURI));
2146 NS_ENSURE_SUCCESS(rv, rv);
2147 if (!codebaseURI) {
2148 rv = aPrincipal->GetURI(getter_AddRefs(codebaseURI));
2149 NS_ENSURE_SUCCESS(rv, rv);
2152 if (!codebaseURI)
2153 return NS_OK;
2155 nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(codebaseURI);
2156 NS_ASSERTION(innerURI, "Failed to get innermost URI");
2157 NS_ENSURE_SUCCESS(rv, rv);
2159 rv = innerURI->GetAsciiHost(aDomain);
2160 if (NS_FAILED(rv))
2161 return rv;
2163 return NS_OK;
2166 NS_IMETHODIMP
2167 nsDocShell::GetSessionStorageForPrincipal(nsIPrincipal* aPrincipal,
2168 const nsAString& aDocumentURI,
2169 PRBool aCreate,
2170 nsIDOMStorage** aStorage)
2172 NS_ENSURE_ARG_POINTER(aStorage);
2173 *aStorage = nsnull;
2175 if (!aPrincipal)
2176 return NS_OK;
2178 nsresult rv;
2180 nsCOMPtr<nsIDocShellTreeItem> topItem;
2181 rv = GetSameTypeRootTreeItem(getter_AddRefs(topItem));
2182 if (NS_FAILED(rv))
2183 return rv;
2185 if (!topItem)
2186 return NS_ERROR_FAILURE;
2188 nsDocShell* topDocShell = static_cast<nsDocShell*>(topItem.get());
2189 if (topDocShell != this)
2190 return topDocShell->GetSessionStorageForPrincipal(aPrincipal,
2191 aDocumentURI,
2192 aCreate,
2193 aStorage);
2195 nsCAutoString currentDomain;
2196 rv = GetPrincipalDomain(aPrincipal, currentDomain);
2197 if (NS_FAILED(rv))
2198 return rv;
2200 if (currentDomain.IsEmpty())
2201 return NS_OK;
2203 if (!mStorages.Get(currentDomain, aStorage) && aCreate) {
2204 nsCOMPtr<nsIDOMStorage> newstorage =
2205 do_CreateInstance("@mozilla.org/dom/storage;2");
2206 if (!newstorage)
2207 return NS_ERROR_OUT_OF_MEMORY;
2209 nsCOMPtr<nsPIDOMStorage> pistorage = do_QueryInterface(newstorage);
2210 if (!pistorage)
2211 return NS_ERROR_FAILURE;
2212 rv = pistorage->InitAsSessionStorage(aPrincipal, aDocumentURI);
2213 if (NS_FAILED(rv))
2214 return rv;
2216 if (!mStorages.Put(currentDomain, newstorage))
2217 return NS_ERROR_OUT_OF_MEMORY;
2219 newstorage.swap(*aStorage);
2220 #if defined(PR_LOGGING) && defined(DEBUG)
2221 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
2222 ("nsDocShell[%p]: created a new sessionStorage %p",
2223 this, *aStorage));
2224 #endif
2226 else if (*aStorage) {
2227 nsCOMPtr<nsPIDOMStorage> piStorage = do_QueryInterface(*aStorage);
2228 if (piStorage) {
2229 PRBool canAccess = piStorage->CanAccess(aPrincipal);
2230 NS_ASSERTION(canAccess,
2231 "GetSessionStorageForPrincipal got a storage "
2232 "that could not be accessed!");
2233 if (!canAccess) {
2234 NS_RELEASE(*aStorage);
2235 return NS_ERROR_DOM_SECURITY_ERR;
2239 #if defined(PR_LOGGING) && defined(DEBUG)
2240 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
2241 ("nsDocShell[%p]: returns existing sessionStorage %p",
2242 this, *aStorage));
2243 #endif
2246 if (aCreate) {
2247 // We are asked to create a new storage object. This indicates
2248 // that a new windows wants it. At this moment we "fork" the existing
2249 // storage object (what it means is described in the paragraph bellow).
2250 // We must create a single object per a single window to distinguish
2251 // a window originating oparations on the storage object to succesfully
2252 // prevent dispatch of a storage event to this same window that ivoked
2253 // a change in its storage. We also do this to correctly fill
2254 // documentURI property in the storage event.
2256 // The difference between clone and fork is that clone creates
2257 // a completelly new and independent storage, but fork only creates
2258 // a new object wrapping the storage implementation and data and
2259 // the forked storage then behaves completelly the same way as
2260 // the storage it has been forked of, all such forked storage objects
2261 // shares their state and data and change on one such object affects
2262 // all others the same way.
2263 nsCOMPtr<nsPIDOMStorage> piStorage = do_QueryInterface(*aStorage);
2264 nsCOMPtr<nsIDOMStorage> fork = piStorage->Fork(aDocumentURI);
2265 #if defined(PR_LOGGING) && defined(DEBUG)
2266 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
2267 ("nsDocShell[%p]: forked sessionStorage %p to %p",
2268 this, *aStorage, fork.get()));
2269 #endif
2270 fork.swap(*aStorage);
2273 return NS_OK;
2276 NS_IMETHODIMP
2277 nsDocShell::GetSessionStorageForURI(nsIURI* aURI,
2278 const nsAString& aDocumentURI,
2279 nsIDOMStorage** aStorage)
2281 return GetSessionStorageForURI(aURI, aDocumentURI, PR_TRUE, aStorage);
2284 nsresult
2285 nsDocShell::GetSessionStorageForURI(nsIURI* aURI,
2286 const nsSubstring& aDocumentURI,
2287 PRBool aCreate,
2288 nsIDOMStorage** aStorage)
2290 NS_ENSURE_ARG(aURI);
2291 NS_ENSURE_ARG_POINTER(aStorage);
2293 *aStorage = nsnull;
2295 nsresult rv;
2297 nsCOMPtr<nsIScriptSecurityManager> securityManager =
2298 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
2299 NS_ENSURE_SUCCESS(rv, rv);
2301 // This is terrible hack and should go away along with this whole method.
2302 nsCOMPtr<nsIPrincipal> principal;
2303 rv = securityManager->GetCodebasePrincipal(aURI, getter_AddRefs(principal));
2304 if (NS_FAILED(rv))
2305 return rv;
2307 return GetSessionStorageForPrincipal(principal, aDocumentURI, aCreate, aStorage);
2310 nsresult
2311 nsDocShell::AddSessionStorage(nsIPrincipal* aPrincipal,
2312 nsIDOMStorage* aStorage)
2314 NS_ENSURE_ARG_POINTER(aStorage);
2316 if (!aPrincipal)
2317 return NS_OK;
2319 nsCOMPtr<nsIDocShellTreeItem> topItem;
2320 nsresult rv = GetSameTypeRootTreeItem(getter_AddRefs(topItem));
2321 if (NS_FAILED(rv))
2322 return rv;
2324 if (topItem) {
2325 nsCOMPtr<nsIDocShell> topDocShell = do_QueryInterface(topItem);
2326 if (topDocShell == this) {
2327 nsCAutoString currentDomain;
2328 rv = GetPrincipalDomain(aPrincipal, currentDomain);
2329 if (NS_FAILED(rv))
2330 return rv;
2332 if (currentDomain.IsEmpty())
2333 return NS_ERROR_FAILURE;
2335 // Do not replace an existing session storage.
2336 if (mStorages.GetWeak(currentDomain))
2337 return NS_ERROR_NOT_AVAILABLE;
2339 #if defined(PR_LOGGING) && defined(DEBUG)
2340 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
2341 ("nsDocShell[%p]: was added a sessionStorage %p",
2342 this, aStorage));
2343 #endif
2344 if (!mStorages.Put(currentDomain, aStorage))
2345 return NS_ERROR_OUT_OF_MEMORY;
2347 else {
2348 return topDocShell->AddSessionStorage(aPrincipal, aStorage);
2352 return NS_OK;
2355 NS_IMETHODIMP
2356 nsDocShell::GetCurrentDocumentChannel(nsIChannel** aResult)
2358 NS_IF_ADDREF(*aResult = GetCurrentDocChannel());
2359 return NS_OK;
2362 nsIChannel*
2363 nsDocShell::GetCurrentDocChannel()
2365 if (mContentViewer) {
2366 nsIDocument* doc = mContentViewer->GetDocument();
2367 if (doc) {
2368 return doc->GetChannel();
2371 return nsnull;
2374 //*****************************************************************************
2375 // nsDocShell::nsIDocShellTreeItem
2376 //*****************************************************************************
2378 NS_IMETHODIMP
2379 nsDocShell::GetName(PRUnichar ** aName)
2381 NS_ENSURE_ARG_POINTER(aName);
2382 *aName = ToNewUnicode(mName);
2383 return NS_OK;
2386 NS_IMETHODIMP
2387 nsDocShell::SetName(const PRUnichar * aName)
2389 mName = aName; // this does a copy of aName
2390 return NS_OK;
2393 NS_IMETHODIMP
2394 nsDocShell::NameEquals(const PRUnichar *aName, PRBool *_retval)
2396 NS_ENSURE_ARG_POINTER(aName);
2397 NS_ENSURE_ARG_POINTER(_retval);
2398 *_retval = mName.Equals(aName);
2399 return NS_OK;
2402 NS_IMETHODIMP
2403 nsDocShell::GetItemType(PRInt32 * aItemType)
2405 NS_ENSURE_ARG_POINTER(aItemType);
2407 *aItemType = mItemType;
2408 return NS_OK;
2411 NS_IMETHODIMP
2412 nsDocShell::SetItemType(PRInt32 aItemType)
2414 NS_ENSURE_ARG((aItemType == typeChrome) || (typeContent == aItemType));
2416 // Only allow setting the type on root docshells. Those would be the ones
2417 // that have the docloader service as mParent or have no mParent at all.
2418 nsCOMPtr<nsIDocumentLoader> docLoaderService =
2419 do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID);
2420 NS_ENSURE_TRUE(docLoaderService, NS_ERROR_UNEXPECTED);
2422 NS_ENSURE_STATE(!mParent || mParent == docLoaderService);
2424 mItemType = aItemType;
2426 // disable auth prompting for anything but content
2427 mAllowAuth = mItemType == typeContent;
2429 nsRefPtr<nsPresContext> presContext = nsnull;
2430 GetPresContext(getter_AddRefs(presContext));
2431 if (presContext) {
2432 presContext->InvalidateIsChromeCache();
2435 return NS_OK;
2438 NS_IMETHODIMP
2439 nsDocShell::GetParent(nsIDocShellTreeItem ** aParent)
2441 if (!mParent) {
2442 *aParent = nsnull;
2443 } else {
2444 CallQueryInterface(mParent, aParent);
2446 // Note that in the case when the parent is not an nsIDocShellTreeItem we
2447 // don't want to throw; we just want to return null.
2448 return NS_OK;
2451 nsresult
2452 nsDocShell::SetDocLoaderParent(nsDocLoader * aParent)
2454 nsDocLoader::SetDocLoaderParent(aParent);
2456 // Curse ambiguous nsISupports inheritance!
2457 nsISupports* parent = GetAsSupports(aParent);
2459 // If parent is another docshell, we inherit all their flags for
2460 // allowing plugins, scripting etc.
2461 nsCOMPtr<nsIDocShell> parentAsDocShell(do_QueryInterface(parent));
2462 if (parentAsDocShell)
2464 PRBool value;
2465 if (NS_SUCCEEDED(parentAsDocShell->GetAllowPlugins(&value)))
2467 SetAllowPlugins(value);
2469 if (NS_SUCCEEDED(parentAsDocShell->GetAllowJavascript(&value)))
2471 SetAllowJavascript(value);
2473 if (NS_SUCCEEDED(parentAsDocShell->GetAllowMetaRedirects(&value)))
2475 SetAllowMetaRedirects(value);
2477 if (NS_SUCCEEDED(parentAsDocShell->GetAllowSubframes(&value)))
2479 SetAllowSubframes(value);
2481 if (NS_SUCCEEDED(parentAsDocShell->GetAllowImages(&value)))
2483 SetAllowImages(value);
2485 if (NS_FAILED(parentAsDocShell->GetAllowDNSPrefetch(&value))) {
2486 value = PR_FALSE;
2488 SetAllowDNSPrefetch(value);
2491 nsCOMPtr<nsIURIContentListener> parentURIListener(do_GetInterface(parent));
2492 if (parentURIListener)
2493 mContentListener->SetParentContentListener(parentURIListener);
2494 return NS_OK;
2497 NS_IMETHODIMP
2498 nsDocShell::GetSameTypeParent(nsIDocShellTreeItem ** aParent)
2500 NS_ENSURE_ARG_POINTER(aParent);
2501 *aParent = nsnull;
2503 nsCOMPtr<nsIDocShellTreeItem> parent =
2504 do_QueryInterface(GetAsSupports(mParent));
2505 if (!parent)
2506 return NS_OK;
2508 PRInt32 parentType;
2509 NS_ENSURE_SUCCESS(parent->GetItemType(&parentType), NS_ERROR_FAILURE);
2511 if (parentType == mItemType) {
2512 parent.swap(*aParent);
2514 return NS_OK;
2517 NS_IMETHODIMP
2518 nsDocShell::GetRootTreeItem(nsIDocShellTreeItem ** aRootTreeItem)
2520 NS_ENSURE_ARG_POINTER(aRootTreeItem);
2521 *aRootTreeItem = static_cast<nsIDocShellTreeItem *>(this);
2523 nsCOMPtr<nsIDocShellTreeItem> parent;
2524 NS_ENSURE_SUCCESS(GetParent(getter_AddRefs(parent)), NS_ERROR_FAILURE);
2525 while (parent) {
2526 *aRootTreeItem = parent;
2527 NS_ENSURE_SUCCESS((*aRootTreeItem)->GetParent(getter_AddRefs(parent)),
2528 NS_ERROR_FAILURE);
2530 NS_ADDREF(*aRootTreeItem);
2531 return NS_OK;
2534 NS_IMETHODIMP
2535 nsDocShell::GetSameTypeRootTreeItem(nsIDocShellTreeItem ** aRootTreeItem)
2537 NS_ENSURE_ARG_POINTER(aRootTreeItem);
2538 *aRootTreeItem = static_cast<nsIDocShellTreeItem *>(this);
2540 nsCOMPtr<nsIDocShellTreeItem> parent;
2541 NS_ENSURE_SUCCESS(GetSameTypeParent(getter_AddRefs(parent)),
2542 NS_ERROR_FAILURE);
2543 while (parent) {
2544 *aRootTreeItem = parent;
2545 NS_ENSURE_SUCCESS((*aRootTreeItem)->
2546 GetSameTypeParent(getter_AddRefs(parent)),
2547 NS_ERROR_FAILURE);
2549 NS_ADDREF(*aRootTreeItem);
2550 return NS_OK;
2553 /* static */
2554 PRBool
2555 nsDocShell::CanAccessItem(nsIDocShellTreeItem* aTargetItem,
2556 nsIDocShellTreeItem* aAccessingItem,
2557 PRBool aConsiderOpener)
2559 NS_PRECONDITION(aTargetItem, "Must have target item!");
2561 if (!gValidateOrigin || !aAccessingItem) {
2562 // Good to go
2563 return PR_TRUE;
2566 // XXXbz should we care if aAccessingItem or the document therein is
2567 // chrome? Should those get extra privileges?
2569 // For historical context, see:
2571 // Bug 13871: Prevent frameset spoofing
2572 // Bug 103638: Targets with same name in different windows open in wrong
2573 // window with javascript
2574 // Bug 408052: Adopt "ancestor" frame navigation policy
2576 // Now do a security check
2578 // Allow navigation if
2579 // 1) aAccessingItem can script aTargetItem or one of its ancestors in
2580 // the frame hierarchy or
2581 // 2) aTargetItem is a top-level frame and aAccessingItem is its descendant
2582 // 3) aTargetItem is a top-level frame and aAccessingItem can target
2583 // its opener per rule (1) or (2).
2585 if (aTargetItem == aAccessingItem) {
2586 // A frame is allowed to navigate itself.
2587 return PR_TRUE;
2590 nsCOMPtr<nsIDocShellTreeItem> accessingRoot;
2591 aAccessingItem->GetSameTypeRootTreeItem(getter_AddRefs(accessingRoot));
2593 if (aTargetItem == accessingRoot) {
2594 // A frame can navigate its root.
2595 return PR_TRUE;
2598 // Check if aAccessingItem can navigate one of aTargetItem's ancestors.
2599 nsCOMPtr<nsIDocShellTreeItem> target = aTargetItem;
2600 do {
2601 if (ValidateOrigin(aAccessingItem, target)) {
2602 return PR_TRUE;
2605 nsCOMPtr<nsIDocShellTreeItem> parent;
2606 target->GetSameTypeParent(getter_AddRefs(parent));
2607 parent.swap(target);
2608 } while (target);
2610 nsCOMPtr<nsIDocShellTreeItem> targetRoot;
2611 aTargetItem->GetSameTypeRootTreeItem(getter_AddRefs(targetRoot));
2613 if (aTargetItem != targetRoot) {
2614 // target is a subframe, not in accessor's frame hierarchy, and all its
2615 // ancestors have origins different from that of the accessor. Don't
2616 // allow access.
2617 return PR_FALSE;
2620 if (!aConsiderOpener) {
2621 // All done here
2622 return PR_FALSE;
2625 nsCOMPtr<nsIDOMWindow> targetWindow(do_GetInterface(aTargetItem));
2626 nsCOMPtr<nsIDOMWindowInternal> targetInternal(do_QueryInterface(targetWindow));
2627 if (!targetInternal) {
2628 NS_ERROR("This should not happen, really");
2629 return PR_FALSE;
2632 nsCOMPtr<nsIDOMWindowInternal> targetOpener;
2633 targetInternal->GetOpener(getter_AddRefs(targetOpener));
2634 nsCOMPtr<nsIWebNavigation> openerWebNav(do_GetInterface(targetOpener));
2635 nsCOMPtr<nsIDocShellTreeItem> openerItem(do_QueryInterface(openerWebNav));
2637 if (!openerItem) {
2638 return PR_FALSE;
2641 return CanAccessItem(openerItem, aAccessingItem, PR_FALSE);
2644 static PRBool
2645 ItemIsActive(nsIDocShellTreeItem *aItem)
2647 nsCOMPtr<nsIDOMWindow> tmp(do_GetInterface(aItem));
2648 nsCOMPtr<nsIDOMWindowInternal> window(do_QueryInterface(tmp));
2650 if (window) {
2651 PRBool isClosed;
2653 if (NS_SUCCEEDED(window->GetClosed(&isClosed)) && !isClosed) {
2654 return PR_TRUE;
2658 return PR_FALSE;
2661 NS_IMETHODIMP
2662 nsDocShell::FindItemWithName(const PRUnichar * aName,
2663 nsISupports * aRequestor,
2664 nsIDocShellTreeItem * aOriginalRequestor,
2665 nsIDocShellTreeItem ** _retval)
2667 NS_ENSURE_ARG(aName);
2668 NS_ENSURE_ARG_POINTER(_retval);
2670 // If we don't find one, we return NS_OK and a null result
2671 *_retval = nsnull;
2673 if (!*aName)
2674 return NS_OK;
2676 if (!aRequestor)
2678 nsCOMPtr<nsIDocShellTreeItem> foundItem;
2680 // This is the entry point into the target-finding algorithm. Check
2681 // for special names. This should only be done once, hence the check
2682 // for a null aRequestor.
2684 nsDependentString name(aName);
2685 if (name.LowerCaseEqualsLiteral("_self")) {
2686 foundItem = this;
2688 else if (name.LowerCaseEqualsLiteral("_blank"))
2690 // Just return null. Caller must handle creating a new window with
2691 // a blank name himself.
2692 return NS_OK;
2694 else if (name.LowerCaseEqualsLiteral("_parent"))
2696 GetSameTypeParent(getter_AddRefs(foundItem));
2697 if(!foundItem)
2698 foundItem = this;
2700 else if (name.LowerCaseEqualsLiteral("_top"))
2702 GetSameTypeRootTreeItem(getter_AddRefs(foundItem));
2703 NS_ASSERTION(foundItem, "Must have this; worst case it's us!");
2705 // _main is an IE target which should be case-insensitive but isn't
2706 // see bug 217886 for details
2707 else if (name.LowerCaseEqualsLiteral("_content") ||
2708 name.EqualsLiteral("_main"))
2710 // Must pass our same type root as requestor to the
2711 // treeowner to make sure things work right.
2712 nsCOMPtr<nsIDocShellTreeItem> root;
2713 GetSameTypeRootTreeItem(getter_AddRefs(root));
2714 if (mTreeOwner) {
2715 NS_ASSERTION(root, "Must have this; worst case it's us!");
2716 mTreeOwner->FindItemWithName(aName, root, aOriginalRequestor,
2717 getter_AddRefs(foundItem));
2719 #ifdef DEBUG
2720 else {
2721 NS_ERROR("Someone isn't setting up the tree owner. "
2722 "You might like to try that. "
2723 "Things will.....you know, work.");
2724 // Note: _content should always exist. If we don't have one
2725 // hanging off the treeowner, just create a named window....
2726 // so don't return here, in case we did that and can now find
2727 // it.
2728 // XXXbz should we be using |root| instead of creating
2729 // a new window?
2731 #endif
2734 if (foundItem && !CanAccessItem(foundItem, aOriginalRequestor)) {
2735 foundItem = nsnull;
2738 if (foundItem) {
2739 // We return foundItem here even if it's not an active
2740 // item since all the names we've dealt with so far are
2741 // special cases that we won't bother looking for further.
2743 foundItem.swap(*_retval);
2744 return NS_OK;
2748 // Keep looking
2750 // First we check our name.
2751 if (mName.Equals(aName) && ItemIsActive(this) &&
2752 CanAccessItem(this, aOriginalRequestor)) {
2753 NS_ADDREF(*_retval = this);
2754 return NS_OK;
2757 // This QI may fail, but the places where we want to compare, comparing
2758 // against nsnull serves the same purpose.
2759 nsCOMPtr<nsIDocShellTreeItem> reqAsTreeItem(do_QueryInterface(aRequestor));
2761 // Second we check our children making sure not to ask a child if
2762 // it is the aRequestor.
2763 #ifdef DEBUG
2764 nsresult rv =
2765 #endif
2766 FindChildWithName(aName, PR_TRUE, PR_TRUE, reqAsTreeItem,
2767 aOriginalRequestor, _retval);
2768 NS_ASSERTION(NS_SUCCEEDED(rv),
2769 "FindChildWithName should not be failing here.");
2770 if (*_retval)
2771 return NS_OK;
2773 // Third if we have a parent and it isn't the requestor then we
2774 // should ask it to do the search. If it is the requestor we
2775 // should just stop here and let the parent do the rest. If we
2776 // don't have a parent, then we should ask the
2777 // docShellTreeOwner to do the search.
2778 nsCOMPtr<nsIDocShellTreeItem> parentAsTreeItem =
2779 do_QueryInterface(GetAsSupports(mParent));
2780 if (parentAsTreeItem) {
2781 if (parentAsTreeItem == reqAsTreeItem)
2782 return NS_OK;
2784 PRInt32 parentType;
2785 parentAsTreeItem->GetItemType(&parentType);
2786 if (parentType == mItemType) {
2787 return parentAsTreeItem->
2788 FindItemWithName(aName,
2789 static_cast<nsIDocShellTreeItem*>
2790 (this),
2791 aOriginalRequestor,
2792 _retval);
2796 // If the parent is null or not of the same type fall through and ask tree
2797 // owner.
2799 // This may fail, but comparing against null serves the same purpose
2800 nsCOMPtr<nsIDocShellTreeOwner>
2801 reqAsTreeOwner(do_QueryInterface(aRequestor));
2803 if (mTreeOwner && mTreeOwner != reqAsTreeOwner) {
2804 return mTreeOwner->
2805 FindItemWithName(aName, this, aOriginalRequestor, _retval);
2808 return NS_OK;
2811 NS_IMETHODIMP
2812 nsDocShell::GetTreeOwner(nsIDocShellTreeOwner ** aTreeOwner)
2814 NS_ENSURE_ARG_POINTER(aTreeOwner);
2816 *aTreeOwner = mTreeOwner;
2817 NS_IF_ADDREF(*aTreeOwner);
2818 return NS_OK;
2821 #ifdef DEBUG_DOCSHELL_FOCUS
2822 static void
2823 PrintDocTree(nsIDocShellTreeItem * aParentNode, int aLevel)
2825 for (PRInt32 i=0;i<aLevel;i++) printf(" ");
2827 PRInt32 childWebshellCount;
2828 aParentNode->GetChildCount(&childWebshellCount);
2829 nsCOMPtr<nsIDocShell> parentAsDocShell(do_QueryInterface(aParentNode));
2830 PRInt32 type;
2831 aParentNode->GetItemType(&type);
2832 nsCOMPtr<nsIPresShell> presShell;
2833 parentAsDocShell->GetPresShell(getter_AddRefs(presShell));
2834 nsRefPtr<nsPresContext> presContext;
2835 parentAsDocShell->GetPresContext(getter_AddRefs(presContext));
2836 nsIDocument *doc = presShell->GetDocument();
2838 nsCOMPtr<nsIDOMWindowInternal> domwin(doc->GetWindow());
2840 nsCOMPtr<nsIWidget> widget;
2841 nsIViewManager* vm = presShell->GetViewManager();
2842 if (vm) {
2843 vm->GetWidget(getter_AddRefs(widget));
2845 dom::Element* rootElement = doc->GetRootElement();
2847 printf("DS %p Ty %s Doc %p DW %p EM %p CN %p\n",
2848 (void*)parentAsDocShell.get(),
2849 type==nsIDocShellTreeItem::typeChrome?"Chr":"Con",
2850 (void*)doc, (void*)domwin.get(),
2851 (void*)presContext->EventStateManager(), (void*)rootElement);
2853 if (childWebshellCount > 0) {
2854 for (PRInt32 i=0;i<childWebshellCount;i++) {
2855 nsCOMPtr<nsIDocShellTreeItem> child;
2856 aParentNode->GetChildAt(i, getter_AddRefs(child));
2857 PrintDocTree(child, aLevel+1);
2862 static void
2863 PrintDocTree(nsIDocShellTreeItem * aParentNode)
2865 NS_ASSERTION(aParentNode, "Pointer is null!");
2867 nsCOMPtr<nsIDocShellTreeItem> parentItem;
2868 aParentNode->GetParent(getter_AddRefs(parentItem));
2869 while (parentItem) {
2870 nsCOMPtr<nsIDocShellTreeItem>tmp;
2871 parentItem->GetParent(getter_AddRefs(tmp));
2872 if (!tmp) {
2873 break;
2875 parentItem = tmp;
2878 if (!parentItem) {
2879 parentItem = aParentNode;
2882 PrintDocTree(parentItem, 0);
2884 #endif
2886 NS_IMETHODIMP
2887 nsDocShell::SetTreeOwner(nsIDocShellTreeOwner * aTreeOwner)
2889 #ifdef DEBUG_DOCSHELL_FOCUS
2890 nsCOMPtr<nsIDocShellTreeItem> item(do_QueryInterface(aTreeOwner));
2891 if (item) {
2892 PrintDocTree(item);
2894 #endif
2896 // Don't automatically set the progress based on the tree owner for frames
2897 if (!IsFrame()) {
2898 nsCOMPtr<nsIWebProgress> webProgress =
2899 do_QueryInterface(GetAsSupports(this));
2901 if (webProgress) {
2902 nsCOMPtr<nsIWebProgressListener>
2903 oldListener(do_QueryInterface(mTreeOwner));
2904 nsCOMPtr<nsIWebProgressListener>
2905 newListener(do_QueryInterface(aTreeOwner));
2907 if (oldListener) {
2908 webProgress->RemoveProgressListener(oldListener);
2911 if (newListener) {
2912 webProgress->AddProgressListener(newListener,
2913 nsIWebProgress::NOTIFY_ALL);
2918 mTreeOwner = aTreeOwner; // Weak reference per API
2920 PRInt32 i, n = mChildList.Count();
2921 for (i = 0; i < n; i++) {
2922 nsCOMPtr<nsIDocShellTreeItem> child = do_QueryInterface(ChildAt(i));
2923 NS_ENSURE_TRUE(child, NS_ERROR_FAILURE);
2924 PRInt32 childType = ~mItemType; // Set it to not us in case the get fails
2925 child->GetItemType(&childType); // We don't care if this fails, if it does we won't set the owner
2926 if (childType == mItemType)
2927 child->SetTreeOwner(aTreeOwner);
2930 return NS_OK;
2933 NS_IMETHODIMP
2934 nsDocShell::SetChildOffset(PRUint32 aChildOffset)
2936 mChildOffset = aChildOffset;
2937 return NS_OK;
2940 NS_IMETHODIMP
2941 nsDocShell::GetIsInUnload(PRBool* aIsInUnload)
2943 *aIsInUnload = mFiredUnloadEvent;
2944 return NS_OK;
2947 //*****************************************************************************
2948 // nsDocShell::nsIDocShellTreeNode
2949 //*****************************************************************************
2951 NS_IMETHODIMP
2952 nsDocShell::GetChildCount(PRInt32 * aChildCount)
2954 NS_ENSURE_ARG_POINTER(aChildCount);
2955 *aChildCount = mChildList.Count();
2956 return NS_OK;
2961 NS_IMETHODIMP
2962 nsDocShell::AddChild(nsIDocShellTreeItem * aChild)
2964 NS_ENSURE_ARG_POINTER(aChild);
2966 nsRefPtr<nsDocLoader> childAsDocLoader = GetAsDocLoader(aChild);
2967 NS_ENSURE_TRUE(childAsDocLoader, NS_ERROR_UNEXPECTED);
2969 // Make sure we're not creating a loop in the docshell tree
2970 nsDocLoader* ancestor = this;
2971 do {
2972 if (childAsDocLoader == ancestor) {
2973 return NS_ERROR_ILLEGAL_VALUE;
2975 ancestor = ancestor->GetParent();
2976 } while (ancestor);
2978 // Make sure to remove the child from its current parent.
2979 nsDocLoader* childsParent = childAsDocLoader->GetParent();
2980 if (childsParent) {
2981 childsParent->RemoveChildLoader(childAsDocLoader);
2984 // Make sure to clear the treeowner in case this child is a different type
2985 // from us.
2986 aChild->SetTreeOwner(nsnull);
2988 nsresult res = AddChildLoader(childAsDocLoader);
2989 NS_ENSURE_SUCCESS(res, res);
2990 NS_ASSERTION(mChildList.Count() > 0,
2991 "child list must not be empty after a successful add");
2993 // Set the child's index in the parent's children list
2994 // XXX What if the parent had different types of children?
2995 // XXX in that case docshell hierarchy and SH hierarchy won't match.
2997 nsCOMPtr<nsIDocShell> childDocShell = do_QueryInterface(aChild);
2998 if (childDocShell) {
2999 // If there are frameloaders in the finalization list, reduce
3000 // the offset so that the SH hierarchy is more likely to match the
3001 // docshell hierarchy
3002 nsCOMPtr<nsIDOMDocument> domDoc =
3003 do_GetInterface(GetAsSupports(this));
3004 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
3005 PRUint32 offset = mChildList.Count() - 1;
3006 if (doc) {
3007 PRUint32 oldChildCount = offset; // Current child count - 1
3008 for (PRUint32 i = 0; i < oldChildCount; ++i) {
3009 nsCOMPtr<nsIDocShell> child = do_QueryInterface(ChildAt(i));
3010 if (doc->FrameLoaderScheduledToBeFinalized(child)) {
3011 --offset;
3016 childDocShell->SetChildOffset(offset);
3020 /* Set the child's global history if the parent has one */
3021 if (mGlobalHistory) {
3022 nsCOMPtr<nsIDocShellHistory>
3023 dsHistoryChild(do_QueryInterface(aChild));
3024 if (dsHistoryChild)
3025 dsHistoryChild->SetUseGlobalHistory(PR_TRUE);
3029 PRInt32 childType = ~mItemType; // Set it to not us in case the get fails
3030 aChild->GetItemType(&childType);
3031 if (childType != mItemType)
3032 return NS_OK;
3033 // Everything below here is only done when the child is the same type.
3036 aChild->SetTreeOwner(mTreeOwner);
3038 nsCOMPtr<nsIDocShell> childAsDocShell(do_QueryInterface(aChild));
3039 if (!childAsDocShell)
3040 return NS_OK;
3042 // charset, style-disabling, and zoom will be inherited in SetupNewViewer()
3044 // Now take this document's charset and set the parentCharset field of the
3045 // child's DocumentCharsetInfo to it. We'll later use that field, in the
3046 // loading process, for the charset choosing algorithm.
3047 // If we fail, at any point, we just return NS_OK.
3048 // This code has some performance impact. But this will be reduced when
3049 // the current charset will finally be stored as an Atom, avoiding the
3050 // alias resolution extra look-up.
3052 // we are NOT going to propagate the charset is this Chrome's docshell
3053 if (mItemType == nsIDocShellTreeItem::typeChrome)
3054 return NS_OK;
3056 // get the child's docCSInfo object
3057 nsCOMPtr<nsIDocumentCharsetInfo> dcInfo = NULL;
3058 res = childAsDocShell->GetDocumentCharsetInfo(getter_AddRefs(dcInfo));
3059 if (NS_FAILED(res) || (!dcInfo))
3060 return NS_OK;
3062 // get the parent's current charset
3063 if (!mContentViewer)
3064 return NS_OK;
3065 nsIDocument* doc = mContentViewer->GetDocument();
3066 if (!doc)
3067 return NS_OK;
3068 const nsACString &parentCS = doc->GetDocumentCharacterSet();
3070 PRBool isWyciwyg = PR_FALSE;
3072 if (mCurrentURI) {
3073 // Check if the url is wyciwyg
3074 mCurrentURI->SchemeIs("wyciwyg", &isWyciwyg);
3077 if (!isWyciwyg) {
3078 // If this docshell is loaded from a wyciwyg: URI, don't
3079 // advertise our charset since it does not in any way reflect
3080 // the actual source charset, which is what we're trying to
3081 // expose here.
3083 // set the child's parentCharset
3084 nsCOMPtr<nsIAtom> parentCSAtom(do_GetAtom(parentCS));
3085 res = dcInfo->SetParentCharset(parentCSAtom);
3086 if (NS_FAILED(res))
3087 return NS_OK;
3089 PRInt32 charsetSource = doc->GetDocumentCharacterSetSource();
3091 // set the child's parentCharset
3092 res = dcInfo->SetParentCharsetSource(charsetSource);
3093 if (NS_FAILED(res))
3094 return NS_OK;
3097 // printf("### 1 >>> Adding child. Parent CS = %s. ItemType = %d.\n", NS_LossyConvertUTF16toASCII(parentCS).get(), mItemType);
3099 return NS_OK;
3102 NS_IMETHODIMP
3103 nsDocShell::RemoveChild(nsIDocShellTreeItem * aChild)
3105 NS_ENSURE_ARG_POINTER(aChild);
3107 nsRefPtr<nsDocLoader> childAsDocLoader = GetAsDocLoader(aChild);
3108 NS_ENSURE_TRUE(childAsDocLoader, NS_ERROR_UNEXPECTED);
3110 nsresult rv = RemoveChildLoader(childAsDocLoader);
3111 NS_ENSURE_SUCCESS(rv, rv);
3113 aChild->SetTreeOwner(nsnull);
3115 return nsDocLoader::AddDocLoaderAsChildOfRoot(childAsDocLoader);
3118 NS_IMETHODIMP
3119 nsDocShell::GetChildAt(PRInt32 aIndex, nsIDocShellTreeItem ** aChild)
3121 NS_ENSURE_ARG_POINTER(aChild);
3123 #ifdef DEBUG
3124 if (aIndex < 0) {
3125 NS_WARNING("Negative index passed to GetChildAt");
3127 else if (aIndex >= mChildList.Count()) {
3128 NS_WARNING("Too large an index passed to GetChildAt");
3130 #endif
3132 nsIDocumentLoader* child = SafeChildAt(aIndex);
3133 NS_ENSURE_TRUE(child, NS_ERROR_UNEXPECTED);
3135 return CallQueryInterface(child, aChild);
3138 NS_IMETHODIMP
3139 nsDocShell::FindChildWithName(const PRUnichar * aName,
3140 PRBool aRecurse, PRBool aSameType,
3141 nsIDocShellTreeItem * aRequestor,
3142 nsIDocShellTreeItem * aOriginalRequestor,
3143 nsIDocShellTreeItem ** _retval)
3145 NS_ENSURE_ARG(aName);
3146 NS_ENSURE_ARG_POINTER(_retval);
3148 *_retval = nsnull; // if we don't find one, we return NS_OK and a null result
3150 if (!*aName)
3151 return NS_OK;
3153 nsXPIDLString childName;
3154 PRInt32 i, n = mChildList.Count();
3155 for (i = 0; i < n; i++) {
3156 nsCOMPtr<nsIDocShellTreeItem> child = do_QueryInterface(ChildAt(i));
3157 NS_ENSURE_TRUE(child, NS_ERROR_FAILURE);
3158 PRInt32 childType;
3159 child->GetItemType(&childType);
3161 if (aSameType && (childType != mItemType))
3162 continue;
3164 PRBool childNameEquals = PR_FALSE;
3165 child->NameEquals(aName, &childNameEquals);
3166 if (childNameEquals && ItemIsActive(child) &&
3167 CanAccessItem(child, aOriginalRequestor)) {
3168 child.swap(*_retval);
3169 break;
3172 if (childType != mItemType) //Only ask it to check children if it is same type
3173 continue;
3175 if (aRecurse && (aRequestor != child)) // Only ask the child if it isn't the requestor
3177 // See if child contains the shell with the given name
3178 #ifdef DEBUG
3179 nsresult rv =
3180 #endif
3181 child->FindChildWithName(aName, PR_TRUE,
3182 aSameType,
3183 static_cast<nsIDocShellTreeItem*>
3184 (this),
3185 aOriginalRequestor,
3186 _retval);
3187 NS_ASSERTION(NS_SUCCEEDED(rv),
3188 "FindChildWithName should not fail here");
3189 if (*_retval) // found it
3190 return NS_OK;
3193 return NS_OK;
3196 //*****************************************************************************
3197 // nsDocShell::nsIDocShellHistory
3198 //*****************************************************************************
3199 NS_IMETHODIMP
3200 nsDocShell::GetChildSHEntry(PRInt32 aChildOffset, nsISHEntry ** aResult)
3202 nsresult rv = NS_OK;
3204 NS_ENSURE_ARG_POINTER(aResult);
3205 *aResult = nsnull;
3208 // A nsISHEntry for a child is *only* available when the parent is in
3209 // the progress of loading a document too...
3211 if (mLSHE) {
3212 /* Before looking for the subframe's url, check
3213 * the expiration status of the parent. If the parent
3214 * has expired from cache, then subframes will not be
3215 * loaded from history in certain situations.
3217 PRBool parentExpired=PR_FALSE;
3218 mLSHE->GetExpirationStatus(&parentExpired);
3220 /* Get the parent's Load Type so that it can be set on the child too.
3221 * By default give a loadHistory value
3223 PRUint32 loadType = nsIDocShellLoadInfo::loadHistory;
3224 mLSHE->GetLoadType(&loadType);
3225 // If the user did a shift-reload on this frameset page,
3226 // we don't want to load the subframes from history.
3227 if (loadType == nsIDocShellLoadInfo::loadReloadBypassCache ||
3228 loadType == nsIDocShellLoadInfo::loadReloadBypassProxy ||
3229 loadType == nsIDocShellLoadInfo::loadReloadBypassProxyAndCache ||
3230 loadType == nsIDocShellLoadInfo::loadRefresh)
3231 return rv;
3233 /* If the user pressed reload and the parent frame has expired
3234 * from cache, we do not want to load the child frame from history.
3236 if (parentExpired && (loadType == nsIDocShellLoadInfo::loadReloadNormal)) {
3237 // The parent has expired. Return null.
3238 *aResult = nsnull;
3239 return rv;
3242 nsCOMPtr<nsISHContainer> container(do_QueryInterface(mLSHE));
3243 if (container) {
3244 // Get the child subframe from session history.
3245 rv = container->GetChildAt(aChildOffset, aResult);
3246 if (*aResult)
3247 (*aResult)->SetLoadType(loadType);
3250 return rv;
3253 NS_IMETHODIMP
3254 nsDocShell::AddChildSHEntry(nsISHEntry * aCloneRef, nsISHEntry * aNewEntry,
3255 PRInt32 aChildOffset, PRUint32 loadType)
3257 nsresult rv;
3259 if (mLSHE && loadType != LOAD_PUSHSTATE) {
3260 /* You get here if you are currently building a
3261 * hierarchy ie.,you just visited a frameset page
3263 nsCOMPtr<nsISHContainer> container(do_QueryInterface(mLSHE, &rv));
3264 if (container) {
3265 rv = container->AddChild(aNewEntry, aChildOffset);
3268 else if (!aCloneRef) {
3269 /* This is an initial load in some subframe. Just append it if we can */
3270 nsCOMPtr<nsISHContainer> container(do_QueryInterface(mOSHE, &rv));
3271 if (container) {
3272 rv = container->AddChild(aNewEntry, aChildOffset);
3275 else if (mSessionHistory) {
3276 /* You are currently in the rootDocShell.
3277 * You will get here when a subframe has a new url
3278 * to load and you have walked up the tree all the
3279 * way to the top to clone the current SHEntry hierarchy
3280 * and replace the subframe where a new url was loaded with
3281 * a new entry.
3283 PRInt32 index = -1;
3284 nsCOMPtr<nsIHistoryEntry> currentHE;
3285 mSessionHistory->GetIndex(&index);
3286 if (index < 0)
3287 return NS_ERROR_FAILURE;
3289 rv = mSessionHistory->GetEntryAtIndex(index, PR_FALSE,
3290 getter_AddRefs(currentHE));
3291 NS_ENSURE_TRUE(currentHE, NS_ERROR_FAILURE);
3293 nsCOMPtr<nsISHEntry> currentEntry(do_QueryInterface(currentHE));
3294 if (currentEntry) {
3295 PRUint32 cloneID = 0;
3296 nsCOMPtr<nsISHEntry> nextEntry;
3297 aCloneRef->GetID(&cloneID);
3298 rv = CloneAndReplace(currentEntry, this, cloneID, aNewEntry,
3299 getter_AddRefs(nextEntry));
3301 if (NS_SUCCEEDED(rv)) {
3302 nsCOMPtr<nsISHistoryInternal>
3303 shPrivate(do_QueryInterface(mSessionHistory));
3304 NS_ENSURE_TRUE(shPrivate, NS_ERROR_FAILURE);
3305 rv = shPrivate->AddEntry(nextEntry, PR_TRUE);
3309 else {
3310 /* Just pass this along */
3311 nsCOMPtr<nsIDocShellHistory> parent =
3312 do_QueryInterface(GetAsSupports(mParent), &rv);
3313 if (parent) {
3314 rv = parent->AddChildSHEntry(aCloneRef, aNewEntry, aChildOffset,
3315 loadType);
3318 return rv;
3321 nsresult
3322 nsDocShell::DoAddChildSHEntry(nsISHEntry* aNewEntry, PRInt32 aChildOffset)
3324 /* You will get here when you are in a subframe and
3325 * a new url has been loaded on you.
3326 * The mOSHE in this subframe will be the previous url's
3327 * mOSHE. This mOSHE will be used as the identification
3328 * for this subframe in the CloneAndReplace function.
3331 // In this case, we will end up calling AddEntry, which increases the
3332 // current index by 1
3333 nsCOMPtr<nsISHistory> rootSH;
3334 GetRootSessionHistory(getter_AddRefs(rootSH));
3335 if (rootSH) {
3336 rootSH->GetIndex(&mPreviousTransIndex);
3339 nsresult rv;
3340 nsCOMPtr<nsIDocShellHistory> parent =
3341 do_QueryInterface(GetAsSupports(mParent), &rv);
3342 if (parent) {
3343 rv = parent->AddChildSHEntry(mOSHE, aNewEntry, aChildOffset, mLoadType);
3347 if (rootSH) {
3348 rootSH->GetIndex(&mLoadedTransIndex);
3349 #ifdef DEBUG_PAGE_CACHE
3350 printf("Previous index: %d, Loaded index: %d\n\n", mPreviousTransIndex,
3351 mLoadedTransIndex);
3352 #endif
3355 return rv;
3358 NS_IMETHODIMP
3359 nsDocShell::SetUseGlobalHistory(PRBool aUseGlobalHistory)
3361 nsresult rv;
3363 if (!aUseGlobalHistory) {
3364 mGlobalHistory = nsnull;
3365 return NS_OK;
3368 if (mGlobalHistory) {
3369 return NS_OK;
3372 mGlobalHistory = do_GetService(NS_GLOBALHISTORY2_CONTRACTID, &rv);
3373 return rv;
3376 NS_IMETHODIMP
3377 nsDocShell::GetUseGlobalHistory(PRBool *aUseGlobalHistory)
3379 *aUseGlobalHistory = (mGlobalHistory != nsnull);
3380 return NS_OK;
3383 //-------------------------------------
3384 //-- Helper Method for Print discovery
3385 //-------------------------------------
3386 PRBool
3387 nsDocShell::IsPrintingOrPP(PRBool aDisplayErrorDialog)
3389 if (mIsPrintingOrPP && aDisplayErrorDialog) {
3390 DisplayLoadError(NS_ERROR_DOCUMENT_IS_PRINTMODE, nsnull, nsnull);
3393 return mIsPrintingOrPP;
3396 PRBool
3397 nsDocShell::IsNavigationAllowed(PRBool aDisplayPrintErrorDialog)
3399 return !IsPrintingOrPP(aDisplayPrintErrorDialog) && !mFiredUnloadEvent;
3402 //*****************************************************************************
3403 // nsDocShell::nsIWebNavigation
3404 //*****************************************************************************
3406 NS_IMETHODIMP
3407 nsDocShell::GetCanGoBack(PRBool * aCanGoBack)
3409 if (!IsNavigationAllowed(PR_FALSE)) {
3410 *aCanGoBack = PR_FALSE;
3411 return NS_OK; // JS may not handle returning of an error code
3413 nsresult rv;
3414 nsCOMPtr<nsISHistory> rootSH;
3415 rv = GetRootSessionHistory(getter_AddRefs(rootSH));
3416 nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
3417 NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
3418 rv = webnav->GetCanGoBack(aCanGoBack);
3419 return rv;
3423 NS_IMETHODIMP
3424 nsDocShell::GetCanGoForward(PRBool * aCanGoForward)
3426 if (!IsNavigationAllowed(PR_FALSE)) {
3427 *aCanGoForward = PR_FALSE;
3428 return NS_OK; // JS may not handle returning of an error code
3430 nsresult rv;
3431 nsCOMPtr<nsISHistory> rootSH;
3432 rv = GetRootSessionHistory(getter_AddRefs(rootSH));
3433 nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
3434 NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
3435 rv = webnav->GetCanGoForward(aCanGoForward);
3436 return rv;
3440 NS_IMETHODIMP
3441 nsDocShell::GoBack()
3443 if (!IsNavigationAllowed()) {
3444 return NS_OK; // JS may not handle returning of an error code
3446 nsresult rv;
3447 nsCOMPtr<nsISHistory> rootSH;
3448 rv = GetRootSessionHistory(getter_AddRefs(rootSH));
3449 nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
3450 NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
3451 rv = webnav->GoBack();
3452 return rv;
3456 NS_IMETHODIMP
3457 nsDocShell::GoForward()
3459 if (!IsNavigationAllowed()) {
3460 return NS_OK; // JS may not handle returning of an error code
3462 nsresult rv;
3463 nsCOMPtr<nsISHistory> rootSH;
3464 rv = GetRootSessionHistory(getter_AddRefs(rootSH));
3465 nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
3466 NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
3467 rv = webnav->GoForward();
3468 return rv;
3472 NS_IMETHODIMP nsDocShell::GotoIndex(PRInt32 aIndex)
3474 if (!IsNavigationAllowed()) {
3475 return NS_OK; // JS may not handle returning of an error code
3477 nsresult rv;
3478 nsCOMPtr<nsISHistory> rootSH;
3479 rv = GetRootSessionHistory(getter_AddRefs(rootSH));
3480 nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
3481 NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
3482 rv = webnav->GotoIndex(aIndex);
3483 return rv;
3487 NS_IMETHODIMP
3488 nsDocShell::LoadURI(const PRUnichar * aURI,
3489 PRUint32 aLoadFlags,
3490 nsIURI * aReferringURI,
3491 nsIInputStream * aPostStream,
3492 nsIInputStream * aHeaderStream)
3494 NS_ASSERTION((aLoadFlags & 0xf) == 0, "Unexpected flags");
3496 if (!IsNavigationAllowed()) {
3497 return NS_OK; // JS may not handle returning of an error code
3499 nsCOMPtr<nsIURI> uri;
3500 nsresult rv = NS_OK;
3502 // Create a URI from our string; if that succeeds, we want to
3503 // change aLoadFlags to not include the ALLOW_THIRD_PARTY_FIXUP
3504 // flag.
3506 NS_ConvertUTF16toUTF8 uriString(aURI);
3507 // Cleanup the empty spaces that might be on each end.
3508 uriString.Trim(" ");
3509 // Eliminate embedded newlines, which single-line text fields now allow:
3510 uriString.StripChars("\r\n");
3511 NS_ENSURE_TRUE(!uriString.IsEmpty(), NS_ERROR_FAILURE);
3513 rv = NS_NewURI(getter_AddRefs(uri), uriString);
3514 if (uri) {
3515 aLoadFlags &= ~LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
3518 if (sURIFixup) {
3519 // Call the fixup object. This will clobber the rv from NS_NewURI
3520 // above, but that's fine with us. Note that we need to do this even
3521 // if NS_NewURI returned a URI, because fixup handles nested URIs, etc
3522 // (things like view-source:mozilla.org for example).
3523 PRUint32 fixupFlags = 0;
3524 if (aLoadFlags & LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) {
3525 fixupFlags |= nsIURIFixup::FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP;
3527 rv = sURIFixup->CreateFixupURI(uriString, fixupFlags,
3528 getter_AddRefs(uri));
3530 // else no fixup service so just use the URI we created and see
3531 // what happens
3533 if (NS_ERROR_MALFORMED_URI == rv) {
3534 DisplayLoadError(rv, uri, aURI);
3537 if (NS_FAILED(rv) || !uri)
3538 return NS_ERROR_FAILURE;
3540 PopupControlState popupState;
3541 if (aLoadFlags & LOAD_FLAGS_ALLOW_POPUPS) {
3542 popupState = openAllowed;
3543 aLoadFlags &= ~LOAD_FLAGS_ALLOW_POPUPS;
3544 } else {
3545 popupState = openOverridden;
3547 nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(mScriptGlobal));
3548 nsAutoPopupStatePusher statePusher(win, popupState);
3550 // Don't pass certain flags that aren't needed and end up confusing
3551 // ConvertLoadTypeToDocShellLoadInfo. We do need to ensure that they are
3552 // passed to LoadURI though, since it uses them.
3553 PRUint32 extraFlags = (aLoadFlags & EXTRA_LOAD_FLAGS);
3554 aLoadFlags &= ~EXTRA_LOAD_FLAGS;
3556 nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
3557 rv = CreateLoadInfo(getter_AddRefs(loadInfo));
3558 if (NS_FAILED(rv)) return rv;
3560 PRUint32 loadType = MAKE_LOAD_TYPE(LOAD_NORMAL, aLoadFlags);
3561 loadInfo->SetLoadType(ConvertLoadTypeToDocShellLoadInfo(loadType));
3562 loadInfo->SetPostDataStream(aPostStream);
3563 loadInfo->SetReferrer(aReferringURI);
3564 loadInfo->SetHeadersStream(aHeaderStream);
3566 rv = LoadURI(uri, loadInfo, extraFlags, PR_TRUE);
3568 return rv;
3571 NS_IMETHODIMP
3572 nsDocShell::DisplayLoadError(nsresult aError, nsIURI *aURI,
3573 const PRUnichar *aURL,
3574 nsIChannel* aFailedChannel)
3576 // Get prompt and string bundle servcies
3577 nsCOMPtr<nsIPrompt> prompter;
3578 nsCOMPtr<nsIStringBundle> stringBundle;
3579 GetPromptAndStringBundle(getter_AddRefs(prompter),
3580 getter_AddRefs(stringBundle));
3582 NS_ENSURE_TRUE(stringBundle, NS_ERROR_FAILURE);
3583 NS_ENSURE_TRUE(prompter, NS_ERROR_FAILURE);
3585 nsAutoString error;
3586 const PRUint32 kMaxFormatStrArgs = 3;
3587 nsAutoString formatStrs[kMaxFormatStrArgs];
3588 PRUint32 formatStrCount = 0;
3589 PRBool addHostPort = PR_FALSE;
3590 nsresult rv = NS_OK;
3591 nsAutoString messageStr;
3592 nsCAutoString cssClass;
3593 nsCAutoString errorPage;
3595 errorPage.AssignLiteral("neterror");
3597 // Turn the error code into a human readable error message.
3598 if (NS_ERROR_UNKNOWN_PROTOCOL == aError) {
3599 NS_ENSURE_ARG_POINTER(aURI);
3600 // extract the scheme
3601 nsCAutoString scheme;
3602 aURI->GetScheme(scheme);
3603 CopyASCIItoUTF16(scheme, formatStrs[0]);
3604 formatStrCount = 1;
3605 error.AssignLiteral("protocolNotFound");
3607 else if (NS_ERROR_FILE_NOT_FOUND == aError) {
3608 NS_ENSURE_ARG_POINTER(aURI);
3609 error.AssignLiteral("fileNotFound");
3611 else if (NS_ERROR_UNKNOWN_HOST == aError) {
3612 NS_ENSURE_ARG_POINTER(aURI);
3613 // Get the host
3614 nsCAutoString host;
3615 nsCOMPtr<nsIURI> innermostURI = NS_GetInnermostURI(aURI);
3616 innermostURI->GetHost(host);
3617 CopyUTF8toUTF16(host, formatStrs[0]);
3618 formatStrCount = 1;
3619 error.AssignLiteral("dnsNotFound");
3621 else if(NS_ERROR_CONNECTION_REFUSED == aError) {
3622 NS_ENSURE_ARG_POINTER(aURI);
3623 addHostPort = PR_TRUE;
3624 error.AssignLiteral("connectionFailure");
3626 else if(NS_ERROR_NET_INTERRUPT == aError) {
3627 NS_ENSURE_ARG_POINTER(aURI);
3628 addHostPort = PR_TRUE;
3629 error.AssignLiteral("netInterrupt");
3631 else if (NS_ERROR_NET_TIMEOUT == aError) {
3632 NS_ENSURE_ARG_POINTER(aURI);
3633 // Get the host
3634 nsCAutoString host;
3635 aURI->GetHost(host);
3636 CopyUTF8toUTF16(host, formatStrs[0]);
3637 formatStrCount = 1;
3638 error.AssignLiteral("netTimeout");
3640 else if (NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION == aError) {
3641 // CSP error
3642 cssClass.AssignLiteral("neterror");
3643 error.AssignLiteral("cspFrameAncestorBlocked");
3645 else if (NS_ERROR_GET_MODULE(aError) == NS_ERROR_MODULE_SECURITY) {
3646 nsCOMPtr<nsINSSErrorsService> nsserr =
3647 do_GetService(NS_NSS_ERRORS_SERVICE_CONTRACTID);
3649 PRUint32 errorClass;
3650 if (!nsserr ||
3651 NS_FAILED(nsserr->GetErrorClass(aError, &errorClass))) {
3652 errorClass = nsINSSErrorsService::ERROR_CLASS_SSL_PROTOCOL;
3655 nsCOMPtr<nsISupports> securityInfo;
3656 nsCOMPtr<nsITransportSecurityInfo> tsi;
3657 if (aFailedChannel)
3658 aFailedChannel->GetSecurityInfo(getter_AddRefs(securityInfo));
3659 tsi = do_QueryInterface(securityInfo);
3660 if (tsi) {
3661 // Usually we should have aFailedChannel and get a detailed message
3662 tsi->GetErrorMessage(getter_Copies(messageStr));
3664 else {
3665 // No channel, let's obtain the generic error message
3666 if (nsserr) {
3667 nsserr->GetErrorMessage(aError, messageStr);
3670 if (!messageStr.IsEmpty()) {
3671 if (errorClass == nsINSSErrorsService::ERROR_CLASS_BAD_CERT) {
3672 error.AssignLiteral("nssBadCert");
3673 PRBool expert = PR_FALSE;
3674 mPrefs->GetBoolPref("browser.xul.error_pages.expert_bad_cert",
3675 &expert);
3676 if (expert) {
3677 cssClass.AssignLiteral("expertBadCert");
3680 // See if an alternate cert error page is registered
3681 nsXPIDLCString alternateErrorPage;
3682 mPrefs->GetCharPref("security.alternate_certificate_error_page",
3683 getter_Copies(alternateErrorPage));
3684 if (alternateErrorPage)
3685 errorPage.Assign(alternateErrorPage);
3686 } else {
3687 error.AssignLiteral("nssFailure2");
3690 } else if (NS_ERROR_PHISHING_URI == aError || NS_ERROR_MALWARE_URI == aError) {
3691 nsCAutoString host;
3692 aURI->GetHost(host);
3693 CopyUTF8toUTF16(host, formatStrs[0]);
3694 formatStrCount = 1;
3696 // Malware and phishing detectors may want to use an alternate error
3697 // page, but if the pref's not set, we'll fall back on the standard page
3698 nsXPIDLCString alternateErrorPage;
3699 mPrefs->GetCharPref("urlclassifier.alternate_error_page",
3700 getter_Copies(alternateErrorPage));
3701 if (alternateErrorPage)
3702 errorPage.Assign(alternateErrorPage);
3704 if (NS_ERROR_PHISHING_URI == aError)
3705 error.AssignLiteral("phishingBlocked");
3706 else
3707 error.AssignLiteral("malwareBlocked");
3708 cssClass.AssignLiteral("blacklist");
3710 else {
3711 // Errors requiring simple formatting
3712 switch (aError) {
3713 case NS_ERROR_MALFORMED_URI:
3714 // URI is malformed
3715 error.AssignLiteral("malformedURI");
3716 break;
3717 case NS_ERROR_REDIRECT_LOOP:
3718 // Doc failed to load because the server generated too many redirects
3719 error.AssignLiteral("redirectLoop");
3720 break;
3721 case NS_ERROR_UNKNOWN_SOCKET_TYPE:
3722 // Doc failed to load because PSM is not installed
3723 error.AssignLiteral("unknownSocketType");
3724 break;
3725 case NS_ERROR_NET_RESET:
3726 // Doc failed to load because the server kept reseting the connection
3727 // before we could read any data from it
3728 error.AssignLiteral("netReset");
3729 break;
3730 case NS_ERROR_DOCUMENT_NOT_CACHED:
3731 // Doc failed to load because we are offline and the cache does not
3732 // contain a copy of the document.
3733 error.AssignLiteral("netOffline");
3734 break;
3735 case NS_ERROR_DOCUMENT_IS_PRINTMODE:
3736 // Doc navigation attempted while Printing or Print Preview
3737 error.AssignLiteral("isprinting");
3738 break;
3739 case NS_ERROR_PORT_ACCESS_NOT_ALLOWED:
3740 // Port blocked for security reasons
3741 addHostPort = PR_TRUE;
3742 error.AssignLiteral("deniedPortAccess");
3743 break;
3744 case NS_ERROR_UNKNOWN_PROXY_HOST:
3745 // Proxy hostname could not be resolved.
3746 error.AssignLiteral("proxyResolveFailure");
3747 break;
3748 case NS_ERROR_PROXY_CONNECTION_REFUSED:
3749 // Proxy connection was refused.
3750 error.AssignLiteral("proxyConnectFailure");
3751 break;
3752 case NS_ERROR_INVALID_CONTENT_ENCODING:
3753 // Bad Content Encoding.
3754 error.AssignLiteral("contentEncodingError");
3755 break;
3756 case NS_ERROR_UNSAFE_CONTENT_TYPE:
3757 // Channel refused to load from an unrecognized content type.
3758 error.AssignLiteral("unsafeContentType");
3759 break;
3763 // Test if the error should be displayed
3764 if (error.IsEmpty()) {
3765 return NS_OK;
3768 // Test if the error needs to be formatted
3769 if (!messageStr.IsEmpty()) {
3770 // already obtained message
3772 else {
3773 if (addHostPort) {
3774 // Build up the host:port string.
3775 nsCAutoString hostport;
3776 if (aURI) {
3777 aURI->GetHostPort(hostport);
3778 } else {
3779 hostport.AssignLiteral("?");
3781 CopyUTF8toUTF16(hostport, formatStrs[formatStrCount++]);
3784 nsCAutoString spec;
3785 rv = NS_ERROR_NOT_AVAILABLE;
3786 if (aURI) {
3787 // displaying "file://" is aesthetically unpleasing and could even be
3788 // confusing to the user
3789 PRBool isFileURI = PR_FALSE;
3790 rv = aURI->SchemeIs("file", &isFileURI);
3791 if (NS_SUCCEEDED(rv) && isFileURI)
3792 aURI->GetPath(spec);
3793 else
3794 aURI->GetSpec(spec);
3796 nsCAutoString charset;
3797 // unescape and convert from origin charset
3798 aURI->GetOriginCharset(charset);
3799 nsCOMPtr<nsITextToSubURI> textToSubURI(
3800 do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv));
3801 if (NS_SUCCEEDED(rv)) {
3802 rv = textToSubURI->UnEscapeURIForUI(charset, spec, formatStrs[formatStrCount]);
3804 } else {
3805 spec.AssignLiteral("?");
3807 if (NS_FAILED(rv))
3808 CopyUTF8toUTF16(spec, formatStrs[formatStrCount]);
3809 rv = NS_OK;
3810 ++formatStrCount;
3812 const PRUnichar *strs[kMaxFormatStrArgs];
3813 for (PRUint32 i = 0; i < formatStrCount; i++) {
3814 strs[i] = formatStrs[i].get();
3816 nsXPIDLString str;
3817 rv = stringBundle->FormatStringFromName(
3818 error.get(),
3819 strs, formatStrCount, getter_Copies(str));
3820 NS_ENSURE_SUCCESS(rv, rv);
3821 messageStr.Assign(str.get());
3824 // Display the error as a page or an alert prompt
3825 NS_ENSURE_FALSE(messageStr.IsEmpty(), NS_ERROR_FAILURE);
3826 // Note: For now, display an alert instead of an error page if we have no
3827 // URI object. Missing URI objects are handled badly by session history.
3828 if (mUseErrorPages && aURI && aFailedChannel) {
3829 // Display an error page
3830 LoadErrorPage(aURI, aURL, errorPage.get(), error.get(),
3831 messageStr.get(), cssClass.get(), aFailedChannel);
3833 else
3835 // The prompter reqires that our private window has a document (or it
3836 // asserts). Satisfy that assertion now since GetDocument will force
3837 // creation of one if it hasn't already been created.
3838 nsCOMPtr<nsPIDOMWindow> pwin(do_QueryInterface(mScriptGlobal));
3839 if (pwin) {
3840 nsCOMPtr<nsIDOMDocument> doc;
3841 pwin->GetDocument(getter_AddRefs(doc));
3844 // Display a message box
3845 prompter->Alert(nsnull, messageStr.get());
3848 return NS_OK;
3852 NS_IMETHODIMP
3853 nsDocShell::LoadErrorPage(nsIURI *aURI, const PRUnichar *aURL,
3854 const char *aErrorPage,
3855 const PRUnichar *aErrorType,
3856 const PRUnichar *aDescription,
3857 const char *aCSSClass,
3858 nsIChannel* aFailedChannel)
3860 #if defined(PR_LOGGING) && defined(DEBUG)
3861 if (PR_LOG_TEST(gDocShellLog, PR_LOG_DEBUG)) {
3862 nsCAutoString spec;
3863 aURI->GetSpec(spec);
3865 nsCAutoString chanName;
3866 if (aFailedChannel)
3867 aFailedChannel->GetName(chanName);
3868 else
3869 chanName.AssignLiteral("<no channel>");
3871 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
3872 ("nsDocShell[%p]::LoadErrorPage(\"%s\", \"%s\", {...}, [%s])\n", this,
3873 spec.get(), NS_ConvertUTF16toUTF8(aURL).get(), chanName.get()));
3875 #endif
3876 mFailedChannel = aFailedChannel;
3877 mFailedURI = aURI;
3878 mFailedLoadType = mLoadType;
3880 if (mLSHE) {
3881 // If we don't give mLSHE a new doc identifier here, when we go back or
3882 // forward to another SHEntry with the same doc identifier, the error
3883 // page will persist.
3884 mLSHE->SetUniqueDocIdentifier();
3887 nsCAutoString url;
3888 nsCAutoString charset;
3889 if (aURI)
3891 nsresult rv = aURI->GetSpec(url);
3892 rv |= aURI->GetOriginCharset(charset);
3893 NS_ENSURE_SUCCESS(rv, rv);
3895 else if (aURL)
3897 CopyUTF16toUTF8(aURL, url);
3899 else
3901 return NS_ERROR_INVALID_POINTER;
3904 // Create a URL to pass all the error information through to the page.
3906 char *escapedUrl = nsEscape(url.get(), url_Path);
3907 char *escapedCharset = nsEscape(charset.get(), url_Path);
3908 char *escapedError = nsEscape(NS_ConvertUTF16toUTF8(aErrorType).get(), url_Path);
3909 char *escapedDescription = nsEscape(NS_ConvertUTF16toUTF8(aDescription).get(), url_Path);
3910 char *escapedCSSClass = nsEscape(aCSSClass, url_Path);
3912 nsCString errorPageUrl("about:");
3913 errorPageUrl.AppendASCII(aErrorPage);
3914 errorPageUrl.AppendLiteral("?e=");
3916 errorPageUrl.AppendASCII(escapedError);
3917 errorPageUrl.AppendLiteral("&u=");
3918 errorPageUrl.AppendASCII(escapedUrl);
3919 if (escapedCSSClass && escapedCSSClass[0]) {
3920 errorPageUrl.AppendASCII("&s=");
3921 errorPageUrl.AppendASCII(escapedCSSClass);
3923 errorPageUrl.AppendLiteral("&c=");
3924 errorPageUrl.AppendASCII(escapedCharset);
3925 errorPageUrl.AppendLiteral("&d=");
3926 errorPageUrl.AppendASCII(escapedDescription);
3928 nsMemory::Free(escapedDescription);
3929 nsMemory::Free(escapedError);
3930 nsMemory::Free(escapedUrl);
3931 nsMemory::Free(escapedCharset);
3932 nsMemory::Free(escapedCSSClass);
3934 nsCOMPtr<nsIURI> errorPageURI;
3935 nsresult rv = NS_NewURI(getter_AddRefs(errorPageURI), errorPageUrl);
3936 NS_ENSURE_SUCCESS(rv, rv);
3938 return InternalLoad(errorPageURI, nsnull, nsnull,
3939 INTERNAL_LOAD_FLAGS_INHERIT_OWNER, nsnull, nsnull,
3940 nsnull, nsnull, LOAD_ERROR_PAGE,
3941 nsnull, PR_TRUE, nsnull, nsnull);
3945 NS_IMETHODIMP
3946 nsDocShell::Reload(PRUint32 aReloadFlags)
3948 if (!IsNavigationAllowed()) {
3949 return NS_OK; // JS may not handle returning of an error code
3951 nsresult rv;
3952 NS_ASSERTION(((aReloadFlags & 0xf) == 0),
3953 "Reload command not updated to use load flags!");
3954 NS_ASSERTION((aReloadFlags & EXTRA_LOAD_FLAGS) == 0,
3955 "Don't pass these flags to Reload");
3957 PRUint32 loadType = MAKE_LOAD_TYPE(LOAD_RELOAD_NORMAL, aReloadFlags);
3958 NS_ENSURE_TRUE(IsValidLoadType(loadType), NS_ERROR_INVALID_ARG);
3960 // Send notifications to the HistoryListener if any, about the impending reload
3961 nsCOMPtr<nsISHistory> rootSH;
3962 rv = GetRootSessionHistory(getter_AddRefs(rootSH));
3963 nsCOMPtr<nsISHistoryInternal> shistInt(do_QueryInterface(rootSH));
3964 PRBool canReload = PR_TRUE;
3965 if (rootSH) {
3966 nsCOMPtr<nsISHistoryListener> listener;
3967 shistInt->GetListener(getter_AddRefs(listener));
3968 if (listener) {
3969 listener->OnHistoryReload(mCurrentURI, aReloadFlags, &canReload);
3973 if (!canReload)
3974 return NS_OK;
3976 /* If you change this part of code, make sure bug 45297 does not re-occur */
3977 if (mOSHE) {
3978 rv = LoadHistoryEntry(mOSHE, loadType);
3980 else if (mLSHE) { // In case a reload happened before the current load is done
3981 rv = LoadHistoryEntry(mLSHE, loadType);
3983 else {
3984 nsCOMPtr<nsIDOMDocument> domDoc(do_GetInterface(GetAsSupports(this)));
3985 nsCOMPtr<nsIDocument> doc(do_QueryInterface(domDoc));
3987 nsIPrincipal* principal = nsnull;
3988 nsAutoString contentTypeHint;
3989 if (doc) {
3990 principal = doc->NodePrincipal();
3991 doc->GetContentType(contentTypeHint);
3994 rv = InternalLoad(mCurrentURI,
3995 mReferrerURI,
3996 principal,
3997 INTERNAL_LOAD_FLAGS_NONE, // Do not inherit owner from document
3998 nsnull, // No window target
3999 NS_LossyConvertUTF16toASCII(contentTypeHint).get(),
4000 nsnull, // No post data
4001 nsnull, // No headers data
4002 loadType, // Load type
4003 nsnull, // No SHEntry
4004 PR_TRUE,
4005 nsnull, // No nsIDocShell
4006 nsnull); // No nsIRequest
4010 return rv;
4013 NS_IMETHODIMP
4014 nsDocShell::Stop(PRUint32 aStopFlags)
4016 // Revoke any pending event related to content viewer restoration
4017 mRestorePresentationEvent.Revoke();
4019 if (mLoadType == LOAD_ERROR_PAGE) {
4020 if (mLSHE) {
4021 // Since error page loads never unset mLSHE, do so now
4022 SetHistoryEntry(&mOSHE, mLSHE);
4023 SetHistoryEntry(&mLSHE, nsnull);
4026 mFailedChannel = nsnull;
4027 mFailedURI = nsnull;
4030 if (nsIWebNavigation::STOP_CONTENT & aStopFlags) {
4031 // Stop the document loading
4032 if (mContentViewer)
4033 mContentViewer->Stop();
4036 if (nsIWebNavigation::STOP_NETWORK & aStopFlags) {
4037 // Suspend any timers that were set for this loader. We'll clear
4038 // them out for good in CreateContentViewer.
4039 if (mRefreshURIList) {
4040 SuspendRefreshURIs();
4041 mSavedRefreshURIList.swap(mRefreshURIList);
4042 mRefreshURIList = nsnull;
4045 // XXXbz We could also pass |this| to nsIURILoader::Stop. That will
4046 // just call Stop() on us as an nsIDocumentLoader... We need fewer
4047 // redundant apis!
4048 Stop();
4051 PRInt32 n;
4052 PRInt32 count = mChildList.Count();
4053 for (n = 0; n < count; n++) {
4054 nsCOMPtr<nsIWebNavigation> shellAsNav(do_QueryInterface(ChildAt(n)));
4055 if (shellAsNav)
4056 shellAsNav->Stop(aStopFlags);
4059 return NS_OK;
4062 NS_IMETHODIMP
4063 nsDocShell::GetDocument(nsIDOMDocument ** aDocument)
4065 NS_ENSURE_ARG_POINTER(aDocument);
4066 NS_ENSURE_SUCCESS(EnsureContentViewer(), NS_ERROR_FAILURE);
4068 return mContentViewer->GetDOMDocument(aDocument);
4071 NS_IMETHODIMP
4072 nsDocShell::GetCurrentURI(nsIURI ** aURI)
4074 NS_ENSURE_ARG_POINTER(aURI);
4076 if (mCurrentURI) {
4077 return NS_EnsureSafeToReturn(mCurrentURI, aURI);
4080 *aURI = nsnull;
4081 return NS_OK;
4084 NS_IMETHODIMP
4085 nsDocShell::GetReferringURI(nsIURI ** aURI)
4087 NS_ENSURE_ARG_POINTER(aURI);
4089 *aURI = mReferrerURI;
4090 NS_IF_ADDREF(*aURI);
4092 return NS_OK;
4095 NS_IMETHODIMP
4096 nsDocShell::SetSessionHistory(nsISHistory * aSessionHistory)
4099 NS_ENSURE_TRUE(aSessionHistory, NS_ERROR_FAILURE);
4100 // make sure that we are the root docshell and
4101 // set a handle to root docshell in SH.
4103 nsCOMPtr<nsIDocShellTreeItem> root;
4104 /* Get the root docshell. If *this* is the root docshell
4105 * then save a handle to *this* in SH. SH needs it to do
4106 * traversions thro' its entries
4108 GetSameTypeRootTreeItem(getter_AddRefs(root));
4109 NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
4110 if (root.get() == static_cast<nsIDocShellTreeItem *>(this)) {
4111 mSessionHistory = aSessionHistory;
4112 nsCOMPtr<nsISHistoryInternal>
4113 shPrivate(do_QueryInterface(mSessionHistory));
4114 NS_ENSURE_TRUE(shPrivate, NS_ERROR_FAILURE);
4115 shPrivate->SetRootDocShell(this);
4116 return NS_OK;
4118 return NS_ERROR_FAILURE;
4123 NS_IMETHODIMP
4124 nsDocShell::GetSessionHistory(nsISHistory ** aSessionHistory)
4126 NS_ENSURE_ARG_POINTER(aSessionHistory);
4127 *aSessionHistory = mSessionHistory;
4128 NS_IF_ADDREF(*aSessionHistory);
4129 return NS_OK;
4132 //*****************************************************************************
4133 // nsDocShell::nsIWebPageDescriptor
4134 //*****************************************************************************
4135 NS_IMETHODIMP
4136 nsDocShell::LoadPage(nsISupports *aPageDescriptor, PRUint32 aDisplayType)
4138 nsCOMPtr<nsISHEntry> shEntryIn(do_QueryInterface(aPageDescriptor));
4140 // Currently, the opaque 'page descriptor' is an nsISHEntry...
4141 if (!shEntryIn) {
4142 return NS_ERROR_INVALID_POINTER;
4145 // Now clone shEntryIn, since we might end up modifying it later on, and we
4146 // want a page descriptor to be reusable.
4147 nsCOMPtr<nsISHEntry> shEntry;
4148 nsresult rv = shEntryIn->Clone(getter_AddRefs(shEntry));
4149 NS_ENSURE_SUCCESS(rv, rv);
4152 // load the page as view-source
4154 if (nsIWebPageDescriptor::DISPLAY_AS_SOURCE == aDisplayType) {
4155 nsCOMPtr<nsIURI> oldUri, newUri;
4156 nsCString spec, newSpec;
4158 // Create a new view-source URI and replace the original.
4159 rv = shEntry->GetURI(getter_AddRefs(oldUri));
4160 if (NS_FAILED(rv))
4161 return rv;
4163 oldUri->GetSpec(spec);
4164 newSpec.AppendLiteral("view-source:");
4165 newSpec.Append(spec);
4167 rv = NS_NewURI(getter_AddRefs(newUri), newSpec);
4168 if (NS_FAILED(rv)) {
4169 return rv;
4171 shEntry->SetURI(newUri);
4174 rv = LoadHistoryEntry(shEntry, LOAD_HISTORY);
4175 return rv;
4178 NS_IMETHODIMP
4179 nsDocShell::GetCurrentDescriptor(nsISupports **aPageDescriptor)
4181 NS_PRECONDITION(aPageDescriptor, "Null out param?");
4183 *aPageDescriptor = nsnull;
4185 nsISHEntry* src = mOSHE ? mOSHE : mLSHE;
4186 if (src) {
4187 nsCOMPtr<nsISHEntry> dest;
4189 nsresult rv = src->Clone(getter_AddRefs(dest));
4190 if (NS_FAILED(rv)) {
4191 return rv;
4194 // null out inappropriate cloned attributes...
4195 dest->SetParent(nsnull);
4196 dest->SetIsSubFrame(PR_FALSE);
4198 return CallQueryInterface(dest, aPageDescriptor);
4201 return NS_ERROR_NOT_AVAILABLE;
4205 //*****************************************************************************
4206 // nsDocShell::nsIBaseWindow
4207 //*****************************************************************************
4209 NS_IMETHODIMP
4210 nsDocShell::InitWindow(nativeWindow parentNativeWindow,
4211 nsIWidget * parentWidget, PRInt32 x, PRInt32 y,
4212 PRInt32 cx, PRInt32 cy)
4214 SetParentWidget(parentWidget);
4215 SetPositionAndSize(x, y, cx, cy, PR_FALSE);
4217 return NS_OK;
4220 NS_IMETHODIMP
4221 nsDocShell::Create()
4223 if (mPrefs) {
4224 // We've already been created
4225 return NS_OK;
4228 NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome,
4229 "Unexpected item type in docshell");
4231 nsresult rv = NS_ERROR_FAILURE;
4232 mPrefs = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
4233 NS_ENSURE_SUCCESS(rv, rv);
4235 PRBool tmpbool;
4237 rv = mPrefs->GetBoolPref("browser.frames.enabled", &tmpbool);
4238 if (NS_SUCCEEDED(rv))
4239 mAllowSubframes = tmpbool;
4241 if (gValidateOrigin == (PRBool)0xffffffff) {
4242 // Check pref to see if we should prevent frameset spoofing
4243 rv = mPrefs->GetBoolPref("browser.frame.validate_origin", &tmpbool);
4244 if (NS_SUCCEEDED(rv)) {
4245 gValidateOrigin = tmpbool;
4246 } else {
4247 gValidateOrigin = PR_TRUE;
4251 // Should we use XUL error pages instead of alerts if possible?
4252 rv = mPrefs->GetBoolPref("browser.xul.error_pages.enabled", &tmpbool);
4253 if (NS_SUCCEEDED(rv))
4254 mUseErrorPages = tmpbool;
4256 nsCOMPtr<nsIPrefBranch2> prefs(do_QueryInterface(mPrefs, &rv));
4257 if (NS_SUCCEEDED(rv) && mObserveErrorPages) {
4258 prefs->AddObserver("browser.xul.error_pages.enabled", this, PR_FALSE);
4261 nsCOMPtr<nsIObserverService> serv = do_GetService(NS_OBSERVERSERVICE_CONTRACTID);
4262 if (serv) {
4263 const char* msg = mItemType == typeContent ?
4264 NS_WEBNAVIGATION_CREATE : NS_CHROME_WEBNAVIGATION_CREATE;
4265 serv->NotifyObservers(GetAsSupports(this), msg, nsnull);
4268 return NS_OK;
4271 NS_IMETHODIMP
4272 nsDocShell::Destroy()
4274 NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome,
4275 "Unexpected item type in docshell");
4277 if (!mIsBeingDestroyed) {
4278 nsCOMPtr<nsIObserverService> serv =
4279 do_GetService(NS_OBSERVERSERVICE_CONTRACTID);
4280 if (serv) {
4281 const char* msg = mItemType == typeContent ?
4282 NS_WEBNAVIGATION_DESTROY : NS_CHROME_WEBNAVIGATION_DESTROY;
4283 serv->NotifyObservers(GetAsSupports(this), msg, nsnull);
4287 mIsBeingDestroyed = PR_TRUE;
4289 // Remove our pref observers
4290 if (mObserveErrorPages) {
4291 nsCOMPtr<nsIPrefBranch2> prefs(do_QueryInterface(mPrefs));
4292 if (prefs) {
4293 prefs->RemoveObserver("browser.xul.error_pages.enabled", this);
4294 mObserveErrorPages = PR_FALSE;
4298 // Make sure to blow away our mLoadingURI just in case. No loads
4299 // from inside this pagehide.
4300 mLoadingURI = nsnull;
4302 // Fire unload event before we blow anything away.
4303 (void) FirePageHideNotification(PR_TRUE);
4305 // Clear pointers to any detached nsEditorData that's lying
4306 // around in shistory entries. Breaks cycle. See bug 430921.
4307 if (mOSHE)
4308 mOSHE->SetEditorData(nsnull);
4309 if (mLSHE)
4310 mLSHE->SetEditorData(nsnull);
4312 // Note: mContentListener can be null if Init() failed and we're being
4313 // called from the destructor.
4314 if (mContentListener) {
4315 mContentListener->DropDocShellreference();
4316 mContentListener->SetParentContentListener(nsnull);
4317 // Note that we do NOT set mContentListener to null here; that
4318 // way if someone tries to do a load in us after this point
4319 // the nsDSURIContentListener will block it. All of which
4320 // means that we should do this before calling Stop(), of
4321 // course.
4324 // Stop any URLs that are currently being loaded...
4325 Stop(nsIWebNavigation::STOP_ALL);
4327 mEditorData = nsnull;
4329 mTransferableHookData = nsnull;
4331 // Save the state of the current document, before destroying the window.
4332 // This is needed to capture the state of a frameset when the new document
4333 // causes the frameset to be destroyed...
4334 PersistLayoutHistoryState();
4336 // Remove this docshell from its parent's child list
4337 nsCOMPtr<nsIDocShellTreeItem> docShellParentAsItem =
4338 do_QueryInterface(GetAsSupports(mParent));
4339 if (docShellParentAsItem)
4340 docShellParentAsItem->RemoveChild(this);
4342 if (mContentViewer) {
4343 mContentViewer->Close(nsnull);
4344 mContentViewer->Destroy();
4345 mContentViewer = nsnull;
4348 nsDocLoader::Destroy();
4350 mParentWidget = nsnull;
4351 mCurrentURI = nsnull;
4353 if (mScriptGlobal) {
4354 nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(mScriptGlobal));
4355 win->SetDocShell(nsnull);
4357 mScriptGlobal = nsnull;
4360 if (mSessionHistory) {
4361 // We want to destroy these content viewers now rather than
4362 // letting their destruction wait for the session history
4363 // entries to get garbage collected. (Bug 488394)
4364 nsCOMPtr<nsISHistoryInternal> shPrivate =
4365 do_QueryInterface(mSessionHistory);
4366 if (shPrivate) {
4367 shPrivate->EvictAllContentViewers();
4369 mSessionHistory = nsnull;
4372 SetTreeOwner(nsnull);
4374 // required to break ref cycle
4375 mSecurityUI = nsnull;
4377 // Cancel any timers that were set for this docshell; this is needed
4378 // to break the cycle between us and the timers.
4379 CancelRefreshURITimers();
4380 return NS_OK;
4383 NS_IMETHODIMP
4384 nsDocShell::SetPosition(PRInt32 x, PRInt32 y)
4386 mBounds.x = x;
4387 mBounds.y = y;
4389 if (mContentViewer)
4390 NS_ENSURE_SUCCESS(mContentViewer->Move(x, y), NS_ERROR_FAILURE);
4392 return NS_OK;
4395 NS_IMETHODIMP
4396 nsDocShell::GetPosition(PRInt32 * aX, PRInt32 * aY)
4398 PRInt32 dummyHolder;
4399 return GetPositionAndSize(aX, aY, &dummyHolder, &dummyHolder);
4402 NS_IMETHODIMP
4403 nsDocShell::SetSize(PRInt32 aCX, PRInt32 aCY, PRBool aRepaint)
4405 PRInt32 x = 0, y = 0;
4406 GetPosition(&x, &y);
4407 return SetPositionAndSize(x, y, aCX, aCY, aRepaint);
4410 NS_IMETHODIMP
4411 nsDocShell::GetSize(PRInt32 * aCX, PRInt32 * aCY)
4413 PRInt32 dummyHolder;
4414 return GetPositionAndSize(&dummyHolder, &dummyHolder, aCX, aCY);
4417 NS_IMETHODIMP
4418 nsDocShell::SetPositionAndSize(PRInt32 x, PRInt32 y, PRInt32 cx,
4419 PRInt32 cy, PRBool fRepaint)
4421 mBounds.x = x;
4422 mBounds.y = y;
4423 mBounds.width = cx;
4424 mBounds.height = cy;
4426 // Hold strong ref, since SetBounds can make us null out mContentViewer
4427 nsCOMPtr<nsIContentViewer> viewer = mContentViewer;
4428 if (viewer) {
4429 //XXX Border figured in here or is that handled elsewhere?
4430 NS_ENSURE_SUCCESS(viewer->SetBounds(mBounds), NS_ERROR_FAILURE);
4433 return NS_OK;
4436 NS_IMETHODIMP
4437 nsDocShell::GetPositionAndSize(PRInt32 * x, PRInt32 * y, PRInt32 * cx,
4438 PRInt32 * cy)
4440 // We should really consider just getting this information from
4441 // our window instead of duplicating the storage and code...
4442 if (cx || cy) {
4443 // Caller wants to know our size; make sure to give them up to
4444 // date information.
4445 nsCOMPtr<nsIDOMDocument> document(do_GetInterface(GetAsSupports(mParent)));
4446 nsCOMPtr<nsIDocument> doc(do_QueryInterface(document));
4447 if (doc) {
4448 doc->FlushPendingNotifications(Flush_Layout);
4452 DoGetPositionAndSize(x, y, cx, cy);
4453 return NS_OK;
4456 void
4457 nsDocShell::DoGetPositionAndSize(PRInt32 * x, PRInt32 * y, PRInt32 * cx,
4458 PRInt32 * cy)
4460 if (x)
4461 *x = mBounds.x;
4462 if (y)
4463 *y = mBounds.y;
4464 if (cx)
4465 *cx = mBounds.width;
4466 if (cy)
4467 *cy = mBounds.height;
4470 NS_IMETHODIMP
4471 nsDocShell::Repaint(PRBool aForce)
4473 nsCOMPtr<nsIPresShell> presShell;
4474 GetPresShell(getter_AddRefs(presShell));
4475 NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
4477 nsIViewManager* viewManager = presShell->GetViewManager();
4478 NS_ENSURE_TRUE(viewManager, NS_ERROR_FAILURE);
4480 // what about aForce ?
4481 NS_ENSURE_SUCCESS(viewManager->UpdateAllViews(0), NS_ERROR_FAILURE);
4482 return NS_OK;
4485 NS_IMETHODIMP
4486 nsDocShell::GetParentWidget(nsIWidget ** parentWidget)
4488 NS_ENSURE_ARG_POINTER(parentWidget);
4490 *parentWidget = mParentWidget;
4491 NS_IF_ADDREF(*parentWidget);
4493 return NS_OK;
4496 NS_IMETHODIMP
4497 nsDocShell::SetParentWidget(nsIWidget * aParentWidget)
4499 mParentWidget = aParentWidget;
4501 return NS_OK;
4504 NS_IMETHODIMP
4505 nsDocShell::GetParentNativeWindow(nativeWindow * parentNativeWindow)
4507 NS_ENSURE_ARG_POINTER(parentNativeWindow);
4509 if (mParentWidget)
4510 *parentNativeWindow = mParentWidget->GetNativeData(NS_NATIVE_WIDGET);
4511 else
4512 *parentNativeWindow = nsnull;
4514 return NS_OK;
4517 NS_IMETHODIMP
4518 nsDocShell::SetParentNativeWindow(nativeWindow parentNativeWindow)
4520 return NS_ERROR_NOT_IMPLEMENTED;
4523 NS_IMETHODIMP
4524 nsDocShell::GetVisibility(PRBool * aVisibility)
4526 NS_ENSURE_ARG_POINTER(aVisibility);
4528 *aVisibility = PR_FALSE;
4530 if (!mContentViewer)
4531 return NS_OK;
4533 nsCOMPtr<nsIPresShell> presShell;
4534 GetPresShell(getter_AddRefs(presShell));
4535 if (!presShell)
4536 return NS_OK;
4538 // get the view manager
4539 nsIViewManager* vm = presShell->GetViewManager();
4540 NS_ENSURE_TRUE(vm, NS_ERROR_FAILURE);
4542 // get the root view
4543 nsIView *view = nsnull; // views are not ref counted
4544 NS_ENSURE_SUCCESS(vm->GetRootView(view), NS_ERROR_FAILURE);
4545 NS_ENSURE_TRUE(view, NS_ERROR_FAILURE);
4547 // if our root view is hidden, we are not visible
4548 if (view->GetVisibility() == nsViewVisibility_kHide)
4549 return NS_OK;
4551 // otherwise, we must walk up the document and view trees checking
4552 // for a hidden view, unless we're an off screen browser, which
4553 // would make this test meaningless.
4555 nsCOMPtr<nsIDocShellTreeItem> treeItem = this;
4556 nsCOMPtr<nsIDocShellTreeItem> parentItem;
4557 treeItem->GetParent(getter_AddRefs(parentItem));
4558 while (parentItem) {
4559 nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(treeItem));
4560 docShell->GetPresShell(getter_AddRefs(presShell));
4562 nsCOMPtr<nsIDocShell> parentDS = do_QueryInterface(parentItem);
4563 nsCOMPtr<nsIPresShell> pPresShell;
4564 parentDS->GetPresShell(getter_AddRefs(pPresShell));
4566 // Null-check for crash in bug 267804
4567 if (!pPresShell) {
4568 NS_NOTREACHED("parent docshell has null pres shell");
4569 return NS_OK;
4572 nsIContent *shellContent =
4573 pPresShell->GetDocument()->FindContentForSubDocument(presShell->GetDocument());
4574 NS_ASSERTION(shellContent, "subshell not in the map");
4576 nsIFrame* frame = shellContent ? shellContent->GetPrimaryFrame() : nsnull;
4577 PRBool isDocShellOffScreen = PR_FALSE;
4578 docShell->GetIsOffScreenBrowser(&isDocShellOffScreen);
4579 if (frame && !frame->AreAncestorViewsVisible() && !isDocShellOffScreen)
4580 return NS_OK;
4582 treeItem = parentItem;
4583 treeItem->GetParent(getter_AddRefs(parentItem));
4586 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_QueryInterface(mTreeOwner));
4587 if (!treeOwnerAsWin) {
4588 *aVisibility = PR_TRUE;
4589 return NS_OK;
4592 // Check with the tree owner as well to give embedders a chance to
4593 // expose visibility as well.
4594 return treeOwnerAsWin->GetVisibility(aVisibility);
4597 NS_IMETHODIMP
4598 nsDocShell::SetIsOffScreenBrowser(PRBool aIsOffScreen)
4600 mIsOffScreenBrowser = aIsOffScreen;
4601 return NS_OK;
4604 NS_IMETHODIMP
4605 nsDocShell::GetIsOffScreenBrowser(PRBool *aIsOffScreen)
4607 *aIsOffScreen = mIsOffScreenBrowser;
4608 return NS_OK;
4611 NS_IMETHODIMP
4612 nsDocShell::SetVisibility(PRBool aVisibility)
4614 if (!mContentViewer)
4615 return NS_OK;
4616 if (aVisibility) {
4617 mContentViewer->Show();
4619 else {
4620 mContentViewer->Hide();
4623 return NS_OK;
4626 NS_IMETHODIMP
4627 nsDocShell::GetEnabled(PRBool *aEnabled)
4629 NS_ENSURE_ARG_POINTER(aEnabled);
4630 *aEnabled = PR_TRUE;
4631 return NS_ERROR_NOT_IMPLEMENTED;
4634 NS_IMETHODIMP
4635 nsDocShell::SetEnabled(PRBool aEnabled)
4637 return NS_ERROR_NOT_IMPLEMENTED;
4640 NS_IMETHODIMP
4641 nsDocShell::GetBlurSuppression(PRBool *aBlurSuppression)
4643 NS_ENSURE_ARG_POINTER(aBlurSuppression);
4644 *aBlurSuppression = PR_FALSE;
4645 return NS_ERROR_NOT_IMPLEMENTED;
4648 NS_IMETHODIMP
4649 nsDocShell::SetBlurSuppression(PRBool aBlurSuppression)
4651 return NS_ERROR_NOT_IMPLEMENTED;
4654 NS_IMETHODIMP
4655 nsDocShell::SetFocus()
4657 return NS_OK;
4660 NS_IMETHODIMP
4661 nsDocShell::GetMainWidget(nsIWidget ** aMainWidget)
4663 // We don't create our own widget, so simply return the parent one.
4664 return GetParentWidget(aMainWidget);
4667 NS_IMETHODIMP
4668 nsDocShell::GetTitle(PRUnichar ** aTitle)
4670 NS_ENSURE_ARG_POINTER(aTitle);
4672 *aTitle = ToNewUnicode(mTitle);
4673 return NS_OK;
4676 NS_IMETHODIMP
4677 nsDocShell::SetTitle(const PRUnichar * aTitle)
4679 // Store local title
4680 mTitle = aTitle;
4682 nsCOMPtr<nsIDocShellTreeItem> parent;
4683 GetSameTypeParent(getter_AddRefs(parent));
4685 // When title is set on the top object it should then be passed to the
4686 // tree owner.
4687 if (!parent) {
4688 nsCOMPtr<nsIBaseWindow>
4689 treeOwnerAsWin(do_QueryInterface(mTreeOwner));
4690 if (treeOwnerAsWin)
4691 treeOwnerAsWin->SetTitle(aTitle);
4694 if (mGlobalHistory && mCurrentURI && mLoadType != LOAD_ERROR_PAGE) {
4695 mGlobalHistory->SetPageTitle(mCurrentURI, nsString(mTitle));
4699 // Update SessionHistory with the document's title.
4700 if (mOSHE && mLoadType != LOAD_BYPASS_HISTORY &&
4701 mLoadType != LOAD_ERROR_PAGE) {
4703 mOSHE->SetTitle(mTitle);
4706 return NS_OK;
4709 //*****************************************************************************
4710 // nsDocShell::nsIScrollable
4711 //*****************************************************************************
4713 NS_IMETHODIMP
4714 nsDocShell::GetCurScrollPos(PRInt32 scrollOrientation, PRInt32 * curPos)
4716 NS_ENSURE_ARG_POINTER(curPos);
4718 nsIScrollableFrame* sf = GetRootScrollFrame();
4719 NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
4721 nsPoint pt = sf->GetScrollPosition();
4723 switch (scrollOrientation) {
4724 case ScrollOrientation_X:
4725 *curPos = pt.x;
4726 return NS_OK;
4728 case ScrollOrientation_Y:
4729 *curPos = pt.y;
4730 return NS_OK;
4732 default:
4733 NS_ENSURE_TRUE(PR_FALSE, NS_ERROR_INVALID_ARG);
4737 NS_IMETHODIMP
4738 nsDocShell::SetCurScrollPos(PRInt32 scrollOrientation, PRInt32 curPos)
4740 nsIScrollableFrame* sf = GetRootScrollFrame();
4741 NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
4743 nsPoint pt = sf->GetScrollPosition();
4745 switch (scrollOrientation) {
4746 case ScrollOrientation_X:
4747 pt.x = curPos;
4748 break;
4750 case ScrollOrientation_Y:
4751 pt.y = curPos;
4752 break;
4754 default:
4755 NS_ENSURE_TRUE(PR_FALSE, NS_ERROR_INVALID_ARG);
4758 sf->ScrollTo(pt, nsIScrollableFrame::INSTANT);
4759 return NS_OK;
4762 NS_IMETHODIMP
4763 nsDocShell::SetCurScrollPosEx(PRInt32 curHorizontalPos, PRInt32 curVerticalPos)
4765 nsIScrollableFrame* sf = GetRootScrollFrame();
4766 NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
4768 sf->ScrollTo(nsPoint(curHorizontalPos, curVerticalPos),
4769 nsIScrollableFrame::INSTANT);
4770 return NS_OK;
4773 // XXX This is wrong
4774 NS_IMETHODIMP
4775 nsDocShell::GetScrollRange(PRInt32 scrollOrientation,
4776 PRInt32 * minPos, PRInt32 * maxPos)
4778 NS_ENSURE_ARG_POINTER(minPos && maxPos);
4780 nsIScrollableFrame* sf = GetRootScrollFrame();
4781 NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
4783 nsSize portSize = sf->GetScrollPortRect().Size();
4784 nsRect range = sf->GetScrollRange();
4786 switch (scrollOrientation) {
4787 case ScrollOrientation_X:
4788 *minPos = range.x;
4789 *maxPos = range.XMost() + portSize.width;
4790 return NS_OK;
4792 case ScrollOrientation_Y:
4793 *minPos = range.y;
4794 *maxPos = range.YMost() + portSize.height;
4795 return NS_OK;
4797 default:
4798 NS_ENSURE_TRUE(PR_FALSE, NS_ERROR_INVALID_ARG);
4802 NS_IMETHODIMP
4803 nsDocShell::SetScrollRange(PRInt32 scrollOrientation,
4804 PRInt32 minPos, PRInt32 maxPos)
4806 //XXX First Check
4808 Retrieves or Sets the valid ranges for the thumb. When maxPos is set to
4809 something less than the current thumb position, curPos is set = to maxPos.
4811 @return NS_OK - Setting or Getting completed successfully.
4812 NS_ERROR_INVALID_ARG - returned when curPos is not within the
4813 minPos and maxPos.
4815 return NS_ERROR_FAILURE;
4818 NS_IMETHODIMP
4819 nsDocShell::SetScrollRangeEx(PRInt32 minHorizontalPos,
4820 PRInt32 maxHorizontalPos, PRInt32 minVerticalPos,
4821 PRInt32 maxVerticalPos)
4823 //XXX First Check
4825 Retrieves or Sets the valid ranges for the thumb. When maxPos is set to
4826 something less than the current thumb position, curPos is set = to maxPos.
4828 @return NS_OK - Setting or Getting completed successfully.
4829 NS_ERROR_INVALID_ARG - returned when curPos is not within the
4830 minPos and maxPos.
4832 return NS_ERROR_FAILURE;
4835 // This returns setting for all documents in this docshell
4836 NS_IMETHODIMP
4837 nsDocShell::GetDefaultScrollbarPreferences(PRInt32 scrollOrientation,
4838 PRInt32 * scrollbarPref)
4840 NS_ENSURE_ARG_POINTER(scrollbarPref);
4841 switch (scrollOrientation) {
4842 case ScrollOrientation_X:
4843 *scrollbarPref = mDefaultScrollbarPref.x;
4844 return NS_OK;
4846 case ScrollOrientation_Y:
4847 *scrollbarPref = mDefaultScrollbarPref.y;
4848 return NS_OK;
4850 default:
4851 NS_ENSURE_TRUE(PR_FALSE, NS_ERROR_INVALID_ARG);
4853 return NS_ERROR_FAILURE;
4856 // Set scrolling preference for all documents in this shell
4858 // There are three possible values stored in the shell:
4859 // 1) nsIScrollable::Scrollbar_Never = no scrollbar
4860 // 2) nsIScrollable::Scrollbar_Auto = scrollbar appears if the document
4861 // being displayed would normally have scrollbar
4862 // 3) nsIScrollable::Scrollbar_Always = scrollbar always appears
4864 // One important client is nsHTMLFrameInnerFrame::CreateWebShell()
4865 NS_IMETHODIMP
4866 nsDocShell::SetDefaultScrollbarPreferences(PRInt32 scrollOrientation,
4867 PRInt32 scrollbarPref)
4869 switch (scrollOrientation) {
4870 case ScrollOrientation_X:
4871 mDefaultScrollbarPref.x = scrollbarPref;
4872 return NS_OK;
4874 case ScrollOrientation_Y:
4875 mDefaultScrollbarPref.y = scrollbarPref;
4876 return NS_OK;
4878 default:
4879 NS_ENSURE_TRUE(PR_FALSE, NS_ERROR_INVALID_ARG);
4881 return NS_ERROR_FAILURE;
4884 NS_IMETHODIMP
4885 nsDocShell::GetScrollbarVisibility(PRBool * verticalVisible,
4886 PRBool * horizontalVisible)
4888 nsIScrollableFrame* sf = GetRootScrollFrame();
4889 NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
4891 PRUint32 scrollbarVisibility = sf->GetScrollbarVisibility();
4892 if (verticalVisible)
4893 *verticalVisible = (scrollbarVisibility & nsIScrollableFrame::VERTICAL) != 0;
4894 if (horizontalVisible)
4895 *horizontalVisible = (scrollbarVisibility & nsIScrollableFrame::HORIZONTAL) != 0;
4897 return NS_OK;
4900 //*****************************************************************************
4901 // nsDocShell::nsITextScroll
4902 //*****************************************************************************
4904 NS_IMETHODIMP
4905 nsDocShell::ScrollByLines(PRInt32 numLines)
4907 nsIScrollableFrame* sf = GetRootScrollFrame();
4908 NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
4910 sf->ScrollBy(nsIntPoint(0, numLines), nsIScrollableFrame::LINES,
4911 nsIScrollableFrame::SMOOTH);
4912 return NS_OK;
4915 NS_IMETHODIMP
4916 nsDocShell::ScrollByPages(PRInt32 numPages)
4918 nsIScrollableFrame* sf = GetRootScrollFrame();
4919 NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
4921 sf->ScrollBy(nsIntPoint(0, numPages), nsIScrollableFrame::PAGES,
4922 nsIScrollableFrame::SMOOTH);
4923 return NS_OK;
4926 //*****************************************************************************
4927 // nsDocShell::nsIScriptGlobalObjectOwner
4928 //*****************************************************************************
4930 nsIScriptGlobalObject*
4931 nsDocShell::GetScriptGlobalObject()
4933 NS_ENSURE_SUCCESS(EnsureScriptEnvironment(), nsnull);
4935 return mScriptGlobal;
4938 //*****************************************************************************
4939 // nsDocShell::nsIRefreshURI
4940 //*****************************************************************************
4942 NS_IMETHODIMP
4943 nsDocShell::RefreshURI(nsIURI * aURI, PRInt32 aDelay, PRBool aRepeat,
4944 PRBool aMetaRefresh)
4946 NS_ENSURE_ARG(aURI);
4948 /* Check if Meta refresh/redirects are permitted. Some
4949 * embedded applications may not want to do this.
4950 * Must do this before sending out NOTIFY_REFRESH events
4951 * because listeners may have side effects (e.g. displaying a
4952 * button to manually trigger the refresh later).
4954 PRBool allowRedirects = PR_TRUE;
4955 GetAllowMetaRedirects(&allowRedirects);
4956 if (!allowRedirects)
4957 return NS_OK;
4959 // If any web progress listeners are listening for NOTIFY_REFRESH events,
4960 // give them a chance to block this refresh.
4961 PRBool sameURI;
4962 nsresult rv = aURI->Equals(mCurrentURI, &sameURI);
4963 if (NS_FAILED(rv))
4964 sameURI = PR_FALSE;
4965 if (!RefreshAttempted(this, aURI, aDelay, sameURI))
4966 return NS_OK;
4968 nsRefreshTimer *refreshTimer = new nsRefreshTimer();
4969 NS_ENSURE_TRUE(refreshTimer, NS_ERROR_OUT_OF_MEMORY);
4970 PRUint32 busyFlags = 0;
4971 GetBusyFlags(&busyFlags);
4973 nsCOMPtr<nsISupports> dataRef = refreshTimer; // Get the ref count to 1
4975 refreshTimer->mDocShell = this;
4976 refreshTimer->mURI = aURI;
4977 refreshTimer->mDelay = aDelay;
4978 refreshTimer->mRepeat = aRepeat;
4979 refreshTimer->mMetaRefresh = aMetaRefresh;
4981 if (!mRefreshURIList) {
4982 NS_ENSURE_SUCCESS(NS_NewISupportsArray(getter_AddRefs(mRefreshURIList)),
4983 NS_ERROR_FAILURE);
4986 if (busyFlags & BUSY_FLAGS_BUSY) {
4987 // We are busy loading another page. Don't create the
4988 // timer right now. Instead queue up the request and trigger the
4989 // timer in EndPageLoad().
4990 mRefreshURIList->AppendElement(refreshTimer);
4992 else {
4993 // There is no page loading going on right now. Create the
4994 // timer and fire it right away.
4995 nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1");
4996 NS_ENSURE_TRUE(timer, NS_ERROR_FAILURE);
4998 mRefreshURIList->AppendElement(timer); // owning timer ref
4999 timer->InitWithCallback(refreshTimer, aDelay, nsITimer::TYPE_ONE_SHOT);
5001 return NS_OK;
5004 nsresult
5005 nsDocShell::ForceRefreshURIFromTimer(nsIURI * aURI,
5006 PRInt32 aDelay,
5007 PRBool aMetaRefresh,
5008 nsITimer* aTimer)
5010 NS_PRECONDITION(aTimer, "Must have a timer here");
5012 // Remove aTimer from mRefreshURIList if needed
5013 if (mRefreshURIList) {
5014 PRUint32 n = 0;
5015 mRefreshURIList->Count(&n);
5017 for (PRUint32 i = 0; i < n; ++i) {
5018 nsCOMPtr<nsITimer> timer = do_QueryElementAt(mRefreshURIList, i);
5019 if (timer == aTimer) {
5020 mRefreshURIList->RemoveElementAt(i);
5021 break;
5026 return ForceRefreshURI(aURI, aDelay, aMetaRefresh);
5029 NS_IMETHODIMP
5030 nsDocShell::ForceRefreshURI(nsIURI * aURI,
5031 PRInt32 aDelay,
5032 PRBool aMetaRefresh)
5034 NS_ENSURE_ARG(aURI);
5036 nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
5037 CreateLoadInfo(getter_AddRefs(loadInfo));
5038 NS_ENSURE_TRUE(loadInfo, NS_ERROR_OUT_OF_MEMORY);
5040 /* We do need to pass in a referrer, but we don't want it to
5041 * be sent to the server.
5043 loadInfo->SetSendReferrer(PR_FALSE);
5045 /* for most refreshes the current URI is an appropriate
5046 * internal referrer
5048 loadInfo->SetReferrer(mCurrentURI);
5050 /* Don't ever "guess" on which owner to use to avoid picking
5051 * the current owner.
5053 loadInfo->SetOwnerIsExplicit(PR_TRUE);
5055 /* Check if this META refresh causes a redirection
5056 * to another site.
5058 PRBool equalUri = PR_FALSE;
5059 nsresult rv = aURI->Equals(mCurrentURI, &equalUri);
5060 if (NS_SUCCEEDED(rv) && (!equalUri) && aMetaRefresh &&
5061 aDelay <= REFRESH_REDIRECT_TIMER) {
5063 /* It is a META refresh based redirection within the threshold time
5064 * we have in mind (15000 ms as defined by REFRESH_REDIRECT_TIMER).
5065 * Pass a REPLACE flag to LoadURI().
5067 loadInfo->SetLoadType(nsIDocShellLoadInfo::loadNormalReplace);
5069 /* for redirects we mimic HTTP, which passes the
5070 * original referrer
5072 nsCOMPtr<nsIURI> internalReferrer;
5073 GetReferringURI(getter_AddRefs(internalReferrer));
5074 if (internalReferrer) {
5075 loadInfo->SetReferrer(internalReferrer);
5078 else {
5079 loadInfo->SetLoadType(nsIDocShellLoadInfo::loadRefresh);
5083 * LoadURI(...) will cancel all refresh timers... This causes the
5084 * Timer and its refreshData instance to be released...
5086 LoadURI(aURI, loadInfo, nsIWebNavigation::LOAD_FLAGS_NONE, PR_TRUE);
5088 return NS_OK;
5091 nsresult
5092 nsDocShell::SetupRefreshURIFromHeader(nsIURI * aBaseURI,
5093 const nsACString & aHeader)
5095 // Refresh headers are parsed with the following format in mind
5096 // <META HTTP-EQUIV=REFRESH CONTENT="5; URL=http://uri">
5097 // By the time we are here, the following is true:
5098 // header = "REFRESH"
5099 // content = "5; URL=http://uri" // note the URL attribute is
5100 // optional, if it is absent, the currently loaded url is used.
5101 // Also note that the seconds and URL separator can be either
5102 // a ';' or a ','. The ',' separator should be illegal but CNN
5103 // is using it.
5105 // We need to handle the following strings, where
5106 // - X is a set of digits
5107 // - URI is either a relative or absolute URI
5109 // Note that URI should start with "url=" but we allow omission
5111 // "" || ";" || ","
5112 // empty string. use the currently loaded URI
5113 // and refresh immediately.
5114 // "X" || "X;" || "X,"
5115 // Refresh the currently loaded URI in X seconds.
5116 // "X; URI" || "X, URI"
5117 // Refresh using URI as the destination in X seconds.
5118 // "URI" || "; URI" || ", URI"
5119 // Refresh immediately using URI as the destination.
5121 // Currently, anything immediately following the URI, if
5122 // separated by any char in the set "'\"\t\r\n " will be
5123 // ignored. So "10; url=go.html ; foo=bar" will work,
5124 // and so will "10; url='go.html'; foo=bar". However,
5125 // "10; url=go.html; foo=bar" will result in the uri
5126 // "go.html;" since ';' and ',' are valid uri characters.
5128 // Note that we need to remove any tokens wrapping the URI.
5129 // These tokens currently include spaces, double and single
5130 // quotes.
5132 // when done, seconds is 0 or the given number of seconds
5133 // uriAttrib is empty or the URI specified
5134 nsCAutoString uriAttrib;
5135 PRInt32 seconds = 0;
5136 PRBool specifiesSeconds = PR_FALSE;
5138 nsACString::const_iterator iter, tokenStart, doneIterating;
5140 aHeader.BeginReading(iter);
5141 aHeader.EndReading(doneIterating);
5143 // skip leading whitespace
5144 while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter))
5145 ++iter;
5147 tokenStart = iter;
5149 // skip leading + and -
5150 if (iter != doneIterating && (*iter == '-' || *iter == '+'))
5151 ++iter;
5153 // parse number
5154 while (iter != doneIterating && (*iter >= '0' && *iter <= '9')) {
5155 seconds = seconds * 10 + (*iter - '0');
5156 specifiesSeconds = PR_TRUE;
5157 ++iter;
5160 if (iter != doneIterating) {
5161 // if we started with a '-', number is negative
5162 if (*tokenStart == '-')
5163 seconds = -seconds;
5165 // skip to next ';' or ','
5166 nsACString::const_iterator iterAfterDigit = iter;
5167 while (iter != doneIterating && !(*iter == ';' || *iter == ','))
5169 if (specifiesSeconds)
5171 // Non-whitespace characters here mean that the string is
5172 // malformed but tolerate sites that specify a decimal point,
5173 // even though meta refresh only works on whole seconds.
5174 if (iter == iterAfterDigit &&
5175 !nsCRT::IsAsciiSpace(*iter) && *iter != '.')
5177 // The characters between the seconds and the next
5178 // section are just garbage!
5179 // e.g. content="2a0z+,URL=http://www.mozilla.org/"
5180 // Just ignore this redirect.
5181 return NS_ERROR_FAILURE;
5183 else if (nsCRT::IsAsciiSpace(*iter))
5185 // We've had at least one whitespace so tolerate the mistake
5186 // and drop through.
5187 // e.g. content="10 foo"
5188 ++iter;
5189 break;
5192 ++iter;
5195 // skip any remaining whitespace
5196 while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter))
5197 ++iter;
5199 // skip ';' or ','
5200 if (iter != doneIterating && (*iter == ';' || *iter == ',')) {
5201 ++iter;
5204 // skip whitespace
5205 while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter))
5206 ++iter;
5209 // possible start of URI
5210 tokenStart = iter;
5212 // skip "url = " to real start of URI
5213 if (iter != doneIterating && (*iter == 'u' || *iter == 'U')) {
5214 ++iter;
5215 if (iter != doneIterating && (*iter == 'r' || *iter == 'R')) {
5216 ++iter;
5217 if (iter != doneIterating && (*iter == 'l' || *iter == 'L')) {
5218 ++iter;
5220 // skip whitespace
5221 while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter))
5222 ++iter;
5224 if (iter != doneIterating && *iter == '=') {
5225 ++iter;
5227 // skip whitespace
5228 while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter))
5229 ++iter;
5231 // found real start of URI
5232 tokenStart = iter;
5238 // skip a leading '"' or '\''.
5240 PRBool isQuotedURI = PR_FALSE;
5241 if (tokenStart != doneIterating && (*tokenStart == '"' || *tokenStart == '\''))
5243 isQuotedURI = PR_TRUE;
5244 ++tokenStart;
5247 // set iter to start of URI
5248 iter = tokenStart;
5250 // tokenStart here points to the beginning of URI
5252 // grab the rest of the URI
5253 while (iter != doneIterating)
5255 if (isQuotedURI && (*iter == '"' || *iter == '\''))
5256 break;
5257 ++iter;
5260 // move iter one back if the last character is a '"' or '\''
5261 if (iter != tokenStart && isQuotedURI) {
5262 --iter;
5263 if (!(*iter == '"' || *iter == '\''))
5264 ++iter;
5267 // URI is whatever's contained from tokenStart to iter.
5268 // note: if tokenStart == doneIterating, so is iter.
5270 nsresult rv = NS_OK;
5272 nsCOMPtr<nsIURI> uri;
5273 PRBool specifiesURI = PR_FALSE;
5274 if (tokenStart == iter) {
5275 uri = aBaseURI;
5277 else {
5278 uriAttrib = Substring(tokenStart, iter);
5279 // NS_NewURI takes care of any whitespace surrounding the URL
5280 rv = NS_NewURI(getter_AddRefs(uri), uriAttrib, nsnull, aBaseURI);
5281 specifiesURI = PR_TRUE;
5284 // No URI or seconds were specified
5285 if (!specifiesSeconds && !specifiesURI)
5287 // Do nothing because the alternative is to spin around in a refresh
5288 // loop forever!
5289 return NS_ERROR_FAILURE;
5292 if (NS_SUCCEEDED(rv)) {
5293 nsCOMPtr<nsIScriptSecurityManager>
5294 securityManager(do_GetService
5295 (NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv));
5296 if (NS_SUCCEEDED(rv)) {
5297 rv = securityManager->
5298 CheckLoadURI(aBaseURI, uri,
5299 nsIScriptSecurityManager::
5300 LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT);
5302 if (NS_SUCCEEDED(rv)) {
5303 PRBool isjs = PR_TRUE;
5304 rv = NS_URIChainHasFlags(uri,
5305 nsIProtocolHandler::URI_OPENING_EXECUTES_SCRIPT, &isjs);
5306 NS_ENSURE_SUCCESS(rv, rv);
5308 if (isjs) {
5309 return NS_ERROR_FAILURE;
5313 if (NS_SUCCEEDED(rv)) {
5314 // Since we can't travel back in time yet, just pretend
5315 // negative numbers do nothing at all.
5316 if (seconds < 0)
5317 return NS_ERROR_FAILURE;
5319 rv = RefreshURI(uri, seconds * 1000, PR_FALSE, PR_TRUE);
5323 return rv;
5326 NS_IMETHODIMP nsDocShell::SetupRefreshURI(nsIChannel * aChannel)
5328 nsresult rv;
5329 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel, &rv));
5330 if (NS_SUCCEEDED(rv)) {
5331 nsCAutoString refreshHeader;
5332 rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("refresh"),
5333 refreshHeader);
5335 if (!refreshHeader.IsEmpty()) {
5336 SetupReferrerFromChannel(aChannel);
5337 rv = SetupRefreshURIFromHeader(mCurrentURI, refreshHeader);
5338 if (NS_SUCCEEDED(rv)) {
5339 return NS_REFRESHURI_HEADER_FOUND;
5343 return rv;
5346 static void
5347 DoCancelRefreshURITimers(nsISupportsArray* aTimerList)
5349 if (!aTimerList)
5350 return;
5352 PRUint32 n=0;
5353 aTimerList->Count(&n);
5355 while (n) {
5356 nsCOMPtr<nsITimer> timer(do_QueryElementAt(aTimerList, --n));
5358 aTimerList->RemoveElementAt(n); // bye bye owning timer ref
5360 if (timer)
5361 timer->Cancel();
5365 NS_IMETHODIMP
5366 nsDocShell::CancelRefreshURITimers()
5368 DoCancelRefreshURITimers(mRefreshURIList);
5369 DoCancelRefreshURITimers(mSavedRefreshURIList);
5370 mRefreshURIList = nsnull;
5371 mSavedRefreshURIList = nsnull;
5373 return NS_OK;
5376 NS_IMETHODIMP
5377 nsDocShell::GetRefreshPending(PRBool* _retval)
5379 if (!mRefreshURIList) {
5380 *_retval = PR_FALSE;
5381 return NS_OK;
5384 PRUint32 count;
5385 nsresult rv = mRefreshURIList->Count(&count);
5386 if (NS_SUCCEEDED(rv))
5387 *_retval = (count != 0);
5388 return rv;
5391 NS_IMETHODIMP
5392 nsDocShell::SuspendRefreshURIs()
5394 if (mRefreshURIList) {
5395 PRUint32 n = 0;
5396 mRefreshURIList->Count(&n);
5398 for (PRUint32 i = 0; i < n; ++i) {
5399 nsCOMPtr<nsITimer> timer = do_QueryElementAt(mRefreshURIList, i);
5400 if (!timer)
5401 continue; // this must be a nsRefreshURI already
5403 // Replace this timer object with a nsRefreshTimer object.
5404 nsCOMPtr<nsITimerCallback> callback;
5405 timer->GetCallback(getter_AddRefs(callback));
5407 timer->Cancel();
5409 nsCOMPtr<nsITimerCallback> rt = do_QueryInterface(callback);
5410 NS_ASSERTION(rt, "RefreshURIList timer callbacks should only be RefreshTimer objects");
5412 mRefreshURIList->ReplaceElementAt(rt, i);
5416 // Suspend refresh URIs for our child shells as well.
5417 PRInt32 n = mChildList.Count();
5419 for (PRInt32 i = 0; i < n; ++i) {
5420 nsCOMPtr<nsIDocShell> shell = do_QueryInterface(ChildAt(i));
5421 if (shell)
5422 shell->SuspendRefreshURIs();
5425 return NS_OK;
5428 NS_IMETHODIMP
5429 nsDocShell::ResumeRefreshURIs()
5431 RefreshURIFromQueue();
5433 // Resume refresh URIs for our child shells as well.
5434 PRInt32 n = mChildList.Count();
5436 for (PRInt32 i = 0; i < n; ++i) {
5437 nsCOMPtr<nsIDocShell> shell = do_QueryInterface(ChildAt(i));
5438 if (shell)
5439 shell->ResumeRefreshURIs();
5442 return NS_OK;
5445 nsresult
5446 nsDocShell::RefreshURIFromQueue()
5448 if (!mRefreshURIList)
5449 return NS_OK;
5450 PRUint32 n = 0;
5451 mRefreshURIList->Count(&n);
5453 while (n) {
5454 nsCOMPtr<nsISupports> element;
5455 mRefreshURIList->GetElementAt(--n, getter_AddRefs(element));
5456 nsCOMPtr<nsITimerCallback> refreshInfo(do_QueryInterface(element));
5458 if (refreshInfo) {
5459 // This is the nsRefreshTimer object, waiting to be
5460 // setup in a timer object and fired.
5461 // Create the timer and trigger it.
5462 PRUint32 delay = static_cast<nsRefreshTimer*>(static_cast<nsITimerCallback*>(refreshInfo))->GetDelay();
5463 nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1");
5464 if (timer) {
5465 // Replace the nsRefreshTimer element in the queue with
5466 // its corresponding timer object, so that in case another
5467 // load comes through before the timer can go off, the timer will
5468 // get cancelled in CancelRefreshURITimer()
5469 mRefreshURIList->ReplaceElementAt(timer, n);
5470 timer->InitWithCallback(refreshInfo, delay, nsITimer::TYPE_ONE_SHOT);
5473 } // while
5475 return NS_OK;
5478 //*****************************************************************************
5479 // nsDocShell::nsIContentViewerContainer
5480 //*****************************************************************************
5482 NS_IMETHODIMP
5483 nsDocShell::Embed(nsIContentViewer * aContentViewer,
5484 const char *aCommand, nsISupports * aExtraInfo)
5486 // Save the LayoutHistoryState of the previous document, before
5487 // setting up new document
5488 PersistLayoutHistoryState();
5490 nsresult rv = SetupNewViewer(aContentViewer);
5492 // If we are loading a wyciwyg url from history, change the base URI for
5493 // the document to the original http url that created the document.write().
5494 // This makes sure that all relative urls in a document.written page loaded
5495 // via history work properly.
5496 if (mCurrentURI &&
5497 (mLoadType & LOAD_CMD_HISTORY ||
5498 mLoadType == LOAD_RELOAD_NORMAL ||
5499 mLoadType == LOAD_RELOAD_CHARSET_CHANGE)){
5500 PRBool isWyciwyg = PR_FALSE;
5501 // Check if the url is wyciwyg
5502 rv = mCurrentURI->SchemeIs("wyciwyg", &isWyciwyg);
5503 if (isWyciwyg && NS_SUCCEEDED(rv))
5504 SetBaseUrlForWyciwyg(aContentViewer);
5506 // XXX What if SetupNewViewer fails?
5507 if (mLSHE) {
5508 // Restore the editing state, if it's stored in session history.
5509 if (mLSHE->HasDetachedEditor()) {
5510 ReattachEditorToWindow(mLSHE);
5512 SetHistoryEntry(&mOSHE, mLSHE);
5515 PRBool updateHistory = PR_TRUE;
5517 // Determine if this type of load should update history
5518 switch (mLoadType) {
5519 case LOAD_NORMAL_REPLACE:
5520 case LOAD_STOP_CONTENT_AND_REPLACE:
5521 case LOAD_RELOAD_BYPASS_CACHE:
5522 case LOAD_RELOAD_BYPASS_PROXY:
5523 case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
5524 updateHistory = PR_FALSE;
5525 break;
5526 default:
5527 break;
5530 if (!updateHistory)
5531 SetLayoutHistoryState(nsnull);
5533 return NS_OK;
5536 /* void setIsPrinting (in boolean aIsPrinting); */
5537 NS_IMETHODIMP
5538 nsDocShell::SetIsPrinting(PRBool aIsPrinting)
5540 mIsPrintingOrPP = aIsPrinting;
5541 return NS_OK;
5544 //*****************************************************************************
5545 // nsDocShell::nsIWebProgressListener
5546 //*****************************************************************************
5548 NS_IMETHODIMP
5549 nsDocShell::OnProgressChange(nsIWebProgress * aProgress,
5550 nsIRequest * aRequest,
5551 PRInt32 aCurSelfProgress,
5552 PRInt32 aMaxSelfProgress,
5553 PRInt32 aCurTotalProgress,
5554 PRInt32 aMaxTotalProgress)
5556 return NS_OK;
5559 NS_IMETHODIMP
5560 nsDocShell::OnStateChange(nsIWebProgress * aProgress, nsIRequest * aRequest,
5561 PRUint32 aStateFlags, nsresult aStatus)
5563 nsresult rv;
5565 if ((~aStateFlags & (STATE_START | STATE_IS_NETWORK)) == 0) {
5566 nsCOMPtr<nsIWyciwygChannel> wcwgChannel(do_QueryInterface(aRequest));
5567 nsCOMPtr<nsIWebProgress> webProgress =
5568 do_QueryInterface(GetAsSupports(this));
5570 // Was the wyciwyg document loaded on this docshell?
5571 if (wcwgChannel && !mLSHE && (mItemType == typeContent) && aProgress == webProgress.get()) {
5572 nsCOMPtr<nsIURI> uri;
5573 wcwgChannel->GetURI(getter_AddRefs(uri));
5575 PRBool equalUri = PR_TRUE;
5576 // Store the wyciwyg url in session history, only if it is
5577 // being loaded fresh for the first time. We don't want
5578 // multiple entries for successive loads
5579 if (mCurrentURI &&
5580 NS_SUCCEEDED(uri->Equals(mCurrentURI, &equalUri)) &&
5581 !equalUri) {
5582 // This is a document.write(). Get the made-up url
5583 // from the channel and store it in session history.
5584 rv = AddToSessionHistory(uri, wcwgChannel, nsnull,
5585 getter_AddRefs(mLSHE));
5586 SetCurrentURI(uri, aRequest, PR_TRUE);
5587 // Save history state of the previous page
5588 rv = PersistLayoutHistoryState();
5589 // We'll never get an Embed() for this load, so just go ahead
5590 // and SetHistoryEntry now.
5591 SetHistoryEntry(&mOSHE, mLSHE);
5595 // Page has begun to load
5596 mBusyFlags = BUSY_FLAGS_BUSY | BUSY_FLAGS_BEFORE_PAGE_LOAD;
5598 if ((aStateFlags & STATE_RESTORING) == 0) {
5599 // Show the progress cursor if the pref is set
5600 PRBool tmpBool = PR_FALSE;
5601 if (NS_SUCCEEDED(mPrefs->GetBoolPref("ui.use_activity_cursor", &tmpBool))
5602 && tmpBool) {
5603 nsCOMPtr<nsIWidget> mainWidget;
5604 GetMainWidget(getter_AddRefs(mainWidget));
5605 if (mainWidget) {
5606 mainWidget->SetCursor(eCursor_spinning);
5611 else if ((~aStateFlags & (STATE_TRANSFERRING | STATE_IS_DOCUMENT)) == 0) {
5612 // Page is loading
5613 mBusyFlags = BUSY_FLAGS_BUSY | BUSY_FLAGS_PAGE_LOADING;
5615 else if ((aStateFlags & STATE_STOP) && (aStateFlags & STATE_IS_NETWORK)) {
5616 // Page has finished loading
5617 mBusyFlags = BUSY_FLAGS_NONE;
5619 // Hide the progress cursor if the pref is set
5620 PRBool tmpBool = PR_FALSE;
5621 if (NS_SUCCEEDED(mPrefs->GetBoolPref("ui.use_activity_cursor", &tmpBool))
5622 && tmpBool) {
5623 nsCOMPtr<nsIWidget> mainWidget;
5624 GetMainWidget(getter_AddRefs(mainWidget));
5625 if (mainWidget) {
5626 mainWidget->SetCursor(eCursor_standard);
5630 if ((~aStateFlags & (STATE_IS_DOCUMENT | STATE_STOP)) == 0) {
5631 nsCOMPtr<nsIWebProgress> webProgress =
5632 do_QueryInterface(GetAsSupports(this));
5633 // Is the document stop notification for this document?
5634 if (aProgress == webProgress.get()) {
5635 nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
5636 EndPageLoad(aProgress, channel, aStatus);
5639 // note that redirect state changes will go through here as well, but it
5640 // is better to handle those in OnRedirectStateChange where more
5641 // information is available.
5642 return NS_OK;
5645 NS_IMETHODIMP
5646 nsDocShell::OnLocationChange(nsIWebProgress * aProgress,
5647 nsIRequest * aRequest, nsIURI * aURI)
5649 NS_NOTREACHED("notification excluded in AddProgressListener(...)");
5650 return NS_OK;
5653 void
5654 nsDocShell::OnRedirectStateChange(nsIChannel* aOldChannel,
5655 nsIChannel* aNewChannel,
5656 PRUint32 aRedirectFlags,
5657 PRUint32 aStateFlags)
5659 NS_ASSERTION(aStateFlags & STATE_REDIRECTING,
5660 "Calling OnRedirectStateChange when there is no redirect");
5661 if (!(aStateFlags & STATE_IS_DOCUMENT))
5662 return; // not a toplevel document
5664 nsCOMPtr<nsIGlobalHistory3> history3(do_QueryInterface(mGlobalHistory));
5665 nsresult result = NS_ERROR_NOT_IMPLEMENTED;
5666 if (history3) {
5667 // notify global history of this redirect
5668 result = history3->AddDocumentRedirect(aOldChannel, aNewChannel,
5669 aRedirectFlags, !IsFrame());
5672 if (result == NS_ERROR_NOT_IMPLEMENTED) {
5673 // when there is no GlobalHistory3, or it doesn't implement
5674 // AddToplevelRedirect, we fall back to GlobalHistory2. Just notify
5675 // that the redirecting page was a rePdirect so it will be link colored
5676 // but not visible.
5677 nsCOMPtr<nsIURI> oldURI;
5678 aOldChannel->GetURI(getter_AddRefs(oldURI));
5679 if (! oldURI)
5680 return; // nothing to tell anybody about
5681 AddToGlobalHistory(oldURI, PR_TRUE, aOldChannel);
5684 // check if the new load should go through the application cache.
5685 nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
5686 do_QueryInterface(aNewChannel);
5687 if (appCacheChannel) {
5688 nsCOMPtr<nsIURI> newURI;
5689 aNewChannel->GetURI(getter_AddRefs(newURI));
5690 appCacheChannel->SetChooseApplicationCache(ShouldCheckAppCache(newURI));
5693 if (!(aRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL) &&
5694 mLoadType & (LOAD_CMD_RELOAD | LOAD_CMD_HISTORY)) {
5695 mLoadType = LOAD_NORMAL_REPLACE;
5696 SetHistoryEntry(&mLSHE, nsnull);
5700 NS_IMETHODIMP
5701 nsDocShell::OnStatusChange(nsIWebProgress * aWebProgress,
5702 nsIRequest * aRequest,
5703 nsresult aStatus, const PRUnichar * aMessage)
5705 NS_NOTREACHED("notification excluded in AddProgressListener(...)");
5706 return NS_OK;
5709 NS_IMETHODIMP
5710 nsDocShell::OnSecurityChange(nsIWebProgress * aWebProgress,
5711 nsIRequest * aRequest, PRUint32 state)
5713 NS_NOTREACHED("notification excluded in AddProgressListener(...)");
5714 return NS_OK;
5718 nsresult
5719 nsDocShell::EndPageLoad(nsIWebProgress * aProgress,
5720 nsIChannel * aChannel, nsresult aStatus)
5722 if(!aChannel)
5723 return NS_ERROR_NULL_POINTER;
5725 nsCOMPtr<nsIURI> url;
5726 nsresult rv = aChannel->GetURI(getter_AddRefs(url));
5727 if (NS_FAILED(rv)) return rv;
5729 // clean up reload state for meta charset
5730 if (eCharsetReloadRequested == mCharsetReloadState)
5731 mCharsetReloadState = eCharsetReloadStopOrigional;
5732 else
5733 mCharsetReloadState = eCharsetReloadInit;
5735 // Save a pointer to the currently-loading history entry.
5736 // nsDocShell::EndPageLoad will clear mLSHE, but we may need this history
5737 // entry further down in this method.
5738 nsCOMPtr<nsISHEntry> loadingSHE = mLSHE;
5741 // one of many safeguards that prevent death and destruction if
5742 // someone is so very very rude as to bring this window down
5743 // during this load handler.
5745 nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
5747 // Notify the ContentViewer that the Document has finished loading. This
5748 // will cause any OnLoad(...) and PopState(...) handlers to fire.
5749 if (!mEODForCurrentDocument && mContentViewer) {
5750 // Set the pending state object which will be returned to the page in
5751 // the popstate event.
5752 SetDocPendingStateObj(mLSHE);
5754 mIsExecutingOnLoadHandler = PR_TRUE;
5755 mContentViewer->LoadComplete(aStatus);
5756 mIsExecutingOnLoadHandler = PR_FALSE;
5758 mEODForCurrentDocument = PR_TRUE;
5760 // If all documents have completed their loading
5761 // favor native event dispatch priorities
5762 // over performance
5763 if (--gNumberOfDocumentsLoading == 0) {
5764 // Hint to use normal native event dispatch priorities
5765 FavorPerformanceHint(PR_FALSE, NS_EVENT_STARVATION_DELAY_HINT);
5768 /* Check if the httpChannel has any cache-control related response headers,
5769 * like no-store, no-cache. If so, update SHEntry so that
5770 * when a user goes back/forward to this page, we appropriately do
5771 * form value restoration or load from server.
5773 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
5774 if (!httpChannel) // HttpChannel could be hiding underneath a Multipart channel.
5775 GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
5777 if (httpChannel) {
5778 // figure out if SH should be saving layout state.
5779 PRBool discardLayoutState = ShouldDiscardLayoutState(httpChannel);
5780 if (mLSHE && discardLayoutState && (mLoadType & LOAD_CMD_NORMAL) &&
5781 (mLoadType != LOAD_BYPASS_HISTORY) && (mLoadType != LOAD_ERROR_PAGE))
5782 mLSHE->SetSaveLayoutStateFlag(PR_FALSE);
5785 // Clear mLSHE after calling the onLoadHandlers. This way, if the
5786 // onLoadHandler tries to load something different in
5787 // itself or one of its children, we can deal with it appropriately.
5788 if (mLSHE) {
5789 mLSHE->SetLoadType(nsIDocShellLoadInfo::loadHistory);
5791 // Clear the mLSHE reference to indicate document loading is done one
5792 // way or another.
5793 SetHistoryEntry(&mLSHE, nsnull);
5795 // if there's a refresh header in the channel, this method
5796 // will set it up for us.
5797 RefreshURIFromQueue();
5799 // Test whether this is the top frame or a subframe
5800 PRBool isTopFrame = PR_TRUE;
5801 nsCOMPtr<nsIDocShellTreeItem> targetParentTreeItem;
5802 rv = GetSameTypeParent(getter_AddRefs(targetParentTreeItem));
5803 if (NS_SUCCEEDED(rv) && targetParentTreeItem) {
5804 isTopFrame = PR_FALSE;
5808 // If the page load failed, then deal with the error condition...
5809 // Errors are handled as follows:
5810 // 1. Check to see if it's a file not found error or bad content
5811 // encoding error.
5812 // 2. Send the URI to a keyword server (if enabled)
5813 // 3. If the error was DNS failure, then add www and .com to the URI
5814 // (if appropriate).
5815 // 4. Throw an error dialog box...
5817 if (url && NS_FAILED(aStatus)) {
5818 if (aStatus == NS_ERROR_FILE_NOT_FOUND ||
5819 aStatus == NS_ERROR_INVALID_CONTENT_ENCODING) {
5820 DisplayLoadError(aStatus, url, nsnull, aChannel);
5821 return NS_OK;
5824 if (sURIFixup) {
5826 // Try and make an alternative URI from the old one
5828 nsCOMPtr<nsIURI> newURI;
5830 nsCAutoString oldSpec;
5831 url->GetSpec(oldSpec);
5834 // First try keyword fixup
5836 if (aStatus == NS_ERROR_UNKNOWN_HOST && mAllowKeywordFixup) {
5837 PRBool keywordsEnabled = PR_FALSE;
5839 if (mPrefs &&
5840 NS_FAILED(mPrefs->GetBoolPref("keyword.enabled",
5841 &keywordsEnabled)))
5842 keywordsEnabled = PR_FALSE;
5844 nsCAutoString host;
5845 url->GetHost(host);
5847 nsCAutoString scheme;
5848 url->GetScheme(scheme);
5850 PRInt32 dotLoc = host.FindChar('.');
5852 // we should only perform a keyword search under the following
5853 // conditions:
5854 // (0) Pref keyword.enabled is true
5855 // (1) the url scheme is http (or https)
5856 // (2) the url does not have a protocol scheme
5857 // If we don't enforce such a policy, then we end up doing
5858 // keyword searchs on urls we don't intend like imap, file,
5859 // mailbox, etc. This could lead to a security problem where we
5860 // send data to the keyword server that we shouldn't be.
5861 // Someone needs to clean up keywords in general so we can
5862 // determine on a per url basis if we want keywords
5863 // enabled...this is just a bandaid...
5864 if (keywordsEnabled && !scheme.IsEmpty() &&
5865 (scheme.Find("http") != 0)) {
5866 keywordsEnabled = PR_FALSE;
5869 if (keywordsEnabled && (kNotFound == dotLoc)) {
5870 // only send non-qualified hosts to the keyword server
5872 // If this string was passed through nsStandardURL by
5873 // chance, then it may have been converted from UTF-8 to
5874 // ACE, which would result in a completely bogus keyword
5875 // query. Here we try to recover the original Unicode
5876 // value, but this is not 100% correct since the value may
5877 // have been normalized per the IDN normalization rules.
5879 // Since we don't have access to the exact original string
5880 // that was entered by the user, this will just have to do.
5881 PRBool isACE;
5882 nsCAutoString utf8Host;
5883 nsCOMPtr<nsIIDNService> idnSrv =
5884 do_GetService(NS_IDNSERVICE_CONTRACTID);
5885 if (idnSrv &&
5886 NS_SUCCEEDED(idnSrv->IsACE(host, &isACE)) && isACE &&
5887 NS_SUCCEEDED(idnSrv->ConvertACEtoUTF8(host, utf8Host)))
5888 sURIFixup->KeywordToURI(utf8Host,
5889 getter_AddRefs(newURI));
5890 else
5891 sURIFixup->KeywordToURI(host, getter_AddRefs(newURI));
5892 } // end keywordsEnabled
5896 // Now try change the address, e.g. turn http://foo into
5897 // http://www.foo.com
5899 if (aStatus == NS_ERROR_UNKNOWN_HOST ||
5900 aStatus == NS_ERROR_NET_RESET) {
5901 PRBool doCreateAlternate = PR_TRUE;
5903 // Skip fixup for anything except a normal document load
5904 // operation on the topframe.
5906 if (mLoadType != LOAD_NORMAL || !isTopFrame) {
5907 doCreateAlternate = PR_FALSE;
5909 else {
5910 // Test if keyword lookup produced a new URI or not
5911 if (newURI) {
5912 PRBool sameURI = PR_FALSE;
5913 url->Equals(newURI, &sameURI);
5914 if (!sameURI) {
5915 // Keyword lookup made a new URI so no need to try
5916 // an alternate one.
5917 doCreateAlternate = PR_FALSE;
5921 if (doCreateAlternate) {
5922 newURI = nsnull;
5923 sURIFixup->CreateFixupURI(oldSpec,
5924 nsIURIFixup::FIXUP_FLAGS_MAKE_ALTERNATE_URI,
5925 getter_AddRefs(newURI));
5929 // Did we make a new URI that is different to the old one? If so
5930 // load it.
5932 if (newURI) {
5933 // Make sure the new URI is different from the old one,
5934 // otherwise there's little point trying to load it again.
5935 PRBool sameURI = PR_FALSE;
5936 url->Equals(newURI, &sameURI);
5937 if (!sameURI) {
5938 nsCAutoString newSpec;
5939 newURI->GetSpec(newSpec);
5940 NS_ConvertUTF8toUTF16 newSpecW(newSpec);
5942 return LoadURI(newSpecW.get(), // URI string
5943 LOAD_FLAGS_NONE, // Load flags
5944 nsnull, // Referring URI
5945 nsnull, // Post data stream
5946 nsnull); // Headers stream
5951 // Well, fixup didn't work :-(
5952 // It is time to throw an error dialog box, and be done with it...
5954 // Errors to be shown only on top-level frames
5955 if ((aStatus == NS_ERROR_UNKNOWN_HOST ||
5956 aStatus == NS_ERROR_CONNECTION_REFUSED ||
5957 aStatus == NS_ERROR_UNKNOWN_PROXY_HOST ||
5958 aStatus == NS_ERROR_PROXY_CONNECTION_REFUSED) &&
5959 (isTopFrame || mUseErrorPages)) {
5960 DisplayLoadError(aStatus, url, nsnull, aChannel);
5962 // Errors to be shown for any frame
5963 else if (aStatus == NS_ERROR_NET_TIMEOUT ||
5964 aStatus == NS_ERROR_REDIRECT_LOOP ||
5965 aStatus == NS_ERROR_UNKNOWN_SOCKET_TYPE ||
5966 aStatus == NS_ERROR_NET_INTERRUPT ||
5967 aStatus == NS_ERROR_NET_RESET ||
5968 aStatus == NS_ERROR_MALWARE_URI ||
5969 aStatus == NS_ERROR_PHISHING_URI ||
5970 aStatus == NS_ERROR_UNSAFE_CONTENT_TYPE ||
5971 NS_ERROR_GET_MODULE(aStatus) == NS_ERROR_MODULE_SECURITY) {
5972 DisplayLoadError(aStatus, url, nsnull, aChannel);
5974 else if (aStatus == NS_ERROR_DOCUMENT_NOT_CACHED) {
5975 /* A document that was requested to be fetched *only* from
5976 * the cache is not in cache. May be this is one of those
5977 * postdata results. Throw a dialog to the user,
5978 * saying that the page has expired from cache and ask if
5979 * they wish to refetch the page from the net. Do this only
5980 * if the request is a form post.
5982 nsCAutoString method;
5983 if (httpChannel)
5984 httpChannel->GetRequestMethod(method);
5985 if (method.Equals("POST") && !NS_IsOffline()) {
5986 PRBool repost;
5987 rv = ConfirmRepost(&repost);
5988 if (NS_FAILED(rv)) return rv;
5989 // If the user pressed cancel in the dialog, return. Don't try
5990 // to load the page without the post data.
5991 if (!repost)
5992 return NS_OK;
5994 // The user wants to repost the data to the server.
5995 // If the page was loaded due to a back/forward/go
5996 // operation, update the session history index.
5997 // This is similar to the updating done in
5998 // nsDocShell::OnNewURI() for regular pages
5999 nsCOMPtr<nsISHistory> rootSH=mSessionHistory;
6000 if (!mSessionHistory) {
6001 nsCOMPtr<nsIDocShellTreeItem> root;
6002 //Get the root docshell
6003 GetSameTypeRootTreeItem(getter_AddRefs(root));
6004 if (root) {
6005 // QI root to nsIWebNavigation
6006 nsCOMPtr<nsIWebNavigation> rootAsWebnav =
6007 do_QueryInterface(root);
6008 if (rootAsWebnav) {
6009 // Get the handle to SH from the root docshell
6010 rootAsWebnav->GetSessionHistory(getter_AddRefs(rootSH));
6013 } // mSessionHistory
6015 if (rootSH && (mLoadType & LOAD_CMD_HISTORY)) {
6016 nsCOMPtr<nsISHistoryInternal> shInternal =
6017 do_QueryInterface(rootSH);
6018 if (shInternal) {
6019 rootSH->GetIndex(&mPreviousTransIndex);
6020 shInternal->UpdateIndex();
6021 rootSH->GetIndex(&mLoadedTransIndex);
6022 #ifdef DEBUG_PAGE_CACHE
6023 printf("Previous index: %d, Loaded index: %d\n\n",
6024 mPreviousTransIndex, mLoadedTransIndex);
6025 #endif
6029 // Make it look like we really did honestly finish loading the
6030 // history page we were loading, since the "reload" load we're
6031 // about to kick off will reload our current history entry.
6032 // This is a bit of a hack, and if the force-load fails I think
6033 // we'll end up being confused about what page we're on... but
6034 // we would anyway, since we've updated the session history
6035 // index above.
6036 SetHistoryEntry(&mOSHE, loadingSHE);
6038 // The user does want to repost the data to the server.
6039 // Initiate a new load again.
6041 // Get the postdata if any from the channel.
6042 nsCOMPtr<nsIInputStream> inputStream;
6043 nsCOMPtr<nsIURI> referrer;
6044 if (httpChannel) {
6045 httpChannel->GetReferrer(getter_AddRefs(referrer));
6046 nsCOMPtr<nsIUploadChannel> uploadChannel =
6047 do_QueryInterface(aChannel);
6048 if (uploadChannel) {
6049 uploadChannel->GetUploadStream(getter_AddRefs(inputStream));
6052 nsCOMPtr<nsISeekableStream> postDataSeekable =
6053 do_QueryInterface(inputStream);
6054 if (postDataSeekable) {
6055 postDataSeekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
6057 InternalLoad(url, // URI
6058 referrer, // Referring URI
6059 nsnull, // Owner
6060 INTERNAL_LOAD_FLAGS_INHERIT_OWNER, // Inherit owner
6061 nsnull, // No window target
6062 nsnull, // No type hint
6063 inputStream, // Post data stream
6064 nsnull, // No headers stream
6065 LOAD_RELOAD_BYPASS_PROXY_AND_CACHE,// Load type
6066 nsnull, // No SHEntry
6067 PR_TRUE, // first party site
6068 nsnull, // No nsIDocShell
6069 nsnull); // No nsIRequest
6071 else {
6072 DisplayLoadError(aStatus, url, nsnull, aChannel);
6075 } // if we have a host
6077 return NS_OK;
6081 //*****************************************************************************
6082 // nsDocShell: Content Viewer Management
6083 //*****************************************************************************
6085 NS_IMETHODIMP
6086 nsDocShell::EnsureContentViewer()
6088 if (mContentViewer)
6089 return NS_OK;
6090 if (mIsBeingDestroyed)
6091 return NS_ERROR_FAILURE;
6093 NS_TIME_FUNCTION;
6095 nsIPrincipal* principal = nsnull;
6096 nsCOMPtr<nsIURI> baseURI;
6098 nsCOMPtr<nsPIDOMWindow> piDOMWindow(do_QueryInterface(mScriptGlobal));
6099 if (piDOMWindow) {
6100 principal = piDOMWindow->GetOpenerScriptPrincipal();
6103 if (!principal) {
6104 principal = GetInheritedPrincipal(PR_FALSE);
6105 nsCOMPtr<nsIDocShellTreeItem> parentItem;
6106 GetSameTypeParent(getter_AddRefs(parentItem));
6107 if (parentItem) {
6108 nsCOMPtr<nsPIDOMWindow> domWin = do_GetInterface(GetAsSupports(this));
6109 if (domWin) {
6110 nsCOMPtr<nsIContent> parentContent =
6111 do_QueryInterface(domWin->GetFrameElementInternal());
6112 if (parentContent) {
6113 baseURI = parentContent->GetBaseURI();
6119 nsresult rv = CreateAboutBlankContentViewer(principal, baseURI);
6121 if (NS_SUCCEEDED(rv)) {
6122 nsCOMPtr<nsIDOMDocument> domDoc;
6123 mContentViewer->GetDOMDocument(getter_AddRefs(domDoc));
6124 nsCOMPtr<nsIDocument> doc(do_QueryInterface(domDoc));
6125 NS_ASSERTION(doc,
6126 "Should have doc if CreateAboutBlankContentViewer "
6127 "succeeded!");
6129 doc->SetIsInitialDocument(PR_TRUE);
6132 return rv;
6135 nsresult
6136 nsDocShell::CreateAboutBlankContentViewer(nsIPrincipal* aPrincipal,
6137 nsIURI* aBaseURI)
6139 nsCOMPtr<nsIDocument> blankDoc;
6140 nsCOMPtr<nsIContentViewer> viewer;
6141 nsresult rv = NS_ERROR_FAILURE;
6143 /* mCreatingDocument should never be true at this point. However, it's
6144 a theoretical possibility. We want to know about it and make it stop,
6145 and this sounds like a job for an assertion. */
6146 NS_ASSERTION(!mCreatingDocument, "infinite(?) loop creating document averted");
6147 if (mCreatingDocument)
6148 return NS_ERROR_FAILURE;
6150 mCreatingDocument = PR_TRUE;
6152 // mContentViewer->PermitUnload may release |this| docshell.
6153 nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
6155 if (mContentViewer) {
6156 // We've got a content viewer already. Make sure the user
6157 // permits us to discard the current document and replace it
6158 // with about:blank. And also ensure we fire the unload events
6159 // in the current document.
6161 PRBool okToUnload;
6162 rv = mContentViewer->PermitUnload(PR_FALSE, &okToUnload);
6164 if (NS_SUCCEEDED(rv) && !okToUnload) {
6165 // The user chose not to unload the page, interrupt the load.
6166 return NS_ERROR_FAILURE;
6169 mSavingOldViewer = CanSavePresentation(LOAD_NORMAL, nsnull, nsnull);
6171 // Make sure to blow away our mLoadingURI just in case. No loads
6172 // from inside this pagehide.
6173 mLoadingURI = nsnull;
6175 // Notify the current document that it is about to be unloaded!!
6177 // It is important to fire the unload() notification *before* any state
6178 // is changed within the DocShell - otherwise, javascript will get the
6179 // wrong information :-(
6181 (void) FirePageHideNotification(!mSavingOldViewer);
6184 // Now make sure we don't think we're in the middle of firing unload after
6185 // this point. This will make us fire unload when the about:blank document
6186 // unloads... but that's ok, more or less. Would be nice if it fired load
6187 // too, of course.
6188 mFiredUnloadEvent = PR_FALSE;
6190 // one helper factory, please
6191 nsCOMPtr<nsICategoryManager> catMan(do_GetService(NS_CATEGORYMANAGER_CONTRACTID));
6192 if (!catMan)
6193 return NS_ERROR_FAILURE;
6195 nsXPIDLCString contractId;
6196 rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", "text/html", getter_Copies(contractId));
6197 if (NS_FAILED(rv))
6198 return rv;
6200 nsCOMPtr<nsIDocumentLoaderFactory> docFactory(do_GetService(contractId));
6201 if (docFactory) {
6202 // generate (about:blank) document to load
6203 docFactory->CreateBlankDocument(mLoadGroup, aPrincipal,
6204 getter_AddRefs(blankDoc));
6205 if (blankDoc) {
6206 // Hack: set the base URI manually, since this document never
6207 // got Reset() with a channel.
6208 blankDoc->SetBaseURI(aBaseURI);
6210 blankDoc->SetContainer(static_cast<nsIDocShell *>(this));
6212 // create a content viewer for us and the new document
6213 docFactory->CreateInstanceForDocument(NS_ISUPPORTS_CAST(nsIDocShell *, this),
6214 blankDoc, "view", getter_AddRefs(viewer));
6216 // hook 'em up
6217 if (viewer) {
6218 viewer->SetContainer(static_cast<nsIContentViewerContainer *>(this));
6219 nsCOMPtr<nsIDOMDocument> domdoc(do_QueryInterface(blankDoc));
6220 Embed(viewer, "", 0);
6221 viewer->SetDOMDocument(domdoc);
6223 SetCurrentURI(blankDoc->GetDocumentURI(), nsnull, PR_TRUE);
6224 rv = mIsBeingDestroyed ? NS_ERROR_NOT_AVAILABLE : NS_OK;
6228 mCreatingDocument = PR_FALSE;
6230 // The transient about:blank viewer doesn't have a session history entry.
6231 SetHistoryEntry(&mOSHE, nsnull);
6233 return rv;
6236 PRBool
6237 nsDocShell::CanSavePresentation(PRUint32 aLoadType,
6238 nsIRequest *aNewRequest,
6239 nsIDocument *aNewDocument)
6241 if (!mOSHE)
6242 return PR_FALSE; // no entry to save into
6244 // Only save presentation for "normal" loads and link loads. Anything else
6245 // probably wants to refetch the page, so caching the old presentation
6246 // would be incorrect.
6247 if (aLoadType != LOAD_NORMAL &&
6248 aLoadType != LOAD_HISTORY &&
6249 aLoadType != LOAD_LINK &&
6250 aLoadType != LOAD_STOP_CONTENT &&
6251 aLoadType != LOAD_STOP_CONTENT_AND_REPLACE &&
6252 aLoadType != LOAD_ERROR_PAGE)
6253 return PR_FALSE;
6255 // If the session history entry has the saveLayoutState flag set to false,
6256 // then we should not cache the presentation.
6257 PRBool canSaveState;
6258 mOSHE->GetSaveLayoutStateFlag(&canSaveState);
6259 if (!canSaveState)
6260 return PR_FALSE;
6262 // If the document is not done loading, don't cache it.
6263 nsCOMPtr<nsPIDOMWindow> pWin = do_QueryInterface(mScriptGlobal);
6264 if (!pWin || pWin->IsLoading())
6265 return PR_FALSE;
6267 if (pWin->WouldReuseInnerWindow(aNewDocument))
6268 return PR_FALSE;
6270 // Avoid doing the work of saving the presentation state in the case where
6271 // the content viewer cache is disabled.
6272 if (nsSHistory::GetMaxTotalViewers() == 0)
6273 return PR_FALSE;
6275 // Don't cache the content viewer if we're in a subframe and the subframe
6276 // pref is disabled.
6277 PRBool cacheFrames = PR_FALSE;
6278 mPrefs->GetBoolPref("browser.sessionhistory.cache_subframes",
6279 &cacheFrames);
6280 if (!cacheFrames) {
6281 nsCOMPtr<nsIDocShellTreeItem> root;
6282 GetSameTypeParent(getter_AddRefs(root));
6283 if (root && root != this) {
6284 return PR_FALSE; // this is a subframe load
6288 // If the document does not want its presentation cached, then don't.
6289 nsCOMPtr<nsIDocument> doc = do_QueryInterface(pWin->GetExtantDocument());
6290 if (!doc || !doc->CanSavePresentation(aNewRequest))
6291 return PR_FALSE;
6293 return PR_TRUE;
6296 void
6297 nsDocShell::ReattachEditorToWindow(nsISHEntry *aSHEntry)
6299 NS_ASSERTION(!mEditorData,
6300 "Why reattach an editor when we already have one?");
6301 NS_ASSERTION(aSHEntry && aSHEntry->HasDetachedEditor(),
6302 "Reattaching when there's not a detached editor.");
6304 if (mEditorData || !aSHEntry)
6305 return;
6307 mEditorData = aSHEntry->ForgetEditorData();
6308 if (mEditorData) {
6309 nsresult res = mEditorData->ReattachToWindow(this);
6310 NS_ASSERTION(NS_SUCCEEDED(res), "Failed to reattach editing session");
6314 void
6315 nsDocShell::DetachEditorFromWindow()
6317 if (!mEditorData || mEditorData->WaitingForLoad()) {
6318 // If there's nothing to detach, or if the editor data is actually set
6319 // up for the _new_ page that's coming in, don't detach.
6320 return;
6323 NS_ASSERTION(!mOSHE || !mOSHE->HasDetachedEditor(),
6324 "Detaching editor when it's already detached.");
6326 nsresult res = mEditorData->DetachFromWindow();
6327 NS_ASSERTION(NS_SUCCEEDED(res), "Failed to detach editor");
6329 if (NS_SUCCEEDED(res)) {
6330 // Make mOSHE hold the owning ref to the editor data.
6331 if (mOSHE)
6332 mOSHE->SetEditorData(mEditorData.forget());
6333 else
6334 mEditorData = nsnull;
6337 #ifdef DEBUG
6339 PRBool isEditable;
6340 GetEditable(&isEditable);
6341 NS_ASSERTION(!isEditable,
6342 "Window is still editable after detaching editor.");
6344 #endif // DEBUG
6347 nsresult
6348 nsDocShell::CaptureState()
6350 if (!mOSHE || mOSHE == mLSHE) {
6351 // No entry to save into, or we're replacing the existing entry.
6352 return NS_ERROR_FAILURE;
6355 nsCOMPtr<nsPIDOMWindow> privWin = do_QueryInterface(mScriptGlobal);
6356 if (!privWin)
6357 return NS_ERROR_FAILURE;
6359 nsCOMPtr<nsISupports> windowState;
6360 nsresult rv = privWin->SaveWindowState(getter_AddRefs(windowState));
6361 NS_ENSURE_SUCCESS(rv, rv);
6363 #ifdef DEBUG_PAGE_CACHE
6364 nsCOMPtr<nsIURI> uri;
6365 mOSHE->GetURI(getter_AddRefs(uri));
6366 nsCAutoString spec;
6367 if (uri)
6368 uri->GetSpec(spec);
6369 printf("Saving presentation into session history\n");
6370 printf(" SH URI: %s\n", spec.get());
6371 #endif
6373 rv = mOSHE->SetWindowState(windowState);
6374 NS_ENSURE_SUCCESS(rv, rv);
6376 // Suspend refresh URIs and save off the timer queue
6377 rv = mOSHE->SetRefreshURIList(mSavedRefreshURIList);
6378 NS_ENSURE_SUCCESS(rv, rv);
6380 // Capture the current content viewer bounds.
6381 if (mContentViewer) {
6382 nsIntRect bounds;
6383 mContentViewer->GetBounds(bounds);
6384 rv = mOSHE->SetViewerBounds(bounds);
6385 NS_ENSURE_SUCCESS(rv, rv);
6388 // Capture the docshell hierarchy.
6389 mOSHE->ClearChildShells();
6391 PRInt32 childCount = mChildList.Count();
6392 for (PRInt32 i = 0; i < childCount; ++i) {
6393 nsCOMPtr<nsIDocShellTreeItem> childShell = do_QueryInterface(ChildAt(i));
6394 NS_ASSERTION(childShell, "null child shell");
6396 mOSHE->AddChildShell(childShell);
6399 return NS_OK;
6402 NS_IMETHODIMP
6403 nsDocShell::RestorePresentationEvent::Run()
6405 if (mDocShell && NS_FAILED(mDocShell->RestoreFromHistory()))
6406 NS_WARNING("RestoreFromHistory failed");
6407 return NS_OK;
6410 NS_IMETHODIMP
6411 nsDocShell::BeginRestore(nsIContentViewer *aContentViewer, PRBool aTop)
6413 nsresult rv;
6414 if (!aContentViewer) {
6415 rv = EnsureContentViewer();
6416 NS_ENSURE_SUCCESS(rv, rv);
6418 aContentViewer = mContentViewer;
6421 // Dispatch events for restoring the presentation. We try to simulate
6422 // the progress notifications loading the document would cause, so we add
6423 // the document's channel to the loadgroup to initiate stateChange
6424 // notifications.
6426 nsCOMPtr<nsIDOMDocument> domDoc;
6427 aContentViewer->GetDOMDocument(getter_AddRefs(domDoc));
6428 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
6429 if (doc) {
6430 nsIChannel *channel = doc->GetChannel();
6431 if (channel) {
6432 mEODForCurrentDocument = PR_FALSE;
6433 mIsRestoringDocument = PR_TRUE;
6434 mLoadGroup->AddRequest(channel, nsnull);
6435 mIsRestoringDocument = PR_FALSE;
6439 if (!aTop) {
6440 // This point corresponds to us having gotten OnStartRequest or
6441 // STATE_START, so do the same thing that CreateContentViewer does at
6442 // this point to ensure that unload/pagehide events for this document
6443 // will fire when it's unloaded again.
6444 mFiredUnloadEvent = PR_FALSE;
6446 // For non-top frames, there is no notion of making sure that the
6447 // previous document is in the domwindow when STATE_START notifications
6448 // happen. We can just call BeginRestore for all of the child shells
6449 // now.
6450 rv = BeginRestoreChildren();
6451 NS_ENSURE_SUCCESS(rv, rv);
6454 return NS_OK;
6457 nsresult
6458 nsDocShell::BeginRestoreChildren()
6460 PRInt32 n = mChildList.Count();
6461 for (PRInt32 i = 0; i < n; ++i) {
6462 nsCOMPtr<nsIDocShell> child = do_QueryInterface(ChildAt(i));
6463 if (child) {
6464 nsresult rv = child->BeginRestore(nsnull, PR_FALSE);
6465 NS_ENSURE_SUCCESS(rv, rv);
6468 return NS_OK;
6471 NS_IMETHODIMP
6472 nsDocShell::FinishRestore()
6474 // First we call finishRestore() on our children. In the simulated load,
6475 // all of the child frames finish loading before the main document.
6477 PRInt32 n = mChildList.Count();
6478 for (PRInt32 i = 0; i < n; ++i) {
6479 nsCOMPtr<nsIDocShell> child = do_QueryInterface(ChildAt(i));
6480 if (child) {
6481 child->FinishRestore();
6485 if (mOSHE && mOSHE->HasDetachedEditor()) {
6486 ReattachEditorToWindow(mOSHE);
6489 if (mContentViewer) {
6490 nsCOMPtr<nsIDOMDocument> domDoc;
6491 mContentViewer->GetDOMDocument(getter_AddRefs(domDoc));
6493 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
6494 if (doc) {
6495 // Finally, we remove the request from the loadgroup. This will
6496 // cause onStateChange(STATE_STOP) to fire, which will fire the
6497 // pageshow event to the chrome.
6499 nsIChannel *channel = doc->GetChannel();
6500 if (channel) {
6501 mIsRestoringDocument = PR_TRUE;
6502 mLoadGroup->RemoveRequest(channel, nsnull, NS_OK);
6503 mIsRestoringDocument = PR_FALSE;
6508 return NS_OK;
6511 NS_IMETHODIMP
6512 nsDocShell::GetRestoringDocument(PRBool *aRestoring)
6514 *aRestoring = mIsRestoringDocument;
6515 return NS_OK;
6518 nsresult
6519 nsDocShell::RestorePresentation(nsISHEntry *aSHEntry, PRBool *aRestoring)
6521 NS_ASSERTION(mLoadType & LOAD_CMD_HISTORY,
6522 "RestorePresentation should only be called for history loads");
6524 nsCOMPtr<nsIContentViewer> viewer;
6525 aSHEntry->GetContentViewer(getter_AddRefs(viewer));
6527 #ifdef DEBUG_PAGE_CACHE
6528 nsCOMPtr<nsIURI> uri;
6529 aSHEntry->GetURI(getter_AddRefs(uri));
6531 nsCAutoString spec;
6532 if (uri)
6533 uri->GetSpec(spec);
6534 #endif
6536 *aRestoring = PR_FALSE;
6538 if (!viewer) {
6539 #ifdef DEBUG_PAGE_CACHE
6540 printf("no saved presentation for uri: %s\n", spec.get());
6541 #endif
6542 return NS_OK;
6545 // We need to make sure the content viewer's container is this docshell.
6546 // In subframe navigation, it's possible for the docshell that the
6547 // content viewer was originally loaded into to be replaced with a
6548 // different one. We don't currently support restoring the presentation
6549 // in that case.
6551 nsCOMPtr<nsISupports> container;
6552 viewer->GetContainer(getter_AddRefs(container));
6553 if (!::SameCOMIdentity(container, GetAsSupports(this))) {
6554 #ifdef DEBUG_PAGE_CACHE
6555 printf("No valid container, clearing presentation\n");
6556 #endif
6557 aSHEntry->SetContentViewer(nsnull);
6558 return NS_ERROR_FAILURE;
6561 NS_ASSERTION(mContentViewer != viewer, "Restoring existing presentation");
6563 #ifdef DEBUG_PAGE_CACHE
6564 printf("restoring presentation from session history: %s\n", spec.get());
6565 #endif
6567 SetHistoryEntry(&mLSHE, aSHEntry);
6569 // Add the request to our load group. We do this before swapping out
6570 // the content viewers so that consumers of STATE_START can access
6571 // the old document. We only deal with the toplevel load at this time --
6572 // to be consistent with normal document loading, subframes cannot start
6573 // loading until after data arrives, which is after STATE_START completes.
6575 BeginRestore(viewer, PR_TRUE);
6577 // Post an event that will remove the request after we've returned
6578 // to the event loop. This mimics the way it is called by nsIChannel
6579 // implementations.
6581 // Revoke any pending restore (just in case)
6582 NS_ASSERTION(!mRestorePresentationEvent.IsPending(),
6583 "should only have one RestorePresentationEvent");
6584 mRestorePresentationEvent.Revoke();
6586 nsRefPtr<RestorePresentationEvent> evt = new RestorePresentationEvent(this);
6587 nsresult rv = NS_DispatchToCurrentThread(evt);
6588 if (NS_SUCCEEDED(rv)) {
6589 mRestorePresentationEvent = evt.get();
6590 // The rest of the restore processing will happen on our event
6591 // callback.
6592 *aRestoring = PR_TRUE;
6595 return rv;
6598 nsresult
6599 nsDocShell::RestoreFromHistory()
6601 mRestorePresentationEvent.Forget();
6603 // This section of code follows the same ordering as CreateContentViewer.
6604 if (!mLSHE)
6605 return NS_ERROR_FAILURE;
6607 nsCOMPtr<nsIContentViewer> viewer;
6608 mLSHE->GetContentViewer(getter_AddRefs(viewer));
6609 if (!viewer)
6610 return NS_ERROR_FAILURE;
6612 if (mSavingOldViewer) {
6613 // We determined that it was safe to cache the document presentation
6614 // at the time we initiated the new load. We need to check whether
6615 // it's still safe to do so, since there may have been DOM mutations
6616 // or new requests initiated.
6617 nsCOMPtr<nsIDOMDocument> domDoc;
6618 viewer->GetDOMDocument(getter_AddRefs(domDoc));
6619 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
6620 nsIRequest *request = nsnull;
6621 if (doc)
6622 request = doc->GetChannel();
6623 mSavingOldViewer = CanSavePresentation(mLoadType, request, doc);
6626 nsCOMPtr<nsIMarkupDocumentViewer> oldMUDV(do_QueryInterface(mContentViewer));
6627 nsCOMPtr<nsIMarkupDocumentViewer> newMUDV(do_QueryInterface(viewer));
6628 float textZoom = 1.0f;
6629 float pageZoom = 1.0f;
6630 if (oldMUDV && newMUDV) {
6631 oldMUDV->GetTextZoom(&textZoom);
6632 oldMUDV->GetFullZoom(&pageZoom);
6635 // Protect against mLSHE going away via a load triggered from
6636 // pagehide or unload.
6637 nsCOMPtr<nsISHEntry> origLSHE = mLSHE;
6639 // Make sure to blow away our mLoadingURI just in case. No loads
6640 // from inside this pagehide.
6641 mLoadingURI = nsnull;
6643 // Notify the old content viewer that it's being hidden.
6644 FirePageHideNotification(!mSavingOldViewer);
6646 // If mLSHE was changed as a result of the pagehide event, then
6647 // something else was loaded. Don't finish restoring.
6648 if (mLSHE != origLSHE)
6649 return NS_OK;
6651 // Set mFiredUnloadEvent = PR_FALSE so that the unload handler for the
6652 // *new* document will fire.
6653 mFiredUnloadEvent = PR_FALSE;
6655 mURIResultedInDocument = PR_TRUE;
6656 nsCOMPtr<nsISHistory> rootSH;
6657 GetRootSessionHistory(getter_AddRefs(rootSH));
6658 if (rootSH) {
6659 nsCOMPtr<nsISHistoryInternal> hist = do_QueryInterface(rootSH);
6660 rootSH->GetIndex(&mPreviousTransIndex);
6661 hist->UpdateIndex();
6662 rootSH->GetIndex(&mLoadedTransIndex);
6663 #ifdef DEBUG_PAGE_CACHE
6664 printf("Previous index: %d, Loaded index: %d\n\n", mPreviousTransIndex,
6665 mLoadedTransIndex);
6666 #endif
6669 // Rather than call Embed(), we will retrieve the viewer from the session
6670 // history entry and swap it in.
6671 // XXX can we refactor this so that we can just call Embed()?
6672 PersistLayoutHistoryState();
6673 nsresult rv;
6674 if (mContentViewer) {
6675 if (mSavingOldViewer && NS_FAILED(CaptureState())) {
6676 if (mOSHE) {
6677 mOSHE->SyncPresentationState();
6679 mSavingOldViewer = PR_FALSE;
6683 mSavedRefreshURIList = nsnull;
6685 // In cases where we use a transient about:blank viewer between loads,
6686 // we never show the transient viewer, so _its_ previous viewer is never
6687 // unhooked from the view hierarchy. Destroy any such previous viewer now,
6688 // before we grab the root view sibling, so that we don't grab a view
6689 // that's about to go away.
6691 if (mContentViewer) {
6692 nsCOMPtr<nsIContentViewer> previousViewer;
6693 mContentViewer->GetPreviousViewer(getter_AddRefs(previousViewer));
6694 if (previousViewer) {
6695 mContentViewer->SetPreviousViewer(nsnull);
6696 previousViewer->Destroy();
6700 // Save off the root view's parent and sibling so that we can insert the
6701 // new content viewer's root view at the same position. Also save the
6702 // bounds of the root view's widget.
6704 nsIView *rootViewSibling = nsnull, *rootViewParent = nsnull;
6705 nsIntRect newBounds(0, 0, 0, 0);
6707 nsCOMPtr<nsIPresShell> oldPresShell;
6708 nsDocShell::GetPresShell(getter_AddRefs(oldPresShell));
6709 if (oldPresShell) {
6710 nsIViewManager *vm = oldPresShell->GetViewManager();
6711 if (vm) {
6712 nsIView *oldRootView = nsnull;
6713 vm->GetRootView(oldRootView);
6715 if (oldRootView) {
6716 rootViewSibling = oldRootView->GetNextSibling();
6717 rootViewParent = oldRootView->GetParent();
6719 mContentViewer->GetBounds(newBounds);
6724 // Transfer ownership to mContentViewer. By ensuring that either the
6725 // docshell or the session history, but not both, have references to the
6726 // content viewer, we prevent the viewer from being torn down after
6727 // Destroy() is called.
6729 if (mContentViewer) {
6730 mContentViewer->Close(mSavingOldViewer ? mOSHE.get() : nsnull);
6731 viewer->SetPreviousViewer(mContentViewer);
6734 // Order the mContentViewer setup just like Embed does.
6735 mContentViewer = nsnull;
6737 // Now that we're about to switch documents, forget all of our children.
6738 // Note that we cached them as needed up in CaptureState above.
6739 DestroyChildren();
6741 mContentViewer.swap(viewer);
6743 // Grab all of the related presentation from the SHEntry now.
6744 // Clearing the viewer from the SHEntry will clear all of this state.
6745 nsCOMPtr<nsISupports> windowState;
6746 mLSHE->GetWindowState(getter_AddRefs(windowState));
6747 mLSHE->SetWindowState(nsnull);
6749 PRBool sticky;
6750 mLSHE->GetSticky(&sticky);
6752 nsCOMPtr<nsIDOMDocument> domDoc;
6753 mContentViewer->GetDOMDocument(getter_AddRefs(domDoc));
6755 nsCOMArray<nsIDocShellTreeItem> childShells;
6756 PRInt32 i = 0;
6757 nsCOMPtr<nsIDocShellTreeItem> child;
6758 while (NS_SUCCEEDED(mLSHE->ChildShellAt(i++, getter_AddRefs(child))) &&
6759 child) {
6760 childShells.AppendObject(child);
6763 // get the previous content viewer size
6764 nsIntRect oldBounds(0, 0, 0, 0);
6765 mLSHE->GetViewerBounds(oldBounds);
6767 // Restore the refresh URI list. The refresh timers will be restarted
6768 // when EndPageLoad() is called.
6769 nsCOMPtr<nsISupportsArray> refreshURIList;
6770 mLSHE->GetRefreshURIList(getter_AddRefs(refreshURIList));
6772 // Reattach to the window object.
6773 rv = mContentViewer->Open(windowState, mLSHE);
6775 // Now remove it from the cached presentation.
6776 mLSHE->SetContentViewer(nsnull);
6777 mEODForCurrentDocument = PR_FALSE;
6779 #ifdef DEBUG
6781 nsCOMPtr<nsISupportsArray> refreshURIs;
6782 mLSHE->GetRefreshURIList(getter_AddRefs(refreshURIs));
6783 nsCOMPtr<nsIDocShellTreeItem> childShell;
6784 mLSHE->ChildShellAt(0, getter_AddRefs(childShell));
6785 NS_ASSERTION(!refreshURIs && !childShell,
6786 "SHEntry should have cleared presentation state");
6788 #endif
6790 // Restore the sticky state of the viewer. The viewer has set this state
6791 // on the history entry in Destroy() just before marking itself non-sticky,
6792 // to avoid teardown of the presentation.
6793 mContentViewer->SetSticky(sticky);
6795 NS_ENSURE_SUCCESS(rv, rv);
6797 // mLSHE is now our currently-loaded document.
6798 SetHistoryEntry(&mOSHE, mLSHE);
6800 // XXX special wyciwyg handling in Embed()?
6802 // We aren't going to restore any items from the LayoutHistoryState,
6803 // but we don't want them to stay around in case the page is reloaded.
6804 SetLayoutHistoryState(nsnull);
6806 // This is the end of our Embed() replacement
6808 mSavingOldViewer = PR_FALSE;
6809 mEODForCurrentDocument = PR_FALSE;
6811 // Tell the event loop to favor plevents over user events, see comments
6812 // in CreateContentViewer.
6813 if (++gNumberOfDocumentsLoading == 1)
6814 FavorPerformanceHint(PR_TRUE, NS_EVENT_STARVATION_DELAY_HINT);
6817 if (oldMUDV && newMUDV) {
6818 newMUDV->SetTextZoom(textZoom);
6819 newMUDV->SetFullZoom(pageZoom);
6822 nsCOMPtr<nsIDocument> document = do_QueryInterface(domDoc);
6823 PRUint32 parentSuspendCount = 0;
6824 if (document) {
6825 nsCOMPtr<nsIDocShellTreeItem> parent;
6826 GetParent(getter_AddRefs(parent));
6827 nsCOMPtr<nsIDOMDocument> parentDoc = do_GetInterface(parent);
6828 nsCOMPtr<nsIDocument> d = do_QueryInterface(parentDoc);
6829 if (d) {
6830 if (d->EventHandlingSuppressed()) {
6831 document->SuppressEventHandling(d->EventHandlingSuppressed());
6833 nsCOMPtr<nsPIDOMWindow> parentWindow = d->GetWindow();
6834 if (parentWindow) {
6835 parentSuspendCount = parentWindow->TimeoutSuspendCount();
6839 // Use the uri from the mLSHE we had when we entered this function
6840 // (which need not match the document's URI if anchors are involved),
6841 // since that's the history entry we're loading. Note that if we use
6842 // origLSHE we don't have to worry about whether the entry in question
6843 // is still mLSHE or whether it's now mOSHE.
6844 nsCOMPtr<nsIURI> uri;
6845 origLSHE->GetURI(getter_AddRefs(uri));
6846 SetCurrentURI(uri, document->GetChannel(), PR_TRUE);
6849 // This is the end of our CreateContentViewer() replacement.
6850 // Now we simulate a load. First, we restore the state of the javascript
6851 // window object.
6852 nsCOMPtr<nsPIDOMWindow> privWin =
6853 do_GetInterface(static_cast<nsIInterfaceRequestor*>(this));
6854 NS_ASSERTION(privWin, "could not get nsPIDOMWindow interface");
6856 rv = privWin->RestoreWindowState(windowState);
6857 NS_ENSURE_SUCCESS(rv, rv);
6859 // Now, dispatch a title change event which would happen as the
6860 // <head> is parsed.
6861 document->NotifyPossibleTitleChange(PR_FALSE);
6863 // Now we simulate appending child docshells for subframes.
6864 for (i = 0; i < childShells.Count(); ++i) {
6865 nsIDocShellTreeItem *childItem = childShells.ObjectAt(i);
6866 nsCOMPtr<nsIDocShell> childShell = do_QueryInterface(childItem);
6868 // Make sure to not clobber the state of the child. Since AddChild
6869 // always clobbers it, save it off first.
6870 PRBool allowPlugins;
6871 childShell->GetAllowPlugins(&allowPlugins);
6873 PRBool allowJavascript;
6874 childShell->GetAllowJavascript(&allowJavascript);
6876 PRBool allowRedirects;
6877 childShell->GetAllowMetaRedirects(&allowRedirects);
6879 PRBool allowSubframes;
6880 childShell->GetAllowSubframes(&allowSubframes);
6882 PRBool allowImages;
6883 childShell->GetAllowImages(&allowImages);
6885 PRBool allowDNSPrefetch;
6886 childShell->GetAllowDNSPrefetch(&allowDNSPrefetch);
6888 AddChild(childItem);
6890 childShell->SetAllowPlugins(allowPlugins);
6891 childShell->SetAllowJavascript(allowJavascript);
6892 childShell->SetAllowMetaRedirects(allowRedirects);
6893 childShell->SetAllowSubframes(allowSubframes);
6894 childShell->SetAllowImages(allowImages);
6895 childShell->SetAllowDNSPrefetch(allowDNSPrefetch);
6897 rv = childShell->BeginRestore(nsnull, PR_FALSE);
6898 NS_ENSURE_SUCCESS(rv, rv);
6901 nsCOMPtr<nsIPresShell> shell;
6902 nsDocShell::GetPresShell(getter_AddRefs(shell));
6904 nsIViewManager *newVM = shell ? shell->GetViewManager() : nsnull;
6905 nsIView *newRootView = nsnull;
6906 if (newVM)
6907 newVM->GetRootView(newRootView);
6909 // Insert the new root view at the correct location in the view tree.
6910 if (rootViewParent) {
6911 nsIViewManager *parentVM = rootViewParent->GetViewManager();
6913 if (parentVM && newRootView) {
6914 // InsertChild(parent, child, sib, PR_TRUE) inserts the child after
6915 // sib in content order, which is before sib in view order. BUT
6916 // when sib is null it inserts at the end of the the document
6917 // order, i.e., first in view order. But when oldRootSibling is
6918 // null, the old root as at the end of the view list --- last in
6919 // content order --- and we want to call InsertChild(parent, child,
6920 // nsnull, PR_FALSE) in that case.
6921 parentVM->InsertChild(rootViewParent, newRootView,
6922 rootViewSibling,
6923 rootViewSibling ? PR_TRUE : PR_FALSE);
6925 NS_ASSERTION(newRootView->GetNextSibling() == rootViewSibling,
6926 "error in InsertChild");
6930 // If parent is suspended, increase suspension count.
6931 // This can't be done as early as event suppression since this
6932 // depends on docshell tree.
6933 if (parentSuspendCount) {
6934 privWin->SuspendTimeouts(parentSuspendCount, PR_FALSE);
6937 // Now that all of the child docshells have been put into place, we can
6938 // restart the timers for the window and all of the child frames.
6939 privWin->ResumeTimeouts();
6941 // Restore the refresh URI list. The refresh timers will be restarted
6942 // when EndPageLoad() is called.
6943 mRefreshURIList = refreshURIList;
6945 // Meta-refresh timers have been restarted for this shell, but not
6946 // for our children. Walk the child shells and restart their timers.
6947 PRInt32 n = mChildList.Count();
6948 for (i = 0; i < n; ++i) {
6949 nsCOMPtr<nsIDocShell> child = do_QueryInterface(ChildAt(i));
6950 if (child)
6951 child->ResumeRefreshURIs();
6954 // Make sure this presentation is the same size as the previous
6955 // presentation. If this is not the same size we showed it at last time,
6956 // then we need to resize the widget.
6958 // XXXbryner This interacts poorly with Firefox's infobar. If the old
6959 // presentation had the infobar visible, then we will resize the new
6960 // presentation to that smaller size. However, firing the locationchanged
6961 // event will hide the infobar, which will immediately resize the window
6962 // back to the larger size. A future optimization might be to restore
6963 // the presentation at the "wrong" size, then fire the locationchanged
6964 // event and check whether the docshell's new size is the same as the
6965 // cached viewer size (skipping the resize if they are equal).
6967 if (newRootView) {
6968 if (!newBounds.IsEmpty() && newBounds != oldBounds) {
6969 #ifdef DEBUG_PAGE_CACHE
6970 printf("resize widget(%d, %d, %d, %d)\n", newBounds.x,
6971 newBounds.y, newBounds.width, newBounds.height);
6972 #endif
6973 mContentViewer->SetBounds(newBounds);
6974 } else {
6975 nsIScrollableFrame *rootScrollFrame =
6976 shell->GetRootScrollFrameAsScrollableExternal();
6977 if (rootScrollFrame) {
6978 rootScrollFrame->PostScrolledAreaEventForCurrentArea();
6983 // Simulate the completion of the load.
6984 nsDocShell::FinishRestore();
6986 // Restart plugins, and paint the content.
6987 if (shell)
6988 shell->Thaw();
6990 return privWin->FireDelayedDOMEvents();
6993 NS_IMETHODIMP
6994 nsDocShell::CreateContentViewer(const char *aContentType,
6995 nsIRequest * request,
6996 nsIStreamListener ** aContentHandler)
6998 *aContentHandler = nsnull;
7000 // Can we check the content type of the current content viewer
7001 // and reuse it without destroying it and re-creating it?
7003 NS_ASSERTION(mLoadGroup, "Someone ignored return from Init()?");
7005 // Instantiate the content viewer object
7006 nsCOMPtr<nsIContentViewer> viewer;
7007 nsresult rv = NewContentViewerObj(aContentType, request, mLoadGroup,
7008 aContentHandler, getter_AddRefs(viewer));
7010 if (NS_FAILED(rv))
7011 return NS_ERROR_FAILURE;
7013 // Notify the current document that it is about to be unloaded!!
7015 // It is important to fire the unload() notification *before* any state
7016 // is changed within the DocShell - otherwise, javascript will get the
7017 // wrong information :-(
7020 if (mSavingOldViewer) {
7021 // We determined that it was safe to cache the document presentation
7022 // at the time we initiated the new load. We need to check whether
7023 // it's still safe to do so, since there may have been DOM mutations
7024 // or new requests initiated.
7025 nsCOMPtr<nsIDOMDocument> domDoc;
7026 viewer->GetDOMDocument(getter_AddRefs(domDoc));
7027 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
7028 mSavingOldViewer = CanSavePresentation(mLoadType, request, doc);
7031 NS_ASSERTION(!mLoadingURI, "Re-entering unload?");
7033 nsCOMPtr<nsIChannel> aOpenedChannel = do_QueryInterface(request);
7034 if (aOpenedChannel) {
7035 aOpenedChannel->GetURI(getter_AddRefs(mLoadingURI));
7037 FirePageHideNotification(!mSavingOldViewer);
7038 mLoadingURI = nsnull;
7040 // Set mFiredUnloadEvent = PR_FALSE so that the unload handler for the
7041 // *new* document will fire.
7042 mFiredUnloadEvent = PR_FALSE;
7044 // we've created a new document so go ahead and call
7045 // OnLoadingSite(), but don't fire OnLocationChange()
7046 // notifications before we've called Embed(). See bug 284993.
7047 mURIResultedInDocument = PR_TRUE;
7049 if (mLoadType == LOAD_ERROR_PAGE) {
7050 // We need to set the SH entry and our current URI here and not
7051 // at the moment we load the page. We want the same behavior
7052 // of Stop() as for a normal page load. See bug 514232 for details.
7054 // Revert mLoadType to load type to state the page load failed,
7055 // following function calls need it.
7056 mLoadType = mFailedLoadType;
7058 nsCOMPtr<nsIChannel> failedChannel = mFailedChannel;
7059 nsCOMPtr<nsIURI> failedURI = mFailedURI;
7060 mFailedChannel = nsnull;
7061 mFailedURI = nsnull;
7063 // Create an shistory entry for the old load, if we have a channel
7064 if (failedChannel) {
7065 mURIResultedInDocument = PR_TRUE;
7066 OnLoadingSite(failedChannel, PR_TRUE, PR_FALSE);
7067 } else if (failedURI) {
7068 mURIResultedInDocument = PR_TRUE;
7069 OnNewURI(failedURI, nsnull, nsnull, mLoadType, PR_TRUE, PR_FALSE);
7072 // Be sure to have a correct mLSHE, it may have been cleared by
7073 // EndPageLoad. See bug 302115.
7074 if (mSessionHistory && !mLSHE) {
7075 PRInt32 idx;
7076 mSessionHistory->GetRequestedIndex(&idx);
7077 if (idx == -1)
7078 mSessionHistory->GetIndex(&idx);
7080 nsCOMPtr<nsIHistoryEntry> entry;
7081 mSessionHistory->GetEntryAtIndex(idx, PR_FALSE,
7082 getter_AddRefs(entry));
7083 mLSHE = do_QueryInterface(entry);
7086 // Set our current URI
7087 SetCurrentURI(failedURI);
7089 mLoadType = LOAD_ERROR_PAGE;
7092 PRBool onLocationChangeNeeded = OnLoadingSite(aOpenedChannel, PR_FALSE);
7094 // let's try resetting the load group if we need to...
7095 nsCOMPtr<nsILoadGroup> currentLoadGroup;
7096 NS_ENSURE_SUCCESS(aOpenedChannel->
7097 GetLoadGroup(getter_AddRefs(currentLoadGroup)),
7098 NS_ERROR_FAILURE);
7100 if (currentLoadGroup != mLoadGroup) {
7101 nsLoadFlags loadFlags = 0;
7103 //Cancel any URIs that are currently loading...
7104 /// XXX: Need to do this eventually Stop();
7106 // Retarget the document to this loadgroup...
7108 /* First attach the channel to the right loadgroup
7109 * and then remove from the old loadgroup. This
7110 * puts the notifications in the right order and
7111 * we don't null-out mLSHE in OnStateChange() for
7112 * all redirected urls
7114 aOpenedChannel->SetLoadGroup(mLoadGroup);
7116 // Mark the channel as being a document URI...
7117 aOpenedChannel->GetLoadFlags(&loadFlags);
7118 loadFlags |= nsIChannel::LOAD_DOCUMENT_URI;
7120 aOpenedChannel->SetLoadFlags(loadFlags);
7122 mLoadGroup->AddRequest(request, nsnull);
7123 if (currentLoadGroup)
7124 currentLoadGroup->RemoveRequest(request, nsnull,
7125 NS_BINDING_RETARGETED);
7127 // Update the notification callbacks, so that progress and
7128 // status information are sent to the right docshell...
7129 aOpenedChannel->SetNotificationCallbacks(this);
7132 NS_ENSURE_SUCCESS(Embed(viewer, "", (nsISupports *) nsnull),
7133 NS_ERROR_FAILURE);
7135 mSavedRefreshURIList = nsnull;
7136 mSavingOldViewer = PR_FALSE;
7137 mEODForCurrentDocument = PR_FALSE;
7139 // if this document is part of a multipart document,
7140 // the ID can be used to distinguish it from the other parts.
7141 nsCOMPtr<nsIMultiPartChannel> multiPartChannel(do_QueryInterface(request));
7142 if (multiPartChannel) {
7143 nsCOMPtr<nsIPresShell> shell;
7144 rv = GetPresShell(getter_AddRefs(shell));
7145 if (NS_SUCCEEDED(rv) && shell) {
7146 nsIDocument *doc = shell->GetDocument();
7147 if (doc) {
7148 PRUint32 partID;
7149 multiPartChannel->GetPartID(&partID);
7150 doc->SetPartID(partID);
7155 // Give hint to native plevent dispatch mechanism. If a document
7156 // is loading the native plevent dispatch mechanism should favor
7157 // performance over normal native event dispatch priorities.
7158 if (++gNumberOfDocumentsLoading == 1) {
7159 // Hint to favor performance for the plevent notification mechanism.
7160 // We want the pages to load as fast as possible even if its means
7161 // native messages might be starved.
7162 FavorPerformanceHint(PR_TRUE, NS_EVENT_STARVATION_DELAY_HINT);
7165 if (onLocationChangeNeeded) {
7166 FireOnLocationChange(this, request, mCurrentURI);
7169 return NS_OK;
7172 nsresult
7173 nsDocShell::NewContentViewerObj(const char *aContentType,
7174 nsIRequest * request, nsILoadGroup * aLoadGroup,
7175 nsIStreamListener ** aContentHandler,
7176 nsIContentViewer ** aViewer)
7178 nsCOMPtr<nsIChannel> aOpenedChannel = do_QueryInterface(request);
7180 nsresult rv;
7181 nsCOMPtr<nsICategoryManager> catMan(do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv));
7182 if (NS_FAILED(rv))
7183 return rv;
7185 nsXPIDLCString contractId;
7186 rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", aContentType, getter_Copies(contractId));
7188 // Create an instance of the document-loader-factory
7189 nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory;
7190 if (NS_SUCCEEDED(rv))
7191 docLoaderFactory = do_GetService(contractId.get());
7193 if (!docLoaderFactory) {
7194 return NS_ERROR_FAILURE;
7197 // Now create an instance of the content viewer
7198 // nsLayoutDLF makes the determination if it should be a "view-source" instead of "view"
7199 NS_ENSURE_SUCCESS(docLoaderFactory->CreateInstance("view",
7200 aOpenedChannel,
7201 aLoadGroup, aContentType,
7202 static_cast<nsIContentViewerContainer*>(this),
7203 nsnull,
7204 aContentHandler,
7205 aViewer),
7206 NS_ERROR_FAILURE);
7208 (*aViewer)->SetContainer(static_cast<nsIContentViewerContainer *>(this));
7209 return NS_OK;
7212 NS_IMETHODIMP
7213 nsDocShell::SetupNewViewer(nsIContentViewer * aNewViewer)
7216 // Copy content viewer state from previous or parent content viewer.
7218 // The following logic is mirrored in nsHTMLDocument::StartDocumentLoad!
7220 // Do NOT to maintain a reference to the old content viewer outside
7221 // of this "copying" block, or it will not be destroyed until the end of
7222 // this routine and all <SCRIPT>s and event handlers fail! (bug 20315)
7224 // In this block of code, if we get an error result, we return it
7225 // but if we get a null pointer, that's perfectly legal for parent
7226 // and parentContentViewer.
7229 PRInt32 x = 0;
7230 PRInt32 y = 0;
7231 PRInt32 cx = 0;
7232 PRInt32 cy = 0;
7234 // This will get the size from the current content viewer or from the
7235 // Init settings
7236 DoGetPositionAndSize(&x, &y, &cx, &cy);
7238 nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
7239 NS_ENSURE_SUCCESS(GetSameTypeParent(getter_AddRefs(parentAsItem)),
7240 NS_ERROR_FAILURE);
7241 nsCOMPtr<nsIDocShell> parent(do_QueryInterface(parentAsItem));
7243 nsCAutoString defaultCharset;
7244 nsCAutoString forceCharset;
7245 nsCAutoString hintCharset;
7246 PRInt32 hintCharsetSource;
7247 nsCAutoString prevDocCharset;
7248 float textZoom;
7249 float pageZoom;
7250 PRBool styleDisabled;
7251 // |newMUDV| also serves as a flag to set the data from the above vars
7252 nsCOMPtr<nsIMarkupDocumentViewer> newMUDV;
7254 if (mContentViewer || parent) {
7255 nsCOMPtr<nsIMarkupDocumentViewer> oldMUDV;
7256 if (mContentViewer) {
7257 // Get any interesting state from old content viewer
7258 // XXX: it would be far better to just reuse the document viewer ,
7259 // since we know we're just displaying the same document as before
7260 oldMUDV = do_QueryInterface(mContentViewer);
7262 // Tell the old content viewer to hibernate in session history when
7263 // it is destroyed.
7265 if (mSavingOldViewer && NS_FAILED(CaptureState())) {
7266 if (mOSHE) {
7267 mOSHE->SyncPresentationState();
7269 mSavingOldViewer = PR_FALSE;
7272 else {
7273 // No old content viewer, so get state from parent's content viewer
7274 nsCOMPtr<nsIContentViewer> parentContentViewer;
7275 parent->GetContentViewer(getter_AddRefs(parentContentViewer));
7276 oldMUDV = do_QueryInterface(parentContentViewer);
7279 if (oldMUDV) {
7280 nsresult rv;
7282 newMUDV = do_QueryInterface(aNewViewer,&rv);
7283 if (newMUDV) {
7284 NS_ENSURE_SUCCESS(oldMUDV->
7285 GetDefaultCharacterSet(defaultCharset),
7286 NS_ERROR_FAILURE);
7287 NS_ENSURE_SUCCESS(oldMUDV->
7288 GetForceCharacterSet(forceCharset),
7289 NS_ERROR_FAILURE);
7290 NS_ENSURE_SUCCESS(oldMUDV->
7291 GetHintCharacterSet(hintCharset),
7292 NS_ERROR_FAILURE);
7293 NS_ENSURE_SUCCESS(oldMUDV->
7294 GetHintCharacterSetSource(&hintCharsetSource),
7295 NS_ERROR_FAILURE);
7296 NS_ENSURE_SUCCESS(oldMUDV->
7297 GetTextZoom(&textZoom),
7298 NS_ERROR_FAILURE);
7299 NS_ENSURE_SUCCESS(oldMUDV->
7300 GetFullZoom(&pageZoom),
7301 NS_ERROR_FAILURE);
7302 NS_ENSURE_SUCCESS(oldMUDV->
7303 GetAuthorStyleDisabled(&styleDisabled),
7304 NS_ERROR_FAILURE);
7305 NS_ENSURE_SUCCESS(oldMUDV->
7306 GetPrevDocCharacterSet(prevDocCharset),
7307 NS_ERROR_FAILURE);
7312 nscolor bgcolor = NS_RGBA(0, 0, 0, 0);
7313 // Ensure that the content viewer is destroyed *after* the GC - bug 71515
7314 nsCOMPtr<nsIContentViewer> kungfuDeathGrip = mContentViewer;
7315 if (mContentViewer) {
7316 // Stop any activity that may be happening in the old document before
7317 // releasing it...
7318 mContentViewer->Stop();
7320 // Try to extract the canvas background color from the old
7321 // presentation shell, so we can use it for the next document.
7322 nsCOMPtr<nsIDocumentViewer> docviewer =
7323 do_QueryInterface(mContentViewer);
7325 if (docviewer) {
7326 nsCOMPtr<nsIPresShell> shell;
7327 docviewer->GetPresShell(getter_AddRefs(shell));
7329 if (shell) {
7330 bgcolor = shell->GetCanvasBackground();
7334 mContentViewer->Close(mSavingOldViewer ? mOSHE.get() : nsnull);
7335 aNewViewer->SetPreviousViewer(mContentViewer);
7337 mContentViewer = nsnull;
7340 // Now that we're about to switch documents, forget all of our children.
7341 // Note that we cached them as needed up in CaptureState above.
7342 DestroyChildren();
7344 mContentViewer = aNewViewer;
7346 nsCOMPtr<nsIWidget> widget;
7347 NS_ENSURE_SUCCESS(GetMainWidget(getter_AddRefs(widget)), NS_ERROR_FAILURE);
7349 nsIntRect bounds(x, y, cx, cy);
7351 if (NS_FAILED(mContentViewer->Init(widget, bounds))) {
7352 mContentViewer = nsnull;
7353 NS_ERROR("ContentViewer Initialization failed");
7354 return NS_ERROR_FAILURE;
7357 // If we have old state to copy, set the old state onto the new content
7358 // viewer
7359 if (newMUDV) {
7360 NS_ENSURE_SUCCESS(newMUDV->SetDefaultCharacterSet(defaultCharset),
7361 NS_ERROR_FAILURE);
7362 NS_ENSURE_SUCCESS(newMUDV->SetForceCharacterSet(forceCharset),
7363 NS_ERROR_FAILURE);
7364 NS_ENSURE_SUCCESS(newMUDV->SetHintCharacterSet(hintCharset),
7365 NS_ERROR_FAILURE);
7366 NS_ENSURE_SUCCESS(newMUDV->
7367 SetHintCharacterSetSource(hintCharsetSource),
7368 NS_ERROR_FAILURE);
7369 NS_ENSURE_SUCCESS(newMUDV->SetPrevDocCharacterSet(prevDocCharset),
7370 NS_ERROR_FAILURE);
7371 NS_ENSURE_SUCCESS(newMUDV->SetTextZoom(textZoom),
7372 NS_ERROR_FAILURE);
7373 NS_ENSURE_SUCCESS(newMUDV->SetFullZoom(pageZoom),
7374 NS_ERROR_FAILURE);
7375 NS_ENSURE_SUCCESS(newMUDV->SetAuthorStyleDisabled(styleDisabled),
7376 NS_ERROR_FAILURE);
7379 // Stuff the bgcolor from the old pres shell into the new
7380 // pres shell. This improves page load continuity.
7381 nsCOMPtr<nsIDocumentViewer> docviewer =
7382 do_QueryInterface(mContentViewer);
7384 if (docviewer) {
7385 nsCOMPtr<nsIPresShell> shell;
7386 docviewer->GetPresShell(getter_AddRefs(shell));
7388 if (shell) {
7389 shell->SetCanvasBackground(bgcolor);
7393 // XXX: It looks like the LayoutState gets restored again in Embed()
7394 // right after the call to SetupNewViewer(...)
7396 // We don't show the mContentViewer yet, since we want to draw the old page
7397 // until we have enough of the new page to show. Just return with the new
7398 // viewer still set to hidden.
7400 return NS_OK;
7403 nsresult
7404 nsDocShell::SetDocPendingStateObj(nsISHEntry *shEntry)
7406 nsresult rv;
7408 nsCOMPtr<nsIDocument> document = do_GetInterface(GetAsSupports(this));
7409 NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
7411 nsAutoString stateData;
7412 if (shEntry) {
7413 rv = shEntry->GetStateData(stateData);
7414 NS_ENSURE_SUCCESS(rv, rv);
7416 // if shEntry is null, we just set the pending state object to the
7417 // empty string.
7420 document->SetPendingStateObject(stateData);
7421 return NS_OK;
7424 nsresult
7425 nsDocShell::CheckLoadingPermissions()
7427 // This method checks whether the caller may load content into
7428 // this docshell. Even though we've done our best to hide windows
7429 // from code that doesn't have the right to access them, it's
7430 // still possible for an evil site to open a window and access
7431 // frames in the new window through window.frames[] (which is
7432 // allAccess for historic reasons), so we still need to do this
7433 // check on load.
7434 nsresult rv = NS_OK, sameOrigin = NS_OK;
7436 if (!gValidateOrigin || !IsFrame()) {
7437 // Origin validation was turned off, or we're not a frame.
7438 // Permit all loads.
7440 return rv;
7443 // We're a frame. Check that the caller has write permission to
7444 // the parent before allowing it to load anything into this
7445 // docshell.
7447 nsCOMPtr<nsIScriptSecurityManager> securityManager =
7448 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
7449 NS_ENSURE_SUCCESS(rv, rv);
7451 PRBool ubwEnabled = PR_FALSE;
7452 rv = securityManager->IsCapabilityEnabled("UniversalBrowserWrite",
7453 &ubwEnabled);
7454 if (NS_FAILED(rv) || ubwEnabled) {
7455 return rv;
7458 nsCOMPtr<nsIPrincipal> subjPrincipal;
7459 rv = securityManager->GetSubjectPrincipal(getter_AddRefs(subjPrincipal));
7460 NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && subjPrincipal, rv);
7462 // Check if the caller is from the same origin as this docshell,
7463 // or any of its ancestors.
7464 nsCOMPtr<nsIDocShellTreeItem> item(this);
7465 do {
7466 nsCOMPtr<nsIScriptGlobalObject> sgo(do_GetInterface(item));
7467 nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(sgo));
7469 nsIPrincipal *p;
7470 if (!sop || !(p = sop->GetPrincipal())) {
7471 return NS_ERROR_UNEXPECTED;
7474 // Compare origins
7475 PRBool equal;
7476 sameOrigin = subjPrincipal->Equals(p, &equal);
7477 if (NS_SUCCEEDED(sameOrigin)) {
7478 if (equal) {
7479 // Same origin, permit load
7481 return sameOrigin;
7484 sameOrigin = NS_ERROR_DOM_PROP_ACCESS_DENIED;
7487 nsCOMPtr<nsIDocShellTreeItem> tmp;
7488 item->GetSameTypeParent(getter_AddRefs(tmp));
7489 item.swap(tmp);
7490 } while (item);
7492 return sameOrigin;
7495 //*****************************************************************************
7496 // nsDocShell: Site Loading
7497 //*****************************************************************************
7498 class InternalLoadEvent : public nsRunnable
7500 public:
7501 InternalLoadEvent(nsDocShell* aDocShell, nsIURI * aURI, nsIURI * aReferrer,
7502 nsISupports * aOwner, PRUint32 aFlags,
7503 const char* aTypeHint, nsIInputStream * aPostData,
7504 nsIInputStream * aHeadersData, PRUint32 aLoadType,
7505 nsISHEntry * aSHEntry, PRBool aFirstParty) :
7506 mDocShell(aDocShell),
7507 mURI(aURI),
7508 mReferrer(aReferrer),
7509 mOwner(aOwner),
7510 mPostData(aPostData),
7511 mHeadersData(aHeadersData),
7512 mSHEntry(aSHEntry),
7513 mFlags(aFlags),
7514 mLoadType(aLoadType),
7515 mFirstParty(aFirstParty)
7517 // Make sure to keep null things null as needed
7518 if (aTypeHint) {
7519 mTypeHint = aTypeHint;
7523 NS_IMETHOD Run() {
7524 return mDocShell->InternalLoad(mURI, mReferrer, mOwner, mFlags,
7525 nsnull, mTypeHint.get(),
7526 mPostData, mHeadersData, mLoadType,
7527 mSHEntry, mFirstParty, nsnull, nsnull);
7530 private:
7532 // Use IDL strings so .get() returns null by default
7533 nsXPIDLString mWindowTarget;
7534 nsXPIDLCString mTypeHint;
7536 nsRefPtr<nsDocShell> mDocShell;
7537 nsCOMPtr<nsIURI> mURI;
7538 nsCOMPtr<nsIURI> mReferrer;
7539 nsCOMPtr<nsISupports> mOwner;
7540 nsCOMPtr<nsIInputStream> mPostData;
7541 nsCOMPtr<nsIInputStream> mHeadersData;
7542 nsCOMPtr<nsISHEntry> mSHEntry;
7543 PRUint32 mFlags;
7544 PRUint32 mLoadType;
7545 PRBool mFirstParty;
7548 NS_IMETHODIMP
7549 nsDocShell::InternalLoad(nsIURI * aURI,
7550 nsIURI * aReferrer,
7551 nsISupports * aOwner,
7552 PRUint32 aFlags,
7553 const PRUnichar *aWindowTarget,
7554 const char* aTypeHint,
7555 nsIInputStream * aPostData,
7556 nsIInputStream * aHeadersData,
7557 PRUint32 aLoadType,
7558 nsISHEntry * aSHEntry,
7559 PRBool aFirstParty,
7560 nsIDocShell** aDocShell,
7561 nsIRequest** aRequest)
7563 nsresult rv = NS_OK;
7565 #ifdef PR_LOGGING
7566 if (gDocShellLeakLog && PR_LOG_TEST(gDocShellLeakLog, PR_LOG_DEBUG)) {
7567 nsCAutoString spec;
7568 if (aURI)
7569 aURI->GetSpec(spec);
7570 PR_LogPrint("DOCSHELL %p InternalLoad %s\n", this, spec.get());
7572 #endif
7574 // Initialize aDocShell/aRequest
7575 if (aDocShell) {
7576 *aDocShell = nsnull;
7578 if (aRequest) {
7579 *aRequest = nsnull;
7582 if (!aURI) {
7583 return NS_ERROR_NULL_POINTER;
7586 NS_ENSURE_TRUE(IsValidLoadType(aLoadType), NS_ERROR_INVALID_ARG);
7588 NS_ENSURE_TRUE(!mIsBeingDestroyed, NS_ERROR_NOT_AVAILABLE);
7590 // wyciwyg urls can only be loaded through history. Any normal load of
7591 // wyciwyg through docshell is illegal. Disallow such loads.
7592 if (aLoadType & LOAD_CMD_NORMAL) {
7593 PRBool isWyciwyg = PR_FALSE;
7594 rv = aURI->SchemeIs("wyciwyg", &isWyciwyg);
7595 if ((isWyciwyg && NS_SUCCEEDED(rv)) || NS_FAILED(rv))
7596 return NS_ERROR_FAILURE;
7599 PRBool bIsJavascript = PR_FALSE;
7600 if (NS_FAILED(aURI->SchemeIs("javascript", &bIsJavascript))) {
7601 bIsJavascript = PR_FALSE;
7605 // First, notify any nsIContentPolicy listeners about the document load.
7606 // Only abort the load if a content policy listener explicitly vetos it!
7608 nsCOMPtr<nsIDOMElement> requestingElement;
7609 // Use nsPIDOMWindow since we _want_ to cross the chrome boundary if needed
7610 nsCOMPtr<nsPIDOMWindow> privateWin(do_QueryInterface(mScriptGlobal));
7611 if (privateWin)
7612 requestingElement = privateWin->GetFrameElementInternal();
7614 PRInt16 shouldLoad = nsIContentPolicy::ACCEPT;
7615 PRUint32 contentType;
7616 if (IsFrame()) {
7617 NS_ASSERTION(requestingElement, "A frame but no DOM element!?");
7618 contentType = nsIContentPolicy::TYPE_SUBDOCUMENT;
7619 } else {
7620 contentType = nsIContentPolicy::TYPE_DOCUMENT;
7623 nsISupports* context = requestingElement;
7624 if (!context) {
7625 context = mScriptGlobal;
7628 // XXXbz would be nice to know the loading principal here... but we don't
7629 nsCOMPtr<nsIPrincipal> loadingPrincipal;
7630 if (aReferrer) {
7631 nsCOMPtr<nsIScriptSecurityManager> secMan =
7632 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
7633 NS_ENSURE_SUCCESS(rv, rv);
7635 rv = secMan->GetCodebasePrincipal(aReferrer,
7636 getter_AddRefs(loadingPrincipal));
7639 rv = NS_CheckContentLoadPolicy(contentType,
7640 aURI,
7641 loadingPrincipal,
7642 context,
7643 EmptyCString(), //mime guess
7644 nsnull, //extra
7645 &shouldLoad);
7647 if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
7648 if (NS_SUCCEEDED(rv) && shouldLoad == nsIContentPolicy::REJECT_TYPE) {
7649 return NS_ERROR_CONTENT_BLOCKED_SHOW_ALT;
7652 return NS_ERROR_CONTENT_BLOCKED;
7655 nsCOMPtr<nsISupports> owner(aOwner);
7657 // Get an owner from the current document if necessary. Note that we only
7658 // do this for URIs that inherit a security context and local file URIs;
7659 // in particular we do NOT do this for about:blank. This way, random
7660 // about:blank loads that have no owner (which basically means they were
7661 // done by someone from chrome manually messing with our nsIWebNavigation
7662 // or by C++ setting document.location) don't get a funky principal. If
7663 // callers want something interesting to happen with the about:blank
7664 // principal in this case, they should pass an owner in.
7667 PRBool inherits;
7668 // One more twist: Don't inherit the owner for external loads.
7669 if (aLoadType != LOAD_NORMAL_EXTERNAL && !owner &&
7670 (aFlags & INTERNAL_LOAD_FLAGS_INHERIT_OWNER) &&
7671 NS_SUCCEEDED(URIInheritsSecurityContext(aURI, &inherits)) &&
7672 inherits) {
7674 // Don't allow loads that would inherit our security context
7675 // if this document came from an unsafe channel.
7676 nsCOMPtr<nsIDocShellTreeItem> treeItem = this;
7677 do {
7678 nsCOMPtr<nsIDocShell> itemDocShell =
7679 do_QueryInterface(treeItem);
7680 PRBool isUnsafe;
7681 if (itemDocShell &&
7682 NS_SUCCEEDED(itemDocShell->GetChannelIsUnsafe(&isUnsafe)) &&
7683 isUnsafe) {
7684 return NS_ERROR_DOM_SECURITY_ERR;
7687 nsCOMPtr<nsIDocShellTreeItem> parent;
7688 treeItem->GetSameTypeParent(getter_AddRefs(parent));
7689 parent.swap(treeItem);
7690 } while (treeItem);
7692 owner = GetInheritedPrincipal(PR_TRUE);
7697 // Resolve the window target before going any further...
7698 // If the load has been targeted to another DocShell, then transfer the
7699 // load to it...
7701 if (aWindowTarget && *aWindowTarget) {
7702 // We've already done our owner-inheriting. Mask out that bit, so we
7703 // don't try inheriting an owner from the target window if we came up
7704 // with a null owner above.
7705 aFlags = aFlags & ~INTERNAL_LOAD_FLAGS_INHERIT_OWNER;
7707 // Locate the target DocShell.
7708 // This may involve creating a new toplevel window - if necessary.
7710 nsCOMPtr<nsIDocShellTreeItem> targetItem;
7711 FindItemWithName(aWindowTarget, nsnull, this,
7712 getter_AddRefs(targetItem));
7714 nsCOMPtr<nsIDocShell> targetDocShell = do_QueryInterface(targetItem);
7716 PRBool isNewWindow = PR_FALSE;
7717 if (!targetDocShell) {
7718 nsCOMPtr<nsIDOMWindowInternal> win =
7719 do_GetInterface(GetAsSupports(this));
7720 NS_ENSURE_TRUE(win, NS_ERROR_NOT_AVAILABLE);
7722 nsDependentString name(aWindowTarget);
7723 nsCOMPtr<nsIDOMWindow> newWin;
7724 rv = win->Open(EmptyString(), // URL to load
7725 name, // window name
7726 EmptyString(), // Features
7727 getter_AddRefs(newWin));
7729 // In some cases the Open call doesn't actually result in a new
7730 // window being opened. We can detect these cases by examining the
7731 // document in |newWin|, if any.
7732 nsCOMPtr<nsPIDOMWindow> piNewWin = do_QueryInterface(newWin);
7733 if (piNewWin) {
7734 nsCOMPtr<nsIDocument> newDoc =
7735 do_QueryInterface(piNewWin->GetExtantDocument());
7736 if (!newDoc || newDoc->IsInitialDocument()) {
7737 isNewWindow = PR_TRUE;
7738 aFlags |= INTERNAL_LOAD_FLAGS_FIRST_LOAD;
7742 nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(newWin);
7743 targetDocShell = do_QueryInterface(webNav);
7747 // Transfer the load to the target DocShell... Pass nsnull as the
7748 // window target name from to prevent recursive retargeting!
7750 if (NS_SUCCEEDED(rv) && targetDocShell) {
7751 rv = targetDocShell->InternalLoad(aURI,
7752 aReferrer,
7753 owner,
7754 aFlags,
7755 nsnull, // No window target
7756 aTypeHint,
7757 aPostData,
7758 aHeadersData,
7759 aLoadType,
7760 aSHEntry,
7761 aFirstParty,
7762 aDocShell,
7763 aRequest);
7764 if (rv == NS_ERROR_NO_CONTENT) {
7765 // XXXbz except we never reach this code!
7766 if (isNewWindow) {
7768 // At this point, a new window has been created, but the
7769 // URI did not have any data associated with it...
7771 // So, the best we can do, is to tear down the new window
7772 // that was just created!
7774 nsCOMPtr<nsIDOMWindowInternal> domWin =
7775 do_GetInterface(targetDocShell);
7776 if (domWin) {
7777 domWin->Close();
7781 // NS_ERROR_NO_CONTENT should not be returned to the
7782 // caller... This is an internal error code indicating that
7783 // the URI had no data associated with it - probably a
7784 // helper-app style protocol (ie. mailto://)
7786 rv = NS_OK;
7788 else if (isNewWindow) {
7789 // XXX: Once new windows are created hidden, the new
7790 // window will need to be made visible... For now,
7791 // do nothing.
7795 // Else we ran out of memory, or were a popup and got blocked,
7796 // or something.
7798 return rv;
7802 // Load is being targetted at this docshell so return an error if the
7803 // docshell is in the process of being destroyed.
7805 if (mIsBeingDestroyed) {
7806 return NS_ERROR_FAILURE;
7809 rv = CheckLoadingPermissions();
7810 if (NS_FAILED(rv)) {
7811 return rv;
7814 // If this docshell is owned by a frameloader, make sure to cancel
7815 // possible frameloader initialization before loading a new page.
7816 nsCOMPtr<nsIDocShellTreeItem> parent;
7817 GetParent(getter_AddRefs(parent));
7818 if (parent) {
7819 nsCOMPtr<nsIDOMDocument> domDoc = do_GetInterface(parent);
7820 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
7821 if (doc) {
7822 doc->TryCancelFrameLoaderInitialization(this);
7826 if (mFiredUnloadEvent) {
7827 if (IsOKToLoadURI(aURI)) {
7828 NS_PRECONDITION(!aWindowTarget || !*aWindowTarget,
7829 "Shouldn't have a window target here!");
7831 // If this is a replace load, make whatever load triggered
7832 // the unload event also a replace load, so we don't
7833 // create extra history entries.
7834 if (LOAD_TYPE_HAS_FLAGS(aLoadType, LOAD_FLAGS_REPLACE_HISTORY)) {
7835 mLoadType = LOAD_NORMAL_REPLACE;
7838 // Do this asynchronously
7839 nsCOMPtr<nsIRunnable> ev =
7840 new InternalLoadEvent(this, aURI, aReferrer, aOwner, aFlags,
7841 aTypeHint, aPostData, aHeadersData,
7842 aLoadType, aSHEntry, aFirstParty);
7843 return NS_DispatchToCurrentThread(ev);
7846 // Just ignore this load attempt
7847 return NS_OK;
7850 // Before going any further vet loads initiated by external programs.
7851 if (aLoadType == LOAD_NORMAL_EXTERNAL) {
7852 // Disallow external chrome: loads targetted at content windows
7853 PRBool isChrome = PR_FALSE;
7854 if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &isChrome)) && isChrome) {
7855 NS_WARNING("blocked external chrome: url -- use '-chrome' option");
7856 return NS_ERROR_FAILURE;
7859 // clear the decks to prevent context bleed-through (bug 298255)
7860 rv = CreateAboutBlankContentViewer(nsnull, nsnull);
7861 if (NS_FAILED(rv))
7862 return NS_ERROR_FAILURE;
7864 // reset loadType so we don't have to add lots of tests for
7865 // LOAD_NORMAL_EXTERNAL after this point
7866 aLoadType = LOAD_NORMAL;
7869 mAllowKeywordFixup =
7870 (aFlags & INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) != 0;
7871 mURIResultedInDocument = PR_FALSE; // reset the clock...
7874 // First:
7875 // Check to see if the new URI is an anchor in the existing document.
7876 // Skip this check if we're doing some sort of abnormal load, if the
7877 // new load is a non-history load and has postdata, or if we're doing
7878 // a history load and the page identifiers of mOSHE and aSHEntry
7879 // don't match.
7881 PRBool allowScroll = PR_TRUE;
7882 if (!aSHEntry) {
7883 allowScroll = (aPostData == nsnull);
7884 } else if (mOSHE) {
7885 PRUint32 ourPageIdent;
7886 mOSHE->GetPageIdentifier(&ourPageIdent);
7887 PRUint32 otherPageIdent;
7888 aSHEntry->GetPageIdentifier(&otherPageIdent);
7889 allowScroll = (ourPageIdent == otherPageIdent);
7890 #ifdef DEBUG
7891 if (allowScroll) {
7892 nsCOMPtr<nsIInputStream> currentPostData;
7893 mOSHE->GetPostData(getter_AddRefs(currentPostData));
7894 NS_ASSERTION(currentPostData == aPostData,
7895 "Different POST data for entries for the same page?");
7897 #endif
7900 if (aLoadType == LOAD_NORMAL ||
7901 aLoadType == LOAD_STOP_CONTENT ||
7902 LOAD_TYPE_HAS_FLAGS(aLoadType, LOAD_FLAGS_REPLACE_HISTORY) ||
7903 aLoadType == LOAD_HISTORY ||
7904 aLoadType == LOAD_LINK) {
7906 PRBool wasAnchor = PR_FALSE;
7907 PRBool doHashchange = PR_FALSE;
7908 nscoord cx = 0, cy = 0;
7910 if (allowScroll) {
7911 NS_ENSURE_SUCCESS(ScrollIfAnchor(aURI, &wasAnchor, aLoadType, &cx,
7912 &cy, &doHashchange),
7913 NS_ERROR_FAILURE);
7916 // If this is a history load, aSHEntry will have document identifier X
7917 // if it was created as a result of a History.pushState() from a
7918 // SHEntry with doc ident X, or if it was created by changing the hash
7919 // of the URI corresponding to a SHEntry with doc ident X.
7920 PRBool sameDocIdent = PR_FALSE;
7921 if (mOSHE && aSHEntry) {
7922 PRUint64 ourDocIdent, otherDocIdent;
7923 mOSHE->GetDocIdentifier(&ourDocIdent);
7924 aSHEntry->GetDocIdentifier(&otherDocIdent);
7925 sameDocIdent = (ourDocIdent == otherDocIdent);
7928 // Do a short-circuited load if the new URI differs from the current
7929 // URI only in the hash, or if the two entries belong to the same
7930 // document and don't point to the same object.
7932 // (If we didn't check that the SHEntries are different objects,
7933 // history.go(0) would short-circuit instead of triggering a true
7934 // load, and we wouldn't dispatch an onload event to the page.)
7935 if (wasAnchor || (sameDocIdent && (mOSHE != aSHEntry))) {
7936 mLoadType = aLoadType;
7937 mURIResultedInDocument = PR_TRUE;
7939 /* we need to assign mLSHE to aSHEntry right here, so that on History loads,
7940 * SetCurrentURI() called from OnNewURI() will send proper
7941 * onLocationChange() notifications to the browser to update
7942 * back/forward buttons.
7944 SetHistoryEntry(&mLSHE, aSHEntry);
7946 /* This is a anchor traversal with in the same page.
7947 * call OnNewURI() so that, this traversal will be
7948 * recorded in session and global history.
7950 nsCOMPtr<nsISupports> owner;
7951 if (mOSHE) {
7952 mOSHE->GetOwner(getter_AddRefs(owner));
7954 OnNewURI(aURI, nsnull, owner, mLoadType, PR_TRUE, PR_TRUE);
7956 nsCOMPtr<nsIInputStream> postData;
7957 PRUint32 pageIdent = PR_UINT32_MAX;
7958 nsCOMPtr<nsISupports> cacheKey;
7960 if (mOSHE) {
7961 /* save current position of scroller(s) (bug 59774) */
7962 mOSHE->SetScrollPosition(cx, cy);
7963 // Get the postdata and page ident from the current page, if
7964 // the new load is being done via normal means. Note that
7965 // "normal means" can be checked for just by checking for
7966 // LOAD_CMD_NORMAL, given the loadType and allowScroll check
7967 // above -- it filters out some LOAD_CMD_NORMAL cases that we
7968 // wouldn't want here.
7969 if (aLoadType & LOAD_CMD_NORMAL) {
7970 mOSHE->GetPostData(getter_AddRefs(postData));
7971 mOSHE->GetPageIdentifier(&pageIdent);
7972 mOSHE->GetCacheKey(getter_AddRefs(cacheKey));
7975 if (mLSHE && wasAnchor) {
7976 // If it's an anchor load, set mLSHE's doc identifier to
7977 // mOSHE's doc identifier -- These are the same documents,
7978 // as far as HTML5 is concerned.
7979 PRUint64 docIdent;
7980 rv = mOSHE->GetDocIdentifier(&docIdent);
7981 if (NS_SUCCEEDED(rv)) {
7982 mLSHE->SetDocIdentifier(docIdent);
7987 /* Assign mOSHE to mLSHE. This will either be a new entry created
7988 * by OnNewURI() for normal loads or aSHEntry for history loads.
7990 if (mLSHE) {
7991 SetHistoryEntry(&mOSHE, mLSHE);
7992 // Save the postData obtained from the previous page
7993 // in to the session history entry created for the
7994 // anchor page, so that any history load of the anchor
7995 // page will restore the appropriate postData.
7996 if (postData)
7997 mOSHE->SetPostData(postData);
7999 // Make sure we won't just repost without hitting the
8000 // cache first
8001 if (cacheKey)
8002 mOSHE->SetCacheKey(cacheKey);
8004 // Propagate our page ident to the new mOSHE so that
8005 // we'll know it just differed by a scroll on the page.
8006 if (pageIdent != PR_UINT32_MAX)
8007 mOSHE->SetPageIdentifier(pageIdent);
8010 /* restore previous position of scroller(s), if we're moving
8011 * back in history (bug 59774)
8013 if (mOSHE && (aLoadType == LOAD_HISTORY || aLoadType == LOAD_RELOAD_NORMAL))
8015 nscoord bx, by;
8016 mOSHE->GetScrollPosition(&bx, &by);
8017 SetCurScrollPosEx(bx, by);
8020 /* Clear out mLSHE so that further anchor visits get
8021 * recorded in SH and SH won't misbehave.
8023 SetHistoryEntry(&mLSHE, nsnull);
8024 /* Set the title for the SH entry for this target url. so that
8025 * SH menus in go/back/forward buttons won't be empty for this.
8027 if (mSessionHistory) {
8028 PRInt32 index = -1;
8029 mSessionHistory->GetIndex(&index);
8030 nsCOMPtr<nsIHistoryEntry> hEntry;
8031 mSessionHistory->GetEntryAtIndex(index, PR_FALSE,
8032 getter_AddRefs(hEntry));
8033 NS_ENSURE_TRUE(hEntry, NS_ERROR_FAILURE);
8034 nsCOMPtr<nsISHEntry> shEntry(do_QueryInterface(hEntry));
8035 if (shEntry)
8036 shEntry->SetTitle(mTitle);
8039 /* Set the title for the Global History entry for this anchor url.
8041 if (mGlobalHistory) {
8042 mGlobalHistory->SetPageTitle(aURI, mTitle);
8045 if (sameDocIdent) {
8046 // Set the doc's URI according to the new history entry's URI
8047 nsCOMPtr<nsIURI> newURI;
8048 mOSHE->GetURI(getter_AddRefs(newURI));
8049 NS_ENSURE_TRUE(newURI, NS_ERROR_FAILURE);
8050 nsCOMPtr<nsIDocument> doc =
8051 do_GetInterface(GetAsSupports(this));
8052 NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
8054 doc->SetDocumentURI(newURI);
8057 SetDocPendingStateObj(mOSHE);
8059 // Dispatch the popstate and hashchange events, as appropriate
8060 nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mScriptGlobal);
8061 if (window) {
8062 window->DispatchSyncPopState();
8064 if (doHashchange)
8065 window->DispatchSyncHashchange();
8068 return NS_OK;
8072 // mContentViewer->PermitUnload can destroy |this| docShell, which
8073 // causes the next call of CanSavePresentation to crash.
8074 // Hold onto |this| until we return, to prevent a crash from happening.
8075 // (bug#331040)
8076 nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
8078 // Check if the page doesn't want to be unloaded. The javascript:
8079 // protocol handler deals with this for javascript: URLs.
8080 if (!bIsJavascript && mContentViewer) {
8081 PRBool okToUnload;
8082 rv = mContentViewer->PermitUnload(PR_FALSE, &okToUnload);
8084 if (NS_SUCCEEDED(rv) && !okToUnload) {
8085 // The user chose not to unload the page, interrupt the
8086 // load.
8087 return NS_OK;
8091 // Check for saving the presentation here, before calling Stop().
8092 // This is necessary so that we can catch any pending requests.
8093 // Since the new request has not been created yet, we pass null for the
8094 // new request parameter.
8095 // Also pass nsnull for the document, since it doesn't affect the return
8096 // value for our purposes here.
8097 PRBool savePresentation = CanSavePresentation(aLoadType, nsnull, nsnull);
8099 // Don't stop current network activity for javascript: URL's since
8100 // they might not result in any data, and thus nothing should be
8101 // stopped in those cases. In the case where they do result in
8102 // data, the javascript: URL channel takes care of stopping
8103 // current network activity.
8104 if (!bIsJavascript) {
8105 // Stop any current network activity.
8106 // Also stop content if this is a zombie doc. otherwise
8107 // the onload will be delayed by other loads initiated in the
8108 // background by the first document that
8109 // didn't fully load before the next load was initiated.
8110 // If not a zombie, don't stop content until data
8111 // starts arriving from the new URI...
8113 nsCOMPtr<nsIContentViewer> zombieViewer;
8114 if (mContentViewer) {
8115 mContentViewer->GetPreviousViewer(getter_AddRefs(zombieViewer));
8118 if (zombieViewer ||
8119 LOAD_TYPE_HAS_FLAGS(aLoadType, LOAD_FLAGS_STOP_CONTENT)) {
8120 rv = Stop(nsIWebNavigation::STOP_ALL);
8121 } else {
8122 rv = Stop(nsIWebNavigation::STOP_NETWORK);
8125 if (NS_FAILED(rv))
8126 return rv;
8129 mLoadType = aLoadType;
8131 // mLSHE should be assigned to aSHEntry, only after Stop() has
8132 // been called. But when loading an error page, do not clear the
8133 // mLSHE for the real page.
8134 if (mLoadType != LOAD_ERROR_PAGE)
8135 SetHistoryEntry(&mLSHE, aSHEntry);
8137 mSavingOldViewer = savePresentation;
8139 // If we have a saved content viewer in history, restore and show it now.
8140 if (aSHEntry && (mLoadType & LOAD_CMD_HISTORY)) {
8141 // It's possible that the previous viewer of mContentViewer is the
8142 // viewer that will end up in aSHEntry when it gets closed. If that's
8143 // the case, we need to go ahead and force it into its shentry so we
8144 // can restore it.
8145 if (mContentViewer) {
8146 nsCOMPtr<nsIContentViewer> prevViewer;
8147 mContentViewer->GetPreviousViewer(getter_AddRefs(prevViewer));
8148 if (prevViewer) {
8149 #ifdef DEBUG
8150 nsCOMPtr<nsIContentViewer> prevPrevViewer;
8151 prevViewer->GetPreviousViewer(getter_AddRefs(prevPrevViewer));
8152 NS_ASSERTION(!prevPrevViewer, "Should never have viewer chain here");
8153 #endif
8154 nsCOMPtr<nsISHEntry> viewerEntry;
8155 prevViewer->GetHistoryEntry(getter_AddRefs(viewerEntry));
8156 if (viewerEntry == aSHEntry) {
8157 // Make sure this viewer ends up in the right place
8158 mContentViewer->SetPreviousViewer(nsnull);
8159 prevViewer->Destroy();
8163 nsCOMPtr<nsISHEntry> oldEntry = mOSHE;
8164 PRBool restoring;
8165 rv = RestorePresentation(aSHEntry, &restoring);
8166 if (restoring)
8167 return rv;
8169 // We failed to restore the presentation, so clean up.
8170 // Both the old and new history entries could potentially be in
8171 // an inconsistent state.
8172 if (NS_FAILED(rv)) {
8173 if (oldEntry)
8174 oldEntry->SyncPresentationState();
8176 aSHEntry->SyncPresentationState();
8180 nsCOMPtr<nsIRequest> req;
8181 rv = DoURILoad(aURI, aReferrer,
8182 !(aFlags & INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER),
8183 owner, aTypeHint, aPostData, aHeadersData, aFirstParty,
8184 aDocShell, getter_AddRefs(req),
8185 (aFlags & INTERNAL_LOAD_FLAGS_FIRST_LOAD) != 0,
8186 (aFlags & INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER) != 0,
8187 (aFlags & INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES) != 0);
8188 if (req && aRequest)
8189 NS_ADDREF(*aRequest = req);
8191 if (NS_FAILED(rv)) {
8192 nsCOMPtr<nsIChannel> chan(do_QueryInterface(req));
8193 DisplayLoadError(rv, aURI, nsnull, chan);
8196 return rv;
8199 nsIPrincipal*
8200 nsDocShell::GetInheritedPrincipal(PRBool aConsiderCurrentDocument)
8202 nsCOMPtr<nsIDocument> document;
8204 if (aConsiderCurrentDocument && mContentViewer) {
8205 document = mContentViewer->GetDocument();
8208 if (!document) {
8209 nsCOMPtr<nsIDocShellTreeItem> parentItem;
8210 GetSameTypeParent(getter_AddRefs(parentItem));
8211 if (parentItem) {
8212 nsCOMPtr<nsIDOMDocument> parentDomDoc(do_GetInterface(parentItem));
8213 document = do_QueryInterface(parentDomDoc);
8217 if (!document) {
8218 if (!aConsiderCurrentDocument) {
8219 return nsnull;
8222 // Make sure we end up with _something_ as the principal no matter
8223 // what.
8224 EnsureContentViewer(); // If this fails, we'll just get a null
8225 // docViewer and bail.
8227 if (!mContentViewer)
8228 return nsnull;
8229 document = mContentViewer->GetDocument();
8232 //-- Get the document's principal
8233 if (document) {
8234 return document->NodePrincipal();
8237 return nsnull;
8240 PRBool
8241 nsDocShell::ShouldCheckAppCache(nsIURI *aURI)
8243 nsCOMPtr<nsIOfflineCacheUpdateService> offlineService =
8244 do_GetService(NS_OFFLINECACHEUPDATESERVICE_CONTRACTID);
8245 if (!offlineService) {
8246 return PR_FALSE;
8249 PRBool allowed;
8250 nsresult rv = offlineService->OfflineAppAllowedForURI(aURI,
8251 nsnull,
8252 &allowed);
8253 return NS_SUCCEEDED(rv) && allowed;
8256 nsresult
8257 nsDocShell::DoURILoad(nsIURI * aURI,
8258 nsIURI * aReferrerURI,
8259 PRBool aSendReferrer,
8260 nsISupports * aOwner,
8261 const char * aTypeHint,
8262 nsIInputStream * aPostData,
8263 nsIInputStream * aHeadersData,
8264 PRBool aFirstParty,
8265 nsIDocShell ** aDocShell,
8266 nsIRequest ** aRequest,
8267 PRBool aIsNewWindowTarget,
8268 PRBool aBypassClassifier,
8269 PRBool aForceAllowCookies)
8271 nsresult rv;
8272 nsCOMPtr<nsIURILoader> uriLoader;
8274 uriLoader = do_GetService(NS_URI_LOADER_CONTRACTID, &rv);
8275 if (NS_FAILED(rv)) return rv;
8277 nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL;
8278 if (aFirstParty) {
8279 // tag first party URL loads
8280 loadFlags |= nsIChannel::LOAD_INITIAL_DOCUMENT_URI;
8283 if (mLoadType == LOAD_ERROR_PAGE) {
8284 // Error pages are LOAD_BACKGROUND
8285 loadFlags |= nsIChannel::LOAD_BACKGROUND;
8288 // check for Content Security Policy to pass along with the
8289 // new channel we are creating
8290 nsCOMPtr<nsIChannelPolicy> channelPolicy;
8291 if (IsFrame()) {
8292 // check the parent docshell for a CSP
8293 nsCOMPtr<nsIContentSecurityPolicy> csp;
8294 nsCOMPtr<nsIDocShellTreeItem> parentItem;
8295 GetSameTypeParent(getter_AddRefs(parentItem));
8296 nsCOMPtr<nsIDOMDocument> domDoc(do_GetInterface(parentItem));
8297 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
8298 if (doc) {
8299 rv = doc->NodePrincipal()->GetCsp(getter_AddRefs(csp));
8300 NS_ENSURE_SUCCESS(rv, rv);
8301 if (csp) {
8302 channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
8303 channelPolicy->SetContentSecurityPolicy(csp);
8304 channelPolicy->SetLoadType(nsIContentPolicy::TYPE_SUBDOCUMENT);
8309 // open a channel for the url
8310 nsCOMPtr<nsIChannel> channel;
8312 rv = NS_NewChannel(getter_AddRefs(channel),
8313 aURI,
8314 nsnull,
8315 nsnull,
8316 static_cast<nsIInterfaceRequestor *>(this),
8317 loadFlags,
8318 channelPolicy);
8319 if (NS_FAILED(rv)) {
8320 if (rv == NS_ERROR_UNKNOWN_PROTOCOL) {
8321 // This is a uri with a protocol scheme we don't know how
8322 // to handle. Embedders might still be interested in
8323 // handling the load, though, so we fire a notification
8324 // before throwing the load away.
8325 PRBool abort = PR_FALSE;
8326 nsresult rv2 = mContentListener->OnStartURIOpen(aURI, &abort);
8327 if (NS_SUCCEEDED(rv2) && abort) {
8328 // Hey, they're handling the load for us! How convenient!
8329 return NS_OK;
8333 return rv;
8336 nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
8337 do_QueryInterface(channel);
8338 if (appCacheChannel) {
8339 // Any document load should not inherit application cache.
8340 appCacheChannel->SetInheritApplicationCache(PR_FALSE);
8342 // Loads with the correct permissions should check for a matching
8343 // application cache.
8344 appCacheChannel->SetChooseApplicationCache(ShouldCheckAppCache(aURI));
8347 // Make sure to give the caller a channel if we managed to create one
8348 // This is important for correct error page/session history interaction
8349 if (aRequest)
8350 NS_ADDREF(*aRequest = channel);
8352 channel->SetOriginalURI(aURI);
8353 if (aTypeHint && *aTypeHint) {
8354 channel->SetContentType(nsDependentCString(aTypeHint));
8355 mContentTypeHint = aTypeHint;
8357 else {
8358 mContentTypeHint.Truncate();
8361 //hack
8362 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
8363 nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal(do_QueryInterface(channel));
8364 if (httpChannelInternal) {
8365 if (aForceAllowCookies) {
8366 httpChannelInternal->SetForceAllowThirdPartyCookie(PR_TRUE);
8368 if (aFirstParty) {
8369 httpChannelInternal->SetDocumentURI(aURI);
8370 } else {
8371 httpChannelInternal->SetDocumentURI(aReferrerURI);
8375 nsCOMPtr<nsIWritablePropertyBag2> props(do_QueryInterface(channel));
8376 if (props)
8378 // save true referrer for those who need it (e.g. xpinstall whitelisting)
8379 // Currently only http and ftp channels support this.
8380 props->SetPropertyAsInterface(NS_LITERAL_STRING("docshell.internalReferrer"),
8381 aReferrerURI);
8385 // If this is a HTTP channel, then set up the HTTP specific information
8386 // (ie. POST data, referrer, ...)
8388 if (httpChannel) {
8389 nsCOMPtr<nsICachingChannel> cacheChannel(do_QueryInterface(httpChannel));
8390 /* Get the cache Key from SH */
8391 nsCOMPtr<nsISupports> cacheKey;
8392 if (mLSHE) {
8393 mLSHE->GetCacheKey(getter_AddRefs(cacheKey));
8395 else if (mOSHE) // for reload cases
8396 mOSHE->GetCacheKey(getter_AddRefs(cacheKey));
8398 // figure out if we need to set the post data stream on the channel...
8399 // right now, this is only done for http channels.....
8400 if (aPostData) {
8401 // XXX it's a bit of a hack to rewind the postdata stream here but
8402 // it has to be done in case the post data is being reused multiple
8403 // times.
8404 nsCOMPtr<nsISeekableStream>
8405 postDataSeekable(do_QueryInterface(aPostData));
8406 if (postDataSeekable) {
8407 rv = postDataSeekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
8408 NS_ENSURE_SUCCESS(rv, rv);
8411 nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
8412 NS_ASSERTION(uploadChannel, "http must support nsIUploadChannel");
8414 // we really need to have a content type associated with this stream!!
8415 uploadChannel->SetUploadStream(aPostData, EmptyCString(), -1);
8416 /* If there is a valid postdata *and* it is a History Load,
8417 * set up the cache key on the channel, to retrieve the
8418 * data *only* from the cache. If it is a normal reload, the
8419 * cache is free to go to the server for updated postdata.
8421 if (cacheChannel && cacheKey) {
8422 if (mLoadType == LOAD_HISTORY || mLoadType == LOAD_RELOAD_CHARSET_CHANGE) {
8423 cacheChannel->SetCacheKey(cacheKey);
8424 PRUint32 loadFlags;
8425 if (NS_SUCCEEDED(channel->GetLoadFlags(&loadFlags)))
8426 channel->SetLoadFlags(loadFlags | nsICachingChannel::LOAD_ONLY_FROM_CACHE);
8428 else if (mLoadType == LOAD_RELOAD_NORMAL)
8429 cacheChannel->SetCacheKey(cacheKey);
8432 else {
8433 /* If there is no postdata, set the cache key on the channel, and
8434 * do not set the LOAD_ONLY_FROM_CACHE flag, so that the channel
8435 * will be free to get it from net if it is not found in cache.
8436 * New cache may use it creatively on CGI pages with GET
8437 * method and even on those that say "no-cache"
8439 if (mLoadType == LOAD_HISTORY || mLoadType == LOAD_RELOAD_NORMAL
8440 || mLoadType == LOAD_RELOAD_CHARSET_CHANGE) {
8441 if (cacheChannel && cacheKey)
8442 cacheChannel->SetCacheKey(cacheKey);
8445 if (aHeadersData) {
8446 rv = AddHeadersToChannel(aHeadersData, httpChannel);
8448 // Set the referrer explicitly
8449 if (aReferrerURI && aSendReferrer) {
8450 // Referrer is currenly only set for link clicks here.
8451 httpChannel->SetReferrer(aReferrerURI);
8455 // Set the owner of the channel, but only for channels that can't
8456 // provide their own security context.
8458 // XXX: Is seems wrong that the owner is ignored - even if one is
8459 // supplied) unless the URI is javascript or data or about:blank.
8460 // XXX: If this is ever changed, check all callers for what owners they're
8461 // passing in. In particular, see the code and comments in LoadURI
8462 // where we fall back on inheriting the owner if called
8463 // from chrome. That would be very wrong if this code changed
8464 // anything but channels that can't provide their own security context!
8466 // (Currently chrome URIs set the owner when they are created!
8467 // So setting a NULL owner would be bad!)
8469 // If this code ever changes, change nsObjectLoadingContent::LoadObject
8470 // accordingly.
8471 PRBool inherit;
8472 // We expect URIInheritsSecurityContext to return success for an
8473 // about:blank URI, so don't call IsAboutBlank() if this call fails.
8474 rv = URIInheritsSecurityContext(aURI, &inherit);
8475 if (NS_SUCCEEDED(rv) && (inherit || IsAboutBlank(aURI))) {
8476 channel->SetOwner(aOwner);
8480 // file: uri special-casing
8482 // If this is a file: load opened from another file: then it may need
8483 // to inherit the owner from the referrer so they can script each other.
8484 // If we don't set the owner explicitly then each file: gets an owner
8485 // based on its own codebase later.
8487 nsCOMPtr<nsIPrincipal> ownerPrincipal(do_QueryInterface(aOwner));
8488 if (URIIsLocalFile(aURI) && ownerPrincipal &&
8489 NS_SUCCEEDED(ownerPrincipal->CheckMayLoad(aURI, PR_FALSE))) {
8490 // One more check here. CheckMayLoad will always return true for the
8491 // system principal, but we do NOT want to inherit in that case.
8492 PRBool isSystem;
8493 nsCOMPtr<nsIScriptSecurityManager> secMan =
8494 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
8495 if (secMan &&
8496 NS_SUCCEEDED(secMan->IsSystemPrincipal(ownerPrincipal,
8497 &isSystem)) &&
8498 !isSystem) {
8499 channel->SetOwner(aOwner);
8503 nsCOMPtr<nsIScriptChannel> scriptChannel = do_QueryInterface(channel);
8504 if (scriptChannel) {
8505 // Allow execution against our context if the principals match
8506 scriptChannel->
8507 SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL);
8510 if (aIsNewWindowTarget) {
8511 nsCOMPtr<nsIWritablePropertyBag2> props = do_QueryInterface(channel);
8512 if (props) {
8513 props->SetPropertyAsBool(
8514 NS_LITERAL_STRING("docshell.newWindowTarget"),
8515 PR_TRUE);
8519 rv = DoChannelLoad(channel, uriLoader, aBypassClassifier);
8522 // If the channel load failed, we failed and nsIWebProgress just ain't
8523 // gonna happen.
8525 if (NS_SUCCEEDED(rv)) {
8526 if (aDocShell) {
8527 *aDocShell = this;
8528 NS_ADDREF(*aDocShell);
8532 return rv;
8535 static NS_METHOD
8536 AppendSegmentToString(nsIInputStream *in,
8537 void *closure,
8538 const char *fromRawSegment,
8539 PRUint32 toOffset,
8540 PRUint32 count,
8541 PRUint32 *writeCount)
8543 // aFromSegment now contains aCount bytes of data.
8545 nsCAutoString *buf = static_cast<nsCAutoString *>(closure);
8546 buf->Append(fromRawSegment, count);
8548 // Indicate that we have consumed all of aFromSegment
8549 *writeCount = count;
8550 return NS_OK;
8553 NS_IMETHODIMP
8554 nsDocShell::AddHeadersToChannel(nsIInputStream *aHeadersData,
8555 nsIChannel *aGenericChannel)
8557 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aGenericChannel);
8558 NS_ENSURE_STATE(httpChannel);
8560 PRUint32 numRead;
8561 nsCAutoString headersString;
8562 nsresult rv = aHeadersData->ReadSegments(AppendSegmentToString,
8563 &headersString,
8564 PR_UINT32_MAX,
8565 &numRead);
8566 NS_ENSURE_SUCCESS(rv, rv);
8568 // used during the manipulation of the String from the InputStream
8569 nsCAutoString headerName;
8570 nsCAutoString headerValue;
8571 PRInt32 crlf;
8572 PRInt32 colon;
8575 // Iterate over the headersString: for each "\r\n" delimited chunk,
8576 // add the value as a header to the nsIHttpChannel
8579 static const char kWhitespace[] = "\b\t\r\n ";
8580 while (PR_TRUE) {
8581 crlf = headersString.Find("\r\n");
8582 if (crlf == kNotFound)
8583 return NS_OK;
8585 const nsCSubstring &oneHeader = StringHead(headersString, crlf);
8587 colon = oneHeader.FindChar(':');
8588 if (colon == kNotFound)
8589 return NS_ERROR_UNEXPECTED;
8591 headerName = StringHead(oneHeader, colon);
8592 headerValue = Substring(oneHeader, colon + 1);
8594 headerName.Trim(kWhitespace);
8595 headerValue.Trim(kWhitespace);
8597 headersString.Cut(0, crlf + 2);
8600 // FINALLY: we can set the header!
8603 rv = httpChannel->SetRequestHeader(headerName, headerValue, PR_TRUE);
8604 NS_ENSURE_SUCCESS(rv, rv);
8607 NS_NOTREACHED("oops");
8608 return NS_ERROR_UNEXPECTED;
8611 nsresult nsDocShell::DoChannelLoad(nsIChannel * aChannel,
8612 nsIURILoader * aURILoader,
8613 PRBool aBypassClassifier)
8615 nsresult rv;
8616 // Mark the channel as being a document URI and allow content sniffing...
8617 nsLoadFlags loadFlags = 0;
8618 (void) aChannel->GetLoadFlags(&loadFlags);
8619 loadFlags |= nsIChannel::LOAD_DOCUMENT_URI |
8620 nsIChannel::LOAD_CALL_CONTENT_SNIFFERS;
8622 // Load attributes depend on load type...
8623 switch (mLoadType) {
8624 case LOAD_HISTORY:
8625 loadFlags |= nsIRequest::VALIDATE_NEVER;
8626 break;
8628 case LOAD_RELOAD_CHARSET_CHANGE:
8629 loadFlags |= nsIRequest::LOAD_FROM_CACHE;
8630 break;
8632 case LOAD_RELOAD_NORMAL:
8633 case LOAD_REFRESH:
8634 loadFlags |= nsIRequest::VALIDATE_ALWAYS;
8635 break;
8637 case LOAD_NORMAL_BYPASS_CACHE:
8638 case LOAD_NORMAL_BYPASS_PROXY:
8639 case LOAD_NORMAL_BYPASS_PROXY_AND_CACHE:
8640 case LOAD_RELOAD_BYPASS_CACHE:
8641 case LOAD_RELOAD_BYPASS_PROXY:
8642 case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
8643 loadFlags |= nsIRequest::LOAD_BYPASS_CACHE;
8644 break;
8646 case LOAD_NORMAL:
8647 case LOAD_LINK:
8648 // Set cache checking flags
8649 PRInt32 prefSetting;
8650 if (NS_SUCCEEDED
8651 (mPrefs->
8652 GetIntPref("browser.cache.check_doc_frequency",
8653 &prefSetting))) {
8654 switch (prefSetting) {
8655 case 0:
8656 loadFlags |= nsIRequest::VALIDATE_ONCE_PER_SESSION;
8657 break;
8658 case 1:
8659 loadFlags |= nsIRequest::VALIDATE_ALWAYS;
8660 break;
8661 case 2:
8662 loadFlags |= nsIRequest::VALIDATE_NEVER;
8663 break;
8666 break;
8669 if (!aBypassClassifier) {
8670 loadFlags |= nsIChannel::LOAD_CLASSIFY_URI;
8673 (void) aChannel->SetLoadFlags(loadFlags);
8675 rv = aURILoader->OpenURI(aChannel,
8676 (mLoadType == LOAD_LINK),
8677 this);
8678 NS_ENSURE_SUCCESS(rv, rv);
8680 return NS_OK;
8683 nsresult
8684 nsDocShell::ScrollIfAnchor(nsIURI * aURI, PRBool * aWasAnchor,
8685 PRUint32 aLoadType, nscoord *cx, nscoord *cy,
8686 PRBool * aDoHashchange)
8688 NS_ASSERTION(aURI, "null uri arg");
8689 NS_ASSERTION(aWasAnchor, "null anchor arg");
8690 NS_PRECONDITION(aDoHashchange, "null hashchange arg");
8692 if (!aURI || !aWasAnchor) {
8693 return NS_ERROR_FAILURE;
8696 *aWasAnchor = PR_FALSE;
8697 *aDoHashchange = PR_FALSE;
8699 if (!mCurrentURI) {
8700 return NS_OK;
8703 nsCOMPtr<nsIPresShell> shell;
8704 nsresult rv = GetPresShell(getter_AddRefs(shell));
8705 if (NS_FAILED(rv) || !shell) {
8706 // If we failed to get the shell, or if there is no shell,
8707 // nothing left to do here.
8709 return rv;
8712 // NOTE: we assume URIs are absolute for comparison purposes
8714 nsCAutoString currentSpec;
8715 NS_ENSURE_SUCCESS(mCurrentURI->GetSpec(currentSpec),
8716 NS_ERROR_FAILURE);
8718 nsCAutoString newSpec;
8719 NS_ENSURE_SUCCESS(aURI->GetSpec(newSpec), NS_ERROR_FAILURE);
8721 // Search for hash marks in the current URI and the new URI and
8722 // take a copy of everything to the left of the hash for
8723 // comparison.
8725 const char kHash = '#';
8727 // Split the new URI into a left and right part
8728 // (assume we're parsing it out right)
8729 nsACString::const_iterator urlStart, urlEnd, refStart, refEnd;
8730 newSpec.BeginReading(urlStart);
8731 newSpec.EndReading(refEnd);
8733 PRInt32 hashNew = newSpec.FindChar(kHash);
8734 if (hashNew == 0) {
8735 return NS_OK; // Strange URI
8738 if (hashNew > 0) {
8739 // found it
8740 urlEnd = urlStart;
8741 urlEnd.advance(hashNew);
8743 refStart = urlEnd;
8744 ++refStart; // advanced past '#'
8747 else {
8748 // no hash at all
8749 urlEnd = refStart = refEnd;
8751 const nsACString& sNewLeft = Substring(urlStart, urlEnd);
8752 const nsACString& sNewRef = Substring(refStart, refEnd);
8754 // Split the current URI in a left and right part
8755 nsACString::const_iterator currentLeftStart, currentLeftEnd,
8756 currentRefStart, currentRefEnd;
8757 currentSpec.BeginReading(currentLeftStart);
8758 currentSpec.EndReading(currentRefEnd);
8760 PRInt32 hashCurrent = currentSpec.FindChar(kHash);
8761 if (hashCurrent == 0) {
8762 return NS_OK; // Strange URI
8765 if (hashCurrent > 0) {
8766 currentLeftEnd = currentLeftStart;
8767 currentLeftEnd.advance(hashCurrent);
8769 currentRefStart = currentLeftEnd;
8770 ++currentRefStart; // advance past '#'
8772 else {
8773 // no hash at all in currentSpec
8774 currentLeftEnd = currentRefStart = currentRefEnd;
8777 // If we have no new anchor, we do not want to scroll, unless there is a
8778 // current anchor and we are doing a history load. So return if we have no
8779 // new anchor, and there is no current anchor or the load is not a history
8780 // load.
8781 NS_ASSERTION(hashNew != 0 && hashCurrent != 0,
8782 "What happened to the early returns above?");
8783 if (hashNew == kNotFound &&
8784 (hashCurrent == kNotFound || aLoadType != LOAD_HISTORY)) {
8785 return NS_OK;
8788 // Compare the URIs.
8790 // NOTE: this is a case sensitive comparison because some parts of the
8791 // URI are case sensitive, and some are not. i.e. the domain name
8792 // is case insensitive but the the paths are not.
8794 // This means that comparing "http://www.ABC.com/" to "http://www.abc.com/"
8795 // will fail this test.
8797 if (!Substring(currentLeftStart, currentLeftEnd).Equals(sNewLeft)) {
8798 return NS_OK; // URIs not the same
8801 // Now we know we are dealing with an anchor
8802 *aWasAnchor = PR_TRUE;
8804 // We should fire a hashchange event once we're done here if the two hashes
8805 // are different.
8806 *aDoHashchange = !Substring(currentRefStart, currentRefEnd).Equals(sNewRef);
8808 // Both the new and current URIs refer to the same page. We can now
8809 // browse to the hash stored in the new URI.
8811 // But first let's capture positions of scroller(s) that can
8812 // (and usually will) be modified by GoToAnchor() call.
8814 GetCurScrollPos(ScrollOrientation_X, cx);
8815 GetCurScrollPos(ScrollOrientation_Y, cy);
8817 if (!sNewRef.IsEmpty()) {
8818 // anchor is there, but if it's a load from history,
8819 // we don't have any anchor jumping to do
8820 PRBool scroll = aLoadType != LOAD_HISTORY &&
8821 aLoadType != LOAD_RELOAD_NORMAL;
8823 char *str = ToNewCString(sNewRef);
8824 if (!str) {
8825 return NS_ERROR_OUT_OF_MEMORY;
8828 // nsUnescape modifies the string that is passed into it.
8829 nsUnescape(str);
8831 // We assume that the bytes are in UTF-8, as it says in the
8832 // spec:
8833 // http://www.w3.org/TR/html4/appendix/notes.html#h-B.2.1
8835 // We try the UTF-8 string first, and then try the document's
8836 // charset (see below). If the string is not UTF-8,
8837 // conversion will fail and give us an empty Unicode string.
8838 // In that case, we should just fall through to using the
8839 // page's charset.
8840 rv = NS_ERROR_FAILURE;
8841 NS_ConvertUTF8toUTF16 uStr(str);
8842 if (!uStr.IsEmpty()) {
8843 rv = shell->GoToAnchor(NS_ConvertUTF8toUTF16(str), scroll);
8845 nsMemory::Free(str);
8847 // Above will fail if the anchor name is not UTF-8. Need to
8848 // convert from document charset to unicode.
8849 if (NS_FAILED(rv)) {
8851 // Get a document charset
8852 NS_ENSURE_TRUE(mContentViewer, NS_ERROR_FAILURE);
8853 nsIDocument* doc = mContentViewer->GetDocument();
8854 NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
8855 const nsACString &aCharset = doc->GetDocumentCharacterSet();
8857 nsCOMPtr<nsITextToSubURI> textToSubURI =
8858 do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
8859 NS_ENSURE_SUCCESS(rv, rv);
8861 // Unescape and convert to unicode
8862 nsXPIDLString uStr;
8864 rv = textToSubURI->UnEscapeAndConvert(PromiseFlatCString(aCharset).get(),
8865 PromiseFlatCString(sNewRef).get(),
8866 getter_Copies(uStr));
8867 NS_ENSURE_SUCCESS(rv, rv);
8869 // Ignore return value of GoToAnchor, since it will return an error
8870 // if there is no such anchor in the document, which is actually a
8871 // success condition for us (we want to update the session history
8872 // with the new URI no matter whether we actually scrolled
8873 // somewhere).
8874 shell->GoToAnchor(uStr, scroll);
8877 else {
8879 // Tell the shell it's at an anchor, without scrolling.
8880 shell->GoToAnchor(EmptyString(), PR_FALSE);
8882 // An empty anchor was found, but if it's a load from history,
8883 // we don't have to jump to the top of the page. Scrollbar
8884 // position will be restored by the caller, based on positions
8885 // stored in session history.
8886 if (aLoadType == LOAD_HISTORY || aLoadType == LOAD_RELOAD_NORMAL)
8887 return rv;
8888 //An empty anchor. Scroll to the top of the page.
8889 rv = SetCurScrollPosEx(0, 0);
8892 return rv;
8895 void
8896 nsDocShell::SetupReferrerFromChannel(nsIChannel * aChannel)
8898 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
8899 if (httpChannel) {
8900 nsCOMPtr<nsIURI> referrer;
8901 nsresult rv = httpChannel->GetReferrer(getter_AddRefs(referrer));
8902 if (NS_SUCCEEDED(rv)) {
8903 SetReferrerURI(referrer);
8908 PRBool
8909 nsDocShell::OnNewURI(nsIURI * aURI, nsIChannel * aChannel, nsISupports* aOwner,
8910 PRUint32 aLoadType, PRBool aFireOnLocationChange,
8911 PRBool aAddToGlobalHistory)
8913 NS_PRECONDITION(aURI, "uri is null");
8914 NS_PRECONDITION(!aChannel || !aOwner, "Shouldn't have both set");
8916 #if defined(PR_LOGGING) && defined(DEBUG)
8917 if (PR_LOG_TEST(gDocShellLog, PR_LOG_DEBUG)) {
8918 nsCAutoString spec;
8919 aURI->GetSpec(spec);
8921 nsCAutoString chanName;
8922 if (aChannel)
8923 aChannel->GetName(chanName);
8924 else
8925 chanName.AssignLiteral("<no channel>");
8927 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
8928 ("nsDocShell[%p]::OnNewURI(\"%s\", [%s], 0x%x)\n", this, spec.get(),
8929 chanName.get(), aLoadType));
8931 #endif
8933 PRBool updateHistory = PR_TRUE;
8934 PRBool equalUri = PR_FALSE;
8935 PRBool shAvailable = PR_TRUE;
8937 // Get the post data from the channel
8938 nsCOMPtr<nsIInputStream> inputStream;
8939 if (aChannel) {
8940 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
8942 // Check if the HTTPChannel is hiding under a multiPartChannel
8943 if (!httpChannel) {
8944 GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
8947 if (httpChannel) {
8948 nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
8949 if (uploadChannel) {
8950 uploadChannel->GetUploadStream(getter_AddRefs(inputStream));
8953 // If the response status indicates an error, unlink this session
8954 // history entry from any entries sharing its doc ident.
8955 PRUint32 responseStatus;
8956 nsresult rv = httpChannel->GetResponseStatus(&responseStatus);
8957 if (mLSHE && NS_SUCCEEDED(rv) && responseStatus >= 400) {
8958 mLSHE->SetUniqueDocIdentifier();
8962 /* Create SH Entry (mLSHE) only if there is a SessionHistory object (mSessionHistory) in
8963 * the current frame or in the root docshell
8965 nsCOMPtr<nsISHistory> rootSH = mSessionHistory;
8966 if (!rootSH) {
8967 // Get the handle to SH from the root docshell
8968 GetRootSessionHistory(getter_AddRefs(rootSH));
8969 if (!rootSH)
8970 shAvailable = PR_FALSE;
8971 } // rootSH
8974 // Determine if this type of load should update history.
8975 if (aLoadType == LOAD_BYPASS_HISTORY ||
8976 aLoadType == LOAD_ERROR_PAGE ||
8977 aLoadType & LOAD_CMD_HISTORY ||
8978 aLoadType & LOAD_CMD_RELOAD)
8979 updateHistory = PR_FALSE;
8981 // Check if the url to be loaded is the same as the one already loaded.
8982 if (mCurrentURI)
8983 aURI->Equals(mCurrentURI, &equalUri);
8985 #ifdef DEBUG
8986 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
8987 (" shAvailable=%i updateHistory=%i equalURI=%i\n",
8988 shAvailable, updateHistory, equalUri));
8989 #endif
8991 /* If the url to be loaded is the same as the one already there,
8992 * and the original loadType is LOAD_NORMAL, LOAD_LINK, or
8993 * LOAD_STOP_CONTENT, set loadType to LOAD_NORMAL_REPLACE so that
8994 * AddToSessionHistory() won't mess with the current SHEntry and
8995 * if this page has any frame children, it also will be handled
8996 * properly. see bug 83684
8998 * XXX Hopefully changing the loadType at this time will not hurt
8999 * anywhere. The other way to take care of sequentially repeating
9000 * frameset pages is to add new methods to nsIDocShellTreeItem.
9001 * Hopefully I don't have to do that.
9003 if (equalUri &&
9004 (mLoadType == LOAD_NORMAL ||
9005 mLoadType == LOAD_LINK ||
9006 mLoadType == LOAD_STOP_CONTENT) &&
9007 !inputStream)
9009 mLoadType = LOAD_NORMAL_REPLACE;
9012 // If this is a refresh to the currently loaded url, we don't
9013 // have to update session or global history.
9014 if (mLoadType == LOAD_REFRESH && !inputStream && equalUri) {
9015 SetHistoryEntry(&mLSHE, mOSHE);
9018 /* If the user pressed shift-reload, cache will create a new cache key
9019 * for the page. Save the new cacheKey in Session History.
9020 * see bug 90098
9022 if (aChannel &&
9023 (aLoadType == LOAD_RELOAD_BYPASS_CACHE ||
9024 aLoadType == LOAD_RELOAD_BYPASS_PROXY ||
9025 aLoadType == LOAD_RELOAD_BYPASS_PROXY_AND_CACHE)) {
9026 NS_ASSERTION(!updateHistory,
9027 "We shouldn't be updating history for forced reloads!");
9029 nsCOMPtr<nsICachingChannel> cacheChannel(do_QueryInterface(aChannel));
9030 nsCOMPtr<nsISupports> cacheKey;
9031 // Get the Cache Key and store it in SH.
9032 if (cacheChannel)
9033 cacheChannel->GetCacheKey(getter_AddRefs(cacheKey));
9034 // If we already have a loading history entry, store the new cache key
9035 // in it. Otherwise, since we're doing a reload and won't be updating
9036 // our history entry, store the cache key in our current history entry.
9037 if (mLSHE)
9038 mLSHE->SetCacheKey(cacheKey);
9039 else if (mOSHE)
9040 mOSHE->SetCacheKey(cacheKey);
9043 if (updateHistory && shAvailable) {
9044 // Update session history if necessary...
9045 if (!mLSHE && (mItemType == typeContent) && mURIResultedInDocument) {
9046 /* This is a fresh page getting loaded for the first time
9047 *.Create a Entry for it and add it to SH, if this is the
9048 * rootDocShell
9050 (void) AddToSessionHistory(aURI, aChannel, aOwner,
9051 getter_AddRefs(mLSHE));
9054 // Update Global history
9055 if (aAddToGlobalHistory) {
9056 // Get the referrer uri from the channel
9057 AddToGlobalHistory(aURI, PR_FALSE, aChannel);
9061 // If this was a history load, update the index in
9062 // SH.
9063 if (rootSH && (mLoadType & LOAD_CMD_HISTORY)) {
9064 nsCOMPtr<nsISHistoryInternal> shInternal(do_QueryInterface(rootSH));
9065 if (shInternal) {
9066 rootSH->GetIndex(&mPreviousTransIndex);
9067 shInternal->UpdateIndex();
9068 rootSH->GetIndex(&mLoadedTransIndex);
9069 #ifdef DEBUG_PAGE_CACHE
9070 printf("Previous index: %d, Loaded index: %d\n\n",
9071 mPreviousTransIndex, mLoadedTransIndex);
9072 #endif
9075 PRBool onLocationChangeNeeded = SetCurrentURI(aURI, aChannel,
9076 aFireOnLocationChange);
9077 // Make sure to store the referrer from the channel, if any
9078 SetupReferrerFromChannel(aChannel);
9079 return onLocationChangeNeeded;
9082 PRBool
9083 nsDocShell::OnLoadingSite(nsIChannel * aChannel, PRBool aFireOnLocationChange,
9084 PRBool aAddToGlobalHistory)
9086 nsCOMPtr<nsIURI> uri;
9087 // If this a redirect, use the final url (uri)
9088 // else use the original url
9090 // Note that this should match what documents do (see nsDocument::Reset).
9091 NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
9092 NS_ENSURE_TRUE(uri, PR_FALSE);
9094 return OnNewURI(uri, aChannel, nsnull, mLoadType, aFireOnLocationChange,
9095 aAddToGlobalHistory);
9099 void
9100 nsDocShell::SetReferrerURI(nsIURI * aURI)
9102 mReferrerURI = aURI; // This assigment addrefs
9105 //*****************************************************************************
9106 // nsDocShell: Session History
9107 //*****************************************************************************
9109 nsresult
9110 nsDocShell::StringifyJSValVariant(nsIVariant *aData, nsAString &aResult)
9112 nsresult rv;
9113 aResult.Truncate();
9115 // First, try to extract a jsval from the variant |aData|. This works only
9116 // if the variant implements GetAsJSVal.
9117 jsval jsData;
9118 rv = aData->GetAsJSVal(&jsData);
9119 NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED);
9121 // Now get the JSContext associated with the current document.
9122 // First get the current document.
9123 nsCOMPtr<nsIDocument> document = do_GetInterface(GetAsSupports(this));
9124 NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
9126 // Get the JSContext from the document, like we do in
9127 // nsContentUtils::GetContextFromDocument().
9128 nsIScriptGlobalObject *sgo = document->GetScopeObject();
9129 NS_ENSURE_TRUE(sgo, NS_ERROR_FAILURE);
9131 nsIScriptContext *scx = sgo->GetContext();
9132 NS_ENSURE_TRUE(scx, NS_ERROR_FAILURE);
9134 JSContext *cx = (JSContext *)scx->GetNativeContext();
9136 // If our json call triggers a JS-to-C++ call, we want that call to use cx
9137 // as the context. So we push cx onto the context stack.
9138 nsCOMPtr<nsIJSContextStack> contextStack =
9139 do_GetService("@mozilla.org/js/xpc/ContextStack;1", &rv);
9140 NS_ENSURE_SUCCESS(rv, rv);
9142 contextStack->Push(cx);
9144 nsCOMPtr<nsIJSON> json = do_GetService("@mozilla.org/dom/json;1");
9145 if(json) {
9146 // Do the encoding
9147 rv = json->EncodeFromJSVal(&jsData, cx, aResult);
9149 else {
9150 rv = NS_ERROR_FAILURE;
9153 // Always pop the stack!
9154 contextStack->Pop(&cx);
9156 return rv;
9159 NS_IMETHODIMP
9160 nsDocShell::AddState(nsIVariant *aData, const nsAString& aTitle,
9161 const nsAString& aURL, PRBool aReplace)
9163 // Implements History.pushState and History.replaceState
9165 // Here's what we do, roughly in the order specified by HTML5:
9166 // 1. Serialize aData to JSON.
9167 // 2. If the third argument is present,
9168 // a. Resolve the url, relative to the first script's base URL
9169 // b. If (a) fails, raise a SECURITY_ERR
9170 // c. Compare the resulting absolute URL to the document's address. If
9171 // any part of the URLs difer other than the <path>, <query>, and
9172 // <fragment> components, raise a SECURITY_ERR and abort.
9173 // 3. If !aReplace:
9174 // Remove from the session history all entries after the current entry,
9175 // as we would after a regular navigation.
9176 // 4. As apropriate, either add a state object entry to the session history
9177 // after the current entry with the following properties, or modify the
9178 // current session history entry to set
9179 // a. cloned data as the state object,
9180 // b. the given title as the title, and,
9181 // c. if the third argument was present, the absolute URL found in
9182 // step 2
9183 // 5. If aReplace is false (i.e. we're doing a pushState instead of a
9184 // replaceState), notify bfcache that we've navigated to a new page.
9185 // 6. If the third argument is present, set the document's current address
9186 // to the absolute URL found in step 2.
9188 // It's important that this function not run arbitrary scripts after step 1
9189 // and before completing step 5. For example, if a script called
9190 // history.back() before we completed step 5, bfcache might destroy an
9191 // active content viewer. Since EvictContentViewers at the end of step 5
9192 // might run script, we can't just put a script blocker around the critical
9193 // section.
9195 nsresult rv;
9197 nsCOMPtr<nsIDocument> document = do_GetInterface(GetAsSupports(this));
9198 NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
9200 mLoadType = LOAD_PUSHSTATE;
9202 // Step 1: Clone aData by getting its JSON representation
9203 nsString dataStr;
9204 rv = StringifyJSValVariant(aData, dataStr);
9205 NS_ENSURE_SUCCESS(rv, rv);
9207 // Check that the state object isn't too long.
9208 // Default max length: 640k chars.
9209 PRInt32 maxStateObjSize = 0xA0000;
9210 if (mPrefs) {
9211 mPrefs->GetIntPref("browser.history.maxStateObjectSize",
9212 &maxStateObjSize);
9214 if (maxStateObjSize < 0) {
9215 maxStateObjSize = 0;
9217 NS_ENSURE_TRUE(dataStr.Length() <= (PRUint32)maxStateObjSize,
9218 NS_ERROR_ILLEGAL_VALUE);
9220 // Step 2: Resolve aURL
9221 PRBool equalURIs = PR_TRUE;
9222 nsCOMPtr<nsIURI> oldURI = mCurrentURI;
9223 nsCOMPtr<nsIURI> newURI;
9224 if (aURL.Length() == 0) {
9225 newURI = mCurrentURI;
9227 else {
9228 // 2a: Resolve aURL relative to mURI
9230 nsIURI* docBaseURI = document->GetDocBaseURI();
9231 if (!docBaseURI)
9232 return NS_ERROR_FAILURE;
9234 nsCAutoString spec;
9235 docBaseURI->GetSpec(spec);
9237 nsCAutoString charset;
9238 rv = docBaseURI->GetOriginCharset(charset);
9239 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
9241 rv = NS_NewURI(getter_AddRefs(newURI), aURL,
9242 charset.get(), docBaseURI);
9244 // 2b: If 2a fails, raise a SECURITY_ERR
9245 if (NS_FAILED(rv)) {
9246 return NS_ERROR_DOM_SECURITY_ERR;
9249 // 2c: Same-origin check.
9250 if (!URIIsLocalFile(newURI)) {
9251 // In addition to checking that the security manager says that
9252 // the new URI has the same origin as our current URI, we also
9253 // check that the two URIs have the same userpass. (The
9254 // security manager says that |http://foo.com| and
9255 // |http://me@foo.com| have the same origin.) mCurrentURI
9256 // won't contain the password part of the userpass, so this
9257 // means that it's never valid to specify a password in a
9258 // pushState or replaceState URI.
9260 nsCOMPtr<nsIScriptSecurityManager> secMan =
9261 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
9262 NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE);
9264 // It's very important that we check that newURI is of the same
9265 // origin as mCurrentURI, not docBaseURI, because a page can
9266 // set docBaseURI arbitrarily to any domain.
9267 nsCAutoString currentUserPass, newUserPass;
9268 NS_ENSURE_SUCCESS(mCurrentURI->GetUserPass(currentUserPass),
9269 NS_ERROR_FAILURE);
9270 NS_ENSURE_SUCCESS(newURI->GetUserPass(newUserPass),
9271 NS_ERROR_FAILURE);
9272 if (NS_FAILED(secMan->CheckSameOriginURI(mCurrentURI,
9273 newURI, PR_TRUE)) ||
9274 !currentUserPass.Equals(newUserPass)) {
9276 return NS_ERROR_DOM_SECURITY_ERR;
9279 else {
9280 // It's a file:// URI
9281 nsCOMPtr<nsIScriptObjectPrincipal> docScriptObj =
9282 do_QueryInterface(document);
9284 if (!docScriptObj) {
9285 return NS_ERROR_DOM_SECURITY_ERR;
9288 nsCOMPtr<nsIPrincipal> principal = docScriptObj->GetPrincipal();
9290 if (!principal ||
9291 NS_FAILED(principal->CheckMayLoad(newURI, PR_TRUE))) {
9293 return NS_ERROR_DOM_SECURITY_ERR;
9297 mCurrentURI->Equals(newURI, &equalURIs);
9299 } // end of same-origin check
9301 nsCOMPtr<nsISHistory> sessionHistory = mSessionHistory;
9302 if (!sessionHistory) {
9303 // Get the handle to SH from the root docshell
9304 GetRootSessionHistory(getter_AddRefs(sessionHistory));
9306 NS_ENSURE_TRUE(sessionHistory, NS_ERROR_FAILURE);
9308 nsCOMPtr<nsISHistoryInternal> shInternal =
9309 do_QueryInterface(sessionHistory, &rv);
9310 NS_ENSURE_SUCCESS(rv, rv);
9312 // Step 3: Create a new entry in the session history; this will erase
9313 // all SHEntries after the new entry and make this entry the current
9314 // one. This operation may modify mOSHE, which we need later, so we
9315 // keep a reference here.
9316 NS_ENSURE_TRUE(mOSHE, NS_ERROR_FAILURE);
9317 nsCOMPtr<nsISHEntry> oldOSHE = mOSHE;
9319 nsCOMPtr<nsISHEntry> newSHEntry;
9320 if (!aReplace) {
9321 rv = AddToSessionHistory(newURI, nsnull, nsnull,
9322 getter_AddRefs(newSHEntry));
9323 NS_ENSURE_SUCCESS(rv, rv);
9325 NS_ENSURE_TRUE(newSHEntry, NS_ERROR_FAILURE);
9327 // Set the new SHEntry's document identifier, if we can.
9328 PRUint64 ourDocIdent;
9329 NS_ENSURE_SUCCESS(oldOSHE->GetDocIdentifier(&ourDocIdent),
9330 NS_ERROR_FAILURE);
9331 NS_ENSURE_SUCCESS(newSHEntry->SetDocIdentifier(ourDocIdent),
9332 NS_ERROR_FAILURE);
9334 // AddToSessionHistory may not modify mOSHE. In case it doesn't,
9335 // we'll just set mOSHE here.
9336 mOSHE = newSHEntry;
9338 } else {
9339 newSHEntry = mOSHE;
9340 newSHEntry->SetURI(newURI);
9343 // Step 4: Modify new/original session history entry
9344 newSHEntry->SetStateData(dataStr);
9346 // Step 5: If aReplace is false, indicating that we're doing a pushState
9347 // rather than a replaceState, notify bfcache that we've added a page to
9348 // the history so it can evict content viewers if appropriate.
9349 if (!aReplace) {
9350 nsCOMPtr<nsISHistory> rootSH;
9351 GetRootSessionHistory(getter_AddRefs(rootSH));
9352 NS_ENSURE_TRUE(rootSH, NS_ERROR_UNEXPECTED);
9354 nsCOMPtr<nsISHistoryInternal> internalSH =
9355 do_QueryInterface(rootSH);
9356 NS_ENSURE_TRUE(internalSH, NS_ERROR_UNEXPECTED);
9358 PRInt32 curIndex = -1;
9359 rv = rootSH->GetIndex(&curIndex);
9360 if (NS_SUCCEEDED(rv) && curIndex > -1) {
9361 internalSH->EvictContentViewers(curIndex - 1, curIndex);
9365 // Step 6: If the document's URI changed, update document's URI and update
9366 // global history.
9368 // We need to call FireOnLocationChange so that the browser's address bar
9369 // gets updated and the back button is enabled, but we only need to
9370 // explicitly call FireOnLocationChange if we're not calling SetCurrentURI,
9371 // since SetCurrentURI will call FireOnLocationChange for us.
9372 if (!equalURIs) {
9373 SetCurrentURI(newURI, nsnull, PR_TRUE);
9374 document->SetDocumentURI(newURI);
9376 AddToGlobalHistory(newURI, PR_FALSE, oldURI);
9378 else {
9379 FireOnLocationChange(this, nsnull, mCurrentURI);
9382 // Try to set the title of the current history element
9383 if (mOSHE)
9384 mOSHE->SetTitle(aTitle);
9386 return NS_OK;
9389 PRBool
9390 nsDocShell::ShouldAddToSessionHistory(nsIURI * aURI)
9392 // I believe none of the about: urls should go in the history. But then
9393 // that could just be me... If the intent is only deny about:blank then we
9394 // should just do a spec compare, rather than two gets of the scheme and
9395 // then the path. -Gagan
9396 nsresult rv;
9397 nsCAutoString buf;
9399 rv = aURI->GetScheme(buf);
9400 if (NS_FAILED(rv))
9401 return PR_FALSE;
9403 if (buf.Equals("about")) {
9404 rv = aURI->GetPath(buf);
9405 if (NS_FAILED(rv))
9406 return PR_FALSE;
9408 if (buf.Equals("blank")) {
9409 return PR_FALSE;
9412 return PR_TRUE;
9415 nsresult
9416 nsDocShell::AddToSessionHistory(nsIURI * aURI, nsIChannel * aChannel,
9417 nsISupports* aOwner, nsISHEntry ** aNewEntry)
9419 NS_PRECONDITION(aURI, "uri is null");
9420 NS_PRECONDITION(!aChannel || !aOwner, "Shouldn't have both set");
9422 #if defined(PR_LOGGING) && defined(DEBUG)
9423 if (PR_LOG_TEST(gDocShellLog, PR_LOG_DEBUG)) {
9424 nsCAutoString spec;
9425 aURI->GetSpec(spec);
9427 nsCAutoString chanName;
9428 if (aChannel)
9429 aChannel->GetName(chanName);
9430 else
9431 chanName.AssignLiteral("<no channel>");
9433 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
9434 ("nsDocShell[%p]::AddToSessionHistory(\"%s\", [%s])\n", this, spec.get(),
9435 chanName.get()));
9437 #endif
9439 nsresult rv = NS_OK;
9440 nsCOMPtr<nsISHEntry> entry;
9441 PRBool shouldPersist;
9443 shouldPersist = ShouldAddToSessionHistory(aURI);
9445 // Get a handle to the root docshell
9446 nsCOMPtr<nsIDocShellTreeItem> root;
9447 GetSameTypeRootTreeItem(getter_AddRefs(root));
9449 * If this is a LOAD_FLAGS_REPLACE_HISTORY in a subframe, we use
9450 * the existing SH entry in the page and replace the url and
9451 * other vitalities.
9453 if (LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY) &&
9454 root != static_cast<nsIDocShellTreeItem *>(this)) {
9455 // This is a subframe
9456 entry = mOSHE;
9457 nsCOMPtr<nsISHContainer> shContainer(do_QueryInterface(entry));
9458 if (shContainer) {
9459 PRInt32 childCount = 0;
9460 shContainer->GetChildCount(&childCount);
9461 // Remove all children of this entry
9462 for (PRInt32 i = childCount - 1; i >= 0; i--) {
9463 nsCOMPtr<nsISHEntry> child;
9464 shContainer->GetChildAt(i, getter_AddRefs(child));
9465 shContainer->RemoveChild(child);
9466 } // for
9467 } // shContainer
9470 // Create a new entry if necessary.
9471 if (!entry) {
9472 entry = do_CreateInstance(NS_SHENTRY_CONTRACTID);
9474 if (!entry) {
9475 return NS_ERROR_OUT_OF_MEMORY;
9479 // Get the post data & referrer
9480 nsCOMPtr<nsIInputStream> inputStream;
9481 nsCOMPtr<nsIURI> referrerURI;
9482 nsCOMPtr<nsISupports> cacheKey;
9483 nsCOMPtr<nsISupports> cacheToken;
9484 nsCOMPtr<nsISupports> owner = aOwner;
9485 PRBool expired = PR_FALSE;
9486 PRBool discardLayoutState = PR_FALSE;
9487 if (aChannel) {
9488 nsCOMPtr<nsICachingChannel>
9489 cacheChannel(do_QueryInterface(aChannel));
9490 /* If there is a caching channel, get the Cache Key and store it
9491 * in SH.
9493 if (cacheChannel) {
9494 cacheChannel->GetCacheKey(getter_AddRefs(cacheKey));
9495 cacheChannel->GetCacheToken(getter_AddRefs(cacheToken));
9497 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
9499 // Check if the httpChannel is hiding under a multipartChannel
9500 if (!httpChannel) {
9501 GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
9503 if (httpChannel) {
9504 nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
9505 if (uploadChannel) {
9506 uploadChannel->GetUploadStream(getter_AddRefs(inputStream));
9508 httpChannel->GetReferrer(getter_AddRefs(referrerURI));
9510 discardLayoutState = ShouldDiscardLayoutState(httpChannel);
9512 aChannel->GetOwner(getter_AddRefs(owner));
9515 //Title is set in nsDocShell::SetTitle()
9516 entry->Create(aURI, // uri
9517 EmptyString(), // Title
9518 inputStream, // Post data stream
9519 nsnull, // LayoutHistory state
9520 cacheKey, // CacheKey
9521 mContentTypeHint, // Content-type
9522 owner); // Channel or provided owner
9523 entry->SetReferrerURI(referrerURI);
9524 /* If cache got a 'no-store', ask SH not to store
9525 * HistoryLayoutState. By default, SH will set this
9526 * flag to PR_TRUE and save HistoryLayoutState.
9528 if (discardLayoutState) {
9529 entry->SetSaveLayoutStateFlag(PR_FALSE);
9531 if (cacheToken) {
9532 // Check if the page has expired from cache
9533 nsCOMPtr<nsICacheEntryInfo> cacheEntryInfo(do_QueryInterface(cacheToken));
9534 if (cacheEntryInfo) {
9535 PRUint32 expTime;
9536 cacheEntryInfo->GetExpirationTime(&expTime);
9537 PRUint32 now = PRTimeToSeconds(PR_Now());
9538 if (expTime <= now)
9539 expired = PR_TRUE;
9543 if (expired)
9544 entry->SetExpirationStatus(PR_TRUE);
9547 if (root == static_cast<nsIDocShellTreeItem *>(this) && mSessionHistory) {
9548 // This is the root docshell
9549 if (LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY)) {
9550 // Replace current entry in session history.
9551 PRInt32 index = 0;
9552 mSessionHistory->GetIndex(&index);
9553 nsCOMPtr<nsISHistoryInternal> shPrivate(do_QueryInterface(mSessionHistory));
9554 // Replace the current entry with the new entry
9555 if (shPrivate)
9556 rv = shPrivate->ReplaceEntry(index, entry);
9558 else {
9559 // Add to session history
9560 nsCOMPtr<nsISHistoryInternal>
9561 shPrivate(do_QueryInterface(mSessionHistory));
9562 NS_ENSURE_TRUE(shPrivate, NS_ERROR_FAILURE);
9563 mSessionHistory->GetIndex(&mPreviousTransIndex);
9564 rv = shPrivate->AddEntry(entry, shouldPersist);
9565 mSessionHistory->GetIndex(&mLoadedTransIndex);
9566 #ifdef DEBUG_PAGE_CACHE
9567 printf("Previous index: %d, Loaded index: %d\n\n",
9568 mPreviousTransIndex, mLoadedTransIndex);
9569 #endif
9572 else {
9573 // This is a subframe.
9574 if (!mOSHE || !LOAD_TYPE_HAS_FLAGS(mLoadType,
9575 LOAD_FLAGS_REPLACE_HISTORY))
9576 rv = DoAddChildSHEntry(entry, mChildOffset);
9579 // Return the new SH entry...
9580 if (aNewEntry) {
9581 *aNewEntry = nsnull;
9582 if (NS_SUCCEEDED(rv)) {
9583 *aNewEntry = entry;
9584 NS_ADDREF(*aNewEntry);
9588 return rv;
9592 NS_IMETHODIMP
9593 nsDocShell::LoadHistoryEntry(nsISHEntry * aEntry, PRUint32 aLoadType)
9595 if (!IsNavigationAllowed()) {
9596 return NS_OK;
9599 nsCOMPtr<nsIURI> uri;
9600 nsCOMPtr<nsIInputStream> postData;
9601 nsCOMPtr<nsIURI> referrerURI;
9602 nsCAutoString contentType;
9603 nsCOMPtr<nsISupports> owner;
9605 NS_ENSURE_TRUE(aEntry, NS_ERROR_FAILURE);
9607 NS_ENSURE_SUCCESS(aEntry->GetURI(getter_AddRefs(uri)), NS_ERROR_FAILURE);
9608 NS_ENSURE_SUCCESS(aEntry->GetReferrerURI(getter_AddRefs(referrerURI)),
9609 NS_ERROR_FAILURE);
9610 NS_ENSURE_SUCCESS(aEntry->GetPostData(getter_AddRefs(postData)),
9611 NS_ERROR_FAILURE);
9612 NS_ENSURE_SUCCESS(aEntry->GetContentType(contentType), NS_ERROR_FAILURE);
9613 NS_ENSURE_SUCCESS(aEntry->GetOwner(getter_AddRefs(owner)),
9614 NS_ERROR_FAILURE);
9616 // Calling CreateAboutBlankContentViewer can set mOSHE to null, and if
9617 // that's the only thing holding a ref to aEntry that will cause aEntry to
9618 // die while we're loading it. So hold a strong ref to aEntry here, just
9619 // in case.
9620 nsCOMPtr<nsISHEntry> kungFuDeathGrip(aEntry);
9621 PRBool isJS;
9622 nsresult rv = uri->SchemeIs("javascript", &isJS);
9623 if (NS_FAILED(rv) || isJS) {
9624 // We're loading a URL that will execute script from inside asyncOpen.
9625 // Replace the current document with about:blank now to prevent
9626 // anything from the current document from leaking into any JavaScript
9627 // code in the URL.
9628 nsCOMPtr<nsIPrincipal> prin = do_QueryInterface(owner);
9629 rv = CreateAboutBlankContentViewer(prin, nsnull);
9631 if (NS_FAILED(rv)) {
9632 // The creation of the intermittent about:blank content
9633 // viewer failed for some reason (potentially because the
9634 // user prevented it). Interrupt the history load.
9635 return NS_OK;
9638 if (!owner) {
9639 // Ensure that we have an owner. Otherwise javascript: URIs will
9640 // pick it up from the about:blank page we just loaded, and we
9641 // don't really want even that in this case.
9642 owner = do_CreateInstance("@mozilla.org/nullprincipal;1");
9643 NS_ENSURE_TRUE(owner, NS_ERROR_OUT_OF_MEMORY);
9647 /* If there is a valid postdata *and* the user pressed
9648 * reload or shift-reload, take user's permission before we
9649 * repost the data to the server.
9651 if ((aLoadType & LOAD_CMD_RELOAD) && postData) {
9652 PRBool repost;
9653 rv = ConfirmRepost(&repost);
9654 if (NS_FAILED(rv)) return rv;
9656 // If the user pressed cancel in the dialog, return. We're done here.
9657 if (!repost)
9658 return NS_BINDING_ABORTED;
9661 rv = InternalLoad(uri,
9662 referrerURI,
9663 owner,
9664 INTERNAL_LOAD_FLAGS_NONE, // Do not inherit owner from document (security-critical!)
9665 nsnull, // No window target
9666 contentType.get(), // Type hint
9667 postData, // Post data stream
9668 nsnull, // No headers stream
9669 aLoadType, // Load type
9670 aEntry, // SHEntry
9671 PR_TRUE,
9672 nsnull, // No nsIDocShell
9673 nsnull); // No nsIRequest
9674 return rv;
9677 NS_IMETHODIMP nsDocShell::GetShouldSaveLayoutState(PRBool* aShould)
9679 *aShould = PR_FALSE;
9680 if (mOSHE) {
9681 // Don't capture historystate and save it in history
9682 // if the page asked not to do so.
9683 mOSHE->GetSaveLayoutStateFlag(aShould);
9686 return NS_OK;
9689 NS_IMETHODIMP nsDocShell::PersistLayoutHistoryState()
9691 nsresult rv = NS_OK;
9693 if (mOSHE) {
9694 nsCOMPtr<nsIPresShell> shell;
9695 rv = GetPresShell(getter_AddRefs(shell));
9696 if (NS_SUCCEEDED(rv) && shell) {
9697 nsCOMPtr<nsILayoutHistoryState> layoutState;
9698 rv = shell->CaptureHistoryState(getter_AddRefs(layoutState),
9699 PR_TRUE);
9703 return rv;
9706 /* static */ nsresult
9707 nsDocShell::WalkHistoryEntries(nsISHEntry *aRootEntry,
9708 nsDocShell *aRootShell,
9709 WalkHistoryEntriesFunc aCallback,
9710 void *aData)
9712 NS_ENSURE_TRUE(aRootEntry, NS_ERROR_FAILURE);
9714 nsCOMPtr<nsISHContainer> container(do_QueryInterface(aRootEntry));
9715 if (!container)
9716 return NS_ERROR_FAILURE;
9718 PRInt32 childCount;
9719 container->GetChildCount(&childCount);
9720 for (PRInt32 i = 0; i < childCount; i++) {
9721 nsCOMPtr<nsISHEntry> childEntry;
9722 container->GetChildAt(i, getter_AddRefs(childEntry));
9723 if (!childEntry) {
9724 // childEntry can be null for valid reasons, for example if the
9725 // docshell at index i never loaded anything useful.
9726 continue;
9729 nsDocShell *childShell = nsnull;
9730 if (aRootShell) {
9731 // Walk the children of aRootShell and see if one of them
9732 // has srcChild as a SHEntry.
9734 PRInt32 childCount = aRootShell->mChildList.Count();
9735 for (PRInt32 j = 0; j < childCount; ++j) {
9736 nsDocShell *child =
9737 static_cast<nsDocShell*>(aRootShell->ChildAt(j));
9739 if (child->HasHistoryEntry(childEntry)) {
9740 childShell = child;
9741 break;
9745 nsresult rv = aCallback(childEntry, childShell, i, aData);
9746 NS_ENSURE_SUCCESS(rv, rv);
9749 return NS_OK;
9752 // callback data for WalkHistoryEntries
9753 struct NS_STACK_CLASS CloneAndReplaceData
9755 CloneAndReplaceData(PRUint32 aCloneID, nsISHEntry *aReplaceEntry,
9756 nsISHEntry *aDestTreeParent)
9757 : cloneID(aCloneID),
9758 replaceEntry(aReplaceEntry),
9759 destTreeParent(aDestTreeParent) { }
9761 PRUint32 cloneID;
9762 nsISHEntry *replaceEntry;
9763 nsISHEntry *destTreeParent;
9764 nsCOMPtr<nsISHEntry> resultEntry;
9767 /* static */ nsresult
9768 nsDocShell::CloneAndReplaceChild(nsISHEntry *aEntry, nsDocShell *aShell,
9769 PRInt32 aEntryIndex, void *aData)
9771 nsresult result = NS_OK;
9772 nsCOMPtr<nsISHEntry> dest;
9774 CloneAndReplaceData *data = static_cast<CloneAndReplaceData*>(aData);
9775 PRUint32 cloneID = data->cloneID;
9776 nsISHEntry *replaceEntry = data->replaceEntry;
9778 PRUint32 srcID;
9779 aEntry->GetID(&srcID);
9781 if (srcID == cloneID) {
9782 // Just replace the entry, and don't walk the children.
9783 dest = replaceEntry;
9784 dest->SetIsSubFrame(PR_TRUE);
9785 } else {
9786 // Clone the SHEntry...
9787 result = aEntry->Clone(getter_AddRefs(dest));
9788 if (NS_FAILED(result))
9789 return result;
9791 // This entry is for a subframe navigation
9792 dest->SetIsSubFrame(PR_TRUE);
9794 // Walk the children
9795 CloneAndReplaceData childData(cloneID, replaceEntry, dest);
9796 result = WalkHistoryEntries(aEntry, aShell,
9797 CloneAndReplaceChild, &childData);
9798 if (NS_FAILED(result))
9799 return result;
9801 if (aShell)
9802 aShell->SwapHistoryEntries(aEntry, dest);
9805 nsCOMPtr<nsISHContainer> container =
9806 do_QueryInterface(data->destTreeParent);
9807 if (container)
9808 container->AddChild(dest, aEntryIndex);
9810 data->resultEntry = dest;
9811 return result;
9814 /* static */ nsresult
9815 nsDocShell::CloneAndReplace(nsISHEntry *aSrcEntry,
9816 nsDocShell *aSrcShell,
9817 PRUint32 aCloneID,
9818 nsISHEntry *aReplaceEntry,
9819 nsISHEntry **aResultEntry)
9821 NS_ENSURE_ARG_POINTER(aResultEntry);
9822 NS_ENSURE_TRUE(aReplaceEntry, NS_ERROR_FAILURE);
9824 CloneAndReplaceData data(aCloneID, aReplaceEntry, nsnull);
9825 nsresult rv = CloneAndReplaceChild(aSrcEntry, aSrcShell, 0, &data);
9827 data.resultEntry.swap(*aResultEntry);
9828 return rv;
9831 void
9832 nsDocShell::SwapHistoryEntries(nsISHEntry *aOldEntry, nsISHEntry *aNewEntry)
9834 if (aOldEntry == mOSHE)
9835 mOSHE = aNewEntry;
9837 if (aOldEntry == mLSHE)
9838 mLSHE = aNewEntry;
9842 struct SwapEntriesData
9844 nsDocShell *ignoreShell; // constant; the shell to ignore
9845 nsISHEntry *destTreeRoot; // constant; the root of the dest tree
9846 nsISHEntry *destTreeParent; // constant; the node under destTreeRoot
9847 // whose children will correspond to aEntry
9851 nsresult
9852 nsDocShell::SetChildHistoryEntry(nsISHEntry *aEntry, nsDocShell *aShell,
9853 PRInt32 aEntryIndex, void *aData)
9855 SwapEntriesData *data = static_cast<SwapEntriesData*>(aData);
9856 nsDocShell *ignoreShell = data->ignoreShell;
9858 if (!aShell || aShell == ignoreShell)
9859 return NS_OK;
9861 nsISHEntry *destTreeRoot = data->destTreeRoot;
9863 nsCOMPtr<nsISHEntry> destEntry;
9864 nsCOMPtr<nsISHContainer> container =
9865 do_QueryInterface(data->destTreeParent);
9867 if (container) {
9868 // aEntry is a clone of some child of destTreeParent, but since the
9869 // trees aren't necessarily in sync, we'll have to locate it.
9870 // Note that we could set aShell's entry to null if we don't find a
9871 // corresponding entry under destTreeParent.
9873 PRUint32 targetID, id;
9874 aEntry->GetID(&targetID);
9876 // First look at the given index, since this is the common case.
9877 nsCOMPtr<nsISHEntry> entry;
9878 container->GetChildAt(aEntryIndex, getter_AddRefs(entry));
9879 if (entry && NS_SUCCEEDED(entry->GetID(&id)) && id == targetID) {
9880 destEntry.swap(entry);
9881 } else {
9882 PRInt32 childCount;
9883 container->GetChildCount(&childCount);
9884 for (PRInt32 i = 0; i < childCount; ++i) {
9885 container->GetChildAt(i, getter_AddRefs(entry));
9886 if (!entry)
9887 continue;
9889 entry->GetID(&id);
9890 if (id == targetID) {
9891 destEntry.swap(entry);
9892 break;
9896 } else {
9897 destEntry = destTreeRoot;
9900 aShell->SwapHistoryEntries(aEntry, destEntry);
9902 // Now handle the children of aEntry.
9903 SwapEntriesData childData = { ignoreShell, destTreeRoot, destEntry };
9904 return WalkHistoryEntries(aEntry, aShell,
9905 SetChildHistoryEntry, &childData);
9909 static nsISHEntry*
9910 GetRootSHEntry(nsISHEntry *aEntry)
9912 nsCOMPtr<nsISHEntry> rootEntry = aEntry;
9913 nsISHEntry *result = nsnull;
9914 while (rootEntry) {
9915 result = rootEntry;
9916 result->GetParent(getter_AddRefs(rootEntry));
9919 return result;
9923 void
9924 nsDocShell::SetHistoryEntry(nsCOMPtr<nsISHEntry> *aPtr, nsISHEntry *aEntry)
9926 // We need to sync up the docshell and session history trees for
9927 // subframe navigation. If the load was in a subframe, we forward up to
9928 // the root docshell, which will then recursively sync up all docshells
9929 // to their corresponding entries in the new session history tree.
9930 // If we don't do this, then we can cache a content viewer on the wrong
9931 // cloned entry, and subsequently restore it at the wrong time.
9933 nsISHEntry *newRootEntry = GetRootSHEntry(aEntry);
9934 if (newRootEntry) {
9935 // newRootEntry is now the new root entry.
9936 // Find the old root entry as well.
9938 // Need a strong ref. on |oldRootEntry| so it isn't destroyed when
9939 // SetChildHistoryEntry() does SwapHistoryEntries() (bug 304639).
9940 nsCOMPtr<nsISHEntry> oldRootEntry = GetRootSHEntry(*aPtr);
9941 if (oldRootEntry) {
9942 nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
9943 GetSameTypeParent(getter_AddRefs(parentAsItem));
9944 nsCOMPtr<nsIDocShell> rootShell = do_QueryInterface(parentAsItem);
9945 if (rootShell) { // if we're the root just set it, nothing to swap
9946 SwapEntriesData data = { this, newRootEntry };
9947 nsIDocShell *rootIDocShell =
9948 static_cast<nsIDocShell*>(rootShell);
9949 nsDocShell *rootDocShell = static_cast<nsDocShell*>
9950 (rootIDocShell);
9952 #ifdef NS_DEBUG
9953 nsresult rv =
9954 #endif
9955 SetChildHistoryEntry(oldRootEntry, rootDocShell,
9956 0, &data);
9957 NS_ASSERTION(NS_SUCCEEDED(rv), "SetChildHistoryEntry failed");
9962 *aPtr = aEntry;
9966 nsresult
9967 nsDocShell::GetRootSessionHistory(nsISHistory ** aReturn)
9969 nsresult rv;
9971 nsCOMPtr<nsIDocShellTreeItem> root;
9972 //Get the root docshell
9973 rv = GetSameTypeRootTreeItem(getter_AddRefs(root));
9974 // QI to nsIWebNavigation
9975 nsCOMPtr<nsIWebNavigation> rootAsWebnav(do_QueryInterface(root));
9976 if (rootAsWebnav) {
9977 // Get the handle to SH from the root docshell
9978 rv = rootAsWebnav->GetSessionHistory(aReturn);
9980 return rv;
9983 nsresult
9984 nsDocShell::GetHttpChannel(nsIChannel * aChannel, nsIHttpChannel ** aReturn)
9986 NS_ENSURE_ARG_POINTER(aReturn);
9987 if (!aChannel)
9988 return NS_ERROR_FAILURE;
9990 nsCOMPtr<nsIMultiPartChannel> multiPartChannel(do_QueryInterface(aChannel));
9991 if (multiPartChannel) {
9992 nsCOMPtr<nsIChannel> baseChannel;
9993 multiPartChannel->GetBaseChannel(getter_AddRefs(baseChannel));
9994 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(baseChannel));
9995 *aReturn = httpChannel;
9996 NS_IF_ADDREF(*aReturn);
9998 return NS_OK;
10001 PRBool
10002 nsDocShell::ShouldDiscardLayoutState(nsIHttpChannel * aChannel)
10004 // By default layout State will be saved.
10005 if (!aChannel)
10006 return PR_FALSE;
10008 // figure out if SH should be saving layout state
10009 nsCOMPtr<nsISupports> securityInfo;
10010 PRBool noStore = PR_FALSE, noCache = PR_FALSE;
10011 aChannel->GetSecurityInfo(getter_AddRefs(securityInfo));
10012 aChannel->IsNoStoreResponse(&noStore);
10013 aChannel->IsNoCacheResponse(&noCache);
10015 return (noStore || (noCache && securityInfo));
10018 //*****************************************************************************
10019 // nsDocShell: nsIEditorDocShell
10020 //*****************************************************************************
10022 NS_IMETHODIMP nsDocShell::GetEditor(nsIEditor * *aEditor)
10024 NS_ENSURE_ARG_POINTER(aEditor);
10026 if (!mEditorData) {
10027 *aEditor = nsnull;
10028 return NS_OK;
10031 return mEditorData->GetEditor(aEditor);
10034 NS_IMETHODIMP nsDocShell::SetEditor(nsIEditor * aEditor)
10036 nsresult rv = EnsureEditorData();
10037 if (NS_FAILED(rv)) return rv;
10039 return mEditorData->SetEditor(aEditor);
10043 NS_IMETHODIMP nsDocShell::GetEditable(PRBool *aEditable)
10045 NS_ENSURE_ARG_POINTER(aEditable);
10046 *aEditable = mEditorData && mEditorData->GetEditable();
10047 return NS_OK;
10051 NS_IMETHODIMP nsDocShell::GetHasEditingSession(PRBool *aHasEditingSession)
10053 NS_ENSURE_ARG_POINTER(aHasEditingSession);
10055 if (mEditorData)
10057 nsCOMPtr<nsIEditingSession> editingSession;
10058 mEditorData->GetEditingSession(getter_AddRefs(editingSession));
10059 *aHasEditingSession = (editingSession.get() != nsnull);
10061 else
10063 *aHasEditingSession = PR_FALSE;
10066 return NS_OK;
10069 NS_IMETHODIMP nsDocShell::MakeEditable(PRBool inWaitForUriLoad)
10071 nsresult rv = EnsureEditorData();
10072 if (NS_FAILED(rv)) return rv;
10074 return mEditorData->MakeEditable(inWaitForUriLoad);
10077 nsresult
10078 nsDocShell::AddToGlobalHistory(nsIURI * aURI, PRBool aRedirect,
10079 nsIChannel * aChannel)
10081 // If this is a POST request, we do not want to include this in global
10082 // history, so return early.
10083 nsCOMPtr<nsIHttpChannel> hchan(do_QueryInterface(aChannel));
10084 if (hchan) {
10085 nsCAutoString type;
10086 nsresult rv = hchan->GetRequestMethod(type);
10087 if (NS_SUCCEEDED(rv) && type.EqualsLiteral("POST"))
10088 return NS_OK;
10091 nsCOMPtr<nsIURI> referrer;
10092 if (aChannel)
10093 NS_GetReferrerFromChannel(aChannel, getter_AddRefs(referrer));
10095 return AddToGlobalHistory(aURI, aRedirect, referrer);
10098 nsresult
10099 nsDocShell::AddToGlobalHistory(nsIURI * aURI, PRBool aRedirect,
10100 nsIURI * aReferrer)
10102 if (mItemType != typeContent || !mGlobalHistory)
10103 return NS_OK;
10105 PRBool visited;
10106 nsresult rv = mGlobalHistory->IsVisited(aURI, &visited);
10107 if (NS_FAILED(rv))
10108 return rv;
10110 rv = mGlobalHistory->AddURI(aURI, aRedirect, !IsFrame(), aReferrer);
10111 if (NS_FAILED(rv))
10112 return rv;
10114 if (!visited) {
10115 nsCOMPtr<nsIObserverService> obsService =
10116 mozilla::services::GetObserverService();
10117 if (obsService) {
10118 obsService->NotifyObservers(aURI, NS_LINK_VISITED_EVENT_TOPIC, nsnull);
10122 return NS_OK;
10126 //*****************************************************************************
10127 // nsDocShell: Helper Routines
10128 //*****************************************************************************
10130 NS_IMETHODIMP
10131 nsDocShell::SetLoadType(PRUint32 aLoadType)
10133 mLoadType = aLoadType;
10134 return NS_OK;
10137 NS_IMETHODIMP
10138 nsDocShell::GetLoadType(PRUint32 * aLoadType)
10140 *aLoadType = mLoadType;
10141 return NS_OK;
10144 nsresult
10145 nsDocShell::ConfirmRepost(PRBool * aRepost)
10147 nsCOMPtr<nsIPrompt> prompter;
10148 CallGetInterface(this, static_cast<nsIPrompt**>(getter_AddRefs(prompter)));
10150 nsCOMPtr<nsIStringBundleService> stringBundleService =
10151 mozilla::services::GetStringBundleService();
10152 if (!stringBundleService)
10153 return NS_ERROR_FAILURE;
10155 nsCOMPtr<nsIStringBundle> appBundle;
10156 nsresult rv = stringBundleService->CreateBundle(kAppstringsBundleURL,
10157 getter_AddRefs(appBundle));
10158 NS_ENSURE_SUCCESS(rv, rv);
10160 nsCOMPtr<nsIStringBundle> brandBundle;
10161 rv = stringBundleService->CreateBundle(kBrandBundleURL, getter_AddRefs(brandBundle));
10162 NS_ENSURE_SUCCESS(rv, rv);
10164 NS_ASSERTION(prompter && brandBundle && appBundle,
10165 "Unable to set up repost prompter.");
10167 nsXPIDLString brandName;
10168 rv = brandBundle->GetStringFromName(NS_LITERAL_STRING("brandShortName").get(),
10169 getter_Copies(brandName));
10171 nsXPIDLString msgString, button0Title;
10172 if (NS_FAILED(rv)) { // No brand, use the generic version.
10173 rv = appBundle->GetStringFromName(NS_LITERAL_STRING("confirmRepostPrompt").get(),
10174 getter_Copies(msgString));
10176 else {
10177 // Brand available - if the app has an override file with formatting, the app name will
10178 // be included. Without an override, the prompt will look like the generic version.
10179 const PRUnichar *formatStrings[] = { brandName.get() };
10180 rv = appBundle->FormatStringFromName(NS_LITERAL_STRING("confirmRepostPrompt").get(),
10181 formatStrings, NS_ARRAY_LENGTH(formatStrings),
10182 getter_Copies(msgString));
10184 if (NS_FAILED(rv)) return rv;
10186 rv = appBundle->GetStringFromName(NS_LITERAL_STRING("resendButton.label").get(),
10187 getter_Copies(button0Title));
10188 if (NS_FAILED(rv)) return rv;
10190 PRInt32 buttonPressed;
10191 PRBool checkState;
10192 rv = prompter->
10193 ConfirmEx(nsnull, msgString.get(),
10194 (nsIPrompt::BUTTON_POS_0 * nsIPrompt::BUTTON_TITLE_IS_STRING) +
10195 (nsIPrompt::BUTTON_POS_1 * nsIPrompt::BUTTON_TITLE_CANCEL),
10196 button0Title.get(), nsnull, nsnull, nsnull, &checkState, &buttonPressed);
10197 if (NS_FAILED(rv)) return rv;
10199 *aRepost = (buttonPressed == 0);
10200 return NS_OK;
10203 NS_IMETHODIMP
10204 nsDocShell::GetPromptAndStringBundle(nsIPrompt ** aPrompt,
10205 nsIStringBundle ** aStringBundle)
10207 NS_ENSURE_SUCCESS(GetInterface(NS_GET_IID(nsIPrompt), (void **) aPrompt),
10208 NS_ERROR_FAILURE);
10210 nsCOMPtr<nsIStringBundleService> stringBundleService =
10211 mozilla::services::GetStringBundleService();
10212 NS_ENSURE_TRUE(stringBundleService, NS_ERROR_FAILURE);
10214 NS_ENSURE_SUCCESS(stringBundleService->
10215 CreateBundle(kAppstringsBundleURL,
10216 aStringBundle),
10217 NS_ERROR_FAILURE);
10219 return NS_OK;
10222 NS_IMETHODIMP
10223 nsDocShell::GetChildOffset(nsIDOMNode * aChild, nsIDOMNode * aParent,
10224 PRInt32 * aOffset)
10226 NS_ENSURE_ARG_POINTER(aChild || aParent);
10228 nsCOMPtr<nsIDOMNodeList> childNodes;
10229 NS_ENSURE_SUCCESS(aParent->GetChildNodes(getter_AddRefs(childNodes)),
10230 NS_ERROR_FAILURE);
10231 NS_ENSURE_TRUE(childNodes, NS_ERROR_FAILURE);
10233 PRInt32 i = 0;
10235 for (; PR_TRUE; i++) {
10236 nsCOMPtr<nsIDOMNode> childNode;
10237 NS_ENSURE_SUCCESS(childNodes->Item(i, getter_AddRefs(childNode)),
10238 NS_ERROR_FAILURE);
10239 NS_ENSURE_TRUE(childNode, NS_ERROR_FAILURE);
10241 if (childNode.get() == aChild) {
10242 *aOffset = i;
10243 return NS_OK;
10247 return NS_ERROR_FAILURE;
10250 nsIScrollableFrame *
10251 nsDocShell::GetRootScrollFrame()
10253 nsCOMPtr<nsIPresShell> shell;
10254 NS_ENSURE_SUCCESS(GetPresShell(getter_AddRefs(shell)), nsnull);
10255 NS_ENSURE_TRUE(shell, nsnull);
10257 return shell->GetRootScrollFrameAsScrollableExternal();
10260 #ifdef DEBUG
10261 class nsDebugAutoBoolTrueSetter
10263 public:
10264 nsDebugAutoBoolTrueSetter(PRPackedBool *aBool)
10265 : mBool(aBool)
10267 *mBool = PR_TRUE;
10270 ~nsDebugAutoBoolTrueSetter()
10272 *mBool = PR_FALSE;
10274 protected:
10275 PRPackedBool *mBool;
10277 #endif
10279 NS_IMETHODIMP
10280 nsDocShell::EnsureScriptEnvironment()
10282 if (mScriptGlobal)
10283 return NS_OK;
10285 if (mIsBeingDestroyed) {
10286 return NS_ERROR_NOT_AVAILABLE;
10289 NS_TIME_FUNCTION;
10291 #ifdef DEBUG
10292 NS_ASSERTION(!mInEnsureScriptEnv,
10293 "Infinite loop! Calling EnsureScriptEnvironment() from "
10294 "within EnsureScriptEnvironment()!");
10296 // Yeah, this isn't re-entrant safe, but that's ok since if we
10297 // re-enter this method, we'll infinitely loop...
10298 nsDebugAutoBoolTrueSetter boolSetter(&mInEnsureScriptEnv);
10299 #endif
10301 nsCOMPtr<nsIDOMScriptObjectFactory> factory =
10302 do_GetService(kDOMScriptObjectFactoryCID);
10303 NS_ENSURE_TRUE(factory, NS_ERROR_FAILURE);
10305 nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(mTreeOwner));
10306 NS_ENSURE_TRUE(browserChrome, NS_ERROR_NOT_AVAILABLE);
10308 PRUint32 chromeFlags;
10309 browserChrome->GetChromeFlags(&chromeFlags);
10311 PRBool isModalContentWindow =
10312 (chromeFlags & nsIWebBrowserChrome::CHROME_MODAL) &&
10313 !(chromeFlags & nsIWebBrowserChrome::CHROME_OPENAS_CHROME);
10315 // If our window is modal and we're not opened as chrome, make
10316 // this window a modal content window.
10317 factory->NewScriptGlobalObject(mItemType == typeChrome,
10318 isModalContentWindow,
10319 getter_AddRefs(mScriptGlobal));
10320 NS_ENSURE_TRUE(mScriptGlobal, NS_ERROR_FAILURE);
10322 nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(mScriptGlobal));
10323 win->SetDocShell(static_cast<nsIDocShell *>(this));
10325 // Ensure the script object is set to run javascript - other languages
10326 // setup on demand.
10327 // XXXmarkh - should this be setup to run the default language for this doc?
10328 nsresult rv;
10329 rv = mScriptGlobal->EnsureScriptEnvironment(nsIProgrammingLanguage::JAVASCRIPT);
10330 NS_ENSURE_SUCCESS(rv, rv);
10332 return NS_OK;
10336 NS_IMETHODIMP
10337 nsDocShell::EnsureEditorData()
10339 PRBool openDocHasDetachedEditor = mOSHE && mOSHE->HasDetachedEditor();
10340 if (!mEditorData && !mIsBeingDestroyed && !openDocHasDetachedEditor) {
10341 // We shouldn't recreate the editor data if it already exists, or
10342 // we're shutting down, or we already have a detached editor data
10343 // stored in the session history. We should only have one editordata
10344 // per docshell.
10345 mEditorData = new nsDocShellEditorData(this);
10348 return mEditorData ? NS_OK : NS_ERROR_NOT_AVAILABLE;
10351 nsresult
10352 nsDocShell::EnsureTransferableHookData()
10354 if (!mTransferableHookData) {
10355 mTransferableHookData = new nsTransferableHookData();
10356 if (!mTransferableHookData) return NS_ERROR_OUT_OF_MEMORY;
10359 return NS_OK;
10363 NS_IMETHODIMP nsDocShell::EnsureFind()
10365 nsresult rv;
10366 if (!mFind)
10368 mFind = do_CreateInstance("@mozilla.org/embedcomp/find;1", &rv);
10369 if (NS_FAILED(rv)) return rv;
10372 // we promise that the nsIWebBrowserFind that we return has been set
10373 // up to point to the focused, or content window, so we have to
10374 // set that up each time.
10376 nsIScriptGlobalObject* scriptGO = GetScriptGlobalObject();
10377 NS_ENSURE_TRUE(scriptGO, NS_ERROR_UNEXPECTED);
10379 // default to our window
10380 nsCOMPtr<nsIDOMWindow> windowToSearch(do_QueryInterface(mScriptGlobal));
10382 nsCOMPtr<nsIDocShellTreeItem> root;
10383 GetRootTreeItem(getter_AddRefs(root));
10385 // if the active window is the same window that this docshell is in,
10386 // use the currently focused frame
10387 nsCOMPtr<nsIDOMWindow> rootWindow = do_GetInterface(root);
10388 nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID);
10389 if (fm) {
10390 nsCOMPtr<nsIDOMWindow> activeWindow;
10391 fm->GetActiveWindow(getter_AddRefs(activeWindow));
10392 if (activeWindow == rootWindow)
10393 fm->GetFocusedWindow(getter_AddRefs(windowToSearch));
10396 nsCOMPtr<nsIWebBrowserFindInFrames> findInFrames = do_QueryInterface(mFind);
10397 if (!findInFrames) return NS_ERROR_NO_INTERFACE;
10399 rv = findInFrames->SetRootSearchFrame(rootWindow);
10400 if (NS_FAILED(rv)) return rv;
10401 rv = findInFrames->SetCurrentSearchFrame(windowToSearch);
10402 if (NS_FAILED(rv)) return rv;
10404 return NS_OK;
10407 PRBool
10408 nsDocShell::IsFrame()
10410 nsCOMPtr<nsIDocShellTreeItem> parent =
10411 do_QueryInterface(GetAsSupports(mParent));
10412 if (parent) {
10413 PRInt32 parentType = ~mItemType; // Not us
10414 parent->GetItemType(&parentType);
10415 if (parentType == mItemType) // This is a frame
10416 return PR_TRUE;
10419 return PR_FALSE;
10422 /* boolean IsBeingDestroyed (); */
10423 NS_IMETHODIMP
10424 nsDocShell::IsBeingDestroyed(PRBool *aDoomed)
10426 NS_ENSURE_ARG(aDoomed);
10427 *aDoomed = mIsBeingDestroyed;
10428 return NS_OK;
10432 NS_IMETHODIMP
10433 nsDocShell::GetIsExecutingOnLoadHandler(PRBool *aResult)
10435 NS_ENSURE_ARG(aResult);
10436 *aResult = mIsExecutingOnLoadHandler;
10437 return NS_OK;
10440 NS_IMETHODIMP
10441 nsDocShell::GetLayoutHistoryState(nsILayoutHistoryState **aLayoutHistoryState)
10443 if (mOSHE)
10444 mOSHE->GetLayoutHistoryState(aLayoutHistoryState);
10445 return NS_OK;
10448 NS_IMETHODIMP
10449 nsDocShell::SetLayoutHistoryState(nsILayoutHistoryState *aLayoutHistoryState)
10451 if (mOSHE)
10452 mOSHE->SetLayoutHistoryState(aLayoutHistoryState);
10453 return NS_OK;
10456 //*****************************************************************************
10457 //*** nsRefreshTimer: Object Management
10458 //*****************************************************************************
10460 nsRefreshTimer::nsRefreshTimer()
10461 : mDelay(0), mRepeat(PR_FALSE), mMetaRefresh(PR_FALSE)
10465 nsRefreshTimer::~nsRefreshTimer()
10469 //*****************************************************************************
10470 // nsRefreshTimer::nsISupports
10471 //*****************************************************************************
10473 NS_IMPL_THREADSAFE_ADDREF(nsRefreshTimer)
10474 NS_IMPL_THREADSAFE_RELEASE(nsRefreshTimer)
10476 NS_INTERFACE_MAP_BEGIN(nsRefreshTimer)
10477 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITimerCallback)
10478 NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
10479 NS_INTERFACE_MAP_END_THREADSAFE
10481 ///*****************************************************************************
10482 // nsRefreshTimer::nsITimerCallback
10483 //******************************************************************************
10484 NS_IMETHODIMP
10485 nsRefreshTimer::Notify(nsITimer * aTimer)
10487 NS_ASSERTION(mDocShell, "DocShell is somehow null");
10489 if (mDocShell && aTimer) {
10490 // Get the delay count to determine load type
10491 PRUint32 delay = 0;
10492 aTimer->GetDelay(&delay);
10493 mDocShell->ForceRefreshURIFromTimer(mURI, delay, mMetaRefresh, aTimer);
10495 return NS_OK;
10498 //*****************************************************************************
10499 // nsDocShell::InterfaceRequestorProxy
10500 //*****************************************************************************
10501 nsDocShell::InterfaceRequestorProxy::InterfaceRequestorProxy(nsIInterfaceRequestor* p)
10503 if (p) {
10504 mWeakPtr = do_GetWeakReference(p);
10508 nsDocShell::InterfaceRequestorProxy::~InterfaceRequestorProxy()
10510 mWeakPtr = nsnull;
10513 NS_IMPL_THREADSAFE_ISUPPORTS1(nsDocShell::InterfaceRequestorProxy, nsIInterfaceRequestor)
10515 NS_IMETHODIMP
10516 nsDocShell::InterfaceRequestorProxy::GetInterface(const nsIID & aIID, void **aSink)
10518 NS_ENSURE_ARG_POINTER(aSink);
10519 nsCOMPtr<nsIInterfaceRequestor> ifReq = do_QueryReferent(mWeakPtr);
10520 if (ifReq) {
10521 return ifReq->GetInterface(aIID, aSink);
10523 *aSink = nsnull;
10524 return NS_NOINTERFACE;
10527 nsresult
10528 nsDocShell::SetBaseUrlForWyciwyg(nsIContentViewer * aContentViewer)
10530 if (!aContentViewer)
10531 return NS_ERROR_FAILURE;
10533 nsCOMPtr<nsIURI> baseURI;
10534 nsresult rv = NS_ERROR_NOT_AVAILABLE;
10536 if (sURIFixup)
10537 rv = sURIFixup->CreateExposableURI(mCurrentURI,
10538 getter_AddRefs(baseURI));
10540 // Get the current document and set the base uri
10541 if (baseURI) {
10542 nsIDocument* document = aContentViewer->GetDocument();
10543 if (document) {
10544 rv = document->SetBaseURI(baseURI);
10547 return rv;
10550 //*****************************************************************************
10551 // nsDocShell::nsIAuthPromptProvider
10552 //*****************************************************************************
10554 NS_IMETHODIMP
10555 nsDocShell::GetAuthPrompt(PRUint32 aPromptReason, const nsIID& iid,
10556 void** aResult)
10558 // a priority prompt request will override a false mAllowAuth setting
10559 PRBool priorityPrompt = (aPromptReason == PROMPT_PROXY);
10561 if (!mAllowAuth && !priorityPrompt)
10562 return NS_ERROR_NOT_AVAILABLE;
10564 // we're either allowing auth, or it's a proxy request
10565 nsresult rv;
10566 nsCOMPtr<nsIPromptFactory> wwatch =
10567 do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
10568 NS_ENSURE_SUCCESS(rv, rv);
10570 rv = EnsureScriptEnvironment();
10571 NS_ENSURE_SUCCESS(rv, rv);
10573 nsCOMPtr<nsIDOMWindow> window(do_QueryInterface(mScriptGlobal));
10575 // Get the an auth prompter for our window so that the parenting
10576 // of the dialogs works as it should when using tabs.
10578 return wwatch->GetPrompt(window, iid,
10579 reinterpret_cast<void**>(aResult));
10582 //*****************************************************************************
10583 // nsDocShell::nsIObserver
10584 //*****************************************************************************
10586 NS_IMETHODIMP
10587 nsDocShell::Observe(nsISupports *aSubject, const char *aTopic,
10588 const PRUnichar *aData)
10590 nsresult rv = NS_OK;
10591 if (mObserveErrorPages &&
10592 !nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) &&
10593 !nsCRT::strcmp(aData,
10594 NS_LITERAL_STRING("browser.xul.error_pages.enabled").get())) {
10596 nsCOMPtr<nsIPrefBranch> prefs(do_QueryInterface(aSubject, &rv));
10597 NS_ENSURE_SUCCESS(rv, rv);
10599 PRBool tmpbool;
10600 rv = prefs->GetBoolPref("browser.xul.error_pages.enabled", &tmpbool);
10601 if (NS_SUCCEEDED(rv))
10602 mUseErrorPages = tmpbool;
10604 } else {
10605 rv = NS_ERROR_UNEXPECTED;
10607 return rv;
10610 //*****************************************************************************
10611 // nsDocShell::nsILoadContext
10612 //*****************************************************************************
10613 NS_IMETHODIMP
10614 nsDocShell::GetAssociatedWindow(nsIDOMWindow** aWindow)
10616 return CallGetInterface(this, aWindow);
10619 NS_IMETHODIMP
10620 nsDocShell::GetTopWindow(nsIDOMWindow** aWindow)
10622 nsresult rv;
10623 nsCOMPtr<nsIDOMWindow> win = do_GetInterface(GetAsSupports(this), &rv);
10624 NS_ENSURE_SUCCESS(rv, rv);
10625 return win->GetTop(aWindow);
10628 NS_IMETHODIMP
10629 nsDocShell::IsAppOfType(PRUint32 aAppType, PRBool *aIsOfType)
10631 nsCOMPtr<nsIDocShell> shell = this;
10632 while (shell) {
10633 PRUint32 type;
10634 shell->GetAppType(&type);
10635 if (type == aAppType) {
10636 *aIsOfType = PR_TRUE;
10637 return NS_OK;
10639 nsCOMPtr<nsIDocShellTreeItem> item = do_QueryInterface(shell);
10640 nsCOMPtr<nsIDocShellTreeItem> parent;
10641 item->GetParent(getter_AddRefs(parent));
10642 shell = do_QueryInterface(parent);
10645 *aIsOfType = PR_FALSE;
10646 return NS_OK;
10649 NS_IMETHODIMP
10650 nsDocShell::GetIsContent(PRBool *aIsContent)
10652 *aIsContent = (mItemType == typeContent);
10653 return NS_OK;
10656 /* static */
10657 nsresult
10658 nsDocShell::URIInheritsSecurityContext(nsIURI* aURI, PRBool* aResult)
10660 // Note: about:blank URIs do NOT inherit the security context from the
10661 // current document, which is what this function tests for...
10662 return NS_URIChainHasFlags(aURI,
10663 nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT,
10664 aResult);
10667 /* static */
10668 PRBool
10669 nsDocShell::URIIsLocalFile(nsIURI *aURI)
10671 PRBool isFile;
10672 nsCOMPtr<nsINetUtil> util = do_GetNetUtil();
10674 return util && NS_SUCCEEDED(util->ProtocolHasFlags(aURI,
10675 nsIProtocolHandler::URI_IS_LOCAL_FILE,
10676 &isFile)) &&
10677 isFile;
10680 /* static */
10681 PRBool
10682 nsDocShell::IsAboutBlank(nsIURI* aURI)
10684 NS_PRECONDITION(aURI, "Must have URI");
10686 // GetSpec can be expensive for some URIs, so check the scheme first.
10687 PRBool isAbout = PR_FALSE;
10688 if (NS_FAILED(aURI->SchemeIs("about", &isAbout)) || !isAbout) {
10689 return PR_FALSE;
10692 nsCAutoString str;
10693 aURI->GetSpec(str);
10694 return str.EqualsLiteral("about:blank");
10697 PRBool
10698 nsDocShell::IsOKToLoadURI(nsIURI* aURI)
10700 NS_PRECONDITION(aURI, "Must have a URI!");
10702 if (!mFiredUnloadEvent) {
10703 return PR_TRUE;
10706 if (!mLoadingURI) {
10707 return PR_FALSE;
10710 nsCOMPtr<nsIScriptSecurityManager> secMan =
10711 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
10712 return
10713 secMan &&
10714 NS_SUCCEEDED(secMan->CheckSameOriginURI(aURI, mLoadingURI, PR_FALSE));
10718 // Routines for selection and clipboard
10720 nsresult
10721 nsDocShell::GetControllerForCommand(const char * inCommand,
10722 nsIController** outController)
10724 NS_ENSURE_ARG_POINTER(outController);
10725 *outController = nsnull;
10727 nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(mScriptGlobal));
10728 if (window) {
10729 nsCOMPtr<nsPIWindowRoot> root = window->GetTopWindowRoot();
10730 if (root) {
10731 return root->GetControllerForCommand(inCommand, outController);
10735 return NS_ERROR_FAILURE;
10738 nsresult
10739 nsDocShell::IsCommandEnabled(const char * inCommand, PRBool* outEnabled)
10741 NS_ENSURE_ARG_POINTER(outEnabled);
10742 *outEnabled = PR_FALSE;
10744 nsresult rv = NS_ERROR_FAILURE;
10746 nsCOMPtr<nsIController> controller;
10747 rv = GetControllerForCommand (inCommand, getter_AddRefs(controller));
10748 if (controller)
10749 rv = controller->IsCommandEnabled(inCommand, outEnabled);
10751 return rv;
10754 nsresult
10755 nsDocShell::DoCommand(const char * inCommand)
10757 nsresult rv = NS_ERROR_FAILURE;
10759 nsCOMPtr<nsIController> controller;
10760 rv = GetControllerForCommand(inCommand, getter_AddRefs(controller));
10761 if (controller)
10762 rv = controller->DoCommand(inCommand);
10764 return rv;
10767 nsresult
10768 nsDocShell::EnsureCommandHandler()
10770 if (!mCommandManager)
10772 nsCOMPtr<nsPICommandUpdater> commandUpdater =
10773 do_CreateInstance("@mozilla.org/embedcomp/command-manager;1");
10774 if (!commandUpdater) return NS_ERROR_OUT_OF_MEMORY;
10776 nsCOMPtr<nsIDOMWindow> domWindow =
10777 do_GetInterface(static_cast<nsIInterfaceRequestor *>(this));
10779 nsresult rv = commandUpdater->Init(domWindow);
10780 if (NS_SUCCEEDED(rv))
10781 mCommandManager = do_QueryInterface(commandUpdater);
10784 return mCommandManager ? NS_OK : NS_ERROR_FAILURE;
10787 NS_IMETHODIMP
10788 nsDocShell::CanCutSelection(PRBool* aResult)
10790 return IsCommandEnabled("cmd_cut", aResult);
10793 NS_IMETHODIMP
10794 nsDocShell::CanCopySelection(PRBool* aResult)
10796 return IsCommandEnabled("cmd_copy", aResult);
10799 NS_IMETHODIMP
10800 nsDocShell::CanCopyLinkLocation(PRBool* aResult)
10802 return IsCommandEnabled("cmd_copyLink", aResult);
10805 NS_IMETHODIMP
10806 nsDocShell::CanCopyImageLocation(PRBool* aResult)
10808 return IsCommandEnabled("cmd_copyImageLocation",
10809 aResult);
10812 NS_IMETHODIMP
10813 nsDocShell::CanCopyImageContents(PRBool* aResult)
10815 return IsCommandEnabled("cmd_copyImageContents",
10816 aResult);
10819 NS_IMETHODIMP
10820 nsDocShell::CanPaste(PRBool* aResult)
10822 return IsCommandEnabled("cmd_paste", aResult);
10825 NS_IMETHODIMP
10826 nsDocShell::CutSelection(void)
10828 return DoCommand ( "cmd_cut" );
10831 NS_IMETHODIMP
10832 nsDocShell::CopySelection(void)
10834 return DoCommand ( "cmd_copy" );
10837 NS_IMETHODIMP
10838 nsDocShell::CopyLinkLocation(void)
10840 return DoCommand ( "cmd_copyLink" );
10843 NS_IMETHODIMP
10844 nsDocShell::CopyImageLocation(void)
10846 return DoCommand ( "cmd_copyImageLocation" );
10849 NS_IMETHODIMP
10850 nsDocShell::CopyImageContents(void)
10852 return DoCommand ( "cmd_copyImageContents" );
10855 NS_IMETHODIMP
10856 nsDocShell::Paste(void)
10858 return DoCommand ( "cmd_paste" );
10861 NS_IMETHODIMP
10862 nsDocShell::SelectAll(void)
10864 return DoCommand ( "cmd_selectAll" );
10868 // SelectNone
10870 // Collapses the current selection, insertion point ends up at beginning
10871 // of previous selection.
10873 NS_IMETHODIMP
10874 nsDocShell::SelectNone(void)
10876 return DoCommand ( "cmd_selectNone" );
10879 //----------------------------------------------------------------------
10881 // link handling
10883 class OnLinkClickEvent : public nsRunnable {
10884 public:
10885 OnLinkClickEvent(nsDocShell* aHandler, nsIContent* aContent,
10886 nsIURI* aURI,
10887 const PRUnichar* aTargetSpec,
10888 nsIInputStream* aPostDataStream = 0,
10889 nsIInputStream* aHeadersDataStream = 0);
10891 NS_IMETHOD Run() {
10892 nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(mHandler->mScriptGlobal));
10893 nsAutoPopupStatePusher popupStatePusher(window, mPopupState);
10895 mHandler->OnLinkClickSync(mContent, mURI,
10896 mTargetSpec.get(), mPostDataStream,
10897 mHeadersDataStream,
10898 nsnull, nsnull);
10899 return NS_OK;
10902 private:
10903 nsRefPtr<nsDocShell> mHandler;
10904 nsCOMPtr<nsIURI> mURI;
10905 nsString mTargetSpec;
10906 nsCOMPtr<nsIInputStream> mPostDataStream;
10907 nsCOMPtr<nsIInputStream> mHeadersDataStream;
10908 nsCOMPtr<nsIContent> mContent;
10909 PopupControlState mPopupState;
10912 OnLinkClickEvent::OnLinkClickEvent(nsDocShell* aHandler,
10913 nsIContent *aContent,
10914 nsIURI* aURI,
10915 const PRUnichar* aTargetSpec,
10916 nsIInputStream* aPostDataStream,
10917 nsIInputStream* aHeadersDataStream)
10918 : mHandler(aHandler)
10919 , mURI(aURI)
10920 , mTargetSpec(aTargetSpec)
10921 , mPostDataStream(aPostDataStream)
10922 , mHeadersDataStream(aHeadersDataStream)
10923 , mContent(aContent)
10925 nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(mHandler->mScriptGlobal));
10927 mPopupState = window->GetPopupControlState();
10930 //----------------------------------------
10932 NS_IMETHODIMP
10933 nsDocShell::OnLinkClick(nsIContent* aContent,
10934 nsIURI* aURI,
10935 const PRUnichar* aTargetSpec,
10936 nsIInputStream* aPostDataStream,
10937 nsIInputStream* aHeadersDataStream)
10939 NS_ASSERTION(NS_IsMainThread(), "wrong thread");
10941 if (!IsOKToLoadURI(aURI)) {
10942 return NS_OK;
10945 if (aContent->IsEditable()) {
10946 return NS_OK;
10949 nsCOMPtr<nsIRunnable> ev =
10950 new OnLinkClickEvent(this, aContent, aURI, aTargetSpec,
10951 aPostDataStream, aHeadersDataStream);
10952 return NS_DispatchToCurrentThread(ev);
10955 NS_IMETHODIMP
10956 nsDocShell::OnLinkClickSync(nsIContent *aContent,
10957 nsIURI* aURI,
10958 const PRUnichar* aTargetSpec,
10959 nsIInputStream* aPostDataStream,
10960 nsIInputStream* aHeadersDataStream,
10961 nsIDocShell** aDocShell,
10962 nsIRequest** aRequest)
10964 // Initialize the DocShell / Request
10965 if (aDocShell) {
10966 *aDocShell = nsnull;
10968 if (aRequest) {
10969 *aRequest = nsnull;
10972 if (!IsOKToLoadURI(aURI)) {
10973 return NS_OK;
10976 if (aContent->IsEditable()) {
10977 return NS_OK;
10981 // defer to an external protocol handler if necessary...
10982 nsCOMPtr<nsIExternalProtocolService> extProtService =
10983 do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID);
10984 if (extProtService) {
10985 nsCAutoString scheme;
10986 aURI->GetScheme(scheme);
10987 if (!scheme.IsEmpty()) {
10988 // if the URL scheme does not correspond to an exposed protocol, then we
10989 // need to hand this link click over to the external protocol handler.
10990 PRBool isExposed;
10991 nsresult rv = extProtService->IsExposedProtocol(scheme.get(), &isExposed);
10992 if (NS_SUCCEEDED(rv) && !isExposed) {
10993 return extProtService->LoadURI(aURI, this);
10999 // Get the owner document of the link that was clicked, this will be
11000 // the document that the link is in, or the last document that the
11001 // link was in. From that document, we'll get the URI to use as the
11002 // referer, since the current URI in this docshell may be a
11003 // new document that we're in the process of loading.
11004 nsCOMPtr<nsIDocument> refererDoc = aContent->GetOwnerDoc();
11005 NS_ENSURE_TRUE(refererDoc, NS_ERROR_UNEXPECTED);
11007 nsCOMPtr<nsIURI> referer = refererDoc->GetDocumentURI();
11009 // referer could be null here in some odd cases, but that's ok,
11010 // we'll just load the link w/o sending a referer in those cases.
11012 nsAutoString target(aTargetSpec);
11014 // If this is an anchor element, grab its type property to use as a hint
11015 nsAutoString typeHint;
11016 nsCOMPtr<nsIDOMHTMLAnchorElement> anchor(do_QueryInterface(aContent));
11017 if (anchor) {
11018 anchor->GetType(typeHint);
11021 nsresult rv = InternalLoad(aURI, // New URI
11022 referer, // Referer URI
11023 aContent->NodePrincipal(), // Owner is our node's
11024 // principal
11025 INTERNAL_LOAD_FLAGS_NONE,
11026 target.get(), // Window target
11027 NS_LossyConvertUTF16toASCII(typeHint).get(),
11028 aPostDataStream, // Post data stream
11029 aHeadersDataStream, // Headers stream
11030 LOAD_LINK, // Load type
11031 nsnull, // No SHEntry
11032 PR_TRUE, // first party site
11033 aDocShell, // DocShell out-param
11034 aRequest); // Request out-param
11035 if (NS_SUCCEEDED(rv)) {
11036 DispatchPings(aContent, referer);
11038 return rv;
11041 NS_IMETHODIMP
11042 nsDocShell::OnOverLink(nsIContent* aContent,
11043 nsIURI* aURI,
11044 const PRUnichar* aTargetSpec)
11046 if (aContent->IsEditable()) {
11047 return NS_OK;
11050 nsCOMPtr<nsIWebBrowserChrome2> browserChrome2 = do_GetInterface(mTreeOwner);
11051 nsresult rv = NS_ERROR_FAILURE;
11053 nsCOMPtr<nsIWebBrowserChrome> browserChrome;
11054 if (!browserChrome2) {
11055 browserChrome = do_GetInterface(mTreeOwner);
11056 if (!browserChrome)
11057 return rv;
11060 nsCOMPtr<nsITextToSubURI> textToSubURI =
11061 do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
11062 if (NS_FAILED(rv))
11063 return rv;
11065 // use url origin charset to unescape the URL
11066 nsCAutoString charset;
11067 rv = aURI->GetOriginCharset(charset);
11068 NS_ENSURE_SUCCESS(rv, rv);
11070 nsCAutoString spec;
11071 rv = aURI->GetSpec(spec);
11072 NS_ENSURE_SUCCESS(rv, rv);
11074 nsAutoString uStr;
11075 rv = textToSubURI->UnEscapeURIForUI(charset, spec, uStr);
11076 NS_ENSURE_SUCCESS(rv, rv);
11078 if (browserChrome2) {
11079 nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aContent);
11080 rv = browserChrome2->SetStatusWithContext(nsIWebBrowserChrome::STATUS_LINK,
11081 uStr, element);
11082 } else {
11083 rv = browserChrome->SetStatus(nsIWebBrowserChrome::STATUS_LINK, uStr.get());
11085 return rv;
11088 NS_IMETHODIMP
11089 nsDocShell::OnLeaveLink()
11091 nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(mTreeOwner));
11092 nsresult rv = NS_ERROR_FAILURE;
11094 if (browserChrome) {
11095 rv = browserChrome->SetStatus(nsIWebBrowserChrome::STATUS_LINK,
11096 EmptyString().get());
11098 return rv;
11101 //----------------------------------------------------------------------
11102 // Web Shell Services API
11104 //This functions is only called when a new charset is detected in loading a document.
11105 //Its name should be changed to "CharsetReloadDocument"
11106 NS_IMETHODIMP
11107 nsDocShell::ReloadDocument(const char* aCharset,
11108 PRInt32 aSource)
11111 // XXX hack. keep the aCharset and aSource wait to pick it up
11112 nsCOMPtr<nsIContentViewer> cv;
11113 NS_ENSURE_SUCCESS(GetContentViewer(getter_AddRefs(cv)), NS_ERROR_FAILURE);
11114 if (cv)
11116 nsCOMPtr<nsIMarkupDocumentViewer> muDV = do_QueryInterface(cv);
11117 if (muDV)
11119 PRInt32 hint;
11120 muDV->GetHintCharacterSetSource(&hint);
11121 if (aSource > hint)
11123 nsCString charset(aCharset);
11124 muDV->SetHintCharacterSet(charset);
11125 muDV->SetHintCharacterSetSource(aSource);
11126 if(eCharsetReloadRequested != mCharsetReloadState)
11128 mCharsetReloadState = eCharsetReloadRequested;
11129 return Reload(LOAD_FLAGS_CHARSET_CHANGE);
11134 //return failure if this request is not accepted due to mCharsetReloadState
11135 return NS_ERROR_DOCSHELL_REQUEST_REJECTED;
11139 NS_IMETHODIMP
11140 nsDocShell::StopDocumentLoad(void)
11142 if(eCharsetReloadRequested != mCharsetReloadState)
11144 Stop(nsIWebNavigation::STOP_ALL);
11145 return NS_OK;
11147 //return failer if this request is not accepted due to mCharsetReloadState
11148 return NS_ERROR_DOCSHELL_REQUEST_REJECTED;
11151 NS_IMETHODIMP
11152 nsDocShell::GetPrintPreview(nsIWebBrowserPrint** aPrintPreview)
11154 *aPrintPreview = nsnull;
11155 #if NS_PRINT_PREVIEW
11156 nsCOMPtr<nsIDocumentViewerPrint> print = do_QueryInterface(mContentViewer);
11157 if (!print || !print->IsInitializedForPrintPreview()) {
11158 Stop(nsIWebNavigation::STOP_ALL);
11159 nsCOMPtr<nsIPrincipal> principal =
11160 do_CreateInstance("@mozilla.org/nullprincipal;1");
11161 NS_ENSURE_STATE(principal);
11162 nsresult rv = CreateAboutBlankContentViewer(principal, nsnull);
11163 NS_ENSURE_SUCCESS(rv, rv);
11164 print = do_QueryInterface(mContentViewer);
11165 NS_ENSURE_STATE(print);
11166 print->InitializeForPrintPreview();
11168 nsCOMPtr<nsIWebBrowserPrint> result = do_QueryInterface(print);
11169 result.forget(aPrintPreview);
11170 return NS_OK;
11171 #else
11172 return NS_ERROR_NOT_IMPLEMENTED;
11173 #endif
11177 #ifdef DEBUG
11178 unsigned long nsDocShell::gNumberOfDocShells = 0;
11179 #endif