Bug 634435: Add a property to expose the current pushState-state. r=jlebar a=beltzner
[mozilla-central.git] / docshell / base / nsDocShell.cpp
blob10c1a6bc7a9c47eeb2de69ebdc813a9158afffb0
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim: set ts=4 sw=4 tw=80 et: */
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 "nsIContentUtils.h"
53 #include "mozilla/dom/Element.h"
54 #include "nsIDocument.h"
55 #include "nsIDOMDocument.h"
56 #include "nsIDOM3Document.h"
57 #include "nsIDOMNSDocument.h"
58 #include "nsIDOMElement.h"
59 #include "nsIDOMStorageObsolete.h"
60 #include "nsIDOMStorage.h"
61 #include "nsPIDOMStorage.h"
62 #include "nsIDocumentViewer.h"
63 #include "nsIDocumentLoaderFactory.h"
64 #include "nsCURILoader.h"
65 #include "nsURILoader.h"
66 #include "nsDocShellCID.h"
67 #include "nsLayoutCID.h"
68 #include "nsDOMCID.h"
69 #include "nsIDOMScriptObjectFactory.h"
70 #include "nsNetUtil.h"
71 #include "nsRect.h"
72 #include "prprf.h"
73 #include "nsIMarkupDocumentViewer.h"
74 #include "nsXPIDLString.h"
75 #include "nsReadableUtils.h"
76 #include "nsIDOMEventTarget.h"
77 #include "nsIDOMChromeWindow.h"
78 #include "nsIDOMWindowInternal.h"
79 #include "nsIWebBrowserChrome.h"
80 #include "nsPoint.h"
81 #include "nsGfxCIID.h"
82 #include "nsIObserverService.h"
83 #include "nsIPrompt.h"
84 #include "nsIAuthPrompt.h"
85 #include "nsIAuthPrompt2.h"
86 #include "nsTextFormatter.h"
87 #include "nsIChannelEventSink.h"
88 #include "nsIAsyncVerifyRedirectCallback.h"
89 #include "nsIUploadChannel.h"
90 #include "nsISecurityEventSink.h"
91 #include "mozilla/FunctionTimer.h"
92 #include "nsIScriptSecurityManager.h"
93 #include "nsIJSContextStack.h"
94 #include "nsIScriptObjectPrincipal.h"
95 #include "nsDocumentCharsetInfoCID.h"
96 #include "nsIScrollableFrame.h"
97 #include "nsContentPolicyUtils.h" // NS_CheckContentLoadPolicy(...)
98 #include "nsICategoryManager.h"
99 #include "nsXPCOMCID.h"
100 #include "nsISeekableStream.h"
101 #include "nsAutoPtr.h"
102 #include "nsIPrefService.h"
103 #include "nsIPrefBranch.h"
104 #include "nsIPrefBranch2.h"
105 #include "nsIWritablePropertyBag2.h"
106 #include "nsIAppShell.h"
107 #include "nsWidgetsCID.h"
108 #include "nsDOMJSUtils.h"
109 #include "nsIInterfaceRequestorUtils.h"
110 #include "nsIView.h"
111 #include "nsIViewManager.h"
112 #include "nsIScriptChannel.h"
113 #include "nsIOfflineCacheUpdate.h"
114 #include "nsCPrefetchService.h"
115 #include "nsJSON.h"
116 #include "IHistory.h"
117 #include "mozilla/Services.h"
119 // we want to explore making the document own the load group
120 // so we can associate the document URI with the load group.
121 // until this point, we have an evil hack:
122 #include "nsIHttpChannelInternal.h"
125 // Local Includes
126 #include "nsDocShell.h"
127 #include "nsDocShellLoadInfo.h"
128 #include "nsCDefaultURIFixup.h"
129 #include "nsDocShellEnumerator.h"
130 #include "nsSHistory.h"
131 #include "nsDocShellEditorData.h"
133 // Helper Classes
134 #include "nsDOMError.h"
135 #include "nsEscape.h"
137 // Interfaces Needed
138 #include "nsIUploadChannel.h"
139 #include "nsIProgressEventSink.h"
140 #include "nsIWebProgress.h"
141 #include "nsILayoutHistoryState.h"
142 #include "nsITimer.h"
143 #include "nsISHistoryInternal.h"
144 #include "nsIPrincipal.h"
145 #include "nsIFileURL.h"
146 #include "nsIHistoryEntry.h"
147 #include "nsISHistoryListener.h"
148 #include "nsIWindowWatcher.h"
149 #include "nsIPromptFactory.h"
150 #include "nsIObserver.h"
151 #include "nsINestedURI.h"
152 #include "nsITransportSecurityInfo.h"
153 #include "nsINSSErrorsService.h"
154 #include "nsIApplicationCache.h"
155 #include "nsIApplicationCacheChannel.h"
156 #include "nsIApplicationCacheContainer.h"
157 #include "nsIPermissionManager.h"
158 #include "nsStreamUtils.h"
159 #include "nsIController.h"
160 #include "nsPICommandUpdater.h"
161 #include "nsIDOMHTMLAnchorElement.h"
162 #include "nsIWebBrowserChrome3.h"
163 #include "nsITabChild.h"
164 #include "nsIStrictTransportSecurityService.h"
166 // Editor-related
167 #include "nsIEditingSession.h"
169 #include "nsPIDOMWindow.h"
170 #include "nsPIWindowRoot.h"
171 #include "nsIDOMDocument.h"
172 #include "nsICachingChannel.h"
173 #include "nsICacheVisitor.h"
174 #include "nsICacheEntryDescriptor.h"
175 #include "nsIMultiPartChannel.h"
176 #include "nsIWyciwygChannel.h"
178 // The following are for bug #13871: Prevent frameset spoofing
179 #include "nsIHTMLDocument.h"
181 // For reporting errors with the console service.
182 // These can go away if error reporting is propagated up past nsDocShell.
183 #include "nsIConsoleService.h"
184 #include "nsIScriptError.h"
186 // used to dispatch urls to default protocol handlers
187 #include "nsCExternalHandlerService.h"
188 #include "nsIExternalProtocolService.h"
190 #include "nsFocusManager.h"
192 #include "nsITextToSubURI.h"
194 #include "nsIJARChannel.h"
196 #include "prlog.h"
197 #include "prmem.h"
199 #include "nsISelectionDisplay.h"
201 #include "nsIGlobalHistory2.h"
202 #include "nsIGlobalHistory3.h"
204 #ifdef DEBUG_DOCSHELL_FOCUS
205 #include "nsIEventStateManager.h"
206 #endif
208 #include "nsIFrame.h"
210 // for embedding
211 #include "nsIWebBrowserChromeFocus.h"
213 #if NS_PRINT_PREVIEW
214 #include "nsIDocumentViewerPrint.h"
215 #include "nsIWebBrowserPrint.h"
216 #endif
218 #include "nsPluginError.h"
220 static NS_DEFINE_CID(kDOMScriptObjectFactoryCID,
221 NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
222 static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
224 #if defined(DEBUG_bryner) || defined(DEBUG_chb)
225 //#define DEBUG_DOCSHELL_FOCUS
226 #define DEBUG_PAGE_CACHE
227 #endif
229 #include "nsContentErrors.h"
230 #include "nsIChannelPolicy.h"
231 #include "nsIContentSecurityPolicy.h"
233 #ifdef MOZ_IPC
234 #include "nsXULAppAPI.h"
235 #endif
237 using namespace mozilla;
239 // Number of documents currently loading
240 static PRInt32 gNumberOfDocumentsLoading = 0;
242 // Global count of existing docshells.
243 static PRInt32 gDocShellCount = 0;
245 // Global reference to the URI fixup service.
246 nsIURIFixup *nsDocShell::sURIFixup = 0;
248 // True means we validate window targets to prevent frameset
249 // spoofing. Initialize this to a non-bolean value so we know to check
250 // the pref on the creation of the first docshell.
251 static PRBool gValidateOrigin = (PRBool)0xffffffff;
253 // Hint for native dispatch of events on how long to delay after
254 // all documents have loaded in milliseconds before favoring normal
255 // native event dispatch priorites over performance
256 #define NS_EVENT_STARVATION_DELAY_HINT 2000
258 // This is needed for displaying an error message
259 // when navigation is attempted on a document when printing
260 // The value arbitrary as long as it doesn't conflict with
261 // any of the other values in the errors in DisplayLoadError
262 #define NS_ERROR_DOCUMENT_IS_PRINTMODE NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_GENERAL,2001)
264 #ifdef PR_LOGGING
265 #ifdef DEBUG
266 static PRLogModuleInfo* gDocShellLog;
267 #endif
268 static PRLogModuleInfo* gDocShellLeakLog;
269 #endif
271 const char kBrandBundleURL[] = "chrome://branding/locale/brand.properties";
272 const char kAppstringsBundleURL[] = "chrome://global/locale/appstrings.properties";
274 static void
275 FavorPerformanceHint(PRBool perfOverStarvation, PRUint32 starvationDelay)
277 nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
278 if (appShell)
279 appShell->FavorPerformanceHint(perfOverStarvation, starvationDelay);
282 //*****************************************************************************
283 // <a ping> support
284 //*****************************************************************************
286 #define PREF_PINGS_ENABLED "browser.send_pings"
287 #define PREF_PINGS_MAX_PER_LINK "browser.send_pings.max_per_link"
288 #define PREF_PINGS_REQUIRE_SAME_HOST "browser.send_pings.require_same_host"
290 // Check prefs to see if pings are enabled and if so what restrictions might
291 // be applied.
293 // @param maxPerLink
294 // This parameter returns the number of pings that are allowed per link click
296 // @param requireSameHost
297 // This parameter returns PR_TRUE if pings are restricted to the same host as
298 // the document in which the click occurs. If the same host restriction is
299 // imposed, then we still allow for pings to cross over to different
300 // protocols and ports for flexibility and because it is not possible to send
301 // a ping via FTP.
303 // @returns
304 // PR_TRUE if pings are enabled and PR_FALSE otherwise.
306 static PRBool
307 PingsEnabled(PRInt32 *maxPerLink, PRBool *requireSameHost)
309 PRBool allow = PR_FALSE;
311 *maxPerLink = 1;
312 *requireSameHost = PR_TRUE;
314 nsCOMPtr<nsIPrefBranch> prefs =
315 do_GetService(NS_PREFSERVICE_CONTRACTID);
316 if (prefs) {
317 PRBool val;
318 if (NS_SUCCEEDED(prefs->GetBoolPref(PREF_PINGS_ENABLED, &val)))
319 allow = val;
320 if (allow) {
321 prefs->GetIntPref(PREF_PINGS_MAX_PER_LINK, maxPerLink);
322 prefs->GetBoolPref(PREF_PINGS_REQUIRE_SAME_HOST, requireSameHost);
326 return allow;
329 static PRBool
330 CheckPingURI(nsIURI* uri, nsIContent* content)
332 if (!uri)
333 return PR_FALSE;
335 // Check with nsIScriptSecurityManager
336 nsCOMPtr<nsIScriptSecurityManager> ssmgr =
337 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
338 NS_ENSURE_TRUE(ssmgr, PR_FALSE);
340 nsresult rv =
341 ssmgr->CheckLoadURIWithPrincipal(content->NodePrincipal(), uri,
342 nsIScriptSecurityManager::STANDARD);
343 if (NS_FAILED(rv)) {
344 return PR_FALSE;
347 // Ignore non-HTTP(S)
348 PRBool match;
349 if ((NS_FAILED(uri->SchemeIs("http", &match)) || !match) &&
350 (NS_FAILED(uri->SchemeIs("https", &match)) || !match)) {
351 return PR_FALSE;
354 // Check with contentpolicy
355 PRInt16 shouldLoad = nsIContentPolicy::ACCEPT;
356 rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_PING,
357 uri,
358 content->NodePrincipal(),
359 content,
360 EmptyCString(), // mime hint
361 nsnull, //extra
362 &shouldLoad);
363 return NS_SUCCEEDED(rv) && NS_CP_ACCEPTED(shouldLoad);
366 typedef void (* ForEachPingCallback)(void *closure, nsIContent *content,
367 nsIURI *uri, nsIIOService *ios);
369 static void
370 ForEachPing(nsIContent *content, ForEachPingCallback callback, void *closure)
372 // NOTE: Using nsIDOMHTMLAnchorElement::GetPing isn't really worth it here
373 // since we'd still need to parse the resulting string. Instead, we
374 // just parse the raw attribute. It might be nice if the content node
375 // implemented an interface that exposed an enumeration of nsIURIs.
377 // Make sure we are dealing with either an <A> or <AREA> element in the HTML
378 // or XHTML namespace.
379 if (!content->IsHTML())
380 return;
381 nsIAtom *nameAtom = content->Tag();
382 if (!nameAtom->Equals(NS_LITERAL_STRING("a")) &&
383 !nameAtom->Equals(NS_LITERAL_STRING("area")))
384 return;
386 nsCOMPtr<nsIAtom> pingAtom = do_GetAtom("ping");
387 if (!pingAtom)
388 return;
390 nsAutoString value;
391 content->GetAttr(kNameSpaceID_None, pingAtom, value);
392 if (value.IsEmpty())
393 return;
395 nsCOMPtr<nsIIOService> ios = do_GetIOService();
396 if (!ios)
397 return;
399 nsIDocument *doc = content->GetOwnerDoc();
400 if (!doc)
401 return;
403 // value contains relative URIs split on spaces (U+0020)
404 const PRUnichar *start = value.BeginReading();
405 const PRUnichar *end = value.EndReading();
406 const PRUnichar *iter = start;
407 for (;;) {
408 if (iter < end && *iter != ' ') {
409 ++iter;
410 } else { // iter is pointing at either end or a space
411 while (*start == ' ' && start < iter)
412 ++start;
413 if (iter != start) {
414 nsCOMPtr<nsIURI> uri, baseURI = content->GetBaseURI();
415 ios->NewURI(NS_ConvertUTF16toUTF8(Substring(start, iter)),
416 doc->GetDocumentCharacterSet().get(),
417 baseURI, getter_AddRefs(uri));
418 if (CheckPingURI(uri, content)) {
419 callback(closure, content, uri, ios);
422 start = iter = iter + 1;
423 if (iter >= end)
424 break;
429 //----------------------------------------------------------------------
431 // We wait this many milliseconds before killing the ping channel...
432 #define PING_TIMEOUT 10000
434 static void
435 OnPingTimeout(nsITimer *timer, void *closure)
437 nsILoadGroup *loadGroup = static_cast<nsILoadGroup *>(closure);
438 loadGroup->Cancel(NS_ERROR_ABORT);
439 loadGroup->Release();
442 // Check to see if two URIs have the same host or not
443 static PRBool
444 IsSameHost(nsIURI *uri1, nsIURI *uri2)
446 nsCAutoString host1, host2;
447 uri1->GetAsciiHost(host1);
448 uri2->GetAsciiHost(host2);
449 return host1.Equals(host2);
452 class nsPingListener : public nsIStreamListener
453 , public nsIInterfaceRequestor
454 , public nsIChannelEventSink
456 public:
457 NS_DECL_ISUPPORTS
458 NS_DECL_NSIREQUESTOBSERVER
459 NS_DECL_NSISTREAMLISTENER
460 NS_DECL_NSIINTERFACEREQUESTOR
461 NS_DECL_NSICHANNELEVENTSINK
463 nsPingListener(PRBool requireSameHost, nsIContent* content)
464 : mRequireSameHost(requireSameHost),
465 mContent(content)
468 private:
469 PRBool mRequireSameHost;
470 nsCOMPtr<nsIContent> mContent;
473 NS_IMPL_ISUPPORTS4(nsPingListener, nsIStreamListener, nsIRequestObserver,
474 nsIInterfaceRequestor, nsIChannelEventSink)
476 NS_IMETHODIMP
477 nsPingListener::OnStartRequest(nsIRequest *request, nsISupports *context)
479 return NS_OK;
482 NS_IMETHODIMP
483 nsPingListener::OnDataAvailable(nsIRequest *request, nsISupports *context,
484 nsIInputStream *stream, PRUint32 offset,
485 PRUint32 count)
487 PRUint32 result;
488 return stream->ReadSegments(NS_DiscardSegment, nsnull, count, &result);
491 NS_IMETHODIMP
492 nsPingListener::OnStopRequest(nsIRequest *request, nsISupports *context,
493 nsresult status)
495 return NS_OK;
498 NS_IMETHODIMP
499 nsPingListener::GetInterface(const nsIID &iid, void **result)
501 if (iid.Equals(NS_GET_IID(nsIChannelEventSink))) {
502 NS_ADDREF_THIS();
503 *result = (nsIChannelEventSink *) this;
504 return NS_OK;
507 return NS_ERROR_NO_INTERFACE;
510 NS_IMETHODIMP
511 nsPingListener::AsyncOnChannelRedirect(nsIChannel *oldChan, nsIChannel *newChan,
512 PRUint32 flags,
513 nsIAsyncVerifyRedirectCallback *callback)
515 nsCOMPtr<nsIURI> newURI;
516 newChan->GetURI(getter_AddRefs(newURI));
518 if (!CheckPingURI(newURI, mContent))
519 return NS_ERROR_ABORT;
521 if (!mRequireSameHost) {
522 callback->OnRedirectVerifyCallback(NS_OK);
523 return NS_OK;
526 // XXXbz should this be using something more like the nsContentUtils
527 // same-origin checker?
528 nsCOMPtr<nsIURI> oldURI;
529 oldChan->GetURI(getter_AddRefs(oldURI));
530 NS_ENSURE_STATE(oldURI && newURI);
532 if (!IsSameHost(oldURI, newURI))
533 return NS_ERROR_ABORT;
535 callback->OnRedirectVerifyCallback(NS_OK);
536 return NS_OK;
539 struct SendPingInfo {
540 PRInt32 numPings;
541 PRInt32 maxPings;
542 PRBool requireSameHost;
543 nsIURI *referrer;
546 static void
547 SendPing(void *closure, nsIContent *content, nsIURI *uri, nsIIOService *ios)
549 SendPingInfo *info = static_cast<SendPingInfo *>(closure);
550 if (info->numPings >= info->maxPings)
551 return;
553 if (info->requireSameHost) {
554 // Make sure the referrer and the given uri share the same origin. We
555 // only require the same hostname. The scheme and port may differ.
556 if (!IsSameHost(uri, info->referrer))
557 return;
560 nsIDocument *doc = content->GetOwnerDoc();
561 if (!doc)
562 return;
564 nsCOMPtr<nsIChannel> chan;
565 ios->NewChannelFromURI(uri, getter_AddRefs(chan));
566 if (!chan)
567 return;
569 // Don't bother caching the result of this URI load.
570 chan->SetLoadFlags(nsIRequest::INHIBIT_CACHING);
572 nsCOMPtr<nsIHttpChannel> httpChan = do_QueryInterface(chan);
573 if (!httpChan)
574 return;
576 // This is needed in order for 3rd-party cookie blocking to work.
577 nsCOMPtr<nsIHttpChannelInternal> httpInternal = do_QueryInterface(httpChan);
578 if (httpInternal)
579 httpInternal->SetDocumentURI(doc->GetDocumentURI());
581 if (info->referrer)
582 httpChan->SetReferrer(info->referrer);
584 httpChan->SetRequestMethod(NS_LITERAL_CSTRING("POST"));
586 // Remove extraneous request headers (to reduce request size)
587 httpChan->SetRequestHeader(NS_LITERAL_CSTRING("accept"),
588 EmptyCString(), PR_FALSE);
589 httpChan->SetRequestHeader(NS_LITERAL_CSTRING("accept-language"),
590 EmptyCString(), PR_FALSE);
591 httpChan->SetRequestHeader(NS_LITERAL_CSTRING("accept-charset"),
592 EmptyCString(), PR_FALSE);
593 httpChan->SetRequestHeader(NS_LITERAL_CSTRING("accept-encoding"),
594 EmptyCString(), PR_FALSE);
596 nsCOMPtr<nsIUploadChannel> uploadChan = do_QueryInterface(httpChan);
597 if (!uploadChan)
598 return;
600 // To avoid sending an unnecessary Content-Type header, we encode the
601 // closing portion of the headers in the POST body.
602 NS_NAMED_LITERAL_CSTRING(uploadData, "Content-Length: 0\r\n\r\n");
604 nsCOMPtr<nsIInputStream> uploadStream;
605 NS_NewPostDataStream(getter_AddRefs(uploadStream), PR_FALSE,
606 uploadData, 0);
607 if (!uploadStream)
608 return;
610 uploadChan->SetUploadStream(uploadStream, EmptyCString(), -1);
612 // The channel needs to have a loadgroup associated with it, so that we can
613 // cancel the channel and any redirected channels it may create.
614 nsCOMPtr<nsILoadGroup> loadGroup =
615 do_CreateInstance(NS_LOADGROUP_CONTRACTID);
616 if (!loadGroup)
617 return;
618 chan->SetLoadGroup(loadGroup);
620 // Construct a listener that merely discards any response. If successful at
621 // opening the channel, then it is not necessary to hold a reference to the
622 // channel. The networking subsystem will take care of that for us.
623 nsCOMPtr<nsIStreamListener> listener =
624 new nsPingListener(info->requireSameHost, content);
625 if (!listener)
626 return;
628 // Observe redirects as well:
629 nsCOMPtr<nsIInterfaceRequestor> callbacks = do_QueryInterface(listener);
630 NS_ASSERTION(callbacks, "oops");
631 loadGroup->SetNotificationCallbacks(callbacks);
633 chan->AsyncOpen(listener, nsnull);
635 // Even if AsyncOpen failed, we still count this as a successful ping. It's
636 // possible that AsyncOpen may have failed after triggering some background
637 // process that may have written something to the network.
638 info->numPings++;
640 // Prevent ping requests from stalling and never being garbage collected...
641 nsCOMPtr<nsITimer> timer =
642 do_CreateInstance(NS_TIMER_CONTRACTID);
643 if (timer) {
644 nsresult rv = timer->InitWithFuncCallback(OnPingTimeout, loadGroup,
645 PING_TIMEOUT,
646 nsITimer::TYPE_ONE_SHOT);
647 if (NS_SUCCEEDED(rv)) {
648 // When the timer expires, the callback function will release this
649 // reference to the loadgroup.
650 static_cast<nsILoadGroup *>(loadGroup.get())->AddRef();
651 loadGroup = 0;
655 // If we failed to setup the timer, then we should just cancel the channel
656 // because we won't be able to ensure that it goes away in a timely manner.
657 if (loadGroup)
658 chan->Cancel(NS_ERROR_ABORT);
661 // Spec: http://whatwg.org/specs/web-apps/current-work/#ping
662 static void
663 DispatchPings(nsIContent *content, nsIURI *referrer)
665 SendPingInfo info;
667 if (!PingsEnabled(&info.maxPings, &info.requireSameHost))
668 return;
669 if (info.maxPings == 0)
670 return;
672 info.numPings = 0;
673 info.referrer = referrer;
675 ForEachPing(content, SendPing, &info);
678 static nsISHEntry* GetRootSHEntry(nsISHEntry *entry);
680 //*****************************************************************************
681 //*** nsDocShell: Object Management
682 //*****************************************************************************
684 static PRUint64 gDocshellIDCounter = 0;
686 // Note: operator new zeros our memory
687 nsDocShell::nsDocShell():
688 nsDocLoader(),
689 mDefaultScrollbarPref(Scrollbar_Auto, Scrollbar_Auto),
690 mTreeOwner(nsnull),
691 mChromeEventHandler(nsnull),
692 mCharsetReloadState(eCharsetReloadInit),
693 mChildOffset(0),
694 mBusyFlags(BUSY_FLAGS_NONE),
695 mAppType(nsIDocShell::APP_TYPE_UNKNOWN),
696 mMarginWidth(-1),
697 mMarginHeight(-1),
698 mItemType(typeContent),
699 mPreviousTransIndex(-1),
700 mLoadedTransIndex(-1),
701 mAllowSubframes(PR_TRUE),
702 mAllowPlugins(PR_TRUE),
703 mAllowJavascript(PR_TRUE),
704 mAllowMetaRedirects(PR_TRUE),
705 mAllowImages(PR_TRUE),
706 mAllowDNSPrefetch(PR_TRUE),
707 mCreatingDocument(PR_FALSE),
708 mUseErrorPages(PR_FALSE),
709 mObserveErrorPages(PR_TRUE),
710 mAllowAuth(PR_TRUE),
711 mAllowKeywordFixup(PR_FALSE),
712 mIsOffScreenBrowser(PR_FALSE),
713 mIsActive(PR_TRUE),
714 mIsAppTab(PR_FALSE),
715 mUseGlobalHistory(PR_FALSE),
716 mFiredUnloadEvent(PR_FALSE),
717 mEODForCurrentDocument(PR_FALSE),
718 mURIResultedInDocument(PR_FALSE),
719 mIsBeingDestroyed(PR_FALSE),
720 mIsExecutingOnLoadHandler(PR_FALSE),
721 mIsPrintingOrPP(PR_FALSE),
722 mSavingOldViewer(PR_FALSE)
723 #ifdef DEBUG
724 , mInEnsureScriptEnv(PR_FALSE)
725 #endif
727 mHistoryID = ++gDocshellIDCounter;
728 if (gDocShellCount++ == 0) {
729 NS_ASSERTION(sURIFixup == nsnull,
730 "Huh, sURIFixup not null in first nsDocShell ctor!");
732 CallGetService(NS_URIFIXUP_CONTRACTID, &sURIFixup);
735 #ifdef PR_LOGGING
736 #ifdef DEBUG
737 if (! gDocShellLog)
738 gDocShellLog = PR_NewLogModule("nsDocShell");
739 #endif
740 if (nsnull == gDocShellLeakLog)
741 gDocShellLeakLog = PR_NewLogModule("nsDocShellLeak");
742 if (gDocShellLeakLog)
743 PR_LOG(gDocShellLeakLog, PR_LOG_DEBUG, ("DOCSHELL %p created\n", this));
744 #endif
746 #ifdef DEBUG
747 // We're counting the number of |nsDocShells| to help find leaks
748 ++gNumberOfDocShells;
749 #endif
750 #ifdef DEBUG
751 printf("++DOCSHELL %p == %ld\n", (void*) this, gNumberOfDocShells);
752 #endif
755 nsDocShell::~nsDocShell()
757 Destroy();
759 nsCOMPtr<nsISHistoryInternal>
760 shPrivate(do_QueryInterface(mSessionHistory));
761 if (shPrivate) {
762 shPrivate->SetRootDocShell(nsnull);
765 if (--gDocShellCount == 0) {
766 NS_IF_RELEASE(sURIFixup);
769 #ifdef PR_LOGGING
770 if (gDocShellLeakLog)
771 PR_LOG(gDocShellLeakLog, PR_LOG_DEBUG, ("DOCSHELL %p destroyed\n", this));
772 #endif
774 #ifdef DEBUG
775 // We're counting the number of |nsDocShells| to help find leaks
776 --gNumberOfDocShells;
777 #endif
778 #ifdef DEBUG
779 printf("--DOCSHELL %p == %ld\n", (void*) this, gNumberOfDocShells);
780 #endif
783 nsresult
784 nsDocShell::Init()
786 nsresult rv = nsDocLoader::Init();
787 NS_ENSURE_SUCCESS(rv, rv);
789 NS_ASSERTION(mLoadGroup, "Something went wrong!");
791 mContentListener = new nsDSURIContentListener(this);
792 NS_ENSURE_TRUE(mContentListener, NS_ERROR_OUT_OF_MEMORY);
794 rv = mContentListener->Init();
795 NS_ENSURE_SUCCESS(rv, rv);
797 if (!mStorages.Init())
798 return NS_ERROR_OUT_OF_MEMORY;
800 // We want to hold a strong ref to the loadgroup, so it better hold a weak
801 // ref to us... use an InterfaceRequestorProxy to do this.
802 nsCOMPtr<InterfaceRequestorProxy> proxy =
803 new InterfaceRequestorProxy(static_cast<nsIInterfaceRequestor*>
804 (this));
805 NS_ENSURE_TRUE(proxy, NS_ERROR_OUT_OF_MEMORY);
806 mLoadGroup->SetNotificationCallbacks(proxy);
808 rv = nsDocLoader::AddDocLoaderAsChildOfRoot(this);
809 NS_ENSURE_SUCCESS(rv, rv);
811 // Add as |this| a progress listener to itself. A little weird, but
812 // simpler than reproducing all the listener-notification logic in
813 // overrides of the various methods via which nsDocLoader can be
814 // notified. Note that this holds an nsWeakPtr to ourselves, so it's ok.
815 return AddProgressListener(this, nsIWebProgress::NOTIFY_STATE_DOCUMENT |
816 nsIWebProgress::NOTIFY_STATE_NETWORK);
820 void
821 nsDocShell::DestroyChildren()
823 nsCOMPtr<nsIDocShellTreeItem> shell;
824 PRInt32 n = mChildList.Count();
825 for (PRInt32 i = 0; i < n; i++) {
826 shell = do_QueryInterface(ChildAt(i));
827 NS_ASSERTION(shell, "docshell has null child");
829 if (shell) {
830 shell->SetTreeOwner(nsnull);
834 nsDocLoader::DestroyChildren();
837 //*****************************************************************************
838 // nsDocShell::nsISupports
839 //*****************************************************************************
841 NS_IMPL_ADDREF_INHERITED(nsDocShell, nsDocLoader)
842 NS_IMPL_RELEASE_INHERITED(nsDocShell, nsDocLoader)
844 NS_INTERFACE_MAP_BEGIN(nsDocShell)
845 NS_INTERFACE_MAP_ENTRY(nsIDocShell)
846 NS_INTERFACE_MAP_ENTRY(nsIDocShellTreeItem)
847 NS_INTERFACE_MAP_ENTRY(nsIDocShellTreeNode)
848 NS_INTERFACE_MAP_ENTRY(nsIDocShellHistory)
849 NS_INTERFACE_MAP_ENTRY(nsIWebNavigation)
850 NS_INTERFACE_MAP_ENTRY(nsIBaseWindow)
851 NS_INTERFACE_MAP_ENTRY(nsIScrollable)
852 NS_INTERFACE_MAP_ENTRY(nsITextScroll)
853 NS_INTERFACE_MAP_ENTRY(nsIDocCharset)
854 NS_INTERFACE_MAP_ENTRY(nsIScriptGlobalObjectOwner)
855 NS_INTERFACE_MAP_ENTRY(nsIRefreshURI)
856 NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
857 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
858 NS_INTERFACE_MAP_ENTRY(nsIContentViewerContainer)
859 NS_INTERFACE_MAP_ENTRY(nsIEditorDocShell)
860 NS_INTERFACE_MAP_ENTRY(nsIWebPageDescriptor)
861 NS_INTERFACE_MAP_ENTRY(nsIAuthPromptProvider)
862 NS_INTERFACE_MAP_ENTRY(nsIObserver)
863 NS_INTERFACE_MAP_ENTRY(nsILoadContext)
864 NS_INTERFACE_MAP_ENTRY(nsIWebShellServices)
865 NS_INTERFACE_MAP_ENTRY(nsILinkHandler)
866 NS_INTERFACE_MAP_ENTRY(nsIClipboardCommands)
867 NS_INTERFACE_MAP_ENTRY(nsIDocShell_MOZILLA_2_0_BRANCH)
868 NS_INTERFACE_MAP_END_INHERITING(nsDocLoader)
870 ///*****************************************************************************
871 // nsDocShell::nsIInterfaceRequestor
872 //*****************************************************************************
873 NS_IMETHODIMP nsDocShell::GetInterface(const nsIID & aIID, void **aSink)
875 NS_PRECONDITION(aSink, "null out param");
877 *aSink = nsnull;
879 if (aIID.Equals(NS_GET_IID(nsICommandManager))) {
880 NS_ENSURE_SUCCESS(EnsureCommandHandler(), NS_ERROR_FAILURE);
881 *aSink = mCommandManager;
883 else if (aIID.Equals(NS_GET_IID(nsIURIContentListener))) {
884 *aSink = mContentListener;
886 else if (aIID.Equals(NS_GET_IID(nsIScriptGlobalObject)) &&
887 NS_SUCCEEDED(EnsureScriptEnvironment())) {
888 *aSink = mScriptGlobal;
890 else if ((aIID.Equals(NS_GET_IID(nsIDOMWindowInternal)) ||
891 aIID.Equals(NS_GET_IID(nsPIDOMWindow)) ||
892 aIID.Equals(NS_GET_IID(nsIDOMWindow))) &&
893 NS_SUCCEEDED(EnsureScriptEnvironment())) {
894 return mScriptGlobal->QueryInterface(aIID, aSink);
896 else if (aIID.Equals(NS_GET_IID(nsIDOMDocument)) &&
897 NS_SUCCEEDED(EnsureContentViewer())) {
898 mContentViewer->GetDOMDocument((nsIDOMDocument **) aSink);
899 return *aSink ? NS_OK : NS_NOINTERFACE;
901 else if (aIID.Equals(NS_GET_IID(nsIDocument)) &&
902 NS_SUCCEEDED(EnsureContentViewer())) {
903 nsCOMPtr<nsIDOMDocument> domDoc;
904 mContentViewer->GetDOMDocument(getter_AddRefs(domDoc));
905 if (!domDoc)
906 return NS_NOINTERFACE;
907 return domDoc->QueryInterface(aIID, aSink);
909 else if (aIID.Equals(NS_GET_IID(nsIApplicationCacheContainer))) {
910 *aSink = nsnull;
912 // Return application cache associated with this docshell, if any
914 nsCOMPtr<nsIContentViewer> contentViewer;
915 GetContentViewer(getter_AddRefs(contentViewer));
916 if (!contentViewer)
917 return NS_ERROR_NO_INTERFACE;
919 nsCOMPtr<nsIDOMDocument> domDoc;
920 contentViewer->GetDOMDocument(getter_AddRefs(domDoc));
921 NS_ASSERTION(domDoc, "Should have a document.");
922 if (!domDoc)
923 return NS_ERROR_NO_INTERFACE;
925 #if defined(PR_LOGGING) && defined(DEBUG)
926 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
927 ("nsDocShell[%p]: returning app cache container %p",
928 this, domDoc.get()));
929 #endif
930 return domDoc->QueryInterface(aIID, aSink);
932 else if (aIID.Equals(NS_GET_IID(nsIPrompt)) &&
933 NS_SUCCEEDED(EnsureScriptEnvironment())) {
934 nsresult rv;
935 nsCOMPtr<nsIWindowWatcher> wwatch =
936 do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
937 NS_ENSURE_SUCCESS(rv, rv);
939 nsCOMPtr<nsIDOMWindow> window(do_QueryInterface(mScriptGlobal));
941 // Get the an auth prompter for our window so that the parenting
942 // of the dialogs works as it should when using tabs.
944 nsIPrompt *prompt;
945 rv = wwatch->GetNewPrompter(window, &prompt);
946 NS_ENSURE_SUCCESS(rv, rv);
948 *aSink = prompt;
949 return NS_OK;
951 else if (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) ||
952 aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) {
953 return NS_SUCCEEDED(
954 GetAuthPrompt(PROMPT_NORMAL, aIID, aSink)) ?
955 NS_OK : NS_NOINTERFACE;
957 else if (aIID.Equals(NS_GET_IID(nsISHistory))) {
958 nsCOMPtr<nsISHistory> shistory;
959 nsresult
960 rv =
961 GetSessionHistory(getter_AddRefs(shistory));
962 if (NS_SUCCEEDED(rv) && shistory) {
963 *aSink = shistory;
964 NS_ADDREF((nsISupports *) * aSink);
965 return NS_OK;
967 return NS_NOINTERFACE;
969 else if (aIID.Equals(NS_GET_IID(nsIWebBrowserFind))) {
970 nsresult rv = EnsureFind();
971 if (NS_FAILED(rv)) return rv;
973 *aSink = mFind;
974 NS_ADDREF((nsISupports*)*aSink);
975 return NS_OK;
977 else if (aIID.Equals(NS_GET_IID(nsIEditingSession)) && NS_SUCCEEDED(EnsureEditorData())) {
978 nsCOMPtr<nsIEditingSession> editingSession;
979 mEditorData->GetEditingSession(getter_AddRefs(editingSession));
980 if (editingSession)
982 *aSink = editingSession;
983 NS_ADDREF((nsISupports *)*aSink);
984 return NS_OK;
987 return NS_NOINTERFACE;
989 else if (aIID.Equals(NS_GET_IID(nsIClipboardDragDropHookList))
990 && NS_SUCCEEDED(EnsureTransferableHookData())) {
991 *aSink = mTransferableHookData;
992 NS_ADDREF((nsISupports *)*aSink);
993 return NS_OK;
995 else if (aIID.Equals(NS_GET_IID(nsISelectionDisplay))) {
996 nsCOMPtr<nsIPresShell> shell;
997 nsresult rv = GetPresShell(getter_AddRefs(shell));
998 if (NS_SUCCEEDED(rv) && shell)
999 return shell->QueryInterface(aIID,aSink);
1001 else if (aIID.Equals(NS_GET_IID(nsIDocShellTreeOwner))) {
1002 nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
1003 nsresult rv = GetTreeOwner(getter_AddRefs(treeOwner));
1004 if (NS_SUCCEEDED(rv) && treeOwner)
1005 return treeOwner->QueryInterface(aIID, aSink);
1007 else if (aIID.Equals(NS_GET_IID(nsITabChild))) {
1008 nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
1009 nsresult rv = GetTreeOwner(getter_AddRefs(treeOwner));
1010 if (NS_SUCCEEDED(rv) && treeOwner) {
1011 nsCOMPtr<nsIInterfaceRequestor> ir = do_QueryInterface(treeOwner);
1012 if (ir)
1013 return ir->GetInterface(aIID, aSink);
1016 else {
1017 return nsDocLoader::GetInterface(aIID, aSink);
1020 NS_IF_ADDREF(((nsISupports *) * aSink));
1021 return *aSink ? NS_OK : NS_NOINTERFACE;
1024 PRUint32
1025 nsDocShell::
1026 ConvertDocShellLoadInfoToLoadType(nsDocShellInfoLoadType aDocShellLoadType)
1028 PRUint32 loadType = LOAD_NORMAL;
1030 switch (aDocShellLoadType) {
1031 case nsIDocShellLoadInfo::loadNormal:
1032 loadType = LOAD_NORMAL;
1033 break;
1034 case nsIDocShellLoadInfo::loadNormalReplace:
1035 loadType = LOAD_NORMAL_REPLACE;
1036 break;
1037 case nsIDocShellLoadInfo::loadNormalExternal:
1038 loadType = LOAD_NORMAL_EXTERNAL;
1039 break;
1040 case nsIDocShellLoadInfo::loadHistory:
1041 loadType = LOAD_HISTORY;
1042 break;
1043 case nsIDocShellLoadInfo::loadNormalBypassCache:
1044 loadType = LOAD_NORMAL_BYPASS_CACHE;
1045 break;
1046 case nsIDocShellLoadInfo::loadNormalBypassProxy:
1047 loadType = LOAD_NORMAL_BYPASS_PROXY;
1048 break;
1049 case nsIDocShellLoadInfo::loadNormalBypassProxyAndCache:
1050 loadType = LOAD_NORMAL_BYPASS_PROXY_AND_CACHE;
1051 break;
1052 case nsIDocShellLoadInfo::loadReloadNormal:
1053 loadType = LOAD_RELOAD_NORMAL;
1054 break;
1055 case nsIDocShellLoadInfo::loadReloadCharsetChange:
1056 loadType = LOAD_RELOAD_CHARSET_CHANGE;
1057 break;
1058 case nsIDocShellLoadInfo::loadReloadBypassCache:
1059 loadType = LOAD_RELOAD_BYPASS_CACHE;
1060 break;
1061 case nsIDocShellLoadInfo::loadReloadBypassProxy:
1062 loadType = LOAD_RELOAD_BYPASS_PROXY;
1063 break;
1064 case nsIDocShellLoadInfo::loadReloadBypassProxyAndCache:
1065 loadType = LOAD_RELOAD_BYPASS_PROXY_AND_CACHE;
1066 break;
1067 case nsIDocShellLoadInfo::loadLink:
1068 loadType = LOAD_LINK;
1069 break;
1070 case nsIDocShellLoadInfo::loadRefresh:
1071 loadType = LOAD_REFRESH;
1072 break;
1073 case nsIDocShellLoadInfo::loadBypassHistory:
1074 loadType = LOAD_BYPASS_HISTORY;
1075 break;
1076 case nsIDocShellLoadInfo::loadStopContent:
1077 loadType = LOAD_STOP_CONTENT;
1078 break;
1079 case nsIDocShellLoadInfo::loadStopContentAndReplace:
1080 loadType = LOAD_STOP_CONTENT_AND_REPLACE;
1081 break;
1082 case nsIDocShellLoadInfo::loadPushState:
1083 loadType = LOAD_PUSHSTATE;
1084 break;
1085 default:
1086 NS_NOTREACHED("Unexpected nsDocShellInfoLoadType value");
1089 return loadType;
1093 nsDocShellInfoLoadType
1094 nsDocShell::ConvertLoadTypeToDocShellLoadInfo(PRUint32 aLoadType)
1096 nsDocShellInfoLoadType docShellLoadType = nsIDocShellLoadInfo::loadNormal;
1097 switch (aLoadType) {
1098 case LOAD_NORMAL:
1099 docShellLoadType = nsIDocShellLoadInfo::loadNormal;
1100 break;
1101 case LOAD_NORMAL_REPLACE:
1102 docShellLoadType = nsIDocShellLoadInfo::loadNormalReplace;
1103 break;
1104 case LOAD_NORMAL_EXTERNAL:
1105 docShellLoadType = nsIDocShellLoadInfo::loadNormalExternal;
1106 break;
1107 case LOAD_NORMAL_BYPASS_CACHE:
1108 docShellLoadType = nsIDocShellLoadInfo::loadNormalBypassCache;
1109 break;
1110 case LOAD_NORMAL_BYPASS_PROXY:
1111 docShellLoadType = nsIDocShellLoadInfo::loadNormalBypassProxy;
1112 break;
1113 case LOAD_NORMAL_BYPASS_PROXY_AND_CACHE:
1114 docShellLoadType = nsIDocShellLoadInfo::loadNormalBypassProxyAndCache;
1115 break;
1116 case LOAD_HISTORY:
1117 docShellLoadType = nsIDocShellLoadInfo::loadHistory;
1118 break;
1119 case LOAD_RELOAD_NORMAL:
1120 docShellLoadType = nsIDocShellLoadInfo::loadReloadNormal;
1121 break;
1122 case LOAD_RELOAD_CHARSET_CHANGE:
1123 docShellLoadType = nsIDocShellLoadInfo::loadReloadCharsetChange;
1124 break;
1125 case LOAD_RELOAD_BYPASS_CACHE:
1126 docShellLoadType = nsIDocShellLoadInfo::loadReloadBypassCache;
1127 break;
1128 case LOAD_RELOAD_BYPASS_PROXY:
1129 docShellLoadType = nsIDocShellLoadInfo::loadReloadBypassProxy;
1130 break;
1131 case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
1132 docShellLoadType = nsIDocShellLoadInfo::loadReloadBypassProxyAndCache;
1133 break;
1134 case LOAD_LINK:
1135 docShellLoadType = nsIDocShellLoadInfo::loadLink;
1136 break;
1137 case LOAD_REFRESH:
1138 docShellLoadType = nsIDocShellLoadInfo::loadRefresh;
1139 break;
1140 case LOAD_BYPASS_HISTORY:
1141 case LOAD_ERROR_PAGE:
1142 docShellLoadType = nsIDocShellLoadInfo::loadBypassHistory;
1143 break;
1144 case LOAD_STOP_CONTENT:
1145 docShellLoadType = nsIDocShellLoadInfo::loadStopContent;
1146 break;
1147 case LOAD_STOP_CONTENT_AND_REPLACE:
1148 docShellLoadType = nsIDocShellLoadInfo::loadStopContentAndReplace;
1149 break;
1150 case LOAD_PUSHSTATE:
1151 docShellLoadType = nsIDocShellLoadInfo::loadPushState;
1152 break;
1153 default:
1154 NS_NOTREACHED("Unexpected load type value");
1157 return docShellLoadType;
1160 //*****************************************************************************
1161 // nsDocShell::nsIDocShell
1162 //*****************************************************************************
1163 NS_IMETHODIMP
1164 nsDocShell::LoadURI(nsIURI * aURI,
1165 nsIDocShellLoadInfo * aLoadInfo,
1166 PRUint32 aLoadFlags,
1167 PRBool aFirstParty)
1169 NS_PRECONDITION(aLoadInfo || (aLoadFlags & EXTRA_LOAD_FLAGS) == 0,
1170 "Unexpected flags");
1171 NS_PRECONDITION((aLoadFlags & 0xf) == 0, "Should not have these flags set");
1173 // Note: we allow loads to get through here even if mFiredUnloadEvent is
1174 // true; that case will get handled in LoadInternal or LoadHistoryEntry.
1175 if (IsPrintingOrPP()) {
1176 return NS_OK; // JS may not handle returning of an error code
1178 nsresult rv;
1179 nsCOMPtr<nsIURI> referrer;
1180 nsCOMPtr<nsIInputStream> postStream;
1181 nsCOMPtr<nsIInputStream> headersStream;
1182 nsCOMPtr<nsISupports> owner;
1183 PRBool inheritOwner = PR_FALSE;
1184 PRBool ownerIsExplicit = PR_FALSE;
1185 PRBool sendReferrer = PR_TRUE;
1186 nsCOMPtr<nsISHEntry> shEntry;
1187 nsXPIDLString target;
1188 PRUint32 loadType = MAKE_LOAD_TYPE(LOAD_NORMAL, aLoadFlags);
1190 NS_ENSURE_ARG(aURI);
1192 // Extract the info from the DocShellLoadInfo struct...
1193 if (aLoadInfo) {
1194 aLoadInfo->GetReferrer(getter_AddRefs(referrer));
1196 nsDocShellInfoLoadType lt = nsIDocShellLoadInfo::loadNormal;
1197 aLoadInfo->GetLoadType(&lt);
1198 // Get the appropriate loadType from nsIDocShellLoadInfo type
1199 loadType = ConvertDocShellLoadInfoToLoadType(lt);
1201 aLoadInfo->GetOwner(getter_AddRefs(owner));
1202 aLoadInfo->GetInheritOwner(&inheritOwner);
1203 aLoadInfo->GetOwnerIsExplicit(&ownerIsExplicit);
1204 aLoadInfo->GetSHEntry(getter_AddRefs(shEntry));
1205 aLoadInfo->GetTarget(getter_Copies(target));
1206 aLoadInfo->GetPostDataStream(getter_AddRefs(postStream));
1207 aLoadInfo->GetHeadersStream(getter_AddRefs(headersStream));
1208 aLoadInfo->GetSendReferrer(&sendReferrer);
1211 #if defined(PR_LOGGING) && defined(DEBUG)
1212 if (PR_LOG_TEST(gDocShellLog, PR_LOG_DEBUG)) {
1213 nsCAutoString uristr;
1214 aURI->GetAsciiSpec(uristr);
1215 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
1216 ("nsDocShell[%p]: loading %s with flags 0x%08x",
1217 this, uristr.get(), aLoadFlags));
1219 #endif
1221 if (!shEntry &&
1222 !LOAD_TYPE_HAS_FLAGS(loadType, LOAD_FLAGS_REPLACE_HISTORY)) {
1223 // First verify if this is a subframe.
1224 nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
1225 GetSameTypeParent(getter_AddRefs(parentAsItem));
1226 nsCOMPtr<nsIDocShell> parentDS(do_QueryInterface(parentAsItem));
1227 PRUint32 parentLoadType;
1229 if (parentDS && parentDS != static_cast<nsIDocShell *>(this)) {
1230 /* OK. It is a subframe. Checkout the
1231 * parent's loadtype. If the parent was loaded thro' a history
1232 * mechanism, then get the SH entry for the child from the parent.
1233 * This is done to restore frameset navigation while going back/forward.
1234 * If the parent was loaded through any other loadType, set the
1235 * child's loadType too accordingly, so that session history does not
1236 * get confused.
1239 // Get the parent's load type
1240 parentDS->GetLoadType(&parentLoadType);
1242 nsCOMPtr<nsIDocShellHistory> parent(do_QueryInterface(parentAsItem));
1243 if (parent) {
1244 // Get the ShEntry for the child from the parent
1245 nsCOMPtr<nsISHEntry> currentSH;
1246 PRBool oshe = PR_FALSE;
1247 parent->GetCurrentSHEntry(getter_AddRefs(currentSH), &oshe);
1248 PRBool dynamicallyAddedChild = mDynamicallyCreated;
1249 if (!dynamicallyAddedChild && !oshe && currentSH) {
1250 currentSH->HasDynamicallyAddedChild(&dynamicallyAddedChild);
1252 if (!dynamicallyAddedChild) {
1253 // Only use the old SHEntry, if we're sure enough that
1254 // it wasn't originally for some other frame.
1255 parent->GetChildSHEntry(mChildOffset, getter_AddRefs(shEntry));
1258 // Make some decisions on the child frame's loadType based on the
1259 // parent's loadType.
1260 if (mCurrentURI == nsnull) {
1261 // This is a newly created frame. Check for exception cases first.
1262 // By default the subframe will inherit the parent's loadType.
1263 if (shEntry && (parentLoadType == LOAD_NORMAL ||
1264 parentLoadType == LOAD_LINK ||
1265 parentLoadType == LOAD_NORMAL_EXTERNAL)) {
1266 // The parent was loaded normally. In this case, this *brand new* child really shouldn't
1267 // have a SHEntry. If it does, it could be because the parent is replacing an
1268 // existing frame with a new frame, in the onLoadHandler. We don't want this
1269 // url to get into session history. Clear off shEntry, and set load type to
1270 // LOAD_BYPASS_HISTORY.
1271 PRBool inOnLoadHandler=PR_FALSE;
1272 parentDS->GetIsExecutingOnLoadHandler(&inOnLoadHandler);
1273 if (inOnLoadHandler) {
1274 loadType = LOAD_NORMAL_REPLACE;
1275 shEntry = nsnull;
1278 else if (parentLoadType == LOAD_REFRESH) {
1279 // Clear shEntry. For refresh loads, we have to load
1280 // what comes thro' the pipe, not what's in history.
1281 shEntry = nsnull;
1283 else if ((parentLoadType == LOAD_BYPASS_HISTORY) ||
1284 (parentLoadType == LOAD_ERROR_PAGE) ||
1285 (shEntry &&
1286 ((parentLoadType & LOAD_CMD_HISTORY) ||
1287 (parentLoadType == LOAD_RELOAD_NORMAL) ||
1288 (parentLoadType == LOAD_RELOAD_CHARSET_CHANGE)))) {
1289 // If the parent url, bypassed history or was loaded from
1290 // history, pass on the parent's loadType to the new child
1291 // frame too, so that the child frame will also
1292 // avoid getting into history.
1293 loadType = parentLoadType;
1296 else {
1297 // This is a pre-existing subframe. If the load was not originally initiated
1298 // by session history, (if (!shEntry) condition succeeded) and mCurrentURI is not null,
1299 // it is possible that a parent's onLoadHandler or even self's onLoadHandler is loading
1300 // a new page in this child. Check parent's and self's busy flag and if it is set,
1301 // we don't want this onLoadHandler load to get in to session history.
1302 PRUint32 parentBusy = BUSY_FLAGS_NONE;
1303 PRUint32 selfBusy = BUSY_FLAGS_NONE;
1304 parentDS->GetBusyFlags(&parentBusy);
1305 GetBusyFlags(&selfBusy);
1306 if (((parentBusy & BUSY_FLAGS_BUSY) ||
1307 (selfBusy & BUSY_FLAGS_BUSY)) &&
1308 shEntry) {
1309 loadType = LOAD_NORMAL_REPLACE;
1310 shEntry = nsnull;
1313 } // parent
1314 } //parentDS
1315 else {
1316 // This is the root docshell. If we got here while
1317 // executing an onLoad Handler,this load will not go
1318 // into session history.
1319 PRBool inOnLoadHandler=PR_FALSE;
1320 GetIsExecutingOnLoadHandler(&inOnLoadHandler);
1321 if (inOnLoadHandler) {
1322 loadType = LOAD_NORMAL_REPLACE;
1325 } // !shEntry
1327 if (shEntry) {
1328 #ifdef DEBUG
1329 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
1330 ("nsDocShell[%p]: loading from session history", this));
1331 #endif
1333 return LoadHistoryEntry(shEntry, loadType);
1336 // Perform the load...
1338 // We need an owner (a referring principal).
1340 // If ownerIsExplicit is not set there are 4 possibilities:
1341 // (1) If the system principal was passed in and we're a typeContent
1342 // docshell, inherit the principal from the current document
1343 // instead.
1344 // (2) In all other cases when the principal passed in is not null,
1345 // use that principal.
1346 // (3) If the caller has allowed inheriting from the current document,
1347 // or if we're being called from system code (eg chrome JS or pure
1348 // C++) then inheritOwner should be true and InternalLoad will get
1349 // an owner from the current document. If none of these things are
1350 // true, then
1351 // (4) we pass a null owner into the channel, and an owner will be
1352 // created later from the channel's internal data.
1354 // If ownerIsExplicit *is* set, there are 4 possibilities
1355 // (1) If the system principal was passed in and we're a typeContent
1356 // docshell, return an error.
1357 // (2) In all other cases when the principal passed in is not null,
1358 // use that principal.
1359 // (3) If the caller has allowed inheriting from the current document,
1360 // then inheritOwner should be true and InternalLoad will get an owner
1361 // from the current document. If none of these things are true, then
1362 // (4) we pass a null owner into the channel, and an owner will be
1363 // created later from the channel's internal data.
1365 // NOTE: This all only works because the only thing the owner is used
1366 // for in InternalLoad is data:, javascript:, and about:blank
1367 // URIs. For other URIs this would all be dead wrong!
1369 nsCOMPtr<nsIScriptSecurityManager> secMan =
1370 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
1371 NS_ENSURE_SUCCESS(rv, rv);
1373 if (owner && mItemType != typeChrome) {
1374 nsCOMPtr<nsIPrincipal> ownerPrincipal = do_QueryInterface(owner);
1375 PRBool isSystem;
1376 rv = secMan->IsSystemPrincipal(ownerPrincipal, &isSystem);
1377 NS_ENSURE_SUCCESS(rv, rv);
1379 if (isSystem) {
1380 if (ownerIsExplicit) {
1381 return NS_ERROR_DOM_SECURITY_ERR;
1383 owner = nsnull;
1384 inheritOwner = PR_TRUE;
1387 if (!owner && !inheritOwner && !ownerIsExplicit) {
1388 // See if there's system or chrome JS code running
1389 rv = secMan->SubjectPrincipalIsSystem(&inheritOwner);
1390 if (NS_FAILED(rv)) {
1391 // Set it back to false
1392 inheritOwner = PR_FALSE;
1396 PRUint32 flags = 0;
1398 if (inheritOwner)
1399 flags |= INTERNAL_LOAD_FLAGS_INHERIT_OWNER;
1401 if (!sendReferrer)
1402 flags |= INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER;
1404 if (aLoadFlags & LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP)
1405 flags |= INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
1407 if (aLoadFlags & LOAD_FLAGS_FIRST_LOAD)
1408 flags |= INTERNAL_LOAD_FLAGS_FIRST_LOAD;
1410 if (aLoadFlags & LOAD_FLAGS_BYPASS_CLASSIFIER)
1411 flags |= INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER;
1413 if (aLoadFlags & LOAD_FLAGS_FORCE_ALLOW_COOKIES)
1414 flags |= INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES;
1416 return InternalLoad(aURI,
1417 referrer,
1418 owner,
1419 flags,
1420 target.get(),
1421 nsnull, // No type hint
1422 postStream,
1423 headersStream,
1424 loadType,
1425 nsnull, // No SHEntry
1426 aFirstParty,
1427 nsnull, // No nsIDocShell
1428 nsnull); // No nsIRequest
1431 NS_IMETHODIMP
1432 nsDocShell::LoadStream(nsIInputStream *aStream, nsIURI * aURI,
1433 const nsACString &aContentType,
1434 const nsACString &aContentCharset,
1435 nsIDocShellLoadInfo * aLoadInfo)
1437 NS_ENSURE_ARG(aStream);
1439 mAllowKeywordFixup = PR_FALSE;
1441 // if the caller doesn't pass in a URI we need to create a dummy URI. necko
1442 // currently requires a URI in various places during the load. Some consumers
1443 // do as well.
1444 nsCOMPtr<nsIURI> uri = aURI;
1445 if (!uri) {
1446 // HACK ALERT
1447 nsresult rv = NS_OK;
1448 uri = do_CreateInstance(NS_SIMPLEURI_CONTRACTID, &rv);
1449 if (NS_FAILED(rv))
1450 return rv;
1451 // Make sure that the URI spec "looks" like a protocol and path...
1452 // For now, just use a bogus protocol called "internal"
1453 rv = uri->SetSpec(NS_LITERAL_CSTRING("internal:load-stream"));
1454 if (NS_FAILED(rv))
1455 return rv;
1458 PRUint32 loadType = LOAD_NORMAL;
1459 if (aLoadInfo) {
1460 nsDocShellInfoLoadType lt = nsIDocShellLoadInfo::loadNormal;
1461 (void) aLoadInfo->GetLoadType(&lt);
1462 // Get the appropriate LoadType from nsIDocShellLoadInfo type
1463 loadType = ConvertDocShellLoadInfoToLoadType(lt);
1466 NS_ENSURE_SUCCESS(Stop(nsIWebNavigation::STOP_NETWORK), NS_ERROR_FAILURE);
1468 mLoadType = loadType;
1470 // build up a channel for this stream.
1471 nsCOMPtr<nsIChannel> channel;
1472 NS_ENSURE_SUCCESS(NS_NewInputStreamChannel
1473 (getter_AddRefs(channel), uri, aStream,
1474 aContentType, aContentCharset),
1475 NS_ERROR_FAILURE);
1477 nsCOMPtr<nsIURILoader>
1478 uriLoader(do_GetService(NS_URI_LOADER_CONTRACTID));
1479 NS_ENSURE_TRUE(uriLoader, NS_ERROR_FAILURE);
1481 NS_ENSURE_SUCCESS(DoChannelLoad(channel, uriLoader, PR_FALSE),
1482 NS_ERROR_FAILURE);
1483 return NS_OK;
1486 NS_IMETHODIMP
1487 nsDocShell::CreateLoadInfo(nsIDocShellLoadInfo ** aLoadInfo)
1489 nsDocShellLoadInfo *loadInfo = new nsDocShellLoadInfo();
1490 NS_ENSURE_TRUE(loadInfo, NS_ERROR_OUT_OF_MEMORY);
1491 nsCOMPtr<nsIDocShellLoadInfo> localRef(loadInfo);
1493 *aLoadInfo = localRef;
1494 NS_ADDREF(*aLoadInfo);
1495 return NS_OK;
1500 * Reset state to a new content model within the current document and the document
1501 * viewer. Called by the document before initiating an out of band document.write().
1503 NS_IMETHODIMP
1504 nsDocShell::PrepareForNewContentModel()
1506 mEODForCurrentDocument = PR_FALSE;
1507 return NS_OK;
1511 NS_IMETHODIMP
1512 nsDocShell::FirePageHideNotification(PRBool aIsUnload)
1514 if (mContentViewer && !mFiredUnloadEvent) {
1515 // Keep an explicit reference since calling PageHide could release
1516 // mContentViewer
1517 nsCOMPtr<nsIContentViewer> kungFuDeathGrip(mContentViewer);
1518 mFiredUnloadEvent = PR_TRUE;
1520 mContentViewer->PageHide(aIsUnload);
1522 nsAutoTArray<nsCOMPtr<nsIDocShell>, 8> kids;
1523 PRInt32 i, n = mChildList.Count();
1524 kids.SetCapacity(n);
1525 for (i = 0; i < n; i++) {
1526 kids.AppendElement(do_QueryInterface(ChildAt(i)));
1529 n = kids.Length();
1530 for (i = 0; i < n; ++i) {
1531 if (kids[i]) {
1532 kids[i]->FirePageHideNotification(aIsUnload);
1535 // Now make sure our editor, if any, is detached before we go
1536 // any farther.
1537 DetachEditorFromWindow();
1540 return NS_OK;
1544 // Bug 13871: Prevent frameset spoofing
1546 // This routine answers: 'Is origin's document from same domain as
1547 // target's document?'
1549 // file: uris are considered the same domain for the purpose of
1550 // frame navigation regardless of script accessibility (bug 420425)
1552 /* static */
1553 PRBool
1554 nsDocShell::ValidateOrigin(nsIDocShellTreeItem* aOriginTreeItem,
1555 nsIDocShellTreeItem* aTargetTreeItem)
1557 nsCOMPtr<nsIScriptSecurityManager> securityManager =
1558 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
1559 NS_ENSURE_TRUE(securityManager, PR_FALSE);
1561 nsCOMPtr<nsIPrincipal> subjectPrincipal;
1562 nsresult rv =
1563 securityManager->GetSubjectPrincipal(getter_AddRefs(subjectPrincipal));
1564 NS_ENSURE_SUCCESS(rv, PR_FALSE);
1566 if (subjectPrincipal) {
1567 // We're called from JS, check if UniversalBrowserWrite is
1568 // enabled.
1569 PRBool ubwEnabled = PR_FALSE;
1570 rv = securityManager->IsCapabilityEnabled("UniversalBrowserWrite",
1571 &ubwEnabled);
1572 NS_ENSURE_SUCCESS(rv, PR_FALSE);
1574 if (ubwEnabled) {
1575 return PR_TRUE;
1579 // Get origin document principal
1580 nsCOMPtr<nsIDocument> originDocument(do_GetInterface(aOriginTreeItem));
1581 NS_ENSURE_TRUE(originDocument, PR_FALSE);
1583 // Get target principal
1584 nsCOMPtr<nsIDocument> targetDocument(do_GetInterface(aTargetTreeItem));
1585 NS_ENSURE_TRUE(targetDocument, PR_FALSE);
1587 PRBool equal;
1588 rv = originDocument->NodePrincipal()->
1589 Equals(targetDocument->NodePrincipal(), &equal);
1590 if (NS_SUCCEEDED(rv) && equal) {
1591 return PR_TRUE;
1594 // Not strictly equal, special case if both are file: uris
1595 PRBool originIsFile = PR_FALSE;
1596 PRBool targetIsFile = PR_FALSE;
1597 nsCOMPtr<nsIURI> originURI;
1598 nsCOMPtr<nsIURI> targetURI;
1599 nsCOMPtr<nsIURI> innerOriginURI;
1600 nsCOMPtr<nsIURI> innerTargetURI;
1602 rv = originDocument->NodePrincipal()->GetURI(getter_AddRefs(originURI));
1603 if (NS_SUCCEEDED(rv) && originURI)
1604 innerOriginURI = NS_GetInnermostURI(originURI);
1606 rv = targetDocument->NodePrincipal()->GetURI(getter_AddRefs(targetURI));
1607 if (NS_SUCCEEDED(rv) && targetURI)
1608 innerTargetURI = NS_GetInnermostURI(targetURI);
1610 return innerOriginURI && innerTargetURI &&
1611 NS_SUCCEEDED(innerOriginURI->SchemeIs("file", &originIsFile)) &&
1612 NS_SUCCEEDED(innerTargetURI->SchemeIs("file", &targetIsFile)) &&
1613 originIsFile && targetIsFile;
1616 NS_IMETHODIMP
1617 nsDocShell::GetEldestPresContext(nsPresContext** aPresContext)
1619 nsresult rv = NS_OK;
1621 NS_ENSURE_ARG_POINTER(aPresContext);
1622 *aPresContext = nsnull;
1624 nsCOMPtr<nsIContentViewer> viewer = mContentViewer;
1625 while (viewer) {
1626 nsCOMPtr<nsIContentViewer> prevViewer;
1627 viewer->GetPreviousViewer(getter_AddRefs(prevViewer));
1628 if (prevViewer)
1629 viewer = prevViewer;
1630 else {
1631 nsCOMPtr<nsIDocumentViewer> docv(do_QueryInterface(viewer));
1632 if (docv)
1633 rv = docv->GetPresContext(aPresContext);
1634 break;
1638 return rv;
1641 NS_IMETHODIMP
1642 nsDocShell::GetPresContext(nsPresContext ** aPresContext)
1644 NS_ENSURE_ARG_POINTER(aPresContext);
1645 *aPresContext = nsnull;
1647 if (!mContentViewer)
1648 return NS_OK;
1650 nsCOMPtr<nsIDocumentViewer> docv(do_QueryInterface(mContentViewer));
1651 NS_ENSURE_TRUE(docv, NS_ERROR_NO_INTERFACE);
1653 return docv->GetPresContext(aPresContext);
1656 NS_IMETHODIMP
1657 nsDocShell::GetPresShell(nsIPresShell ** aPresShell)
1659 nsresult rv = NS_OK;
1661 NS_ENSURE_ARG_POINTER(aPresShell);
1662 *aPresShell = nsnull;
1664 nsRefPtr<nsPresContext> presContext;
1665 (void) GetPresContext(getter_AddRefs(presContext));
1667 if (presContext) {
1668 NS_IF_ADDREF(*aPresShell = presContext->GetPresShell());
1671 return rv;
1674 NS_IMETHODIMP
1675 nsDocShell::GetEldestPresShell(nsIPresShell** aPresShell)
1677 nsresult rv = NS_OK;
1679 NS_ENSURE_ARG_POINTER(aPresShell);
1680 *aPresShell = nsnull;
1682 nsRefPtr<nsPresContext> presContext;
1683 (void) GetEldestPresContext(getter_AddRefs(presContext));
1685 if (presContext) {
1686 NS_IF_ADDREF(*aPresShell = presContext->GetPresShell());
1689 return rv;
1692 NS_IMETHODIMP
1693 nsDocShell::GetContentViewer(nsIContentViewer ** aContentViewer)
1695 NS_ENSURE_ARG_POINTER(aContentViewer);
1697 *aContentViewer = mContentViewer;
1698 NS_IF_ADDREF(*aContentViewer);
1699 return NS_OK;
1702 NS_IMETHODIMP
1703 nsDocShell::SetChromeEventHandler(nsIDOMEventTarget* aChromeEventHandler)
1705 nsCOMPtr<nsPIDOMEventTarget> piTarget =
1706 do_QueryInterface(aChromeEventHandler);
1707 // Weak reference. Don't addref.
1708 mChromeEventHandler = piTarget;
1710 nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(mScriptGlobal));
1711 if (win) {
1712 win->SetChromeEventHandler(piTarget);
1715 return NS_OK;
1718 NS_IMETHODIMP
1719 nsDocShell::GetChromeEventHandler(nsIDOMEventTarget** aChromeEventHandler)
1721 NS_ENSURE_ARG_POINTER(aChromeEventHandler);
1722 nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(mChromeEventHandler);
1723 target.swap(*aChromeEventHandler);
1724 return NS_OK;
1727 /* [noscript] void setCurrentURI (in nsIURI uri); */
1728 NS_IMETHODIMP
1729 nsDocShell::SetCurrentURI(nsIURI *aURI)
1731 SetCurrentURI(aURI, nsnull, PR_TRUE);
1732 return NS_OK;
1735 PRBool
1736 nsDocShell::SetCurrentURI(nsIURI *aURI, nsIRequest *aRequest,
1737 PRBool aFireOnLocationChange)
1739 #ifdef PR_LOGGING
1740 if (gDocShellLeakLog && PR_LOG_TEST(gDocShellLeakLog, PR_LOG_DEBUG)) {
1741 nsCAutoString spec;
1742 if (aURI)
1743 aURI->GetSpec(spec);
1744 PR_LogPrint("DOCSHELL %p SetCurrentURI %s\n", this, spec.get());
1746 #endif
1748 // We don't want to send a location change when we're displaying an error
1749 // page, and we don't want to change our idea of "current URI" either
1750 if (mLoadType == LOAD_ERROR_PAGE) {
1751 return PR_FALSE;
1754 mCurrentURI = NS_TryToMakeImmutable(aURI);
1756 PRBool isRoot = PR_FALSE; // Is this the root docshell
1757 PRBool isSubFrame = PR_FALSE; // Is this a subframe navigation?
1759 nsCOMPtr<nsIDocShellTreeItem> root;
1761 GetSameTypeRootTreeItem(getter_AddRefs(root));
1762 if (root.get() == static_cast<nsIDocShellTreeItem *>(this))
1764 // This is the root docshell
1765 isRoot = PR_TRUE;
1767 if (mLSHE) {
1768 mLSHE->GetIsSubFrame(&isSubFrame);
1771 if (!isSubFrame && !isRoot) {
1773 * We don't want to send OnLocationChange notifications when
1774 * a subframe is being loaded for the first time, while
1775 * visiting a frameset page
1777 return PR_FALSE;
1780 if (aFireOnLocationChange) {
1781 FireOnLocationChange(this, aRequest, aURI);
1783 return !aFireOnLocationChange;
1786 NS_IMETHODIMP
1787 nsDocShell::GetCharset(char** aCharset)
1789 NS_ENSURE_ARG_POINTER(aCharset);
1790 *aCharset = nsnull;
1792 nsCOMPtr<nsIPresShell> presShell;
1793 GetPresShell(getter_AddRefs(presShell));
1794 NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
1795 nsIDocument *doc = presShell->GetDocument();
1796 NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
1797 *aCharset = ToNewCString(doc->GetDocumentCharacterSet());
1798 if (!*aCharset) {
1799 return NS_ERROR_OUT_OF_MEMORY;
1802 return NS_OK;
1805 NS_IMETHODIMP
1806 nsDocShell::SetCharset(const char* aCharset)
1808 // set the default charset
1809 nsCOMPtr<nsIContentViewer> viewer;
1810 GetContentViewer(getter_AddRefs(viewer));
1811 if (viewer) {
1812 nsCOMPtr<nsIMarkupDocumentViewer> muDV(do_QueryInterface(viewer));
1813 if (muDV) {
1814 nsCString charset(aCharset);
1815 NS_ENSURE_SUCCESS(muDV->SetDefaultCharacterSet(charset),
1816 NS_ERROR_FAILURE);
1820 // set the charset override
1821 nsCOMPtr<nsIDocumentCharsetInfo> dcInfo;
1822 GetDocumentCharsetInfo(getter_AddRefs(dcInfo));
1823 if (dcInfo) {
1824 nsCOMPtr<nsIAtom> csAtom;
1825 csAtom = do_GetAtom(aCharset);
1826 dcInfo->SetForcedCharset(csAtom);
1829 return NS_OK;
1832 NS_IMETHODIMP
1833 nsDocShell::GetDocumentCharsetInfo(nsIDocumentCharsetInfo **
1834 aDocumentCharsetInfo)
1836 NS_ENSURE_ARG_POINTER(aDocumentCharsetInfo);
1838 // if the mDocumentCharsetInfo does not exist already, we create it now
1839 if (!mDocumentCharsetInfo) {
1840 mDocumentCharsetInfo = do_CreateInstance(NS_DOCUMENTCHARSETINFO_CONTRACTID);
1841 if (!mDocumentCharsetInfo)
1842 return NS_ERROR_FAILURE;
1845 *aDocumentCharsetInfo = mDocumentCharsetInfo;
1846 NS_IF_ADDREF(*aDocumentCharsetInfo);
1847 return NS_OK;
1850 NS_IMETHODIMP
1851 nsDocShell::SetDocumentCharsetInfo(nsIDocumentCharsetInfo *
1852 aDocumentCharsetInfo)
1854 mDocumentCharsetInfo = aDocumentCharsetInfo;
1855 return NS_OK;
1858 NS_IMETHODIMP
1859 nsDocShell::GetChannelIsUnsafe(PRBool *aUnsafe)
1861 *aUnsafe = PR_FALSE;
1863 nsIChannel* channel = GetCurrentDocChannel();
1864 if (!channel) {
1865 return NS_OK;
1868 nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(channel);
1869 if (!jarChannel) {
1870 return NS_OK;
1873 return jarChannel->GetIsUnsafe(aUnsafe);
1876 NS_IMETHODIMP
1877 nsDocShell::GetAllowPlugins(PRBool * aAllowPlugins)
1879 NS_ENSURE_ARG_POINTER(aAllowPlugins);
1881 *aAllowPlugins = mAllowPlugins;
1882 if (!mAllowPlugins) {
1883 return NS_OK;
1886 PRBool unsafe;
1887 *aAllowPlugins = NS_SUCCEEDED(GetChannelIsUnsafe(&unsafe)) && !unsafe;
1888 return NS_OK;
1891 NS_IMETHODIMP
1892 nsDocShell::SetAllowPlugins(PRBool aAllowPlugins)
1894 mAllowPlugins = aAllowPlugins;
1895 //XXX should enable or disable a plugin host
1896 return NS_OK;
1899 NS_IMETHODIMP
1900 nsDocShell::GetAllowJavascript(PRBool * aAllowJavascript)
1902 NS_ENSURE_ARG_POINTER(aAllowJavascript);
1904 *aAllowJavascript = mAllowJavascript;
1905 if (!mAllowJavascript) {
1906 return NS_OK;
1909 PRBool unsafe;
1910 *aAllowJavascript = NS_SUCCEEDED(GetChannelIsUnsafe(&unsafe)) && !unsafe;
1911 return NS_OK;
1914 NS_IMETHODIMP
1915 nsDocShell::SetAllowJavascript(PRBool aAllowJavascript)
1917 mAllowJavascript = aAllowJavascript;
1918 return NS_OK;
1921 NS_IMETHODIMP nsDocShell::GetAllowMetaRedirects(PRBool * aReturn)
1923 NS_ENSURE_ARG_POINTER(aReturn);
1925 *aReturn = mAllowMetaRedirects;
1926 if (!mAllowMetaRedirects) {
1927 return NS_OK;
1930 PRBool unsafe;
1931 *aReturn = NS_SUCCEEDED(GetChannelIsUnsafe(&unsafe)) && !unsafe;
1932 return NS_OK;
1935 NS_IMETHODIMP nsDocShell::SetAllowMetaRedirects(PRBool aValue)
1937 mAllowMetaRedirects = aValue;
1938 return NS_OK;
1941 NS_IMETHODIMP nsDocShell::GetAllowSubframes(PRBool * aAllowSubframes)
1943 NS_ENSURE_ARG_POINTER(aAllowSubframes);
1945 *aAllowSubframes = mAllowSubframes;
1946 return NS_OK;
1949 NS_IMETHODIMP nsDocShell::SetAllowSubframes(PRBool aAllowSubframes)
1951 mAllowSubframes = aAllowSubframes;
1952 return NS_OK;
1955 NS_IMETHODIMP nsDocShell::GetAllowImages(PRBool * aAllowImages)
1957 NS_ENSURE_ARG_POINTER(aAllowImages);
1959 *aAllowImages = mAllowImages;
1960 return NS_OK;
1963 NS_IMETHODIMP nsDocShell::SetAllowImages(PRBool aAllowImages)
1965 mAllowImages = aAllowImages;
1966 return NS_OK;
1969 NS_IMETHODIMP nsDocShell::GetAllowDNSPrefetch(PRBool * aAllowDNSPrefetch)
1971 *aAllowDNSPrefetch = mAllowDNSPrefetch;
1972 return NS_OK;
1975 NS_IMETHODIMP nsDocShell::SetAllowDNSPrefetch(PRBool aAllowDNSPrefetch)
1977 mAllowDNSPrefetch = aAllowDNSPrefetch;
1978 return NS_OK;
1981 NS_IMETHODIMP
1982 nsDocShell::GetDocShellEnumerator(PRInt32 aItemType, PRInt32 aDirection, nsISimpleEnumerator **outEnum)
1984 NS_ENSURE_ARG_POINTER(outEnum);
1985 *outEnum = nsnull;
1987 nsRefPtr<nsDocShellEnumerator> docShellEnum;
1988 if (aDirection == ENUMERATE_FORWARDS)
1989 docShellEnum = new nsDocShellForwardsEnumerator;
1990 else
1991 docShellEnum = new nsDocShellBackwardsEnumerator;
1993 if (!docShellEnum) return NS_ERROR_OUT_OF_MEMORY;
1995 nsresult rv = docShellEnum->SetEnumDocShellType(aItemType);
1996 if (NS_FAILED(rv)) return rv;
1998 rv = docShellEnum->SetEnumerationRootItem((nsIDocShellTreeItem *)this);
1999 if (NS_FAILED(rv)) return rv;
2001 rv = docShellEnum->First();
2002 if (NS_FAILED(rv)) return rv;
2004 rv = docShellEnum->QueryInterface(NS_GET_IID(nsISimpleEnumerator), (void **)outEnum);
2006 return rv;
2009 NS_IMETHODIMP
2010 nsDocShell::GetAppType(PRUint32 * aAppType)
2012 *aAppType = mAppType;
2013 return NS_OK;
2016 NS_IMETHODIMP
2017 nsDocShell::SetAppType(PRUint32 aAppType)
2019 mAppType = aAppType;
2020 return NS_OK;
2024 NS_IMETHODIMP
2025 nsDocShell::GetAllowAuth(PRBool * aAllowAuth)
2027 *aAllowAuth = mAllowAuth;
2028 return NS_OK;
2031 NS_IMETHODIMP
2032 nsDocShell::SetAllowAuth(PRBool aAllowAuth)
2034 mAllowAuth = aAllowAuth;
2035 return NS_OK;
2038 NS_IMETHODIMP
2039 nsDocShell::GetZoom(float *zoom)
2041 NS_ENSURE_ARG_POINTER(zoom);
2042 *zoom = 1.0f;
2043 return NS_OK;
2046 NS_IMETHODIMP
2047 nsDocShell::SetZoom(float zoom)
2049 return NS_ERROR_NOT_IMPLEMENTED;
2052 NS_IMETHODIMP
2053 nsDocShell::GetMarginWidth(PRInt32 * aWidth)
2055 NS_ENSURE_ARG_POINTER(aWidth);
2057 *aWidth = mMarginWidth;
2058 return NS_OK;
2061 NS_IMETHODIMP
2062 nsDocShell::SetMarginWidth(PRInt32 aWidth)
2064 mMarginWidth = aWidth;
2065 return NS_OK;
2068 NS_IMETHODIMP
2069 nsDocShell::GetMarginHeight(PRInt32 * aHeight)
2071 NS_ENSURE_ARG_POINTER(aHeight);
2073 *aHeight = mMarginHeight;
2074 return NS_OK;
2077 NS_IMETHODIMP
2078 nsDocShell::SetMarginHeight(PRInt32 aHeight)
2080 mMarginHeight = aHeight;
2081 return NS_OK;
2084 NS_IMETHODIMP
2085 nsDocShell::GetBusyFlags(PRUint32 * aBusyFlags)
2087 NS_ENSURE_ARG_POINTER(aBusyFlags);
2089 *aBusyFlags = mBusyFlags;
2090 return NS_OK;
2093 NS_IMETHODIMP
2094 nsDocShell::TabToTreeOwner(PRBool aForward, PRBool* aTookFocus)
2096 NS_ENSURE_ARG_POINTER(aTookFocus);
2098 nsCOMPtr<nsIWebBrowserChromeFocus> chromeFocus = do_GetInterface(mTreeOwner);
2099 if (chromeFocus) {
2100 if (aForward)
2101 *aTookFocus = NS_SUCCEEDED(chromeFocus->FocusNextElement());
2102 else
2103 *aTookFocus = NS_SUCCEEDED(chromeFocus->FocusPrevElement());
2104 } else
2105 *aTookFocus = PR_FALSE;
2107 return NS_OK;
2110 NS_IMETHODIMP
2111 nsDocShell::GetSecurityUI(nsISecureBrowserUI **aSecurityUI)
2113 NS_IF_ADDREF(*aSecurityUI = mSecurityUI);
2114 return NS_OK;
2117 NS_IMETHODIMP
2118 nsDocShell::SetSecurityUI(nsISecureBrowserUI *aSecurityUI)
2120 mSecurityUI = aSecurityUI;
2121 return NS_OK;
2124 NS_IMETHODIMP
2125 nsDocShell::GetUseErrorPages(PRBool *aUseErrorPages)
2127 *aUseErrorPages = mUseErrorPages;
2128 return NS_OK;
2131 NS_IMETHODIMP
2132 nsDocShell::SetUseErrorPages(PRBool aUseErrorPages)
2134 // If mUseErrorPages is set explicitly, stop observing the pref.
2135 if (mObserveErrorPages) {
2136 nsCOMPtr<nsIPrefBranch2> prefs(do_QueryInterface(mPrefs));
2137 if (prefs) {
2138 prefs->RemoveObserver("browser.xul.error_pages.enabled", this);
2139 mObserveErrorPages = PR_FALSE;
2142 mUseErrorPages = aUseErrorPages;
2143 return NS_OK;
2146 NS_IMETHODIMP
2147 nsDocShell::GetPreviousTransIndex(PRInt32 *aPreviousTransIndex)
2149 *aPreviousTransIndex = mPreviousTransIndex;
2150 return NS_OK;
2153 NS_IMETHODIMP
2154 nsDocShell::GetLoadedTransIndex(PRInt32 *aLoadedTransIndex)
2156 *aLoadedTransIndex = mLoadedTransIndex;
2157 return NS_OK;
2160 NS_IMETHODIMP
2161 nsDocShell::HistoryPurged(PRInt32 aNumEntries)
2163 // These indices are used for fastback cache eviction, to determine
2164 // which session history entries are candidates for content viewer
2165 // eviction. We need to adjust by the number of entries that we
2166 // just purged from history, so that we look at the right session history
2167 // entries during eviction.
2168 mPreviousTransIndex = NS_MAX(-1, mPreviousTransIndex - aNumEntries);
2169 mLoadedTransIndex = NS_MAX(0, mLoadedTransIndex - aNumEntries);
2171 PRInt32 count = mChildList.Count();
2172 for (PRInt32 i = 0; i < count; ++i) {
2173 nsCOMPtr<nsIDocShell> shell = do_QueryInterface(ChildAt(i));
2174 if (shell) {
2175 shell->HistoryPurged(aNumEntries);
2179 return NS_OK;
2182 nsresult
2183 nsDocShell::HistoryTransactionRemoved(PRInt32 aIndex)
2185 // These indices are used for fastback cache eviction, to determine
2186 // which session history entries are candidates for content viewer
2187 // eviction. We need to adjust by the number of entries that we
2188 // just purged from history, so that we look at the right session history
2189 // entries during eviction.
2190 if (aIndex == mPreviousTransIndex) {
2191 mPreviousTransIndex = -1;
2192 } else if (aIndex < mPreviousTransIndex) {
2193 --mPreviousTransIndex;
2195 if (mLoadedTransIndex == aIndex) {
2196 mLoadedTransIndex = 0;
2197 } else if (aIndex < mLoadedTransIndex) {
2198 --mLoadedTransIndex;
2201 PRInt32 count = mChildList.Count();
2202 for (PRInt32 i = 0; i < count; ++i) {
2203 nsCOMPtr<nsIDocShell> shell = do_QueryInterface(ChildAt(i));
2204 if (shell) {
2205 static_cast<nsDocShell*>(shell.get())->
2206 HistoryTransactionRemoved(aIndex);
2210 return NS_OK;
2213 static
2214 nsresult
2215 GetPrincipalDomain(nsIPrincipal* aPrincipal, nsACString& aDomain)
2217 aDomain.Truncate();
2219 nsCOMPtr<nsIURI> codebaseURI;
2220 nsresult rv = aPrincipal->GetDomain(getter_AddRefs(codebaseURI));
2221 NS_ENSURE_SUCCESS(rv, rv);
2222 if (!codebaseURI) {
2223 rv = aPrincipal->GetURI(getter_AddRefs(codebaseURI));
2224 NS_ENSURE_SUCCESS(rv, rv);
2227 if (!codebaseURI)
2228 return NS_OK;
2230 nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(codebaseURI);
2231 NS_ASSERTION(innerURI, "Failed to get innermost URI");
2232 NS_ENSURE_SUCCESS(rv, rv);
2234 rv = innerURI->GetAsciiHost(aDomain);
2235 if (NS_FAILED(rv))
2236 return rv;
2238 return NS_OK;
2241 NS_IMETHODIMP
2242 nsDocShell::GetSessionStorageForPrincipal(nsIPrincipal* aPrincipal,
2243 const nsAString& aDocumentURI,
2244 PRBool aCreate,
2245 nsIDOMStorage** aStorage)
2247 NS_ENSURE_ARG_POINTER(aStorage);
2248 *aStorage = nsnull;
2250 if (!aPrincipal)
2251 return NS_OK;
2253 nsresult rv;
2255 nsCOMPtr<nsIDocShellTreeItem> topItem;
2256 rv = GetSameTypeRootTreeItem(getter_AddRefs(topItem));
2257 if (NS_FAILED(rv))
2258 return rv;
2260 if (!topItem)
2261 return NS_ERROR_FAILURE;
2263 nsDocShell* topDocShell = static_cast<nsDocShell*>(topItem.get());
2264 if (topDocShell != this)
2265 return topDocShell->GetSessionStorageForPrincipal(aPrincipal,
2266 aDocumentURI,
2267 aCreate,
2268 aStorage);
2270 nsCAutoString currentDomain;
2271 rv = GetPrincipalDomain(aPrincipal, currentDomain);
2272 if (NS_FAILED(rv))
2273 return rv;
2275 if (currentDomain.IsEmpty())
2276 return NS_OK;
2278 if (!mStorages.Get(currentDomain, aStorage) && aCreate) {
2279 nsCOMPtr<nsIDOMStorage> newstorage =
2280 do_CreateInstance("@mozilla.org/dom/storage;2");
2281 if (!newstorage)
2282 return NS_ERROR_OUT_OF_MEMORY;
2284 nsCOMPtr<nsPIDOMStorage> pistorage = do_QueryInterface(newstorage);
2285 if (!pistorage)
2286 return NS_ERROR_FAILURE;
2287 rv = pistorage->InitAsSessionStorage(aPrincipal, aDocumentURI);
2288 if (NS_FAILED(rv))
2289 return rv;
2291 if (!mStorages.Put(currentDomain, newstorage))
2292 return NS_ERROR_OUT_OF_MEMORY;
2294 newstorage.swap(*aStorage);
2295 #if defined(PR_LOGGING) && defined(DEBUG)
2296 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
2297 ("nsDocShell[%p]: created a new sessionStorage %p",
2298 this, *aStorage));
2299 #endif
2301 else if (*aStorage) {
2302 nsCOMPtr<nsPIDOMStorage> piStorage = do_QueryInterface(*aStorage);
2303 if (piStorage) {
2304 PRBool canAccess = piStorage->CanAccess(aPrincipal);
2305 NS_ASSERTION(canAccess,
2306 "GetSessionStorageForPrincipal got a storage "
2307 "that could not be accessed!");
2308 if (!canAccess) {
2309 NS_RELEASE(*aStorage);
2310 return NS_ERROR_DOM_SECURITY_ERR;
2314 #if defined(PR_LOGGING) && defined(DEBUG)
2315 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
2316 ("nsDocShell[%p]: returns existing sessionStorage %p",
2317 this, *aStorage));
2318 #endif
2321 if (aCreate) {
2322 // We are asked to create a new storage object. This indicates
2323 // that a new windows wants it. At this moment we "fork" the existing
2324 // storage object (what it means is described in the paragraph bellow).
2325 // We must create a single object per a single window to distinguish
2326 // a window originating oparations on the storage object to succesfully
2327 // prevent dispatch of a storage event to this same window that ivoked
2328 // a change in its storage. We also do this to correctly fill
2329 // documentURI property in the storage event.
2331 // The difference between clone and fork is that clone creates
2332 // a completelly new and independent storage, but fork only creates
2333 // a new object wrapping the storage implementation and data and
2334 // the forked storage then behaves completelly the same way as
2335 // the storage it has been forked of, all such forked storage objects
2336 // shares their state and data and change on one such object affects
2337 // all others the same way.
2338 nsCOMPtr<nsPIDOMStorage> piStorage = do_QueryInterface(*aStorage);
2339 nsCOMPtr<nsIDOMStorage> fork = piStorage->Fork(aDocumentURI);
2340 #if defined(PR_LOGGING) && defined(DEBUG)
2341 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
2342 ("nsDocShell[%p]: forked sessionStorage %p to %p",
2343 this, *aStorage, fork.get()));
2344 #endif
2345 fork.swap(*aStorage);
2348 return NS_OK;
2351 NS_IMETHODIMP
2352 nsDocShell::GetSessionStorageForURI(nsIURI* aURI,
2353 const nsAString& aDocumentURI,
2354 nsIDOMStorage** aStorage)
2356 return GetSessionStorageForURI(aURI, aDocumentURI, PR_TRUE, aStorage);
2359 nsresult
2360 nsDocShell::GetSessionStorageForURI(nsIURI* aURI,
2361 const nsSubstring& aDocumentURI,
2362 PRBool aCreate,
2363 nsIDOMStorage** aStorage)
2365 NS_ENSURE_ARG(aURI);
2366 NS_ENSURE_ARG_POINTER(aStorage);
2368 *aStorage = nsnull;
2370 nsresult rv;
2372 nsCOMPtr<nsIScriptSecurityManager> securityManager =
2373 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
2374 NS_ENSURE_SUCCESS(rv, rv);
2376 // This is terrible hack and should go away along with this whole method.
2377 nsCOMPtr<nsIPrincipal> principal;
2378 rv = securityManager->GetCodebasePrincipal(aURI, getter_AddRefs(principal));
2379 if (NS_FAILED(rv))
2380 return rv;
2382 return GetSessionStorageForPrincipal(principal, aDocumentURI, aCreate, aStorage);
2385 nsresult
2386 nsDocShell::AddSessionStorage(nsIPrincipal* aPrincipal,
2387 nsIDOMStorage* aStorage)
2389 NS_ENSURE_ARG_POINTER(aStorage);
2391 if (!aPrincipal)
2392 return NS_OK;
2394 nsCOMPtr<nsIDocShellTreeItem> topItem;
2395 nsresult rv = GetSameTypeRootTreeItem(getter_AddRefs(topItem));
2396 if (NS_FAILED(rv))
2397 return rv;
2399 if (topItem) {
2400 nsCOMPtr<nsIDocShell> topDocShell = do_QueryInterface(topItem);
2401 if (topDocShell == this) {
2402 nsCAutoString currentDomain;
2403 rv = GetPrincipalDomain(aPrincipal, currentDomain);
2404 if (NS_FAILED(rv))
2405 return rv;
2407 if (currentDomain.IsEmpty())
2408 return NS_ERROR_FAILURE;
2410 // Do not replace an existing session storage.
2411 if (mStorages.GetWeak(currentDomain))
2412 return NS_ERROR_NOT_AVAILABLE;
2414 #if defined(PR_LOGGING) && defined(DEBUG)
2415 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
2416 ("nsDocShell[%p]: was added a sessionStorage %p",
2417 this, aStorage));
2418 #endif
2419 if (!mStorages.Put(currentDomain, aStorage))
2420 return NS_ERROR_OUT_OF_MEMORY;
2422 else {
2423 return topDocShell->AddSessionStorage(aPrincipal, aStorage);
2427 return NS_OK;
2430 NS_IMETHODIMP
2431 nsDocShell::GetCurrentDocumentChannel(nsIChannel** aResult)
2433 NS_IF_ADDREF(*aResult = GetCurrentDocChannel());
2434 return NS_OK;
2437 nsIChannel*
2438 nsDocShell::GetCurrentDocChannel()
2440 if (mContentViewer) {
2441 nsIDocument* doc = mContentViewer->GetDocument();
2442 if (doc) {
2443 return doc->GetChannel();
2446 return nsnull;
2449 //*****************************************************************************
2450 // nsDocShell::nsIDocShellTreeItem
2451 //*****************************************************************************
2453 NS_IMETHODIMP
2454 nsDocShell::GetName(PRUnichar ** aName)
2456 NS_ENSURE_ARG_POINTER(aName);
2457 *aName = ToNewUnicode(mName);
2458 return NS_OK;
2461 NS_IMETHODIMP
2462 nsDocShell::SetName(const PRUnichar * aName)
2464 mName = aName; // this does a copy of aName
2465 return NS_OK;
2468 NS_IMETHODIMP
2469 nsDocShell::NameEquals(const PRUnichar *aName, PRBool *_retval)
2471 NS_ENSURE_ARG_POINTER(aName);
2472 NS_ENSURE_ARG_POINTER(_retval);
2473 *_retval = mName.Equals(aName);
2474 return NS_OK;
2477 NS_IMETHODIMP
2478 nsDocShell::GetItemType(PRInt32 * aItemType)
2480 NS_ENSURE_ARG_POINTER(aItemType);
2482 *aItemType = mItemType;
2483 return NS_OK;
2486 NS_IMETHODIMP
2487 nsDocShell::SetItemType(PRInt32 aItemType)
2489 NS_ENSURE_ARG((aItemType == typeChrome) || (typeContent == aItemType));
2491 // Only allow setting the type on root docshells. Those would be the ones
2492 // that have the docloader service as mParent or have no mParent at all.
2493 nsCOMPtr<nsIDocumentLoader> docLoaderService =
2494 do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID);
2495 NS_ENSURE_TRUE(docLoaderService, NS_ERROR_UNEXPECTED);
2497 NS_ENSURE_STATE(!mParent || mParent == docLoaderService);
2499 mItemType = aItemType;
2501 // disable auth prompting for anything but content
2502 mAllowAuth = mItemType == typeContent;
2504 nsRefPtr<nsPresContext> presContext = nsnull;
2505 GetPresContext(getter_AddRefs(presContext));
2506 if (presContext) {
2507 presContext->InvalidateIsChromeCache();
2510 return NS_OK;
2513 NS_IMETHODIMP
2514 nsDocShell::GetParent(nsIDocShellTreeItem ** aParent)
2516 if (!mParent) {
2517 *aParent = nsnull;
2518 } else {
2519 CallQueryInterface(mParent, aParent);
2521 // Note that in the case when the parent is not an nsIDocShellTreeItem we
2522 // don't want to throw; we just want to return null.
2523 return NS_OK;
2526 nsresult
2527 nsDocShell::SetDocLoaderParent(nsDocLoader * aParent)
2529 nsDocLoader::SetDocLoaderParent(aParent);
2531 // Curse ambiguous nsISupports inheritance!
2532 nsISupports* parent = GetAsSupports(aParent);
2534 // If parent is another docshell, we inherit all their flags for
2535 // allowing plugins, scripting etc.
2536 nsCOMPtr<nsIDocShell> parentAsDocShell(do_QueryInterface(parent));
2537 if (parentAsDocShell)
2539 PRBool value;
2540 if (NS_SUCCEEDED(parentAsDocShell->GetAllowPlugins(&value)))
2542 SetAllowPlugins(value);
2544 if (NS_SUCCEEDED(parentAsDocShell->GetAllowJavascript(&value)))
2546 SetAllowJavascript(value);
2548 if (NS_SUCCEEDED(parentAsDocShell->GetAllowMetaRedirects(&value)))
2550 SetAllowMetaRedirects(value);
2552 if (NS_SUCCEEDED(parentAsDocShell->GetAllowSubframes(&value)))
2554 SetAllowSubframes(value);
2556 if (NS_SUCCEEDED(parentAsDocShell->GetAllowImages(&value)))
2558 SetAllowImages(value);
2560 if (NS_SUCCEEDED(parentAsDocShell->GetIsActive(&value)))
2562 SetIsActive(value);
2564 if (NS_FAILED(parentAsDocShell->GetAllowDNSPrefetch(&value))) {
2565 value = PR_FALSE;
2567 SetAllowDNSPrefetch(value);
2570 nsCOMPtr<nsIURIContentListener> parentURIListener(do_GetInterface(parent));
2571 if (parentURIListener)
2572 mContentListener->SetParentContentListener(parentURIListener);
2573 return NS_OK;
2576 NS_IMETHODIMP
2577 nsDocShell::GetSameTypeParent(nsIDocShellTreeItem ** aParent)
2579 NS_ENSURE_ARG_POINTER(aParent);
2580 *aParent = nsnull;
2582 nsCOMPtr<nsIDocShellTreeItem> parent =
2583 do_QueryInterface(GetAsSupports(mParent));
2584 if (!parent)
2585 return NS_OK;
2587 PRInt32 parentType;
2588 NS_ENSURE_SUCCESS(parent->GetItemType(&parentType), NS_ERROR_FAILURE);
2590 if (parentType == mItemType) {
2591 parent.swap(*aParent);
2593 return NS_OK;
2596 NS_IMETHODIMP
2597 nsDocShell::GetRootTreeItem(nsIDocShellTreeItem ** aRootTreeItem)
2599 NS_ENSURE_ARG_POINTER(aRootTreeItem);
2600 *aRootTreeItem = static_cast<nsIDocShellTreeItem *>(this);
2602 nsCOMPtr<nsIDocShellTreeItem> parent;
2603 NS_ENSURE_SUCCESS(GetParent(getter_AddRefs(parent)), NS_ERROR_FAILURE);
2604 while (parent) {
2605 *aRootTreeItem = parent;
2606 NS_ENSURE_SUCCESS((*aRootTreeItem)->GetParent(getter_AddRefs(parent)),
2607 NS_ERROR_FAILURE);
2609 NS_ADDREF(*aRootTreeItem);
2610 return NS_OK;
2613 NS_IMETHODIMP
2614 nsDocShell::GetSameTypeRootTreeItem(nsIDocShellTreeItem ** aRootTreeItem)
2616 NS_ENSURE_ARG_POINTER(aRootTreeItem);
2617 *aRootTreeItem = static_cast<nsIDocShellTreeItem *>(this);
2619 nsCOMPtr<nsIDocShellTreeItem> parent;
2620 NS_ENSURE_SUCCESS(GetSameTypeParent(getter_AddRefs(parent)),
2621 NS_ERROR_FAILURE);
2622 while (parent) {
2623 *aRootTreeItem = parent;
2624 NS_ENSURE_SUCCESS((*aRootTreeItem)->
2625 GetSameTypeParent(getter_AddRefs(parent)),
2626 NS_ERROR_FAILURE);
2628 NS_ADDREF(*aRootTreeItem);
2629 return NS_OK;
2632 /* static */
2633 PRBool
2634 nsDocShell::CanAccessItem(nsIDocShellTreeItem* aTargetItem,
2635 nsIDocShellTreeItem* aAccessingItem,
2636 PRBool aConsiderOpener)
2638 NS_PRECONDITION(aTargetItem, "Must have target item!");
2640 if (!gValidateOrigin || !aAccessingItem) {
2641 // Good to go
2642 return PR_TRUE;
2645 // XXXbz should we care if aAccessingItem or the document therein is
2646 // chrome? Should those get extra privileges?
2648 // For historical context, see:
2650 // Bug 13871: Prevent frameset spoofing
2651 // Bug 103638: Targets with same name in different windows open in wrong
2652 // window with javascript
2653 // Bug 408052: Adopt "ancestor" frame navigation policy
2655 // Now do a security check
2657 // Allow navigation if
2658 // 1) aAccessingItem can script aTargetItem or one of its ancestors in
2659 // the frame hierarchy or
2660 // 2) aTargetItem is a top-level frame and aAccessingItem is its descendant
2661 // 3) aTargetItem is a top-level frame and aAccessingItem can target
2662 // its opener per rule (1) or (2).
2664 if (aTargetItem == aAccessingItem) {
2665 // A frame is allowed to navigate itself.
2666 return PR_TRUE;
2669 nsCOMPtr<nsIDocShellTreeItem> accessingRoot;
2670 aAccessingItem->GetSameTypeRootTreeItem(getter_AddRefs(accessingRoot));
2672 if (aTargetItem == accessingRoot) {
2673 // A frame can navigate its root.
2674 return PR_TRUE;
2677 // Check if aAccessingItem can navigate one of aTargetItem's ancestors.
2678 nsCOMPtr<nsIDocShellTreeItem> target = aTargetItem;
2679 do {
2680 if (ValidateOrigin(aAccessingItem, target)) {
2681 return PR_TRUE;
2684 nsCOMPtr<nsIDocShellTreeItem> parent;
2685 target->GetSameTypeParent(getter_AddRefs(parent));
2686 parent.swap(target);
2687 } while (target);
2689 nsCOMPtr<nsIDocShellTreeItem> targetRoot;
2690 aTargetItem->GetSameTypeRootTreeItem(getter_AddRefs(targetRoot));
2692 if (aTargetItem != targetRoot) {
2693 // target is a subframe, not in accessor's frame hierarchy, and all its
2694 // ancestors have origins different from that of the accessor. Don't
2695 // allow access.
2696 return PR_FALSE;
2699 if (!aConsiderOpener) {
2700 // All done here
2701 return PR_FALSE;
2704 nsCOMPtr<nsIDOMWindow> targetWindow(do_GetInterface(aTargetItem));
2705 nsCOMPtr<nsIDOMWindowInternal> targetInternal(do_QueryInterface(targetWindow));
2706 if (!targetInternal) {
2707 NS_ERROR("This should not happen, really");
2708 return PR_FALSE;
2711 nsCOMPtr<nsIDOMWindowInternal> targetOpener;
2712 targetInternal->GetOpener(getter_AddRefs(targetOpener));
2713 nsCOMPtr<nsIWebNavigation> openerWebNav(do_GetInterface(targetOpener));
2714 nsCOMPtr<nsIDocShellTreeItem> openerItem(do_QueryInterface(openerWebNav));
2716 if (!openerItem) {
2717 return PR_FALSE;
2720 return CanAccessItem(openerItem, aAccessingItem, PR_FALSE);
2723 static PRBool
2724 ItemIsActive(nsIDocShellTreeItem *aItem)
2726 nsCOMPtr<nsIDOMWindow> tmp(do_GetInterface(aItem));
2727 nsCOMPtr<nsIDOMWindowInternal> window(do_QueryInterface(tmp));
2729 if (window) {
2730 PRBool isClosed;
2732 if (NS_SUCCEEDED(window->GetClosed(&isClosed)) && !isClosed) {
2733 return PR_TRUE;
2737 return PR_FALSE;
2740 NS_IMETHODIMP
2741 nsDocShell::FindItemWithName(const PRUnichar * aName,
2742 nsISupports * aRequestor,
2743 nsIDocShellTreeItem * aOriginalRequestor,
2744 nsIDocShellTreeItem ** _retval)
2746 NS_ENSURE_ARG(aName);
2747 NS_ENSURE_ARG_POINTER(_retval);
2749 // If we don't find one, we return NS_OK and a null result
2750 *_retval = nsnull;
2752 if (!*aName)
2753 return NS_OK;
2755 if (!aRequestor)
2757 nsCOMPtr<nsIDocShellTreeItem> foundItem;
2759 // This is the entry point into the target-finding algorithm. Check
2760 // for special names. This should only be done once, hence the check
2761 // for a null aRequestor.
2763 nsDependentString name(aName);
2764 if (name.LowerCaseEqualsLiteral("_self")) {
2765 foundItem = this;
2767 else if (name.LowerCaseEqualsLiteral("_blank"))
2769 // Just return null. Caller must handle creating a new window with
2770 // a blank name himself.
2771 return NS_OK;
2773 else if (name.LowerCaseEqualsLiteral("_parent"))
2775 GetSameTypeParent(getter_AddRefs(foundItem));
2776 if(!foundItem)
2777 foundItem = this;
2779 else if (name.LowerCaseEqualsLiteral("_top"))
2781 GetSameTypeRootTreeItem(getter_AddRefs(foundItem));
2782 NS_ASSERTION(foundItem, "Must have this; worst case it's us!");
2784 // _main is an IE target which should be case-insensitive but isn't
2785 // see bug 217886 for details
2786 else if (name.LowerCaseEqualsLiteral("_content") ||
2787 name.EqualsLiteral("_main"))
2789 // Must pass our same type root as requestor to the
2790 // treeowner to make sure things work right.
2791 nsCOMPtr<nsIDocShellTreeItem> root;
2792 GetSameTypeRootTreeItem(getter_AddRefs(root));
2793 if (mTreeOwner) {
2794 NS_ASSERTION(root, "Must have this; worst case it's us!");
2795 mTreeOwner->FindItemWithName(aName, root, aOriginalRequestor,
2796 getter_AddRefs(foundItem));
2798 #ifdef DEBUG
2799 else {
2800 NS_ERROR("Someone isn't setting up the tree owner. "
2801 "You might like to try that. "
2802 "Things will.....you know, work.");
2803 // Note: _content should always exist. If we don't have one
2804 // hanging off the treeowner, just create a named window....
2805 // so don't return here, in case we did that and can now find
2806 // it.
2807 // XXXbz should we be using |root| instead of creating
2808 // a new window?
2810 #endif
2813 if (foundItem && !CanAccessItem(foundItem, aOriginalRequestor)) {
2814 foundItem = nsnull;
2817 if (foundItem) {
2818 // We return foundItem here even if it's not an active
2819 // item since all the names we've dealt with so far are
2820 // special cases that we won't bother looking for further.
2822 foundItem.swap(*_retval);
2823 return NS_OK;
2827 // Keep looking
2829 // First we check our name.
2830 if (mName.Equals(aName) && ItemIsActive(this) &&
2831 CanAccessItem(this, aOriginalRequestor)) {
2832 NS_ADDREF(*_retval = this);
2833 return NS_OK;
2836 // This QI may fail, but the places where we want to compare, comparing
2837 // against nsnull serves the same purpose.
2838 nsCOMPtr<nsIDocShellTreeItem> reqAsTreeItem(do_QueryInterface(aRequestor));
2840 // Second we check our children making sure not to ask a child if
2841 // it is the aRequestor.
2842 #ifdef DEBUG
2843 nsresult rv =
2844 #endif
2845 FindChildWithName(aName, PR_TRUE, PR_TRUE, reqAsTreeItem,
2846 aOriginalRequestor, _retval);
2847 NS_ASSERTION(NS_SUCCEEDED(rv),
2848 "FindChildWithName should not be failing here.");
2849 if (*_retval)
2850 return NS_OK;
2852 // Third if we have a parent and it isn't the requestor then we
2853 // should ask it to do the search. If it is the requestor we
2854 // should just stop here and let the parent do the rest. If we
2855 // don't have a parent, then we should ask the
2856 // docShellTreeOwner to do the search.
2857 nsCOMPtr<nsIDocShellTreeItem> parentAsTreeItem =
2858 do_QueryInterface(GetAsSupports(mParent));
2859 if (parentAsTreeItem) {
2860 if (parentAsTreeItem == reqAsTreeItem)
2861 return NS_OK;
2863 PRInt32 parentType;
2864 parentAsTreeItem->GetItemType(&parentType);
2865 if (parentType == mItemType) {
2866 return parentAsTreeItem->
2867 FindItemWithName(aName,
2868 static_cast<nsIDocShellTreeItem*>
2869 (this),
2870 aOriginalRequestor,
2871 _retval);
2875 // If the parent is null or not of the same type fall through and ask tree
2876 // owner.
2878 // This may fail, but comparing against null serves the same purpose
2879 nsCOMPtr<nsIDocShellTreeOwner>
2880 reqAsTreeOwner(do_QueryInterface(aRequestor));
2882 if (mTreeOwner && mTreeOwner != reqAsTreeOwner) {
2883 return mTreeOwner->
2884 FindItemWithName(aName, this, aOriginalRequestor, _retval);
2887 return NS_OK;
2890 NS_IMETHODIMP
2891 nsDocShell::GetTreeOwner(nsIDocShellTreeOwner ** aTreeOwner)
2893 NS_ENSURE_ARG_POINTER(aTreeOwner);
2895 *aTreeOwner = mTreeOwner;
2896 NS_IF_ADDREF(*aTreeOwner);
2897 return NS_OK;
2900 #ifdef DEBUG_DOCSHELL_FOCUS
2901 static void
2902 PrintDocTree(nsIDocShellTreeItem * aParentNode, int aLevel)
2904 for (PRInt32 i=0;i<aLevel;i++) printf(" ");
2906 PRInt32 childWebshellCount;
2907 aParentNode->GetChildCount(&childWebshellCount);
2908 nsCOMPtr<nsIDocShell> parentAsDocShell(do_QueryInterface(aParentNode));
2909 PRInt32 type;
2910 aParentNode->GetItemType(&type);
2911 nsCOMPtr<nsIPresShell> presShell;
2912 parentAsDocShell->GetPresShell(getter_AddRefs(presShell));
2913 nsRefPtr<nsPresContext> presContext;
2914 parentAsDocShell->GetPresContext(getter_AddRefs(presContext));
2915 nsIDocument *doc = presShell->GetDocument();
2917 nsCOMPtr<nsIDOMWindowInternal> domwin(doc->GetWindow());
2919 nsCOMPtr<nsIWidget> widget;
2920 nsIViewManager* vm = presShell->GetViewManager();
2921 if (vm) {
2922 vm->GetWidget(getter_AddRefs(widget));
2924 dom::Element* rootElement = doc->GetRootElement();
2926 printf("DS %p Ty %s Doc %p DW %p EM %p CN %p\n",
2927 (void*)parentAsDocShell.get(),
2928 type==nsIDocShellTreeItem::typeChrome?"Chr":"Con",
2929 (void*)doc, (void*)domwin.get(),
2930 (void*)presContext->EventStateManager(), (void*)rootElement);
2932 if (childWebshellCount > 0) {
2933 for (PRInt32 i=0;i<childWebshellCount;i++) {
2934 nsCOMPtr<nsIDocShellTreeItem> child;
2935 aParentNode->GetChildAt(i, getter_AddRefs(child));
2936 PrintDocTree(child, aLevel+1);
2941 static void
2942 PrintDocTree(nsIDocShellTreeItem * aParentNode)
2944 NS_ASSERTION(aParentNode, "Pointer is null!");
2946 nsCOMPtr<nsIDocShellTreeItem> parentItem;
2947 aParentNode->GetParent(getter_AddRefs(parentItem));
2948 while (parentItem) {
2949 nsCOMPtr<nsIDocShellTreeItem>tmp;
2950 parentItem->GetParent(getter_AddRefs(tmp));
2951 if (!tmp) {
2952 break;
2954 parentItem = tmp;
2957 if (!parentItem) {
2958 parentItem = aParentNode;
2961 PrintDocTree(parentItem, 0);
2963 #endif
2965 NS_IMETHODIMP
2966 nsDocShell::SetTreeOwner(nsIDocShellTreeOwner * aTreeOwner)
2968 #ifdef DEBUG_DOCSHELL_FOCUS
2969 nsCOMPtr<nsIDocShellTreeItem> item(do_QueryInterface(aTreeOwner));
2970 if (item) {
2971 PrintDocTree(item);
2973 #endif
2975 // Don't automatically set the progress based on the tree owner for frames
2976 if (!IsFrame()) {
2977 nsCOMPtr<nsIWebProgress> webProgress =
2978 do_QueryInterface(GetAsSupports(this));
2980 if (webProgress) {
2981 nsCOMPtr<nsIWebProgressListener>
2982 oldListener(do_QueryInterface(mTreeOwner));
2983 nsCOMPtr<nsIWebProgressListener>
2984 newListener(do_QueryInterface(aTreeOwner));
2986 if (oldListener) {
2987 webProgress->RemoveProgressListener(oldListener);
2990 if (newListener) {
2991 webProgress->AddProgressListener(newListener,
2992 nsIWebProgress::NOTIFY_ALL);
2997 mTreeOwner = aTreeOwner; // Weak reference per API
2999 PRInt32 i, n = mChildList.Count();
3000 for (i = 0; i < n; i++) {
3001 nsCOMPtr<nsIDocShellTreeItem> child = do_QueryInterface(ChildAt(i));
3002 NS_ENSURE_TRUE(child, NS_ERROR_FAILURE);
3003 PRInt32 childType = ~mItemType; // Set it to not us in case the get fails
3004 child->GetItemType(&childType); // We don't care if this fails, if it does we won't set the owner
3005 if (childType == mItemType)
3006 child->SetTreeOwner(aTreeOwner);
3009 return NS_OK;
3012 NS_IMETHODIMP
3013 nsDocShell::SetChildOffset(PRUint32 aChildOffset)
3015 mChildOffset = aChildOffset;
3016 return NS_OK;
3019 NS_IMETHODIMP
3020 nsDocShell::GetHistoryID(PRUint64* aID)
3022 *aID = mHistoryID;
3023 return NS_OK;
3026 NS_IMETHODIMP
3027 nsDocShell::GetIsInUnload(PRBool* aIsInUnload)
3029 *aIsInUnload = mFiredUnloadEvent;
3030 return NS_OK;
3033 //*****************************************************************************
3034 // nsDocShell::nsIDocShellTreeNode
3035 //*****************************************************************************
3037 NS_IMETHODIMP
3038 nsDocShell::GetChildCount(PRInt32 * aChildCount)
3040 NS_ENSURE_ARG_POINTER(aChildCount);
3041 *aChildCount = mChildList.Count();
3042 return NS_OK;
3047 NS_IMETHODIMP
3048 nsDocShell::AddChild(nsIDocShellTreeItem * aChild)
3050 NS_ENSURE_ARG_POINTER(aChild);
3052 nsRefPtr<nsDocLoader> childAsDocLoader = GetAsDocLoader(aChild);
3053 NS_ENSURE_TRUE(childAsDocLoader, NS_ERROR_UNEXPECTED);
3055 // Make sure we're not creating a loop in the docshell tree
3056 nsDocLoader* ancestor = this;
3057 do {
3058 if (childAsDocLoader == ancestor) {
3059 return NS_ERROR_ILLEGAL_VALUE;
3061 ancestor = ancestor->GetParent();
3062 } while (ancestor);
3064 // Make sure to remove the child from its current parent.
3065 nsDocLoader* childsParent = childAsDocLoader->GetParent();
3066 if (childsParent) {
3067 childsParent->RemoveChildLoader(childAsDocLoader);
3070 // Make sure to clear the treeowner in case this child is a different type
3071 // from us.
3072 aChild->SetTreeOwner(nsnull);
3074 nsresult res = AddChildLoader(childAsDocLoader);
3075 NS_ENSURE_SUCCESS(res, res);
3076 NS_ASSERTION(mChildList.Count() > 0,
3077 "child list must not be empty after a successful add");
3079 nsCOMPtr<nsIDocShellHistory> docshellhistory = do_QueryInterface(aChild);
3080 PRBool dynamic = PR_FALSE;
3081 docshellhistory->GetCreatedDynamically(&dynamic);
3082 if (!dynamic) {
3083 nsCOMPtr<nsISHEntry> currentSH;
3084 PRBool oshe = PR_FALSE;
3085 GetCurrentSHEntry(getter_AddRefs(currentSH), &oshe);
3086 if (currentSH) {
3087 currentSH->HasDynamicallyAddedChild(&dynamic);
3090 nsCOMPtr<nsIDocShell> childDocShell = do_QueryInterface(aChild);
3091 childDocShell->SetChildOffset(dynamic ? -1 : mChildList.Count() - 1);
3093 /* Set the child's global history if the parent has one */
3094 if (mUseGlobalHistory) {
3095 nsCOMPtr<nsIDocShellHistory>
3096 dsHistoryChild(do_QueryInterface(aChild));
3097 if (dsHistoryChild)
3098 dsHistoryChild->SetUseGlobalHistory(PR_TRUE);
3102 PRInt32 childType = ~mItemType; // Set it to not us in case the get fails
3103 aChild->GetItemType(&childType);
3104 if (childType != mItemType)
3105 return NS_OK;
3106 // Everything below here is only done when the child is the same type.
3109 aChild->SetTreeOwner(mTreeOwner);
3111 nsCOMPtr<nsIDocShell> childAsDocShell(do_QueryInterface(aChild));
3112 if (!childAsDocShell)
3113 return NS_OK;
3115 // charset, style-disabling, and zoom will be inherited in SetupNewViewer()
3117 // Now take this document's charset and set the parentCharset field of the
3118 // child's DocumentCharsetInfo to it. We'll later use that field, in the
3119 // loading process, for the charset choosing algorithm.
3120 // If we fail, at any point, we just return NS_OK.
3121 // This code has some performance impact. But this will be reduced when
3122 // the current charset will finally be stored as an Atom, avoiding the
3123 // alias resolution extra look-up.
3125 // we are NOT going to propagate the charset is this Chrome's docshell
3126 if (mItemType == nsIDocShellTreeItem::typeChrome)
3127 return NS_OK;
3129 // get the child's docCSInfo object
3130 nsCOMPtr<nsIDocumentCharsetInfo> dcInfo = NULL;
3131 res = childAsDocShell->GetDocumentCharsetInfo(getter_AddRefs(dcInfo));
3132 if (NS_FAILED(res) || (!dcInfo))
3133 return NS_OK;
3135 // get the parent's current charset
3136 if (!mContentViewer)
3137 return NS_OK;
3138 nsIDocument* doc = mContentViewer->GetDocument();
3139 if (!doc)
3140 return NS_OK;
3141 const nsACString &parentCS = doc->GetDocumentCharacterSet();
3143 PRBool isWyciwyg = PR_FALSE;
3145 if (mCurrentURI) {
3146 // Check if the url is wyciwyg
3147 mCurrentURI->SchemeIs("wyciwyg", &isWyciwyg);
3150 if (!isWyciwyg) {
3151 // If this docshell is loaded from a wyciwyg: URI, don't
3152 // advertise our charset since it does not in any way reflect
3153 // the actual source charset, which is what we're trying to
3154 // expose here.
3156 // set the child's parentCharset
3157 nsCOMPtr<nsIAtom> parentCSAtom(do_GetAtom(parentCS));
3158 res = dcInfo->SetParentCharset(parentCSAtom);
3159 if (NS_FAILED(res))
3160 return NS_OK;
3162 PRInt32 charsetSource = doc->GetDocumentCharacterSetSource();
3164 // set the child's parentCharset
3165 res = dcInfo->SetParentCharsetSource(charsetSource);
3166 if (NS_FAILED(res))
3167 return NS_OK;
3170 // printf("### 1 >>> Adding child. Parent CS = %s. ItemType = %d.\n", NS_LossyConvertUTF16toASCII(parentCS).get(), mItemType);
3172 return NS_OK;
3175 NS_IMETHODIMP
3176 nsDocShell::RemoveChild(nsIDocShellTreeItem * aChild)
3178 NS_ENSURE_ARG_POINTER(aChild);
3180 nsRefPtr<nsDocLoader> childAsDocLoader = GetAsDocLoader(aChild);
3181 NS_ENSURE_TRUE(childAsDocLoader, NS_ERROR_UNEXPECTED);
3183 nsresult rv = RemoveChildLoader(childAsDocLoader);
3184 NS_ENSURE_SUCCESS(rv, rv);
3186 aChild->SetTreeOwner(nsnull);
3188 return nsDocLoader::AddDocLoaderAsChildOfRoot(childAsDocLoader);
3191 NS_IMETHODIMP
3192 nsDocShell::GetChildAt(PRInt32 aIndex, nsIDocShellTreeItem ** aChild)
3194 NS_ENSURE_ARG_POINTER(aChild);
3196 #ifdef DEBUG
3197 if (aIndex < 0) {
3198 NS_WARNING("Negative index passed to GetChildAt");
3200 else if (aIndex >= mChildList.Count()) {
3201 NS_WARNING("Too large an index passed to GetChildAt");
3203 #endif
3205 nsIDocumentLoader* child = SafeChildAt(aIndex);
3206 NS_ENSURE_TRUE(child, NS_ERROR_UNEXPECTED);
3208 return CallQueryInterface(child, aChild);
3211 NS_IMETHODIMP
3212 nsDocShell::FindChildWithName(const PRUnichar * aName,
3213 PRBool aRecurse, PRBool aSameType,
3214 nsIDocShellTreeItem * aRequestor,
3215 nsIDocShellTreeItem * aOriginalRequestor,
3216 nsIDocShellTreeItem ** _retval)
3218 NS_ENSURE_ARG(aName);
3219 NS_ENSURE_ARG_POINTER(_retval);
3221 *_retval = nsnull; // if we don't find one, we return NS_OK and a null result
3223 if (!*aName)
3224 return NS_OK;
3226 nsXPIDLString childName;
3227 PRInt32 i, n = mChildList.Count();
3228 for (i = 0; i < n; i++) {
3229 nsCOMPtr<nsIDocShellTreeItem> child = do_QueryInterface(ChildAt(i));
3230 NS_ENSURE_TRUE(child, NS_ERROR_FAILURE);
3231 PRInt32 childType;
3232 child->GetItemType(&childType);
3234 if (aSameType && (childType != mItemType))
3235 continue;
3237 PRBool childNameEquals = PR_FALSE;
3238 child->NameEquals(aName, &childNameEquals);
3239 if (childNameEquals && ItemIsActive(child) &&
3240 CanAccessItem(child, aOriginalRequestor)) {
3241 child.swap(*_retval);
3242 break;
3245 if (childType != mItemType) //Only ask it to check children if it is same type
3246 continue;
3248 if (aRecurse && (aRequestor != child)) // Only ask the child if it isn't the requestor
3250 // See if child contains the shell with the given name
3251 #ifdef DEBUG
3252 nsresult rv =
3253 #endif
3254 child->FindChildWithName(aName, PR_TRUE,
3255 aSameType,
3256 static_cast<nsIDocShellTreeItem*>
3257 (this),
3258 aOriginalRequestor,
3259 _retval);
3260 NS_ASSERTION(NS_SUCCEEDED(rv),
3261 "FindChildWithName should not fail here");
3262 if (*_retval) // found it
3263 return NS_OK;
3266 return NS_OK;
3269 //*****************************************************************************
3270 // nsDocShell::nsIDocShellHistory
3271 //*****************************************************************************
3272 NS_IMETHODIMP
3273 nsDocShell::GetChildSHEntry(PRInt32 aChildOffset, nsISHEntry ** aResult)
3275 nsresult rv = NS_OK;
3277 NS_ENSURE_ARG_POINTER(aResult);
3278 *aResult = nsnull;
3281 // A nsISHEntry for a child is *only* available when the parent is in
3282 // the progress of loading a document too...
3284 if (mLSHE) {
3285 /* Before looking for the subframe's url, check
3286 * the expiration status of the parent. If the parent
3287 * has expired from cache, then subframes will not be
3288 * loaded from history in certain situations.
3290 PRBool parentExpired=PR_FALSE;
3291 mLSHE->GetExpirationStatus(&parentExpired);
3293 /* Get the parent's Load Type so that it can be set on the child too.
3294 * By default give a loadHistory value
3296 PRUint32 loadType = nsIDocShellLoadInfo::loadHistory;
3297 mLSHE->GetLoadType(&loadType);
3298 // If the user did a shift-reload on this frameset page,
3299 // we don't want to load the subframes from history.
3300 if (loadType == nsIDocShellLoadInfo::loadReloadBypassCache ||
3301 loadType == nsIDocShellLoadInfo::loadReloadBypassProxy ||
3302 loadType == nsIDocShellLoadInfo::loadReloadBypassProxyAndCache ||
3303 loadType == nsIDocShellLoadInfo::loadRefresh)
3304 return rv;
3306 /* If the user pressed reload and the parent frame has expired
3307 * from cache, we do not want to load the child frame from history.
3309 if (parentExpired && (loadType == nsIDocShellLoadInfo::loadReloadNormal)) {
3310 // The parent has expired. Return null.
3311 *aResult = nsnull;
3312 return rv;
3315 nsCOMPtr<nsISHContainer> container(do_QueryInterface(mLSHE));
3316 if (container) {
3317 // Get the child subframe from session history.
3318 rv = container->GetChildAt(aChildOffset, aResult);
3319 if (*aResult)
3320 (*aResult)->SetLoadType(loadType);
3323 return rv;
3326 NS_IMETHODIMP
3327 nsDocShell::AddChildSHEntry(nsISHEntry * aCloneRef, nsISHEntry * aNewEntry,
3328 PRInt32 aChildOffset, PRUint32 loadType)
3330 nsresult rv;
3332 if (mLSHE && loadType != LOAD_PUSHSTATE) {
3333 /* You get here if you are currently building a
3334 * hierarchy ie.,you just visited a frameset page
3336 nsCOMPtr<nsISHContainer> container(do_QueryInterface(mLSHE, &rv));
3337 if (container) {
3338 rv = container->AddChild(aNewEntry, aChildOffset);
3341 else if (!aCloneRef) {
3342 /* This is an initial load in some subframe. Just append it if we can */
3343 nsCOMPtr<nsISHContainer> container(do_QueryInterface(mOSHE, &rv));
3344 if (container) {
3345 rv = container->AddChild(aNewEntry, aChildOffset);
3348 else if (mSessionHistory) {
3349 /* You are currently in the rootDocShell.
3350 * You will get here when a subframe has a new url
3351 * to load and you have walked up the tree all the
3352 * way to the top to clone the current SHEntry hierarchy
3353 * and replace the subframe where a new url was loaded with
3354 * a new entry.
3356 PRInt32 index = -1;
3357 nsCOMPtr<nsIHistoryEntry> currentHE;
3358 mSessionHistory->GetIndex(&index);
3359 if (index < 0)
3360 return NS_ERROR_FAILURE;
3362 rv = mSessionHistory->GetEntryAtIndex(index, PR_FALSE,
3363 getter_AddRefs(currentHE));
3364 NS_ENSURE_TRUE(currentHE, NS_ERROR_FAILURE);
3366 nsCOMPtr<nsISHEntry> currentEntry(do_QueryInterface(currentHE));
3367 if (currentEntry) {
3368 PRUint32 cloneID = 0;
3369 nsCOMPtr<nsISHEntry> nextEntry;
3370 aCloneRef->GetID(&cloneID);
3371 rv = CloneAndReplace(currentEntry, this, cloneID, aNewEntry,
3372 loadType == LOAD_PUSHSTATE,
3373 getter_AddRefs(nextEntry));
3375 if (NS_SUCCEEDED(rv)) {
3376 nsCOMPtr<nsISHistoryInternal>
3377 shPrivate(do_QueryInterface(mSessionHistory));
3378 NS_ENSURE_TRUE(shPrivate, NS_ERROR_FAILURE);
3379 rv = shPrivate->AddEntry(nextEntry, PR_TRUE);
3383 else {
3384 /* Just pass this along */
3385 nsCOMPtr<nsIDocShellHistory> parent =
3386 do_QueryInterface(GetAsSupports(mParent), &rv);
3387 if (parent) {
3388 rv = parent->AddChildSHEntry(aCloneRef, aNewEntry, aChildOffset,
3389 loadType);
3392 return rv;
3395 nsresult
3396 nsDocShell::DoAddChildSHEntry(nsISHEntry* aNewEntry, PRInt32 aChildOffset)
3398 /* You will get here when you are in a subframe and
3399 * a new url has been loaded on you.
3400 * The mOSHE in this subframe will be the previous url's
3401 * mOSHE. This mOSHE will be used as the identification
3402 * for this subframe in the CloneAndReplace function.
3405 // In this case, we will end up calling AddEntry, which increases the
3406 // current index by 1
3407 nsCOMPtr<nsISHistory> rootSH;
3408 GetRootSessionHistory(getter_AddRefs(rootSH));
3409 if (rootSH) {
3410 rootSH->GetIndex(&mPreviousTransIndex);
3413 nsresult rv;
3414 nsCOMPtr<nsIDocShellHistory> parent =
3415 do_QueryInterface(GetAsSupports(mParent), &rv);
3416 if (parent) {
3417 rv = parent->AddChildSHEntry(mOSHE, aNewEntry, aChildOffset, mLoadType);
3421 if (rootSH) {
3422 rootSH->GetIndex(&mLoadedTransIndex);
3423 #ifdef DEBUG_PAGE_CACHE
3424 printf("Previous index: %d, Loaded index: %d\n\n", mPreviousTransIndex,
3425 mLoadedTransIndex);
3426 #endif
3429 return rv;
3432 NS_IMETHODIMP
3433 nsDocShell::SetUseGlobalHistory(PRBool aUseGlobalHistory)
3435 nsresult rv;
3437 mUseGlobalHistory = aUseGlobalHistory;
3439 if (!aUseGlobalHistory) {
3440 mGlobalHistory = nsnull;
3441 return NS_OK;
3444 // No need to initialize mGlobalHistory if IHistory is available.
3445 nsCOMPtr<IHistory> history = services::GetHistoryService();
3446 if (history) {
3447 return NS_OK;
3450 if (mGlobalHistory) {
3451 return NS_OK;
3454 mGlobalHistory = do_GetService(NS_GLOBALHISTORY2_CONTRACTID, &rv);
3455 return rv;
3458 NS_IMETHODIMP
3459 nsDocShell::GetUseGlobalHistory(PRBool *aUseGlobalHistory)
3461 *aUseGlobalHistory = mUseGlobalHistory;
3462 return NS_OK;
3465 NS_IMETHODIMP
3466 nsDocShell::RemoveFromSessionHistory()
3468 nsCOMPtr<nsISHistoryInternal> internalHistory;
3469 nsCOMPtr<nsISHistory> sessionHistory;
3470 nsCOMPtr<nsIDocShellTreeItem> root;
3471 GetSameTypeRootTreeItem(getter_AddRefs(root));
3472 if (root) {
3473 nsCOMPtr<nsIWebNavigation> rootAsWebnav =
3474 do_QueryInterface(root);
3475 if (rootAsWebnav) {
3476 rootAsWebnav->GetSessionHistory(getter_AddRefs(sessionHistory));
3477 internalHistory = do_QueryInterface(sessionHistory);
3480 if (!internalHistory) {
3481 return NS_OK;
3484 PRInt32 index = 0;
3485 sessionHistory->GetIndex(&index);
3486 nsAutoTArray<PRUint64, 16> ids;
3487 ids.AppendElement(mHistoryID);
3488 internalHistory->RemoveEntries(ids, index);
3489 return NS_OK;
3492 NS_IMETHODIMP
3493 nsDocShell::SetCreatedDynamically(PRBool aDynamic)
3495 mDynamicallyCreated = aDynamic;
3496 return NS_OK;
3499 NS_IMETHODIMP
3500 nsDocShell::GetCreatedDynamically(PRBool* aDynamic)
3502 *aDynamic = mDynamicallyCreated;
3503 return NS_OK;
3506 NS_IMETHODIMP
3507 nsDocShell::GetCurrentSHEntry(nsISHEntry** aEntry, PRBool* aOSHE)
3509 *aOSHE = PR_FALSE;
3510 *aEntry = nsnull;
3511 if (mLSHE) {
3512 NS_ADDREF(*aEntry = mLSHE);
3513 } else if (mOSHE) {
3514 NS_ADDREF(*aEntry = mOSHE);
3515 *aOSHE = PR_TRUE;
3517 return NS_OK;
3520 void
3521 nsDocShell::ClearFrameHistory(nsISHEntry* aEntry)
3523 nsCOMPtr<nsISHContainer> shcontainer = do_QueryInterface(aEntry);
3524 nsCOMPtr<nsISHistory> rootSH;
3525 GetRootSessionHistory(getter_AddRefs(rootSH));
3526 nsCOMPtr<nsISHistoryInternal> history = do_QueryInterface(rootSH);
3527 if (!history || !shcontainer) {
3528 return;
3531 PRInt32 count = 0;
3532 shcontainer->GetChildCount(&count);
3533 nsAutoTArray<PRUint64, 16> ids;
3534 for (PRInt32 i = 0; i < count; ++i) {
3535 nsCOMPtr<nsISHEntry> child;
3536 shcontainer->GetChildAt(i, getter_AddRefs(child));
3537 if (child) {
3538 PRUint64 id = 0;
3539 child->GetDocshellID(&id);
3540 ids.AppendElement(id);
3543 PRInt32 index = 0;
3544 rootSH->GetIndex(&index);
3545 history->RemoveEntries(ids, index);
3548 //-------------------------------------
3549 //-- Helper Method for Print discovery
3550 //-------------------------------------
3551 PRBool
3552 nsDocShell::IsPrintingOrPP(PRBool aDisplayErrorDialog)
3554 if (mIsPrintingOrPP && aDisplayErrorDialog) {
3555 DisplayLoadError(NS_ERROR_DOCUMENT_IS_PRINTMODE, nsnull, nsnull);
3558 return mIsPrintingOrPP;
3561 PRBool
3562 nsDocShell::IsNavigationAllowed(PRBool aDisplayPrintErrorDialog)
3564 return !IsPrintingOrPP(aDisplayPrintErrorDialog) && !mFiredUnloadEvent;
3567 //*****************************************************************************
3568 // nsDocShell::nsIWebNavigation
3569 //*****************************************************************************
3571 NS_IMETHODIMP
3572 nsDocShell::GetCanGoBack(PRBool * aCanGoBack)
3574 if (!IsNavigationAllowed(PR_FALSE)) {
3575 *aCanGoBack = PR_FALSE;
3576 return NS_OK; // JS may not handle returning of an error code
3578 nsresult rv;
3579 nsCOMPtr<nsISHistory> rootSH;
3580 rv = GetRootSessionHistory(getter_AddRefs(rootSH));
3581 nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
3582 NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
3583 rv = webnav->GetCanGoBack(aCanGoBack);
3584 return rv;
3588 NS_IMETHODIMP
3589 nsDocShell::GetCanGoForward(PRBool * aCanGoForward)
3591 if (!IsNavigationAllowed(PR_FALSE)) {
3592 *aCanGoForward = PR_FALSE;
3593 return NS_OK; // JS may not handle returning of an error code
3595 nsresult rv;
3596 nsCOMPtr<nsISHistory> rootSH;
3597 rv = GetRootSessionHistory(getter_AddRefs(rootSH));
3598 nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
3599 NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
3600 rv = webnav->GetCanGoForward(aCanGoForward);
3601 return rv;
3605 NS_IMETHODIMP
3606 nsDocShell::GoBack()
3608 if (!IsNavigationAllowed()) {
3609 return NS_OK; // JS may not handle returning of an error code
3611 nsresult rv;
3612 nsCOMPtr<nsISHistory> rootSH;
3613 rv = GetRootSessionHistory(getter_AddRefs(rootSH));
3614 nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
3615 NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
3616 rv = webnav->GoBack();
3617 return rv;
3621 NS_IMETHODIMP
3622 nsDocShell::GoForward()
3624 if (!IsNavigationAllowed()) {
3625 return NS_OK; // JS may not handle returning of an error code
3627 nsresult rv;
3628 nsCOMPtr<nsISHistory> rootSH;
3629 rv = GetRootSessionHistory(getter_AddRefs(rootSH));
3630 nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
3631 NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
3632 rv = webnav->GoForward();
3633 return rv;
3637 NS_IMETHODIMP nsDocShell::GotoIndex(PRInt32 aIndex)
3639 if (!IsNavigationAllowed()) {
3640 return NS_OK; // JS may not handle returning of an error code
3642 nsresult rv;
3643 nsCOMPtr<nsISHistory> rootSH;
3644 rv = GetRootSessionHistory(getter_AddRefs(rootSH));
3645 nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
3646 NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
3647 rv = webnav->GotoIndex(aIndex);
3648 return rv;
3652 NS_IMETHODIMP
3653 nsDocShell::LoadURI(const PRUnichar * aURI,
3654 PRUint32 aLoadFlags,
3655 nsIURI * aReferringURI,
3656 nsIInputStream * aPostStream,
3657 nsIInputStream * aHeaderStream)
3659 NS_ASSERTION((aLoadFlags & 0xf) == 0, "Unexpected flags");
3661 if (!IsNavigationAllowed()) {
3662 return NS_OK; // JS may not handle returning of an error code
3664 nsCOMPtr<nsIURI> uri;
3665 nsresult rv = NS_OK;
3667 // Create a URI from our string; if that succeeds, we want to
3668 // change aLoadFlags to not include the ALLOW_THIRD_PARTY_FIXUP
3669 // flag.
3671 NS_ConvertUTF16toUTF8 uriString(aURI);
3672 // Cleanup the empty spaces that might be on each end.
3673 uriString.Trim(" ");
3674 // Eliminate embedded newlines, which single-line text fields now allow:
3675 uriString.StripChars("\r\n");
3676 NS_ENSURE_TRUE(!uriString.IsEmpty(), NS_ERROR_FAILURE);
3678 rv = NS_NewURI(getter_AddRefs(uri), uriString);
3679 if (uri) {
3680 aLoadFlags &= ~LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
3683 if (sURIFixup) {
3684 // Call the fixup object. This will clobber the rv from NS_NewURI
3685 // above, but that's fine with us. Note that we need to do this even
3686 // if NS_NewURI returned a URI, because fixup handles nested URIs, etc
3687 // (things like view-source:mozilla.org for example).
3688 PRUint32 fixupFlags = 0;
3689 if (aLoadFlags & LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) {
3690 fixupFlags |= nsIURIFixup::FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP;
3692 rv = sURIFixup->CreateFixupURI(uriString, fixupFlags,
3693 getter_AddRefs(uri));
3695 // else no fixup service so just use the URI we created and see
3696 // what happens
3698 if (NS_ERROR_MALFORMED_URI == rv) {
3699 DisplayLoadError(rv, uri, aURI);
3702 if (NS_FAILED(rv) || !uri)
3703 return NS_ERROR_FAILURE;
3705 PopupControlState popupState;
3706 if (aLoadFlags & LOAD_FLAGS_ALLOW_POPUPS) {
3707 popupState = openAllowed;
3708 aLoadFlags &= ~LOAD_FLAGS_ALLOW_POPUPS;
3709 } else {
3710 popupState = openOverridden;
3712 nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(mScriptGlobal));
3713 nsAutoPopupStatePusher statePusher(win, popupState);
3715 // Don't pass certain flags that aren't needed and end up confusing
3716 // ConvertLoadTypeToDocShellLoadInfo. We do need to ensure that they are
3717 // passed to LoadURI though, since it uses them.
3718 PRUint32 extraFlags = (aLoadFlags & EXTRA_LOAD_FLAGS);
3719 aLoadFlags &= ~EXTRA_LOAD_FLAGS;
3721 nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
3722 rv = CreateLoadInfo(getter_AddRefs(loadInfo));
3723 if (NS_FAILED(rv)) return rv;
3725 PRUint32 loadType = MAKE_LOAD_TYPE(LOAD_NORMAL, aLoadFlags);
3726 loadInfo->SetLoadType(ConvertLoadTypeToDocShellLoadInfo(loadType));
3727 loadInfo->SetPostDataStream(aPostStream);
3728 loadInfo->SetReferrer(aReferringURI);
3729 loadInfo->SetHeadersStream(aHeaderStream);
3731 rv = LoadURI(uri, loadInfo, extraFlags, PR_TRUE);
3733 return rv;
3736 NS_IMETHODIMP
3737 nsDocShell::DisplayLoadError(nsresult aError, nsIURI *aURI,
3738 const PRUnichar *aURL,
3739 nsIChannel* aFailedChannel)
3741 // Get prompt and string bundle servcies
3742 nsCOMPtr<nsIPrompt> prompter;
3743 nsCOMPtr<nsIStringBundle> stringBundle;
3744 GetPromptAndStringBundle(getter_AddRefs(prompter),
3745 getter_AddRefs(stringBundle));
3747 NS_ENSURE_TRUE(stringBundle, NS_ERROR_FAILURE);
3748 NS_ENSURE_TRUE(prompter, NS_ERROR_FAILURE);
3750 nsAutoString error;
3751 const PRUint32 kMaxFormatStrArgs = 3;
3752 nsAutoString formatStrs[kMaxFormatStrArgs];
3753 PRUint32 formatStrCount = 0;
3754 PRBool addHostPort = PR_FALSE;
3755 nsresult rv = NS_OK;
3756 nsAutoString messageStr;
3757 nsCAutoString cssClass;
3758 nsCAutoString errorPage;
3760 errorPage.AssignLiteral("neterror");
3762 // Turn the error code into a human readable error message.
3763 if (NS_ERROR_UNKNOWN_PROTOCOL == aError) {
3764 NS_ENSURE_ARG_POINTER(aURI);
3765 // extract the scheme
3766 nsCAutoString scheme;
3767 aURI->GetScheme(scheme);
3768 CopyASCIItoUTF16(scheme, formatStrs[0]);
3769 formatStrCount = 1;
3770 error.AssignLiteral("protocolNotFound");
3772 else if (NS_ERROR_FILE_NOT_FOUND == aError) {
3773 NS_ENSURE_ARG_POINTER(aURI);
3774 error.AssignLiteral("fileNotFound");
3776 else if (NS_ERROR_UNKNOWN_HOST == aError) {
3777 NS_ENSURE_ARG_POINTER(aURI);
3778 // Get the host
3779 nsCAutoString host;
3780 nsCOMPtr<nsIURI> innermostURI = NS_GetInnermostURI(aURI);
3781 innermostURI->GetHost(host);
3782 CopyUTF8toUTF16(host, formatStrs[0]);
3783 formatStrCount = 1;
3784 error.AssignLiteral("dnsNotFound");
3786 else if(NS_ERROR_CONNECTION_REFUSED == aError) {
3787 NS_ENSURE_ARG_POINTER(aURI);
3788 addHostPort = PR_TRUE;
3789 error.AssignLiteral("connectionFailure");
3791 else if(NS_ERROR_NET_INTERRUPT == aError) {
3792 NS_ENSURE_ARG_POINTER(aURI);
3793 addHostPort = PR_TRUE;
3794 error.AssignLiteral("netInterrupt");
3796 else if (NS_ERROR_NET_TIMEOUT == aError) {
3797 NS_ENSURE_ARG_POINTER(aURI);
3798 // Get the host
3799 nsCAutoString host;
3800 aURI->GetHost(host);
3801 CopyUTF8toUTF16(host, formatStrs[0]);
3802 formatStrCount = 1;
3803 error.AssignLiteral("netTimeout");
3805 else if (NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION == aError) {
3806 // CSP error
3807 cssClass.AssignLiteral("neterror");
3808 error.AssignLiteral("cspFrameAncestorBlocked");
3810 else if (NS_ERROR_GET_MODULE(aError) == NS_ERROR_MODULE_SECURITY) {
3811 nsCOMPtr<nsINSSErrorsService> nsserr =
3812 do_GetService(NS_NSS_ERRORS_SERVICE_CONTRACTID);
3814 PRUint32 errorClass;
3815 if (!nsserr ||
3816 NS_FAILED(nsserr->GetErrorClass(aError, &errorClass))) {
3817 errorClass = nsINSSErrorsService::ERROR_CLASS_SSL_PROTOCOL;
3820 nsCOMPtr<nsISupports> securityInfo;
3821 nsCOMPtr<nsITransportSecurityInfo> tsi;
3822 if (aFailedChannel)
3823 aFailedChannel->GetSecurityInfo(getter_AddRefs(securityInfo));
3824 tsi = do_QueryInterface(securityInfo);
3825 if (tsi) {
3826 // Usually we should have aFailedChannel and get a detailed message
3827 tsi->GetErrorMessage(getter_Copies(messageStr));
3829 else {
3830 // No channel, let's obtain the generic error message
3831 if (nsserr) {
3832 nsserr->GetErrorMessage(aError, messageStr);
3835 if (!messageStr.IsEmpty()) {
3836 if (errorClass == nsINSSErrorsService::ERROR_CLASS_BAD_CERT) {
3837 error.AssignLiteral("nssBadCert");
3839 // if this is a Strict-Transport-Security host and the cert
3840 // is bad, don't allow overrides (STS Spec section 7.3).
3841 nsCOMPtr<nsIStrictTransportSecurityService> stss =
3842 do_GetService(NS_STSSERVICE_CONTRACTID, &rv);
3843 NS_ENSURE_SUCCESS(rv, rv);
3845 PRBool isStsHost = PR_FALSE;
3846 rv = stss->IsStsURI(aURI, &isStsHost);
3847 NS_ENSURE_SUCCESS(rv, rv);
3849 if (isStsHost)
3850 cssClass.AssignLiteral("badStsCert");
3852 PRBool expert = PR_FALSE;
3853 mPrefs->GetBoolPref("browser.xul.error_pages.expert_bad_cert",
3854 &expert);
3855 if (expert) {
3856 cssClass.AssignLiteral("expertBadCert");
3859 // See if an alternate cert error page is registered
3860 nsXPIDLCString alternateErrorPage;
3861 mPrefs->GetCharPref("security.alternate_certificate_error_page",
3862 getter_Copies(alternateErrorPage));
3863 if (alternateErrorPage)
3864 errorPage.Assign(alternateErrorPage);
3865 } else {
3866 error.AssignLiteral("nssFailure2");
3869 } else if (NS_ERROR_PHISHING_URI == aError || NS_ERROR_MALWARE_URI == aError) {
3870 nsCAutoString host;
3871 aURI->GetHost(host);
3872 CopyUTF8toUTF16(host, formatStrs[0]);
3873 formatStrCount = 1;
3875 // Malware and phishing detectors may want to use an alternate error
3876 // page, but if the pref's not set, we'll fall back on the standard page
3877 nsXPIDLCString alternateErrorPage;
3878 mPrefs->GetCharPref("urlclassifier.alternate_error_page",
3879 getter_Copies(alternateErrorPage));
3880 if (alternateErrorPage)
3881 errorPage.Assign(alternateErrorPage);
3883 if (NS_ERROR_PHISHING_URI == aError)
3884 error.AssignLiteral("phishingBlocked");
3885 else
3886 error.AssignLiteral("malwareBlocked");
3887 cssClass.AssignLiteral("blacklist");
3889 else {
3890 // Errors requiring simple formatting
3891 switch (aError) {
3892 case NS_ERROR_MALFORMED_URI:
3893 // URI is malformed
3894 error.AssignLiteral("malformedURI");
3895 break;
3896 case NS_ERROR_REDIRECT_LOOP:
3897 // Doc failed to load because the server generated too many redirects
3898 error.AssignLiteral("redirectLoop");
3899 break;
3900 case NS_ERROR_UNKNOWN_SOCKET_TYPE:
3901 // Doc failed to load because PSM is not installed
3902 error.AssignLiteral("unknownSocketType");
3903 break;
3904 case NS_ERROR_NET_RESET:
3905 // Doc failed to load because the server kept reseting the connection
3906 // before we could read any data from it
3907 error.AssignLiteral("netReset");
3908 break;
3909 case NS_ERROR_DOCUMENT_NOT_CACHED:
3910 // Doc failed to load because we are offline and the cache does not
3911 // contain a copy of the document.
3912 error.AssignLiteral("netOffline");
3913 break;
3914 case NS_ERROR_DOCUMENT_IS_PRINTMODE:
3915 // Doc navigation attempted while Printing or Print Preview
3916 error.AssignLiteral("isprinting");
3917 break;
3918 case NS_ERROR_PORT_ACCESS_NOT_ALLOWED:
3919 // Port blocked for security reasons
3920 addHostPort = PR_TRUE;
3921 error.AssignLiteral("deniedPortAccess");
3922 break;
3923 case NS_ERROR_UNKNOWN_PROXY_HOST:
3924 // Proxy hostname could not be resolved.
3925 error.AssignLiteral("proxyResolveFailure");
3926 break;
3927 case NS_ERROR_PROXY_CONNECTION_REFUSED:
3928 // Proxy connection was refused.
3929 error.AssignLiteral("proxyConnectFailure");
3930 break;
3931 case NS_ERROR_INVALID_CONTENT_ENCODING:
3932 // Bad Content Encoding.
3933 error.AssignLiteral("contentEncodingError");
3934 break;
3935 case NS_ERROR_REMOTE_XUL:
3937 error.AssignLiteral("remoteXUL");
3940 * We want to set an hardcoded messageStr which uses the
3941 * brandShortName.
3943 nsCOMPtr<nsIStringBundleService> stringBundleService =
3944 mozilla::services::GetStringBundleService();
3945 if (!stringBundleService) {
3946 return NS_ERROR_FAILURE;
3949 nsCOMPtr<nsIStringBundle> brandBundle;
3950 rv = stringBundleService->CreateBundle(kBrandBundleURL,
3951 getter_AddRefs(brandBundle));
3952 NS_ENSURE_SUCCESS(rv, rv);
3954 nsXPIDLString brandName;
3955 rv = brandBundle->GetStringFromName(NS_LITERAL_STRING("brandShortName").get(),
3956 getter_Copies(brandName));
3958 // We could use something like nsTextFormatter::smprintf.
3959 messageStr.AssignLiteral("This page uses an unsupported technology "
3960 "that is no longer available by default in ");
3961 messageStr.Append(brandName);
3962 messageStr.AppendLiteral(".");
3963 break;
3965 case NS_ERROR_UNSAFE_CONTENT_TYPE:
3966 // Channel refused to load from an unrecognized content type.
3967 error.AssignLiteral("unsafeContentType");
3968 break;
3972 // Test if the error should be displayed
3973 if (error.IsEmpty()) {
3974 return NS_OK;
3977 // Test if the error needs to be formatted
3978 if (!messageStr.IsEmpty()) {
3979 // already obtained message
3981 else {
3982 if (addHostPort) {
3983 // Build up the host:port string.
3984 nsCAutoString hostport;
3985 if (aURI) {
3986 aURI->GetHostPort(hostport);
3987 } else {
3988 hostport.AssignLiteral("?");
3990 CopyUTF8toUTF16(hostport, formatStrs[formatStrCount++]);
3993 nsCAutoString spec;
3994 rv = NS_ERROR_NOT_AVAILABLE;
3995 if (aURI) {
3996 // displaying "file://" is aesthetically unpleasing and could even be
3997 // confusing to the user
3998 PRBool isFileURI = PR_FALSE;
3999 rv = aURI->SchemeIs("file", &isFileURI);
4000 if (NS_SUCCEEDED(rv) && isFileURI)
4001 aURI->GetPath(spec);
4002 else
4003 aURI->GetSpec(spec);
4005 nsCAutoString charset;
4006 // unescape and convert from origin charset
4007 aURI->GetOriginCharset(charset);
4008 nsCOMPtr<nsITextToSubURI> textToSubURI(
4009 do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv));
4010 if (NS_SUCCEEDED(rv)) {
4011 rv = textToSubURI->UnEscapeURIForUI(charset, spec, formatStrs[formatStrCount]);
4013 } else {
4014 spec.AssignLiteral("?");
4016 if (NS_FAILED(rv))
4017 CopyUTF8toUTF16(spec, formatStrs[formatStrCount]);
4018 rv = NS_OK;
4019 ++formatStrCount;
4021 const PRUnichar *strs[kMaxFormatStrArgs];
4022 for (PRUint32 i = 0; i < formatStrCount; i++) {
4023 strs[i] = formatStrs[i].get();
4025 nsXPIDLString str;
4026 rv = stringBundle->FormatStringFromName(
4027 error.get(),
4028 strs, formatStrCount, getter_Copies(str));
4029 NS_ENSURE_SUCCESS(rv, rv);
4030 messageStr.Assign(str.get());
4033 // Display the error as a page or an alert prompt
4034 NS_ENSURE_FALSE(messageStr.IsEmpty(), NS_ERROR_FAILURE);
4035 // Note: For now, display an alert instead of an error page if we have no
4036 // URI object. Missing URI objects are handled badly by session history.
4037 if (mUseErrorPages && aURI && aFailedChannel) {
4038 // Display an error page
4039 LoadErrorPage(aURI, aURL, errorPage.get(), error.get(),
4040 messageStr.get(), cssClass.get(), aFailedChannel);
4042 else
4044 // The prompter reqires that our private window has a document (or it
4045 // asserts). Satisfy that assertion now since GetDocument will force
4046 // creation of one if it hasn't already been created.
4047 nsCOMPtr<nsPIDOMWindow> pwin(do_QueryInterface(mScriptGlobal));
4048 if (pwin) {
4049 nsCOMPtr<nsIDOMDocument> doc;
4050 pwin->GetDocument(getter_AddRefs(doc));
4053 // Display a message box
4054 prompter->Alert(nsnull, messageStr.get());
4057 return NS_OK;
4061 NS_IMETHODIMP
4062 nsDocShell::LoadErrorPage(nsIURI *aURI, const PRUnichar *aURL,
4063 const char *aErrorPage,
4064 const PRUnichar *aErrorType,
4065 const PRUnichar *aDescription,
4066 const char *aCSSClass,
4067 nsIChannel* aFailedChannel)
4069 #if defined(PR_LOGGING) && defined(DEBUG)
4070 if (PR_LOG_TEST(gDocShellLog, PR_LOG_DEBUG)) {
4071 nsCAutoString spec;
4072 aURI->GetSpec(spec);
4074 nsCAutoString chanName;
4075 if (aFailedChannel)
4076 aFailedChannel->GetName(chanName);
4077 else
4078 chanName.AssignLiteral("<no channel>");
4080 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
4081 ("nsDocShell[%p]::LoadErrorPage(\"%s\", \"%s\", {...}, [%s])\n", this,
4082 spec.get(), NS_ConvertUTF16toUTF8(aURL).get(), chanName.get()));
4084 #endif
4085 mFailedChannel = aFailedChannel;
4086 mFailedURI = aURI;
4087 mFailedLoadType = mLoadType;
4089 if (mLSHE) {
4090 // If we don't give mLSHE a new doc identifier here, when we go back or
4091 // forward to another SHEntry with the same doc identifier, the error
4092 // page will persist.
4093 mLSHE->SetUniqueDocIdentifier();
4096 nsCAutoString url;
4097 nsCAutoString charset;
4098 if (aURI)
4100 nsresult rv = aURI->GetSpec(url);
4101 rv |= aURI->GetOriginCharset(charset);
4102 NS_ENSURE_SUCCESS(rv, rv);
4104 else if (aURL)
4106 CopyUTF16toUTF8(aURL, url);
4108 else
4110 return NS_ERROR_INVALID_POINTER;
4113 // Create a URL to pass all the error information through to the page.
4115 #undef SAFE_ESCAPE
4116 #define SAFE_ESCAPE(cstring, escArg1, escArg2) \
4118 char* s = nsEscape(escArg1, escArg2); \
4119 if (!s) \
4120 return NS_ERROR_OUT_OF_MEMORY; \
4121 cstring.Adopt(s); \
4123 nsCString escapedUrl, escapedCharset, escapedError, escapedDescription,
4124 escapedCSSClass;
4125 SAFE_ESCAPE(escapedUrl, url.get(), url_Path);
4126 SAFE_ESCAPE(escapedCharset, charset.get(), url_Path);
4127 SAFE_ESCAPE(escapedError,
4128 NS_ConvertUTF16toUTF8(aErrorType).get(), url_Path);
4129 SAFE_ESCAPE(escapedDescription,
4130 NS_ConvertUTF16toUTF8(aDescription).get(), url_Path);
4131 if (aCSSClass) {
4132 SAFE_ESCAPE(escapedCSSClass, aCSSClass, url_Path);
4134 nsCString errorPageUrl("about:");
4135 errorPageUrl.AppendASCII(aErrorPage);
4136 errorPageUrl.AppendLiteral("?e=");
4138 errorPageUrl.AppendASCII(escapedError.get());
4139 errorPageUrl.AppendLiteral("&u=");
4140 errorPageUrl.AppendASCII(escapedUrl.get());
4141 if (!escapedCSSClass.IsEmpty()) {
4142 errorPageUrl.AppendASCII("&s=");
4143 errorPageUrl.AppendASCII(escapedCSSClass.get());
4145 errorPageUrl.AppendLiteral("&c=");
4146 errorPageUrl.AppendASCII(escapedCharset.get());
4147 errorPageUrl.AppendLiteral("&d=");
4148 errorPageUrl.AppendASCII(escapedDescription.get());
4150 nsCOMPtr<nsIURI> errorPageURI;
4151 nsresult rv = NS_NewURI(getter_AddRefs(errorPageURI), errorPageUrl);
4152 NS_ENSURE_SUCCESS(rv, rv);
4154 return InternalLoad(errorPageURI, nsnull, nsnull,
4155 INTERNAL_LOAD_FLAGS_INHERIT_OWNER, nsnull, nsnull,
4156 nsnull, nsnull, LOAD_ERROR_PAGE,
4157 nsnull, PR_TRUE, nsnull, nsnull);
4161 NS_IMETHODIMP
4162 nsDocShell::Reload(PRUint32 aReloadFlags)
4164 if (!IsNavigationAllowed()) {
4165 return NS_OK; // JS may not handle returning of an error code
4167 nsresult rv;
4168 NS_ASSERTION(((aReloadFlags & 0xf) == 0),
4169 "Reload command not updated to use load flags!");
4170 NS_ASSERTION((aReloadFlags & EXTRA_LOAD_FLAGS) == 0,
4171 "Don't pass these flags to Reload");
4173 PRUint32 loadType = MAKE_LOAD_TYPE(LOAD_RELOAD_NORMAL, aReloadFlags);
4174 NS_ENSURE_TRUE(IsValidLoadType(loadType), NS_ERROR_INVALID_ARG);
4176 // Send notifications to the HistoryListener if any, about the impending reload
4177 nsCOMPtr<nsISHistory> rootSH;
4178 rv = GetRootSessionHistory(getter_AddRefs(rootSH));
4179 nsCOMPtr<nsISHistoryInternal> shistInt(do_QueryInterface(rootSH));
4180 PRBool canReload = PR_TRUE;
4181 if (rootSH) {
4182 nsCOMPtr<nsISHistoryListener> listener;
4183 shistInt->GetListener(getter_AddRefs(listener));
4184 if (listener) {
4185 listener->OnHistoryReload(mCurrentURI, aReloadFlags, &canReload);
4189 if (!canReload)
4190 return NS_OK;
4192 /* If you change this part of code, make sure bug 45297 does not re-occur */
4193 if (mOSHE) {
4194 rv = LoadHistoryEntry(mOSHE, loadType);
4196 else if (mLSHE) { // In case a reload happened before the current load is done
4197 rv = LoadHistoryEntry(mLSHE, loadType);
4199 else {
4200 nsCOMPtr<nsIDocument> doc(do_GetInterface(GetAsSupports(this)));
4202 nsIPrincipal* principal = nsnull;
4203 nsAutoString contentTypeHint;
4204 if (doc) {
4205 principal = doc->NodePrincipal();
4206 doc->GetContentType(contentTypeHint);
4209 rv = InternalLoad(mCurrentURI,
4210 mReferrerURI,
4211 principal,
4212 INTERNAL_LOAD_FLAGS_NONE, // Do not inherit owner from document
4213 nsnull, // No window target
4214 NS_LossyConvertUTF16toASCII(contentTypeHint).get(),
4215 nsnull, // No post data
4216 nsnull, // No headers data
4217 loadType, // Load type
4218 nsnull, // No SHEntry
4219 PR_TRUE,
4220 nsnull, // No nsIDocShell
4221 nsnull); // No nsIRequest
4225 return rv;
4228 NS_IMETHODIMP
4229 nsDocShell::Stop(PRUint32 aStopFlags)
4231 // Revoke any pending event related to content viewer restoration
4232 mRestorePresentationEvent.Revoke();
4234 if (mLoadType == LOAD_ERROR_PAGE) {
4235 if (mLSHE) {
4236 // Since error page loads never unset mLSHE, do so now
4237 SetHistoryEntry(&mOSHE, mLSHE);
4238 SetHistoryEntry(&mLSHE, nsnull);
4241 mFailedChannel = nsnull;
4242 mFailedURI = nsnull;
4245 if (nsIWebNavigation::STOP_CONTENT & aStopFlags) {
4246 // Stop the document loading
4247 if (mContentViewer)
4248 mContentViewer->Stop();
4251 if (nsIWebNavigation::STOP_NETWORK & aStopFlags) {
4252 // Suspend any timers that were set for this loader. We'll clear
4253 // them out for good in CreateContentViewer.
4254 if (mRefreshURIList) {
4255 SuspendRefreshURIs();
4256 mSavedRefreshURIList.swap(mRefreshURIList);
4257 mRefreshURIList = nsnull;
4260 // XXXbz We could also pass |this| to nsIURILoader::Stop. That will
4261 // just call Stop() on us as an nsIDocumentLoader... We need fewer
4262 // redundant apis!
4263 Stop();
4266 PRInt32 n;
4267 PRInt32 count = mChildList.Count();
4268 for (n = 0; n < count; n++) {
4269 nsCOMPtr<nsIWebNavigation> shellAsNav(do_QueryInterface(ChildAt(n)));
4270 if (shellAsNav)
4271 shellAsNav->Stop(aStopFlags);
4274 return NS_OK;
4277 NS_IMETHODIMP
4278 nsDocShell::GetDocument(nsIDOMDocument ** aDocument)
4280 NS_ENSURE_ARG_POINTER(aDocument);
4281 NS_ENSURE_SUCCESS(EnsureContentViewer(), NS_ERROR_FAILURE);
4283 return mContentViewer->GetDOMDocument(aDocument);
4286 NS_IMETHODIMP
4287 nsDocShell::GetCurrentURI(nsIURI ** aURI)
4289 NS_ENSURE_ARG_POINTER(aURI);
4291 if (mCurrentURI) {
4292 return NS_EnsureSafeToReturn(mCurrentURI, aURI);
4295 *aURI = nsnull;
4296 return NS_OK;
4299 NS_IMETHODIMP
4300 nsDocShell::GetReferringURI(nsIURI ** aURI)
4302 NS_ENSURE_ARG_POINTER(aURI);
4304 *aURI = mReferrerURI;
4305 NS_IF_ADDREF(*aURI);
4307 return NS_OK;
4310 NS_IMETHODIMP
4311 nsDocShell::SetSessionHistory(nsISHistory * aSessionHistory)
4314 NS_ENSURE_TRUE(aSessionHistory, NS_ERROR_FAILURE);
4315 // make sure that we are the root docshell and
4316 // set a handle to root docshell in SH.
4318 nsCOMPtr<nsIDocShellTreeItem> root;
4319 /* Get the root docshell. If *this* is the root docshell
4320 * then save a handle to *this* in SH. SH needs it to do
4321 * traversions thro' its entries
4323 GetSameTypeRootTreeItem(getter_AddRefs(root));
4324 NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
4325 if (root.get() == static_cast<nsIDocShellTreeItem *>(this)) {
4326 mSessionHistory = aSessionHistory;
4327 nsCOMPtr<nsISHistoryInternal>
4328 shPrivate(do_QueryInterface(mSessionHistory));
4329 NS_ENSURE_TRUE(shPrivate, NS_ERROR_FAILURE);
4330 shPrivate->SetRootDocShell(this);
4331 return NS_OK;
4333 return NS_ERROR_FAILURE;
4338 NS_IMETHODIMP
4339 nsDocShell::GetSessionHistory(nsISHistory ** aSessionHistory)
4341 NS_ENSURE_ARG_POINTER(aSessionHistory);
4342 *aSessionHistory = mSessionHistory;
4343 NS_IF_ADDREF(*aSessionHistory);
4344 return NS_OK;
4347 //*****************************************************************************
4348 // nsDocShell::nsIWebPageDescriptor
4349 //*****************************************************************************
4350 NS_IMETHODIMP
4351 nsDocShell::LoadPage(nsISupports *aPageDescriptor, PRUint32 aDisplayType)
4353 nsCOMPtr<nsISHEntry> shEntryIn(do_QueryInterface(aPageDescriptor));
4355 // Currently, the opaque 'page descriptor' is an nsISHEntry...
4356 if (!shEntryIn) {
4357 return NS_ERROR_INVALID_POINTER;
4360 // Now clone shEntryIn, since we might end up modifying it later on, and we
4361 // want a page descriptor to be reusable.
4362 nsCOMPtr<nsISHEntry> shEntry;
4363 nsresult rv = shEntryIn->Clone(getter_AddRefs(shEntry));
4364 NS_ENSURE_SUCCESS(rv, rv);
4366 // Give our cloned shEntry a new document identifier so this load is
4367 // independent of all other loads. (This is important, in particular,
4368 // for bugs 582795 and 585298.)
4369 rv = shEntry->SetUniqueDocIdentifier();
4370 NS_ENSURE_SUCCESS(rv, rv);
4373 // load the page as view-source
4375 if (nsIWebPageDescriptor::DISPLAY_AS_SOURCE == aDisplayType) {
4376 nsCOMPtr<nsIURI> oldUri, newUri;
4377 nsCString spec, newSpec;
4379 // Create a new view-source URI and replace the original.
4380 rv = shEntry->GetURI(getter_AddRefs(oldUri));
4381 if (NS_FAILED(rv))
4382 return rv;
4384 oldUri->GetSpec(spec);
4385 newSpec.AppendLiteral("view-source:");
4386 newSpec.Append(spec);
4388 rv = NS_NewURI(getter_AddRefs(newUri), newSpec);
4389 if (NS_FAILED(rv)) {
4390 return rv;
4392 shEntry->SetURI(newUri);
4395 rv = LoadHistoryEntry(shEntry, LOAD_HISTORY);
4396 return rv;
4399 NS_IMETHODIMP
4400 nsDocShell::GetCurrentDescriptor(nsISupports **aPageDescriptor)
4402 NS_PRECONDITION(aPageDescriptor, "Null out param?");
4404 *aPageDescriptor = nsnull;
4406 nsISHEntry* src = mOSHE ? mOSHE : mLSHE;
4407 if (src) {
4408 nsCOMPtr<nsISHEntry> dest;
4410 nsresult rv = src->Clone(getter_AddRefs(dest));
4411 if (NS_FAILED(rv)) {
4412 return rv;
4415 // null out inappropriate cloned attributes...
4416 dest->SetParent(nsnull);
4417 dest->SetIsSubFrame(PR_FALSE);
4419 return CallQueryInterface(dest, aPageDescriptor);
4422 return NS_ERROR_NOT_AVAILABLE;
4426 //*****************************************************************************
4427 // nsDocShell::nsIBaseWindow
4428 //*****************************************************************************
4430 NS_IMETHODIMP
4431 nsDocShell::InitWindow(nativeWindow parentNativeWindow,
4432 nsIWidget * parentWidget, PRInt32 x, PRInt32 y,
4433 PRInt32 cx, PRInt32 cy)
4435 SetParentWidget(parentWidget);
4436 SetPositionAndSize(x, y, cx, cy, PR_FALSE);
4438 return NS_OK;
4441 NS_IMETHODIMP
4442 nsDocShell::Create()
4444 if (mPrefs) {
4445 // We've already been created
4446 return NS_OK;
4449 NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome,
4450 "Unexpected item type in docshell");
4452 nsresult rv = NS_ERROR_FAILURE;
4453 mPrefs = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
4454 NS_ENSURE_SUCCESS(rv, rv);
4456 PRBool tmpbool;
4458 rv = mPrefs->GetBoolPref("browser.frames.enabled", &tmpbool);
4459 if (NS_SUCCEEDED(rv))
4460 mAllowSubframes = tmpbool;
4462 if (gValidateOrigin == (PRBool)0xffffffff) {
4463 // Check pref to see if we should prevent frameset spoofing
4464 rv = mPrefs->GetBoolPref("browser.frame.validate_origin", &tmpbool);
4465 if (NS_SUCCEEDED(rv)) {
4466 gValidateOrigin = tmpbool;
4467 } else {
4468 gValidateOrigin = PR_TRUE;
4472 // Should we use XUL error pages instead of alerts if possible?
4473 rv = mPrefs->GetBoolPref("browser.xul.error_pages.enabled", &tmpbool);
4474 if (NS_SUCCEEDED(rv))
4475 mUseErrorPages = tmpbool;
4477 nsCOMPtr<nsIPrefBranch2> prefs(do_QueryInterface(mPrefs, &rv));
4478 if (NS_SUCCEEDED(rv) && mObserveErrorPages) {
4479 prefs->AddObserver("browser.xul.error_pages.enabled", this, PR_FALSE);
4482 nsCOMPtr<nsIObserverService> serv = do_GetService(NS_OBSERVERSERVICE_CONTRACTID);
4483 if (serv) {
4484 const char* msg = mItemType == typeContent ?
4485 NS_WEBNAVIGATION_CREATE : NS_CHROME_WEBNAVIGATION_CREATE;
4486 serv->NotifyObservers(GetAsSupports(this), msg, nsnull);
4489 return NS_OK;
4492 NS_IMETHODIMP
4493 nsDocShell::Destroy()
4495 NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome,
4496 "Unexpected item type in docshell");
4498 if (!mIsBeingDestroyed) {
4499 nsCOMPtr<nsIObserverService> serv =
4500 do_GetService(NS_OBSERVERSERVICE_CONTRACTID);
4501 if (serv) {
4502 const char* msg = mItemType == typeContent ?
4503 NS_WEBNAVIGATION_DESTROY : NS_CHROME_WEBNAVIGATION_DESTROY;
4504 serv->NotifyObservers(GetAsSupports(this), msg, nsnull);
4508 mIsBeingDestroyed = PR_TRUE;
4510 // Remove our pref observers
4511 if (mObserveErrorPages) {
4512 nsCOMPtr<nsIPrefBranch2> prefs(do_QueryInterface(mPrefs));
4513 if (prefs) {
4514 prefs->RemoveObserver("browser.xul.error_pages.enabled", this);
4515 mObserveErrorPages = PR_FALSE;
4519 // Make sure to blow away our mLoadingURI just in case. No loads
4520 // from inside this pagehide.
4521 mLoadingURI = nsnull;
4523 // Fire unload event before we blow anything away.
4524 (void) FirePageHideNotification(PR_TRUE);
4526 // Clear pointers to any detached nsEditorData that's lying
4527 // around in shistory entries. Breaks cycle. See bug 430921.
4528 if (mOSHE)
4529 mOSHE->SetEditorData(nsnull);
4530 if (mLSHE)
4531 mLSHE->SetEditorData(nsnull);
4533 // Note: mContentListener can be null if Init() failed and we're being
4534 // called from the destructor.
4535 if (mContentListener) {
4536 mContentListener->DropDocShellreference();
4537 mContentListener->SetParentContentListener(nsnull);
4538 // Note that we do NOT set mContentListener to null here; that
4539 // way if someone tries to do a load in us after this point
4540 // the nsDSURIContentListener will block it. All of which
4541 // means that we should do this before calling Stop(), of
4542 // course.
4545 // Stop any URLs that are currently being loaded...
4546 Stop(nsIWebNavigation::STOP_ALL);
4548 mEditorData = nsnull;
4550 mTransferableHookData = nsnull;
4552 // Save the state of the current document, before destroying the window.
4553 // This is needed to capture the state of a frameset when the new document
4554 // causes the frameset to be destroyed...
4555 PersistLayoutHistoryState();
4557 // Remove this docshell from its parent's child list
4558 nsCOMPtr<nsIDocShellTreeItem> docShellParentAsItem =
4559 do_QueryInterface(GetAsSupports(mParent));
4560 if (docShellParentAsItem)
4561 docShellParentAsItem->RemoveChild(this);
4563 if (mContentViewer) {
4564 mContentViewer->Close(nsnull);
4565 mContentViewer->Destroy();
4566 mContentViewer = nsnull;
4569 nsDocLoader::Destroy();
4571 mParentWidget = nsnull;
4572 mCurrentURI = nsnull;
4574 if (mScriptGlobal) {
4575 nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(mScriptGlobal));
4576 win->SetDocShell(nsnull);
4578 mScriptGlobal = nsnull;
4581 if (mSessionHistory) {
4582 // We want to destroy these content viewers now rather than
4583 // letting their destruction wait for the session history
4584 // entries to get garbage collected. (Bug 488394)
4585 nsCOMPtr<nsISHistoryInternal> shPrivate =
4586 do_QueryInterface(mSessionHistory);
4587 if (shPrivate) {
4588 shPrivate->EvictAllContentViewers();
4590 mSessionHistory = nsnull;
4593 SetTreeOwner(nsnull);
4595 // required to break ref cycle
4596 mSecurityUI = nsnull;
4598 // Cancel any timers that were set for this docshell; this is needed
4599 // to break the cycle between us and the timers.
4600 CancelRefreshURITimers();
4601 return NS_OK;
4604 NS_IMETHODIMP
4605 nsDocShell::SetPosition(PRInt32 x, PRInt32 y)
4607 mBounds.x = x;
4608 mBounds.y = y;
4610 if (mContentViewer)
4611 NS_ENSURE_SUCCESS(mContentViewer->Move(x, y), NS_ERROR_FAILURE);
4613 return NS_OK;
4616 NS_IMETHODIMP
4617 nsDocShell::GetPosition(PRInt32 * aX, PRInt32 * aY)
4619 PRInt32 dummyHolder;
4620 return GetPositionAndSize(aX, aY, &dummyHolder, &dummyHolder);
4623 NS_IMETHODIMP
4624 nsDocShell::SetSize(PRInt32 aCX, PRInt32 aCY, PRBool aRepaint)
4626 PRInt32 x = 0, y = 0;
4627 GetPosition(&x, &y);
4628 return SetPositionAndSize(x, y, aCX, aCY, aRepaint);
4631 NS_IMETHODIMP
4632 nsDocShell::GetSize(PRInt32 * aCX, PRInt32 * aCY)
4634 PRInt32 dummyHolder;
4635 return GetPositionAndSize(&dummyHolder, &dummyHolder, aCX, aCY);
4638 NS_IMETHODIMP
4639 nsDocShell::SetPositionAndSize(PRInt32 x, PRInt32 y, PRInt32 cx,
4640 PRInt32 cy, PRBool fRepaint)
4642 mBounds.x = x;
4643 mBounds.y = y;
4644 mBounds.width = cx;
4645 mBounds.height = cy;
4647 // Hold strong ref, since SetBounds can make us null out mContentViewer
4648 nsCOMPtr<nsIContentViewer> viewer = mContentViewer;
4649 if (viewer) {
4650 //XXX Border figured in here or is that handled elsewhere?
4651 NS_ENSURE_SUCCESS(viewer->SetBounds(mBounds), NS_ERROR_FAILURE);
4654 return NS_OK;
4657 NS_IMETHODIMP
4658 nsDocShell::GetPositionAndSize(PRInt32 * x, PRInt32 * y, PRInt32 * cx,
4659 PRInt32 * cy)
4661 // We should really consider just getting this information from
4662 // our window instead of duplicating the storage and code...
4663 if (cx || cy) {
4664 // Caller wants to know our size; make sure to give them up to
4665 // date information.
4666 nsCOMPtr<nsIDocument> doc(do_GetInterface(GetAsSupports(mParent)));
4667 if (doc) {
4668 doc->FlushPendingNotifications(Flush_Layout);
4672 DoGetPositionAndSize(x, y, cx, cy);
4673 return NS_OK;
4676 void
4677 nsDocShell::DoGetPositionAndSize(PRInt32 * x, PRInt32 * y, PRInt32 * cx,
4678 PRInt32 * cy)
4680 if (x)
4681 *x = mBounds.x;
4682 if (y)
4683 *y = mBounds.y;
4684 if (cx)
4685 *cx = mBounds.width;
4686 if (cy)
4687 *cy = mBounds.height;
4690 NS_IMETHODIMP
4691 nsDocShell::Repaint(PRBool aForce)
4693 nsCOMPtr<nsIPresShell> presShell;
4694 GetPresShell(getter_AddRefs(presShell));
4695 NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
4697 nsIViewManager* viewManager = presShell->GetViewManager();
4698 NS_ENSURE_TRUE(viewManager, NS_ERROR_FAILURE);
4700 // what about aForce ?
4701 NS_ENSURE_SUCCESS(viewManager->UpdateAllViews(0), NS_ERROR_FAILURE);
4702 return NS_OK;
4705 NS_IMETHODIMP
4706 nsDocShell::GetParentWidget(nsIWidget ** parentWidget)
4708 NS_ENSURE_ARG_POINTER(parentWidget);
4710 *parentWidget = mParentWidget;
4711 NS_IF_ADDREF(*parentWidget);
4713 return NS_OK;
4716 NS_IMETHODIMP
4717 nsDocShell::SetParentWidget(nsIWidget * aParentWidget)
4719 mParentWidget = aParentWidget;
4721 return NS_OK;
4724 NS_IMETHODIMP
4725 nsDocShell::GetParentNativeWindow(nativeWindow * parentNativeWindow)
4727 NS_ENSURE_ARG_POINTER(parentNativeWindow);
4729 if (mParentWidget)
4730 *parentNativeWindow = mParentWidget->GetNativeData(NS_NATIVE_WIDGET);
4731 else
4732 *parentNativeWindow = nsnull;
4734 return NS_OK;
4737 NS_IMETHODIMP
4738 nsDocShell::SetParentNativeWindow(nativeWindow parentNativeWindow)
4740 return NS_ERROR_NOT_IMPLEMENTED;
4743 NS_IMETHODIMP
4744 nsDocShell::GetVisibility(PRBool * aVisibility)
4746 NS_ENSURE_ARG_POINTER(aVisibility);
4748 *aVisibility = PR_FALSE;
4750 if (!mContentViewer)
4751 return NS_OK;
4753 nsCOMPtr<nsIPresShell> presShell;
4754 GetPresShell(getter_AddRefs(presShell));
4755 if (!presShell)
4756 return NS_OK;
4758 // get the view manager
4759 nsIViewManager* vm = presShell->GetViewManager();
4760 NS_ENSURE_TRUE(vm, NS_ERROR_FAILURE);
4762 // get the root view
4763 nsIView *view = nsnull; // views are not ref counted
4764 NS_ENSURE_SUCCESS(vm->GetRootView(view), NS_ERROR_FAILURE);
4765 NS_ENSURE_TRUE(view, NS_ERROR_FAILURE);
4767 // if our root view is hidden, we are not visible
4768 if (view->GetVisibility() == nsViewVisibility_kHide)
4769 return NS_OK;
4771 // otherwise, we must walk up the document and view trees checking
4772 // for a hidden view, unless we're an off screen browser, which
4773 // would make this test meaningless.
4775 nsCOMPtr<nsIDocShellTreeItem> treeItem = this;
4776 nsCOMPtr<nsIDocShellTreeItem> parentItem;
4777 treeItem->GetParent(getter_AddRefs(parentItem));
4778 while (parentItem) {
4779 nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(treeItem));
4780 docShell->GetPresShell(getter_AddRefs(presShell));
4782 nsCOMPtr<nsIDocShell> parentDS = do_QueryInterface(parentItem);
4783 nsCOMPtr<nsIPresShell> pPresShell;
4784 parentDS->GetPresShell(getter_AddRefs(pPresShell));
4786 // Null-check for crash in bug 267804
4787 if (!pPresShell) {
4788 NS_NOTREACHED("parent docshell has null pres shell");
4789 return NS_OK;
4792 nsIContent *shellContent =
4793 pPresShell->GetDocument()->FindContentForSubDocument(presShell->GetDocument());
4794 NS_ASSERTION(shellContent, "subshell not in the map");
4796 nsIFrame* frame = shellContent ? shellContent->GetPrimaryFrame() : nsnull;
4797 PRBool isDocShellOffScreen = PR_FALSE;
4798 docShell->GetIsOffScreenBrowser(&isDocShellOffScreen);
4799 if (frame && !frame->AreAncestorViewsVisible() && !isDocShellOffScreen)
4800 return NS_OK;
4802 treeItem = parentItem;
4803 treeItem->GetParent(getter_AddRefs(parentItem));
4806 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_QueryInterface(mTreeOwner));
4807 if (!treeOwnerAsWin) {
4808 *aVisibility = PR_TRUE;
4809 return NS_OK;
4812 // Check with the tree owner as well to give embedders a chance to
4813 // expose visibility as well.
4814 return treeOwnerAsWin->GetVisibility(aVisibility);
4817 NS_IMETHODIMP
4818 nsDocShell::SetIsOffScreenBrowser(PRBool aIsOffScreen)
4820 mIsOffScreenBrowser = aIsOffScreen;
4821 return NS_OK;
4824 NS_IMETHODIMP
4825 nsDocShell::GetIsOffScreenBrowser(PRBool *aIsOffScreen)
4827 *aIsOffScreen = mIsOffScreenBrowser;
4828 return NS_OK;
4831 NS_IMETHODIMP
4832 nsDocShell::SetIsActive(PRBool aIsActive)
4834 // We disallow setting active on chrome docshells.
4835 if (mItemType == nsIDocShellTreeItem::typeChrome)
4836 return NS_ERROR_INVALID_ARG;
4838 // Keep track ourselves.
4839 mIsActive = aIsActive;
4841 // Tell the PresShell about it.
4842 nsCOMPtr<nsIPresShell> pshell;
4843 GetPresShell(getter_AddRefs(pshell));
4844 if (pshell)
4845 pshell->SetIsActive(aIsActive);
4847 // Recursively tell all of our children
4848 PRInt32 n = mChildList.Count();
4849 for (PRInt32 i = 0; i < n; ++i) {
4850 nsCOMPtr<nsIDocShell> docshell = do_QueryInterface(ChildAt(i));
4851 if (docshell)
4852 docshell->SetIsActive(aIsActive);
4855 return NS_OK;
4858 NS_IMETHODIMP
4859 nsDocShell::GetIsActive(PRBool *aIsActive)
4861 *aIsActive = mIsActive;
4862 return NS_OK;
4865 NS_IMETHODIMP
4866 nsDocShell::SetIsAppTab(PRBool aIsAppTab)
4868 mIsAppTab = aIsAppTab;
4869 return NS_OK;
4872 NS_IMETHODIMP
4873 nsDocShell::GetIsAppTab(PRBool *aIsAppTab)
4875 *aIsAppTab = mIsAppTab;
4876 return NS_OK;
4879 NS_IMETHODIMP
4880 nsDocShell::SetVisibility(PRBool aVisibility)
4882 if (!mContentViewer)
4883 return NS_OK;
4884 if (aVisibility) {
4885 mContentViewer->Show();
4887 else {
4888 mContentViewer->Hide();
4891 return NS_OK;
4894 NS_IMETHODIMP
4895 nsDocShell::GetEnabled(PRBool *aEnabled)
4897 NS_ENSURE_ARG_POINTER(aEnabled);
4898 *aEnabled = PR_TRUE;
4899 return NS_ERROR_NOT_IMPLEMENTED;
4902 NS_IMETHODIMP
4903 nsDocShell::SetEnabled(PRBool aEnabled)
4905 return NS_ERROR_NOT_IMPLEMENTED;
4908 NS_IMETHODIMP
4909 nsDocShell::GetBlurSuppression(PRBool *aBlurSuppression)
4911 NS_ENSURE_ARG_POINTER(aBlurSuppression);
4912 *aBlurSuppression = PR_FALSE;
4913 return NS_ERROR_NOT_IMPLEMENTED;
4916 NS_IMETHODIMP
4917 nsDocShell::SetBlurSuppression(PRBool aBlurSuppression)
4919 return NS_ERROR_NOT_IMPLEMENTED;
4922 NS_IMETHODIMP
4923 nsDocShell::SetFocus()
4925 return NS_OK;
4928 NS_IMETHODIMP
4929 nsDocShell::GetMainWidget(nsIWidget ** aMainWidget)
4931 // We don't create our own widget, so simply return the parent one.
4932 return GetParentWidget(aMainWidget);
4935 NS_IMETHODIMP
4936 nsDocShell::GetTitle(PRUnichar ** aTitle)
4938 NS_ENSURE_ARG_POINTER(aTitle);
4940 *aTitle = ToNewUnicode(mTitle);
4941 return NS_OK;
4944 NS_IMETHODIMP
4945 nsDocShell::SetTitle(const PRUnichar * aTitle)
4947 // Store local title
4948 mTitle = aTitle;
4950 nsCOMPtr<nsIDocShellTreeItem> parent;
4951 GetSameTypeParent(getter_AddRefs(parent));
4953 // When title is set on the top object it should then be passed to the
4954 // tree owner.
4955 if (!parent) {
4956 nsCOMPtr<nsIBaseWindow>
4957 treeOwnerAsWin(do_QueryInterface(mTreeOwner));
4958 if (treeOwnerAsWin)
4959 treeOwnerAsWin->SetTitle(aTitle);
4962 if (mCurrentURI && mLoadType != LOAD_ERROR_PAGE && mUseGlobalHistory) {
4963 nsCOMPtr<IHistory> history = services::GetHistoryService();
4964 if (history) {
4965 history->SetURITitle(mCurrentURI, mTitle);
4967 else if (mGlobalHistory) {
4968 mGlobalHistory->SetPageTitle(mCurrentURI, nsString(mTitle));
4972 // Update SessionHistory with the document's title.
4973 if (mOSHE && mLoadType != LOAD_BYPASS_HISTORY &&
4974 mLoadType != LOAD_ERROR_PAGE) {
4976 mOSHE->SetTitle(mTitle);
4979 return NS_OK;
4982 //*****************************************************************************
4983 // nsDocShell::nsIScrollable
4984 //*****************************************************************************
4986 NS_IMETHODIMP
4987 nsDocShell::GetCurScrollPos(PRInt32 scrollOrientation, PRInt32 * curPos)
4989 NS_ENSURE_ARG_POINTER(curPos);
4991 nsIScrollableFrame* sf = GetRootScrollFrame();
4992 NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
4994 nsPoint pt = sf->GetScrollPosition();
4996 switch (scrollOrientation) {
4997 case ScrollOrientation_X:
4998 *curPos = pt.x;
4999 return NS_OK;
5001 case ScrollOrientation_Y:
5002 *curPos = pt.y;
5003 return NS_OK;
5005 default:
5006 NS_ENSURE_TRUE(PR_FALSE, NS_ERROR_INVALID_ARG);
5010 NS_IMETHODIMP
5011 nsDocShell::SetCurScrollPos(PRInt32 scrollOrientation, PRInt32 curPos)
5013 nsIScrollableFrame* sf = GetRootScrollFrame();
5014 NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
5016 nsPoint pt = sf->GetScrollPosition();
5018 switch (scrollOrientation) {
5019 case ScrollOrientation_X:
5020 pt.x = curPos;
5021 break;
5023 case ScrollOrientation_Y:
5024 pt.y = curPos;
5025 break;
5027 default:
5028 NS_ENSURE_TRUE(PR_FALSE, NS_ERROR_INVALID_ARG);
5031 sf->ScrollTo(pt, nsIScrollableFrame::INSTANT);
5032 return NS_OK;
5035 NS_IMETHODIMP
5036 nsDocShell::SetCurScrollPosEx(PRInt32 curHorizontalPos, PRInt32 curVerticalPos)
5038 nsIScrollableFrame* sf = GetRootScrollFrame();
5039 NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
5041 sf->ScrollTo(nsPoint(curHorizontalPos, curVerticalPos),
5042 nsIScrollableFrame::INSTANT);
5043 return NS_OK;
5046 // XXX This is wrong
5047 NS_IMETHODIMP
5048 nsDocShell::GetScrollRange(PRInt32 scrollOrientation,
5049 PRInt32 * minPos, PRInt32 * maxPos)
5051 NS_ENSURE_ARG_POINTER(minPos && maxPos);
5053 nsIScrollableFrame* sf = GetRootScrollFrame();
5054 NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
5056 nsSize portSize = sf->GetScrollPortRect().Size();
5057 nsRect range = sf->GetScrollRange();
5059 switch (scrollOrientation) {
5060 case ScrollOrientation_X:
5061 *minPos = range.x;
5062 *maxPos = range.XMost() + portSize.width;
5063 return NS_OK;
5065 case ScrollOrientation_Y:
5066 *minPos = range.y;
5067 *maxPos = range.YMost() + portSize.height;
5068 return NS_OK;
5070 default:
5071 NS_ENSURE_TRUE(PR_FALSE, NS_ERROR_INVALID_ARG);
5075 NS_IMETHODIMP
5076 nsDocShell::SetScrollRange(PRInt32 scrollOrientation,
5077 PRInt32 minPos, PRInt32 maxPos)
5079 //XXX First Check
5081 Retrieves or Sets the valid ranges for the thumb. When maxPos is set to
5082 something less than the current thumb position, curPos is set = to maxPos.
5084 @return NS_OK - Setting or Getting completed successfully.
5085 NS_ERROR_INVALID_ARG - returned when curPos is not within the
5086 minPos and maxPos.
5088 return NS_ERROR_FAILURE;
5091 NS_IMETHODIMP
5092 nsDocShell::SetScrollRangeEx(PRInt32 minHorizontalPos,
5093 PRInt32 maxHorizontalPos, PRInt32 minVerticalPos,
5094 PRInt32 maxVerticalPos)
5096 //XXX First Check
5098 Retrieves or Sets the valid ranges for the thumb. When maxPos is set to
5099 something less than the current thumb position, curPos is set = to maxPos.
5101 @return NS_OK - Setting or Getting completed successfully.
5102 NS_ERROR_INVALID_ARG - returned when curPos is not within the
5103 minPos and maxPos.
5105 return NS_ERROR_FAILURE;
5108 // This returns setting for all documents in this docshell
5109 NS_IMETHODIMP
5110 nsDocShell::GetDefaultScrollbarPreferences(PRInt32 scrollOrientation,
5111 PRInt32 * scrollbarPref)
5113 NS_ENSURE_ARG_POINTER(scrollbarPref);
5114 switch (scrollOrientation) {
5115 case ScrollOrientation_X:
5116 *scrollbarPref = mDefaultScrollbarPref.x;
5117 return NS_OK;
5119 case ScrollOrientation_Y:
5120 *scrollbarPref = mDefaultScrollbarPref.y;
5121 return NS_OK;
5123 default:
5124 NS_ENSURE_TRUE(PR_FALSE, NS_ERROR_INVALID_ARG);
5126 return NS_ERROR_FAILURE;
5129 // Set scrolling preference for all documents in this shell
5131 // There are three possible values stored in the shell:
5132 // 1) nsIScrollable::Scrollbar_Never = no scrollbar
5133 // 2) nsIScrollable::Scrollbar_Auto = scrollbar appears if the document
5134 // being displayed would normally have scrollbar
5135 // 3) nsIScrollable::Scrollbar_Always = scrollbar always appears
5137 // One important client is nsHTMLFrameInnerFrame::CreateWebShell()
5138 NS_IMETHODIMP
5139 nsDocShell::SetDefaultScrollbarPreferences(PRInt32 scrollOrientation,
5140 PRInt32 scrollbarPref)
5142 switch (scrollOrientation) {
5143 case ScrollOrientation_X:
5144 mDefaultScrollbarPref.x = scrollbarPref;
5145 return NS_OK;
5147 case ScrollOrientation_Y:
5148 mDefaultScrollbarPref.y = scrollbarPref;
5149 return NS_OK;
5151 default:
5152 NS_ENSURE_TRUE(PR_FALSE, NS_ERROR_INVALID_ARG);
5154 return NS_ERROR_FAILURE;
5157 NS_IMETHODIMP
5158 nsDocShell::GetScrollbarVisibility(PRBool * verticalVisible,
5159 PRBool * horizontalVisible)
5161 nsIScrollableFrame* sf = GetRootScrollFrame();
5162 NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
5164 PRUint32 scrollbarVisibility = sf->GetScrollbarVisibility();
5165 if (verticalVisible)
5166 *verticalVisible = (scrollbarVisibility & nsIScrollableFrame::VERTICAL) != 0;
5167 if (horizontalVisible)
5168 *horizontalVisible = (scrollbarVisibility & nsIScrollableFrame::HORIZONTAL) != 0;
5170 return NS_OK;
5173 //*****************************************************************************
5174 // nsDocShell::nsITextScroll
5175 //*****************************************************************************
5177 NS_IMETHODIMP
5178 nsDocShell::ScrollByLines(PRInt32 numLines)
5180 nsIScrollableFrame* sf = GetRootScrollFrame();
5181 NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
5183 sf->ScrollBy(nsIntPoint(0, numLines), nsIScrollableFrame::LINES,
5184 nsIScrollableFrame::SMOOTH);
5185 return NS_OK;
5188 NS_IMETHODIMP
5189 nsDocShell::ScrollByPages(PRInt32 numPages)
5191 nsIScrollableFrame* sf = GetRootScrollFrame();
5192 NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
5194 sf->ScrollBy(nsIntPoint(0, numPages), nsIScrollableFrame::PAGES,
5195 nsIScrollableFrame::SMOOTH);
5196 return NS_OK;
5199 //*****************************************************************************
5200 // nsDocShell::nsIScriptGlobalObjectOwner
5201 //*****************************************************************************
5203 nsIScriptGlobalObject*
5204 nsDocShell::GetScriptGlobalObject()
5206 NS_ENSURE_SUCCESS(EnsureScriptEnvironment(), nsnull);
5208 return mScriptGlobal;
5211 //*****************************************************************************
5212 // nsDocShell::nsIRefreshURI
5213 //*****************************************************************************
5215 NS_IMETHODIMP
5216 nsDocShell::RefreshURI(nsIURI * aURI, PRInt32 aDelay, PRBool aRepeat,
5217 PRBool aMetaRefresh)
5219 NS_ENSURE_ARG(aURI);
5221 /* Check if Meta refresh/redirects are permitted. Some
5222 * embedded applications may not want to do this.
5223 * Must do this before sending out NOTIFY_REFRESH events
5224 * because listeners may have side effects (e.g. displaying a
5225 * button to manually trigger the refresh later).
5227 PRBool allowRedirects = PR_TRUE;
5228 GetAllowMetaRedirects(&allowRedirects);
5229 if (!allowRedirects)
5230 return NS_OK;
5232 // If any web progress listeners are listening for NOTIFY_REFRESH events,
5233 // give them a chance to block this refresh.
5234 PRBool sameURI;
5235 nsresult rv = aURI->Equals(mCurrentURI, &sameURI);
5236 if (NS_FAILED(rv))
5237 sameURI = PR_FALSE;
5238 if (!RefreshAttempted(this, aURI, aDelay, sameURI))
5239 return NS_OK;
5241 nsRefreshTimer *refreshTimer = new nsRefreshTimer();
5242 NS_ENSURE_TRUE(refreshTimer, NS_ERROR_OUT_OF_MEMORY);
5243 PRUint32 busyFlags = 0;
5244 GetBusyFlags(&busyFlags);
5246 nsCOMPtr<nsISupports> dataRef = refreshTimer; // Get the ref count to 1
5248 refreshTimer->mDocShell = this;
5249 refreshTimer->mURI = aURI;
5250 refreshTimer->mDelay = aDelay;
5251 refreshTimer->mRepeat = aRepeat;
5252 refreshTimer->mMetaRefresh = aMetaRefresh;
5254 if (!mRefreshURIList) {
5255 NS_ENSURE_SUCCESS(NS_NewISupportsArray(getter_AddRefs(mRefreshURIList)),
5256 NS_ERROR_FAILURE);
5259 if (busyFlags & BUSY_FLAGS_BUSY) {
5260 // We are busy loading another page. Don't create the
5261 // timer right now. Instead queue up the request and trigger the
5262 // timer in EndPageLoad().
5263 mRefreshURIList->AppendElement(refreshTimer);
5265 else {
5266 // There is no page loading going on right now. Create the
5267 // timer and fire it right away.
5268 nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1");
5269 NS_ENSURE_TRUE(timer, NS_ERROR_FAILURE);
5271 mRefreshURIList->AppendElement(timer); // owning timer ref
5272 timer->InitWithCallback(refreshTimer, aDelay, nsITimer::TYPE_ONE_SHOT);
5274 return NS_OK;
5277 nsresult
5278 nsDocShell::ForceRefreshURIFromTimer(nsIURI * aURI,
5279 PRInt32 aDelay,
5280 PRBool aMetaRefresh,
5281 nsITimer* aTimer)
5283 NS_PRECONDITION(aTimer, "Must have a timer here");
5285 // Remove aTimer from mRefreshURIList if needed
5286 if (mRefreshURIList) {
5287 PRUint32 n = 0;
5288 mRefreshURIList->Count(&n);
5290 for (PRUint32 i = 0; i < n; ++i) {
5291 nsCOMPtr<nsITimer> timer = do_QueryElementAt(mRefreshURIList, i);
5292 if (timer == aTimer) {
5293 mRefreshURIList->RemoveElementAt(i);
5294 break;
5299 return ForceRefreshURI(aURI, aDelay, aMetaRefresh);
5302 NS_IMETHODIMP
5303 nsDocShell::ForceRefreshURI(nsIURI * aURI,
5304 PRInt32 aDelay,
5305 PRBool aMetaRefresh)
5307 NS_ENSURE_ARG(aURI);
5309 nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
5310 CreateLoadInfo(getter_AddRefs(loadInfo));
5311 NS_ENSURE_TRUE(loadInfo, NS_ERROR_OUT_OF_MEMORY);
5313 /* We do need to pass in a referrer, but we don't want it to
5314 * be sent to the server.
5316 loadInfo->SetSendReferrer(PR_FALSE);
5318 /* for most refreshes the current URI is an appropriate
5319 * internal referrer
5321 loadInfo->SetReferrer(mCurrentURI);
5323 /* Don't ever "guess" on which owner to use to avoid picking
5324 * the current owner.
5326 loadInfo->SetOwnerIsExplicit(PR_TRUE);
5328 /* Check if this META refresh causes a redirection
5329 * to another site.
5331 PRBool equalUri = PR_FALSE;
5332 nsresult rv = aURI->Equals(mCurrentURI, &equalUri);
5333 if (NS_SUCCEEDED(rv) && (!equalUri) && aMetaRefresh &&
5334 aDelay <= REFRESH_REDIRECT_TIMER) {
5336 /* It is a META refresh based redirection within the threshold time
5337 * we have in mind (15000 ms as defined by REFRESH_REDIRECT_TIMER).
5338 * Pass a REPLACE flag to LoadURI().
5340 loadInfo->SetLoadType(nsIDocShellLoadInfo::loadNormalReplace);
5342 /* for redirects we mimic HTTP, which passes the
5343 * original referrer
5345 nsCOMPtr<nsIURI> internalReferrer;
5346 GetReferringURI(getter_AddRefs(internalReferrer));
5347 if (internalReferrer) {
5348 loadInfo->SetReferrer(internalReferrer);
5351 else {
5352 loadInfo->SetLoadType(nsIDocShellLoadInfo::loadRefresh);
5356 * LoadURI(...) will cancel all refresh timers... This causes the
5357 * Timer and its refreshData instance to be released...
5359 LoadURI(aURI, loadInfo, nsIWebNavigation::LOAD_FLAGS_NONE, PR_TRUE);
5361 return NS_OK;
5364 nsresult
5365 nsDocShell::SetupRefreshURIFromHeader(nsIURI * aBaseURI,
5366 const nsACString & aHeader)
5368 // Refresh headers are parsed with the following format in mind
5369 // <META HTTP-EQUIV=REFRESH CONTENT="5; URL=http://uri">
5370 // By the time we are here, the following is true:
5371 // header = "REFRESH"
5372 // content = "5; URL=http://uri" // note the URL attribute is
5373 // optional, if it is absent, the currently loaded url is used.
5374 // Also note that the seconds and URL separator can be either
5375 // a ';' or a ','. The ',' separator should be illegal but CNN
5376 // is using it.
5378 // We need to handle the following strings, where
5379 // - X is a set of digits
5380 // - URI is either a relative or absolute URI
5382 // Note that URI should start with "url=" but we allow omission
5384 // "" || ";" || ","
5385 // empty string. use the currently loaded URI
5386 // and refresh immediately.
5387 // "X" || "X;" || "X,"
5388 // Refresh the currently loaded URI in X seconds.
5389 // "X; URI" || "X, URI"
5390 // Refresh using URI as the destination in X seconds.
5391 // "URI" || "; URI" || ", URI"
5392 // Refresh immediately using URI as the destination.
5394 // Currently, anything immediately following the URI, if
5395 // separated by any char in the set "'\"\t\r\n " will be
5396 // ignored. So "10; url=go.html ; foo=bar" will work,
5397 // and so will "10; url='go.html'; foo=bar". However,
5398 // "10; url=go.html; foo=bar" will result in the uri
5399 // "go.html;" since ';' and ',' are valid uri characters.
5401 // Note that we need to remove any tokens wrapping the URI.
5402 // These tokens currently include spaces, double and single
5403 // quotes.
5405 // when done, seconds is 0 or the given number of seconds
5406 // uriAttrib is empty or the URI specified
5407 nsCAutoString uriAttrib;
5408 PRInt32 seconds = 0;
5409 PRBool specifiesSeconds = PR_FALSE;
5411 nsACString::const_iterator iter, tokenStart, doneIterating;
5413 aHeader.BeginReading(iter);
5414 aHeader.EndReading(doneIterating);
5416 // skip leading whitespace
5417 while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter))
5418 ++iter;
5420 tokenStart = iter;
5422 // skip leading + and -
5423 if (iter != doneIterating && (*iter == '-' || *iter == '+'))
5424 ++iter;
5426 // parse number
5427 while (iter != doneIterating && (*iter >= '0' && *iter <= '9')) {
5428 seconds = seconds * 10 + (*iter - '0');
5429 specifiesSeconds = PR_TRUE;
5430 ++iter;
5433 if (iter != doneIterating) {
5434 // if we started with a '-', number is negative
5435 if (*tokenStart == '-')
5436 seconds = -seconds;
5438 // skip to next ';' or ','
5439 nsACString::const_iterator iterAfterDigit = iter;
5440 while (iter != doneIterating && !(*iter == ';' || *iter == ','))
5442 if (specifiesSeconds)
5444 // Non-whitespace characters here mean that the string is
5445 // malformed but tolerate sites that specify a decimal point,
5446 // even though meta refresh only works on whole seconds.
5447 if (iter == iterAfterDigit &&
5448 !nsCRT::IsAsciiSpace(*iter) && *iter != '.')
5450 // The characters between the seconds and the next
5451 // section are just garbage!
5452 // e.g. content="2a0z+,URL=http://www.mozilla.org/"
5453 // Just ignore this redirect.
5454 return NS_ERROR_FAILURE;
5456 else if (nsCRT::IsAsciiSpace(*iter))
5458 // We've had at least one whitespace so tolerate the mistake
5459 // and drop through.
5460 // e.g. content="10 foo"
5461 ++iter;
5462 break;
5465 ++iter;
5468 // skip any remaining whitespace
5469 while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter))
5470 ++iter;
5472 // skip ';' or ','
5473 if (iter != doneIterating && (*iter == ';' || *iter == ',')) {
5474 ++iter;
5477 // skip whitespace
5478 while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter))
5479 ++iter;
5482 // possible start of URI
5483 tokenStart = iter;
5485 // skip "url = " to real start of URI
5486 if (iter != doneIterating && (*iter == 'u' || *iter == 'U')) {
5487 ++iter;
5488 if (iter != doneIterating && (*iter == 'r' || *iter == 'R')) {
5489 ++iter;
5490 if (iter != doneIterating && (*iter == 'l' || *iter == 'L')) {
5491 ++iter;
5493 // skip whitespace
5494 while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter))
5495 ++iter;
5497 if (iter != doneIterating && *iter == '=') {
5498 ++iter;
5500 // skip whitespace
5501 while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter))
5502 ++iter;
5504 // found real start of URI
5505 tokenStart = iter;
5511 // skip a leading '"' or '\''.
5513 PRBool isQuotedURI = PR_FALSE;
5514 if (tokenStart != doneIterating && (*tokenStart == '"' || *tokenStart == '\''))
5516 isQuotedURI = PR_TRUE;
5517 ++tokenStart;
5520 // set iter to start of URI
5521 iter = tokenStart;
5523 // tokenStart here points to the beginning of URI
5525 // grab the rest of the URI
5526 while (iter != doneIterating)
5528 if (isQuotedURI && (*iter == '"' || *iter == '\''))
5529 break;
5530 ++iter;
5533 // move iter one back if the last character is a '"' or '\''
5534 if (iter != tokenStart && isQuotedURI) {
5535 --iter;
5536 if (!(*iter == '"' || *iter == '\''))
5537 ++iter;
5540 // URI is whatever's contained from tokenStart to iter.
5541 // note: if tokenStart == doneIterating, so is iter.
5543 nsresult rv = NS_OK;
5545 nsCOMPtr<nsIURI> uri;
5546 PRBool specifiesURI = PR_FALSE;
5547 if (tokenStart == iter) {
5548 uri = aBaseURI;
5550 else {
5551 uriAttrib = Substring(tokenStart, iter);
5552 // NS_NewURI takes care of any whitespace surrounding the URL
5553 rv = NS_NewURI(getter_AddRefs(uri), uriAttrib, nsnull, aBaseURI);
5554 specifiesURI = PR_TRUE;
5557 // No URI or seconds were specified
5558 if (!specifiesSeconds && !specifiesURI)
5560 // Do nothing because the alternative is to spin around in a refresh
5561 // loop forever!
5562 return NS_ERROR_FAILURE;
5565 if (NS_SUCCEEDED(rv)) {
5566 nsCOMPtr<nsIScriptSecurityManager>
5567 securityManager(do_GetService
5568 (NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv));
5569 if (NS_SUCCEEDED(rv)) {
5570 rv = securityManager->
5571 CheckLoadURI(aBaseURI, uri,
5572 nsIScriptSecurityManager::
5573 LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT);
5575 if (NS_SUCCEEDED(rv)) {
5576 PRBool isjs = PR_TRUE;
5577 rv = NS_URIChainHasFlags(uri,
5578 nsIProtocolHandler::URI_OPENING_EXECUTES_SCRIPT, &isjs);
5579 NS_ENSURE_SUCCESS(rv, rv);
5581 if (isjs) {
5582 return NS_ERROR_FAILURE;
5586 if (NS_SUCCEEDED(rv)) {
5587 // Since we can't travel back in time yet, just pretend
5588 // negative numbers do nothing at all.
5589 if (seconds < 0)
5590 return NS_ERROR_FAILURE;
5592 rv = RefreshURI(uri, seconds * 1000, PR_FALSE, PR_TRUE);
5596 return rv;
5599 NS_IMETHODIMP nsDocShell::SetupRefreshURI(nsIChannel * aChannel)
5601 nsresult rv;
5602 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel, &rv));
5603 if (NS_SUCCEEDED(rv)) {
5604 nsCAutoString refreshHeader;
5605 rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("refresh"),
5606 refreshHeader);
5608 if (!refreshHeader.IsEmpty()) {
5609 SetupReferrerFromChannel(aChannel);
5610 rv = SetupRefreshURIFromHeader(mCurrentURI, refreshHeader);
5611 if (NS_SUCCEEDED(rv)) {
5612 return NS_REFRESHURI_HEADER_FOUND;
5616 return rv;
5619 static void
5620 DoCancelRefreshURITimers(nsISupportsArray* aTimerList)
5622 if (!aTimerList)
5623 return;
5625 PRUint32 n=0;
5626 aTimerList->Count(&n);
5628 while (n) {
5629 nsCOMPtr<nsITimer> timer(do_QueryElementAt(aTimerList, --n));
5631 aTimerList->RemoveElementAt(n); // bye bye owning timer ref
5633 if (timer)
5634 timer->Cancel();
5638 NS_IMETHODIMP
5639 nsDocShell::CancelRefreshURITimers()
5641 DoCancelRefreshURITimers(mRefreshURIList);
5642 DoCancelRefreshURITimers(mSavedRefreshURIList);
5643 mRefreshURIList = nsnull;
5644 mSavedRefreshURIList = nsnull;
5646 return NS_OK;
5649 NS_IMETHODIMP
5650 nsDocShell::GetRefreshPending(PRBool* _retval)
5652 if (!mRefreshURIList) {
5653 *_retval = PR_FALSE;
5654 return NS_OK;
5657 PRUint32 count;
5658 nsresult rv = mRefreshURIList->Count(&count);
5659 if (NS_SUCCEEDED(rv))
5660 *_retval = (count != 0);
5661 return rv;
5664 NS_IMETHODIMP
5665 nsDocShell::SuspendRefreshURIs()
5667 if (mRefreshURIList) {
5668 PRUint32 n = 0;
5669 mRefreshURIList->Count(&n);
5671 for (PRUint32 i = 0; i < n; ++i) {
5672 nsCOMPtr<nsITimer> timer = do_QueryElementAt(mRefreshURIList, i);
5673 if (!timer)
5674 continue; // this must be a nsRefreshURI already
5676 // Replace this timer object with a nsRefreshTimer object.
5677 nsCOMPtr<nsITimerCallback> callback;
5678 timer->GetCallback(getter_AddRefs(callback));
5680 timer->Cancel();
5682 nsCOMPtr<nsITimerCallback> rt = do_QueryInterface(callback);
5683 NS_ASSERTION(rt, "RefreshURIList timer callbacks should only be RefreshTimer objects");
5685 mRefreshURIList->ReplaceElementAt(rt, i);
5689 // Suspend refresh URIs for our child shells as well.
5690 PRInt32 n = mChildList.Count();
5692 for (PRInt32 i = 0; i < n; ++i) {
5693 nsCOMPtr<nsIDocShell> shell = do_QueryInterface(ChildAt(i));
5694 if (shell)
5695 shell->SuspendRefreshURIs();
5698 return NS_OK;
5701 NS_IMETHODIMP
5702 nsDocShell::ResumeRefreshURIs()
5704 RefreshURIFromQueue();
5706 // Resume refresh URIs for our child shells as well.
5707 PRInt32 n = mChildList.Count();
5709 for (PRInt32 i = 0; i < n; ++i) {
5710 nsCOMPtr<nsIDocShell> shell = do_QueryInterface(ChildAt(i));
5711 if (shell)
5712 shell->ResumeRefreshURIs();
5715 return NS_OK;
5718 nsresult
5719 nsDocShell::RefreshURIFromQueue()
5721 if (!mRefreshURIList)
5722 return NS_OK;
5723 PRUint32 n = 0;
5724 mRefreshURIList->Count(&n);
5726 while (n) {
5727 nsCOMPtr<nsISupports> element;
5728 mRefreshURIList->GetElementAt(--n, getter_AddRefs(element));
5729 nsCOMPtr<nsITimerCallback> refreshInfo(do_QueryInterface(element));
5731 if (refreshInfo) {
5732 // This is the nsRefreshTimer object, waiting to be
5733 // setup in a timer object and fired.
5734 // Create the timer and trigger it.
5735 PRUint32 delay = static_cast<nsRefreshTimer*>(static_cast<nsITimerCallback*>(refreshInfo))->GetDelay();
5736 nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1");
5737 if (timer) {
5738 // Replace the nsRefreshTimer element in the queue with
5739 // its corresponding timer object, so that in case another
5740 // load comes through before the timer can go off, the timer will
5741 // get cancelled in CancelRefreshURITimer()
5742 mRefreshURIList->ReplaceElementAt(timer, n);
5743 timer->InitWithCallback(refreshInfo, delay, nsITimer::TYPE_ONE_SHOT);
5746 } // while
5748 return NS_OK;
5751 //*****************************************************************************
5752 // nsDocShell::nsIContentViewerContainer
5753 //*****************************************************************************
5755 NS_IMETHODIMP
5756 nsDocShell::Embed(nsIContentViewer * aContentViewer,
5757 const char *aCommand, nsISupports * aExtraInfo)
5759 // Save the LayoutHistoryState of the previous document, before
5760 // setting up new document
5761 PersistLayoutHistoryState();
5763 nsresult rv = SetupNewViewer(aContentViewer);
5765 // If we are loading a wyciwyg url from history, change the base URI for
5766 // the document to the original http url that created the document.write().
5767 // This makes sure that all relative urls in a document.written page loaded
5768 // via history work properly.
5769 if (mCurrentURI &&
5770 (mLoadType & LOAD_CMD_HISTORY ||
5771 mLoadType == LOAD_RELOAD_NORMAL ||
5772 mLoadType == LOAD_RELOAD_CHARSET_CHANGE)){
5773 PRBool isWyciwyg = PR_FALSE;
5774 // Check if the url is wyciwyg
5775 rv = mCurrentURI->SchemeIs("wyciwyg", &isWyciwyg);
5776 if (isWyciwyg && NS_SUCCEEDED(rv))
5777 SetBaseUrlForWyciwyg(aContentViewer);
5779 // XXX What if SetupNewViewer fails?
5780 if (mLSHE) {
5781 // Restore the editing state, if it's stored in session history.
5782 if (mLSHE->HasDetachedEditor()) {
5783 ReattachEditorToWindow(mLSHE);
5785 SetHistoryEntry(&mOSHE, mLSHE);
5788 PRBool updateHistory = PR_TRUE;
5790 // Determine if this type of load should update history
5791 switch (mLoadType) {
5792 case LOAD_NORMAL_REPLACE:
5793 case LOAD_STOP_CONTENT_AND_REPLACE:
5794 case LOAD_RELOAD_BYPASS_CACHE:
5795 case LOAD_RELOAD_BYPASS_PROXY:
5796 case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
5797 updateHistory = PR_FALSE;
5798 break;
5799 default:
5800 break;
5803 if (!updateHistory)
5804 SetLayoutHistoryState(nsnull);
5806 return NS_OK;
5809 /* void setIsPrinting (in boolean aIsPrinting); */
5810 NS_IMETHODIMP
5811 nsDocShell::SetIsPrinting(PRBool aIsPrinting)
5813 mIsPrintingOrPP = aIsPrinting;
5814 return NS_OK;
5817 //*****************************************************************************
5818 // nsDocShell::nsIWebProgressListener
5819 //*****************************************************************************
5821 NS_IMETHODIMP
5822 nsDocShell::OnProgressChange(nsIWebProgress * aProgress,
5823 nsIRequest * aRequest,
5824 PRInt32 aCurSelfProgress,
5825 PRInt32 aMaxSelfProgress,
5826 PRInt32 aCurTotalProgress,
5827 PRInt32 aMaxTotalProgress)
5829 return NS_OK;
5832 NS_IMETHODIMP
5833 nsDocShell::OnStateChange(nsIWebProgress * aProgress, nsIRequest * aRequest,
5834 PRUint32 aStateFlags, nsresult aStatus)
5836 nsresult rv;
5838 if ((~aStateFlags & (STATE_START | STATE_IS_NETWORK)) == 0) {
5839 nsCOMPtr<nsIWyciwygChannel> wcwgChannel(do_QueryInterface(aRequest));
5840 nsCOMPtr<nsIWebProgress> webProgress =
5841 do_QueryInterface(GetAsSupports(this));
5843 // Was the wyciwyg document loaded on this docshell?
5844 if (wcwgChannel && !mLSHE && (mItemType == typeContent) && aProgress == webProgress.get()) {
5845 nsCOMPtr<nsIURI> uri;
5846 wcwgChannel->GetURI(getter_AddRefs(uri));
5848 PRBool equalUri = PR_TRUE;
5849 // Store the wyciwyg url in session history, only if it is
5850 // being loaded fresh for the first time. We don't want
5851 // multiple entries for successive loads
5852 if (mCurrentURI &&
5853 NS_SUCCEEDED(uri->Equals(mCurrentURI, &equalUri)) &&
5854 !equalUri) {
5856 nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
5857 GetSameTypeParent(getter_AddRefs(parentAsItem));
5858 nsCOMPtr<nsIDocShell> parentDS(do_QueryInterface(parentAsItem));
5859 PRBool inOnLoadHandler = PR_FALSE;
5860 if (parentDS) {
5861 parentDS->GetIsExecutingOnLoadHandler(&inOnLoadHandler);
5863 if (inOnLoadHandler) {
5864 // We're handling parent's load event listener, which causes
5865 // document.write in a subdocument.
5866 // Need to clear the session history for all child
5867 // docshells so that we can handle them like they would
5868 // all be added dynamically.
5869 nsCOMPtr<nsIDocShellHistory> parent =
5870 do_QueryInterface(parentAsItem);
5871 if (parent) {
5872 PRBool oshe = PR_FALSE;
5873 nsCOMPtr<nsISHEntry> entry;
5874 parent->GetCurrentSHEntry(getter_AddRefs(entry), &oshe);
5875 static_cast<nsDocShell*>(parent.get())->
5876 ClearFrameHistory(entry);
5880 // This is a document.write(). Get the made-up url
5881 // from the channel and store it in session history.
5882 rv = AddToSessionHistory(uri, wcwgChannel, nsnull,
5883 getter_AddRefs(mLSHE));
5884 SetCurrentURI(uri, aRequest, PR_TRUE);
5885 // Save history state of the previous page
5886 rv = PersistLayoutHistoryState();
5887 // We'll never get an Embed() for this load, so just go ahead
5888 // and SetHistoryEntry now.
5889 SetHistoryEntry(&mOSHE, mLSHE);
5893 // Page has begun to load
5894 mBusyFlags = BUSY_FLAGS_BUSY | BUSY_FLAGS_BEFORE_PAGE_LOAD;
5896 if ((aStateFlags & STATE_RESTORING) == 0) {
5897 // Show the progress cursor if the pref is set
5898 PRBool tmpBool = PR_FALSE;
5899 if (NS_SUCCEEDED(mPrefs->GetBoolPref("ui.use_activity_cursor", &tmpBool))
5900 && tmpBool) {
5901 nsCOMPtr<nsIWidget> mainWidget;
5902 GetMainWidget(getter_AddRefs(mainWidget));
5903 if (mainWidget) {
5904 mainWidget->SetCursor(eCursor_spinning);
5909 else if ((~aStateFlags & (STATE_TRANSFERRING | STATE_IS_DOCUMENT)) == 0) {
5910 // Page is loading
5911 mBusyFlags = BUSY_FLAGS_BUSY | BUSY_FLAGS_PAGE_LOADING;
5913 else if ((aStateFlags & STATE_STOP) && (aStateFlags & STATE_IS_NETWORK)) {
5914 // Page has finished loading
5915 mBusyFlags = BUSY_FLAGS_NONE;
5917 // Hide the progress cursor if the pref is set
5918 PRBool tmpBool = PR_FALSE;
5919 if (NS_SUCCEEDED(mPrefs->GetBoolPref("ui.use_activity_cursor", &tmpBool))
5920 && tmpBool) {
5921 nsCOMPtr<nsIWidget> mainWidget;
5922 GetMainWidget(getter_AddRefs(mainWidget));
5923 if (mainWidget) {
5924 mainWidget->SetCursor(eCursor_standard);
5928 if ((~aStateFlags & (STATE_IS_DOCUMENT | STATE_STOP)) == 0) {
5929 nsCOMPtr<nsIWebProgress> webProgress =
5930 do_QueryInterface(GetAsSupports(this));
5931 // Is the document stop notification for this document?
5932 if (aProgress == webProgress.get()) {
5933 nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
5934 EndPageLoad(aProgress, channel, aStatus);
5937 // note that redirect state changes will go through here as well, but it
5938 // is better to handle those in OnRedirectStateChange where more
5939 // information is available.
5940 return NS_OK;
5943 NS_IMETHODIMP
5944 nsDocShell::OnLocationChange(nsIWebProgress * aProgress,
5945 nsIRequest * aRequest, nsIURI * aURI)
5947 NS_NOTREACHED("notification excluded in AddProgressListener(...)");
5948 return NS_OK;
5951 void
5952 nsDocShell::OnRedirectStateChange(nsIChannel* aOldChannel,
5953 nsIChannel* aNewChannel,
5954 PRUint32 aRedirectFlags,
5955 PRUint32 aStateFlags)
5957 NS_ASSERTION(aStateFlags & STATE_REDIRECTING,
5958 "Calling OnRedirectStateChange when there is no redirect");
5959 if (!(aStateFlags & STATE_IS_DOCUMENT))
5960 return; // not a toplevel document
5962 nsCOMPtr<nsIURI> oldURI, newURI;
5963 aOldChannel->GetURI(getter_AddRefs(oldURI));
5964 aNewChannel->GetURI(getter_AddRefs(newURI));
5965 if (!oldURI || !newURI) {
5966 return;
5969 // Below a URI visit is saved (see AddURIVisit method doc).
5970 // The visit chain looks something like:
5971 // ...
5972 // Site N - 1
5973 // => Site N
5974 // (redirect to =>) Site N + 1 (we are here!)
5976 // Get N - 1 and transition type
5977 nsCOMPtr<nsIURI> previousURI;
5978 PRUint32 previousFlags = 0;
5979 ExtractLastVisit(aOldChannel, getter_AddRefs(previousURI), &previousFlags);
5981 if (aRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL ||
5982 ChannelIsPost(aOldChannel)) {
5983 // 1. Internal redirects are ignored because they are specific to the
5984 // channel implementation.
5985 // 2. POSTs are not saved by global history.
5987 // Regardless, we need to propagate the previous visit to the new
5988 // channel.
5989 SaveLastVisit(aNewChannel, previousURI, previousFlags);
5991 else {
5992 nsCOMPtr<nsIURI> referrer;
5993 // Treat referrer as null if there is an error getting it.
5994 (void)NS_GetReferrerFromChannel(aOldChannel,
5995 getter_AddRefs(referrer));
5997 // Add visit N -1 => N
5998 AddURIVisit(oldURI, referrer, previousURI, previousFlags);
6000 // Since N + 1 could be the final destination, we will not save N => N + 1
6001 // here. OnNewURI will do that, so we will cache it.
6002 SaveLastVisit(aNewChannel, oldURI, aRedirectFlags);
6005 // check if the new load should go through the application cache.
6006 nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
6007 do_QueryInterface(aNewChannel);
6008 if (appCacheChannel) {
6009 #ifdef MOZ_IPC
6010 // Permission will be checked in the parent process.
6011 if (GeckoProcessType_Default != XRE_GetProcessType())
6012 appCacheChannel->SetChooseApplicationCache(PR_TRUE);
6013 else
6014 #endif
6015 appCacheChannel->SetChooseApplicationCache(ShouldCheckAppCache(newURI));
6018 if (!(aRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL) &&
6019 mLoadType & (LOAD_CMD_RELOAD | LOAD_CMD_HISTORY)) {
6020 mLoadType = LOAD_NORMAL_REPLACE;
6021 SetHistoryEntry(&mLSHE, nsnull);
6025 NS_IMETHODIMP
6026 nsDocShell::OnStatusChange(nsIWebProgress * aWebProgress,
6027 nsIRequest * aRequest,
6028 nsresult aStatus, const PRUnichar * aMessage)
6030 NS_NOTREACHED("notification excluded in AddProgressListener(...)");
6031 return NS_OK;
6034 NS_IMETHODIMP
6035 nsDocShell::OnSecurityChange(nsIWebProgress * aWebProgress,
6036 nsIRequest * aRequest, PRUint32 state)
6038 NS_NOTREACHED("notification excluded in AddProgressListener(...)");
6039 return NS_OK;
6043 nsresult
6044 nsDocShell::EndPageLoad(nsIWebProgress * aProgress,
6045 nsIChannel * aChannel, nsresult aStatus)
6047 if(!aChannel)
6048 return NS_ERROR_NULL_POINTER;
6050 nsCOMPtr<nsIURI> url;
6051 nsresult rv = aChannel->GetURI(getter_AddRefs(url));
6052 if (NS_FAILED(rv)) return rv;
6054 // clean up reload state for meta charset
6055 if (eCharsetReloadRequested == mCharsetReloadState)
6056 mCharsetReloadState = eCharsetReloadStopOrigional;
6057 else
6058 mCharsetReloadState = eCharsetReloadInit;
6060 // Save a pointer to the currently-loading history entry.
6061 // nsDocShell::EndPageLoad will clear mLSHE, but we may need this history
6062 // entry further down in this method.
6063 nsCOMPtr<nsISHEntry> loadingSHE = mLSHE;
6066 // one of many safeguards that prevent death and destruction if
6067 // someone is so very very rude as to bring this window down
6068 // during this load handler.
6070 nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
6072 // Notify the ContentViewer that the Document has finished loading. This
6073 // will cause any OnLoad(...) and PopState(...) handlers to fire.
6074 if (!mEODForCurrentDocument && mContentViewer) {
6075 // Set the pending state object which will be returned to the page in
6076 // the popstate event.
6077 SetDocCurrentStateObj(mLSHE);
6079 mIsExecutingOnLoadHandler = PR_TRUE;
6080 rv = mContentViewer->LoadComplete(aStatus);
6082 // If the load wasn't stopped during LoadComplete, fire the popstate
6083 // event, if we're not suppressing it.
6084 if (NS_SUCCEEDED(rv) && rv != NS_SUCCESS_LOAD_STOPPED &&
6085 !mSuppressPopstate) {
6087 // XXX should I get the window via mScriptGlobal? This is tricky
6088 // since we're near onload and things might be changing. But I
6089 // think mContentViewer has the right view of the world.
6090 nsCOMPtr<nsIDocument> document = mContentViewer->GetDocument();
6091 if (document) {
6092 nsCOMPtr<nsPIDOMWindow> window = document->GetWindow();
6093 if (window) {
6094 // Dispatch the popstate event , passing PR_TRUE to indicate
6095 // that this is an "initial" (i.e. after-onload) popstate.
6096 window->DispatchSyncPopState(PR_TRUE);
6101 mIsExecutingOnLoadHandler = PR_FALSE;
6103 mEODForCurrentDocument = PR_TRUE;
6105 // If all documents have completed their loading
6106 // favor native event dispatch priorities
6107 // over performance
6108 if (--gNumberOfDocumentsLoading == 0) {
6109 // Hint to use normal native event dispatch priorities
6110 FavorPerformanceHint(PR_FALSE, NS_EVENT_STARVATION_DELAY_HINT);
6113 /* Check if the httpChannel has any cache-control related response headers,
6114 * like no-store, no-cache. If so, update SHEntry so that
6115 * when a user goes back/forward to this page, we appropriately do
6116 * form value restoration or load from server.
6118 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
6119 if (!httpChannel) // HttpChannel could be hiding underneath a Multipart channel.
6120 GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
6122 if (httpChannel) {
6123 // figure out if SH should be saving layout state.
6124 PRBool discardLayoutState = ShouldDiscardLayoutState(httpChannel);
6125 if (mLSHE && discardLayoutState && (mLoadType & LOAD_CMD_NORMAL) &&
6126 (mLoadType != LOAD_BYPASS_HISTORY) && (mLoadType != LOAD_ERROR_PAGE))
6127 mLSHE->SetSaveLayoutStateFlag(PR_FALSE);
6130 // Clear mLSHE after calling the onLoadHandlers. This way, if the
6131 // onLoadHandler tries to load something different in
6132 // itself or one of its children, we can deal with it appropriately.
6133 if (mLSHE) {
6134 mLSHE->SetLoadType(nsIDocShellLoadInfo::loadHistory);
6136 // Clear the mLSHE reference to indicate document loading is done one
6137 // way or another.
6138 SetHistoryEntry(&mLSHE, nsnull);
6140 // if there's a refresh header in the channel, this method
6141 // will set it up for us.
6142 RefreshURIFromQueue();
6144 // Test whether this is the top frame or a subframe
6145 PRBool isTopFrame = PR_TRUE;
6146 nsCOMPtr<nsIDocShellTreeItem> targetParentTreeItem;
6147 rv = GetSameTypeParent(getter_AddRefs(targetParentTreeItem));
6148 if (NS_SUCCEEDED(rv) && targetParentTreeItem) {
6149 isTopFrame = PR_FALSE;
6153 // If the page load failed, then deal with the error condition...
6154 // Errors are handled as follows:
6155 // 1. Check to see if it's a file not found error or bad content
6156 // encoding error.
6157 // 2. Send the URI to a keyword server (if enabled)
6158 // 3. If the error was DNS failure, then add www and .com to the URI
6159 // (if appropriate).
6160 // 4. Throw an error dialog box...
6162 if (url && NS_FAILED(aStatus)) {
6163 if (aStatus == NS_ERROR_FILE_NOT_FOUND ||
6164 aStatus == NS_ERROR_INVALID_CONTENT_ENCODING) {
6165 DisplayLoadError(aStatus, url, nsnull, aChannel);
6166 return NS_OK;
6169 if (sURIFixup) {
6171 // Try and make an alternative URI from the old one
6173 nsCOMPtr<nsIURI> newURI;
6175 nsCAutoString oldSpec;
6176 url->GetSpec(oldSpec);
6179 // First try keyword fixup
6181 if (aStatus == NS_ERROR_UNKNOWN_HOST && mAllowKeywordFixup) {
6182 PRBool keywordsEnabled = PR_FALSE;
6184 if (mPrefs &&
6185 NS_FAILED(mPrefs->GetBoolPref("keyword.enabled",
6186 &keywordsEnabled)))
6187 keywordsEnabled = PR_FALSE;
6189 nsCAutoString host;
6190 url->GetHost(host);
6192 nsCAutoString scheme;
6193 url->GetScheme(scheme);
6195 PRInt32 dotLoc = host.FindChar('.');
6197 // we should only perform a keyword search under the following
6198 // conditions:
6199 // (0) Pref keyword.enabled is true
6200 // (1) the url scheme is http (or https)
6201 // (2) the url does not have a protocol scheme
6202 // If we don't enforce such a policy, then we end up doing
6203 // keyword searchs on urls we don't intend like imap, file,
6204 // mailbox, etc. This could lead to a security problem where we
6205 // send data to the keyword server that we shouldn't be.
6206 // Someone needs to clean up keywords in general so we can
6207 // determine on a per url basis if we want keywords
6208 // enabled...this is just a bandaid...
6209 if (keywordsEnabled && !scheme.IsEmpty() &&
6210 (scheme.Find("http") != 0)) {
6211 keywordsEnabled = PR_FALSE;
6214 if (keywordsEnabled && (kNotFound == dotLoc)) {
6215 // only send non-qualified hosts to the keyword server
6217 // If this string was passed through nsStandardURL by
6218 // chance, then it may have been converted from UTF-8 to
6219 // ACE, which would result in a completely bogus keyword
6220 // query. Here we try to recover the original Unicode
6221 // value, but this is not 100% correct since the value may
6222 // have been normalized per the IDN normalization rules.
6224 // Since we don't have access to the exact original string
6225 // that was entered by the user, this will just have to do.
6226 PRBool isACE;
6227 nsCAutoString utf8Host;
6228 nsCOMPtr<nsIIDNService> idnSrv =
6229 do_GetService(NS_IDNSERVICE_CONTRACTID);
6230 if (idnSrv &&
6231 NS_SUCCEEDED(idnSrv->IsACE(host, &isACE)) && isACE &&
6232 NS_SUCCEEDED(idnSrv->ConvertACEtoUTF8(host, utf8Host)))
6233 sURIFixup->KeywordToURI(utf8Host,
6234 getter_AddRefs(newURI));
6235 else
6236 sURIFixup->KeywordToURI(host, getter_AddRefs(newURI));
6237 } // end keywordsEnabled
6241 // Now try change the address, e.g. turn http://foo into
6242 // http://www.foo.com
6244 if (aStatus == NS_ERROR_UNKNOWN_HOST ||
6245 aStatus == NS_ERROR_NET_RESET) {
6246 PRBool doCreateAlternate = PR_TRUE;
6248 // Skip fixup for anything except a normal document load
6249 // operation on the topframe.
6251 if (mLoadType != LOAD_NORMAL || !isTopFrame) {
6252 doCreateAlternate = PR_FALSE;
6254 else {
6255 // Test if keyword lookup produced a new URI or not
6256 if (newURI) {
6257 PRBool sameURI = PR_FALSE;
6258 url->Equals(newURI, &sameURI);
6259 if (!sameURI) {
6260 // Keyword lookup made a new URI so no need to try
6261 // an alternate one.
6262 doCreateAlternate = PR_FALSE;
6266 if (doCreateAlternate) {
6267 newURI = nsnull;
6268 sURIFixup->CreateFixupURI(oldSpec,
6269 nsIURIFixup::FIXUP_FLAGS_MAKE_ALTERNATE_URI,
6270 getter_AddRefs(newURI));
6274 // Did we make a new URI that is different to the old one? If so
6275 // load it.
6277 if (newURI) {
6278 // Make sure the new URI is different from the old one,
6279 // otherwise there's little point trying to load it again.
6280 PRBool sameURI = PR_FALSE;
6281 url->Equals(newURI, &sameURI);
6282 if (!sameURI) {
6283 nsCAutoString newSpec;
6284 newURI->GetSpec(newSpec);
6285 NS_ConvertUTF8toUTF16 newSpecW(newSpec);
6287 return LoadURI(newSpecW.get(), // URI string
6288 LOAD_FLAGS_NONE, // Load flags
6289 nsnull, // Referring URI
6290 nsnull, // Post data stream
6291 nsnull); // Headers stream
6296 // Well, fixup didn't work :-(
6297 // It is time to throw an error dialog box, and be done with it...
6299 // Errors to be shown only on top-level frames
6300 if ((aStatus == NS_ERROR_UNKNOWN_HOST ||
6301 aStatus == NS_ERROR_CONNECTION_REFUSED ||
6302 aStatus == NS_ERROR_UNKNOWN_PROXY_HOST ||
6303 aStatus == NS_ERROR_PROXY_CONNECTION_REFUSED) &&
6304 (isTopFrame || mUseErrorPages)) {
6305 DisplayLoadError(aStatus, url, nsnull, aChannel);
6307 // Errors to be shown for any frame
6308 else if (aStatus == NS_ERROR_NET_TIMEOUT ||
6309 aStatus == NS_ERROR_REDIRECT_LOOP ||
6310 aStatus == NS_ERROR_UNKNOWN_SOCKET_TYPE ||
6311 aStatus == NS_ERROR_NET_INTERRUPT ||
6312 aStatus == NS_ERROR_NET_RESET ||
6313 aStatus == NS_ERROR_MALWARE_URI ||
6314 aStatus == NS_ERROR_PHISHING_URI ||
6315 aStatus == NS_ERROR_UNSAFE_CONTENT_TYPE ||
6316 aStatus == NS_ERROR_REMOTE_XUL ||
6317 NS_ERROR_GET_MODULE(aStatus) == NS_ERROR_MODULE_SECURITY) {
6318 DisplayLoadError(aStatus, url, nsnull, aChannel);
6320 else if (aStatus == NS_ERROR_DOCUMENT_NOT_CACHED) {
6321 /* A document that was requested to be fetched *only* from
6322 * the cache is not in cache. May be this is one of those
6323 * postdata results. Throw a dialog to the user,
6324 * saying that the page has expired from cache and ask if
6325 * they wish to refetch the page from the net. Do this only
6326 * if the request is a form post.
6328 nsCAutoString method;
6329 if (httpChannel)
6330 httpChannel->GetRequestMethod(method);
6331 if (method.Equals("POST") && !NS_IsOffline()) {
6332 PRBool repost;
6333 rv = ConfirmRepost(&repost);
6334 if (NS_FAILED(rv)) return rv;
6335 // If the user pressed cancel in the dialog, return. Don't try
6336 // to load the page without the post data.
6337 if (!repost)
6338 return NS_OK;
6340 // The user wants to repost the data to the server.
6341 // If the page was loaded due to a back/forward/go
6342 // operation, update the session history index.
6343 // This is similar to the updating done in
6344 // nsDocShell::OnNewURI() for regular pages
6345 nsCOMPtr<nsISHistory> rootSH=mSessionHistory;
6346 if (!mSessionHistory) {
6347 nsCOMPtr<nsIDocShellTreeItem> root;
6348 //Get the root docshell
6349 GetSameTypeRootTreeItem(getter_AddRefs(root));
6350 if (root) {
6351 // QI root to nsIWebNavigation
6352 nsCOMPtr<nsIWebNavigation> rootAsWebnav =
6353 do_QueryInterface(root);
6354 if (rootAsWebnav) {
6355 // Get the handle to SH from the root docshell
6356 rootAsWebnav->GetSessionHistory(getter_AddRefs(rootSH));
6359 } // mSessionHistory
6361 if (rootSH && (mLoadType & LOAD_CMD_HISTORY)) {
6362 nsCOMPtr<nsISHistoryInternal> shInternal =
6363 do_QueryInterface(rootSH);
6364 if (shInternal) {
6365 rootSH->GetIndex(&mPreviousTransIndex);
6366 shInternal->UpdateIndex();
6367 rootSH->GetIndex(&mLoadedTransIndex);
6368 #ifdef DEBUG_PAGE_CACHE
6369 printf("Previous index: %d, Loaded index: %d\n\n",
6370 mPreviousTransIndex, mLoadedTransIndex);
6371 #endif
6375 // Make it look like we really did honestly finish loading the
6376 // history page we were loading, since the "reload" load we're
6377 // about to kick off will reload our current history entry.
6378 // This is a bit of a hack, and if the force-load fails I think
6379 // we'll end up being confused about what page we're on... but
6380 // we would anyway, since we've updated the session history
6381 // index above.
6382 SetHistoryEntry(&mOSHE, loadingSHE);
6384 // The user does want to repost the data to the server.
6385 // Initiate a new load again.
6387 // Get the postdata if any from the channel.
6388 nsCOMPtr<nsIInputStream> inputStream;
6389 nsCOMPtr<nsIURI> referrer;
6390 if (httpChannel) {
6391 httpChannel->GetReferrer(getter_AddRefs(referrer));
6392 nsCOMPtr<nsIUploadChannel> uploadChannel =
6393 do_QueryInterface(aChannel);
6394 if (uploadChannel) {
6395 uploadChannel->GetUploadStream(getter_AddRefs(inputStream));
6398 nsCOMPtr<nsISeekableStream> postDataSeekable =
6399 do_QueryInterface(inputStream);
6400 if (postDataSeekable) {
6401 postDataSeekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
6403 InternalLoad(url, // URI
6404 referrer, // Referring URI
6405 nsnull, // Owner
6406 INTERNAL_LOAD_FLAGS_INHERIT_OWNER, // Inherit owner
6407 nsnull, // No window target
6408 nsnull, // No type hint
6409 inputStream, // Post data stream
6410 nsnull, // No headers stream
6411 LOAD_RELOAD_BYPASS_PROXY_AND_CACHE,// Load type
6412 nsnull, // No SHEntry
6413 PR_TRUE, // first party site
6414 nsnull, // No nsIDocShell
6415 nsnull); // No nsIRequest
6417 else {
6418 DisplayLoadError(aStatus, url, nsnull, aChannel);
6421 } // if we have a host
6423 return NS_OK;
6427 //*****************************************************************************
6428 // nsDocShell: Content Viewer Management
6429 //*****************************************************************************
6431 NS_IMETHODIMP
6432 nsDocShell::EnsureContentViewer()
6434 if (mContentViewer)
6435 return NS_OK;
6436 if (mIsBeingDestroyed)
6437 return NS_ERROR_FAILURE;
6439 NS_TIME_FUNCTION;
6441 nsIPrincipal* principal = nsnull;
6442 nsCOMPtr<nsIURI> baseURI;
6444 nsCOMPtr<nsPIDOMWindow> piDOMWindow(do_QueryInterface(mScriptGlobal));
6445 if (piDOMWindow) {
6446 principal = piDOMWindow->GetOpenerScriptPrincipal();
6449 if (!principal) {
6450 principal = GetInheritedPrincipal(PR_FALSE);
6451 nsCOMPtr<nsIDocShellTreeItem> parentItem;
6452 GetSameTypeParent(getter_AddRefs(parentItem));
6453 if (parentItem) {
6454 nsCOMPtr<nsPIDOMWindow> domWin = do_GetInterface(GetAsSupports(this));
6455 if (domWin) {
6456 nsCOMPtr<nsIContent> parentContent =
6457 do_QueryInterface(domWin->GetFrameElementInternal());
6458 if (parentContent) {
6459 baseURI = parentContent->GetBaseURI();
6465 nsresult rv = CreateAboutBlankContentViewer(principal, baseURI);
6467 if (NS_SUCCEEDED(rv)) {
6468 nsCOMPtr<nsIDocument> doc(do_GetInterface(GetAsSupports(this)));
6469 NS_ASSERTION(doc,
6470 "Should have doc if CreateAboutBlankContentViewer "
6471 "succeeded!");
6473 doc->SetIsInitialDocument(PR_TRUE);
6476 return rv;
6479 nsresult
6480 nsDocShell::CreateAboutBlankContentViewer(nsIPrincipal* aPrincipal,
6481 nsIURI* aBaseURI,
6482 PRBool aTryToSaveOldPresentation)
6484 nsCOMPtr<nsIDocument> blankDoc;
6485 nsCOMPtr<nsIContentViewer> viewer;
6486 nsresult rv = NS_ERROR_FAILURE;
6488 /* mCreatingDocument should never be true at this point. However, it's
6489 a theoretical possibility. We want to know about it and make it stop,
6490 and this sounds like a job for an assertion. */
6491 NS_ASSERTION(!mCreatingDocument, "infinite(?) loop creating document averted");
6492 if (mCreatingDocument)
6493 return NS_ERROR_FAILURE;
6495 mCreatingDocument = PR_TRUE;
6497 // mContentViewer->PermitUnload may release |this| docshell.
6498 nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
6500 if (mContentViewer) {
6501 // We've got a content viewer already. Make sure the user
6502 // permits us to discard the current document and replace it
6503 // with about:blank. And also ensure we fire the unload events
6504 // in the current document.
6506 PRBool okToUnload;
6507 rv = mContentViewer->PermitUnload(PR_FALSE, &okToUnload);
6509 if (NS_SUCCEEDED(rv) && !okToUnload) {
6510 // The user chose not to unload the page, interrupt the load.
6511 return NS_ERROR_FAILURE;
6514 mSavingOldViewer = aTryToSaveOldPresentation &&
6515 CanSavePresentation(LOAD_NORMAL, nsnull, nsnull);
6517 // Make sure to blow away our mLoadingURI just in case. No loads
6518 // from inside this pagehide.
6519 mLoadingURI = nsnull;
6521 // Notify the current document that it is about to be unloaded!!
6523 // It is important to fire the unload() notification *before* any state
6524 // is changed within the DocShell - otherwise, javascript will get the
6525 // wrong information :-(
6527 (void) FirePageHideNotification(!mSavingOldViewer);
6530 // Now make sure we don't think we're in the middle of firing unload after
6531 // this point. This will make us fire unload when the about:blank document
6532 // unloads... but that's ok, more or less. Would be nice if it fired load
6533 // too, of course.
6534 mFiredUnloadEvent = PR_FALSE;
6536 nsCOMPtr<nsIContentUtils> cutils = do_GetService("@mozilla.org/content/contentutils;1");
6537 if (!cutils)
6538 return NS_ERROR_FAILURE;
6540 nsCOMPtr<nsIDocumentLoaderFactory> docFactory = cutils->FindInternalContentViewer("text/html");
6541 if (docFactory) {
6542 // generate (about:blank) document to load
6543 docFactory->CreateBlankDocument(mLoadGroup, aPrincipal,
6544 getter_AddRefs(blankDoc));
6545 if (blankDoc) {
6546 // Hack: set the base URI manually, since this document never
6547 // got Reset() with a channel.
6548 blankDoc->SetBaseURI(aBaseURI);
6550 blankDoc->SetContainer(static_cast<nsIDocShell *>(this));
6552 // create a content viewer for us and the new document
6553 docFactory->CreateInstanceForDocument(NS_ISUPPORTS_CAST(nsIDocShell *, this),
6554 blankDoc, "view", getter_AddRefs(viewer));
6556 // hook 'em up
6557 if (viewer) {
6558 viewer->SetContainer(static_cast<nsIContentViewerContainer *>(this));
6559 Embed(viewer, "", 0);
6561 SetCurrentURI(blankDoc->GetDocumentURI(), nsnull, PR_TRUE);
6562 rv = mIsBeingDestroyed ? NS_ERROR_NOT_AVAILABLE : NS_OK;
6566 mCreatingDocument = PR_FALSE;
6568 // The transient about:blank viewer doesn't have a session history entry.
6569 SetHistoryEntry(&mOSHE, nsnull);
6571 return rv;
6574 NS_IMETHODIMP
6575 nsDocShell::CreateAboutBlankContentViewer(nsIPrincipal *aPrincipal)
6577 return CreateAboutBlankContentViewer(aPrincipal, nsnull);
6580 PRBool
6581 nsDocShell::CanSavePresentation(PRUint32 aLoadType,
6582 nsIRequest *aNewRequest,
6583 nsIDocument *aNewDocument)
6585 if (!mOSHE)
6586 return PR_FALSE; // no entry to save into
6588 nsCOMPtr<nsIContentViewer> viewer;
6589 mOSHE->GetContentViewer(getter_AddRefs(viewer));
6590 if (viewer) {
6591 NS_WARNING("mOSHE already has a content viewer!");
6592 return PR_FALSE;
6595 // Only save presentation for "normal" loads and link loads. Anything else
6596 // probably wants to refetch the page, so caching the old presentation
6597 // would be incorrect.
6598 if (aLoadType != LOAD_NORMAL &&
6599 aLoadType != LOAD_HISTORY &&
6600 aLoadType != LOAD_LINK &&
6601 aLoadType != LOAD_STOP_CONTENT &&
6602 aLoadType != LOAD_STOP_CONTENT_AND_REPLACE &&
6603 aLoadType != LOAD_ERROR_PAGE)
6604 return PR_FALSE;
6606 // If the session history entry has the saveLayoutState flag set to false,
6607 // then we should not cache the presentation.
6608 PRBool canSaveState;
6609 mOSHE->GetSaveLayoutStateFlag(&canSaveState);
6610 if (!canSaveState)
6611 return PR_FALSE;
6613 // If the document is not done loading, don't cache it.
6614 nsCOMPtr<nsPIDOMWindow> pWin = do_QueryInterface(mScriptGlobal);
6615 if (!pWin || pWin->IsLoading())
6616 return PR_FALSE;
6618 if (pWin->WouldReuseInnerWindow(aNewDocument))
6619 return PR_FALSE;
6621 // Avoid doing the work of saving the presentation state in the case where
6622 // the content viewer cache is disabled.
6623 if (nsSHistory::GetMaxTotalViewers() == 0)
6624 return PR_FALSE;
6626 // Don't cache the content viewer if we're in a subframe and the subframe
6627 // pref is disabled.
6628 PRBool cacheFrames = PR_FALSE;
6629 mPrefs->GetBoolPref("browser.sessionhistory.cache_subframes",
6630 &cacheFrames);
6631 if (!cacheFrames) {
6632 nsCOMPtr<nsIDocShellTreeItem> root;
6633 GetSameTypeParent(getter_AddRefs(root));
6634 if (root && root != this) {
6635 return PR_FALSE; // this is a subframe load
6639 // If the document does not want its presentation cached, then don't.
6640 nsCOMPtr<nsIDocument> doc = do_QueryInterface(pWin->GetExtantDocument());
6641 if (!doc || !doc->CanSavePresentation(aNewRequest))
6642 return PR_FALSE;
6644 return PR_TRUE;
6647 void
6648 nsDocShell::ReattachEditorToWindow(nsISHEntry *aSHEntry)
6650 NS_ASSERTION(!mEditorData,
6651 "Why reattach an editor when we already have one?");
6652 NS_ASSERTION(aSHEntry && aSHEntry->HasDetachedEditor(),
6653 "Reattaching when there's not a detached editor.");
6655 if (mEditorData || !aSHEntry)
6656 return;
6658 mEditorData = aSHEntry->ForgetEditorData();
6659 if (mEditorData) {
6660 #ifdef DEBUG
6661 nsresult rv =
6662 #endif
6663 mEditorData->ReattachToWindow(this);
6664 NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to reattach editing session");
6668 void
6669 nsDocShell::DetachEditorFromWindow()
6671 if (!mEditorData || mEditorData->WaitingForLoad()) {
6672 // If there's nothing to detach, or if the editor data is actually set
6673 // up for the _new_ page that's coming in, don't detach.
6674 return;
6677 NS_ASSERTION(!mOSHE || !mOSHE->HasDetachedEditor(),
6678 "Detaching editor when it's already detached.");
6680 nsresult res = mEditorData->DetachFromWindow();
6681 NS_ASSERTION(NS_SUCCEEDED(res), "Failed to detach editor");
6683 if (NS_SUCCEEDED(res)) {
6684 // Make mOSHE hold the owning ref to the editor data.
6685 if (mOSHE)
6686 mOSHE->SetEditorData(mEditorData.forget());
6687 else
6688 mEditorData = nsnull;
6691 #ifdef DEBUG
6693 PRBool isEditable;
6694 GetEditable(&isEditable);
6695 NS_ASSERTION(!isEditable,
6696 "Window is still editable after detaching editor.");
6698 #endif // DEBUG
6701 nsresult
6702 nsDocShell::CaptureState()
6704 if (!mOSHE || mOSHE == mLSHE) {
6705 // No entry to save into, or we're replacing the existing entry.
6706 return NS_ERROR_FAILURE;
6709 nsCOMPtr<nsPIDOMWindow> privWin = do_QueryInterface(mScriptGlobal);
6710 if (!privWin)
6711 return NS_ERROR_FAILURE;
6713 nsCOMPtr<nsISupports> windowState;
6714 nsresult rv = privWin->SaveWindowState(getter_AddRefs(windowState));
6715 NS_ENSURE_SUCCESS(rv, rv);
6717 #ifdef DEBUG_PAGE_CACHE
6718 nsCOMPtr<nsIURI> uri;
6719 mOSHE->GetURI(getter_AddRefs(uri));
6720 nsCAutoString spec;
6721 if (uri)
6722 uri->GetSpec(spec);
6723 printf("Saving presentation into session history\n");
6724 printf(" SH URI: %s\n", spec.get());
6725 #endif
6727 rv = mOSHE->SetWindowState(windowState);
6728 NS_ENSURE_SUCCESS(rv, rv);
6730 // Suspend refresh URIs and save off the timer queue
6731 rv = mOSHE->SetRefreshURIList(mSavedRefreshURIList);
6732 NS_ENSURE_SUCCESS(rv, rv);
6734 // Capture the current content viewer bounds.
6735 if (mContentViewer) {
6736 nsIntRect bounds;
6737 mContentViewer->GetBounds(bounds);
6738 rv = mOSHE->SetViewerBounds(bounds);
6739 NS_ENSURE_SUCCESS(rv, rv);
6742 // Capture the docshell hierarchy.
6743 mOSHE->ClearChildShells();
6745 PRInt32 childCount = mChildList.Count();
6746 for (PRInt32 i = 0; i < childCount; ++i) {
6747 nsCOMPtr<nsIDocShellTreeItem> childShell = do_QueryInterface(ChildAt(i));
6748 NS_ASSERTION(childShell, "null child shell");
6750 mOSHE->AddChildShell(childShell);
6753 return NS_OK;
6756 NS_IMETHODIMP
6757 nsDocShell::RestorePresentationEvent::Run()
6759 if (mDocShell && NS_FAILED(mDocShell->RestoreFromHistory()))
6760 NS_WARNING("RestoreFromHistory failed");
6761 return NS_OK;
6764 NS_IMETHODIMP
6765 nsDocShell::BeginRestore(nsIContentViewer *aContentViewer, PRBool aTop)
6767 nsresult rv;
6768 if (!aContentViewer) {
6769 rv = EnsureContentViewer();
6770 NS_ENSURE_SUCCESS(rv, rv);
6772 aContentViewer = mContentViewer;
6775 // Dispatch events for restoring the presentation. We try to simulate
6776 // the progress notifications loading the document would cause, so we add
6777 // the document's channel to the loadgroup to initiate stateChange
6778 // notifications.
6780 nsCOMPtr<nsIDOMDocument> domDoc;
6781 aContentViewer->GetDOMDocument(getter_AddRefs(domDoc));
6782 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
6783 if (doc) {
6784 nsIChannel *channel = doc->GetChannel();
6785 if (channel) {
6786 mEODForCurrentDocument = PR_FALSE;
6787 mIsRestoringDocument = PR_TRUE;
6788 mLoadGroup->AddRequest(channel, nsnull);
6789 mIsRestoringDocument = PR_FALSE;
6793 if (!aTop) {
6794 // This point corresponds to us having gotten OnStartRequest or
6795 // STATE_START, so do the same thing that CreateContentViewer does at
6796 // this point to ensure that unload/pagehide events for this document
6797 // will fire when it's unloaded again.
6798 mFiredUnloadEvent = PR_FALSE;
6800 // For non-top frames, there is no notion of making sure that the
6801 // previous document is in the domwindow when STATE_START notifications
6802 // happen. We can just call BeginRestore for all of the child shells
6803 // now.
6804 rv = BeginRestoreChildren();
6805 NS_ENSURE_SUCCESS(rv, rv);
6808 return NS_OK;
6811 nsresult
6812 nsDocShell::BeginRestoreChildren()
6814 PRInt32 n = mChildList.Count();
6815 for (PRInt32 i = 0; i < n; ++i) {
6816 nsCOMPtr<nsIDocShell> child = do_QueryInterface(ChildAt(i));
6817 if (child) {
6818 nsresult rv = child->BeginRestore(nsnull, PR_FALSE);
6819 NS_ENSURE_SUCCESS(rv, rv);
6822 return NS_OK;
6825 NS_IMETHODIMP
6826 nsDocShell::FinishRestore()
6828 // First we call finishRestore() on our children. In the simulated load,
6829 // all of the child frames finish loading before the main document.
6831 PRInt32 n = mChildList.Count();
6832 for (PRInt32 i = 0; i < n; ++i) {
6833 nsCOMPtr<nsIDocShell> child = do_QueryInterface(ChildAt(i));
6834 if (child) {
6835 child->FinishRestore();
6839 if (mOSHE && mOSHE->HasDetachedEditor()) {
6840 ReattachEditorToWindow(mOSHE);
6843 nsCOMPtr<nsIDocument> doc = do_GetInterface(GetAsSupports(this));
6844 if (doc) {
6845 // Finally, we remove the request from the loadgroup. This will
6846 // cause onStateChange(STATE_STOP) to fire, which will fire the
6847 // pageshow event to the chrome.
6849 nsIChannel *channel = doc->GetChannel();
6850 if (channel) {
6851 mIsRestoringDocument = PR_TRUE;
6852 mLoadGroup->RemoveRequest(channel, nsnull, NS_OK);
6853 mIsRestoringDocument = PR_FALSE;
6857 return NS_OK;
6860 NS_IMETHODIMP
6861 nsDocShell::GetRestoringDocument(PRBool *aRestoring)
6863 *aRestoring = mIsRestoringDocument;
6864 return NS_OK;
6867 nsresult
6868 nsDocShell::RestorePresentation(nsISHEntry *aSHEntry, PRBool *aRestoring)
6870 NS_ASSERTION(mLoadType & LOAD_CMD_HISTORY,
6871 "RestorePresentation should only be called for history loads");
6873 nsCOMPtr<nsIContentViewer> viewer;
6874 aSHEntry->GetContentViewer(getter_AddRefs(viewer));
6876 #ifdef DEBUG_PAGE_CACHE
6877 nsCOMPtr<nsIURI> uri;
6878 aSHEntry->GetURI(getter_AddRefs(uri));
6880 nsCAutoString spec;
6881 if (uri)
6882 uri->GetSpec(spec);
6883 #endif
6885 *aRestoring = PR_FALSE;
6887 if (!viewer) {
6888 #ifdef DEBUG_PAGE_CACHE
6889 printf("no saved presentation for uri: %s\n", spec.get());
6890 #endif
6891 return NS_OK;
6894 // We need to make sure the content viewer's container is this docshell.
6895 // In subframe navigation, it's possible for the docshell that the
6896 // content viewer was originally loaded into to be replaced with a
6897 // different one. We don't currently support restoring the presentation
6898 // in that case.
6900 nsCOMPtr<nsISupports> container;
6901 viewer->GetContainer(getter_AddRefs(container));
6902 if (!::SameCOMIdentity(container, GetAsSupports(this))) {
6903 #ifdef DEBUG_PAGE_CACHE
6904 printf("No valid container, clearing presentation\n");
6905 #endif
6906 aSHEntry->SetContentViewer(nsnull);
6907 return NS_ERROR_FAILURE;
6910 NS_ASSERTION(mContentViewer != viewer, "Restoring existing presentation");
6912 #ifdef DEBUG_PAGE_CACHE
6913 printf("restoring presentation from session history: %s\n", spec.get());
6914 #endif
6916 SetHistoryEntry(&mLSHE, aSHEntry);
6918 // Add the request to our load group. We do this before swapping out
6919 // the content viewers so that consumers of STATE_START can access
6920 // the old document. We only deal with the toplevel load at this time --
6921 // to be consistent with normal document loading, subframes cannot start
6922 // loading until after data arrives, which is after STATE_START completes.
6924 BeginRestore(viewer, PR_TRUE);
6926 // Post an event that will remove the request after we've returned
6927 // to the event loop. This mimics the way it is called by nsIChannel
6928 // implementations.
6930 // Revoke any pending restore (just in case)
6931 NS_ASSERTION(!mRestorePresentationEvent.IsPending(),
6932 "should only have one RestorePresentationEvent");
6933 mRestorePresentationEvent.Revoke();
6935 nsRefPtr<RestorePresentationEvent> evt = new RestorePresentationEvent(this);
6936 nsresult rv = NS_DispatchToCurrentThread(evt);
6937 if (NS_SUCCEEDED(rv)) {
6938 mRestorePresentationEvent = evt.get();
6939 // The rest of the restore processing will happen on our event
6940 // callback.
6941 *aRestoring = PR_TRUE;
6944 return rv;
6947 nsresult
6948 nsDocShell::RestoreFromHistory()
6950 mRestorePresentationEvent.Forget();
6952 // This section of code follows the same ordering as CreateContentViewer.
6953 if (!mLSHE)
6954 return NS_ERROR_FAILURE;
6956 nsCOMPtr<nsIContentViewer> viewer;
6957 mLSHE->GetContentViewer(getter_AddRefs(viewer));
6958 if (!viewer)
6959 return NS_ERROR_FAILURE;
6961 if (mSavingOldViewer) {
6962 // We determined that it was safe to cache the document presentation
6963 // at the time we initiated the new load. We need to check whether
6964 // it's still safe to do so, since there may have been DOM mutations
6965 // or new requests initiated.
6966 nsCOMPtr<nsIDOMDocument> domDoc;
6967 viewer->GetDOMDocument(getter_AddRefs(domDoc));
6968 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
6969 nsIRequest *request = nsnull;
6970 if (doc)
6971 request = doc->GetChannel();
6972 mSavingOldViewer = CanSavePresentation(mLoadType, request, doc);
6975 nsCOMPtr<nsIMarkupDocumentViewer> oldMUDV(do_QueryInterface(mContentViewer));
6976 nsCOMPtr<nsIMarkupDocumentViewer> newMUDV(do_QueryInterface(viewer));
6977 float textZoom = 1.0f;
6978 float pageZoom = 1.0f;
6979 PRBool styleDisabled = PR_FALSE;
6980 if (oldMUDV && newMUDV) {
6981 oldMUDV->GetTextZoom(&textZoom);
6982 oldMUDV->GetFullZoom(&pageZoom);
6983 oldMUDV->GetAuthorStyleDisabled(&styleDisabled);
6986 // Protect against mLSHE going away via a load triggered from
6987 // pagehide or unload.
6988 nsCOMPtr<nsISHEntry> origLSHE = mLSHE;
6990 // Make sure to blow away our mLoadingURI just in case. No loads
6991 // from inside this pagehide.
6992 mLoadingURI = nsnull;
6994 // Notify the old content viewer that it's being hidden.
6995 FirePageHideNotification(!mSavingOldViewer);
6997 // If mLSHE was changed as a result of the pagehide event, then
6998 // something else was loaded. Don't finish restoring.
6999 if (mLSHE != origLSHE)
7000 return NS_OK;
7002 // Set mFiredUnloadEvent = PR_FALSE so that the unload handler for the
7003 // *new* document will fire.
7004 mFiredUnloadEvent = PR_FALSE;
7006 mURIResultedInDocument = PR_TRUE;
7007 nsCOMPtr<nsISHistory> rootSH;
7008 GetRootSessionHistory(getter_AddRefs(rootSH));
7009 if (rootSH) {
7010 nsCOMPtr<nsISHistoryInternal> hist = do_QueryInterface(rootSH);
7011 rootSH->GetIndex(&mPreviousTransIndex);
7012 hist->UpdateIndex();
7013 rootSH->GetIndex(&mLoadedTransIndex);
7014 #ifdef DEBUG_PAGE_CACHE
7015 printf("Previous index: %d, Loaded index: %d\n\n", mPreviousTransIndex,
7016 mLoadedTransIndex);
7017 #endif
7020 // Rather than call Embed(), we will retrieve the viewer from the session
7021 // history entry and swap it in.
7022 // XXX can we refactor this so that we can just call Embed()?
7023 PersistLayoutHistoryState();
7024 nsresult rv;
7025 if (mContentViewer) {
7026 if (mSavingOldViewer && NS_FAILED(CaptureState())) {
7027 if (mOSHE) {
7028 mOSHE->SyncPresentationState();
7030 mSavingOldViewer = PR_FALSE;
7034 mSavedRefreshURIList = nsnull;
7036 // In cases where we use a transient about:blank viewer between loads,
7037 // we never show the transient viewer, so _its_ previous viewer is never
7038 // unhooked from the view hierarchy. Destroy any such previous viewer now,
7039 // before we grab the root view sibling, so that we don't grab a view
7040 // that's about to go away.
7042 if (mContentViewer) {
7043 nsCOMPtr<nsIContentViewer> previousViewer;
7044 mContentViewer->GetPreviousViewer(getter_AddRefs(previousViewer));
7045 if (previousViewer) {
7046 mContentViewer->SetPreviousViewer(nsnull);
7047 previousViewer->Destroy();
7051 // Save off the root view's parent and sibling so that we can insert the
7052 // new content viewer's root view at the same position. Also save the
7053 // bounds of the root view's widget.
7055 nsIView *rootViewSibling = nsnull, *rootViewParent = nsnull;
7056 nsIntRect newBounds(0, 0, 0, 0);
7058 nsCOMPtr<nsIPresShell> oldPresShell;
7059 nsDocShell::GetPresShell(getter_AddRefs(oldPresShell));
7060 if (oldPresShell) {
7061 nsIViewManager *vm = oldPresShell->GetViewManager();
7062 if (vm) {
7063 nsIView *oldRootView = nsnull;
7064 vm->GetRootView(oldRootView);
7066 if (oldRootView) {
7067 rootViewSibling = oldRootView->GetNextSibling();
7068 rootViewParent = oldRootView->GetParent();
7070 mContentViewer->GetBounds(newBounds);
7075 // Transfer ownership to mContentViewer. By ensuring that either the
7076 // docshell or the session history, but not both, have references to the
7077 // content viewer, we prevent the viewer from being torn down after
7078 // Destroy() is called.
7080 if (mContentViewer) {
7081 mContentViewer->Close(mSavingOldViewer ? mOSHE.get() : nsnull);
7082 viewer->SetPreviousViewer(mContentViewer);
7085 // Order the mContentViewer setup just like Embed does.
7086 mContentViewer = nsnull;
7088 // Now that we're about to switch documents, forget all of our children.
7089 // Note that we cached them as needed up in CaptureState above.
7090 DestroyChildren();
7092 mContentViewer.swap(viewer);
7094 // Grab all of the related presentation from the SHEntry now.
7095 // Clearing the viewer from the SHEntry will clear all of this state.
7096 nsCOMPtr<nsISupports> windowState;
7097 mLSHE->GetWindowState(getter_AddRefs(windowState));
7098 mLSHE->SetWindowState(nsnull);
7100 PRBool sticky;
7101 mLSHE->GetSticky(&sticky);
7103 nsCOMPtr<nsIDOMDocument> domDoc;
7104 mContentViewer->GetDOMDocument(getter_AddRefs(domDoc));
7106 nsCOMArray<nsIDocShellTreeItem> childShells;
7107 PRInt32 i = 0;
7108 nsCOMPtr<nsIDocShellTreeItem> child;
7109 while (NS_SUCCEEDED(mLSHE->ChildShellAt(i++, getter_AddRefs(child))) &&
7110 child) {
7111 childShells.AppendObject(child);
7114 // get the previous content viewer size
7115 nsIntRect oldBounds(0, 0, 0, 0);
7116 mLSHE->GetViewerBounds(oldBounds);
7118 // Restore the refresh URI list. The refresh timers will be restarted
7119 // when EndPageLoad() is called.
7120 nsCOMPtr<nsISupportsArray> refreshURIList;
7121 mLSHE->GetRefreshURIList(getter_AddRefs(refreshURIList));
7123 // Reattach to the window object.
7124 rv = mContentViewer->Open(windowState, mLSHE);
7126 // Now remove it from the cached presentation.
7127 mLSHE->SetContentViewer(nsnull);
7128 mEODForCurrentDocument = PR_FALSE;
7130 #ifdef DEBUG
7132 nsCOMPtr<nsISupportsArray> refreshURIs;
7133 mLSHE->GetRefreshURIList(getter_AddRefs(refreshURIs));
7134 nsCOMPtr<nsIDocShellTreeItem> childShell;
7135 mLSHE->ChildShellAt(0, getter_AddRefs(childShell));
7136 NS_ASSERTION(!refreshURIs && !childShell,
7137 "SHEntry should have cleared presentation state");
7139 #endif
7141 // Restore the sticky state of the viewer. The viewer has set this state
7142 // on the history entry in Destroy() just before marking itself non-sticky,
7143 // to avoid teardown of the presentation.
7144 mContentViewer->SetSticky(sticky);
7146 NS_ENSURE_SUCCESS(rv, rv);
7148 // mLSHE is now our currently-loaded document.
7149 SetHistoryEntry(&mOSHE, mLSHE);
7151 // XXX special wyciwyg handling in Embed()?
7153 // We aren't going to restore any items from the LayoutHistoryState,
7154 // but we don't want them to stay around in case the page is reloaded.
7155 SetLayoutHistoryState(nsnull);
7157 // This is the end of our Embed() replacement
7159 mSavingOldViewer = PR_FALSE;
7160 mEODForCurrentDocument = PR_FALSE;
7162 // Tell the event loop to favor plevents over user events, see comments
7163 // in CreateContentViewer.
7164 if (++gNumberOfDocumentsLoading == 1)
7165 FavorPerformanceHint(PR_TRUE, NS_EVENT_STARVATION_DELAY_HINT);
7168 if (oldMUDV && newMUDV) {
7169 newMUDV->SetTextZoom(textZoom);
7170 newMUDV->SetFullZoom(pageZoom);
7171 newMUDV->SetAuthorStyleDisabled(styleDisabled);
7174 nsCOMPtr<nsIDocument> document = do_QueryInterface(domDoc);
7175 PRUint32 parentSuspendCount = 0;
7176 if (document) {
7177 nsCOMPtr<nsIDocShellTreeItem> parent;
7178 GetParent(getter_AddRefs(parent));
7179 nsCOMPtr<nsIDocument> d = do_GetInterface(parent);
7180 if (d) {
7181 if (d->EventHandlingSuppressed()) {
7182 document->SuppressEventHandling(d->EventHandlingSuppressed());
7184 nsCOMPtr<nsPIDOMWindow> parentWindow = d->GetWindow();
7185 if (parentWindow) {
7186 parentSuspendCount = parentWindow->TimeoutSuspendCount();
7190 // Use the uri from the mLSHE we had when we entered this function
7191 // (which need not match the document's URI if anchors are involved),
7192 // since that's the history entry we're loading. Note that if we use
7193 // origLSHE we don't have to worry about whether the entry in question
7194 // is still mLSHE or whether it's now mOSHE.
7195 nsCOMPtr<nsIURI> uri;
7196 origLSHE->GetURI(getter_AddRefs(uri));
7197 SetCurrentURI(uri, document->GetChannel(), PR_TRUE);
7200 // This is the end of our CreateContentViewer() replacement.
7201 // Now we simulate a load. First, we restore the state of the javascript
7202 // window object.
7203 nsCOMPtr<nsPIDOMWindow> privWin =
7204 do_GetInterface(static_cast<nsIInterfaceRequestor*>(this));
7205 NS_ASSERTION(privWin, "could not get nsPIDOMWindow interface");
7207 rv = privWin->RestoreWindowState(windowState);
7208 NS_ENSURE_SUCCESS(rv, rv);
7210 // Now, dispatch a title change event which would happen as the
7211 // <head> is parsed.
7212 document->NotifyPossibleTitleChange(PR_FALSE);
7214 // Now we simulate appending child docshells for subframes.
7215 for (i = 0; i < childShells.Count(); ++i) {
7216 nsIDocShellTreeItem *childItem = childShells.ObjectAt(i);
7217 nsCOMPtr<nsIDocShell> childShell = do_QueryInterface(childItem);
7219 // Make sure to not clobber the state of the child. Since AddChild
7220 // always clobbers it, save it off first.
7221 PRBool allowPlugins;
7222 childShell->GetAllowPlugins(&allowPlugins);
7224 PRBool allowJavascript;
7225 childShell->GetAllowJavascript(&allowJavascript);
7227 PRBool allowRedirects;
7228 childShell->GetAllowMetaRedirects(&allowRedirects);
7230 PRBool allowSubframes;
7231 childShell->GetAllowSubframes(&allowSubframes);
7233 PRBool allowImages;
7234 childShell->GetAllowImages(&allowImages);
7236 PRBool allowDNSPrefetch;
7237 childShell->GetAllowDNSPrefetch(&allowDNSPrefetch);
7239 // this.AddChild(child) calls child.SetDocLoaderParent(this), meaning
7240 // that the child inherits our state. Among other things, this means
7241 // that the child inherits our mIsActive, which is what we want.
7242 AddChild(childItem);
7244 childShell->SetAllowPlugins(allowPlugins);
7245 childShell->SetAllowJavascript(allowJavascript);
7246 childShell->SetAllowMetaRedirects(allowRedirects);
7247 childShell->SetAllowSubframes(allowSubframes);
7248 childShell->SetAllowImages(allowImages);
7249 childShell->SetAllowDNSPrefetch(allowDNSPrefetch);
7251 rv = childShell->BeginRestore(nsnull, PR_FALSE);
7252 NS_ENSURE_SUCCESS(rv, rv);
7255 nsCOMPtr<nsIPresShell> shell;
7256 nsDocShell::GetPresShell(getter_AddRefs(shell));
7258 nsIViewManager *newVM = shell ? shell->GetViewManager() : nsnull;
7259 nsIView *newRootView = nsnull;
7260 if (newVM)
7261 newVM->GetRootView(newRootView);
7263 // Insert the new root view at the correct location in the view tree.
7264 if (rootViewParent) {
7265 nsIViewManager *parentVM = rootViewParent->GetViewManager();
7267 if (parentVM && newRootView) {
7268 // InsertChild(parent, child, sib, PR_TRUE) inserts the child after
7269 // sib in content order, which is before sib in view order. BUT
7270 // when sib is null it inserts at the end of the the document
7271 // order, i.e., first in view order. But when oldRootSibling is
7272 // null, the old root as at the end of the view list --- last in
7273 // content order --- and we want to call InsertChild(parent, child,
7274 // nsnull, PR_FALSE) in that case.
7275 parentVM->InsertChild(rootViewParent, newRootView,
7276 rootViewSibling,
7277 rootViewSibling ? PR_TRUE : PR_FALSE);
7279 NS_ASSERTION(newRootView->GetNextSibling() == rootViewSibling,
7280 "error in InsertChild");
7284 // If parent is suspended, increase suspension count.
7285 // This can't be done as early as event suppression since this
7286 // depends on docshell tree.
7287 if (parentSuspendCount) {
7288 privWin->SuspendTimeouts(parentSuspendCount, PR_FALSE);
7291 // Now that all of the child docshells have been put into place, we can
7292 // restart the timers for the window and all of the child frames.
7293 privWin->ResumeTimeouts();
7295 // Restore the refresh URI list. The refresh timers will be restarted
7296 // when EndPageLoad() is called.
7297 mRefreshURIList = refreshURIList;
7299 // Meta-refresh timers have been restarted for this shell, but not
7300 // for our children. Walk the child shells and restart their timers.
7301 PRInt32 n = mChildList.Count();
7302 for (i = 0; i < n; ++i) {
7303 nsCOMPtr<nsIDocShell> child = do_QueryInterface(ChildAt(i));
7304 if (child)
7305 child->ResumeRefreshURIs();
7308 // Make sure this presentation is the same size as the previous
7309 // presentation. If this is not the same size we showed it at last time,
7310 // then we need to resize the widget.
7312 // XXXbryner This interacts poorly with Firefox's infobar. If the old
7313 // presentation had the infobar visible, then we will resize the new
7314 // presentation to that smaller size. However, firing the locationchanged
7315 // event will hide the infobar, which will immediately resize the window
7316 // back to the larger size. A future optimization might be to restore
7317 // the presentation at the "wrong" size, then fire the locationchanged
7318 // event and check whether the docshell's new size is the same as the
7319 // cached viewer size (skipping the resize if they are equal).
7321 if (newRootView) {
7322 if (!newBounds.IsEmpty() && newBounds != oldBounds) {
7323 #ifdef DEBUG_PAGE_CACHE
7324 printf("resize widget(%d, %d, %d, %d)\n", newBounds.x,
7325 newBounds.y, newBounds.width, newBounds.height);
7326 #endif
7327 mContentViewer->SetBounds(newBounds);
7328 } else {
7329 nsIScrollableFrame *rootScrollFrame =
7330 shell->GetRootScrollFrameAsScrollableExternal();
7331 if (rootScrollFrame) {
7332 rootScrollFrame->PostScrolledAreaEventForCurrentArea();
7337 // The FinishRestore call below can kill these, null them out so we don't
7338 // have invalid pointer lying around.
7339 newRootView = rootViewSibling = rootViewParent = nsnull;
7340 newVM = nsnull;
7342 // Simulate the completion of the load.
7343 nsDocShell::FinishRestore();
7345 // Restart plugins, and paint the content.
7346 if (shell) {
7347 shell->Thaw();
7349 newVM = shell->GetViewManager();
7350 if (newVM) {
7351 // When we insert the root view above the resulting invalidate is
7352 // dropped because painting is suppressed in the presshell until we
7353 // call Thaw. So we issue the invalidate here.
7354 newVM->GetRootView(newRootView);
7355 if (newRootView) {
7356 newVM->UpdateView(newRootView, NS_VMREFRESH_NO_SYNC);
7361 return privWin->FireDelayedDOMEvents();
7364 NS_IMETHODIMP
7365 nsDocShell::CreateContentViewer(const char *aContentType,
7366 nsIRequest * request,
7367 nsIStreamListener ** aContentHandler)
7369 *aContentHandler = nsnull;
7371 // Can we check the content type of the current content viewer
7372 // and reuse it without destroying it and re-creating it?
7374 NS_ASSERTION(mLoadGroup, "Someone ignored return from Init()?");
7376 // Instantiate the content viewer object
7377 nsCOMPtr<nsIContentViewer> viewer;
7378 nsresult rv = NewContentViewerObj(aContentType, request, mLoadGroup,
7379 aContentHandler, getter_AddRefs(viewer));
7381 if (NS_FAILED(rv))
7382 return rv;
7384 // Notify the current document that it is about to be unloaded!!
7386 // It is important to fire the unload() notification *before* any state
7387 // is changed within the DocShell - otherwise, javascript will get the
7388 // wrong information :-(
7391 if (mSavingOldViewer) {
7392 // We determined that it was safe to cache the document presentation
7393 // at the time we initiated the new load. We need to check whether
7394 // it's still safe to do so, since there may have been DOM mutations
7395 // or new requests initiated.
7396 nsCOMPtr<nsIDOMDocument> domDoc;
7397 viewer->GetDOMDocument(getter_AddRefs(domDoc));
7398 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
7399 mSavingOldViewer = CanSavePresentation(mLoadType, request, doc);
7402 NS_ASSERTION(!mLoadingURI, "Re-entering unload?");
7404 nsCOMPtr<nsIChannel> aOpenedChannel = do_QueryInterface(request);
7405 if (aOpenedChannel) {
7406 aOpenedChannel->GetURI(getter_AddRefs(mLoadingURI));
7408 FirePageHideNotification(!mSavingOldViewer);
7409 mLoadingURI = nsnull;
7411 // Set mFiredUnloadEvent = PR_FALSE so that the unload handler for the
7412 // *new* document will fire.
7413 mFiredUnloadEvent = PR_FALSE;
7415 // we've created a new document so go ahead and call
7416 // OnLoadingSite(), but don't fire OnLocationChange()
7417 // notifications before we've called Embed(). See bug 284993.
7418 mURIResultedInDocument = PR_TRUE;
7420 if (mLoadType == LOAD_ERROR_PAGE) {
7421 // We need to set the SH entry and our current URI here and not
7422 // at the moment we load the page. We want the same behavior
7423 // of Stop() as for a normal page load. See bug 514232 for details.
7425 // Revert mLoadType to load type to state the page load failed,
7426 // following function calls need it.
7427 mLoadType = mFailedLoadType;
7429 nsCOMPtr<nsIChannel> failedChannel = mFailedChannel;
7430 nsCOMPtr<nsIURI> failedURI = mFailedURI;
7431 mFailedChannel = nsnull;
7432 mFailedURI = nsnull;
7434 // Create an shistory entry for the old load, if we have a channel
7435 if (failedChannel) {
7436 mURIResultedInDocument = PR_TRUE;
7437 OnLoadingSite(failedChannel, PR_TRUE, PR_FALSE);
7438 } else if (failedURI) {
7439 mURIResultedInDocument = PR_TRUE;
7440 OnNewURI(failedURI, nsnull, nsnull, mLoadType, PR_TRUE, PR_FALSE);
7443 // Be sure to have a correct mLSHE, it may have been cleared by
7444 // EndPageLoad. See bug 302115.
7445 if (mSessionHistory && !mLSHE) {
7446 PRInt32 idx;
7447 mSessionHistory->GetRequestedIndex(&idx);
7448 if (idx == -1)
7449 mSessionHistory->GetIndex(&idx);
7451 nsCOMPtr<nsIHistoryEntry> entry;
7452 mSessionHistory->GetEntryAtIndex(idx, PR_FALSE,
7453 getter_AddRefs(entry));
7454 mLSHE = do_QueryInterface(entry);
7457 // Set our current URI
7458 SetCurrentURI(failedURI);
7460 mLoadType = LOAD_ERROR_PAGE;
7463 PRBool onLocationChangeNeeded = OnLoadingSite(aOpenedChannel, PR_FALSE);
7465 // let's try resetting the load group if we need to...
7466 nsCOMPtr<nsILoadGroup> currentLoadGroup;
7467 NS_ENSURE_SUCCESS(aOpenedChannel->
7468 GetLoadGroup(getter_AddRefs(currentLoadGroup)),
7469 NS_ERROR_FAILURE);
7471 if (currentLoadGroup != mLoadGroup) {
7472 nsLoadFlags loadFlags = 0;
7474 //Cancel any URIs that are currently loading...
7475 /// XXX: Need to do this eventually Stop();
7477 // Retarget the document to this loadgroup...
7479 /* First attach the channel to the right loadgroup
7480 * and then remove from the old loadgroup. This
7481 * puts the notifications in the right order and
7482 * we don't null-out mLSHE in OnStateChange() for
7483 * all redirected urls
7485 aOpenedChannel->SetLoadGroup(mLoadGroup);
7487 // Mark the channel as being a document URI...
7488 aOpenedChannel->GetLoadFlags(&loadFlags);
7489 loadFlags |= nsIChannel::LOAD_DOCUMENT_URI;
7491 aOpenedChannel->SetLoadFlags(loadFlags);
7493 mLoadGroup->AddRequest(request, nsnull);
7494 if (currentLoadGroup)
7495 currentLoadGroup->RemoveRequest(request, nsnull,
7496 NS_BINDING_RETARGETED);
7498 // Update the notification callbacks, so that progress and
7499 // status information are sent to the right docshell...
7500 aOpenedChannel->SetNotificationCallbacks(this);
7503 NS_ENSURE_SUCCESS(Embed(viewer, "", (nsISupports *) nsnull),
7504 NS_ERROR_FAILURE);
7506 mSavedRefreshURIList = nsnull;
7507 mSavingOldViewer = PR_FALSE;
7508 mEODForCurrentDocument = PR_FALSE;
7510 // if this document is part of a multipart document,
7511 // the ID can be used to distinguish it from the other parts.
7512 nsCOMPtr<nsIMultiPartChannel> multiPartChannel(do_QueryInterface(request));
7513 if (multiPartChannel) {
7514 nsCOMPtr<nsIPresShell> shell;
7515 rv = GetPresShell(getter_AddRefs(shell));
7516 if (NS_SUCCEEDED(rv) && shell) {
7517 nsIDocument *doc = shell->GetDocument();
7518 if (doc) {
7519 PRUint32 partID;
7520 multiPartChannel->GetPartID(&partID);
7521 doc->SetPartID(partID);
7526 // Give hint to native plevent dispatch mechanism. If a document
7527 // is loading the native plevent dispatch mechanism should favor
7528 // performance over normal native event dispatch priorities.
7529 if (++gNumberOfDocumentsLoading == 1) {
7530 // Hint to favor performance for the plevent notification mechanism.
7531 // We want the pages to load as fast as possible even if its means
7532 // native messages might be starved.
7533 FavorPerformanceHint(PR_TRUE, NS_EVENT_STARVATION_DELAY_HINT);
7536 if (onLocationChangeNeeded) {
7537 FireOnLocationChange(this, request, mCurrentURI);
7540 return NS_OK;
7543 nsresult
7544 nsDocShell::NewContentViewerObj(const char *aContentType,
7545 nsIRequest * request, nsILoadGroup * aLoadGroup,
7546 nsIStreamListener ** aContentHandler,
7547 nsIContentViewer ** aViewer)
7549 nsCOMPtr<nsIChannel> aOpenedChannel = do_QueryInterface(request);
7551 nsCOMPtr<nsIContentUtils> cutils = do_GetService("@mozilla.org/content/contentutils;1");
7552 if (!cutils) {
7553 return NS_ERROR_FAILURE;
7556 nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
7557 cutils->FindInternalContentViewer(aContentType);
7558 if (!docLoaderFactory) {
7559 return NS_ERROR_FAILURE;
7562 // Now create an instance of the content viewer
7563 // nsLayoutDLF makes the determination if it should be a "view-source" instead of "view"
7564 nsresult rv = docLoaderFactory->CreateInstance("view",
7565 aOpenedChannel,
7566 aLoadGroup, aContentType,
7567 static_cast<nsIContentViewerContainer*>(this),
7568 nsnull,
7569 aContentHandler,
7570 aViewer);
7571 NS_ENSURE_SUCCESS(rv, rv);
7573 (*aViewer)->SetContainer(static_cast<nsIContentViewerContainer *>(this));
7574 return NS_OK;
7577 NS_IMETHODIMP
7578 nsDocShell::SetupNewViewer(nsIContentViewer * aNewViewer)
7581 // Copy content viewer state from previous or parent content viewer.
7583 // The following logic is mirrored in nsHTMLDocument::StartDocumentLoad!
7585 // Do NOT to maintain a reference to the old content viewer outside
7586 // of this "copying" block, or it will not be destroyed until the end of
7587 // this routine and all <SCRIPT>s and event handlers fail! (bug 20315)
7589 // In this block of code, if we get an error result, we return it
7590 // but if we get a null pointer, that's perfectly legal for parent
7591 // and parentContentViewer.
7594 PRInt32 x = 0;
7595 PRInt32 y = 0;
7596 PRInt32 cx = 0;
7597 PRInt32 cy = 0;
7599 // This will get the size from the current content viewer or from the
7600 // Init settings
7601 DoGetPositionAndSize(&x, &y, &cx, &cy);
7603 nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
7604 NS_ENSURE_SUCCESS(GetSameTypeParent(getter_AddRefs(parentAsItem)),
7605 NS_ERROR_FAILURE);
7606 nsCOMPtr<nsIDocShell> parent(do_QueryInterface(parentAsItem));
7608 nsCAutoString defaultCharset;
7609 nsCAutoString forceCharset;
7610 nsCAutoString hintCharset;
7611 PRInt32 hintCharsetSource;
7612 nsCAutoString prevDocCharset;
7613 float textZoom;
7614 float pageZoom;
7615 PRBool styleDisabled;
7616 // |newMUDV| also serves as a flag to set the data from the above vars
7617 nsCOMPtr<nsIMarkupDocumentViewer> newMUDV;
7619 if (mContentViewer || parent) {
7620 nsCOMPtr<nsIMarkupDocumentViewer> oldMUDV;
7621 if (mContentViewer) {
7622 // Get any interesting state from old content viewer
7623 // XXX: it would be far better to just reuse the document viewer ,
7624 // since we know we're just displaying the same document as before
7625 oldMUDV = do_QueryInterface(mContentViewer);
7627 // Tell the old content viewer to hibernate in session history when
7628 // it is destroyed.
7630 if (mSavingOldViewer && NS_FAILED(CaptureState())) {
7631 if (mOSHE) {
7632 mOSHE->SyncPresentationState();
7634 mSavingOldViewer = PR_FALSE;
7637 else {
7638 // No old content viewer, so get state from parent's content viewer
7639 nsCOMPtr<nsIContentViewer> parentContentViewer;
7640 parent->GetContentViewer(getter_AddRefs(parentContentViewer));
7641 oldMUDV = do_QueryInterface(parentContentViewer);
7644 if (oldMUDV) {
7645 nsresult rv;
7647 newMUDV = do_QueryInterface(aNewViewer,&rv);
7648 if (newMUDV) {
7649 NS_ENSURE_SUCCESS(oldMUDV->
7650 GetDefaultCharacterSet(defaultCharset),
7651 NS_ERROR_FAILURE);
7652 NS_ENSURE_SUCCESS(oldMUDV->
7653 GetForceCharacterSet(forceCharset),
7654 NS_ERROR_FAILURE);
7655 NS_ENSURE_SUCCESS(oldMUDV->
7656 GetHintCharacterSet(hintCharset),
7657 NS_ERROR_FAILURE);
7658 NS_ENSURE_SUCCESS(oldMUDV->
7659 GetHintCharacterSetSource(&hintCharsetSource),
7660 NS_ERROR_FAILURE);
7661 NS_ENSURE_SUCCESS(oldMUDV->
7662 GetTextZoom(&textZoom),
7663 NS_ERROR_FAILURE);
7664 NS_ENSURE_SUCCESS(oldMUDV->
7665 GetFullZoom(&pageZoom),
7666 NS_ERROR_FAILURE);
7667 NS_ENSURE_SUCCESS(oldMUDV->
7668 GetAuthorStyleDisabled(&styleDisabled),
7669 NS_ERROR_FAILURE);
7670 NS_ENSURE_SUCCESS(oldMUDV->
7671 GetPrevDocCharacterSet(prevDocCharset),
7672 NS_ERROR_FAILURE);
7677 nscolor bgcolor = NS_RGBA(0, 0, 0, 0);
7678 // Ensure that the content viewer is destroyed *after* the GC - bug 71515
7679 nsCOMPtr<nsIContentViewer> kungfuDeathGrip = mContentViewer;
7680 if (mContentViewer) {
7681 // Stop any activity that may be happening in the old document before
7682 // releasing it...
7683 mContentViewer->Stop();
7685 // Try to extract the canvas background color from the old
7686 // presentation shell, so we can use it for the next document.
7687 nsCOMPtr<nsIDocumentViewer> docviewer =
7688 do_QueryInterface(mContentViewer);
7690 if (docviewer) {
7691 nsCOMPtr<nsIPresShell> shell;
7692 docviewer->GetPresShell(getter_AddRefs(shell));
7694 if (shell) {
7695 bgcolor = shell->GetCanvasBackground();
7699 mContentViewer->Close(mSavingOldViewer ? mOSHE.get() : nsnull);
7700 aNewViewer->SetPreviousViewer(mContentViewer);
7702 mContentViewer = nsnull;
7705 // Now that we're about to switch documents, forget all of our children.
7706 // Note that we cached them as needed up in CaptureState above.
7707 DestroyChildren();
7709 mContentViewer = aNewViewer;
7711 nsCOMPtr<nsIWidget> widget;
7712 NS_ENSURE_SUCCESS(GetMainWidget(getter_AddRefs(widget)), NS_ERROR_FAILURE);
7714 nsIntRect bounds(x, y, cx, cy);
7716 if (NS_FAILED(mContentViewer->Init(widget, bounds))) {
7717 mContentViewer = nsnull;
7718 NS_ERROR("ContentViewer Initialization failed");
7719 return NS_ERROR_FAILURE;
7722 // If we have old state to copy, set the old state onto the new content
7723 // viewer
7724 if (newMUDV) {
7725 NS_ENSURE_SUCCESS(newMUDV->SetDefaultCharacterSet(defaultCharset),
7726 NS_ERROR_FAILURE);
7727 NS_ENSURE_SUCCESS(newMUDV->SetForceCharacterSet(forceCharset),
7728 NS_ERROR_FAILURE);
7729 NS_ENSURE_SUCCESS(newMUDV->SetHintCharacterSet(hintCharset),
7730 NS_ERROR_FAILURE);
7731 NS_ENSURE_SUCCESS(newMUDV->
7732 SetHintCharacterSetSource(hintCharsetSource),
7733 NS_ERROR_FAILURE);
7734 NS_ENSURE_SUCCESS(newMUDV->SetPrevDocCharacterSet(prevDocCharset),
7735 NS_ERROR_FAILURE);
7736 NS_ENSURE_SUCCESS(newMUDV->SetTextZoom(textZoom),
7737 NS_ERROR_FAILURE);
7738 NS_ENSURE_SUCCESS(newMUDV->SetFullZoom(pageZoom),
7739 NS_ERROR_FAILURE);
7740 NS_ENSURE_SUCCESS(newMUDV->SetAuthorStyleDisabled(styleDisabled),
7741 NS_ERROR_FAILURE);
7744 // Stuff the bgcolor from the old pres shell into the new
7745 // pres shell. This improves page load continuity.
7746 nsCOMPtr<nsIDocumentViewer> docviewer =
7747 do_QueryInterface(mContentViewer);
7749 if (docviewer) {
7750 nsCOMPtr<nsIPresShell> shell;
7751 docviewer->GetPresShell(getter_AddRefs(shell));
7753 if (shell) {
7754 shell->SetCanvasBackground(bgcolor);
7758 // XXX: It looks like the LayoutState gets restored again in Embed()
7759 // right after the call to SetupNewViewer(...)
7761 // We don't show the mContentViewer yet, since we want to draw the old page
7762 // until we have enough of the new page to show. Just return with the new
7763 // viewer still set to hidden.
7765 return NS_OK;
7768 nsresult
7769 nsDocShell::SetDocCurrentStateObj(nsISHEntry *shEntry)
7771 nsresult rv;
7773 nsCOMPtr<nsIDocument> document = do_GetInterface(GetAsSupports(this));
7774 NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
7776 nsAutoString stateData;
7777 if (shEntry) {
7778 rv = shEntry->GetStateData(stateData);
7779 NS_ENSURE_SUCCESS(rv, rv);
7781 // if shEntry is null, we just set the pending state object to the
7782 // empty string.
7785 document->SetCurrentStateObject(stateData);
7786 return NS_OK;
7789 nsresult
7790 nsDocShell::CheckLoadingPermissions()
7792 // This method checks whether the caller may load content into
7793 // this docshell. Even though we've done our best to hide windows
7794 // from code that doesn't have the right to access them, it's
7795 // still possible for an evil site to open a window and access
7796 // frames in the new window through window.frames[] (which is
7797 // allAccess for historic reasons), so we still need to do this
7798 // check on load.
7799 nsresult rv = NS_OK, sameOrigin = NS_OK;
7801 if (!gValidateOrigin || !IsFrame()) {
7802 // Origin validation was turned off, or we're not a frame.
7803 // Permit all loads.
7805 return rv;
7808 // We're a frame. Check that the caller has write permission to
7809 // the parent before allowing it to load anything into this
7810 // docshell.
7812 nsCOMPtr<nsIScriptSecurityManager> securityManager =
7813 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
7814 NS_ENSURE_SUCCESS(rv, rv);
7816 PRBool ubwEnabled = PR_FALSE;
7817 rv = securityManager->IsCapabilityEnabled("UniversalBrowserWrite",
7818 &ubwEnabled);
7819 if (NS_FAILED(rv) || ubwEnabled) {
7820 return rv;
7823 nsCOMPtr<nsIPrincipal> subjPrincipal;
7824 rv = securityManager->GetSubjectPrincipal(getter_AddRefs(subjPrincipal));
7825 NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && subjPrincipal, rv);
7827 // Check if the caller is from the same origin as this docshell,
7828 // or any of its ancestors.
7829 nsCOMPtr<nsIDocShellTreeItem> item(this);
7830 do {
7831 nsCOMPtr<nsIScriptGlobalObject> sgo(do_GetInterface(item));
7832 nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(sgo));
7834 nsIPrincipal *p;
7835 if (!sop || !(p = sop->GetPrincipal())) {
7836 return NS_ERROR_UNEXPECTED;
7839 // Compare origins
7840 PRBool equal;
7841 sameOrigin = subjPrincipal->Equals(p, &equal);
7842 if (NS_SUCCEEDED(sameOrigin)) {
7843 if (equal) {
7844 // Same origin, permit load
7846 return sameOrigin;
7849 sameOrigin = NS_ERROR_DOM_PROP_ACCESS_DENIED;
7852 nsCOMPtr<nsIDocShellTreeItem> tmp;
7853 item->GetSameTypeParent(getter_AddRefs(tmp));
7854 item.swap(tmp);
7855 } while (item);
7857 return sameOrigin;
7860 //*****************************************************************************
7861 // nsDocShell: Site Loading
7862 //*****************************************************************************
7863 class InternalLoadEvent : public nsRunnable
7865 public:
7866 InternalLoadEvent(nsDocShell* aDocShell, nsIURI * aURI, nsIURI * aReferrer,
7867 nsISupports * aOwner, PRUint32 aFlags,
7868 const char* aTypeHint, nsIInputStream * aPostData,
7869 nsIInputStream * aHeadersData, PRUint32 aLoadType,
7870 nsISHEntry * aSHEntry, PRBool aFirstParty) :
7871 mDocShell(aDocShell),
7872 mURI(aURI),
7873 mReferrer(aReferrer),
7874 mOwner(aOwner),
7875 mPostData(aPostData),
7876 mHeadersData(aHeadersData),
7877 mSHEntry(aSHEntry),
7878 mFlags(aFlags),
7879 mLoadType(aLoadType),
7880 mFirstParty(aFirstParty)
7882 // Make sure to keep null things null as needed
7883 if (aTypeHint) {
7884 mTypeHint = aTypeHint;
7888 NS_IMETHOD Run() {
7889 return mDocShell->InternalLoad(mURI, mReferrer, mOwner, mFlags,
7890 nsnull, mTypeHint.get(),
7891 mPostData, mHeadersData, mLoadType,
7892 mSHEntry, mFirstParty, nsnull, nsnull);
7895 private:
7897 // Use IDL strings so .get() returns null by default
7898 nsXPIDLString mWindowTarget;
7899 nsXPIDLCString mTypeHint;
7901 nsRefPtr<nsDocShell> mDocShell;
7902 nsCOMPtr<nsIURI> mURI;
7903 nsCOMPtr<nsIURI> mReferrer;
7904 nsCOMPtr<nsISupports> mOwner;
7905 nsCOMPtr<nsIInputStream> mPostData;
7906 nsCOMPtr<nsIInputStream> mHeadersData;
7907 nsCOMPtr<nsISHEntry> mSHEntry;
7908 PRUint32 mFlags;
7909 PRUint32 mLoadType;
7910 PRBool mFirstParty;
7913 NS_IMETHODIMP
7914 nsDocShell::InternalLoad(nsIURI * aURI,
7915 nsIURI * aReferrer,
7916 nsISupports * aOwner,
7917 PRUint32 aFlags,
7918 const PRUnichar *aWindowTarget,
7919 const char* aTypeHint,
7920 nsIInputStream * aPostData,
7921 nsIInputStream * aHeadersData,
7922 PRUint32 aLoadType,
7923 nsISHEntry * aSHEntry,
7924 PRBool aFirstParty,
7925 nsIDocShell** aDocShell,
7926 nsIRequest** aRequest)
7928 nsresult rv = NS_OK;
7930 #ifdef PR_LOGGING
7931 if (gDocShellLeakLog && PR_LOG_TEST(gDocShellLeakLog, PR_LOG_DEBUG)) {
7932 nsCAutoString spec;
7933 if (aURI)
7934 aURI->GetSpec(spec);
7935 PR_LogPrint("DOCSHELL %p InternalLoad %s\n", this, spec.get());
7937 #endif
7939 // Initialize aDocShell/aRequest
7940 if (aDocShell) {
7941 *aDocShell = nsnull;
7943 if (aRequest) {
7944 *aRequest = nsnull;
7947 if (!aURI) {
7948 return NS_ERROR_NULL_POINTER;
7951 NS_ENSURE_TRUE(IsValidLoadType(aLoadType), NS_ERROR_INVALID_ARG);
7953 NS_ENSURE_TRUE(!mIsBeingDestroyed, NS_ERROR_NOT_AVAILABLE);
7955 // wyciwyg urls can only be loaded through history. Any normal load of
7956 // wyciwyg through docshell is illegal. Disallow such loads.
7957 if (aLoadType & LOAD_CMD_NORMAL) {
7958 PRBool isWyciwyg = PR_FALSE;
7959 rv = aURI->SchemeIs("wyciwyg", &isWyciwyg);
7960 if ((isWyciwyg && NS_SUCCEEDED(rv)) || NS_FAILED(rv))
7961 return NS_ERROR_FAILURE;
7964 PRBool bIsJavascript = PR_FALSE;
7965 if (NS_FAILED(aURI->SchemeIs("javascript", &bIsJavascript))) {
7966 bIsJavascript = PR_FALSE;
7970 // First, notify any nsIContentPolicy listeners about the document load.
7971 // Only abort the load if a content policy listener explicitly vetos it!
7973 nsCOMPtr<nsIDOMElement> requestingElement;
7974 // Use nsPIDOMWindow since we _want_ to cross the chrome boundary if needed
7975 nsCOMPtr<nsPIDOMWindow> privateWin(do_QueryInterface(mScriptGlobal));
7976 if (privateWin)
7977 requestingElement = privateWin->GetFrameElementInternal();
7979 PRInt16 shouldLoad = nsIContentPolicy::ACCEPT;
7980 PRUint32 contentType;
7981 if (IsFrame()) {
7982 NS_ASSERTION(requestingElement, "A frame but no DOM element!?");
7983 contentType = nsIContentPolicy::TYPE_SUBDOCUMENT;
7984 } else {
7985 contentType = nsIContentPolicy::TYPE_DOCUMENT;
7988 nsISupports* context = requestingElement;
7989 if (!context) {
7990 context = mScriptGlobal;
7993 // XXXbz would be nice to know the loading principal here... but we don't
7994 nsCOMPtr<nsIPrincipal> loadingPrincipal;
7995 if (aReferrer) {
7996 nsCOMPtr<nsIScriptSecurityManager> secMan =
7997 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
7998 NS_ENSURE_SUCCESS(rv, rv);
8000 rv = secMan->GetCodebasePrincipal(aReferrer,
8001 getter_AddRefs(loadingPrincipal));
8004 rv = NS_CheckContentLoadPolicy(contentType,
8005 aURI,
8006 loadingPrincipal,
8007 context,
8008 EmptyCString(), //mime guess
8009 nsnull, //extra
8010 &shouldLoad);
8012 if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
8013 if (NS_SUCCEEDED(rv) && shouldLoad == nsIContentPolicy::REJECT_TYPE) {
8014 return NS_ERROR_CONTENT_BLOCKED_SHOW_ALT;
8017 return NS_ERROR_CONTENT_BLOCKED;
8020 nsCOMPtr<nsISupports> owner(aOwner);
8022 // Get an owner from the current document if necessary. Note that we only
8023 // do this for URIs that inherit a security context and local file URIs;
8024 // in particular we do NOT do this for about:blank. This way, random
8025 // about:blank loads that have no owner (which basically means they were
8026 // done by someone from chrome manually messing with our nsIWebNavigation
8027 // or by C++ setting document.location) don't get a funky principal. If
8028 // callers want something interesting to happen with the about:blank
8029 // principal in this case, they should pass an owner in.
8032 PRBool inherits;
8033 // One more twist: Don't inherit the owner for external loads.
8034 if (aLoadType != LOAD_NORMAL_EXTERNAL && !owner &&
8035 (aFlags & INTERNAL_LOAD_FLAGS_INHERIT_OWNER) &&
8036 NS_SUCCEEDED(URIInheritsSecurityContext(aURI, &inherits)) &&
8037 inherits) {
8039 // Don't allow loads that would inherit our security context
8040 // if this document came from an unsafe channel.
8041 nsCOMPtr<nsIDocShellTreeItem> treeItem = this;
8042 do {
8043 nsCOMPtr<nsIDocShell> itemDocShell =
8044 do_QueryInterface(treeItem);
8045 PRBool isUnsafe;
8046 if (itemDocShell &&
8047 NS_SUCCEEDED(itemDocShell->GetChannelIsUnsafe(&isUnsafe)) &&
8048 isUnsafe) {
8049 return NS_ERROR_DOM_SECURITY_ERR;
8052 nsCOMPtr<nsIDocShellTreeItem> parent;
8053 treeItem->GetSameTypeParent(getter_AddRefs(parent));
8054 parent.swap(treeItem);
8055 } while (treeItem);
8057 owner = GetInheritedPrincipal(PR_TRUE);
8062 // Resolve the window target before going any further...
8063 // If the load has been targeted to another DocShell, then transfer the
8064 // load to it...
8066 if (aWindowTarget && *aWindowTarget) {
8067 // We've already done our owner-inheriting. Mask out that bit, so we
8068 // don't try inheriting an owner from the target window if we came up
8069 // with a null owner above.
8070 aFlags = aFlags & ~INTERNAL_LOAD_FLAGS_INHERIT_OWNER;
8072 // Locate the target DocShell.
8073 // This may involve creating a new toplevel window - if necessary.
8075 nsCOMPtr<nsIDocShellTreeItem> targetItem;
8076 FindItemWithName(aWindowTarget, nsnull, this,
8077 getter_AddRefs(targetItem));
8079 nsCOMPtr<nsIDocShell> targetDocShell = do_QueryInterface(targetItem);
8081 PRBool isNewWindow = PR_FALSE;
8082 if (!targetDocShell) {
8083 nsCOMPtr<nsIDOMWindowInternal> win =
8084 do_GetInterface(GetAsSupports(this));
8085 NS_ENSURE_TRUE(win, NS_ERROR_NOT_AVAILABLE);
8087 nsDependentString name(aWindowTarget);
8088 nsCOMPtr<nsIDOMWindow> newWin;
8089 rv = win->Open(EmptyString(), // URL to load
8090 name, // window name
8091 EmptyString(), // Features
8092 getter_AddRefs(newWin));
8094 // In some cases the Open call doesn't actually result in a new
8095 // window being opened. We can detect these cases by examining the
8096 // document in |newWin|, if any.
8097 nsCOMPtr<nsPIDOMWindow> piNewWin = do_QueryInterface(newWin);
8098 if (piNewWin) {
8099 nsCOMPtr<nsIDocument> newDoc =
8100 do_QueryInterface(piNewWin->GetExtantDocument());
8101 if (!newDoc || newDoc->IsInitialDocument()) {
8102 isNewWindow = PR_TRUE;
8103 aFlags |= INTERNAL_LOAD_FLAGS_FIRST_LOAD;
8107 nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(newWin);
8108 targetDocShell = do_QueryInterface(webNav);
8112 // Transfer the load to the target DocShell... Pass nsnull as the
8113 // window target name from to prevent recursive retargeting!
8115 if (NS_SUCCEEDED(rv) && targetDocShell) {
8116 rv = targetDocShell->InternalLoad(aURI,
8117 aReferrer,
8118 owner,
8119 aFlags,
8120 nsnull, // No window target
8121 aTypeHint,
8122 aPostData,
8123 aHeadersData,
8124 aLoadType,
8125 aSHEntry,
8126 aFirstParty,
8127 aDocShell,
8128 aRequest);
8129 if (rv == NS_ERROR_NO_CONTENT) {
8130 // XXXbz except we never reach this code!
8131 if (isNewWindow) {
8133 // At this point, a new window has been created, but the
8134 // URI did not have any data associated with it...
8136 // So, the best we can do, is to tear down the new window
8137 // that was just created!
8139 nsCOMPtr<nsIDOMWindowInternal> domWin =
8140 do_GetInterface(targetDocShell);
8141 if (domWin) {
8142 domWin->Close();
8146 // NS_ERROR_NO_CONTENT should not be returned to the
8147 // caller... This is an internal error code indicating that
8148 // the URI had no data associated with it - probably a
8149 // helper-app style protocol (ie. mailto://)
8151 rv = NS_OK;
8153 else if (isNewWindow) {
8154 // XXX: Once new windows are created hidden, the new
8155 // window will need to be made visible... For now,
8156 // do nothing.
8160 // Else we ran out of memory, or were a popup and got blocked,
8161 // or something.
8163 return rv;
8167 // Load is being targetted at this docshell so return an error if the
8168 // docshell is in the process of being destroyed.
8170 if (mIsBeingDestroyed) {
8171 return NS_ERROR_FAILURE;
8174 rv = CheckLoadingPermissions();
8175 if (NS_FAILED(rv)) {
8176 return rv;
8179 // If this docshell is owned by a frameloader, make sure to cancel
8180 // possible frameloader initialization before loading a new page.
8181 nsCOMPtr<nsIDocShellTreeItem> parent;
8182 GetParent(getter_AddRefs(parent));
8183 if (parent) {
8184 nsCOMPtr<nsIDocument> doc = do_GetInterface(parent);
8185 if (doc) {
8186 doc->TryCancelFrameLoaderInitialization(this);
8190 if (mFiredUnloadEvent) {
8191 if (IsOKToLoadURI(aURI)) {
8192 NS_PRECONDITION(!aWindowTarget || !*aWindowTarget,
8193 "Shouldn't have a window target here!");
8195 // If this is a replace load, make whatever load triggered
8196 // the unload event also a replace load, so we don't
8197 // create extra history entries.
8198 if (LOAD_TYPE_HAS_FLAGS(aLoadType, LOAD_FLAGS_REPLACE_HISTORY)) {
8199 mLoadType = LOAD_NORMAL_REPLACE;
8202 // Do this asynchronously
8203 nsCOMPtr<nsIRunnable> ev =
8204 new InternalLoadEvent(this, aURI, aReferrer, aOwner, aFlags,
8205 aTypeHint, aPostData, aHeadersData,
8206 aLoadType, aSHEntry, aFirstParty);
8207 return NS_DispatchToCurrentThread(ev);
8210 // Just ignore this load attempt
8211 return NS_OK;
8214 // Before going any further vet loads initiated by external programs.
8215 if (aLoadType == LOAD_NORMAL_EXTERNAL) {
8216 // Disallow external chrome: loads targetted at content windows
8217 PRBool isChrome = PR_FALSE;
8218 if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &isChrome)) && isChrome) {
8219 NS_WARNING("blocked external chrome: url -- use '-chrome' option");
8220 return NS_ERROR_FAILURE;
8223 // clear the decks to prevent context bleed-through (bug 298255)
8224 rv = CreateAboutBlankContentViewer(nsnull, nsnull);
8225 if (NS_FAILED(rv))
8226 return NS_ERROR_FAILURE;
8228 // reset loadType so we don't have to add lots of tests for
8229 // LOAD_NORMAL_EXTERNAL after this point
8230 aLoadType = LOAD_NORMAL;
8233 mAllowKeywordFixup =
8234 (aFlags & INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) != 0;
8235 mURIResultedInDocument = PR_FALSE; // reset the clock...
8237 // If we've gotten this far, reset our "don't fire a popState" flag. This
8238 // will get set to true the next time someone calls push/replaceState.
8239 mSuppressPopstate = PR_FALSE;
8242 // First:
8243 // Check to see if the new URI is an anchor in the existing document.
8244 // Skip this check if we're doing some sort of abnormal load, if the
8245 // new load is a non-history load and has postdata, or if we're doing
8246 // a history load and the page identifiers of mOSHE and aSHEntry
8247 // don't match.
8249 PRBool allowScroll = PR_TRUE;
8250 if (!aSHEntry) {
8251 allowScroll = (aPostData == nsnull);
8252 } else if (mOSHE) {
8253 PRUint32 ourPageIdent;
8254 mOSHE->GetPageIdentifier(&ourPageIdent);
8255 PRUint32 otherPageIdent;
8256 aSHEntry->GetPageIdentifier(&otherPageIdent);
8257 allowScroll = (ourPageIdent == otherPageIdent);
8258 #ifdef DEBUG
8259 if (allowScroll) {
8260 nsCOMPtr<nsIInputStream> currentPostData;
8261 mOSHE->GetPostData(getter_AddRefs(currentPostData));
8262 NS_ASSERTION(currentPostData == aPostData,
8263 "Different POST data for entries for the same page?");
8265 #endif
8268 if (aLoadType == LOAD_NORMAL ||
8269 aLoadType == LOAD_STOP_CONTENT ||
8270 LOAD_TYPE_HAS_FLAGS(aLoadType, LOAD_FLAGS_REPLACE_HISTORY) ||
8271 aLoadType == LOAD_HISTORY ||
8272 aLoadType == LOAD_LINK) {
8274 PRBool wasAnchor = PR_FALSE;
8275 PRBool doHashchange = PR_FALSE;
8277 // Get the position of the scrollers.
8278 nscoord cx = 0, cy = 0;
8279 GetCurScrollPos(ScrollOrientation_X, &cx);
8280 GetCurScrollPos(ScrollOrientation_Y, &cy);
8282 if (allowScroll) {
8283 NS_ENSURE_SUCCESS(ScrollIfAnchor(aURI, &wasAnchor, aLoadType,
8284 &doHashchange),
8285 NS_ERROR_FAILURE);
8288 // If this is a history load, aSHEntry will have document identifier X
8289 // if it was created as a result of a History.pushState() from a
8290 // SHEntry with doc ident X, or if it was created by changing the hash
8291 // of the URI corresponding to a SHEntry with doc ident X.
8292 PRBool sameDocIdent = PR_FALSE;
8293 if (mOSHE && aSHEntry) {
8294 PRUint64 ourDocIdent, otherDocIdent;
8295 mOSHE->GetDocIdentifier(&ourDocIdent);
8296 aSHEntry->GetDocIdentifier(&otherDocIdent);
8297 sameDocIdent = (ourDocIdent == otherDocIdent);
8300 // Do a short-circuited load if the new URI differs from the current
8301 // URI only in the hash, or if the two entries belong to the same
8302 // document and don't point to the same object.
8304 // (If we didn't check that the SHEntries are different objects,
8305 // history.go(0) would short-circuit instead of triggering a true
8306 // load, and we wouldn't dispatch an onload event to the page.)
8307 if (wasAnchor || (sameDocIdent && (mOSHE != aSHEntry))) {
8308 mLoadType = aLoadType;
8309 mURIResultedInDocument = PR_TRUE;
8311 /* we need to assign mLSHE to aSHEntry right here, so that on History loads,
8312 * SetCurrentURI() called from OnNewURI() will send proper
8313 * onLocationChange() notifications to the browser to update
8314 * back/forward buttons.
8316 SetHistoryEntry(&mLSHE, aSHEntry);
8318 /* This is a anchor traversal with in the same page.
8319 * call OnNewURI() so that, this traversal will be
8320 * recorded in session and global history.
8322 nsCOMPtr<nsISupports> owner;
8323 if (mOSHE) {
8324 mOSHE->GetOwner(getter_AddRefs(owner));
8326 OnNewURI(aURI, nsnull, owner, mLoadType, PR_TRUE, PR_TRUE);
8328 nsCOMPtr<nsIInputStream> postData;
8329 PRUint32 pageIdent = PR_UINT32_MAX;
8330 nsCOMPtr<nsISupports> cacheKey;
8332 if (mOSHE) {
8333 /* save current position of scroller(s) (bug 59774) */
8334 mOSHE->SetScrollPosition(cx, cy);
8335 // Get the postdata and page ident from the current page, if
8336 // the new load is being done via normal means. Note that
8337 // "normal means" can be checked for just by checking for
8338 // LOAD_CMD_NORMAL, given the loadType and allowScroll check
8339 // above -- it filters out some LOAD_CMD_NORMAL cases that we
8340 // wouldn't want here.
8341 if (aLoadType & LOAD_CMD_NORMAL) {
8342 mOSHE->GetPostData(getter_AddRefs(postData));
8343 mOSHE->GetPageIdentifier(&pageIdent);
8344 mOSHE->GetCacheKey(getter_AddRefs(cacheKey));
8347 if (mLSHE && wasAnchor) {
8348 // If it's an anchor load, set mLSHE's doc identifier to
8349 // mOSHE's doc identifier -- These are the same documents,
8350 // as far as HTML5 is concerned.
8351 PRUint64 docIdent;
8352 rv = mOSHE->GetDocIdentifier(&docIdent);
8353 if (NS_SUCCEEDED(rv)) {
8354 mLSHE->SetDocIdentifier(docIdent);
8359 /* Assign mOSHE to mLSHE. This will either be a new entry created
8360 * by OnNewURI() for normal loads or aSHEntry for history loads.
8362 if (mLSHE) {
8363 SetHistoryEntry(&mOSHE, mLSHE);
8364 // Save the postData obtained from the previous page
8365 // in to the session history entry created for the
8366 // anchor page, so that any history load of the anchor
8367 // page will restore the appropriate postData.
8368 if (postData)
8369 mOSHE->SetPostData(postData);
8371 // Make sure we won't just repost without hitting the
8372 // cache first
8373 if (cacheKey)
8374 mOSHE->SetCacheKey(cacheKey);
8376 // Propagate our page ident to the new mOSHE so that
8377 // we'll know it just differed by a scroll on the page.
8378 if (pageIdent != PR_UINT32_MAX)
8379 mOSHE->SetPageIdentifier(pageIdent);
8382 /* restore previous position of scroller(s), if we're moving
8383 * back in history (bug 59774)
8385 if (mOSHE && (aLoadType == LOAD_HISTORY || aLoadType == LOAD_RELOAD_NORMAL))
8387 nscoord bx, by;
8388 mOSHE->GetScrollPosition(&bx, &by);
8389 SetCurScrollPosEx(bx, by);
8392 /* Clear out mLSHE so that further anchor visits get
8393 * recorded in SH and SH won't misbehave.
8395 SetHistoryEntry(&mLSHE, nsnull);
8396 /* Set the title for the SH entry for this target url. so that
8397 * SH menus in go/back/forward buttons won't be empty for this.
8399 if (mSessionHistory) {
8400 PRInt32 index = -1;
8401 mSessionHistory->GetIndex(&index);
8402 nsCOMPtr<nsIHistoryEntry> hEntry;
8403 mSessionHistory->GetEntryAtIndex(index, PR_FALSE,
8404 getter_AddRefs(hEntry));
8405 NS_ENSURE_TRUE(hEntry, NS_ERROR_FAILURE);
8406 nsCOMPtr<nsISHEntry> shEntry(do_QueryInterface(hEntry));
8407 if (shEntry)
8408 shEntry->SetTitle(mTitle);
8411 /* Set the title for the Global History entry for this anchor url.
8413 if (mUseGlobalHistory) {
8414 nsCOMPtr<IHistory> history = services::GetHistoryService();
8415 if (history) {
8416 history->SetURITitle(aURI, mTitle);
8418 else if (mGlobalHistory) {
8419 mGlobalHistory->SetPageTitle(aURI, mTitle);
8423 if (sameDocIdent) {
8424 // Set the doc's URI according to the new history entry's URI
8425 nsCOMPtr<nsIURI> newURI;
8426 mOSHE->GetURI(getter_AddRefs(newURI));
8427 NS_ENSURE_TRUE(newURI, NS_ERROR_FAILURE);
8428 nsCOMPtr<nsIDocument> doc =
8429 do_GetInterface(GetAsSupports(this));
8430 NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
8432 doc->SetDocumentURI(newURI);
8435 SetDocCurrentStateObj(mOSHE);
8437 // Dispatch the popstate and hashchange events, as appropriate.
8438 nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mScriptGlobal);
8439 if (window) {
8440 NS_ASSERTION(!mSuppressPopstate,
8441 "Popstate shouldn't be suppressed here.");
8443 // Pass PR_FALSE to indicate that this is not an "initial" (i.e.
8444 // after-onload) popstate.
8445 window->DispatchSyncPopState(PR_FALSE);
8447 if (doHashchange)
8448 window->DispatchAsyncHashchange();
8451 return NS_OK;
8455 // mContentViewer->PermitUnload can destroy |this| docShell, which
8456 // causes the next call of CanSavePresentation to crash.
8457 // Hold onto |this| until we return, to prevent a crash from happening.
8458 // (bug#331040)
8459 nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
8461 // Check if the page doesn't want to be unloaded. The javascript:
8462 // protocol handler deals with this for javascript: URLs.
8463 if (!bIsJavascript && mContentViewer) {
8464 PRBool okToUnload;
8465 rv = mContentViewer->PermitUnload(PR_FALSE, &okToUnload);
8467 if (NS_SUCCEEDED(rv) && !okToUnload) {
8468 // The user chose not to unload the page, interrupt the
8469 // load.
8470 return NS_OK;
8474 // Check for saving the presentation here, before calling Stop().
8475 // This is necessary so that we can catch any pending requests.
8476 // Since the new request has not been created yet, we pass null for the
8477 // new request parameter.
8478 // Also pass nsnull for the document, since it doesn't affect the return
8479 // value for our purposes here.
8480 PRBool savePresentation = CanSavePresentation(aLoadType, nsnull, nsnull);
8482 // Don't stop current network activity for javascript: URL's since
8483 // they might not result in any data, and thus nothing should be
8484 // stopped in those cases. In the case where they do result in
8485 // data, the javascript: URL channel takes care of stopping
8486 // current network activity.
8487 if (!bIsJavascript) {
8488 // Stop any current network activity.
8489 // Also stop content if this is a zombie doc. otherwise
8490 // the onload will be delayed by other loads initiated in the
8491 // background by the first document that
8492 // didn't fully load before the next load was initiated.
8493 // If not a zombie, don't stop content until data
8494 // starts arriving from the new URI...
8496 nsCOMPtr<nsIContentViewer> zombieViewer;
8497 if (mContentViewer) {
8498 mContentViewer->GetPreviousViewer(getter_AddRefs(zombieViewer));
8501 if (zombieViewer ||
8502 LOAD_TYPE_HAS_FLAGS(aLoadType, LOAD_FLAGS_STOP_CONTENT)) {
8503 rv = Stop(nsIWebNavigation::STOP_ALL);
8504 } else {
8505 rv = Stop(nsIWebNavigation::STOP_NETWORK);
8508 if (NS_FAILED(rv))
8509 return rv;
8512 mLoadType = aLoadType;
8514 // mLSHE should be assigned to aSHEntry, only after Stop() has
8515 // been called. But when loading an error page, do not clear the
8516 // mLSHE for the real page.
8517 if (mLoadType != LOAD_ERROR_PAGE)
8518 SetHistoryEntry(&mLSHE, aSHEntry);
8520 mSavingOldViewer = savePresentation;
8522 // If we have a saved content viewer in history, restore and show it now.
8523 if (aSHEntry && (mLoadType & LOAD_CMD_HISTORY)) {
8524 // Make sure our history ID points to the same ID as
8525 // SHEntry's docshell ID.
8526 aSHEntry->GetDocshellID(&mHistoryID);
8528 // It's possible that the previous viewer of mContentViewer is the
8529 // viewer that will end up in aSHEntry when it gets closed. If that's
8530 // the case, we need to go ahead and force it into its shentry so we
8531 // can restore it.
8532 if (mContentViewer) {
8533 nsCOMPtr<nsIContentViewer> prevViewer;
8534 mContentViewer->GetPreviousViewer(getter_AddRefs(prevViewer));
8535 if (prevViewer) {
8536 #ifdef DEBUG
8537 nsCOMPtr<nsIContentViewer> prevPrevViewer;
8538 prevViewer->GetPreviousViewer(getter_AddRefs(prevPrevViewer));
8539 NS_ASSERTION(!prevPrevViewer, "Should never have viewer chain here");
8540 #endif
8541 nsCOMPtr<nsISHEntry> viewerEntry;
8542 prevViewer->GetHistoryEntry(getter_AddRefs(viewerEntry));
8543 if (viewerEntry == aSHEntry) {
8544 // Make sure this viewer ends up in the right place
8545 mContentViewer->SetPreviousViewer(nsnull);
8546 prevViewer->Destroy();
8550 nsCOMPtr<nsISHEntry> oldEntry = mOSHE;
8551 PRBool restoring;
8552 rv = RestorePresentation(aSHEntry, &restoring);
8553 if (restoring)
8554 return rv;
8556 // We failed to restore the presentation, so clean up.
8557 // Both the old and new history entries could potentially be in
8558 // an inconsistent state.
8559 if (NS_FAILED(rv)) {
8560 if (oldEntry)
8561 oldEntry->SyncPresentationState();
8563 aSHEntry->SyncPresentationState();
8567 nsCOMPtr<nsIRequest> req;
8568 rv = DoURILoad(aURI, aReferrer,
8569 !(aFlags & INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER),
8570 owner, aTypeHint, aPostData, aHeadersData, aFirstParty,
8571 aDocShell, getter_AddRefs(req),
8572 (aFlags & INTERNAL_LOAD_FLAGS_FIRST_LOAD) != 0,
8573 (aFlags & INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER) != 0,
8574 (aFlags & INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES) != 0);
8575 if (req && aRequest)
8576 NS_ADDREF(*aRequest = req);
8578 if (NS_FAILED(rv)) {
8579 nsCOMPtr<nsIChannel> chan(do_QueryInterface(req));
8580 DisplayLoadError(rv, aURI, nsnull, chan);
8583 return rv;
8586 nsIPrincipal*
8587 nsDocShell::GetInheritedPrincipal(PRBool aConsiderCurrentDocument)
8589 nsCOMPtr<nsIDocument> document;
8591 if (aConsiderCurrentDocument && mContentViewer) {
8592 document = mContentViewer->GetDocument();
8595 if (!document) {
8596 nsCOMPtr<nsIDocShellTreeItem> parentItem;
8597 GetSameTypeParent(getter_AddRefs(parentItem));
8598 if (parentItem) {
8599 document = do_GetInterface(parentItem);
8603 if (!document) {
8604 if (!aConsiderCurrentDocument) {
8605 return nsnull;
8608 // Make sure we end up with _something_ as the principal no matter
8609 // what.
8610 EnsureContentViewer(); // If this fails, we'll just get a null
8611 // docViewer and bail.
8613 if (!mContentViewer)
8614 return nsnull;
8615 document = mContentViewer->GetDocument();
8618 //-- Get the document's principal
8619 if (document) {
8620 return document->NodePrincipal();
8623 return nsnull;
8626 PRBool
8627 nsDocShell::ShouldCheckAppCache(nsIURI *aURI)
8629 nsCOMPtr<nsIOfflineCacheUpdateService> offlineService =
8630 do_GetService(NS_OFFLINECACHEUPDATESERVICE_CONTRACTID);
8631 if (!offlineService) {
8632 return PR_FALSE;
8635 PRBool allowed;
8636 nsresult rv = offlineService->OfflineAppAllowedForURI(aURI,
8637 nsnull,
8638 &allowed);
8639 return NS_SUCCEEDED(rv) && allowed;
8642 nsresult
8643 nsDocShell::DoURILoad(nsIURI * aURI,
8644 nsIURI * aReferrerURI,
8645 PRBool aSendReferrer,
8646 nsISupports * aOwner,
8647 const char * aTypeHint,
8648 nsIInputStream * aPostData,
8649 nsIInputStream * aHeadersData,
8650 PRBool aFirstParty,
8651 nsIDocShell ** aDocShell,
8652 nsIRequest ** aRequest,
8653 PRBool aIsNewWindowTarget,
8654 PRBool aBypassClassifier,
8655 PRBool aForceAllowCookies)
8657 nsresult rv;
8658 nsCOMPtr<nsIURILoader> uriLoader;
8660 uriLoader = do_GetService(NS_URI_LOADER_CONTRACTID, &rv);
8661 if (NS_FAILED(rv)) return rv;
8663 nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL;
8664 if (aFirstParty) {
8665 // tag first party URL loads
8666 loadFlags |= nsIChannel::LOAD_INITIAL_DOCUMENT_URI;
8669 if (mLoadType == LOAD_ERROR_PAGE) {
8670 // Error pages are LOAD_BACKGROUND
8671 loadFlags |= nsIChannel::LOAD_BACKGROUND;
8674 // check for Content Security Policy to pass along with the
8675 // new channel we are creating
8676 nsCOMPtr<nsIChannelPolicy> channelPolicy;
8677 if (IsFrame()) {
8678 // check the parent docshell for a CSP
8679 nsCOMPtr<nsIContentSecurityPolicy> csp;
8680 nsCOMPtr<nsIDocShellTreeItem> parentItem;
8681 GetSameTypeParent(getter_AddRefs(parentItem));
8682 nsCOMPtr<nsIDocument> doc = do_GetInterface(parentItem);
8683 if (doc) {
8684 rv = doc->NodePrincipal()->GetCsp(getter_AddRefs(csp));
8685 NS_ENSURE_SUCCESS(rv, rv);
8686 if (csp) {
8687 channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
8688 channelPolicy->SetContentSecurityPolicy(csp);
8689 channelPolicy->SetLoadType(nsIContentPolicy::TYPE_SUBDOCUMENT);
8694 // open a channel for the url
8695 nsCOMPtr<nsIChannel> channel;
8697 rv = NS_NewChannel(getter_AddRefs(channel),
8698 aURI,
8699 nsnull,
8700 nsnull,
8701 static_cast<nsIInterfaceRequestor *>(this),
8702 loadFlags,
8703 channelPolicy);
8704 if (NS_FAILED(rv)) {
8705 if (rv == NS_ERROR_UNKNOWN_PROTOCOL) {
8706 // This is a uri with a protocol scheme we don't know how
8707 // to handle. Embedders might still be interested in
8708 // handling the load, though, so we fire a notification
8709 // before throwing the load away.
8710 PRBool abort = PR_FALSE;
8711 nsresult rv2 = mContentListener->OnStartURIOpen(aURI, &abort);
8712 if (NS_SUCCEEDED(rv2) && abort) {
8713 // Hey, they're handling the load for us! How convenient!
8714 return NS_OK;
8718 return rv;
8721 nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
8722 do_QueryInterface(channel);
8723 if (appCacheChannel) {
8724 // Any document load should not inherit application cache.
8725 appCacheChannel->SetInheritApplicationCache(PR_FALSE);
8727 // Loads with the correct permissions should check for a matching
8728 // application cache.
8729 #ifdef MOZ_IPC
8730 // Permission will be checked in the parent process
8731 if (GeckoProcessType_Default != XRE_GetProcessType())
8732 appCacheChannel->SetChooseApplicationCache(PR_TRUE);
8733 else
8734 #endif
8735 appCacheChannel->SetChooseApplicationCache(
8736 ShouldCheckAppCache(aURI));
8739 // Make sure to give the caller a channel if we managed to create one
8740 // This is important for correct error page/session history interaction
8741 if (aRequest)
8742 NS_ADDREF(*aRequest = channel);
8744 channel->SetOriginalURI(aURI);
8745 if (aTypeHint && *aTypeHint) {
8746 channel->SetContentType(nsDependentCString(aTypeHint));
8747 mContentTypeHint = aTypeHint;
8749 else {
8750 mContentTypeHint.Truncate();
8753 //hack
8754 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
8755 nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal(do_QueryInterface(channel));
8756 if (httpChannelInternal) {
8757 if (aForceAllowCookies) {
8758 httpChannelInternal->SetForceAllowThirdPartyCookie(PR_TRUE);
8760 if (aFirstParty) {
8761 httpChannelInternal->SetDocumentURI(aURI);
8762 } else {
8763 httpChannelInternal->SetDocumentURI(aReferrerURI);
8767 nsCOMPtr<nsIWritablePropertyBag2> props(do_QueryInterface(channel));
8768 if (props)
8770 // save true referrer for those who need it (e.g. xpinstall whitelisting)
8771 // Currently only http and ftp channels support this.
8772 props->SetPropertyAsInterface(NS_LITERAL_STRING("docshell.internalReferrer"),
8773 aReferrerURI);
8777 // If this is a HTTP channel, then set up the HTTP specific information
8778 // (ie. POST data, referrer, ...)
8780 if (httpChannel) {
8781 nsCOMPtr<nsICachingChannel> cacheChannel(do_QueryInterface(httpChannel));
8782 /* Get the cache Key from SH */
8783 nsCOMPtr<nsISupports> cacheKey;
8784 if (mLSHE) {
8785 mLSHE->GetCacheKey(getter_AddRefs(cacheKey));
8787 else if (mOSHE) // for reload cases
8788 mOSHE->GetCacheKey(getter_AddRefs(cacheKey));
8790 // figure out if we need to set the post data stream on the channel...
8791 // right now, this is only done for http channels.....
8792 if (aPostData) {
8793 // XXX it's a bit of a hack to rewind the postdata stream here but
8794 // it has to be done in case the post data is being reused multiple
8795 // times.
8796 nsCOMPtr<nsISeekableStream>
8797 postDataSeekable(do_QueryInterface(aPostData));
8798 if (postDataSeekable) {
8799 rv = postDataSeekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
8800 NS_ENSURE_SUCCESS(rv, rv);
8803 nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
8804 NS_ASSERTION(uploadChannel, "http must support nsIUploadChannel");
8806 // we really need to have a content type associated with this stream!!
8807 uploadChannel->SetUploadStream(aPostData, EmptyCString(), -1);
8808 /* If there is a valid postdata *and* it is a History Load,
8809 * set up the cache key on the channel, to retrieve the
8810 * data *only* from the cache. If it is a normal reload, the
8811 * cache is free to go to the server for updated postdata.
8813 if (cacheChannel && cacheKey) {
8814 if (mLoadType == LOAD_HISTORY || mLoadType == LOAD_RELOAD_CHARSET_CHANGE) {
8815 cacheChannel->SetCacheKey(cacheKey);
8816 PRUint32 loadFlags;
8817 if (NS_SUCCEEDED(channel->GetLoadFlags(&loadFlags)))
8818 channel->SetLoadFlags(loadFlags | nsICachingChannel::LOAD_ONLY_FROM_CACHE);
8820 else if (mLoadType == LOAD_RELOAD_NORMAL)
8821 cacheChannel->SetCacheKey(cacheKey);
8824 else {
8825 /* If there is no postdata, set the cache key on the channel, and
8826 * do not set the LOAD_ONLY_FROM_CACHE flag, so that the channel
8827 * will be free to get it from net if it is not found in cache.
8828 * New cache may use it creatively on CGI pages with GET
8829 * method and even on those that say "no-cache"
8831 if (mLoadType == LOAD_HISTORY || mLoadType == LOAD_RELOAD_NORMAL
8832 || mLoadType == LOAD_RELOAD_CHARSET_CHANGE) {
8833 if (cacheChannel && cacheKey)
8834 cacheChannel->SetCacheKey(cacheKey);
8837 if (aHeadersData) {
8838 rv = AddHeadersToChannel(aHeadersData, httpChannel);
8840 // Set the referrer explicitly
8841 if (aReferrerURI && aSendReferrer) {
8842 // Referrer is currenly only set for link clicks here.
8843 httpChannel->SetReferrer(aReferrerURI);
8847 // Set the owner of the channel, but only for channels that can't
8848 // provide their own security context.
8850 // XXX: Is seems wrong that the owner is ignored - even if one is
8851 // supplied) unless the URI is javascript or data or about:blank.
8852 // XXX: If this is ever changed, check all callers for what owners they're
8853 // passing in. In particular, see the code and comments in LoadURI
8854 // where we fall back on inheriting the owner if called
8855 // from chrome. That would be very wrong if this code changed
8856 // anything but channels that can't provide their own security context!
8858 // (Currently chrome URIs set the owner when they are created!
8859 // So setting a NULL owner would be bad!)
8861 // If this code ever changes, change nsObjectLoadingContent::LoadObject
8862 // accordingly.
8863 PRBool inherit;
8864 // We expect URIInheritsSecurityContext to return success for an
8865 // about:blank URI, so don't call IsAboutBlank() if this call fails.
8866 rv = URIInheritsSecurityContext(aURI, &inherit);
8867 if (NS_SUCCEEDED(rv) && (inherit || IsAboutBlank(aURI))) {
8868 channel->SetOwner(aOwner);
8872 // file: uri special-casing
8874 // If this is a file: load opened from another file: then it may need
8875 // to inherit the owner from the referrer so they can script each other.
8876 // If we don't set the owner explicitly then each file: gets an owner
8877 // based on its own codebase later.
8879 nsCOMPtr<nsIPrincipal> ownerPrincipal(do_QueryInterface(aOwner));
8880 if (URIIsLocalFile(aURI) && ownerPrincipal &&
8881 NS_SUCCEEDED(ownerPrincipal->CheckMayLoad(aURI, PR_FALSE))) {
8882 // One more check here. CheckMayLoad will always return true for the
8883 // system principal, but we do NOT want to inherit in that case.
8884 PRBool isSystem;
8885 nsCOMPtr<nsIScriptSecurityManager> secMan =
8886 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
8887 if (secMan &&
8888 NS_SUCCEEDED(secMan->IsSystemPrincipal(ownerPrincipal,
8889 &isSystem)) &&
8890 !isSystem) {
8891 channel->SetOwner(aOwner);
8895 nsCOMPtr<nsIScriptChannel> scriptChannel = do_QueryInterface(channel);
8896 if (scriptChannel) {
8897 // Allow execution against our context if the principals match
8898 scriptChannel->
8899 SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL);
8902 if (aIsNewWindowTarget) {
8903 nsCOMPtr<nsIWritablePropertyBag2> props = do_QueryInterface(channel);
8904 if (props) {
8905 props->SetPropertyAsBool(
8906 NS_LITERAL_STRING("docshell.newWindowTarget"),
8907 PR_TRUE);
8911 rv = DoChannelLoad(channel, uriLoader, aBypassClassifier);
8914 // If the channel load failed, we failed and nsIWebProgress just ain't
8915 // gonna happen.
8917 if (NS_SUCCEEDED(rv)) {
8918 if (aDocShell) {
8919 *aDocShell = this;
8920 NS_ADDREF(*aDocShell);
8924 return rv;
8927 static NS_METHOD
8928 AppendSegmentToString(nsIInputStream *in,
8929 void *closure,
8930 const char *fromRawSegment,
8931 PRUint32 toOffset,
8932 PRUint32 count,
8933 PRUint32 *writeCount)
8935 // aFromSegment now contains aCount bytes of data.
8937 nsCAutoString *buf = static_cast<nsCAutoString *>(closure);
8938 buf->Append(fromRawSegment, count);
8940 // Indicate that we have consumed all of aFromSegment
8941 *writeCount = count;
8942 return NS_OK;
8945 NS_IMETHODIMP
8946 nsDocShell::AddHeadersToChannel(nsIInputStream *aHeadersData,
8947 nsIChannel *aGenericChannel)
8949 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aGenericChannel);
8950 NS_ENSURE_STATE(httpChannel);
8952 PRUint32 numRead;
8953 nsCAutoString headersString;
8954 nsresult rv = aHeadersData->ReadSegments(AppendSegmentToString,
8955 &headersString,
8956 PR_UINT32_MAX,
8957 &numRead);
8958 NS_ENSURE_SUCCESS(rv, rv);
8960 // used during the manipulation of the String from the InputStream
8961 nsCAutoString headerName;
8962 nsCAutoString headerValue;
8963 PRInt32 crlf;
8964 PRInt32 colon;
8967 // Iterate over the headersString: for each "\r\n" delimited chunk,
8968 // add the value as a header to the nsIHttpChannel
8971 static const char kWhitespace[] = "\b\t\r\n ";
8972 while (PR_TRUE) {
8973 crlf = headersString.Find("\r\n");
8974 if (crlf == kNotFound)
8975 return NS_OK;
8977 const nsCSubstring &oneHeader = StringHead(headersString, crlf);
8979 colon = oneHeader.FindChar(':');
8980 if (colon == kNotFound)
8981 return NS_ERROR_UNEXPECTED;
8983 headerName = StringHead(oneHeader, colon);
8984 headerValue = Substring(oneHeader, colon + 1);
8986 headerName.Trim(kWhitespace);
8987 headerValue.Trim(kWhitespace);
8989 headersString.Cut(0, crlf + 2);
8992 // FINALLY: we can set the header!
8995 rv = httpChannel->SetRequestHeader(headerName, headerValue, PR_TRUE);
8996 NS_ENSURE_SUCCESS(rv, rv);
8999 NS_NOTREACHED("oops");
9000 return NS_ERROR_UNEXPECTED;
9003 nsresult nsDocShell::DoChannelLoad(nsIChannel * aChannel,
9004 nsIURILoader * aURILoader,
9005 PRBool aBypassClassifier)
9007 nsresult rv;
9008 // Mark the channel as being a document URI and allow content sniffing...
9009 nsLoadFlags loadFlags = 0;
9010 (void) aChannel->GetLoadFlags(&loadFlags);
9011 loadFlags |= nsIChannel::LOAD_DOCUMENT_URI |
9012 nsIChannel::LOAD_CALL_CONTENT_SNIFFERS;
9014 // Load attributes depend on load type...
9015 switch (mLoadType) {
9016 case LOAD_HISTORY:
9017 loadFlags |= nsIRequest::VALIDATE_NEVER;
9018 break;
9020 case LOAD_RELOAD_CHARSET_CHANGE:
9021 loadFlags |= nsIRequest::LOAD_FROM_CACHE;
9022 break;
9024 case LOAD_RELOAD_NORMAL:
9025 case LOAD_REFRESH:
9026 loadFlags |= nsIRequest::VALIDATE_ALWAYS;
9027 break;
9029 case LOAD_NORMAL_BYPASS_CACHE:
9030 case LOAD_NORMAL_BYPASS_PROXY:
9031 case LOAD_NORMAL_BYPASS_PROXY_AND_CACHE:
9032 case LOAD_RELOAD_BYPASS_CACHE:
9033 case LOAD_RELOAD_BYPASS_PROXY:
9034 case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
9035 loadFlags |= nsIRequest::LOAD_BYPASS_CACHE;
9036 break;
9038 case LOAD_NORMAL:
9039 case LOAD_LINK:
9040 // Set cache checking flags
9041 PRInt32 prefSetting;
9042 if (NS_SUCCEEDED
9043 (mPrefs->
9044 GetIntPref("browser.cache.check_doc_frequency",
9045 &prefSetting))) {
9046 switch (prefSetting) {
9047 case 0:
9048 loadFlags |= nsIRequest::VALIDATE_ONCE_PER_SESSION;
9049 break;
9050 case 1:
9051 loadFlags |= nsIRequest::VALIDATE_ALWAYS;
9052 break;
9053 case 2:
9054 loadFlags |= nsIRequest::VALIDATE_NEVER;
9055 break;
9058 break;
9061 if (!aBypassClassifier) {
9062 loadFlags |= nsIChannel::LOAD_CLASSIFY_URI;
9065 (void) aChannel->SetLoadFlags(loadFlags);
9067 rv = aURILoader->OpenURI(aChannel,
9068 (mLoadType == LOAD_LINK),
9069 this);
9070 NS_ENSURE_SUCCESS(rv, rv);
9072 return NS_OK;
9075 nsresult
9076 nsDocShell::ScrollIfAnchor(nsIURI * aURI, PRBool * aWasAnchor,
9077 PRUint32 aLoadType, PRBool * aDoHashchange)
9079 NS_ASSERTION(aURI, "null uri arg");
9080 NS_ASSERTION(aWasAnchor, "null anchor arg");
9081 NS_PRECONDITION(aDoHashchange, "null hashchange arg");
9083 if (!aURI || !aWasAnchor) {
9084 return NS_ERROR_FAILURE;
9087 *aWasAnchor = PR_FALSE;
9088 *aDoHashchange = PR_FALSE;
9090 if (!mCurrentURI) {
9091 return NS_OK;
9094 nsCOMPtr<nsIPresShell> shell;
9095 nsresult rv = GetPresShell(getter_AddRefs(shell));
9096 if (NS_FAILED(rv) || !shell) {
9097 // If we failed to get the shell, or if there is no shell,
9098 // nothing left to do here.
9100 return rv;
9103 // NOTE: we assume URIs are absolute for comparison purposes
9105 nsCAutoString currentSpec;
9106 NS_ENSURE_SUCCESS(mCurrentURI->GetSpec(currentSpec),
9107 NS_ERROR_FAILURE);
9109 nsCAutoString newSpec;
9110 NS_ENSURE_SUCCESS(aURI->GetSpec(newSpec), NS_ERROR_FAILURE);
9112 // Search for hash marks in the current URI and the new URI and
9113 // take a copy of everything to the left of the hash for
9114 // comparison.
9116 const char kHash = '#';
9118 // Split the new URI into a left and right part
9119 // (assume we're parsing it out right)
9120 nsACString::const_iterator urlStart, urlEnd, refStart, refEnd;
9121 newSpec.BeginReading(urlStart);
9122 newSpec.EndReading(refEnd);
9124 PRInt32 hashNew = newSpec.FindChar(kHash);
9125 if (hashNew == 0) {
9126 return NS_OK; // Strange URI
9129 if (hashNew > 0) {
9130 // found it
9131 urlEnd = urlStart;
9132 urlEnd.advance(hashNew);
9134 refStart = urlEnd;
9135 ++refStart; // advanced past '#'
9138 else {
9139 // no hash at all
9140 urlEnd = refStart = refEnd;
9142 const nsACString& sNewLeft = Substring(urlStart, urlEnd);
9143 const nsACString& sNewRef = Substring(refStart, refEnd);
9145 // Split the current URI in a left and right part
9146 nsACString::const_iterator currentLeftStart, currentLeftEnd,
9147 currentRefStart, currentRefEnd;
9148 currentSpec.BeginReading(currentLeftStart);
9149 currentSpec.EndReading(currentRefEnd);
9151 PRInt32 hashCurrent = currentSpec.FindChar(kHash);
9152 if (hashCurrent == 0) {
9153 return NS_OK; // Strange URI
9156 if (hashCurrent > 0) {
9157 currentLeftEnd = currentLeftStart;
9158 currentLeftEnd.advance(hashCurrent);
9160 currentRefStart = currentLeftEnd;
9161 ++currentRefStart; // advance past '#'
9163 else {
9164 // no hash at all in currentSpec
9165 currentLeftEnd = currentRefStart = currentRefEnd;
9168 // If we have no new anchor, we do not want to scroll, unless there is a
9169 // current anchor and we are doing a history load. So return if we have no
9170 // new anchor, and there is no current anchor or the load is not a history
9171 // load.
9172 NS_ASSERTION(hashNew != 0 && hashCurrent != 0,
9173 "What happened to the early returns above?");
9174 if (hashNew == kNotFound &&
9175 (hashCurrent == kNotFound || aLoadType != LOAD_HISTORY)) {
9176 return NS_OK;
9179 // Compare the URIs.
9181 // NOTE: this is a case sensitive comparison because some parts of the
9182 // URI are case sensitive, and some are not. i.e. the domain name
9183 // is case insensitive but the the paths are not.
9185 // This means that comparing "http://www.ABC.com/" to "http://www.abc.com/"
9186 // will fail this test.
9188 if (!Substring(currentLeftStart, currentLeftEnd).Equals(sNewLeft)) {
9189 return NS_OK; // URIs not the same
9192 // Now we know we are dealing with an anchor
9193 *aWasAnchor = PR_TRUE;
9195 // We should fire a hashchange event once we're done here if the two hashes
9196 // are different.
9197 *aDoHashchange = !Substring(currentRefStart, currentRefEnd).Equals(sNewRef);
9199 // Both the new and current URIs refer to the same page. We can now
9200 // browse to the hash stored in the new URI.
9202 if (!sNewRef.IsEmpty()) {
9203 // anchor is there, but if it's a load from history,
9204 // we don't have any anchor jumping to do
9205 PRBool scroll = aLoadType != LOAD_HISTORY &&
9206 aLoadType != LOAD_RELOAD_NORMAL;
9208 char *str = ToNewCString(sNewRef);
9209 if (!str) {
9210 return NS_ERROR_OUT_OF_MEMORY;
9213 // nsUnescape modifies the string that is passed into it.
9214 nsUnescape(str);
9216 // We assume that the bytes are in UTF-8, as it says in the
9217 // spec:
9218 // http://www.w3.org/TR/html4/appendix/notes.html#h-B.2.1
9220 // We try the UTF-8 string first, and then try the document's
9221 // charset (see below). If the string is not UTF-8,
9222 // conversion will fail and give us an empty Unicode string.
9223 // In that case, we should just fall through to using the
9224 // page's charset.
9225 rv = NS_ERROR_FAILURE;
9226 NS_ConvertUTF8toUTF16 uStr(str);
9227 if (!uStr.IsEmpty()) {
9228 rv = shell->GoToAnchor(NS_ConvertUTF8toUTF16(str), scroll);
9230 nsMemory::Free(str);
9232 // Above will fail if the anchor name is not UTF-8. Need to
9233 // convert from document charset to unicode.
9234 if (NS_FAILED(rv)) {
9236 // Get a document charset
9237 NS_ENSURE_TRUE(mContentViewer, NS_ERROR_FAILURE);
9238 nsIDocument* doc = mContentViewer->GetDocument();
9239 NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
9240 const nsACString &aCharset = doc->GetDocumentCharacterSet();
9242 nsCOMPtr<nsITextToSubURI> textToSubURI =
9243 do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
9244 NS_ENSURE_SUCCESS(rv, rv);
9246 // Unescape and convert to unicode
9247 nsXPIDLString uStr;
9249 rv = textToSubURI->UnEscapeAndConvert(PromiseFlatCString(aCharset).get(),
9250 PromiseFlatCString(sNewRef).get(),
9251 getter_Copies(uStr));
9252 NS_ENSURE_SUCCESS(rv, rv);
9254 // Ignore return value of GoToAnchor, since it will return an error
9255 // if there is no such anchor in the document, which is actually a
9256 // success condition for us (we want to update the session history
9257 // with the new URI no matter whether we actually scrolled
9258 // somewhere).
9259 shell->GoToAnchor(uStr, scroll);
9262 else {
9264 // Tell the shell it's at an anchor, without scrolling.
9265 shell->GoToAnchor(EmptyString(), PR_FALSE);
9267 // An empty anchor was found, but if it's a load from history,
9268 // we don't have to jump to the top of the page. Scrollbar
9269 // position will be restored by the caller, based on positions
9270 // stored in session history.
9271 if (aLoadType == LOAD_HISTORY || aLoadType == LOAD_RELOAD_NORMAL)
9272 return rv;
9273 //An empty anchor. Scroll to the top of the page.
9274 rv = SetCurScrollPosEx(0, 0);
9277 return rv;
9280 void
9281 nsDocShell::SetupReferrerFromChannel(nsIChannel * aChannel)
9283 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
9284 if (httpChannel) {
9285 nsCOMPtr<nsIURI> referrer;
9286 nsresult rv = httpChannel->GetReferrer(getter_AddRefs(referrer));
9287 if (NS_SUCCEEDED(rv)) {
9288 SetReferrerURI(referrer);
9293 PRBool
9294 nsDocShell::OnNewURI(nsIURI * aURI, nsIChannel * aChannel, nsISupports* aOwner,
9295 PRUint32 aLoadType, PRBool aFireOnLocationChange,
9296 PRBool aAddToGlobalHistory)
9298 NS_PRECONDITION(aURI, "uri is null");
9299 NS_PRECONDITION(!aChannel || !aOwner, "Shouldn't have both set");
9301 #if defined(PR_LOGGING) && defined(DEBUG)
9302 if (PR_LOG_TEST(gDocShellLog, PR_LOG_DEBUG)) {
9303 nsCAutoString spec;
9304 aURI->GetSpec(spec);
9306 nsCAutoString chanName;
9307 if (aChannel)
9308 aChannel->GetName(chanName);
9309 else
9310 chanName.AssignLiteral("<no channel>");
9312 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
9313 ("nsDocShell[%p]::OnNewURI(\"%s\", [%s], 0x%x)\n", this, spec.get(),
9314 chanName.get(), aLoadType));
9316 #endif
9318 PRBool updateHistory = PR_TRUE;
9319 PRBool equalUri = PR_FALSE;
9320 PRBool shAvailable = PR_TRUE;
9322 // Get the post data from the channel
9323 nsCOMPtr<nsIInputStream> inputStream;
9324 if (aChannel) {
9325 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
9327 // Check if the HTTPChannel is hiding under a multiPartChannel
9328 if (!httpChannel) {
9329 GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
9332 if (httpChannel) {
9333 nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
9334 if (uploadChannel) {
9335 uploadChannel->GetUploadStream(getter_AddRefs(inputStream));
9338 // If the response status indicates an error, unlink this session
9339 // history entry from any entries sharing its doc ident.
9340 PRUint32 responseStatus;
9341 nsresult rv = httpChannel->GetResponseStatus(&responseStatus);
9342 if (mLSHE && NS_SUCCEEDED(rv) && responseStatus >= 400) {
9343 mLSHE->SetUniqueDocIdentifier();
9347 /* Create SH Entry (mLSHE) only if there is a SessionHistory object (mSessionHistory) in
9348 * the current frame or in the root docshell
9350 nsCOMPtr<nsISHistory> rootSH = mSessionHistory;
9351 if (!rootSH) {
9352 // Get the handle to SH from the root docshell
9353 GetRootSessionHistory(getter_AddRefs(rootSH));
9354 if (!rootSH)
9355 shAvailable = PR_FALSE;
9356 } // rootSH
9359 // Determine if this type of load should update history.
9360 if (aLoadType == LOAD_BYPASS_HISTORY ||
9361 aLoadType == LOAD_ERROR_PAGE ||
9362 aLoadType & LOAD_CMD_HISTORY ||
9363 aLoadType & LOAD_CMD_RELOAD)
9364 updateHistory = PR_FALSE;
9366 // Check if the url to be loaded is the same as the one already loaded.
9367 if (mCurrentURI)
9368 aURI->Equals(mCurrentURI, &equalUri);
9370 #ifdef DEBUG
9371 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
9372 (" shAvailable=%i updateHistory=%i equalURI=%i\n",
9373 shAvailable, updateHistory, equalUri));
9375 if (shAvailable && mCurrentURI && !mOSHE && aLoadType != LOAD_ERROR_PAGE) {
9376 NS_ASSERTION(IsAboutBlank(mCurrentURI), "no SHEntry for a non-transient viewer?");
9378 #endif
9380 /* If the url to be loaded is the same as the one already there,
9381 * and the original loadType is LOAD_NORMAL, LOAD_LINK, or
9382 * LOAD_STOP_CONTENT, set loadType to LOAD_NORMAL_REPLACE so that
9383 * AddToSessionHistory() won't mess with the current SHEntry and
9384 * if this page has any frame children, it also will be handled
9385 * properly. see bug 83684
9387 * NB: If mOSHE is null but we have a current URI, then it means
9388 * that we must be at the transient about:blank content viewer
9389 * (asserted above) and we should let the normal load continue,
9390 * since there's nothing to replace.
9392 * XXX Hopefully changing the loadType at this time will not hurt
9393 * anywhere. The other way to take care of sequentially repeating
9394 * frameset pages is to add new methods to nsIDocShellTreeItem.
9395 * Hopefully I don't have to do that.
9397 if (equalUri &&
9398 mOSHE &&
9399 (mLoadType == LOAD_NORMAL ||
9400 mLoadType == LOAD_LINK ||
9401 mLoadType == LOAD_STOP_CONTENT) &&
9402 !inputStream)
9404 mLoadType = LOAD_NORMAL_REPLACE;
9407 // If this is a refresh to the currently loaded url, we don't
9408 // have to update session or global history.
9409 if (mLoadType == LOAD_REFRESH && !inputStream && equalUri) {
9410 SetHistoryEntry(&mLSHE, mOSHE);
9413 /* If the user pressed shift-reload, cache will create a new cache key
9414 * for the page. Save the new cacheKey in Session History.
9415 * see bug 90098
9417 if (aChannel &&
9418 (aLoadType == LOAD_RELOAD_BYPASS_CACHE ||
9419 aLoadType == LOAD_RELOAD_BYPASS_PROXY ||
9420 aLoadType == LOAD_RELOAD_BYPASS_PROXY_AND_CACHE)) {
9421 NS_ASSERTION(!updateHistory,
9422 "We shouldn't be updating history for forced reloads!");
9424 nsCOMPtr<nsICachingChannel> cacheChannel(do_QueryInterface(aChannel));
9425 nsCOMPtr<nsISupports> cacheKey;
9426 // Get the Cache Key and store it in SH.
9427 if (cacheChannel)
9428 cacheChannel->GetCacheKey(getter_AddRefs(cacheKey));
9429 // If we already have a loading history entry, store the new cache key
9430 // in it. Otherwise, since we're doing a reload and won't be updating
9431 // our history entry, store the cache key in our current history entry.
9432 if (mLSHE)
9433 mLSHE->SetCacheKey(cacheKey);
9434 else if (mOSHE)
9435 mOSHE->SetCacheKey(cacheKey);
9437 // Since we're force-reloading, clear all the sub frame history.
9438 ClearFrameHistory(mLSHE);
9439 ClearFrameHistory(mOSHE);
9442 if (aLoadType == LOAD_RELOAD_NORMAL) {
9443 nsCOMPtr<nsISHEntry> currentSH;
9444 PRBool oshe = PR_FALSE;
9445 GetCurrentSHEntry(getter_AddRefs(currentSH), &oshe);
9446 PRBool dynamicallyAddedChild = PR_FALSE;
9447 if (currentSH) {
9448 currentSH->HasDynamicallyAddedChild(&dynamicallyAddedChild);
9450 if (dynamicallyAddedChild) {
9451 ClearFrameHistory(currentSH);
9455 if (aLoadType == LOAD_REFRESH) {
9456 ClearFrameHistory(mLSHE);
9457 ClearFrameHistory(mOSHE);
9460 if (updateHistory && shAvailable) {
9461 // Update session history if necessary...
9462 if (!mLSHE && (mItemType == typeContent) && mURIResultedInDocument) {
9463 /* This is a fresh page getting loaded for the first time
9464 *.Create a Entry for it and add it to SH, if this is the
9465 * rootDocShell
9467 (void) AddToSessionHistory(aURI, aChannel, aOwner,
9468 getter_AddRefs(mLSHE));
9471 if (aAddToGlobalHistory) {
9472 // If this is a POST request, we do not want to include this in global
9473 // history.
9474 if (!ChannelIsPost(aChannel)) {
9475 nsCOMPtr<nsIURI> previousURI;
9476 PRUint32 previousFlags = 0;
9477 ExtractLastVisit(aChannel, getter_AddRefs(previousURI),
9478 &previousFlags);
9480 nsCOMPtr<nsIURI> referrer;
9481 // Treat referrer as null if there is an error getting it.
9482 (void)NS_GetReferrerFromChannel(aChannel,
9483 getter_AddRefs(referrer));
9485 AddURIVisit(aURI, referrer, previousURI, previousFlags);
9490 // If this was a history load, update the index in
9491 // SH.
9492 if (rootSH && (mLoadType & LOAD_CMD_HISTORY)) {
9493 nsCOMPtr<nsISHistoryInternal> shInternal(do_QueryInterface(rootSH));
9494 if (shInternal) {
9495 rootSH->GetIndex(&mPreviousTransIndex);
9496 shInternal->UpdateIndex();
9497 rootSH->GetIndex(&mLoadedTransIndex);
9498 #ifdef DEBUG_PAGE_CACHE
9499 printf("Previous index: %d, Loaded index: %d\n\n",
9500 mPreviousTransIndex, mLoadedTransIndex);
9501 #endif
9504 PRBool onLocationChangeNeeded = SetCurrentURI(aURI, aChannel,
9505 aFireOnLocationChange);
9506 // Make sure to store the referrer from the channel, if any
9507 SetupReferrerFromChannel(aChannel);
9508 return onLocationChangeNeeded;
9511 PRBool
9512 nsDocShell::OnLoadingSite(nsIChannel * aChannel, PRBool aFireOnLocationChange,
9513 PRBool aAddToGlobalHistory)
9515 nsCOMPtr<nsIURI> uri;
9516 // If this a redirect, use the final url (uri)
9517 // else use the original url
9519 // Note that this should match what documents do (see nsDocument::Reset).
9520 NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
9521 NS_ENSURE_TRUE(uri, PR_FALSE);
9523 return OnNewURI(uri, aChannel, nsnull, mLoadType, aFireOnLocationChange,
9524 aAddToGlobalHistory);
9528 void
9529 nsDocShell::SetReferrerURI(nsIURI * aURI)
9531 mReferrerURI = aURI; // This assigment addrefs
9534 //*****************************************************************************
9535 // nsDocShell: Session History
9536 //*****************************************************************************
9538 nsresult
9539 nsDocShell::StringifyJSValVariant(nsIVariant *aData, nsAString &aResult)
9541 nsresult rv;
9542 aResult.Truncate();
9544 // First, try to extract a jsval from the variant |aData|. This works only
9545 // if the variant implements GetAsJSVal.
9546 jsval jsData;
9547 rv = aData->GetAsJSVal(&jsData);
9548 NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED);
9550 // Now get the JSContext associated with the current document.
9551 // First get the current document.
9552 nsCOMPtr<nsIDocument> document = do_GetInterface(GetAsSupports(this));
9553 NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
9555 // Get the JSContext from the document, like we do in
9556 // nsContentUtils::GetContextFromDocument().
9557 nsIScriptGlobalObject *sgo = document->GetScopeObject();
9558 NS_ENSURE_TRUE(sgo, NS_ERROR_FAILURE);
9560 nsIScriptContext *scx = sgo->GetContext();
9561 NS_ENSURE_TRUE(scx, NS_ERROR_FAILURE);
9563 JSContext *cx = (JSContext *)scx->GetNativeContext();
9565 // If our json call triggers a JS-to-C++ call, we want that call to use cx
9566 // as the context. So we push cx onto the context stack.
9567 nsCOMPtr<nsIJSContextStack> contextStack =
9568 do_GetService("@mozilla.org/js/xpc/ContextStack;1", &rv);
9569 NS_ENSURE_SUCCESS(rv, rv);
9571 contextStack->Push(cx);
9573 nsCOMPtr<nsIJSON> json = do_GetService("@mozilla.org/dom/json;1");
9574 if(json) {
9575 // Do the encoding
9576 rv = json->EncodeFromJSVal(&jsData, cx, aResult);
9578 else {
9579 rv = NS_ERROR_FAILURE;
9582 // Always pop the stack!
9583 contextStack->Pop(&cx);
9585 return rv;
9588 NS_IMETHODIMP
9589 nsDocShell::AddState(nsIVariant *aData, const nsAString& aTitle,
9590 const nsAString& aURL, PRBool aReplace)
9592 // Implements History.pushState and History.replaceState
9594 // Here's what we do, roughly in the order specified by HTML5:
9595 // 1. Serialize aData to JSON.
9596 // 2. If the third argument is present,
9597 // a. Resolve the url, relative to the first script's base URL
9598 // b. If (a) fails, raise a SECURITY_ERR
9599 // c. Compare the resulting absolute URL to the document's address. If
9600 // any part of the URLs difer other than the <path>, <query>, and
9601 // <fragment> components, raise a SECURITY_ERR and abort.
9602 // 3. If !aReplace:
9603 // Remove from the session history all entries after the current entry,
9604 // as we would after a regular navigation, and save the current
9605 // entry's scroll position (bug 590573).
9606 // 4. As apropriate, either add a state object entry to the session history
9607 // after the current entry with the following properties, or modify the
9608 // current session history entry to set
9609 // a. cloned data as the state object,
9610 // b. if the third argument was present, the absolute URL found in
9611 // step 2
9612 // Also clear the new history entry's POST data (see bug 580069).
9613 // 5. If aReplace is false (i.e. we're doing a pushState instead of a
9614 // replaceState), notify bfcache that we've navigated to a new page.
9615 // 6. If the third argument is present, set the document's current address
9616 // to the absolute URL found in step 2.
9618 // It's important that this function not run arbitrary scripts after step 1
9619 // and before completing step 5. For example, if a script called
9620 // history.back() before we completed step 5, bfcache might destroy an
9621 // active content viewer. Since EvictContentViewers at the end of step 5
9622 // might run script, we can't just put a script blocker around the critical
9623 // section.
9625 // Note that we completely ignore the aTitle parameter.
9627 nsresult rv;
9629 nsCOMPtr<nsIDocument> document = do_GetInterface(GetAsSupports(this));
9630 NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
9632 // Step 1: Clone aData by getting its JSON representation
9633 nsString dataStr;
9634 rv = StringifyJSValVariant(aData, dataStr);
9635 NS_ENSURE_SUCCESS(rv, rv);
9637 // Check that the state object isn't too long.
9638 // Default max length: 640k chars.
9639 PRInt32 maxStateObjSize = 0xA0000;
9640 if (mPrefs) {
9641 mPrefs->GetIntPref("browser.history.maxStateObjectSize",
9642 &maxStateObjSize);
9644 if (maxStateObjSize < 0) {
9645 maxStateObjSize = 0;
9647 NS_ENSURE_TRUE(dataStr.Length() <= (PRUint32)maxStateObjSize,
9648 NS_ERROR_ILLEGAL_VALUE);
9650 // Step 2: Resolve aURL
9651 PRBool equalURIs = PR_TRUE;
9652 nsCOMPtr<nsIURI> oldURI = mCurrentURI;
9653 nsCOMPtr<nsIURI> newURI;
9654 if (aURL.Length() == 0) {
9655 newURI = mCurrentURI;
9657 else {
9658 // 2a: Resolve aURL relative to mURI
9660 nsIURI* docBaseURI = document->GetDocBaseURI();
9661 if (!docBaseURI)
9662 return NS_ERROR_FAILURE;
9664 nsCAutoString spec;
9665 docBaseURI->GetSpec(spec);
9667 nsCAutoString charset;
9668 rv = docBaseURI->GetOriginCharset(charset);
9669 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
9671 rv = NS_NewURI(getter_AddRefs(newURI), aURL,
9672 charset.get(), docBaseURI);
9674 // 2b: If 2a fails, raise a SECURITY_ERR
9675 if (NS_FAILED(rv)) {
9676 return NS_ERROR_DOM_SECURITY_ERR;
9679 // 2c: Same-origin check.
9680 if (!URIIsLocalFile(newURI)) {
9681 // In addition to checking that the security manager says that
9682 // the new URI has the same origin as our current URI, we also
9683 // check that the two URIs have the same userpass. (The
9684 // security manager says that |http://foo.com| and
9685 // |http://me@foo.com| have the same origin.) mCurrentURI
9686 // won't contain the password part of the userpass, so this
9687 // means that it's never valid to specify a password in a
9688 // pushState or replaceState URI.
9690 nsCOMPtr<nsIScriptSecurityManager> secMan =
9691 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
9692 NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE);
9694 // It's very important that we check that newURI is of the same
9695 // origin as mCurrentURI, not docBaseURI, because a page can
9696 // set docBaseURI arbitrarily to any domain.
9697 nsCAutoString currentUserPass, newUserPass;
9698 NS_ENSURE_SUCCESS(mCurrentURI->GetUserPass(currentUserPass),
9699 NS_ERROR_FAILURE);
9700 NS_ENSURE_SUCCESS(newURI->GetUserPass(newUserPass),
9701 NS_ERROR_FAILURE);
9702 if (NS_FAILED(secMan->CheckSameOriginURI(mCurrentURI,
9703 newURI, PR_TRUE)) ||
9704 !currentUserPass.Equals(newUserPass)) {
9706 return NS_ERROR_DOM_SECURITY_ERR;
9709 else {
9710 // It's a file:// URI
9711 nsCOMPtr<nsIScriptObjectPrincipal> docScriptObj =
9712 do_QueryInterface(document);
9714 if (!docScriptObj) {
9715 return NS_ERROR_DOM_SECURITY_ERR;
9718 nsCOMPtr<nsIPrincipal> principal = docScriptObj->GetPrincipal();
9720 if (!principal ||
9721 NS_FAILED(principal->CheckMayLoad(newURI, PR_TRUE))) {
9723 return NS_ERROR_DOM_SECURITY_ERR;
9727 mCurrentURI->Equals(newURI, &equalURIs);
9729 } // end of same-origin check
9731 nsCOMPtr<nsISHistory> sessionHistory = mSessionHistory;
9732 if (!sessionHistory) {
9733 // Get the handle to SH from the root docshell
9734 GetRootSessionHistory(getter_AddRefs(sessionHistory));
9736 NS_ENSURE_TRUE(sessionHistory, NS_ERROR_FAILURE);
9738 nsCOMPtr<nsISHistoryInternal> shInternal =
9739 do_QueryInterface(sessionHistory, &rv);
9740 NS_ENSURE_SUCCESS(rv, rv);
9742 // Step 3: Create a new entry in the session history. This will erase
9743 // all SHEntries after the new entry and make this entry the current
9744 // one. This operation may modify mOSHE, which we need later, so we
9745 // keep a reference here.
9746 NS_ENSURE_TRUE(mOSHE, NS_ERROR_FAILURE);
9747 nsCOMPtr<nsISHEntry> oldOSHE = mOSHE;
9749 mLoadType = LOAD_PUSHSTATE;
9751 nsCOMPtr<nsISHEntry> newSHEntry;
9752 if (!aReplace) {
9753 // Save the current scroll position (bug 590573).
9754 nscoord cx = 0, cy = 0;
9755 GetCurScrollPos(ScrollOrientation_X, &cx);
9756 GetCurScrollPos(ScrollOrientation_Y, &cy);
9757 mOSHE->SetScrollPosition(cx, cy);
9759 rv = AddToSessionHistory(newURI, nsnull, nsnull,
9760 getter_AddRefs(newSHEntry));
9761 NS_ENSURE_SUCCESS(rv, rv);
9763 NS_ENSURE_TRUE(newSHEntry, NS_ERROR_FAILURE);
9765 // Set the new SHEntry's document identifier, if we can.
9766 PRUint64 ourDocIdent;
9767 NS_ENSURE_SUCCESS(oldOSHE->GetDocIdentifier(&ourDocIdent),
9768 NS_ERROR_FAILURE);
9769 NS_ENSURE_SUCCESS(newSHEntry->SetDocIdentifier(ourDocIdent),
9770 NS_ERROR_FAILURE);
9772 // AddToSessionHistory may not modify mOSHE. In case it doesn't,
9773 // we'll just set mOSHE here.
9774 mOSHE = newSHEntry;
9776 } else {
9777 newSHEntry = mOSHE;
9778 newSHEntry->SetURI(newURI);
9781 // Step 4: Modify new/original session history entry and clear its POST
9782 // data, if there is any.
9783 newSHEntry->SetStateData(dataStr);
9784 newSHEntry->SetPostData(nsnull);
9786 // Step 5: If aReplace is false, indicating that we're doing a pushState
9787 // rather than a replaceState, notify bfcache that we've added a page to
9788 // the history so it can evict content viewers if appropriate.
9789 if (!aReplace) {
9790 nsCOMPtr<nsISHistory> rootSH;
9791 GetRootSessionHistory(getter_AddRefs(rootSH));
9792 NS_ENSURE_TRUE(rootSH, NS_ERROR_UNEXPECTED);
9794 nsCOMPtr<nsISHistoryInternal> internalSH =
9795 do_QueryInterface(rootSH);
9796 NS_ENSURE_TRUE(internalSH, NS_ERROR_UNEXPECTED);
9798 PRInt32 curIndex = -1;
9799 rv = rootSH->GetIndex(&curIndex);
9800 if (NS_SUCCEEDED(rv) && curIndex > -1) {
9801 internalSH->EvictContentViewers(curIndex - 1, curIndex);
9805 // Step 6: If the document's URI changed, update document's URI and update
9806 // global history.
9808 // We need to call FireOnLocationChange so that the browser's address bar
9809 // gets updated and the back button is enabled, but we only need to
9810 // explicitly call FireOnLocationChange if we're not calling SetCurrentURI,
9811 // since SetCurrentURI will call FireOnLocationChange for us.
9812 if (!equalURIs) {
9813 SetCurrentURI(newURI, nsnull, PR_TRUE);
9814 document->SetDocumentURI(newURI);
9816 AddURIVisit(newURI, oldURI, oldURI, 0);
9818 else {
9819 FireDummyOnLocationChange();
9822 // A call to push/replaceState prevents popstate events from firing until
9823 // the next time we call InternalLoad.
9824 mSuppressPopstate = PR_TRUE;
9826 return NS_OK;
9829 PRBool
9830 nsDocShell::ShouldAddToSessionHistory(nsIURI * aURI)
9832 // I believe none of the about: urls should go in the history. But then
9833 // that could just be me... If the intent is only deny about:blank then we
9834 // should just do a spec compare, rather than two gets of the scheme and
9835 // then the path. -Gagan
9836 nsresult rv;
9837 nsCAutoString buf;
9839 rv = aURI->GetScheme(buf);
9840 if (NS_FAILED(rv))
9841 return PR_FALSE;
9843 if (buf.Equals("about")) {
9844 rv = aURI->GetPath(buf);
9845 if (NS_FAILED(rv))
9846 return PR_FALSE;
9848 if (buf.Equals("blank")) {
9849 return PR_FALSE;
9852 return PR_TRUE;
9855 nsresult
9856 nsDocShell::AddToSessionHistory(nsIURI * aURI, nsIChannel * aChannel,
9857 nsISupports* aOwner, nsISHEntry ** aNewEntry)
9859 NS_PRECONDITION(aURI, "uri is null");
9860 NS_PRECONDITION(!aChannel || !aOwner, "Shouldn't have both set");
9862 #if defined(PR_LOGGING) && defined(DEBUG)
9863 if (PR_LOG_TEST(gDocShellLog, PR_LOG_DEBUG)) {
9864 nsCAutoString spec;
9865 aURI->GetSpec(spec);
9867 nsCAutoString chanName;
9868 if (aChannel)
9869 aChannel->GetName(chanName);
9870 else
9871 chanName.AssignLiteral("<no channel>");
9873 PR_LOG(gDocShellLog, PR_LOG_DEBUG,
9874 ("nsDocShell[%p]::AddToSessionHistory(\"%s\", [%s])\n", this, spec.get(),
9875 chanName.get()));
9877 #endif
9879 nsresult rv = NS_OK;
9880 nsCOMPtr<nsISHEntry> entry;
9881 PRBool shouldPersist;
9883 shouldPersist = ShouldAddToSessionHistory(aURI);
9885 // Get a handle to the root docshell
9886 nsCOMPtr<nsIDocShellTreeItem> root;
9887 GetSameTypeRootTreeItem(getter_AddRefs(root));
9889 * If this is a LOAD_FLAGS_REPLACE_HISTORY in a subframe, we use
9890 * the existing SH entry in the page and replace the url and
9891 * other vitalities.
9893 if (LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY) &&
9894 root != static_cast<nsIDocShellTreeItem *>(this)) {
9895 // This is a subframe
9896 entry = mOSHE;
9897 nsCOMPtr<nsISHContainer> shContainer(do_QueryInterface(entry));
9898 if (shContainer) {
9899 PRInt32 childCount = 0;
9900 shContainer->GetChildCount(&childCount);
9901 // Remove all children of this entry
9902 for (PRInt32 i = childCount - 1; i >= 0; i--) {
9903 nsCOMPtr<nsISHEntry> child;
9904 shContainer->GetChildAt(i, getter_AddRefs(child));
9905 shContainer->RemoveChild(child);
9906 } // for
9907 } // shContainer
9910 // Create a new entry if necessary.
9911 if (!entry) {
9912 entry = do_CreateInstance(NS_SHENTRY_CONTRACTID);
9914 if (!entry) {
9915 return NS_ERROR_OUT_OF_MEMORY;
9919 // Get the post data & referrer
9920 nsCOMPtr<nsIInputStream> inputStream;
9921 nsCOMPtr<nsIURI> referrerURI;
9922 nsCOMPtr<nsISupports> cacheKey;
9923 nsCOMPtr<nsISupports> owner = aOwner;
9924 PRBool expired = PR_FALSE;
9925 PRBool discardLayoutState = PR_FALSE;
9926 nsCOMPtr<nsICachingChannel> cacheChannel;
9927 if (aChannel) {
9928 cacheChannel = do_QueryInterface(aChannel);
9930 /* If there is a caching channel, get the Cache Key and store it
9931 * in SH.
9933 if (cacheChannel) {
9934 cacheChannel->GetCacheKey(getter_AddRefs(cacheKey));
9936 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
9938 // Check if the httpChannel is hiding under a multipartChannel
9939 if (!httpChannel) {
9940 GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
9942 if (httpChannel) {
9943 nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
9944 if (uploadChannel) {
9945 uploadChannel->GetUploadStream(getter_AddRefs(inputStream));
9947 httpChannel->GetReferrer(getter_AddRefs(referrerURI));
9949 discardLayoutState = ShouldDiscardLayoutState(httpChannel);
9951 aChannel->GetOwner(getter_AddRefs(owner));
9954 //Title is set in nsDocShell::SetTitle()
9955 entry->Create(aURI, // uri
9956 EmptyString(), // Title
9957 inputStream, // Post data stream
9958 nsnull, // LayoutHistory state
9959 cacheKey, // CacheKey
9960 mContentTypeHint, // Content-type
9961 owner, // Channel or provided owner
9962 mHistoryID,
9963 mDynamicallyCreated);
9964 entry->SetReferrerURI(referrerURI);
9965 /* If cache got a 'no-store', ask SH not to store
9966 * HistoryLayoutState. By default, SH will set this
9967 * flag to PR_TRUE and save HistoryLayoutState.
9969 if (discardLayoutState) {
9970 entry->SetSaveLayoutStateFlag(PR_FALSE);
9972 if (cacheChannel) {
9973 // Check if the page has expired from cache
9974 PRUint32 expTime = 0;
9975 cacheChannel->GetCacheTokenExpirationTime(&expTime);
9976 PRUint32 now = PRTimeToSeconds(PR_Now());
9977 if (expTime <= now)
9978 expired = PR_TRUE;
9980 if (expired)
9981 entry->SetExpirationStatus(PR_TRUE);
9984 if (root == static_cast<nsIDocShellTreeItem *>(this) && mSessionHistory) {
9985 // Bug 629559: Detect if this is an anchor navigation and clone the
9986 // session history in that case too
9987 if (mLoadType == LOAD_PUSHSTATE && mOSHE) {
9988 PRUint32 cloneID;
9989 mOSHE->GetID(&cloneID);
9990 nsCOMPtr<nsISHEntry> newEntry;
9991 CloneAndReplace(mOSHE, this, cloneID, entry, PR_TRUE, getter_AddRefs(newEntry));
9992 NS_ASSERTION(entry == newEntry, "The new session history should be in the new entry");
9995 // This is the root docshell
9996 if (LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY)) {
9997 // Replace current entry in session history.
9998 PRInt32 index = 0;
9999 mSessionHistory->GetIndex(&index);
10000 nsCOMPtr<nsISHistoryInternal> shPrivate(do_QueryInterface(mSessionHistory));
10001 // Replace the current entry with the new entry
10002 if (shPrivate)
10003 rv = shPrivate->ReplaceEntry(index, entry);
10005 else {
10006 // Add to session history
10007 nsCOMPtr<nsISHistoryInternal>
10008 shPrivate(do_QueryInterface(mSessionHistory));
10009 NS_ENSURE_TRUE(shPrivate, NS_ERROR_FAILURE);
10010 mSessionHistory->GetIndex(&mPreviousTransIndex);
10011 rv = shPrivate->AddEntry(entry, shouldPersist);
10012 mSessionHistory->GetIndex(&mLoadedTransIndex);
10013 #ifdef DEBUG_PAGE_CACHE
10014 printf("Previous index: %d, Loaded index: %d\n\n",
10015 mPreviousTransIndex, mLoadedTransIndex);
10016 #endif
10019 else {
10020 // This is a subframe.
10021 if (!mOSHE || !LOAD_TYPE_HAS_FLAGS(mLoadType,
10022 LOAD_FLAGS_REPLACE_HISTORY))
10023 rv = DoAddChildSHEntry(entry, mChildOffset);
10026 // Return the new SH entry...
10027 if (aNewEntry) {
10028 *aNewEntry = nsnull;
10029 if (NS_SUCCEEDED(rv)) {
10030 *aNewEntry = entry;
10031 NS_ADDREF(*aNewEntry);
10035 return rv;
10039 NS_IMETHODIMP
10040 nsDocShell::LoadHistoryEntry(nsISHEntry * aEntry, PRUint32 aLoadType)
10042 if (!IsNavigationAllowed()) {
10043 return NS_OK;
10046 nsCOMPtr<nsIURI> uri;
10047 nsCOMPtr<nsIInputStream> postData;
10048 nsCOMPtr<nsIURI> referrerURI;
10049 nsCAutoString contentType;
10050 nsCOMPtr<nsISupports> owner;
10052 NS_ENSURE_TRUE(aEntry, NS_ERROR_FAILURE);
10054 NS_ENSURE_SUCCESS(aEntry->GetURI(getter_AddRefs(uri)), NS_ERROR_FAILURE);
10055 NS_ENSURE_SUCCESS(aEntry->GetReferrerURI(getter_AddRefs(referrerURI)),
10056 NS_ERROR_FAILURE);
10057 NS_ENSURE_SUCCESS(aEntry->GetPostData(getter_AddRefs(postData)),
10058 NS_ERROR_FAILURE);
10059 NS_ENSURE_SUCCESS(aEntry->GetContentType(contentType), NS_ERROR_FAILURE);
10060 NS_ENSURE_SUCCESS(aEntry->GetOwner(getter_AddRefs(owner)),
10061 NS_ERROR_FAILURE);
10063 // Calling CreateAboutBlankContentViewer can set mOSHE to null, and if
10064 // that's the only thing holding a ref to aEntry that will cause aEntry to
10065 // die while we're loading it. So hold a strong ref to aEntry here, just
10066 // in case.
10067 nsCOMPtr<nsISHEntry> kungFuDeathGrip(aEntry);
10068 PRBool isJS;
10069 nsresult rv = uri->SchemeIs("javascript", &isJS);
10070 if (NS_FAILED(rv) || isJS) {
10071 // We're loading a URL that will execute script from inside asyncOpen.
10072 // Replace the current document with about:blank now to prevent
10073 // anything from the current document from leaking into any JavaScript
10074 // code in the URL.
10075 nsCOMPtr<nsIPrincipal> prin = do_QueryInterface(owner);
10076 // Don't cache the presentation if we're going to just reload the
10077 // current entry. Caching would lead to trying to save the different
10078 // content viewers in the same nsISHEntry object.
10079 rv = CreateAboutBlankContentViewer(prin, nsnull, aEntry != mOSHE);
10081 if (NS_FAILED(rv)) {
10082 // The creation of the intermittent about:blank content
10083 // viewer failed for some reason (potentially because the
10084 // user prevented it). Interrupt the history load.
10085 return NS_OK;
10088 if (!owner) {
10089 // Ensure that we have an owner. Otherwise javascript: URIs will
10090 // pick it up from the about:blank page we just loaded, and we
10091 // don't really want even that in this case.
10092 owner = do_CreateInstance("@mozilla.org/nullprincipal;1");
10093 NS_ENSURE_TRUE(owner, NS_ERROR_OUT_OF_MEMORY);
10097 /* If there is a valid postdata *and* the user pressed
10098 * reload or shift-reload, take user's permission before we
10099 * repost the data to the server.
10101 if ((aLoadType & LOAD_CMD_RELOAD) && postData) {
10102 PRBool repost;
10103 rv = ConfirmRepost(&repost);
10104 if (NS_FAILED(rv)) return rv;
10106 // If the user pressed cancel in the dialog, return. We're done here.
10107 if (!repost)
10108 return NS_BINDING_ABORTED;
10111 rv = InternalLoad(uri,
10112 referrerURI,
10113 owner,
10114 INTERNAL_LOAD_FLAGS_NONE, // Do not inherit owner from document (security-critical!)
10115 nsnull, // No window target
10116 contentType.get(), // Type hint
10117 postData, // Post data stream
10118 nsnull, // No headers stream
10119 aLoadType, // Load type
10120 aEntry, // SHEntry
10121 PR_TRUE,
10122 nsnull, // No nsIDocShell
10123 nsnull); // No nsIRequest
10124 return rv;
10127 NS_IMETHODIMP nsDocShell::GetShouldSaveLayoutState(PRBool* aShould)
10129 *aShould = PR_FALSE;
10130 if (mOSHE) {
10131 // Don't capture historystate and save it in history
10132 // if the page asked not to do so.
10133 mOSHE->GetSaveLayoutStateFlag(aShould);
10136 return NS_OK;
10139 NS_IMETHODIMP nsDocShell::PersistLayoutHistoryState()
10141 nsresult rv = NS_OK;
10143 if (mOSHE) {
10144 nsCOMPtr<nsIPresShell> shell;
10145 rv = GetPresShell(getter_AddRefs(shell));
10146 if (NS_SUCCEEDED(rv) && shell) {
10147 nsCOMPtr<nsILayoutHistoryState> layoutState;
10148 rv = shell->CaptureHistoryState(getter_AddRefs(layoutState),
10149 PR_TRUE);
10153 return rv;
10156 /* static */ nsresult
10157 nsDocShell::WalkHistoryEntries(nsISHEntry *aRootEntry,
10158 nsDocShell *aRootShell,
10159 WalkHistoryEntriesFunc aCallback,
10160 void *aData)
10162 NS_ENSURE_TRUE(aRootEntry, NS_ERROR_FAILURE);
10164 nsCOMPtr<nsISHContainer> container(do_QueryInterface(aRootEntry));
10165 if (!container)
10166 return NS_ERROR_FAILURE;
10168 PRInt32 childCount;
10169 container->GetChildCount(&childCount);
10170 for (PRInt32 i = 0; i < childCount; i++) {
10171 nsCOMPtr<nsISHEntry> childEntry;
10172 container->GetChildAt(i, getter_AddRefs(childEntry));
10173 if (!childEntry) {
10174 // childEntry can be null for valid reasons, for example if the
10175 // docshell at index i never loaded anything useful.
10176 // Remember to clone also nulls in the child array (bug 464064).
10177 aCallback(nsnull, nsnull, i, aData);
10178 continue;
10181 nsDocShell *childShell = nsnull;
10182 if (aRootShell) {
10183 // Walk the children of aRootShell and see if one of them
10184 // has srcChild as a SHEntry.
10186 PRInt32 childCount = aRootShell->mChildList.Count();
10187 for (PRInt32 j = 0; j < childCount; ++j) {
10188 nsDocShell *child =
10189 static_cast<nsDocShell*>(aRootShell->ChildAt(j));
10191 if (child->HasHistoryEntry(childEntry)) {
10192 childShell = child;
10193 break;
10197 nsresult rv = aCallback(childEntry, childShell, i, aData);
10198 NS_ENSURE_SUCCESS(rv, rv);
10201 return NS_OK;
10204 // callback data for WalkHistoryEntries
10205 struct NS_STACK_CLASS CloneAndReplaceData
10207 CloneAndReplaceData(PRUint32 aCloneID, nsISHEntry *aReplaceEntry,
10208 PRBool aCloneChildren, nsISHEntry *aDestTreeParent)
10209 : cloneID(aCloneID),
10210 cloneChildren(aCloneChildren),
10211 replaceEntry(aReplaceEntry),
10212 destTreeParent(aDestTreeParent) { }
10214 PRUint32 cloneID;
10215 PRBool cloneChildren;
10216 nsISHEntry *replaceEntry;
10217 nsISHEntry *destTreeParent;
10218 nsCOMPtr<nsISHEntry> resultEntry;
10221 /* static */ nsresult
10222 nsDocShell::CloneAndReplaceChild(nsISHEntry *aEntry, nsDocShell *aShell,
10223 PRInt32 aEntryIndex, void *aData)
10225 nsresult result = NS_OK;
10226 nsCOMPtr<nsISHEntry> dest;
10228 CloneAndReplaceData *data = static_cast<CloneAndReplaceData*>(aData);
10229 PRUint32 cloneID = data->cloneID;
10230 nsISHEntry *replaceEntry = data->replaceEntry;
10232 nsCOMPtr<nsISHContainer> container =
10233 do_QueryInterface(data->destTreeParent);
10234 if (!aEntry) {
10235 if (container) {
10236 container->AddChild(nsnull, aEntryIndex);
10238 return NS_OK;
10241 PRUint32 srcID;
10242 aEntry->GetID(&srcID);
10244 if (srcID == cloneID) {
10245 // Replace the entry
10246 dest = replaceEntry;
10247 dest->SetIsSubFrame(PR_TRUE);
10249 if (data->cloneChildren) {
10250 // Walk the children
10251 CloneAndReplaceData childData(cloneID, replaceEntry,
10252 data->cloneChildren, dest);
10253 result = WalkHistoryEntries(aEntry, aShell,
10254 CloneAndReplaceChild, &childData);
10255 if (NS_FAILED(result))
10256 return result;
10258 } else {
10259 // Clone the SHEntry...
10260 result = aEntry->Clone(getter_AddRefs(dest));
10261 if (NS_FAILED(result))
10262 return result;
10264 // This entry is for a subframe navigation
10265 dest->SetIsSubFrame(PR_TRUE);
10267 // Walk the children
10268 CloneAndReplaceData childData(cloneID, replaceEntry,
10269 data->cloneChildren, dest);
10270 result = WalkHistoryEntries(aEntry, aShell,
10271 CloneAndReplaceChild, &childData);
10272 if (NS_FAILED(result))
10273 return result;
10275 if (aShell)
10276 aShell->SwapHistoryEntries(aEntry, dest);
10279 if (container)
10280 container->AddChild(dest, aEntryIndex);
10282 data->resultEntry = dest;
10283 return result;
10286 /* static */ nsresult
10287 nsDocShell::CloneAndReplace(nsISHEntry *aSrcEntry,
10288 nsDocShell *aSrcShell,
10289 PRUint32 aCloneID,
10290 nsISHEntry *aReplaceEntry,
10291 PRBool aCloneChildren,
10292 nsISHEntry **aResultEntry)
10294 NS_ENSURE_ARG_POINTER(aResultEntry);
10295 NS_ENSURE_TRUE(aReplaceEntry, NS_ERROR_FAILURE);
10297 CloneAndReplaceData data(aCloneID, aReplaceEntry, aCloneChildren, nsnull);
10298 nsresult rv = CloneAndReplaceChild(aSrcEntry, aSrcShell, 0, &data);
10300 data.resultEntry.swap(*aResultEntry);
10301 return rv;
10304 void
10305 nsDocShell::SwapHistoryEntries(nsISHEntry *aOldEntry, nsISHEntry *aNewEntry)
10307 if (aOldEntry == mOSHE)
10308 mOSHE = aNewEntry;
10310 if (aOldEntry == mLSHE)
10311 mLSHE = aNewEntry;
10315 struct SwapEntriesData
10317 nsDocShell *ignoreShell; // constant; the shell to ignore
10318 nsISHEntry *destTreeRoot; // constant; the root of the dest tree
10319 nsISHEntry *destTreeParent; // constant; the node under destTreeRoot
10320 // whose children will correspond to aEntry
10324 nsresult
10325 nsDocShell::SetChildHistoryEntry(nsISHEntry *aEntry, nsDocShell *aShell,
10326 PRInt32 aEntryIndex, void *aData)
10328 SwapEntriesData *data = static_cast<SwapEntriesData*>(aData);
10329 nsDocShell *ignoreShell = data->ignoreShell;
10331 if (!aShell || aShell == ignoreShell)
10332 return NS_OK;
10334 nsISHEntry *destTreeRoot = data->destTreeRoot;
10336 nsCOMPtr<nsISHEntry> destEntry;
10337 nsCOMPtr<nsISHContainer> container =
10338 do_QueryInterface(data->destTreeParent);
10340 if (container) {
10341 // aEntry is a clone of some child of destTreeParent, but since the
10342 // trees aren't necessarily in sync, we'll have to locate it.
10343 // Note that we could set aShell's entry to null if we don't find a
10344 // corresponding entry under destTreeParent.
10346 PRUint32 targetID, id;
10347 aEntry->GetID(&targetID);
10349 // First look at the given index, since this is the common case.
10350 nsCOMPtr<nsISHEntry> entry;
10351 container->GetChildAt(aEntryIndex, getter_AddRefs(entry));
10352 if (entry && NS_SUCCEEDED(entry->GetID(&id)) && id == targetID) {
10353 destEntry.swap(entry);
10354 } else {
10355 PRInt32 childCount;
10356 container->GetChildCount(&childCount);
10357 for (PRInt32 i = 0; i < childCount; ++i) {
10358 container->GetChildAt(i, getter_AddRefs(entry));
10359 if (!entry)
10360 continue;
10362 entry->GetID(&id);
10363 if (id == targetID) {
10364 destEntry.swap(entry);
10365 break;
10369 } else {
10370 destEntry = destTreeRoot;
10373 aShell->SwapHistoryEntries(aEntry, destEntry);
10375 // Now handle the children of aEntry.
10376 SwapEntriesData childData = { ignoreShell, destTreeRoot, destEntry };
10377 return WalkHistoryEntries(aEntry, aShell,
10378 SetChildHistoryEntry, &childData);
10382 static nsISHEntry*
10383 GetRootSHEntry(nsISHEntry *aEntry)
10385 nsCOMPtr<nsISHEntry> rootEntry = aEntry;
10386 nsISHEntry *result = nsnull;
10387 while (rootEntry) {
10388 result = rootEntry;
10389 result->GetParent(getter_AddRefs(rootEntry));
10392 return result;
10396 void
10397 nsDocShell::SetHistoryEntry(nsCOMPtr<nsISHEntry> *aPtr, nsISHEntry *aEntry)
10399 // We need to sync up the docshell and session history trees for
10400 // subframe navigation. If the load was in a subframe, we forward up to
10401 // the root docshell, which will then recursively sync up all docshells
10402 // to their corresponding entries in the new session history tree.
10403 // If we don't do this, then we can cache a content viewer on the wrong
10404 // cloned entry, and subsequently restore it at the wrong time.
10406 nsISHEntry *newRootEntry = GetRootSHEntry(aEntry);
10407 if (newRootEntry) {
10408 // newRootEntry is now the new root entry.
10409 // Find the old root entry as well.
10411 // Need a strong ref. on |oldRootEntry| so it isn't destroyed when
10412 // SetChildHistoryEntry() does SwapHistoryEntries() (bug 304639).
10413 nsCOMPtr<nsISHEntry> oldRootEntry = GetRootSHEntry(*aPtr);
10414 if (oldRootEntry) {
10415 nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
10416 GetSameTypeParent(getter_AddRefs(parentAsItem));
10417 nsCOMPtr<nsIDocShell> rootShell = do_QueryInterface(parentAsItem);
10418 if (rootShell) { // if we're the root just set it, nothing to swap
10419 SwapEntriesData data = { this, newRootEntry };
10420 nsIDocShell *rootIDocShell =
10421 static_cast<nsIDocShell*>(rootShell);
10422 nsDocShell *rootDocShell = static_cast<nsDocShell*>
10423 (rootIDocShell);
10425 #ifdef NS_DEBUG
10426 nsresult rv =
10427 #endif
10428 SetChildHistoryEntry(oldRootEntry, rootDocShell,
10429 0, &data);
10430 NS_ASSERTION(NS_SUCCEEDED(rv), "SetChildHistoryEntry failed");
10435 *aPtr = aEntry;
10439 nsresult
10440 nsDocShell::GetRootSessionHistory(nsISHistory ** aReturn)
10442 nsresult rv;
10444 nsCOMPtr<nsIDocShellTreeItem> root;
10445 //Get the root docshell
10446 rv = GetSameTypeRootTreeItem(getter_AddRefs(root));
10447 // QI to nsIWebNavigation
10448 nsCOMPtr<nsIWebNavigation> rootAsWebnav(do_QueryInterface(root));
10449 if (rootAsWebnav) {
10450 // Get the handle to SH from the root docshell
10451 rv = rootAsWebnav->GetSessionHistory(aReturn);
10453 return rv;
10456 nsresult
10457 nsDocShell::GetHttpChannel(nsIChannel * aChannel, nsIHttpChannel ** aReturn)
10459 NS_ENSURE_ARG_POINTER(aReturn);
10460 if (!aChannel)
10461 return NS_ERROR_FAILURE;
10463 nsCOMPtr<nsIMultiPartChannel> multiPartChannel(do_QueryInterface(aChannel));
10464 if (multiPartChannel) {
10465 nsCOMPtr<nsIChannel> baseChannel;
10466 multiPartChannel->GetBaseChannel(getter_AddRefs(baseChannel));
10467 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(baseChannel));
10468 *aReturn = httpChannel;
10469 NS_IF_ADDREF(*aReturn);
10471 return NS_OK;
10474 PRBool
10475 nsDocShell::ShouldDiscardLayoutState(nsIHttpChannel * aChannel)
10477 // By default layout State will be saved.
10478 if (!aChannel)
10479 return PR_FALSE;
10481 // figure out if SH should be saving layout state
10482 nsCOMPtr<nsISupports> securityInfo;
10483 PRBool noStore = PR_FALSE, noCache = PR_FALSE;
10484 aChannel->GetSecurityInfo(getter_AddRefs(securityInfo));
10485 aChannel->IsNoStoreResponse(&noStore);
10486 aChannel->IsNoCacheResponse(&noCache);
10488 return (noStore || (noCache && securityInfo));
10491 //*****************************************************************************
10492 // nsDocShell: nsIEditorDocShell
10493 //*****************************************************************************
10495 NS_IMETHODIMP nsDocShell::GetEditor(nsIEditor * *aEditor)
10497 NS_ENSURE_ARG_POINTER(aEditor);
10499 if (!mEditorData) {
10500 *aEditor = nsnull;
10501 return NS_OK;
10504 return mEditorData->GetEditor(aEditor);
10507 NS_IMETHODIMP nsDocShell::SetEditor(nsIEditor * aEditor)
10509 nsresult rv = EnsureEditorData();
10510 if (NS_FAILED(rv)) return rv;
10512 return mEditorData->SetEditor(aEditor);
10516 NS_IMETHODIMP nsDocShell::GetEditable(PRBool *aEditable)
10518 NS_ENSURE_ARG_POINTER(aEditable);
10519 *aEditable = mEditorData && mEditorData->GetEditable();
10520 return NS_OK;
10524 NS_IMETHODIMP nsDocShell::GetHasEditingSession(PRBool *aHasEditingSession)
10526 NS_ENSURE_ARG_POINTER(aHasEditingSession);
10528 if (mEditorData)
10530 nsCOMPtr<nsIEditingSession> editingSession;
10531 mEditorData->GetEditingSession(getter_AddRefs(editingSession));
10532 *aHasEditingSession = (editingSession.get() != nsnull);
10534 else
10536 *aHasEditingSession = PR_FALSE;
10539 return NS_OK;
10542 NS_IMETHODIMP nsDocShell::MakeEditable(PRBool inWaitForUriLoad)
10544 nsresult rv = EnsureEditorData();
10545 if (NS_FAILED(rv)) return rv;
10547 return mEditorData->MakeEditable(inWaitForUriLoad);
10550 bool
10551 nsDocShell::ChannelIsPost(nsIChannel* aChannel)
10553 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
10554 if (!httpChannel) {
10555 return false;
10558 nsCAutoString method;
10559 httpChannel->GetRequestMethod(method);
10560 return method.Equals("POST");
10563 void
10564 nsDocShell::ExtractLastVisit(nsIChannel* aChannel,
10565 nsIURI** aURI,
10566 PRUint32* aChannelRedirectFlags)
10568 nsCOMPtr<nsIPropertyBag2> props(do_QueryInterface(aChannel));
10569 if (!props) {
10570 return;
10573 nsresult rv = props->GetPropertyAsInterface(
10574 NS_LITERAL_STRING("docshell.previousURI"),
10575 NS_GET_IID(nsIURI),
10576 reinterpret_cast<void**>(aURI)
10579 if (NS_FAILED(rv)) {
10580 // There is no last visit for this channel, so this must be the first
10581 // link. Link the visit to the referrer of this request, if any.
10582 // Treat referrer as null if there is an error getting it.
10583 (void)NS_GetReferrerFromChannel(aChannel, aURI);
10585 else {
10586 rv = props->GetPropertyAsUint32(
10587 NS_LITERAL_STRING("docshell.previousFlags"),
10588 aChannelRedirectFlags
10591 NS_WARN_IF_FALSE(
10592 NS_SUCCEEDED(rv),
10593 "Could not fetch previous flags, URI will be treated like referrer"
10598 void
10599 nsDocShell::SaveLastVisit(nsIChannel* aChannel,
10600 nsIURI* aURI,
10601 PRUint32 aChannelRedirectFlags)
10603 nsCOMPtr<nsIWritablePropertyBag2> props(do_QueryInterface(aChannel));
10604 if (!props || !aURI) {
10605 return;
10608 props->SetPropertyAsInterface(NS_LITERAL_STRING("docshell.previousURI"),
10609 aURI);
10610 props->SetPropertyAsUint32(NS_LITERAL_STRING("docshell.previousFlags"),
10611 aChannelRedirectFlags);
10614 void
10615 nsDocShell::AddURIVisit(nsIURI* aURI,
10616 nsIURI* aReferrerURI,
10617 nsIURI* aPreviousURI,
10618 PRUint32 aChannelRedirectFlags)
10620 NS_ASSERTION(aURI, "Visited URI is null!");
10622 // Only content-type docshells save URI visits. Also don't do
10623 // anything here if we're not supposed to use global history.
10624 if (mItemType != typeContent || !mUseGlobalHistory) {
10625 return;
10628 nsCOMPtr<IHistory> history = services::GetHistoryService();
10630 if (history) {
10631 PRUint32 visitURIFlags = 0;
10633 if (!IsFrame()) {
10634 visitURIFlags |= IHistory::TOP_LEVEL;
10637 if (aChannelRedirectFlags & nsIChannelEventSink::REDIRECT_TEMPORARY) {
10638 visitURIFlags |= IHistory::REDIRECT_TEMPORARY;
10640 else if (aChannelRedirectFlags &
10641 nsIChannelEventSink::REDIRECT_PERMANENT) {
10642 visitURIFlags |= IHistory::REDIRECT_PERMANENT;
10645 (void)history->VisitURI(aURI, aPreviousURI, visitURIFlags);
10647 else if (mGlobalHistory) {
10648 // Falls back to sync global history interface.
10649 (void)mGlobalHistory->AddURI(aURI,
10650 !!aChannelRedirectFlags,
10651 !IsFrame(),
10652 aReferrerURI);
10656 //*****************************************************************************
10657 // nsDocShell: Helper Routines
10658 //*****************************************************************************
10660 NS_IMETHODIMP
10661 nsDocShell::SetLoadType(PRUint32 aLoadType)
10663 mLoadType = aLoadType;
10664 return NS_OK;
10667 NS_IMETHODIMP
10668 nsDocShell::GetLoadType(PRUint32 * aLoadType)
10670 *aLoadType = mLoadType;
10671 return NS_OK;
10674 nsresult
10675 nsDocShell::ConfirmRepost(PRBool * aRepost)
10677 nsCOMPtr<nsIPrompt> prompter;
10678 CallGetInterface(this, static_cast<nsIPrompt**>(getter_AddRefs(prompter)));
10679 if (!prompter) {
10680 return NS_ERROR_NOT_AVAILABLE;
10683 nsCOMPtr<nsIStringBundleService> stringBundleService =
10684 mozilla::services::GetStringBundleService();
10685 if (!stringBundleService)
10686 return NS_ERROR_FAILURE;
10688 nsCOMPtr<nsIStringBundle> appBundle;
10689 nsresult rv = stringBundleService->CreateBundle(kAppstringsBundleURL,
10690 getter_AddRefs(appBundle));
10691 NS_ENSURE_SUCCESS(rv, rv);
10693 nsCOMPtr<nsIStringBundle> brandBundle;
10694 rv = stringBundleService->CreateBundle(kBrandBundleURL, getter_AddRefs(brandBundle));
10695 NS_ENSURE_SUCCESS(rv, rv);
10697 NS_ASSERTION(prompter && brandBundle && appBundle,
10698 "Unable to set up repost prompter.");
10700 nsXPIDLString brandName;
10701 rv = brandBundle->GetStringFromName(NS_LITERAL_STRING("brandShortName").get(),
10702 getter_Copies(brandName));
10704 nsXPIDLString msgString, button0Title;
10705 if (NS_FAILED(rv)) { // No brand, use the generic version.
10706 rv = appBundle->GetStringFromName(NS_LITERAL_STRING("confirmRepostPrompt").get(),
10707 getter_Copies(msgString));
10709 else {
10710 // Brand available - if the app has an override file with formatting, the app name will
10711 // be included. Without an override, the prompt will look like the generic version.
10712 const PRUnichar *formatStrings[] = { brandName.get() };
10713 rv = appBundle->FormatStringFromName(NS_LITERAL_STRING("confirmRepostPrompt").get(),
10714 formatStrings, NS_ARRAY_LENGTH(formatStrings),
10715 getter_Copies(msgString));
10717 if (NS_FAILED(rv)) return rv;
10719 rv = appBundle->GetStringFromName(NS_LITERAL_STRING("resendButton.label").get(),
10720 getter_Copies(button0Title));
10721 if (NS_FAILED(rv)) return rv;
10723 PRInt32 buttonPressed;
10724 PRBool checkState;
10725 rv = prompter->
10726 ConfirmEx(nsnull, msgString.get(),
10727 (nsIPrompt::BUTTON_POS_0 * nsIPrompt::BUTTON_TITLE_IS_STRING) +
10728 (nsIPrompt::BUTTON_POS_1 * nsIPrompt::BUTTON_TITLE_CANCEL),
10729 button0Title.get(), nsnull, nsnull, nsnull, &checkState, &buttonPressed);
10730 if (NS_FAILED(rv)) return rv;
10732 *aRepost = (buttonPressed == 0);
10733 return NS_OK;
10736 NS_IMETHODIMP
10737 nsDocShell::GetPromptAndStringBundle(nsIPrompt ** aPrompt,
10738 nsIStringBundle ** aStringBundle)
10740 NS_ENSURE_SUCCESS(GetInterface(NS_GET_IID(nsIPrompt), (void **) aPrompt),
10741 NS_ERROR_FAILURE);
10743 nsCOMPtr<nsIStringBundleService> stringBundleService =
10744 mozilla::services::GetStringBundleService();
10745 NS_ENSURE_TRUE(stringBundleService, NS_ERROR_FAILURE);
10747 NS_ENSURE_SUCCESS(stringBundleService->
10748 CreateBundle(kAppstringsBundleURL,
10749 aStringBundle),
10750 NS_ERROR_FAILURE);
10752 return NS_OK;
10755 NS_IMETHODIMP
10756 nsDocShell::GetChildOffset(nsIDOMNode * aChild, nsIDOMNode * aParent,
10757 PRInt32 * aOffset)
10759 NS_ENSURE_ARG_POINTER(aChild || aParent);
10761 nsCOMPtr<nsIDOMNodeList> childNodes;
10762 NS_ENSURE_SUCCESS(aParent->GetChildNodes(getter_AddRefs(childNodes)),
10763 NS_ERROR_FAILURE);
10764 NS_ENSURE_TRUE(childNodes, NS_ERROR_FAILURE);
10766 PRInt32 i = 0;
10768 for (; PR_TRUE; i++) {
10769 nsCOMPtr<nsIDOMNode> childNode;
10770 NS_ENSURE_SUCCESS(childNodes->Item(i, getter_AddRefs(childNode)),
10771 NS_ERROR_FAILURE);
10772 NS_ENSURE_TRUE(childNode, NS_ERROR_FAILURE);
10774 if (childNode.get() == aChild) {
10775 *aOffset = i;
10776 return NS_OK;
10780 return NS_ERROR_FAILURE;
10783 nsIScrollableFrame *
10784 nsDocShell::GetRootScrollFrame()
10786 nsCOMPtr<nsIPresShell> shell;
10787 NS_ENSURE_SUCCESS(GetPresShell(getter_AddRefs(shell)), nsnull);
10788 NS_ENSURE_TRUE(shell, nsnull);
10790 return shell->GetRootScrollFrameAsScrollableExternal();
10793 #ifdef DEBUG
10794 class nsDebugAutoBoolTrueSetter
10796 public:
10797 nsDebugAutoBoolTrueSetter(PRPackedBool *aBool)
10798 : mBool(aBool)
10800 *mBool = PR_TRUE;
10803 ~nsDebugAutoBoolTrueSetter()
10805 *mBool = PR_FALSE;
10807 protected:
10808 PRPackedBool *mBool;
10810 #endif
10812 NS_IMETHODIMP
10813 nsDocShell::EnsureScriptEnvironment()
10815 if (mScriptGlobal)
10816 return NS_OK;
10818 if (mIsBeingDestroyed) {
10819 return NS_ERROR_NOT_AVAILABLE;
10822 NS_TIME_FUNCTION;
10824 #ifdef DEBUG
10825 NS_ASSERTION(!mInEnsureScriptEnv,
10826 "Infinite loop! Calling EnsureScriptEnvironment() from "
10827 "within EnsureScriptEnvironment()!");
10829 // Yeah, this isn't re-entrant safe, but that's ok since if we
10830 // re-enter this method, we'll infinitely loop...
10831 nsDebugAutoBoolTrueSetter boolSetter(&mInEnsureScriptEnv);
10832 #endif
10834 nsCOMPtr<nsIDOMScriptObjectFactory> factory =
10835 do_GetService(kDOMScriptObjectFactoryCID);
10836 NS_ENSURE_TRUE(factory, NS_ERROR_FAILURE);
10838 nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(mTreeOwner));
10839 NS_ENSURE_TRUE(browserChrome, NS_ERROR_NOT_AVAILABLE);
10841 PRUint32 chromeFlags;
10842 browserChrome->GetChromeFlags(&chromeFlags);
10844 PRBool isModalContentWindow =
10845 (chromeFlags & nsIWebBrowserChrome::CHROME_MODAL) &&
10846 !(chromeFlags & nsIWebBrowserChrome::CHROME_OPENAS_CHROME);
10848 // If our window is modal and we're not opened as chrome, make
10849 // this window a modal content window.
10850 factory->NewScriptGlobalObject(mItemType == typeChrome,
10851 isModalContentWindow,
10852 getter_AddRefs(mScriptGlobal));
10853 NS_ENSURE_TRUE(mScriptGlobal, NS_ERROR_FAILURE);
10855 nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(mScriptGlobal));
10856 win->SetDocShell(static_cast<nsIDocShell *>(this));
10858 // Ensure the script object is set to run javascript - other languages
10859 // setup on demand.
10860 // XXXmarkh - should this be setup to run the default language for this doc?
10861 nsresult rv;
10862 rv = mScriptGlobal->EnsureScriptEnvironment(nsIProgrammingLanguage::JAVASCRIPT);
10863 NS_ENSURE_SUCCESS(rv, rv);
10865 return NS_OK;
10869 NS_IMETHODIMP
10870 nsDocShell::EnsureEditorData()
10872 PRBool openDocHasDetachedEditor = mOSHE && mOSHE->HasDetachedEditor();
10873 if (!mEditorData && !mIsBeingDestroyed && !openDocHasDetachedEditor) {
10874 // We shouldn't recreate the editor data if it already exists, or
10875 // we're shutting down, or we already have a detached editor data
10876 // stored in the session history. We should only have one editordata
10877 // per docshell.
10878 mEditorData = new nsDocShellEditorData(this);
10881 return mEditorData ? NS_OK : NS_ERROR_NOT_AVAILABLE;
10884 nsresult
10885 nsDocShell::EnsureTransferableHookData()
10887 if (!mTransferableHookData) {
10888 mTransferableHookData = new nsTransferableHookData();
10889 if (!mTransferableHookData) return NS_ERROR_OUT_OF_MEMORY;
10892 return NS_OK;
10896 NS_IMETHODIMP nsDocShell::EnsureFind()
10898 nsresult rv;
10899 if (!mFind)
10901 mFind = do_CreateInstance("@mozilla.org/embedcomp/find;1", &rv);
10902 if (NS_FAILED(rv)) return rv;
10905 // we promise that the nsIWebBrowserFind that we return has been set
10906 // up to point to the focused, or content window, so we have to
10907 // set that up each time.
10909 nsIScriptGlobalObject* scriptGO = GetScriptGlobalObject();
10910 NS_ENSURE_TRUE(scriptGO, NS_ERROR_UNEXPECTED);
10912 // default to our window
10913 nsCOMPtr<nsIDOMWindow> windowToSearch(do_QueryInterface(mScriptGlobal));
10915 nsCOMPtr<nsIDocShellTreeItem> root;
10916 GetRootTreeItem(getter_AddRefs(root));
10918 // if the active window is the same window that this docshell is in,
10919 // use the currently focused frame
10920 nsCOMPtr<nsIDOMWindow> rootWindow = do_GetInterface(root);
10921 nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID);
10922 if (fm) {
10923 nsCOMPtr<nsIDOMWindow> activeWindow;
10924 fm->GetActiveWindow(getter_AddRefs(activeWindow));
10925 if (activeWindow == rootWindow)
10926 fm->GetFocusedWindow(getter_AddRefs(windowToSearch));
10929 nsCOMPtr<nsIWebBrowserFindInFrames> findInFrames = do_QueryInterface(mFind);
10930 if (!findInFrames) return NS_ERROR_NO_INTERFACE;
10932 rv = findInFrames->SetRootSearchFrame(rootWindow);
10933 if (NS_FAILED(rv)) return rv;
10934 rv = findInFrames->SetCurrentSearchFrame(windowToSearch);
10935 if (NS_FAILED(rv)) return rv;
10937 return NS_OK;
10940 PRBool
10941 nsDocShell::IsFrame()
10943 nsCOMPtr<nsIDocShellTreeItem> parent =
10944 do_QueryInterface(GetAsSupports(mParent));
10945 if (parent) {
10946 PRInt32 parentType = ~mItemType; // Not us
10947 parent->GetItemType(&parentType);
10948 if (parentType == mItemType) // This is a frame
10949 return PR_TRUE;
10952 return PR_FALSE;
10955 /* boolean IsBeingDestroyed (); */
10956 NS_IMETHODIMP
10957 nsDocShell::IsBeingDestroyed(PRBool *aDoomed)
10959 NS_ENSURE_ARG(aDoomed);
10960 *aDoomed = mIsBeingDestroyed;
10961 return NS_OK;
10965 NS_IMETHODIMP
10966 nsDocShell::GetIsExecutingOnLoadHandler(PRBool *aResult)
10968 NS_ENSURE_ARG(aResult);
10969 *aResult = mIsExecutingOnLoadHandler;
10970 return NS_OK;
10973 NS_IMETHODIMP
10974 nsDocShell::GetLayoutHistoryState(nsILayoutHistoryState **aLayoutHistoryState)
10976 if (mOSHE)
10977 mOSHE->GetLayoutHistoryState(aLayoutHistoryState);
10978 return NS_OK;
10981 NS_IMETHODIMP
10982 nsDocShell::SetLayoutHistoryState(nsILayoutHistoryState *aLayoutHistoryState)
10984 if (mOSHE)
10985 mOSHE->SetLayoutHistoryState(aLayoutHistoryState);
10986 return NS_OK;
10989 //*****************************************************************************
10990 //*** nsRefreshTimer: Object Management
10991 //*****************************************************************************
10993 nsRefreshTimer::nsRefreshTimer()
10994 : mDelay(0), mRepeat(PR_FALSE), mMetaRefresh(PR_FALSE)
10998 nsRefreshTimer::~nsRefreshTimer()
11002 //*****************************************************************************
11003 // nsRefreshTimer::nsISupports
11004 //*****************************************************************************
11006 NS_IMPL_THREADSAFE_ADDREF(nsRefreshTimer)
11007 NS_IMPL_THREADSAFE_RELEASE(nsRefreshTimer)
11009 NS_INTERFACE_MAP_BEGIN(nsRefreshTimer)
11010 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITimerCallback)
11011 NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
11012 NS_INTERFACE_MAP_END_THREADSAFE
11014 ///*****************************************************************************
11015 // nsRefreshTimer::nsITimerCallback
11016 //******************************************************************************
11017 NS_IMETHODIMP
11018 nsRefreshTimer::Notify(nsITimer * aTimer)
11020 NS_ASSERTION(mDocShell, "DocShell is somehow null");
11022 if (mDocShell && aTimer) {
11023 // Get the delay count to determine load type
11024 PRUint32 delay = 0;
11025 aTimer->GetDelay(&delay);
11026 mDocShell->ForceRefreshURIFromTimer(mURI, delay, mMetaRefresh, aTimer);
11028 return NS_OK;
11031 //*****************************************************************************
11032 // nsDocShell::InterfaceRequestorProxy
11033 //*****************************************************************************
11034 nsDocShell::InterfaceRequestorProxy::InterfaceRequestorProxy(nsIInterfaceRequestor* p)
11036 if (p) {
11037 mWeakPtr = do_GetWeakReference(p);
11041 nsDocShell::InterfaceRequestorProxy::~InterfaceRequestorProxy()
11043 mWeakPtr = nsnull;
11046 NS_IMPL_THREADSAFE_ISUPPORTS1(nsDocShell::InterfaceRequestorProxy, nsIInterfaceRequestor)
11048 NS_IMETHODIMP
11049 nsDocShell::InterfaceRequestorProxy::GetInterface(const nsIID & aIID, void **aSink)
11051 NS_ENSURE_ARG_POINTER(aSink);
11052 nsCOMPtr<nsIInterfaceRequestor> ifReq = do_QueryReferent(mWeakPtr);
11053 if (ifReq) {
11054 return ifReq->GetInterface(aIID, aSink);
11056 *aSink = nsnull;
11057 return NS_NOINTERFACE;
11060 nsresult
11061 nsDocShell::SetBaseUrlForWyciwyg(nsIContentViewer * aContentViewer)
11063 if (!aContentViewer)
11064 return NS_ERROR_FAILURE;
11066 nsCOMPtr<nsIURI> baseURI;
11067 nsresult rv = NS_ERROR_NOT_AVAILABLE;
11069 if (sURIFixup)
11070 rv = sURIFixup->CreateExposableURI(mCurrentURI,
11071 getter_AddRefs(baseURI));
11073 // Get the current document and set the base uri
11074 if (baseURI) {
11075 nsIDocument* document = aContentViewer->GetDocument();
11076 if (document) {
11077 rv = document->SetBaseURI(baseURI);
11080 return rv;
11083 //*****************************************************************************
11084 // nsDocShell::nsIAuthPromptProvider
11085 //*****************************************************************************
11087 NS_IMETHODIMP
11088 nsDocShell::GetAuthPrompt(PRUint32 aPromptReason, const nsIID& iid,
11089 void** aResult)
11091 // a priority prompt request will override a false mAllowAuth setting
11092 PRBool priorityPrompt = (aPromptReason == PROMPT_PROXY);
11094 if (!mAllowAuth && !priorityPrompt)
11095 return NS_ERROR_NOT_AVAILABLE;
11097 // we're either allowing auth, or it's a proxy request
11098 nsresult rv;
11099 nsCOMPtr<nsIPromptFactory> wwatch =
11100 do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
11101 NS_ENSURE_SUCCESS(rv, rv);
11103 rv = EnsureScriptEnvironment();
11104 NS_ENSURE_SUCCESS(rv, rv);
11106 nsCOMPtr<nsIDOMWindow> window(do_QueryInterface(mScriptGlobal));
11108 // Get the an auth prompter for our window so that the parenting
11109 // of the dialogs works as it should when using tabs.
11111 return wwatch->GetPrompt(window, iid,
11112 reinterpret_cast<void**>(aResult));
11115 //*****************************************************************************
11116 // nsDocShell::nsIObserver
11117 //*****************************************************************************
11119 NS_IMETHODIMP
11120 nsDocShell::Observe(nsISupports *aSubject, const char *aTopic,
11121 const PRUnichar *aData)
11123 nsresult rv = NS_OK;
11124 if (mObserveErrorPages &&
11125 !nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) &&
11126 !nsCRT::strcmp(aData,
11127 NS_LITERAL_STRING("browser.xul.error_pages.enabled").get())) {
11129 nsCOMPtr<nsIPrefBranch> prefs(do_QueryInterface(aSubject, &rv));
11130 NS_ENSURE_SUCCESS(rv, rv);
11132 PRBool tmpbool;
11133 rv = prefs->GetBoolPref("browser.xul.error_pages.enabled", &tmpbool);
11134 if (NS_SUCCEEDED(rv))
11135 mUseErrorPages = tmpbool;
11137 } else {
11138 rv = NS_ERROR_UNEXPECTED;
11140 return rv;
11143 //*****************************************************************************
11144 // nsDocShell::nsILoadContext
11145 //*****************************************************************************
11146 NS_IMETHODIMP
11147 nsDocShell::GetAssociatedWindow(nsIDOMWindow** aWindow)
11149 CallGetInterface(this, aWindow);
11150 return NS_OK;
11153 NS_IMETHODIMP
11154 nsDocShell::GetTopWindow(nsIDOMWindow** aWindow)
11156 nsCOMPtr<nsIDOMWindow> win = do_GetInterface(GetAsSupports(this));
11157 if (win) {
11158 win->GetTop(aWindow);
11160 return NS_OK;
11163 NS_IMETHODIMP
11164 nsDocShell::IsAppOfType(PRUint32 aAppType, PRBool *aIsOfType)
11166 nsCOMPtr<nsIDocShell> shell = this;
11167 while (shell) {
11168 PRUint32 type;
11169 shell->GetAppType(&type);
11170 if (type == aAppType) {
11171 *aIsOfType = PR_TRUE;
11172 return NS_OK;
11174 nsCOMPtr<nsIDocShellTreeItem> item = do_QueryInterface(shell);
11175 nsCOMPtr<nsIDocShellTreeItem> parent;
11176 item->GetParent(getter_AddRefs(parent));
11177 shell = do_QueryInterface(parent);
11180 *aIsOfType = PR_FALSE;
11181 return NS_OK;
11184 NS_IMETHODIMP
11185 nsDocShell::GetIsContent(PRBool *aIsContent)
11187 *aIsContent = (mItemType == typeContent);
11188 return NS_OK;
11191 /* static */
11192 nsresult
11193 nsDocShell::URIInheritsSecurityContext(nsIURI* aURI, PRBool* aResult)
11195 // Note: about:blank URIs do NOT inherit the security context from the
11196 // current document, which is what this function tests for...
11197 return NS_URIChainHasFlags(aURI,
11198 nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT,
11199 aResult);
11202 /* static */
11203 PRBool
11204 nsDocShell::URIIsLocalFile(nsIURI *aURI)
11206 PRBool isFile;
11207 nsCOMPtr<nsINetUtil> util = do_GetNetUtil();
11209 return util && NS_SUCCEEDED(util->ProtocolHasFlags(aURI,
11210 nsIProtocolHandler::URI_IS_LOCAL_FILE,
11211 &isFile)) &&
11212 isFile;
11215 /* static */
11216 PRBool
11217 nsDocShell::IsAboutBlank(nsIURI* aURI)
11219 NS_PRECONDITION(aURI, "Must have URI");
11221 // GetSpec can be expensive for some URIs, so check the scheme first.
11222 PRBool isAbout = PR_FALSE;
11223 if (NS_FAILED(aURI->SchemeIs("about", &isAbout)) || !isAbout) {
11224 return PR_FALSE;
11227 nsCAutoString str;
11228 aURI->GetSpec(str);
11229 return str.EqualsLiteral("about:blank");
11232 PRBool
11233 nsDocShell::IsOKToLoadURI(nsIURI* aURI)
11235 NS_PRECONDITION(aURI, "Must have a URI!");
11237 if (!mFiredUnloadEvent) {
11238 return PR_TRUE;
11241 if (!mLoadingURI) {
11242 return PR_FALSE;
11245 nsCOMPtr<nsIScriptSecurityManager> secMan =
11246 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
11247 return
11248 secMan &&
11249 NS_SUCCEEDED(secMan->CheckSameOriginURI(aURI, mLoadingURI, PR_FALSE));
11253 // Routines for selection and clipboard
11255 nsresult
11256 nsDocShell::GetControllerForCommand(const char * inCommand,
11257 nsIController** outController)
11259 NS_ENSURE_ARG_POINTER(outController);
11260 *outController = nsnull;
11262 nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(mScriptGlobal));
11263 if (window) {
11264 nsCOMPtr<nsPIWindowRoot> root = window->GetTopWindowRoot();
11265 if (root) {
11266 return root->GetControllerForCommand(inCommand, outController);
11270 return NS_ERROR_FAILURE;
11273 nsresult
11274 nsDocShell::IsCommandEnabled(const char * inCommand, PRBool* outEnabled)
11276 NS_ENSURE_ARG_POINTER(outEnabled);
11277 *outEnabled = PR_FALSE;
11279 nsresult rv = NS_ERROR_FAILURE;
11281 nsCOMPtr<nsIController> controller;
11282 rv = GetControllerForCommand (inCommand, getter_AddRefs(controller));
11283 if (controller)
11284 rv = controller->IsCommandEnabled(inCommand, outEnabled);
11286 return rv;
11289 nsresult
11290 nsDocShell::DoCommand(const char * inCommand)
11292 nsresult rv = NS_ERROR_FAILURE;
11294 nsCOMPtr<nsIController> controller;
11295 rv = GetControllerForCommand(inCommand, getter_AddRefs(controller));
11296 if (controller)
11297 rv = controller->DoCommand(inCommand);
11299 return rv;
11302 nsresult
11303 nsDocShell::EnsureCommandHandler()
11305 if (!mCommandManager)
11307 nsCOMPtr<nsPICommandUpdater> commandUpdater =
11308 do_CreateInstance("@mozilla.org/embedcomp/command-manager;1");
11309 if (!commandUpdater) return NS_ERROR_OUT_OF_MEMORY;
11311 nsCOMPtr<nsIDOMWindow> domWindow =
11312 do_GetInterface(static_cast<nsIInterfaceRequestor *>(this));
11314 nsresult rv = commandUpdater->Init(domWindow);
11315 if (NS_SUCCEEDED(rv))
11316 mCommandManager = do_QueryInterface(commandUpdater);
11319 return mCommandManager ? NS_OK : NS_ERROR_FAILURE;
11322 NS_IMETHODIMP
11323 nsDocShell::CanCutSelection(PRBool* aResult)
11325 return IsCommandEnabled("cmd_cut", aResult);
11328 NS_IMETHODIMP
11329 nsDocShell::CanCopySelection(PRBool* aResult)
11331 return IsCommandEnabled("cmd_copy", aResult);
11334 NS_IMETHODIMP
11335 nsDocShell::CanCopyLinkLocation(PRBool* aResult)
11337 return IsCommandEnabled("cmd_copyLink", aResult);
11340 NS_IMETHODIMP
11341 nsDocShell::CanCopyImageLocation(PRBool* aResult)
11343 return IsCommandEnabled("cmd_copyImageLocation",
11344 aResult);
11347 NS_IMETHODIMP
11348 nsDocShell::CanCopyImageContents(PRBool* aResult)
11350 return IsCommandEnabled("cmd_copyImageContents",
11351 aResult);
11354 NS_IMETHODIMP
11355 nsDocShell::CanPaste(PRBool* aResult)
11357 return IsCommandEnabled("cmd_paste", aResult);
11360 NS_IMETHODIMP
11361 nsDocShell::CutSelection(void)
11363 return DoCommand ( "cmd_cut" );
11366 NS_IMETHODIMP
11367 nsDocShell::CopySelection(void)
11369 return DoCommand ( "cmd_copy" );
11372 NS_IMETHODIMP
11373 nsDocShell::CopyLinkLocation(void)
11375 return DoCommand ( "cmd_copyLink" );
11378 NS_IMETHODIMP
11379 nsDocShell::CopyImageLocation(void)
11381 return DoCommand ( "cmd_copyImageLocation" );
11384 NS_IMETHODIMP
11385 nsDocShell::CopyImageContents(void)
11387 return DoCommand ( "cmd_copyImageContents" );
11390 NS_IMETHODIMP
11391 nsDocShell::Paste(void)
11393 return DoCommand ( "cmd_paste" );
11396 NS_IMETHODIMP
11397 nsDocShell::SelectAll(void)
11399 return DoCommand ( "cmd_selectAll" );
11403 // SelectNone
11405 // Collapses the current selection, insertion point ends up at beginning
11406 // of previous selection.
11408 NS_IMETHODIMP
11409 nsDocShell::SelectNone(void)
11411 return DoCommand ( "cmd_selectNone" );
11414 //----------------------------------------------------------------------
11416 // link handling
11418 class OnLinkClickEvent : public nsRunnable {
11419 public:
11420 OnLinkClickEvent(nsDocShell* aHandler, nsIContent* aContent,
11421 nsIURI* aURI,
11422 const PRUnichar* aTargetSpec,
11423 nsIInputStream* aPostDataStream = 0,
11424 nsIInputStream* aHeadersDataStream = 0);
11426 NS_IMETHOD Run() {
11427 nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(mHandler->mScriptGlobal));
11428 nsAutoPopupStatePusher popupStatePusher(window, mPopupState);
11430 mHandler->OnLinkClickSync(mContent, mURI,
11431 mTargetSpec.get(), mPostDataStream,
11432 mHeadersDataStream,
11433 nsnull, nsnull);
11434 return NS_OK;
11437 private:
11438 nsRefPtr<nsDocShell> mHandler;
11439 nsCOMPtr<nsIURI> mURI;
11440 nsString mTargetSpec;
11441 nsCOMPtr<nsIInputStream> mPostDataStream;
11442 nsCOMPtr<nsIInputStream> mHeadersDataStream;
11443 nsCOMPtr<nsIContent> mContent;
11444 PopupControlState mPopupState;
11447 OnLinkClickEvent::OnLinkClickEvent(nsDocShell* aHandler,
11448 nsIContent *aContent,
11449 nsIURI* aURI,
11450 const PRUnichar* aTargetSpec,
11451 nsIInputStream* aPostDataStream,
11452 nsIInputStream* aHeadersDataStream)
11453 : mHandler(aHandler)
11454 , mURI(aURI)
11455 , mTargetSpec(aTargetSpec)
11456 , mPostDataStream(aPostDataStream)
11457 , mHeadersDataStream(aHeadersDataStream)
11458 , mContent(aContent)
11460 nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(mHandler->mScriptGlobal));
11462 mPopupState = window->GetPopupControlState();
11465 //----------------------------------------
11467 NS_IMETHODIMP
11468 nsDocShell::OnLinkClick(nsIContent* aContent,
11469 nsIURI* aURI,
11470 const PRUnichar* aTargetSpec,
11471 nsIInputStream* aPostDataStream,
11472 nsIInputStream* aHeadersDataStream)
11474 NS_ASSERTION(NS_IsMainThread(), "wrong thread");
11476 if (!IsOKToLoadURI(aURI)) {
11477 return NS_OK;
11480 if (aContent->IsEditable()) {
11481 return NS_OK;
11484 nsresult rv = NS_ERROR_FAILURE;
11485 nsAutoString target;
11487 nsCOMPtr<nsIWebBrowserChrome3> browserChrome3 = do_GetInterface(mTreeOwner);
11488 if (browserChrome3) {
11489 nsCOMPtr<nsIDOMNode> linkNode = do_QueryInterface(aContent);
11490 nsAutoString oldTarget(aTargetSpec);
11491 rv = browserChrome3->OnBeforeLinkTraversal(oldTarget, aURI,
11492 linkNode, mIsAppTab, target);
11495 if (NS_FAILED(rv))
11496 target = aTargetSpec;
11498 nsCOMPtr<nsIRunnable> ev =
11499 new OnLinkClickEvent(this, aContent, aURI, target.get(),
11500 aPostDataStream, aHeadersDataStream);
11501 return NS_DispatchToCurrentThread(ev);
11504 NS_IMETHODIMP
11505 nsDocShell::OnLinkClickSync(nsIContent *aContent,
11506 nsIURI* aURI,
11507 const PRUnichar* aTargetSpec,
11508 nsIInputStream* aPostDataStream,
11509 nsIInputStream* aHeadersDataStream,
11510 nsIDocShell** aDocShell,
11511 nsIRequest** aRequest)
11513 // Initialize the DocShell / Request
11514 if (aDocShell) {
11515 *aDocShell = nsnull;
11517 if (aRequest) {
11518 *aRequest = nsnull;
11521 if (!IsOKToLoadURI(aURI)) {
11522 return NS_OK;
11525 if (aContent->IsEditable()) {
11526 return NS_OK;
11530 // defer to an external protocol handler if necessary...
11531 nsCOMPtr<nsIExternalProtocolService> extProtService =
11532 do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID);
11533 if (extProtService) {
11534 nsCAutoString scheme;
11535 aURI->GetScheme(scheme);
11536 if (!scheme.IsEmpty()) {
11537 // if the URL scheme does not correspond to an exposed protocol, then we
11538 // need to hand this link click over to the external protocol handler.
11539 PRBool isExposed;
11540 nsresult rv = extProtService->IsExposedProtocol(scheme.get(), &isExposed);
11541 if (NS_SUCCEEDED(rv) && !isExposed) {
11542 return extProtService->LoadURI(aURI, this);
11548 // Get the owner document of the link that was clicked, this will be
11549 // the document that the link is in, or the last document that the
11550 // link was in. From that document, we'll get the URI to use as the
11551 // referer, since the current URI in this docshell may be a
11552 // new document that we're in the process of loading.
11553 nsCOMPtr<nsIDocument> refererDoc = aContent->GetOwnerDoc();
11554 NS_ENSURE_TRUE(refererDoc, NS_ERROR_UNEXPECTED);
11556 nsCOMPtr<nsIURI> referer = refererDoc->GetDocumentURI();
11558 // referer could be null here in some odd cases, but that's ok,
11559 // we'll just load the link w/o sending a referer in those cases.
11561 nsAutoString target(aTargetSpec);
11563 // If this is an anchor element, grab its type property to use as a hint
11564 nsAutoString typeHint;
11565 nsCOMPtr<nsIDOMHTMLAnchorElement> anchor(do_QueryInterface(aContent));
11566 if (anchor) {
11567 anchor->GetType(typeHint);
11568 NS_ConvertUTF16toUTF8 utf8Hint(typeHint);
11569 nsCAutoString type, dummy;
11570 NS_ParseContentType(utf8Hint, type, dummy);
11571 CopyUTF8toUTF16(type, typeHint);
11574 nsresult rv = InternalLoad(aURI, // New URI
11575 referer, // Referer URI
11576 aContent->NodePrincipal(), // Owner is our node's
11577 // principal
11578 INTERNAL_LOAD_FLAGS_NONE,
11579 target.get(), // Window target
11580 NS_LossyConvertUTF16toASCII(typeHint).get(),
11581 aPostDataStream, // Post data stream
11582 aHeadersDataStream, // Headers stream
11583 LOAD_LINK, // Load type
11584 nsnull, // No SHEntry
11585 PR_TRUE, // first party site
11586 aDocShell, // DocShell out-param
11587 aRequest); // Request out-param
11588 if (NS_SUCCEEDED(rv)) {
11589 DispatchPings(aContent, referer);
11591 return rv;
11594 NS_IMETHODIMP
11595 nsDocShell::OnOverLink(nsIContent* aContent,
11596 nsIURI* aURI,
11597 const PRUnichar* aTargetSpec)
11599 if (aContent->IsEditable()) {
11600 return NS_OK;
11603 nsCOMPtr<nsIWebBrowserChrome2> browserChrome2 = do_GetInterface(mTreeOwner);
11604 nsresult rv = NS_ERROR_FAILURE;
11606 nsCOMPtr<nsIWebBrowserChrome> browserChrome;
11607 if (!browserChrome2) {
11608 browserChrome = do_GetInterface(mTreeOwner);
11609 if (!browserChrome)
11610 return rv;
11613 nsCOMPtr<nsITextToSubURI> textToSubURI =
11614 do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
11615 if (NS_FAILED(rv))
11616 return rv;
11618 // use url origin charset to unescape the URL
11619 nsCAutoString charset;
11620 rv = aURI->GetOriginCharset(charset);
11621 NS_ENSURE_SUCCESS(rv, rv);
11623 nsCAutoString spec;
11624 rv = aURI->GetSpec(spec);
11625 NS_ENSURE_SUCCESS(rv, rv);
11627 nsAutoString uStr;
11628 rv = textToSubURI->UnEscapeURIForUI(charset, spec, uStr);
11629 NS_ENSURE_SUCCESS(rv, rv);
11631 if (browserChrome2) {
11632 nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aContent);
11633 rv = browserChrome2->SetStatusWithContext(nsIWebBrowserChrome::STATUS_LINK,
11634 uStr, element);
11635 } else {
11636 rv = browserChrome->SetStatus(nsIWebBrowserChrome::STATUS_LINK, uStr.get());
11638 return rv;
11641 NS_IMETHODIMP
11642 nsDocShell::OnLeaveLink()
11644 nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(mTreeOwner));
11645 nsresult rv = NS_ERROR_FAILURE;
11647 if (browserChrome) {
11648 rv = browserChrome->SetStatus(nsIWebBrowserChrome::STATUS_LINK,
11649 EmptyString().get());
11651 return rv;
11654 //----------------------------------------------------------------------
11655 // Web Shell Services API
11657 //This functions is only called when a new charset is detected in loading a document.
11658 //Its name should be changed to "CharsetReloadDocument"
11659 NS_IMETHODIMP
11660 nsDocShell::ReloadDocument(const char* aCharset,
11661 PRInt32 aSource)
11664 // XXX hack. keep the aCharset and aSource wait to pick it up
11665 nsCOMPtr<nsIContentViewer> cv;
11666 NS_ENSURE_SUCCESS(GetContentViewer(getter_AddRefs(cv)), NS_ERROR_FAILURE);
11667 if (cv)
11669 nsCOMPtr<nsIMarkupDocumentViewer> muDV = do_QueryInterface(cv);
11670 if (muDV)
11672 PRInt32 hint;
11673 muDV->GetHintCharacterSetSource(&hint);
11674 if (aSource > hint)
11676 nsCString charset(aCharset);
11677 muDV->SetHintCharacterSet(charset);
11678 muDV->SetHintCharacterSetSource(aSource);
11679 if(eCharsetReloadRequested != mCharsetReloadState)
11681 mCharsetReloadState = eCharsetReloadRequested;
11682 return Reload(LOAD_FLAGS_CHARSET_CHANGE);
11687 //return failure if this request is not accepted due to mCharsetReloadState
11688 return NS_ERROR_DOCSHELL_REQUEST_REJECTED;
11692 NS_IMETHODIMP
11693 nsDocShell::StopDocumentLoad(void)
11695 if(eCharsetReloadRequested != mCharsetReloadState)
11697 Stop(nsIWebNavigation::STOP_ALL);
11698 return NS_OK;
11700 //return failer if this request is not accepted due to mCharsetReloadState
11701 return NS_ERROR_DOCSHELL_REQUEST_REJECTED;
11704 NS_IMETHODIMP
11705 nsDocShell::GetPrintPreview(nsIWebBrowserPrint** aPrintPreview)
11707 *aPrintPreview = nsnull;
11708 #if NS_PRINT_PREVIEW
11709 nsCOMPtr<nsIDocumentViewerPrint> print = do_QueryInterface(mContentViewer);
11710 if (!print || !print->IsInitializedForPrintPreview()) {
11711 Stop(nsIWebNavigation::STOP_ALL);
11712 nsCOMPtr<nsIPrincipal> principal =
11713 do_CreateInstance("@mozilla.org/nullprincipal;1");
11714 NS_ENSURE_STATE(principal);
11715 nsresult rv = CreateAboutBlankContentViewer(principal, nsnull);
11716 NS_ENSURE_SUCCESS(rv, rv);
11717 print = do_QueryInterface(mContentViewer);
11718 NS_ENSURE_STATE(print);
11719 print->InitializeForPrintPreview();
11721 nsCOMPtr<nsIWebBrowserPrint> result = do_QueryInterface(print);
11722 result.forget(aPrintPreview);
11723 return NS_OK;
11724 #else
11725 return NS_ERROR_NOT_IMPLEMENTED;
11726 #endif
11730 #ifdef DEBUG
11731 unsigned long nsDocShell::gNumberOfDocShells = 0;
11732 #endif
11734 NS_IMETHODIMP
11735 nsDocShell::GetCanExecuteScripts(PRBool *aResult)
11737 NS_ENSURE_ARG_POINTER(aResult);
11738 *aResult = PR_FALSE; // disallow by default
11740 nsCOMPtr<nsIDocShell> docshell = this;
11741 nsCOMPtr<nsIDocShellTreeItem> globalObjTreeItem =
11742 do_QueryInterface(docshell);
11744 if (globalObjTreeItem)
11746 nsCOMPtr<nsIDocShellTreeItem> treeItem(globalObjTreeItem);
11747 nsCOMPtr<nsIDocShellTreeItem> parentItem;
11748 PRBool firstPass = PR_TRUE;
11749 PRBool lookForParents = PR_FALSE;
11751 // Walk up the docshell tree to see if any containing docshell disallows scripts
11754 nsresult rv = docshell->GetAllowJavascript(aResult);
11755 if (NS_FAILED(rv)) return rv;
11756 if (!*aResult) {
11757 nsDocShell* realDocshell = static_cast<nsDocShell*>(docshell.get());
11758 if (realDocshell->mContentViewer) {
11759 nsIDocument* doc = realDocshell->mContentViewer->GetDocument();
11760 if (doc && doc->HasFlag(NODE_IS_EDITABLE) &&
11761 realDocshell->mEditorData) {
11762 nsCOMPtr<nsIEditingSession> editSession;
11763 realDocshell->mEditorData->GetEditingSession(getter_AddRefs(editSession));
11764 PRBool jsDisabled = PR_FALSE;
11765 if (editSession &&
11766 NS_SUCCEEDED(rv = editSession->GetJsAndPluginsDisabled(&jsDisabled))) {
11767 if (firstPass) {
11768 if (jsDisabled) {
11769 // We have a docshell which has been explicitly set
11770 // to design mode, so we disallow scripts.
11771 return NS_OK;
11773 // The docshell was not explicitly set to design mode,
11774 // so it must be so because a parent was explicitly
11775 // set to design mode. We don't need to look at higher
11776 // docshells.
11777 *aResult = PR_TRUE;
11778 break;
11779 } else if (lookForParents && jsDisabled) {
11780 // If a parent was explicitly set to design mode,
11781 // we should allow script execution on the child.
11782 *aResult = PR_TRUE;
11783 break;
11785 // If the child docshell allows scripting, and the
11786 // parent is inside design mode, we don't need to look
11787 // further.
11788 *aResult = PR_TRUE;
11789 return NS_OK;
11791 NS_WARNING("The editing session does not work?");
11792 return NS_FAILED(rv) ? rv : NS_ERROR_FAILURE;
11794 if (firstPass) {
11795 // Don't be too hard on docshells on the first pass.
11796 // There may be a parent docshell which has been set
11797 // to design mode, so look for it.
11798 lookForParents = PR_TRUE;
11799 } else {
11800 // We have a docshell which disallows scripts
11801 // and is not editable, so we shouldn't allow
11802 // scripts at all.
11803 return NS_OK;
11806 } else if (lookForParents) {
11807 // The parent docshell was not explicitly set to design
11808 // mode, so js on the child docshell was disabled for
11809 // another reason. Therefore, we need to disable js.
11810 *aResult = PR_FALSE;
11811 return NS_OK;
11813 firstPass = PR_FALSE;
11815 treeItem->GetParent(getter_AddRefs(parentItem));
11816 treeItem.swap(parentItem);
11817 docshell = do_QueryInterface(treeItem);
11818 #ifdef DEBUG
11819 if (treeItem && !docshell) {
11820 NS_ERROR("cannot get a docshell from a treeItem!");
11822 #endif // DEBUG
11823 } while (treeItem && docshell);
11826 return NS_OK;