summaryrefslogtreecommitdiffstats
path: root/docshell/base
diff options
context:
space:
mode:
Diffstat (limited to 'docshell/base')
-rw-r--r--docshell/base/IHistory.h144
-rw-r--r--docshell/base/LoadContext.cpp229
-rw-r--r--docshell/base/LoadContext.h123
-rw-r--r--docshell/base/SerializedLoadContext.cpp77
-rw-r--r--docshell/base/SerializedLoadContext.h92
-rw-r--r--docshell/base/crashtests/1257730-1.html25
-rw-r--r--docshell/base/crashtests/1331295.html25
-rw-r--r--docshell/base/crashtests/1341657.html14
-rw-r--r--docshell/base/crashtests/369126-1.html16
-rw-r--r--docshell/base/crashtests/403574-1.xhtml23
-rw-r--r--docshell/base/crashtests/40929-1-inner.html14
-rw-r--r--docshell/base/crashtests/40929-1.html6
-rw-r--r--docshell/base/crashtests/430124-1.html5
-rw-r--r--docshell/base/crashtests/430628-1.html8
-rw-r--r--docshell/base/crashtests/432114-1.html8
-rw-r--r--docshell/base/crashtests/432114-2.html16
-rw-r--r--docshell/base/crashtests/436900-1-inner.html21
-rw-r--r--docshell/base/crashtests/436900-1.html8
-rw-r--r--docshell/base/crashtests/436900-2-inner.html21
-rw-r--r--docshell/base/crashtests/436900-2.html8
-rw-r--r--docshell/base/crashtests/500328-1.html17
-rw-r--r--docshell/base/crashtests/514779-1.xhtml9
-rw-r--r--docshell/base/crashtests/614499-1.html20
-rw-r--r--docshell/base/crashtests/678872-1.html36
-rw-r--r--docshell/base/crashtests/914521.html33
-rw-r--r--docshell/base/crashtests/crashtests.list16
-rw-r--r--docshell/base/moz.build88
-rw-r--r--docshell/base/nsAboutRedirector.cpp224
-rw-r--r--docshell/base/nsAboutRedirector.h32
-rw-r--r--docshell/base/nsCDefaultURIFixup.idl13
-rw-r--r--docshell/base/nsDSURIContentListener.cpp539
-rw-r--r--docshell/base/nsDSURIContentListener.h74
-rw-r--r--docshell/base/nsDefaultURIFixup.cpp1152
-rw-r--r--docshell/base/nsDefaultURIFixup.h69
-rw-r--r--docshell/base/nsDocShell.cpp14853
-rw-r--r--docshell/base/nsDocShell.h1083
-rw-r--r--docshell/base/nsDocShellEditorData.cpp192
-rw-r--r--docshell/base/nsDocShellEditorData.h63
-rw-r--r--docshell/base/nsDocShellEnumerator.cpp205
-rw-r--r--docshell/base/nsDocShellEnumerator.h107
-rw-r--r--docshell/base/nsDocShellLoadInfo.cpp297
-rw-r--r--docshell/base/nsDocShellLoadInfo.h53
-rw-r--r--docshell/base/nsDocShellLoadTypes.h117
-rw-r--r--docshell/base/nsDocShellTransferableHooks.cpp54
-rw-r--r--docshell/base/nsDocShellTransferableHooks.h28
-rw-r--r--docshell/base/nsDownloadHistory.cpp52
-rw-r--r--docshell/base/nsDownloadHistory.h28
-rw-r--r--docshell/base/nsIClipboardCommands.idl111
-rw-r--r--docshell/base/nsIContentViewer.idl280
-rw-r--r--docshell/base/nsIContentViewerContainer.idl20
-rw-r--r--docshell/base/nsIContentViewerEdit.idl36
-rw-r--r--docshell/base/nsIContentViewerFile.idl31
-rw-r--r--docshell/base/nsIDocCharset.idl19
-rw-r--r--docshell/base/nsIDocShell.idl1186
-rw-r--r--docshell/base/nsIDocShellLoadInfo.idl125
-rw-r--r--docshell/base/nsIDocShellTreeItem.idl184
-rw-r--r--docshell/base/nsIDocShellTreeOwner.idl109
-rw-r--r--docshell/base/nsIDocumentLoaderFactory.idl46
-rw-r--r--docshell/base/nsIDownloadHistory.idl58
-rw-r--r--docshell/base/nsIGlobalHistory2.idl59
-rw-r--r--docshell/base/nsILinkHandler.h94
-rw-r--r--docshell/base/nsILoadContext.idl155
-rw-r--r--docshell/base/nsIPrivacyTransitionObserver.idl11
-rw-r--r--docshell/base/nsIReflowObserver.idl31
-rw-r--r--docshell/base/nsIRefreshURI.idl91
-rw-r--r--docshell/base/nsIScrollObserver.h42
-rw-r--r--docshell/base/nsIScrollable.idl55
-rw-r--r--docshell/base/nsITextScroll.idl33
-rw-r--r--docshell/base/nsIURIFixup.idl166
-rw-r--r--docshell/base/nsIWebNavigation.idl367
-rw-r--r--docshell/base/nsIWebNavigationInfo.idl63
-rw-r--r--docshell/base/nsIWebPageDescriptor.idl30
-rw-r--r--docshell/base/nsIWebShellServices.h34
-rw-r--r--docshell/base/nsWebNavigationInfo.cpp134
-rw-r--r--docshell/base/nsWebNavigationInfo.h42
-rw-r--r--docshell/base/timeline/AbstractTimelineMarker.cpp90
-rw-r--r--docshell/base/timeline/AbstractTimelineMarker.h73
-rw-r--r--docshell/base/timeline/AutoGlobalTimelineMarker.cpp43
-rw-r--r--docshell/base/timeline/AutoGlobalTimelineMarker.h51
-rw-r--r--docshell/base/timeline/AutoTimelineMarker.cpp51
-rw-r--r--docshell/base/timeline/AutoTimelineMarker.h51
-rw-r--r--docshell/base/timeline/CompositeTimelineMarker.h33
-rw-r--r--docshell/base/timeline/ConsoleTimelineMarker.h58
-rw-r--r--docshell/base/timeline/DocLoadingTimelineMarker.h40
-rw-r--r--docshell/base/timeline/EventTimelineMarker.h43
-rw-r--r--docshell/base/timeline/JavascriptTimelineMarker.h95
-rw-r--r--docshell/base/timeline/LayerTimelineMarker.h43
-rw-r--r--docshell/base/timeline/MarkersStorage.cpp29
-rw-r--r--docshell/base/timeline/MarkersStorage.h48
-rw-r--r--docshell/base/timeline/MessagePortTimelineMarker.h47
-rw-r--r--docshell/base/timeline/ObservedDocShell.cpp171
-rw-r--r--docshell/base/timeline/ObservedDocShell.h52
-rw-r--r--docshell/base/timeline/RestyleTimelineMarker.h42
-rw-r--r--docshell/base/timeline/TimelineConsumers.cpp312
-rw-r--r--docshell/base/timeline/TimelineConsumers.h135
-rw-r--r--docshell/base/timeline/TimelineMarker.cpp71
-rw-r--r--docshell/base/timeline/TimelineMarker.h49
-rw-r--r--docshell/base/timeline/TimelineMarkerEnums.h26
-rw-r--r--docshell/base/timeline/TimestampTimelineMarker.h38
-rw-r--r--docshell/base/timeline/WorkerTimelineMarker.h46
-rw-r--r--docshell/base/timeline/moz.build42
-rw-r--r--docshell/base/timeline/readme.md97
102 files changed, 25825 insertions, 0 deletions
diff --git a/docshell/base/IHistory.h b/docshell/base/IHistory.h
new file mode 100644
index 000000000..b1f5bb983
--- /dev/null
+++ b/docshell/base/IHistory.h
@@ -0,0 +1,144 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_IHistory_h_
+#define mozilla_IHistory_h_
+
+#include "nsISupports.h"
+
+class nsIURI;
+
+namespace mozilla {
+
+namespace dom {
+class Link;
+} // namespace dom
+
+// 0057c9d3-b98e-4933-bdc5-0275d06705e1
+#define IHISTORY_IID \
+ {0x0057c9d3, 0xb98e, 0x4933, {0xbd, 0xc5, 0x02, 0x75, 0xd0, 0x67, 0x05, 0xe1}}
+
+class IHistory : public nsISupports
+{
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(IHISTORY_IID)
+
+ /**
+ * Registers the Link for notifications about the visited-ness of aURI.
+ * Consumers should assume that the URI is unvisited after calling this, and
+ * they will be notified if that state (unvisited) changes by having
+ * SetLinkState called on themselves. This function is guaranteed to run to
+ * completion before aLink is notified. After the node is notified, it will
+ * be unregistered.
+ *
+ * @note SetLinkState must not call RegisterVisitedCallback or
+ * UnregisterVisitedCallback.
+ *
+ * @pre aURI must not be null.
+ * @pre aLink may be null only in the parent (chrome) process.
+ *
+ * @param aURI
+ * The URI to check.
+ * @param aLink
+ * The link to update whenever the history status changes. The
+ * implementation will only hold onto a raw pointer, so if this
+ * object should be destroyed, be sure to call
+ * UnregisterVistedCallback first.
+ */
+ NS_IMETHOD RegisterVisitedCallback(nsIURI* aURI, dom::Link* aLink) = 0;
+
+ /**
+ * Unregisters a previously registered Link object. This must be called
+ * before destroying the registered object.
+ *
+ * @pre aURI must not be null.
+ * @pre aLink must not be null.
+ *
+ * @param aURI
+ * The URI that aLink was registered for.
+ * @param aLink
+ * The link object to unregister for aURI.
+ */
+ NS_IMETHOD UnregisterVisitedCallback(nsIURI* aURI, dom::Link* aLink) = 0;
+
+ enum VisitFlags
+ {
+ /**
+ * Indicates whether the URI was loaded in a top-level window.
+ */
+ TOP_LEVEL = 1 << 0,
+ /**
+ * Indicates whether the URI was loaded as part of a permanent redirect.
+ */
+ REDIRECT_PERMANENT = 1 << 1,
+ /**
+ * Indicates whether the URI was loaded as part of a temporary redirect.
+ */
+ REDIRECT_TEMPORARY = 1 << 2,
+ /**
+ * Indicates the URI is redirecting (Response code 3xx).
+ */
+ REDIRECT_SOURCE = 1 << 3,
+ /**
+ * Indicates the URI caused an error that is unlikely fixable by a
+ * retry, like a not found or unfetchable page.
+ */
+ UNRECOVERABLE_ERROR = 1 << 4
+ };
+
+ /**
+ * Adds a history visit for the URI.
+ *
+ * @pre aURI must not be null.
+ *
+ * @param aURI
+ * The URI of the page being visited.
+ * @param aLastVisitedURI
+ * The URI of the last visit in the chain.
+ * @param aFlags
+ * The VisitFlags describing this visit.
+ */
+ NS_IMETHOD VisitURI(nsIURI* aURI,
+ nsIURI* aLastVisitedURI,
+ uint32_t aFlags) = 0;
+
+ /**
+ * Set the title of the URI.
+ *
+ * @pre aURI must not be null.
+ *
+ * @param aURI
+ * The URI to set the title for.
+ * @param aTitle
+ * The title string.
+ */
+ NS_IMETHOD SetURITitle(nsIURI* aURI, const nsAString& aTitle) = 0;
+
+ /**
+ * Notifies about the visited status of a given URI.
+ *
+ * @param aURI
+ * The URI to notify about.
+ */
+ NS_IMETHOD NotifyVisited(nsIURI* aURI) = 0;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(IHistory, IHISTORY_IID)
+
+#define NS_DECL_IHISTORY \
+ NS_IMETHOD RegisterVisitedCallback(nsIURI* aURI, \
+ mozilla::dom::Link* aContent) override; \
+ NS_IMETHOD UnregisterVisitedCallback(nsIURI* aURI, \
+ mozilla::dom::Link* aContent) override; \
+ NS_IMETHOD VisitURI(nsIURI* aURI, \
+ nsIURI* aLastVisitedURI, \
+ uint32_t aFlags) override; \
+ NS_IMETHOD SetURITitle(nsIURI* aURI, const nsAString& aTitle) override; \
+ NS_IMETHOD NotifyVisited(nsIURI* aURI) override;
+
+} // namespace mozilla
+
+#endif // mozilla_IHistory_h_
diff --git a/docshell/base/LoadContext.cpp b/docshell/base/LoadContext.cpp
new file mode 100644
index 000000000..c4095a951
--- /dev/null
+++ b/docshell/base/LoadContext.cpp
@@ -0,0 +1,229 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/Assertions.h"
+#include "mozilla/BasePrincipal.h"
+#include "mozilla/LoadContext.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/dom/ScriptSettings.h" // for AutoJSAPI
+#include "nsContentUtils.h"
+#include "xpcpublic.h"
+
+bool
+nsILoadContext::GetOriginAttributes(mozilla::DocShellOriginAttributes& aAttrs)
+{
+ mozilla::dom::AutoJSAPI jsapi;
+ bool ok = jsapi.Init(xpc::PrivilegedJunkScope());
+ NS_ENSURE_TRUE(ok, false);
+ JS::Rooted<JS::Value> v(jsapi.cx());
+ nsresult rv = GetOriginAttributes(&v);
+ NS_ENSURE_SUCCESS(rv, false);
+ NS_ENSURE_TRUE(v.isObject(), false);
+ JS::Rooted<JSObject*> obj(jsapi.cx(), &v.toObject());
+
+ // If we're JS-implemented, the object will be left in a different (System-Principaled)
+ // scope, so we may need to enter its compartment.
+ MOZ_ASSERT(nsContentUtils::IsSystemPrincipal(nsContentUtils::ObjectPrincipal(obj)));
+ JSAutoCompartment ac(jsapi.cx(), obj);
+
+ mozilla::DocShellOriginAttributes attrs;
+ ok = attrs.Init(jsapi.cx(), v);
+ NS_ENSURE_TRUE(ok, false);
+ aAttrs = attrs;
+ return true;
+}
+
+namespace mozilla {
+
+NS_IMPL_ISUPPORTS(LoadContext, nsILoadContext, nsIInterfaceRequestor)
+
+LoadContext::LoadContext(nsIPrincipal* aPrincipal,
+ nsILoadContext* aOptionalBase)
+ : mTopFrameElement(nullptr)
+ , mNestedFrameId(0)
+ , mIsContent(true)
+ , mUseRemoteTabs(false)
+#ifdef DEBUG
+ , mIsNotNull(true)
+#endif
+{
+ PrincipalOriginAttributes poa = BasePrincipal::Cast(aPrincipal)->OriginAttributesRef();
+ mOriginAttributes.InheritFromDocToChildDocShell(poa);
+ if (!aOptionalBase) {
+ return;
+ }
+
+ MOZ_ALWAYS_SUCCEEDS(aOptionalBase->GetIsContent(&mIsContent));
+ MOZ_ALWAYS_SUCCEEDS(aOptionalBase->GetUseRemoteTabs(&mUseRemoteTabs));
+}
+
+//-----------------------------------------------------------------------------
+// LoadContext::nsILoadContext
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+LoadContext::GetAssociatedWindow(mozIDOMWindowProxy**)
+{
+ MOZ_ASSERT(mIsNotNull);
+
+ // can't support this in the parent process
+ return NS_ERROR_UNEXPECTED;
+}
+
+NS_IMETHODIMP
+LoadContext::GetTopWindow(mozIDOMWindowProxy**)
+{
+ MOZ_ASSERT(mIsNotNull);
+
+ // can't support this in the parent process
+ return NS_ERROR_UNEXPECTED;
+}
+
+NS_IMETHODIMP
+LoadContext::GetTopFrameElement(nsIDOMElement** aElement)
+{
+ nsCOMPtr<nsIDOMElement> element = do_QueryReferent(mTopFrameElement);
+ element.forget(aElement);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+LoadContext::GetNestedFrameId(uint64_t* aId)
+{
+ NS_ENSURE_ARG(aId);
+ *aId = mNestedFrameId;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+LoadContext::GetIsContent(bool* aIsContent)
+{
+ MOZ_ASSERT(mIsNotNull);
+
+ NS_ENSURE_ARG_POINTER(aIsContent);
+
+ *aIsContent = mIsContent;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+LoadContext::GetUsePrivateBrowsing(bool* aUsePrivateBrowsing)
+{
+ MOZ_ASSERT(mIsNotNull);
+
+ NS_ENSURE_ARG_POINTER(aUsePrivateBrowsing);
+
+ *aUsePrivateBrowsing = mOriginAttributes.mPrivateBrowsingId > 0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+LoadContext::SetUsePrivateBrowsing(bool aUsePrivateBrowsing)
+{
+ MOZ_ASSERT(mIsNotNull);
+
+ // We shouldn't need this on parent...
+ return NS_ERROR_UNEXPECTED;
+}
+
+NS_IMETHODIMP
+LoadContext::SetPrivateBrowsing(bool aUsePrivateBrowsing)
+{
+ MOZ_ASSERT(mIsNotNull);
+
+ // We shouldn't need this on parent...
+ return NS_ERROR_UNEXPECTED;
+}
+
+NS_IMETHODIMP
+LoadContext::GetUseRemoteTabs(bool* aUseRemoteTabs)
+{
+ MOZ_ASSERT(mIsNotNull);
+
+ NS_ENSURE_ARG_POINTER(aUseRemoteTabs);
+
+ *aUseRemoteTabs = mUseRemoteTabs;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+LoadContext::SetRemoteTabs(bool aUseRemoteTabs)
+{
+ MOZ_ASSERT(mIsNotNull);
+
+ // We shouldn't need this on parent...
+ return NS_ERROR_UNEXPECTED;
+}
+
+NS_IMETHODIMP
+LoadContext::GetIsInIsolatedMozBrowserElement(bool* aIsInIsolatedMozBrowserElement)
+{
+ MOZ_ASSERT(mIsNotNull);
+
+ NS_ENSURE_ARG_POINTER(aIsInIsolatedMozBrowserElement);
+
+ *aIsInIsolatedMozBrowserElement = mOriginAttributes.mInIsolatedMozBrowser;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+LoadContext::GetAppId(uint32_t* aAppId)
+{
+ MOZ_ASSERT(mIsNotNull);
+
+ NS_ENSURE_ARG_POINTER(aAppId);
+
+ *aAppId = mOriginAttributes.mAppId;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+LoadContext::GetOriginAttributes(JS::MutableHandleValue aAttrs)
+{
+ JSContext* cx = nsContentUtils::GetCurrentJSContext();
+ MOZ_ASSERT(cx);
+
+ bool ok = ToJSValue(cx, mOriginAttributes, aAttrs);
+ NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+LoadContext::IsTrackingProtectionOn(bool* aIsTrackingProtectionOn)
+{
+ MOZ_ASSERT(mIsNotNull);
+
+ if (Preferences::GetBool("privacy.trackingprotection.enabled", false)) {
+ *aIsTrackingProtectionOn = true;
+ } else if ((mOriginAttributes.mPrivateBrowsingId > 0) &&
+ Preferences::GetBool("privacy.trackingprotection.pbmode.enabled", false)) {
+ *aIsTrackingProtectionOn = true;
+ } else {
+ *aIsTrackingProtectionOn = false;
+ }
+
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// LoadContext::nsIInterfaceRequestor
+//-----------------------------------------------------------------------------
+NS_IMETHODIMP
+LoadContext::GetInterface(const nsIID& aIID, void** aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+ *aResult = nullptr;
+
+ if (aIID.Equals(NS_GET_IID(nsILoadContext))) {
+ *aResult = static_cast<nsILoadContext*>(this);
+ NS_ADDREF_THIS();
+ return NS_OK;
+ }
+
+ return NS_NOINTERFACE;
+}
+
+} // namespace mozilla
diff --git a/docshell/base/LoadContext.h b/docshell/base/LoadContext.h
new file mode 100644
index 000000000..966e7b47f
--- /dev/null
+++ b/docshell/base/LoadContext.h
@@ -0,0 +1,123 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef LoadContext_h
+#define LoadContext_h
+
+#include "SerializedLoadContext.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/BasePrincipal.h"
+#include "nsIWeakReferenceUtils.h"
+#include "mozilla/dom/Element.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsILoadContext.h"
+
+namespace mozilla {
+
+/**
+ * Class that provides nsILoadContext info in Parent process. Typically copied
+ * from Child via SerializedLoadContext.
+ *
+ * Note: this is not the "normal" or "original" nsILoadContext. That is
+ * typically provided by nsDocShell. This is only used when the original
+ * docshell is in a different process and we need to copy certain values from
+ * it.
+ *
+ * Note: we also generate a new nsILoadContext using LoadContext(uint32_t aAppId)
+ * to separate the safebrowsing cookie.
+ */
+
+class LoadContext final
+ : public nsILoadContext
+ , public nsIInterfaceRequestor
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSILOADCONTEXT
+ NS_DECL_NSIINTERFACEREQUESTOR
+
+ // appId/inIsolatedMozBrowser arguments override those in SerializedLoadContext
+ // provided by child process.
+ LoadContext(const IPC::SerializedLoadContext& aToCopy,
+ dom::Element* aTopFrameElement,
+ DocShellOriginAttributes& aAttrs)
+ : mTopFrameElement(do_GetWeakReference(aTopFrameElement))
+ , mNestedFrameId(0)
+ , mIsContent(aToCopy.mIsContent)
+ , mUseRemoteTabs(aToCopy.mUseRemoteTabs)
+ , mOriginAttributes(aAttrs)
+#ifdef DEBUG
+ , mIsNotNull(aToCopy.mIsNotNull)
+#endif
+ {
+ }
+
+ // appId/inIsolatedMozBrowser arguments override those in SerializedLoadContext
+ // provided by child process.
+ LoadContext(const IPC::SerializedLoadContext& aToCopy,
+ uint64_t aNestedFrameId,
+ DocShellOriginAttributes& aAttrs)
+ : mTopFrameElement(nullptr)
+ , mNestedFrameId(aNestedFrameId)
+ , mIsContent(aToCopy.mIsContent)
+ , mUseRemoteTabs(aToCopy.mUseRemoteTabs)
+ , mOriginAttributes(aAttrs)
+#ifdef DEBUG
+ , mIsNotNull(aToCopy.mIsNotNull)
+#endif
+ {
+ }
+
+ LoadContext(dom::Element* aTopFrameElement,
+ bool aIsContent,
+ bool aUsePrivateBrowsing,
+ bool aUseRemoteTabs,
+ const DocShellOriginAttributes& aAttrs)
+ : mTopFrameElement(do_GetWeakReference(aTopFrameElement))
+ , mNestedFrameId(0)
+ , mIsContent(aIsContent)
+ , mUseRemoteTabs(aUseRemoteTabs)
+ , mOriginAttributes(aAttrs)
+#ifdef DEBUG
+ , mIsNotNull(true)
+#endif
+ {
+ }
+
+ // Constructor taking reserved origin attributes.
+ explicit LoadContext(DocShellOriginAttributes& aAttrs)
+ : mTopFrameElement(nullptr)
+ , mNestedFrameId(0)
+ , mIsContent(false)
+ , mUseRemoteTabs(false)
+ , mOriginAttributes(aAttrs)
+#ifdef DEBUG
+ , mIsNotNull(true)
+#endif
+ {
+ }
+
+ // Constructor for creating a LoadContext with a given principal's appId and
+ // browser flag.
+ explicit LoadContext(nsIPrincipal* aPrincipal,
+ nsILoadContext* aOptionalBase = nullptr);
+
+private:
+ ~LoadContext() {}
+
+ nsWeakPtr mTopFrameElement;
+ uint64_t mNestedFrameId;
+ bool mIsContent;
+ bool mUseRemoteTabs;
+ DocShellOriginAttributes mOriginAttributes;
+#ifdef DEBUG
+ bool mIsNotNull;
+#endif
+};
+
+} // namespace mozilla
+
+#endif // LoadContext_h
diff --git a/docshell/base/SerializedLoadContext.cpp b/docshell/base/SerializedLoadContext.cpp
new file mode 100644
index 000000000..b8e3eb929
--- /dev/null
+++ b/docshell/base/SerializedLoadContext.cpp
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SerializedLoadContext.h"
+#include "nsNetUtil.h"
+#include "nsIChannel.h"
+#include "nsIPrivateBrowsingChannel.h"
+#include "nsIWebSocketChannel.h"
+
+namespace IPC {
+
+SerializedLoadContext::SerializedLoadContext(nsILoadContext* aLoadContext)
+{
+ Init(aLoadContext);
+}
+
+SerializedLoadContext::SerializedLoadContext(nsIChannel* aChannel)
+{
+ if (!aChannel) {
+ Init(nullptr);
+ return;
+ }
+
+ nsCOMPtr<nsILoadContext> loadContext;
+ NS_QueryNotificationCallbacks(aChannel, loadContext);
+ Init(loadContext);
+
+ if (!loadContext) {
+ // Attempt to retrieve the private bit from the channel if it has been
+ // overriden.
+ bool isPrivate = false;
+ bool isOverriden = false;
+ nsCOMPtr<nsIPrivateBrowsingChannel> pbChannel = do_QueryInterface(aChannel);
+ if (pbChannel &&
+ NS_SUCCEEDED(pbChannel->IsPrivateModeOverriden(&isPrivate,
+ &isOverriden)) &&
+ isOverriden) {
+ mIsPrivateBitValid = true;
+ }
+ mOriginAttributes.SyncAttributesWithPrivateBrowsing(isPrivate);
+ }
+}
+
+SerializedLoadContext::SerializedLoadContext(nsIWebSocketChannel* aChannel)
+{
+ nsCOMPtr<nsILoadContext> loadContext;
+ if (aChannel) {
+ NS_QueryNotificationCallbacks(aChannel, loadContext);
+ }
+ Init(loadContext);
+}
+
+void
+SerializedLoadContext::Init(nsILoadContext* aLoadContext)
+{
+ if (aLoadContext) {
+ mIsNotNull = true;
+ mIsPrivateBitValid = true;
+ aLoadContext->GetIsContent(&mIsContent);
+ aLoadContext->GetUseRemoteTabs(&mUseRemoteTabs);
+ if (!aLoadContext->GetOriginAttributes(mOriginAttributes)) {
+ NS_WARNING("GetOriginAttributes failed");
+ }
+ } else {
+ mIsNotNull = false;
+ mIsPrivateBitValid = false;
+ // none of below values really matter when mIsNotNull == false:
+ // we won't be GetInterfaced to nsILoadContext
+ mIsContent = true;
+ mUseRemoteTabs = false;
+ }
+}
+
+} // namespace IPC
diff --git a/docshell/base/SerializedLoadContext.h b/docshell/base/SerializedLoadContext.h
new file mode 100644
index 000000000..c81b39853
--- /dev/null
+++ b/docshell/base/SerializedLoadContext.h
@@ -0,0 +1,92 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef SerializedLoadContext_h
+#define SerializedLoadContext_h
+
+#include "base/basictypes.h"
+#include "ipc/IPCMessageUtils.h"
+#include "mozilla/BasePrincipal.h"
+
+class nsILoadContext;
+
+/*
+ * This file contains the IPC::SerializedLoadContext class, which is used to
+ * copy data across IPDL from Child process contexts so it is available in the
+ * Parent.
+ */
+
+class nsIChannel;
+class nsIWebSocketChannel;
+
+namespace IPC {
+
+class SerializedLoadContext
+{
+public:
+ SerializedLoadContext()
+ : mIsNotNull(false)
+ , mIsPrivateBitValid(false)
+ , mIsContent(false)
+ , mUseRemoteTabs(false)
+ {
+ Init(nullptr);
+ }
+
+ explicit SerializedLoadContext(nsILoadContext* aLoadContext);
+ explicit SerializedLoadContext(nsIChannel* aChannel);
+ explicit SerializedLoadContext(nsIWebSocketChannel* aChannel);
+
+ void Init(nsILoadContext* aLoadContext);
+
+ bool IsNotNull() const { return mIsNotNull; }
+ bool IsPrivateBitValid() const { return mIsPrivateBitValid; }
+
+ // used to indicate if child-side LoadContext * was null.
+ bool mIsNotNull;
+ // used to indicate if child-side mUsePrivateBrowsing flag is valid, even if
+ // mIsNotNull is false, i.e., child LoadContext was null.
+ bool mIsPrivateBitValid;
+ bool mIsContent;
+ bool mUseRemoteTabs;
+ mozilla::DocShellOriginAttributes mOriginAttributes;
+};
+
+// Function to serialize over IPDL
+template<>
+struct ParamTraits<SerializedLoadContext>
+{
+ typedef SerializedLoadContext paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ nsAutoCString suffix;
+ aParam.mOriginAttributes.CreateSuffix(suffix);
+
+ WriteParam(aMsg, aParam.mIsNotNull);
+ WriteParam(aMsg, aParam.mIsContent);
+ WriteParam(aMsg, aParam.mIsPrivateBitValid);
+ WriteParam(aMsg, aParam.mUseRemoteTabs);
+ WriteParam(aMsg, suffix);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ nsAutoCString suffix;
+ if (!ReadParam(aMsg, aIter, &aResult->mIsNotNull) ||
+ !ReadParam(aMsg, aIter, &aResult->mIsContent) ||
+ !ReadParam(aMsg, aIter, &aResult->mIsPrivateBitValid) ||
+ !ReadParam(aMsg, aIter, &aResult->mUseRemoteTabs) ||
+ !ReadParam(aMsg, aIter, &suffix)) {
+ return false;
+ }
+ return aResult->mOriginAttributes.PopulateFromSuffix(suffix);
+ }
+};
+
+} // namespace IPC
+
+#endif // SerializedLoadContext_h
diff --git a/docshell/base/crashtests/1257730-1.html b/docshell/base/crashtests/1257730-1.html
new file mode 100644
index 000000000..028a1adb8
--- /dev/null
+++ b/docshell/base/crashtests/1257730-1.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<!--
+user_pref("browser.send_pings", true);
+-->
+<script>
+
+function boom() {
+ var aLink = document.createElement('a');
+ document.body.appendChild(aLink);
+ aLink.ping = "ping";
+ aLink.href = "href";
+ aLink.click();
+
+ var baseElement = document.createElement('base');
+ baseElement.setAttribute("href", "javascript:void 0");
+ document.head.appendChild(baseElement);
+}
+
+</script>
+</head>
+<body onload="boom();"></body>
+</html>
diff --git a/docshell/base/crashtests/1331295.html b/docshell/base/crashtests/1331295.html
new file mode 100644
index 000000000..cdcb29e7f
--- /dev/null
+++ b/docshell/base/crashtests/1331295.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<script>
+function boom() {
+ setTimeout(function(){
+ var o=document.getElementById('b');
+ document.getElementById('a').appendChild(o.parentNode.removeChild(o));
+ },0);
+ var o=document.getElementById('c');
+ var p=document.getElementById('b');
+ p.id=[o.id, o.id=p.id][0];
+ o=document.getElementById('b');
+ o.setAttribute('sandbox', 'disc');
+ window.location.reload(true);
+}
+</script>
+</head>
+<body onload="boom();">
+<header id='a'></header>
+<output id='b'></output>
+<iframe id='c' sandbox='allow-same-origin' src='http://a'></iframe>
+</body>
+</html>
diff --git a/docshell/base/crashtests/1341657.html b/docshell/base/crashtests/1341657.html
new file mode 100644
index 000000000..852b8cc80
--- /dev/null
+++ b/docshell/base/crashtests/1341657.html
@@ -0,0 +1,14 @@
+<html>
+ <head>
+ <script>
+ o1 = document.createElement("script");
+ o2 = document.implementation.createDocument('', '', null);
+ o3 = document.createElement("iframe");
+ document.documentElement.appendChild(o3);
+ o4 = o3.contentWindow;
+ o5 = document.createTextNode('o2.adoptNode(o3); try { o4.location = "" } catch(e) {}');
+ o1.appendChild(o5);
+ document.documentElement.appendChild(o1);
+ </script>
+ </head>
+</html> \ No newline at end of file
diff --git a/docshell/base/crashtests/369126-1.html b/docshell/base/crashtests/369126-1.html
new file mode 100644
index 000000000..e9dacec30
--- /dev/null
+++ b/docshell/base/crashtests/369126-1.html
@@ -0,0 +1,16 @@
+<html class="reftest-wait">
+<head>
+<script>
+function boom()
+{
+ document.getElementById("frameset").removeChild(document.getElementById("frame"));
+ document.documentElement.removeAttribute("class");
+}
+</script>
+</head>
+
+<frameset id="frameset" onload="setTimeout(boom, 100)">
+ <frame id="frame" src="data:text/html,<body onUnload=&quot;location = 'http://www.mozilla.org/'&quot;>This frame's onunload tries to load another page.">
+</frameset>
+
+</html>
diff --git a/docshell/base/crashtests/403574-1.xhtml b/docshell/base/crashtests/403574-1.xhtml
new file mode 100644
index 000000000..cdf7d43a4
--- /dev/null
+++ b/docshell/base/crashtests/403574-1.xhtml
@@ -0,0 +1,23 @@
+<html xmlns="http://www.w3.org/1999/xhtml" class="reftest-wait">
+<head>
+
+<bindings xmlns="http://www.mozilla.org/xbl"><binding id="foo"><content>
+<frame xmlns="http://www.w3.org/1999/xhtml"><children xmlns="http://www.mozilla.org/xbl"/></frame>
+</content></binding></bindings>
+
+<script>
+
+function boom()
+{
+ document.getElementById("span").style.MozBinding = "url('#foo')";
+ document.documentElement.removeAttribute("class");
+}
+
+</script>
+</head>
+
+<body onload="setTimeout(boom, 100);">
+<span id="span"></span>
+</body>
+
+</html>
diff --git a/docshell/base/crashtests/40929-1-inner.html b/docshell/base/crashtests/40929-1-inner.html
new file mode 100644
index 000000000..313046a34
--- /dev/null
+++ b/docshell/base/crashtests/40929-1-inner.html
@@ -0,0 +1,14 @@
+<html><head><title>Infinite Loop</title></head>
+<body onLoad="initNav(); initNav();">
+
+<script language="JavaScript">
+
+function initNav() {
+ ++parent.i;
+ if (parent.i < 10)
+ window.location.href=window.location.href;
+}
+
+</script>
+
+</body></html>
diff --git a/docshell/base/crashtests/40929-1.html b/docshell/base/crashtests/40929-1.html
new file mode 100644
index 000000000..90685d9f1
--- /dev/null
+++ b/docshell/base/crashtests/40929-1.html
@@ -0,0 +1,6 @@
+<html>
+<head><title>Infinite Loop</title><script>var i=0;</script></head>
+<body>
+<iframe src="40929-1-inner.html"></iframe>
+</body>
+</html>
diff --git a/docshell/base/crashtests/430124-1.html b/docshell/base/crashtests/430124-1.html
new file mode 100644
index 000000000..8cdbc1d07
--- /dev/null
+++ b/docshell/base/crashtests/430124-1.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<html>
+<head></head>
+<body onpagehide="document.getElementById('a').focus();"><div id="a"></div></body>
+</html>
diff --git a/docshell/base/crashtests/430628-1.html b/docshell/base/crashtests/430628-1.html
new file mode 100644
index 000000000..4a68a5a01
--- /dev/null
+++ b/docshell/base/crashtests/430628-1.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body onpagehide="document.body.removeChild(document.getElementById('s'));">
+<span id="s" contenteditable="true"></span>
+</body>
+</html>
diff --git a/docshell/base/crashtests/432114-1.html b/docshell/base/crashtests/432114-1.html
new file mode 100644
index 000000000..8878d6605
--- /dev/null
+++ b/docshell/base/crashtests/432114-1.html
@@ -0,0 +1,8 @@
+<html>
+<head>
+<title>Bug - Crash [@ PL_DHashTableOperate] with DOMNodeInserted event listener removing window and frameset contenteditable</title>
+</head>
+<body>
+<iframe id="content" src="data:text/html;charset=utf-8,%3Cscript%3E%0Awindow.addEventListener%28%27DOMNodeInserted%27%2C%20function%28%29%20%7Bwindow.frameElement.parentNode.removeChild%28window.frameElement%29%3B%7D%2C%20true%29%3B%0A%3C/script%3E%0A%3Cframeset%20contenteditable%3D%22true%22%3E"></iframe>
+</body>
+</html>
diff --git a/docshell/base/crashtests/432114-2.html b/docshell/base/crashtests/432114-2.html
new file mode 100644
index 000000000..f8a101946
--- /dev/null
+++ b/docshell/base/crashtests/432114-2.html
@@ -0,0 +1,16 @@
+<html class="reftest-wait">
+<head>
+<title>testcase2 Bug 432114 – Crash [@ PL_DHashTableOperate] with DOMNodeInserted event listener removing window and frameset contenteditable</title>
+</head>
+<body>
+<script>
+ window.addEventListener("DOMNodeRemoved", function() {
+ setTimeout(function() {
+ document.documentElement.removeAttribute("class");
+ }, 0);
+ }, false);
+</script>
+<iframe id="content" src="data:application/xhtml+xml;charset=utf-8,%3Chtml%20xmlns%3D%22http%3A//www.w3.org/1999/xhtml%22%3E%0A%3Cframeset%20contenteditable%3D%22true%22/%3E%0A%3Cscript%3E%0Afunction%20doExecCommand%28%29%7B%0Adocument.execCommand%28%27formatBlock%27%2C%20false%2C%20%27p%27%29%3B%0A%7D%0AsetTimeout%28doExecCommand%2C100%29%3B%0Awindow.addEventListener%28%27DOMNodeRemoved%27%2C%20function%28%29%20%7Bwindow.frameElement.parentNode.removeChild%28window.frameElement%29%3B%7D%2C%20true%29%3B%0A%3C/script%3E%0A%3C/html%3E" style="width:1000px;height: 200px;"></iframe>
+
+</body>
+</html>
diff --git a/docshell/base/crashtests/436900-1-inner.html b/docshell/base/crashtests/436900-1-inner.html
new file mode 100644
index 000000000..6fe35ccb1
--- /dev/null
+++ b/docshell/base/crashtests/436900-1-inner.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<head>
+
+<meta http-equiv="refresh" content="0">
+
+<script language="javascript">
+
+location.hash += "+++";
+
+function done()
+{
+ parent.document.documentElement.removeAttribute("class");
+}
+
+</script>
+</head>
+<body onload="setTimeout(done, 10)">
+
+</body>
+</html>
diff --git a/docshell/base/crashtests/436900-1.html b/docshell/base/crashtests/436900-1.html
new file mode 100644
index 000000000..582d1919d
--- /dev/null
+++ b/docshell/base/crashtests/436900-1.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+</head>
+<body>
+<iframe src="436900-1-inner.html#foo"></iframe>
+</body>
+</html>
diff --git a/docshell/base/crashtests/436900-2-inner.html b/docshell/base/crashtests/436900-2-inner.html
new file mode 100644
index 000000000..ea79f75e8
--- /dev/null
+++ b/docshell/base/crashtests/436900-2-inner.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<head>
+
+<meta http-equiv="refresh" content="0">
+
+<script language="javascript" id="foo+++">
+
+location.hash += "+++";
+
+function done()
+{
+ parent.document.documentElement.removeAttribute("class");
+}
+
+</script>
+</head>
+<body onload="setTimeout(done, 10)">
+
+</body>
+</html>
diff --git a/docshell/base/crashtests/436900-2.html b/docshell/base/crashtests/436900-2.html
new file mode 100644
index 000000000..2e1f0c1de
--- /dev/null
+++ b/docshell/base/crashtests/436900-2.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+</head>
+<body>
+<iframe src="436900-2-inner.html#foo"></iframe>
+</body>
+</html>
diff --git a/docshell/base/crashtests/500328-1.html b/docshell/base/crashtests/500328-1.html
new file mode 100644
index 000000000..fd97f84ae
--- /dev/null
+++ b/docshell/base/crashtests/500328-1.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<html>
+<body onload="test();">
+<script>
+ function test() {
+ // Test that calling pushState() with a state object which calls
+ // history.back() doesn't crash. We need to make sure that there's at least
+ // one entry in the history before we do anything else.
+ history.pushState(null, "");
+
+ x = {};
+ x.toJSON = { history.back(); return "{a:1}"; };
+ history.pushState(x, "");
+ }
+</script>
+</body>
+</html>
diff --git a/docshell/base/crashtests/514779-1.xhtml b/docshell/base/crashtests/514779-1.xhtml
new file mode 100644
index 000000000..16ac3d9d6
--- /dev/null
+++ b/docshell/base/crashtests/514779-1.xhtml
@@ -0,0 +1,9 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head></head>
+
+<body onunload="document.getElementById('tbody').appendChild(document.createElementNS('http://www.w3.org/1999/xhtml', 'span'))">
+ <iframe/>
+ <tbody contenteditable="true" id="tbody">xy</tbody>
+</body>
+
+</html>
diff --git a/docshell/base/crashtests/614499-1.html b/docshell/base/crashtests/614499-1.html
new file mode 100644
index 000000000..7053a3f52
--- /dev/null
+++ b/docshell/base/crashtests/614499-1.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+
+function boom()
+{
+ var f = document.getElementById("f");
+
+ for (var i = 0; i < 50; ++i) {
+ f.contentWindow.history.pushState({}, "");
+ }
+
+ document.body.removeChild(f);
+}
+
+</script>
+</head>
+<body onload="boom();"><iframe id="f" src="data:text/html,1"></iframe></body>
+</html> \ No newline at end of file
diff --git a/docshell/base/crashtests/678872-1.html b/docshell/base/crashtests/678872-1.html
new file mode 100644
index 000000000..9853bbdae
--- /dev/null
+++ b/docshell/base/crashtests/678872-1.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+
+var f1, f2;
+
+function b1()
+{
+ f1 = document.getElementById("f1");
+ f2 = document.getElementById("f2");
+ f1.contentWindow.document.write("11");
+ f1.contentWindow.history.back();
+ setTimeout(b2, 0);
+}
+
+function b2()
+{
+ f2.contentWindow.history.forward();
+ f2.contentWindow.location.reload();
+ f1.parentNode.removeChild(f1);
+}
+
+</script>
+
+
+</script>
+</head>
+
+<body onload="setTimeout(b1, 0);">
+
+<iframe id="f1" src="data:text/html,1"></iframe>
+<iframe id="f2" src="data:text/html,2"></iframe>
+
+</body>
+</html>
diff --git a/docshell/base/crashtests/914521.html b/docshell/base/crashtests/914521.html
new file mode 100644
index 000000000..9ae18b860
--- /dev/null
+++ b/docshell/base/crashtests/914521.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+<meta charset="UTF-8">
+<script>
+
+function f()
+{
+ function spin() {
+ for (var i = 0; i < 8; ++i) {
+ var x = new XMLHttpRequest();
+ x.open('GET', 'data:text/html,' + i, false);
+ x.send();
+ }
+ }
+
+ window.addEventListener("popstate", spin, false);
+ window.close();
+ window.location = "#c";
+ finish();
+}
+
+function start()
+{
+ var html = "<script>" + f + "<\/script><body onload=f()>";
+ var win = window.open("data:text/html," + encodeURIComponent(html), null, "width=300,height=300");
+ win.finish = function() { document.documentElement.removeAttribute("class"); };
+}
+
+</script>
+</head>
+<body onload="start();"></body>
+</html>
diff --git a/docshell/base/crashtests/crashtests.list b/docshell/base/crashtests/crashtests.list
new file mode 100644
index 000000000..bb3a709b9
--- /dev/null
+++ b/docshell/base/crashtests/crashtests.list
@@ -0,0 +1,16 @@
+load 40929-1.html
+load 369126-1.html
+load 403574-1.xhtml
+load 430124-1.html
+load 430628-1.html
+load 432114-1.html
+load 432114-2.html
+load 436900-1.html
+asserts(0-1) load 436900-2.html # bug 566159
+load 500328-1.html
+load 514779-1.xhtml
+load 614499-1.html
+load 678872-1.html
+skip-if(Android) pref(dom.disable_open_during_load,false) load 914521.html
+pref(browser.send_pings,true) load 1257730-1.html
+load 1341657.html
diff --git a/docshell/base/moz.build b/docshell/base/moz.build
new file mode 100644
index 000000000..1eb04c227
--- /dev/null
+++ b/docshell/base/moz.build
@@ -0,0 +1,88 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DIRS += [
+ 'timeline',
+]
+
+XPIDL_SOURCES += [
+ 'nsCDefaultURIFixup.idl',
+ 'nsIClipboardCommands.idl',
+ 'nsIContentViewer.idl',
+ 'nsIContentViewerContainer.idl',
+ 'nsIContentViewerEdit.idl',
+ 'nsIContentViewerFile.idl',
+ 'nsIDocCharset.idl',
+ 'nsIDocShell.idl',
+ 'nsIDocShellLoadInfo.idl',
+ 'nsIDocShellTreeItem.idl',
+ 'nsIDocShellTreeOwner.idl',
+ 'nsIDocumentLoaderFactory.idl',
+ 'nsIDownloadHistory.idl',
+ 'nsIGlobalHistory2.idl',
+ 'nsILoadContext.idl',
+ 'nsIPrivacyTransitionObserver.idl',
+ 'nsIReflowObserver.idl',
+ 'nsIRefreshURI.idl',
+ 'nsIScrollable.idl',
+ 'nsITextScroll.idl',
+ 'nsIURIFixup.idl',
+ 'nsIWebNavigation.idl',
+ 'nsIWebNavigationInfo.idl',
+ 'nsIWebPageDescriptor.idl',
+]
+
+XPIDL_MODULE = 'docshell'
+
+EXPORTS += [
+ 'nsDocShellLoadTypes.h',
+ 'nsILinkHandler.h',
+ 'nsIScrollObserver.h',
+ 'nsIWebShellServices.h',
+ 'SerializedLoadContext.h',
+]
+
+EXPORTS.mozilla += [
+ 'IHistory.h',
+ 'LoadContext.h',
+]
+
+UNIFIED_SOURCES += [
+ 'LoadContext.cpp',
+ 'nsAboutRedirector.cpp',
+ 'nsDefaultURIFixup.cpp',
+ 'nsDocShell.cpp',
+ 'nsDocShellEditorData.cpp',
+ 'nsDocShellEnumerator.cpp',
+ 'nsDocShellLoadInfo.cpp',
+ 'nsDocShellTransferableHooks.cpp',
+ 'nsDownloadHistory.cpp',
+ 'nsDSURIContentListener.cpp',
+ 'nsWebNavigationInfo.cpp',
+ 'SerializedLoadContext.cpp',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'
+LOCAL_INCLUDES += [
+ '/docshell/shistory',
+ '/dom/base',
+ '/layout/base',
+ '/layout/generic',
+ '/layout/xul',
+ '/netwerk/protocol/viewsource',
+ '/tools/profiler',
+]
+
+if CONFIG['MOZ_TOOLKIT_SEARCH']:
+ DEFINES['MOZ_TOOLKIT_SEARCH'] = True
+
+if CONFIG['MOZ_DEVTOOLS'] == 'all':
+ DEFINES['MOZ_DEVTOOLS_ALL'] = True
+
+if CONFIG['GNU_CXX']:
+ CXXFLAGS += ['-Wno-error=shadow']
diff --git a/docshell/base/nsAboutRedirector.cpp b/docshell/base/nsAboutRedirector.cpp
new file mode 100644
index 000000000..f300e0ce2
--- /dev/null
+++ b/docshell/base/nsAboutRedirector.cpp
@@ -0,0 +1,224 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsAboutRedirector.h"
+#include "nsNetUtil.h"
+#include "nsAboutProtocolUtils.h"
+#include "mozilla/ArrayUtils.h"
+#include "nsIProtocolHandler.h"
+
+NS_IMPL_ISUPPORTS(nsAboutRedirector, nsIAboutModule)
+
+struct RedirEntry
+{
+ const char* id;
+ const char* url;
+ uint32_t flags;
+};
+
+/*
+ Entries which do not have URI_SAFE_FOR_UNTRUSTED_CONTENT will run with chrome
+ privileges. This is potentially dangerous. Please use
+ URI_SAFE_FOR_UNTRUSTED_CONTENT in the third argument to each map item below
+ unless your about: page really needs chrome privileges. Security review is
+ required before adding new map entries without
+ URI_SAFE_FOR_UNTRUSTED_CONTENT. Also note, however, that adding
+ URI_SAFE_FOR_UNTRUSTED_CONTENT will allow random web sites to link to that
+ URI. Perhaps we should separate the two concepts out...
+ */
+static RedirEntry kRedirMap[] = {
+ {
+ "", "chrome://global/content/about.xhtml",
+ nsIAboutModule::ALLOW_SCRIPT
+ },
+ { "about", "chrome://global/content/aboutAbout.xhtml", 0 },
+ {
+ "addons", "chrome://mozapps/content/extensions/extensions.xul",
+ nsIAboutModule::ALLOW_SCRIPT
+ },
+ {
+ "buildconfig", "chrome://global/content/buildconfig.html",
+ nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT
+ },
+ {
+ "checkerboard", "chrome://global/content/aboutCheckerboard.xhtml",
+ nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
+ nsIAboutModule::ALLOW_SCRIPT
+ },
+ { "config", "chrome://global/content/config.xul", 0 },
+#ifdef MOZ_CRASHREPORTER
+ { "crashes", "chrome://global/content/crashes.xhtml", 0 },
+#endif
+ {
+ "credits", "https://www.mozilla.org/credits/",
+ nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT
+ },
+#ifdef MOZ_DEVTOOLS_ALL
+ {
+ "debugging", "chrome://devtools/content/aboutdebugging/aboutdebugging.xhtml",
+ nsIAboutModule::ALLOW_SCRIPT
+ },
+#endif
+ {
+ "license", "chrome://global/content/license.html",
+ nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
+ nsIAboutModule::MAKE_LINKABLE
+ },
+ {
+ "logo", "chrome://branding/content/about.png",
+ nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
+ // Linkable for testing reasons.
+ nsIAboutModule::MAKE_LINKABLE
+ },
+ {
+ "memory", "chrome://global/content/aboutMemory.xhtml",
+ nsIAboutModule::ALLOW_SCRIPT
+ },
+ {
+ "mozilla", "chrome://global/content/mozilla.xhtml",
+ nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT
+ },
+ {
+ "neterror", "chrome://global/content/netError.xhtml",
+ nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
+ nsIAboutModule::URI_CAN_LOAD_IN_CHILD |
+ nsIAboutModule::ALLOW_SCRIPT |
+ nsIAboutModule::HIDE_FROM_ABOUTABOUT
+ },
+ {
+ "networking", "chrome://global/content/aboutNetworking.xhtml",
+ nsIAboutModule::ALLOW_SCRIPT
+ },
+ {
+ "newaddon", "chrome://mozapps/content/extensions/newaddon.xul",
+ nsIAboutModule::ALLOW_SCRIPT |
+ nsIAboutModule::HIDE_FROM_ABOUTABOUT
+ },
+ {
+ "performance", "chrome://global/content/aboutPerformance.xhtml",
+ nsIAboutModule::ALLOW_SCRIPT
+ },
+ {
+ "plugins", "chrome://global/content/plugins.html",
+ nsIAboutModule::URI_MUST_LOAD_IN_CHILD
+ },
+ {
+ "serviceworkers", "chrome://global/content/aboutServiceWorkers.xhtml",
+ nsIAboutModule::URI_CAN_LOAD_IN_CHILD |
+ nsIAboutModule::URI_MUST_LOAD_IN_CHILD |
+ nsIAboutModule::ALLOW_SCRIPT
+ },
+#ifndef ANDROID
+ {
+ "profiles", "chrome://global/content/aboutProfiles.xhtml",
+ nsIAboutModule::ALLOW_SCRIPT
+ },
+#endif
+ // about:srcdoc is unresolvable by specification. It is included here
+ // because the security manager would disallow srcdoc iframes otherwise.
+ {
+ "srcdoc", "about:blank",
+ nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
+ nsIAboutModule::HIDE_FROM_ABOUTABOUT |
+ // Needs to be linkable so content can touch its own srcdoc frames
+ nsIAboutModule::MAKE_LINKABLE |
+ nsIAboutModule::URI_CAN_LOAD_IN_CHILD
+ },
+ {
+ "support", "chrome://global/content/aboutSupport.xhtml",
+ nsIAboutModule::ALLOW_SCRIPT
+ },
+ {
+ "telemetry", "chrome://global/content/aboutTelemetry.xhtml",
+ nsIAboutModule::ALLOW_SCRIPT
+ },
+ {
+ "webrtc", "chrome://global/content/aboutwebrtc/aboutWebrtc.html",
+ nsIAboutModule::ALLOW_SCRIPT
+ }
+};
+static const int kRedirTotal = mozilla::ArrayLength(kRedirMap);
+
+NS_IMETHODIMP
+nsAboutRedirector::NewChannel(nsIURI* aURI,
+ nsILoadInfo* aLoadInfo,
+ nsIChannel** aResult)
+{
+ NS_ENSURE_ARG_POINTER(aURI);
+ NS_ASSERTION(aResult, "must not be null");
+
+ nsAutoCString path;
+ nsresult rv = NS_GetAboutModuleName(aURI, path);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ for (int i = 0; i < kRedirTotal; i++) {
+ if (!strcmp(path.get(), kRedirMap[i].id)) {
+ nsCOMPtr<nsIChannel> tempChannel;
+ nsCOMPtr<nsIURI> tempURI;
+ rv = NS_NewURI(getter_AddRefs(tempURI), kRedirMap[i].url);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // If tempURI links to an external URI (i.e. something other than
+ // chrome:// or resource://) then set the LOAD_REPLACE flag on the
+ // channel which forces the channel owner to reflect the displayed
+ // URL rather then being the systemPrincipal.
+ bool isUIResource = false;
+ rv = NS_URIChainHasFlags(tempURI, nsIProtocolHandler::URI_IS_UI_RESOURCE,
+ &isUIResource);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsLoadFlags loadFlags =
+ isUIResource ? static_cast<nsLoadFlags>(nsIChannel::LOAD_NORMAL)
+ : static_cast<nsLoadFlags>(nsIChannel::LOAD_REPLACE);
+
+ rv = NS_NewChannelInternal(getter_AddRefs(tempChannel),
+ tempURI,
+ aLoadInfo,
+ nullptr, // aLoadGroup
+ nullptr, // aCallbacks
+ loadFlags);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ tempChannel->SetOriginalURI(aURI);
+
+ tempChannel.forget(aResult);
+ return rv;
+ }
+ }
+
+ NS_ERROR("nsAboutRedirector called for unknown case");
+ return NS_ERROR_ILLEGAL_VALUE;
+}
+
+NS_IMETHODIMP
+nsAboutRedirector::GetURIFlags(nsIURI* aURI, uint32_t* aResult)
+{
+ NS_ENSURE_ARG_POINTER(aURI);
+
+ nsAutoCString name;
+ nsresult rv = NS_GetAboutModuleName(aURI, name);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ for (int i = 0; i < kRedirTotal; i++) {
+ if (name.EqualsASCII(kRedirMap[i].id)) {
+ *aResult = kRedirMap[i].flags;
+ return NS_OK;
+ }
+ }
+
+ NS_ERROR("nsAboutRedirector called for unknown case");
+ return NS_ERROR_ILLEGAL_VALUE;
+}
+
+nsresult
+nsAboutRedirector::Create(nsISupports* aOuter, REFNSIID aIID, void** aResult)
+{
+ RefPtr<nsAboutRedirector> about = new nsAboutRedirector();
+ return about->QueryInterface(aIID, aResult);
+}
diff --git a/docshell/base/nsAboutRedirector.h b/docshell/base/nsAboutRedirector.h
new file mode 100644
index 000000000..f8e6b2558
--- /dev/null
+++ b/docshell/base/nsAboutRedirector.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsAboutRedirector_h__
+#define nsAboutRedirector_h__
+
+#include "nsIAboutModule.h"
+
+class nsAboutRedirector : public nsIAboutModule
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ NS_DECL_NSIABOUTMODULE
+
+ nsAboutRedirector() {}
+
+ static nsresult Create(nsISupports* aOuter, REFNSIID aIID, void** aResult);
+
+protected:
+ virtual ~nsAboutRedirector() {}
+};
+
+/* 56ebedd4-6ccf-48e8-bdae-adc77f044567 */
+#define NS_ABOUT_REDIRECTOR_MODULE_CID \
+{ 0x56ebedd4, 0x6ccf, 0x48e8, \
+ { 0xbd, 0xae, 0xad, 0xc7, 0x7f, 0x04, 0x45, 0x67 } }
+
+#endif // nsAboutRedirector_h__
diff --git a/docshell/base/nsCDefaultURIFixup.idl b/docshell/base/nsCDefaultURIFixup.idl
new file mode 100644
index 000000000..60fffdf21
--- /dev/null
+++ b/docshell/base/nsCDefaultURIFixup.idl
@@ -0,0 +1,13 @@
+/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+%{ C++
+// {214C48A0-B57F-11d4-959C-0020183BF181}
+#define NS_DEFAULTURIFIXUP_CID \
+{ 0x214c48a0, 0xb57f, 0x11d4, { 0x95, 0x9c, 0x0, 0x20, 0x18, 0x3b, 0xf1, 0x81 } }
+#define NS_URIFIXUP_CONTRACTID \
+"@mozilla.org/docshell/urifixup;1"
+%}
diff --git a/docshell/base/nsDSURIContentListener.cpp b/docshell/base/nsDSURIContentListener.cpp
new file mode 100644
index 000000000..cfac54f7f
--- /dev/null
+++ b/docshell/base/nsDSURIContentListener.cpp
@@ -0,0 +1,539 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsDocShell.h"
+#include "nsDSURIContentListener.h"
+#include "nsIChannel.h"
+#include "nsServiceManagerUtils.h"
+#include "nsDocShellCID.h"
+#include "nsIWebNavigationInfo.h"
+#include "nsIDocument.h"
+#include "nsIDOMWindow.h"
+#include "nsNetUtil.h"
+#include "nsQueryObject.h"
+#include "nsIHttpChannel.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsError.h"
+#include "nsCharSeparatedTokenizer.h"
+#include "nsIConsoleService.h"
+#include "nsIScriptError.h"
+#include "nsDocShellLoadTypes.h"
+#include "nsIMultiPartChannel.h"
+
+using namespace mozilla;
+
+nsDSURIContentListener::nsDSURIContentListener(nsDocShell* aDocShell)
+ : mDocShell(aDocShell)
+ , mExistingJPEGRequest(nullptr)
+ , mParentContentListener(nullptr)
+{
+}
+
+nsDSURIContentListener::~nsDSURIContentListener()
+{
+}
+
+nsresult
+nsDSURIContentListener::Init()
+{
+ nsresult rv;
+ mNavInfo = do_GetService(NS_WEBNAVIGATION_INFO_CONTRACTID, &rv);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to get webnav info");
+ return rv;
+}
+
+NS_IMPL_ADDREF(nsDSURIContentListener)
+NS_IMPL_RELEASE(nsDSURIContentListener)
+
+NS_INTERFACE_MAP_BEGIN(nsDSURIContentListener)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIURIContentListener)
+ NS_INTERFACE_MAP_ENTRY(nsIURIContentListener)
+ NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
+NS_INTERFACE_MAP_END
+
+NS_IMETHODIMP
+nsDSURIContentListener::OnStartURIOpen(nsIURI* aURI, bool* aAbortOpen)
+{
+ // If mDocShell is null here, that means someone's starting a load in our
+ // docshell after it's already been destroyed. Don't let that happen.
+ if (!mDocShell) {
+ *aAbortOpen = true;
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIURIContentListener> parentListener;
+ GetParentContentListener(getter_AddRefs(parentListener));
+ if (parentListener) {
+ return parentListener->OnStartURIOpen(aURI, aAbortOpen);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDSURIContentListener::DoContent(const nsACString& aContentType,
+ bool aIsContentPreferred,
+ nsIRequest* aRequest,
+ nsIStreamListener** aContentHandler,
+ bool* aAbortProcess)
+{
+ nsresult rv;
+ NS_ENSURE_ARG_POINTER(aContentHandler);
+ NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE);
+
+ // Check whether X-Frame-Options permits us to load this content in an
+ // iframe and abort the load (unless we've disabled x-frame-options
+ // checking).
+ if (!CheckFrameOptions(aRequest)) {
+ *aAbortProcess = true;
+ return NS_OK;
+ }
+
+ *aAbortProcess = false;
+
+ // determine if the channel has just been retargeted to us...
+ nsLoadFlags loadFlags = 0;
+ nsCOMPtr<nsIChannel> aOpenedChannel = do_QueryInterface(aRequest);
+
+ if (aOpenedChannel) {
+ aOpenedChannel->GetLoadFlags(&loadFlags);
+ }
+
+ if (loadFlags & nsIChannel::LOAD_RETARGETED_DOCUMENT_URI) {
+ // XXX: Why does this not stop the content too?
+ mDocShell->Stop(nsIWebNavigation::STOP_NETWORK);
+
+ mDocShell->SetLoadType(aIsContentPreferred ? LOAD_LINK : LOAD_NORMAL);
+ }
+
+ // In case of multipart jpeg request (mjpeg) we don't really want to
+ // create new viewer since the one we already have is capable of
+ // rendering multipart jpeg correctly (see bug 625012)
+ nsCOMPtr<nsIChannel> baseChannel;
+ if (nsCOMPtr<nsIMultiPartChannel> mpchan = do_QueryInterface(aRequest)) {
+ mpchan->GetBaseChannel(getter_AddRefs(baseChannel));
+ }
+
+ bool reuseCV = baseChannel && baseChannel == mExistingJPEGRequest &&
+ aContentType.EqualsLiteral("image/jpeg");
+
+ if (mExistingJPEGStreamListener && reuseCV) {
+ RefPtr<nsIStreamListener> copy(mExistingJPEGStreamListener);
+ copy.forget(aContentHandler);
+ rv = NS_OK;
+ } else {
+ rv = mDocShell->CreateContentViewer(aContentType, aRequest, aContentHandler);
+ if (NS_SUCCEEDED(rv) && reuseCV) {
+ mExistingJPEGStreamListener = *aContentHandler;
+ } else {
+ mExistingJPEGStreamListener = nullptr;
+ }
+ mExistingJPEGRequest = baseChannel;
+ }
+
+ if (rv == NS_ERROR_REMOTE_XUL) {
+ aRequest->Cancel(rv);
+ *aAbortProcess = true;
+ return NS_OK;
+ }
+
+ if (NS_FAILED(rv)) {
+ // we don't know how to handle the content
+ *aContentHandler = nullptr;
+ return rv;
+ }
+
+ if (loadFlags & nsIChannel::LOAD_RETARGETED_DOCUMENT_URI) {
+ nsCOMPtr<nsPIDOMWindowOuter> domWindow =
+ mDocShell ? mDocShell->GetWindow() : nullptr;
+ NS_ENSURE_TRUE(domWindow, NS_ERROR_FAILURE);
+ domWindow->Focus();
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDSURIContentListener::IsPreferred(const char* aContentType,
+ char** aDesiredContentType,
+ bool* aCanHandle)
+{
+ NS_ENSURE_ARG_POINTER(aCanHandle);
+ NS_ENSURE_ARG_POINTER(aDesiredContentType);
+
+ // the docshell has no idea if it is the preferred content provider or not.
+ // It needs to ask its parent if it is the preferred content handler or not...
+
+ nsCOMPtr<nsIURIContentListener> parentListener;
+ GetParentContentListener(getter_AddRefs(parentListener));
+ if (parentListener) {
+ return parentListener->IsPreferred(aContentType,
+ aDesiredContentType,
+ aCanHandle);
+ }
+ // we used to return false here if we didn't have a parent properly registered
+ // at the top of the docshell hierarchy to dictate what content types this
+ // docshell should be a preferred handler for. But this really makes it hard
+ // for developers using iframe or browser tags because then they need to make
+ // sure they implement nsIURIContentListener otherwise all link clicks would
+ // get sent to another window because we said we weren't the preferred handler
+ // type. I'm going to change the default now... if we can handle the content,
+ // and someone didn't EXPLICITLY set a nsIURIContentListener at the top of our
+ // docshell chain, then we'll now always attempt to process the content
+ // ourselves...
+ return CanHandleContent(aContentType, true, aDesiredContentType, aCanHandle);
+}
+
+NS_IMETHODIMP
+nsDSURIContentListener::CanHandleContent(const char* aContentType,
+ bool aIsContentPreferred,
+ char** aDesiredContentType,
+ bool* aCanHandleContent)
+{
+ NS_PRECONDITION(aCanHandleContent, "Null out param?");
+ NS_ENSURE_ARG_POINTER(aDesiredContentType);
+
+ *aCanHandleContent = false;
+ *aDesiredContentType = nullptr;
+
+ nsresult rv = NS_OK;
+ if (aContentType) {
+ uint32_t canHandle = nsIWebNavigationInfo::UNSUPPORTED;
+ rv = mNavInfo->IsTypeSupported(nsDependentCString(aContentType),
+ mDocShell,
+ &canHandle);
+ *aCanHandleContent = (canHandle != nsIWebNavigationInfo::UNSUPPORTED);
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDSURIContentListener::GetLoadCookie(nsISupports** aLoadCookie)
+{
+ NS_IF_ADDREF(*aLoadCookie = nsDocShell::GetAsSupports(mDocShell));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDSURIContentListener::SetLoadCookie(nsISupports* aLoadCookie)
+{
+#ifdef DEBUG
+ RefPtr<nsDocLoader> cookieAsDocLoader =
+ nsDocLoader::GetAsDocLoader(aLoadCookie);
+ NS_ASSERTION(cookieAsDocLoader && cookieAsDocLoader == mDocShell,
+ "Invalid load cookie being set!");
+#endif
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDSURIContentListener::GetParentContentListener(
+ nsIURIContentListener** aParentListener)
+{
+ if (mWeakParentContentListener) {
+ nsCOMPtr<nsIURIContentListener> tempListener =
+ do_QueryReferent(mWeakParentContentListener);
+ *aParentListener = tempListener;
+ NS_IF_ADDREF(*aParentListener);
+ } else {
+ *aParentListener = mParentContentListener;
+ NS_IF_ADDREF(*aParentListener);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDSURIContentListener::SetParentContentListener(
+ nsIURIContentListener* aParentListener)
+{
+ if (aParentListener) {
+ // Store the parent listener as a weak ref. Parents not supporting
+ // nsISupportsWeakReference assert but may still be used.
+ mParentContentListener = nullptr;
+ mWeakParentContentListener = do_GetWeakReference(aParentListener);
+ if (!mWeakParentContentListener) {
+ mParentContentListener = aParentListener;
+ }
+ } else {
+ mWeakParentContentListener = nullptr;
+ mParentContentListener = nullptr;
+ }
+ return NS_OK;
+}
+
+bool
+nsDSURIContentListener::CheckOneFrameOptionsPolicy(nsIHttpChannel* aHttpChannel,
+ const nsAString& aPolicy)
+{
+ static const char allowFrom[] = "allow-from";
+ const uint32_t allowFromLen = ArrayLength(allowFrom) - 1;
+ bool isAllowFrom =
+ StringHead(aPolicy, allowFromLen).LowerCaseEqualsLiteral(allowFrom);
+
+ // return early if header does not have one of the values with meaning
+ if (!aPolicy.LowerCaseEqualsLiteral("deny") &&
+ !aPolicy.LowerCaseEqualsLiteral("sameorigin") &&
+ !isAllowFrom) {
+ return true;
+ }
+
+ nsCOMPtr<nsIURI> uri;
+ aHttpChannel->GetURI(getter_AddRefs(uri));
+
+ // XXXkhuey when does this happen? Is returning true safe here?
+ if (!mDocShell) {
+ return true;
+ }
+
+ // We need to check the location of this window and the location of the top
+ // window, if we're not the top. X-F-O: SAMEORIGIN requires that the
+ // document must be same-origin with top window. X-F-O: DENY requires that
+ // the document must never be framed.
+ nsCOMPtr<nsPIDOMWindowOuter> thisWindow = mDocShell->GetWindow();
+ // If we don't have DOMWindow there is no risk of clickjacking
+ if (!thisWindow) {
+ return true;
+ }
+
+ // GetScriptableTop, not GetTop, because we want this to respect
+ // <iframe mozbrowser> boundaries.
+ nsCOMPtr<nsPIDOMWindowOuter> topWindow = thisWindow->GetScriptableTop();
+
+ // if the document is in the top window, it's not in a frame.
+ if (thisWindow == topWindow) {
+ return true;
+ }
+
+ // Find the top docshell in our parent chain that doesn't have the system
+ // principal and use it for the principal comparison. Finding the top
+ // content-type docshell doesn't work because some chrome documents are
+ // loaded in content docshells (see bug 593387).
+ nsCOMPtr<nsIDocShellTreeItem> thisDocShellItem(
+ do_QueryInterface(static_cast<nsIDocShell*>(mDocShell)));
+ nsCOMPtr<nsIDocShellTreeItem> parentDocShellItem;
+ nsCOMPtr<nsIDocShellTreeItem> curDocShellItem = thisDocShellItem;
+ nsCOMPtr<nsIDocument> topDoc;
+ nsresult rv;
+ nsCOMPtr<nsIScriptSecurityManager> ssm =
+ do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
+ if (!ssm) {
+ MOZ_CRASH();
+ }
+
+ // Traverse up the parent chain and stop when we see a docshell whose
+ // parent has a system principal, or a docshell corresponding to
+ // <iframe mozbrowser>.
+ while (NS_SUCCEEDED(
+ curDocShellItem->GetParent(getter_AddRefs(parentDocShellItem))) &&
+ parentDocShellItem) {
+ nsCOMPtr<nsIDocShell> curDocShell = do_QueryInterface(curDocShellItem);
+ if (curDocShell && curDocShell->GetIsMozBrowserOrApp()) {
+ break;
+ }
+
+ bool system = false;
+ topDoc = parentDocShellItem->GetDocument();
+ if (topDoc) {
+ if (NS_SUCCEEDED(
+ ssm->IsSystemPrincipal(topDoc->NodePrincipal(), &system)) &&
+ system) {
+ // Found a system-principled doc: last docshell was top.
+ break;
+ }
+ } else {
+ return false;
+ }
+ curDocShellItem = parentDocShellItem;
+ }
+
+ // If this document has the top non-SystemPrincipal docshell it is not being
+ // framed or it is being framed by a chrome document, which we allow.
+ if (curDocShellItem == thisDocShellItem) {
+ return true;
+ }
+
+ // If the value of the header is DENY, and the previous condition is
+ // not met (current docshell is not the top docshell), prohibit the
+ // load.
+ if (aPolicy.LowerCaseEqualsLiteral("deny")) {
+ ReportXFOViolation(curDocShellItem, uri, eDENY);
+ return false;
+ }
+
+ topDoc = curDocShellItem->GetDocument();
+ nsCOMPtr<nsIURI> topUri;
+ topDoc->NodePrincipal()->GetURI(getter_AddRefs(topUri));
+
+ // If the X-Frame-Options value is SAMEORIGIN, then the top frame in the
+ // parent chain must be from the same origin as this document.
+ if (aPolicy.LowerCaseEqualsLiteral("sameorigin")) {
+ rv = ssm->CheckSameOriginURI(uri, topUri, true);
+ if (NS_FAILED(rv)) {
+ ReportXFOViolation(curDocShellItem, uri, eSAMEORIGIN);
+ return false; /* wasn't same-origin */
+ }
+ }
+
+ // If the X-Frame-Options value is "allow-from [uri]", then the top
+ // frame in the parent chain must be from that origin
+ if (isAllowFrom) {
+ if (aPolicy.Length() == allowFromLen ||
+ (aPolicy[allowFromLen] != ' ' &&
+ aPolicy[allowFromLen] != '\t')) {
+ ReportXFOViolation(curDocShellItem, uri, eALLOWFROM);
+ return false;
+ }
+ rv = NS_NewURI(getter_AddRefs(uri), Substring(aPolicy, allowFromLen));
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+
+ rv = ssm->CheckSameOriginURI(uri, topUri, true);
+ if (NS_FAILED(rv)) {
+ ReportXFOViolation(curDocShellItem, uri, eALLOWFROM);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// Check if X-Frame-Options permits this document to be loaded as a subdocument.
+// This will iterate through and check any number of X-Frame-Options policies
+// in the request (comma-separated in a header, multiple headers, etc).
+bool
+nsDSURIContentListener::CheckFrameOptions(nsIRequest* aRequest)
+{
+ nsresult rv;
+ nsCOMPtr<nsIChannel> chan = do_QueryInterface(aRequest);
+ if (!chan) {
+ return true;
+ }
+
+ nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(chan);
+ if (!httpChannel) {
+ // check if it is hiding in a multipart channel
+ rv = mDocShell->GetHttpChannel(chan, getter_AddRefs(httpChannel));
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+ }
+
+ if (!httpChannel) {
+ return true;
+ }
+
+ nsAutoCString xfoHeaderCValue;
+ httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("X-Frame-Options"),
+ xfoHeaderCValue);
+ NS_ConvertUTF8toUTF16 xfoHeaderValue(xfoHeaderCValue);
+
+ // if no header value, there's nothing to do.
+ if (xfoHeaderValue.IsEmpty()) {
+ return true;
+ }
+
+ // iterate through all the header values (usually there's only one, but can
+ // be many. If any want to deny the load, deny the load.
+ nsCharSeparatedTokenizer tokenizer(xfoHeaderValue, ',');
+ while (tokenizer.hasMoreTokens()) {
+ const nsSubstring& tok = tokenizer.nextToken();
+ if (!CheckOneFrameOptionsPolicy(httpChannel, tok)) {
+ // cancel the load and display about:blank
+ httpChannel->Cancel(NS_BINDING_ABORTED);
+ if (mDocShell) {
+ nsCOMPtr<nsIWebNavigation> webNav(do_QueryObject(mDocShell));
+ if (webNav) {
+ webNav->LoadURI(u"about:blank",
+ 0, nullptr, nullptr, nullptr);
+ }
+ }
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void
+nsDSURIContentListener::ReportXFOViolation(nsIDocShellTreeItem* aTopDocShellItem,
+ nsIURI* aThisURI,
+ XFOHeader aHeader)
+{
+ MOZ_ASSERT(aTopDocShellItem, "Need a top docshell");
+
+ nsCOMPtr<nsPIDOMWindowOuter> topOuterWindow = aTopDocShellItem->GetWindow();
+ if (!topOuterWindow) {
+ return;
+ }
+
+ nsPIDOMWindowInner* topInnerWindow = topOuterWindow->GetCurrentInnerWindow();
+ if (!topInnerWindow) {
+ return;
+ }
+
+ nsCOMPtr<nsIURI> topURI;
+
+ nsCOMPtr<nsIDocument> document = aTopDocShellItem->GetDocument();
+ nsresult rv = document->NodePrincipal()->GetURI(getter_AddRefs(topURI));
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ if (!topURI) {
+ return;
+ }
+
+ nsCString topURIString;
+ nsCString thisURIString;
+
+ rv = topURI->GetSpec(topURIString);
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ rv = aThisURI->GetSpec(thisURIString);
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ nsCOMPtr<nsIConsoleService> consoleService =
+ do_GetService(NS_CONSOLESERVICE_CONTRACTID);
+ nsCOMPtr<nsIScriptError> errorObject =
+ do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
+
+ if (!consoleService || !errorObject) {
+ return;
+ }
+
+ nsString msg = NS_LITERAL_STRING("Load denied by X-Frame-Options: ");
+ msg.Append(NS_ConvertUTF8toUTF16(thisURIString));
+
+ switch (aHeader) {
+ case eDENY:
+ msg.AppendLiteral(" does not permit framing.");
+ break;
+ case eSAMEORIGIN:
+ msg.AppendLiteral(" does not permit cross-origin framing.");
+ break;
+ case eALLOWFROM:
+ msg.AppendLiteral(" does not permit framing by ");
+ msg.Append(NS_ConvertUTF8toUTF16(topURIString));
+ msg.Append('.');
+ break;
+ }
+
+ rv = errorObject->InitWithWindowID(msg, EmptyString(), EmptyString(), 0, 0,
+ nsIScriptError::errorFlag,
+ "X-Frame-Options",
+ topInnerWindow->WindowID());
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ consoleService->LogMessage(errorObject);
+}
diff --git a/docshell/base/nsDSURIContentListener.h b/docshell/base/nsDSURIContentListener.h
new file mode 100644
index 000000000..f33d1c045
--- /dev/null
+++ b/docshell/base/nsDSURIContentListener.h
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsDSURIContentListener_h__
+#define nsDSURIContentListener_h__
+
+#include "nsCOMPtr.h"
+#include "nsIURIContentListener.h"
+#include "nsWeakReference.h"
+
+class nsDocShell;
+class nsIWebNavigationInfo;
+class nsIHttpChannel;
+class nsAString;
+
+class nsDSURIContentListener final
+ : public nsIURIContentListener
+ , public nsSupportsWeakReference
+{
+ friend class nsDocShell;
+
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIURICONTENTLISTENER
+
+ nsresult Init();
+
+protected:
+ explicit nsDSURIContentListener(nsDocShell* aDocShell);
+ virtual ~nsDSURIContentListener();
+
+ void DropDocShellReference()
+ {
+ mDocShell = nullptr;
+ mExistingJPEGRequest = nullptr;
+ mExistingJPEGStreamListener = nullptr;
+ }
+
+ // Determine if X-Frame-Options allows content to be framed
+ // as a subdocument
+ bool CheckFrameOptions(nsIRequest* aRequest);
+ bool CheckOneFrameOptionsPolicy(nsIHttpChannel* aHttpChannel,
+ const nsAString& aPolicy);
+
+ enum XFOHeader
+ {
+ eDENY,
+ eSAMEORIGIN,
+ eALLOWFROM
+ };
+
+ void ReportXFOViolation(nsIDocShellTreeItem* aTopDocShellItem,
+ nsIURI* aThisURI,
+ XFOHeader aHeader);
+
+protected:
+ nsDocShell* mDocShell;
+ // Hack to handle multipart images without creating a new viewer
+ nsCOMPtr<nsIStreamListener> mExistingJPEGStreamListener;
+ nsCOMPtr<nsIChannel> mExistingJPEGRequest;
+
+ // Store the parent listener in either of these depending on
+ // if supports weak references or not. Proper weak refs are
+ // preferred and encouraged!
+ nsWeakPtr mWeakParentContentListener;
+ nsIURIContentListener* mParentContentListener;
+
+ nsCOMPtr<nsIWebNavigationInfo> mNavInfo;
+};
+
+#endif /* nsDSURIContentListener_h__ */
diff --git a/docshell/base/nsDefaultURIFixup.cpp b/docshell/base/nsDefaultURIFixup.cpp
new file mode 100644
index 000000000..e519720ab
--- /dev/null
+++ b/docshell/base/nsDefaultURIFixup.cpp
@@ -0,0 +1,1152 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsNetCID.h"
+#include "nsNetUtil.h"
+#include "nsIProtocolHandler.h"
+#include "nsCRT.h"
+
+#include "nsIFile.h"
+#include <algorithm>
+
+#ifdef MOZ_TOOLKIT_SEARCH
+#include "nsIBrowserSearchService.h"
+#endif
+
+#include "nsIURIFixup.h"
+#include "nsDefaultURIFixup.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/ipc/InputStreamUtils.h"
+#include "mozilla/ipc/URIUtils.h"
+#include "mozilla/Tokenizer.h"
+#include "nsIObserverService.h"
+#include "nsXULAppAPI.h"
+
+// Used to check if external protocol schemes are usable
+#include "nsCExternalHandlerService.h"
+#include "nsIExternalProtocolService.h"
+
+using namespace mozilla;
+
+/* Implementation file */
+NS_IMPL_ISUPPORTS(nsDefaultURIFixup, nsIURIFixup)
+
+static bool sInitializedPrefCaches = false;
+static bool sFixTypos = true;
+static bool sDNSFirstForSingleWords = false;
+static bool sFixupKeywords = true;
+
+nsDefaultURIFixup::nsDefaultURIFixup()
+{
+}
+
+nsDefaultURIFixup::~nsDefaultURIFixup()
+{
+}
+
+NS_IMETHODIMP
+nsDefaultURIFixup::CreateExposableURI(nsIURI* aURI, nsIURI** aReturn)
+{
+ NS_ENSURE_ARG_POINTER(aURI);
+ NS_ENSURE_ARG_POINTER(aReturn);
+
+ bool isWyciwyg = false;
+ aURI->SchemeIs("wyciwyg", &isWyciwyg);
+
+ nsAutoCString userPass;
+ aURI->GetUserPass(userPass);
+
+ // most of the time we can just AddRef and return
+ if (!isWyciwyg && userPass.IsEmpty()) {
+ *aReturn = aURI;
+ NS_ADDREF(*aReturn);
+ return NS_OK;
+ }
+
+ // Rats, we have to massage the URI
+ nsCOMPtr<nsIURI> uri;
+ if (isWyciwyg) {
+ nsAutoCString path;
+ nsresult rv = aURI->GetPath(path);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uint32_t pathLength = path.Length();
+ if (pathLength <= 2) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Path is of the form "//123/http://foo/bar", with a variable number of
+ // digits. To figure out where the "real" URL starts, search path for a '/',
+ // starting at the third character.
+ int32_t slashIndex = path.FindChar('/', 2);
+ if (slashIndex == kNotFound) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Get the charset of the original URI so we can pass it to our fixed up
+ // URI.
+ nsAutoCString charset;
+ aURI->GetOriginCharset(charset);
+
+ rv = NS_NewURI(getter_AddRefs(uri),
+ Substring(path, slashIndex + 1, pathLength - slashIndex - 1),
+ charset.get());
+ NS_ENSURE_SUCCESS(rv, rv);
+ } else {
+ // clone the URI so zapping user:pass doesn't change the original
+ nsresult rv = aURI->Clone(getter_AddRefs(uri));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // hide user:pass unless overridden by pref
+ if (Preferences::GetBool("browser.fixup.hide_user_pass", true)) {
+ uri->SetUserPass(EmptyCString());
+ }
+
+ uri.forget(aReturn);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDefaultURIFixup::CreateFixupURI(const nsACString& aStringURI,
+ uint32_t aFixupFlags,
+ nsIInputStream** aPostData, nsIURI** aURI)
+{
+ nsCOMPtr<nsIURIFixupInfo> fixupInfo;
+ nsresult rv = GetFixupURIInfo(aStringURI, aFixupFlags, aPostData,
+ getter_AddRefs(fixupInfo));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ fixupInfo->GetPreferredURI(aURI);
+ return rv;
+}
+
+// Returns true if the URL contains a user:password@ or user@
+static bool
+HasUserPassword(const nsACString& aStringURI)
+{
+ mozilla::Tokenizer parser(aStringURI);
+ mozilla::Tokenizer::Token token;
+
+ // May start with any of "protocol:", "protocol://", "//", "://"
+ if (parser.Check(Tokenizer::TOKEN_WORD, token)) { // Skip protocol if any
+ }
+ if (parser.CheckChar(':')) { // Skip colon if found
+ }
+ while (parser.CheckChar('/')) { // Skip all of the following slashes
+ }
+
+ while (parser.Next(token)) {
+ if (token.Type() == Tokenizer::TOKEN_CHAR) {
+ if (token.AsChar() == '/') {
+ return false;
+ }
+ if (token.AsChar() == '@') {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+NS_IMETHODIMP
+nsDefaultURIFixup::GetFixupURIInfo(const nsACString& aStringURI,
+ uint32_t aFixupFlags,
+ nsIInputStream** aPostData,
+ nsIURIFixupInfo** aInfo)
+{
+ NS_ENSURE_ARG(!aStringURI.IsEmpty());
+
+ nsresult rv;
+
+ nsAutoCString uriString(aStringURI);
+
+ // Eliminate embedded newlines, which single-line text fields now allow:
+ uriString.StripChars("\r\n");
+ // Cleanup the empty spaces that might be on each end:
+ uriString.Trim(" ");
+
+ NS_ENSURE_TRUE(!uriString.IsEmpty(), NS_ERROR_FAILURE);
+
+ RefPtr<nsDefaultURIFixupInfo> info = new nsDefaultURIFixupInfo(uriString);
+ NS_ADDREF(*aInfo = info);
+
+ nsCOMPtr<nsIIOService> ioService =
+ do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsAutoCString scheme;
+ ioService->ExtractScheme(aStringURI, scheme);
+
+ // View-source is a pseudo scheme. We're interested in fixing up the stuff
+ // after it. The easiest way to do that is to call this method again with the
+ // "view-source:" lopped off and then prepend it again afterwards.
+
+ if (scheme.LowerCaseEqualsLiteral("view-source")) {
+ nsCOMPtr<nsIURIFixupInfo> uriInfo;
+ // We disable keyword lookup and alternate URIs so that small typos don't
+ // cause us to look at very different domains
+ uint32_t newFixupFlags = aFixupFlags & ~FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP
+ & ~FIXUP_FLAGS_MAKE_ALTERNATE_URI;
+
+ const uint32_t viewSourceLen = sizeof("view-source:") - 1;
+ nsAutoCString innerURIString(Substring(uriString, viewSourceLen,
+ uriString.Length() -
+ viewSourceLen));
+ // Prevent recursion:
+ innerURIString.Trim(" ");
+ nsAutoCString innerScheme;
+ ioService->ExtractScheme(innerURIString, innerScheme);
+ if (innerScheme.LowerCaseEqualsLiteral("view-source")) {
+ return NS_ERROR_FAILURE;
+ }
+
+ rv = GetFixupURIInfo(innerURIString, newFixupFlags, aPostData,
+ getter_AddRefs(uriInfo));
+ if (NS_FAILED(rv)) {
+ return NS_ERROR_FAILURE;
+ }
+ nsAutoCString spec;
+ nsCOMPtr<nsIURI> uri;
+ uriInfo->GetPreferredURI(getter_AddRefs(uri));
+ if (!uri) {
+ return NS_ERROR_FAILURE;
+ }
+ uri->GetSpec(spec);
+ uriString.AssignLiteral("view-source:");
+ uriString.Append(spec);
+ } else {
+ // Check for if it is a file URL
+ nsCOMPtr<nsIURI> uri;
+ FileURIFixup(uriString, getter_AddRefs(uri));
+ // NB: FileURIFixup only returns a URI if it had to fix the protocol to
+ // do so, so passing in file:///foo/bar will not hit this path:
+ if (uri) {
+ uri.swap(info->mFixedURI);
+ info->mPreferredURI = info->mFixedURI;
+ info->mFixupChangedProtocol = true;
+ return NS_OK;
+ }
+ }
+
+ if (!sInitializedPrefCaches) {
+ // Check if we want to fix up common scheme typos.
+ rv = Preferences::AddBoolVarCache(&sFixTypos,
+ "browser.fixup.typo.scheme",
+ sFixTypos);
+ MOZ_ASSERT(NS_SUCCEEDED(rv),
+ "Failed to observe \"browser.fixup.typo.scheme\"");
+
+ rv = Preferences::AddBoolVarCache(&sDNSFirstForSingleWords,
+ "browser.fixup.dns_first_for_single_words",
+ sDNSFirstForSingleWords);
+ MOZ_ASSERT(NS_SUCCEEDED(rv),
+ "Failed to observe \"browser.fixup.dns_first_for_single_words\"");
+
+ rv = Preferences::AddBoolVarCache(&sFixupKeywords, "keyword.enabled",
+ sFixupKeywords);
+ MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed to observe \"keyword.enabled\"");
+ sInitializedPrefCaches = true;
+ }
+
+ // Fix up common scheme typos.
+ if (sFixTypos && (aFixupFlags & FIXUP_FLAG_FIX_SCHEME_TYPOS)) {
+ // Fast-path for common cases.
+ if (scheme.IsEmpty() ||
+ scheme.LowerCaseEqualsLiteral("http") ||
+ scheme.LowerCaseEqualsLiteral("https") ||
+ scheme.LowerCaseEqualsLiteral("ftp") ||
+ scheme.LowerCaseEqualsLiteral("file")) {
+ // Do nothing.
+ } else if (scheme.LowerCaseEqualsLiteral("ttp")) {
+ // ttp -> http.
+ uriString.Replace(0, 3, "http");
+ scheme.AssignLiteral("http");
+ info->mFixupChangedProtocol = true;
+ } else if (scheme.LowerCaseEqualsLiteral("ttps")) {
+ // ttps -> https.
+ uriString.Replace(0, 4, "https");
+ scheme.AssignLiteral("https");
+ info->mFixupChangedProtocol = true;
+ } else if (scheme.LowerCaseEqualsLiteral("tps")) {
+ // tps -> https.
+ uriString.Replace(0, 3, "https");
+ scheme.AssignLiteral("https");
+ info->mFixupChangedProtocol = true;
+ } else if (scheme.LowerCaseEqualsLiteral("ps")) {
+ // ps -> https.
+ uriString.Replace(0, 2, "https");
+ scheme.AssignLiteral("https");
+ info->mFixupChangedProtocol = true;
+ } else if (scheme.LowerCaseEqualsLiteral("ile")) {
+ // ile -> file.
+ uriString.Replace(0, 3, "file");
+ scheme.AssignLiteral("file");
+ info->mFixupChangedProtocol = true;
+ } else if (scheme.LowerCaseEqualsLiteral("le")) {
+ // le -> file.
+ uriString.Replace(0, 2, "file");
+ scheme.AssignLiteral("file");
+ info->mFixupChangedProtocol = true;
+ }
+ }
+
+ // Now we need to check whether "scheme" is something we don't
+ // really know about.
+ nsCOMPtr<nsIProtocolHandler> ourHandler, extHandler;
+
+ ioService->GetProtocolHandler(scheme.get(), getter_AddRefs(ourHandler));
+ extHandler = do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "default");
+
+ if (ourHandler != extHandler || !PossiblyHostPortUrl(uriString)) {
+ // Just try to create an URL out of it
+ rv = NS_NewURI(getter_AddRefs(info->mFixedURI), uriString, nullptr);
+
+ if (!info->mFixedURI && rv != NS_ERROR_MALFORMED_URI) {
+ return rv;
+ }
+ }
+
+ if (info->mFixedURI && ourHandler == extHandler && sFixupKeywords &&
+ (aFixupFlags & FIXUP_FLAG_FIX_SCHEME_TYPOS)) {
+ nsCOMPtr<nsIExternalProtocolService> extProtService =
+ do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID);
+ if (extProtService) {
+ bool handlerExists = false;
+ rv = extProtService->ExternalProtocolHandlerExists(scheme.get(),
+ &handlerExists);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ // This basically means we're dealing with a theoretically valid
+ // URI... but we have no idea how to load it. (e.g. "christmas:humbug")
+ // It's more likely the user wants to search, and so we
+ // chuck this over to their preferred search provider instead:
+ if (!handlerExists) {
+ bool hasUserPassword = HasUserPassword(uriString);
+ if (!hasUserPassword) {
+ TryKeywordFixupForURIInfo(uriString, info, aPostData);
+ } else {
+ // If the given URL has a user:password we can't just pass it to the
+ // external protocol handler; we'll try using it with http instead later
+ info->mFixedURI = nullptr;
+ }
+ }
+ }
+ }
+
+ if (info->mFixedURI) {
+ if (!info->mPreferredURI) {
+ if (aFixupFlags & FIXUP_FLAGS_MAKE_ALTERNATE_URI) {
+ info->mFixupCreatedAlternateURI = MakeAlternateURI(info->mFixedURI);
+ }
+ info->mPreferredURI = info->mFixedURI;
+ }
+ return NS_OK;
+ }
+
+ // Fix up protocol string before calling KeywordURIFixup, because
+ // it cares about the hostname of such URIs:
+ nsCOMPtr<nsIURI> uriWithProtocol;
+ bool inputHadDuffProtocol = false;
+
+ // Prune duff protocol schemes
+ //
+ // ://totallybroken.url.com
+ // //shorthand.url.com
+ //
+ if (StringBeginsWith(uriString, NS_LITERAL_CSTRING("://"))) {
+ uriString = StringTail(uriString, uriString.Length() - 3);
+ inputHadDuffProtocol = true;
+ } else if (StringBeginsWith(uriString, NS_LITERAL_CSTRING("//"))) {
+ uriString = StringTail(uriString, uriString.Length() - 2);
+ inputHadDuffProtocol = true;
+ }
+
+ // NB: this rv gets returned at the end of this method if we never
+ // do a keyword fixup after this (because the pref or the flags passed
+ // might not let us).
+ rv = FixupURIProtocol(uriString, info, getter_AddRefs(uriWithProtocol));
+ if (uriWithProtocol) {
+ info->mFixedURI = uriWithProtocol;
+ }
+
+ // See if it is a keyword
+ // Test whether keywords need to be fixed up
+ if (sFixupKeywords && (aFixupFlags & FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP) &&
+ !inputHadDuffProtocol) {
+ if (NS_SUCCEEDED(KeywordURIFixup(uriString, info, aPostData)) &&
+ info->mPreferredURI) {
+ return NS_OK;
+ }
+ }
+
+ // Did the caller want us to try an alternative URI?
+ // If so, attempt to fixup http://foo into http://www.foo.com
+
+ if (info->mFixedURI && aFixupFlags & FIXUP_FLAGS_MAKE_ALTERNATE_URI) {
+ info->mFixupCreatedAlternateURI = MakeAlternateURI(info->mFixedURI);
+ }
+
+ if (info->mFixedURI) {
+ info->mPreferredURI = info->mFixedURI;
+ return NS_OK;
+ }
+
+ // If we still haven't been able to construct a valid URI, try to force a
+ // keyword match. This catches search strings with '.' or ':' in them.
+ if (sFixupKeywords && (aFixupFlags & FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP)) {
+ rv = TryKeywordFixupForURIInfo(aStringURI, info, aPostData);
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDefaultURIFixup::KeywordToURI(const nsACString& aKeyword,
+ nsIInputStream** aPostData,
+ nsIURIFixupInfo** aInfo)
+{
+ RefPtr<nsDefaultURIFixupInfo> info = new nsDefaultURIFixupInfo(aKeyword);
+ NS_ADDREF(*aInfo = info);
+
+ if (aPostData) {
+ *aPostData = nullptr;
+ }
+ NS_ENSURE_STATE(Preferences::GetRootBranch());
+
+ // Strip leading "?" and leading/trailing spaces from aKeyword
+ nsAutoCString keyword(aKeyword);
+ if (StringBeginsWith(keyword, NS_LITERAL_CSTRING("?"))) {
+ keyword.Cut(0, 1);
+ }
+ keyword.Trim(" ");
+
+ if (XRE_IsContentProcess()) {
+ dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
+ if (!contentChild) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ ipc::OptionalInputStreamParams postData;
+ ipc::OptionalURIParams uri;
+ nsAutoString providerName;
+ if (!contentChild->SendKeywordToURI(keyword, &providerName, &postData,
+ &uri)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ CopyUTF8toUTF16(keyword, info->mKeywordAsSent);
+ info->mKeywordProviderName = providerName;
+
+ if (aPostData) {
+ nsTArray<ipc::FileDescriptor> fds;
+ nsCOMPtr<nsIInputStream> temp = DeserializeInputStream(postData, fds);
+ temp.forget(aPostData);
+
+ MOZ_ASSERT(fds.IsEmpty());
+ }
+
+ nsCOMPtr<nsIURI> temp = DeserializeURI(uri);
+ info->mPreferredURI = temp.forget();
+ return NS_OK;
+ }
+
+#ifdef MOZ_TOOLKIT_SEARCH
+ // Try falling back to the search service's default search engine
+ nsCOMPtr<nsIBrowserSearchService> searchSvc =
+ do_GetService("@mozilla.org/browser/search-service;1");
+ if (searchSvc) {
+ nsCOMPtr<nsISearchEngine> defaultEngine;
+ searchSvc->GetDefaultEngine(getter_AddRefs(defaultEngine));
+ if (defaultEngine) {
+ nsCOMPtr<nsISearchSubmission> submission;
+ nsAutoString responseType;
+ // We allow default search plugins to specify alternate
+ // parameters that are specific to keyword searches.
+ NS_NAMED_LITERAL_STRING(mozKeywordSearch,
+ "application/x-moz-keywordsearch");
+ bool supportsResponseType = false;
+ defaultEngine->SupportsResponseType(mozKeywordSearch,
+ &supportsResponseType);
+ if (supportsResponseType) {
+ responseType.Assign(mozKeywordSearch);
+ }
+
+ NS_ConvertUTF8toUTF16 keywordW(keyword);
+ defaultEngine->GetSubmission(keywordW,
+ responseType,
+ NS_LITERAL_STRING("keyword"),
+ getter_AddRefs(submission));
+
+ if (submission) {
+ nsCOMPtr<nsIInputStream> postData;
+ submission->GetPostData(getter_AddRefs(postData));
+ if (aPostData) {
+ postData.forget(aPostData);
+ } else if (postData) {
+ // The submission specifies POST data (i.e. the search
+ // engine's "method" is POST), but our caller didn't allow
+ // passing post data back. No point passing back a URL that
+ // won't load properly.
+ return NS_ERROR_FAILURE;
+ }
+
+ defaultEngine->GetName(info->mKeywordProviderName);
+ info->mKeywordAsSent = keywordW;
+ return submission->GetUri(getter_AddRefs(info->mPreferredURI));
+ }
+ }
+ }
+#endif
+
+ // out of options
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+// Helper to deal with passing around uri fixup stuff
+nsresult
+nsDefaultURIFixup::TryKeywordFixupForURIInfo(const nsACString& aURIString,
+ nsDefaultURIFixupInfo* aFixupInfo,
+ nsIInputStream** aPostData)
+{
+ nsCOMPtr<nsIURIFixupInfo> keywordInfo;
+ nsresult rv = KeywordToURI(aURIString, aPostData,
+ getter_AddRefs(keywordInfo));
+ if (NS_SUCCEEDED(rv)) {
+ keywordInfo->GetKeywordProviderName(aFixupInfo->mKeywordProviderName);
+ keywordInfo->GetKeywordAsSent(aFixupInfo->mKeywordAsSent);
+ keywordInfo->GetPreferredURI(getter_AddRefs(aFixupInfo->mPreferredURI));
+ }
+ return rv;
+}
+
+bool
+nsDefaultURIFixup::MakeAlternateURI(nsIURI* aURI)
+{
+ if (!Preferences::GetRootBranch()) {
+ return false;
+ }
+ if (!Preferences::GetBool("browser.fixup.alternate.enabled", true)) {
+ return false;
+ }
+
+ // Code only works for http. Not for any other protocol including https!
+ bool isHttp = false;
+ aURI->SchemeIs("http", &isHttp);
+ if (!isHttp) {
+ return false;
+ }
+
+ // Security - URLs with user / password info should NOT be fixed up
+ nsAutoCString userpass;
+ aURI->GetUserPass(userpass);
+ if (!userpass.IsEmpty()) {
+ return false;
+ }
+
+ nsAutoCString oldHost;
+ nsAutoCString newHost;
+ aURI->GetHost(oldHost);
+
+ // Count the dots
+ int32_t numDots = 0;
+ nsReadingIterator<char> iter;
+ nsReadingIterator<char> iterEnd;
+ oldHost.BeginReading(iter);
+ oldHost.EndReading(iterEnd);
+ while (iter != iterEnd) {
+ if (*iter == '.') {
+ numDots++;
+ }
+ ++iter;
+ }
+
+ // Get the prefix and suffix to stick onto the new hostname. By default these
+ // are www. & .com but they could be any other value, e.g. www. & .org
+
+ nsAutoCString prefix("www.");
+ nsAdoptingCString prefPrefix =
+ Preferences::GetCString("browser.fixup.alternate.prefix");
+ if (prefPrefix) {
+ prefix.Assign(prefPrefix);
+ }
+
+ nsAutoCString suffix(".com");
+ nsAdoptingCString prefSuffix =
+ Preferences::GetCString("browser.fixup.alternate.suffix");
+ if (prefSuffix) {
+ suffix.Assign(prefSuffix);
+ }
+
+ if (numDots == 0) {
+ newHost.Assign(prefix);
+ newHost.Append(oldHost);
+ newHost.Append(suffix);
+ } else if (numDots == 1) {
+ if (!prefix.IsEmpty() &&
+ oldHost.EqualsIgnoreCase(prefix.get(), prefix.Length())) {
+ newHost.Assign(oldHost);
+ newHost.Append(suffix);
+ } else if (!suffix.IsEmpty()) {
+ newHost.Assign(prefix);
+ newHost.Append(oldHost);
+ } else {
+ // Do nothing
+ return false;
+ }
+ } else {
+ // Do nothing
+ return false;
+ }
+
+ if (newHost.IsEmpty()) {
+ return false;
+ }
+
+ // Assign the new host string over the old one
+ aURI->SetHost(newHost);
+ return true;
+}
+
+nsresult
+nsDefaultURIFixup::FileURIFixup(const nsACString& aStringURI, nsIURI** aURI)
+{
+ nsAutoCString uriSpecOut;
+
+ nsresult rv = ConvertFileToStringURI(aStringURI, uriSpecOut);
+ if (NS_SUCCEEDED(rv)) {
+ // if this is file url, uriSpecOut is already in FS charset
+ if (NS_SUCCEEDED(NS_NewURI(aURI, uriSpecOut.get(), nullptr))) {
+ return NS_OK;
+ }
+ }
+ return NS_ERROR_FAILURE;
+}
+
+nsresult
+nsDefaultURIFixup::ConvertFileToStringURI(const nsACString& aIn,
+ nsCString& aResult)
+{
+ bool attemptFixup = false;
+
+#if defined(XP_WIN)
+ // Check for \ in the url-string or just a drive (PC)
+ if (aIn.Contains('\\') ||
+ (aIn.Length() == 2 && (aIn.Last() == ':' || aIn.Last() == '|'))) {
+ attemptFixup = true;
+ }
+#elif defined(XP_UNIX)
+ // Check if it starts with / (UNIX)
+ if (aIn.First() == '/') {
+ attemptFixup = true;
+ }
+#else
+ // Do nothing (All others for now)
+#endif
+
+ if (attemptFixup) {
+ // Test if this is a valid path by trying to create a local file
+ // object. The URL of that is returned if successful.
+
+ // NOTE: Please be sure to check that the call to NS_NewLocalFile
+ // rejects bad file paths when using this code on a new
+ // platform.
+
+ nsCOMPtr<nsIFile> filePath;
+ nsresult rv;
+
+ // this is not the real fix but a temporary fix
+ // in order to really fix the problem, we need to change the
+ // nsICmdLineService interface to use wstring to pass paramenters
+ // instead of string since path name and other argument could be
+ // in non ascii.(see bug 87127) Since it is too risky to make interface
+ // change right now, we decide not to do so now.
+ // Therefore, the aIn we receive here maybe already in damage form
+ // (e.g. treat every bytes as ISO-8859-1 and cast up to char16_t
+ // while the real data could be in file system charset )
+ // we choice the following logic which will work for most of the case.
+ // Case will still failed only if it meet ALL the following condiction:
+ // 1. running on CJK, Russian, or Greek system, and
+ // 2. user type it from URL bar
+ // 3. the file name contains character in the range of
+ // U+00A1-U+00FF but encode as different code point in file
+ // system charset (e.g. ACP on window)- this is very rare case
+ // We should remove this logic and convert to File system charset here
+ // once we change nsICmdLineService to use wstring and ensure
+ // all the Unicode data come in is correctly converted.
+ // XXXbz nsICmdLineService doesn't hand back unicode, so in some cases
+ // what we have is actually a "utf8" version of a "utf16" string that's
+ // actually byte-expanded native-encoding data. Someone upstream needs
+ // to stop using AssignWithConversion and do things correctly. See bug
+ // 58866 for what happens if we remove this
+ // PossiblyByteExpandedFileName check.
+ NS_ConvertUTF8toUTF16 in(aIn);
+ if (PossiblyByteExpandedFileName(in)) {
+ // removes high byte
+ rv = NS_NewNativeLocalFile(NS_LossyConvertUTF16toASCII(in), false,
+ getter_AddRefs(filePath));
+ } else {
+ // input is unicode
+ rv = NS_NewLocalFile(in, false, getter_AddRefs(filePath));
+ }
+
+ if (NS_SUCCEEDED(rv)) {
+ NS_GetURLSpecFromFile(filePath, aResult);
+ return NS_OK;
+ }
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+nsresult
+nsDefaultURIFixup::FixupURIProtocol(const nsACString& aURIString,
+ nsDefaultURIFixupInfo* aFixupInfo,
+ nsIURI** aURI)
+{
+ nsAutoCString uriString(aURIString);
+ *aURI = nullptr;
+
+ // Add ftp:// or http:// to front of url if it has no spec
+ //
+ // Should fix:
+ //
+ // no-scheme.com
+ // ftp.no-scheme.com
+ // ftp4.no-scheme.com
+ // no-scheme.com/query?foo=http://www.foo.com
+ // user:pass@no-scheme.com
+ //
+ int32_t schemeDelim = uriString.Find("://", 0);
+ int32_t firstDelim = uriString.FindCharInSet("/:");
+ if (schemeDelim <= 0 ||
+ (firstDelim != -1 && schemeDelim > firstDelim)) {
+ // find host name
+ int32_t hostPos = uriString.FindCharInSet("/:?#");
+ if (hostPos == -1) {
+ hostPos = uriString.Length();
+ }
+
+ // extract host name
+ nsAutoCString hostSpec;
+ uriString.Left(hostSpec, hostPos);
+
+ // insert url spec corresponding to host name
+ uriString.InsertLiteral("http://", 0);
+ aFixupInfo->mFixupChangedProtocol = true;
+ } // end if checkprotocol
+
+ return NS_NewURI(aURI, uriString, nullptr);
+}
+
+bool
+nsDefaultURIFixup::PossiblyHostPortUrl(const nsACString& aUrl)
+{
+ // Oh dear, the protocol is invalid. Test if the protocol might
+ // actually be a url without a protocol:
+ //
+ // http://www.faqs.org/rfcs/rfc1738.html
+ // http://www.faqs.org/rfcs/rfc2396.html
+ //
+ // e.g. Anything of the form:
+ //
+ // <hostname>:<port> or
+ // <hostname>:<port>/
+ //
+ // Where <hostname> is a string of alphanumeric characters and dashes
+ // separated by dots.
+ // and <port> is a 5 or less digits. This actually breaks the rfc2396
+ // definition of a scheme which allows dots in schemes.
+ //
+ // Note:
+ // People expecting this to work with
+ // <user>:<password>@<host>:<port>/<url-path> will be disappointed!
+ //
+ // Note: Parser could be a lot tighter, tossing out silly hostnames
+ // such as those containing consecutive dots and so on.
+
+ // Read the hostname which should of the form
+ // [a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]+)*:
+
+ nsACString::const_iterator iterBegin;
+ nsACString::const_iterator iterEnd;
+ aUrl.BeginReading(iterBegin);
+ aUrl.EndReading(iterEnd);
+ nsACString::const_iterator iter = iterBegin;
+
+ while (iter != iterEnd) {
+ uint32_t chunkSize = 0;
+ // Parse a chunk of the address
+ while (iter != iterEnd &&
+ (*iter == '-' ||
+ nsCRT::IsAsciiAlpha(*iter) ||
+ nsCRT::IsAsciiDigit(*iter))) {
+ ++chunkSize;
+ ++iter;
+ }
+ if (chunkSize == 0 || iter == iterEnd) {
+ return false;
+ }
+ if (*iter == ':') {
+ // Go onto checking the for the digits
+ break;
+ }
+ if (*iter != '.') {
+ // Whatever it is, it ain't a hostname!
+ return false;
+ }
+ ++iter;
+ }
+ if (iter == iterEnd) {
+ // No point continuing since there is no colon
+ return false;
+ }
+ ++iter;
+
+ // Count the number of digits after the colon and before the
+ // next forward slash (or end of string)
+
+ uint32_t digitCount = 0;
+ while (iter != iterEnd && digitCount <= 5) {
+ if (nsCRT::IsAsciiDigit(*iter)) {
+ digitCount++;
+ } else if (*iter == '/') {
+ break;
+ } else {
+ // Whatever it is, it ain't a port!
+ return false;
+ }
+ ++iter;
+ }
+ if (digitCount == 0 || digitCount > 5) {
+ // No digits or more digits than a port would have.
+ return false;
+ }
+
+ // Yes, it's possibly a host:port url
+ return true;
+}
+
+bool
+nsDefaultURIFixup::PossiblyByteExpandedFileName(const nsAString& aIn)
+{
+ // XXXXX HACK XXXXX : please don't copy this code.
+ // There are cases where aIn contains the locale byte chars padded to short
+ // (thus the name "ByteExpanded"); whereas other cases
+ // have proper Unicode code points.
+ // This is a temporary fix. Please refer to 58866, 86948
+
+ nsReadingIterator<char16_t> iter;
+ nsReadingIterator<char16_t> iterEnd;
+ aIn.BeginReading(iter);
+ aIn.EndReading(iterEnd);
+ while (iter != iterEnd) {
+ if (*iter >= 0x0080 && *iter <= 0x00FF) {
+ return true;
+ }
+ ++iter;
+ }
+ return false;
+}
+
+nsresult
+nsDefaultURIFixup::KeywordURIFixup(const nsACString& aURIString,
+ nsDefaultURIFixupInfo* aFixupInfo,
+ nsIInputStream** aPostData)
+{
+ // These are keyword formatted strings
+ // "what is mozilla"
+ // "what is mozilla?"
+ // "docshell site:mozilla.org" - has no dot/colon in the first space-separated substring
+ // "?mozilla" - anything that begins with a question mark
+ // "?site:mozilla.org docshell"
+ // Things that have a quote before the first dot/colon
+ // "mozilla" - checked against a whitelist to see if it's a host or not
+ // ".mozilla", "mozilla." - ditto
+
+ // These are not keyword formatted strings
+ // "www.blah.com" - first space-separated substring contains a dot, doesn't start with "?"
+ // "www.blah.com stuff"
+ // "nonQualifiedHost:80" - first space-separated substring contains a colon, doesn't start with "?"
+ // "nonQualifiedHost:80 args"
+ // "nonQualifiedHost?"
+ // "nonQualifiedHost?args"
+ // "nonQualifiedHost?some args"
+ // "blah.com."
+
+ // Note: uint32_t(kNotFound) is greater than any actual location
+ // in practice. So if we cast all locations to uint32_t, then a <
+ // b guarantees that either b is kNotFound and a is found, or both
+ // are found and a found before b.
+
+ uint32_t firstDotLoc = uint32_t(kNotFound);
+ uint32_t lastDotLoc = uint32_t(kNotFound);
+ uint32_t firstColonLoc = uint32_t(kNotFound);
+ uint32_t firstQuoteLoc = uint32_t(kNotFound);
+ uint32_t firstSpaceLoc = uint32_t(kNotFound);
+ uint32_t firstQMarkLoc = uint32_t(kNotFound);
+ uint32_t lastLSBracketLoc = uint32_t(kNotFound);
+ uint32_t lastSlashLoc = uint32_t(kNotFound);
+ uint32_t pos = 0;
+ uint32_t foundDots = 0;
+ uint32_t foundColons = 0;
+ uint32_t foundDigits = 0;
+ uint32_t foundRSBrackets = 0;
+ bool looksLikeIpv6 = true;
+ bool hasAsciiAlpha = false;
+
+ nsACString::const_iterator iterBegin;
+ nsACString::const_iterator iterEnd;
+ aURIString.BeginReading(iterBegin);
+ aURIString.EndReading(iterEnd);
+ nsACString::const_iterator iter = iterBegin;
+
+ while (iter != iterEnd) {
+ if (pos >= 1 && foundRSBrackets == 0) {
+ if (!(lastLSBracketLoc == 0 &&
+ (*iter == ':' ||
+ *iter == '.' ||
+ *iter == ']' ||
+ (*iter >= 'a' && *iter <= 'f') ||
+ (*iter >= 'A' && *iter <= 'F') ||
+ nsCRT::IsAsciiDigit(*iter)))) {
+ looksLikeIpv6 = false;
+ }
+ }
+
+ // If we're at the end of the string or this is the first slash,
+ // check if the thing before the slash looks like ipv4:
+ if ((iterEnd - iter == 1 ||
+ (lastSlashLoc == uint32_t(kNotFound) && *iter == '/')) &&
+ // Need 2 or 3 dots + only digits
+ (foundDots == 2 || foundDots == 3) &&
+ // and they should be all that came before now:
+ (foundDots + foundDigits == pos ||
+ // or maybe there was also exactly 1 colon that came after the last dot,
+ // and the digits, dots and colon were all that came before now:
+ (foundColons == 1 && firstColonLoc > lastDotLoc &&
+ foundDots + foundDigits + foundColons == pos))) {
+ // Hurray, we got ourselves some ipv4!
+ // At this point, there's no way we will do a keyword lookup, so just bail immediately:
+ return NS_OK;
+ }
+
+ if (*iter == '.') {
+ ++foundDots;
+ lastDotLoc = pos;
+ if (firstDotLoc == uint32_t(kNotFound)) {
+ firstDotLoc = pos;
+ }
+ } else if (*iter == ':') {
+ ++foundColons;
+ if (firstColonLoc == uint32_t(kNotFound)) {
+ firstColonLoc = pos;
+ }
+ } else if (*iter == ' ' && firstSpaceLoc == uint32_t(kNotFound)) {
+ firstSpaceLoc = pos;
+ } else if (*iter == '?' && firstQMarkLoc == uint32_t(kNotFound)) {
+ firstQMarkLoc = pos;
+ } else if ((*iter == '\'' || *iter == '"') &&
+ firstQuoteLoc == uint32_t(kNotFound)) {
+ firstQuoteLoc = pos;
+ } else if (*iter == '[') {
+ lastLSBracketLoc = pos;
+ } else if (*iter == ']') {
+ foundRSBrackets++;
+ } else if (*iter == '/') {
+ lastSlashLoc = pos;
+ } else if (nsCRT::IsAsciiAlpha(*iter)) {
+ hasAsciiAlpha = true;
+ } else if (nsCRT::IsAsciiDigit(*iter)) {
+ ++foundDigits;
+ }
+
+ pos++;
+ iter++;
+ }
+
+ if (lastLSBracketLoc > 0 || foundRSBrackets != 1) {
+ looksLikeIpv6 = false;
+ }
+
+ // If there are only colons and only hexadecimal characters ([a-z][0-9])
+ // enclosed in [], then don't do a keyword lookup
+ if (looksLikeIpv6) {
+ return NS_OK;
+ }
+
+ nsAutoCString asciiHost;
+ nsAutoCString host;
+
+ bool isValidAsciiHost =
+ aFixupInfo->mFixedURI &&
+ NS_SUCCEEDED(aFixupInfo->mFixedURI->GetAsciiHost(asciiHost)) &&
+ !asciiHost.IsEmpty();
+
+ bool isValidHost =
+ aFixupInfo->mFixedURI &&
+ NS_SUCCEEDED(aFixupInfo->mFixedURI->GetHost(host)) &&
+ !host.IsEmpty();
+
+ nsresult rv = NS_OK;
+ // We do keyword lookups if a space or quote preceded the dot, colon
+ // or question mark (or if the latter is not found, or if the input starts
+ // with a question mark)
+ if (((firstSpaceLoc < firstDotLoc || firstQuoteLoc < firstDotLoc) &&
+ (firstSpaceLoc < firstColonLoc || firstQuoteLoc < firstColonLoc) &&
+ (firstSpaceLoc < firstQMarkLoc || firstQuoteLoc < firstQMarkLoc)) ||
+ firstQMarkLoc == 0) {
+ rv = TryKeywordFixupForURIInfo(aFixupInfo->mOriginalInput, aFixupInfo,
+ aPostData);
+ // ... or when the host is the same as asciiHost and there are no
+ // characters from [a-z][A-Z]
+ } else if (isValidAsciiHost && isValidHost && !hasAsciiAlpha &&
+ host.EqualsIgnoreCase(asciiHost.get())) {
+ if (!sDNSFirstForSingleWords) {
+ rv = TryKeywordFixupForURIInfo(aFixupInfo->mOriginalInput, aFixupInfo,
+ aPostData);
+ }
+ }
+ // ... or if there is no question mark or colon, and there is either no
+ // dot, or exactly 1 and it is the first or last character of the input:
+ else if ((firstDotLoc == uint32_t(kNotFound) ||
+ (foundDots == 1 && (firstDotLoc == 0 ||
+ firstDotLoc == aURIString.Length() - 1))) &&
+ firstColonLoc == uint32_t(kNotFound) &&
+ firstQMarkLoc == uint32_t(kNotFound)) {
+ if (isValidAsciiHost && IsDomainWhitelisted(asciiHost, firstDotLoc)) {
+ return NS_OK;
+ }
+
+ // ... unless there are no dots, and a slash, and alpha characters, and
+ // this is a valid host:
+ if (firstDotLoc == uint32_t(kNotFound) &&
+ lastSlashLoc != uint32_t(kNotFound) &&
+ hasAsciiAlpha && isValidAsciiHost) {
+ return NS_OK;
+ }
+
+ // If we get here, we don't have a valid URI, or we did but the
+ // host is not whitelisted, so we do a keyword search *anyway*:
+ rv = TryKeywordFixupForURIInfo(aFixupInfo->mOriginalInput, aFixupInfo,
+ aPostData);
+ }
+ return rv;
+}
+
+bool
+nsDefaultURIFixup::IsDomainWhitelisted(const nsACString& aAsciiHost,
+ const uint32_t aDotLoc)
+{
+ if (sDNSFirstForSingleWords) {
+ return true;
+ }
+ // Check if this domain is whitelisted as an actual
+ // domain (which will prevent a keyword query)
+ // NB: any processing of the host here should stay in sync with
+ // code in the front-end(s) that set the pref.
+
+ nsAutoCString pref("browser.fixup.domainwhitelist.");
+
+ if (aDotLoc == aAsciiHost.Length() - 1) {
+ pref.Append(Substring(aAsciiHost, 0, aAsciiHost.Length() - 1));
+ } else {
+ pref.Append(aAsciiHost);
+ }
+
+ return Preferences::GetBool(pref.get(), false);
+}
+
+NS_IMETHODIMP
+nsDefaultURIFixup::IsDomainWhitelisted(const nsACString& aDomain,
+ const uint32_t aDotLoc,
+ bool* aResult)
+{
+ *aResult = IsDomainWhitelisted(aDomain, aDotLoc);
+ return NS_OK;
+}
+
+/* Implementation of nsIURIFixupInfo */
+NS_IMPL_ISUPPORTS(nsDefaultURIFixupInfo, nsIURIFixupInfo)
+
+nsDefaultURIFixupInfo::nsDefaultURIFixupInfo(const nsACString& aOriginalInput)
+ : mFixupChangedProtocol(false)
+ , mFixupCreatedAlternateURI(false)
+{
+ mOriginalInput = aOriginalInput;
+}
+
+nsDefaultURIFixupInfo::~nsDefaultURIFixupInfo()
+{
+}
+
+NS_IMETHODIMP
+nsDefaultURIFixupInfo::GetConsumer(nsISupports** aConsumer)
+{
+ *aConsumer = mConsumer;
+ NS_IF_ADDREF(*aConsumer);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDefaultURIFixupInfo::SetConsumer(nsISupports* aConsumer)
+{
+ mConsumer = aConsumer;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDefaultURIFixupInfo::GetPreferredURI(nsIURI** aPreferredURI)
+{
+ *aPreferredURI = mPreferredURI;
+ NS_IF_ADDREF(*aPreferredURI);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDefaultURIFixupInfo::GetFixedURI(nsIURI** aFixedURI)
+{
+ *aFixedURI = mFixedURI;
+ NS_IF_ADDREF(*aFixedURI);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDefaultURIFixupInfo::GetKeywordProviderName(nsAString& aResult)
+{
+ aResult = mKeywordProviderName;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDefaultURIFixupInfo::GetKeywordAsSent(nsAString& aResult)
+{
+ aResult = mKeywordAsSent;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDefaultURIFixupInfo::GetFixupChangedProtocol(bool* aResult)
+{
+ *aResult = mFixupChangedProtocol;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDefaultURIFixupInfo::GetFixupCreatedAlternateURI(bool* aResult)
+{
+ *aResult = mFixupCreatedAlternateURI;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDefaultURIFixupInfo::GetOriginalInput(nsACString& aResult)
+{
+ aResult = mOriginalInput;
+ return NS_OK;
+}
diff --git a/docshell/base/nsDefaultURIFixup.h b/docshell/base/nsDefaultURIFixup.h
new file mode 100644
index 000000000..6aed66ca5
--- /dev/null
+++ b/docshell/base/nsDefaultURIFixup.h
@@ -0,0 +1,69 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef NSDEFAULTURIFIXUP_H
+#define NSDEFAULTURIFIXUP_H
+
+#include "nsIURIFixup.h"
+
+class nsDefaultURIFixupInfo;
+
+/* Header file */
+class nsDefaultURIFixup : public nsIURIFixup
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIURIFIXUP
+
+ nsDefaultURIFixup();
+
+protected:
+ virtual ~nsDefaultURIFixup();
+
+private:
+ /* additional members */
+ nsresult FileURIFixup(const nsACString& aStringURI, nsIURI** aURI);
+ nsresult ConvertFileToStringURI(const nsACString& aIn, nsCString& aResult);
+ nsresult FixupURIProtocol(const nsACString& aIn,
+ nsDefaultURIFixupInfo* aFixupInfo,
+ nsIURI** aURI);
+ nsresult KeywordURIFixup(const nsACString& aStringURI,
+ nsDefaultURIFixupInfo* aFixupInfo,
+ nsIInputStream** aPostData);
+ nsresult TryKeywordFixupForURIInfo(const nsACString& aStringURI,
+ nsDefaultURIFixupInfo* aFixupInfo,
+ nsIInputStream** aPostData);
+ bool PossiblyByteExpandedFileName(const nsAString& aIn);
+ bool PossiblyHostPortUrl(const nsACString& aUrl);
+ bool MakeAlternateURI(nsIURI* aURI);
+ bool IsDomainWhitelisted(const nsACString& aAsciiHost,
+ const uint32_t aDotLoc);
+};
+
+class nsDefaultURIFixupInfo : public nsIURIFixupInfo
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIURIFIXUPINFO
+
+ explicit nsDefaultURIFixupInfo(const nsACString& aOriginalInput);
+
+ friend class nsDefaultURIFixup;
+
+protected:
+ virtual ~nsDefaultURIFixupInfo();
+
+private:
+ nsCOMPtr<nsISupports> mConsumer;
+ nsCOMPtr<nsIURI> mPreferredURI;
+ nsCOMPtr<nsIURI> mFixedURI;
+ bool mFixupChangedProtocol;
+ bool mFixupCreatedAlternateURI;
+ nsString mKeywordProviderName;
+ nsString mKeywordAsSent;
+ nsCString mOriginalInput;
+};
+#endif
diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp
new file mode 100644
index 000000000..ab119a016
--- /dev/null
+++ b/docshell/base/nsDocShell.cpp
@@ -0,0 +1,14853 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsDocShell.h"
+
+#include <algorithm>
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/AutoRestore.h"
+#include "mozilla/BasePrincipal.h"
+#include "mozilla/Casting.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/ChromeUtils.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/TabChild.h"
+#include "mozilla/dom/ProfileTimelineMarkerBinding.h"
+#include "mozilla/dom/ScreenOrientation.h"
+#include "mozilla/dom/ToJSValue.h"
+#include "mozilla/dom/PermissionMessageUtils.h"
+#include "mozilla/dom/workers/ServiceWorkerManager.h"
+#include "mozilla/EventStateManager.h"
+#include "mozilla/LoadInfo.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Services.h"
+#include "mozilla/StartupTimeline.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/Unused.h"
+#include "Navigator.h"
+#include "URIUtils.h"
+#include "mozilla/dom/DocGroup.h"
+#include "mozilla/dom/TabGroup.h"
+
+#include "nsIContent.h"
+#include "nsIContentInlines.h"
+#include "nsIDocument.h"
+#include "nsIDOMDocument.h"
+#include "nsIDOMElement.h"
+
+#include "nsArray.h"
+#include "nsArrayUtils.h"
+#include "nsICaptivePortalService.h"
+#include "nsIDOMStorage.h"
+#include "nsIContentViewer.h"
+#include "nsIDocumentLoaderFactory.h"
+#include "nsCURILoader.h"
+#include "nsDocShellCID.h"
+#include "nsDOMCID.h"
+#include "nsNetCID.h"
+#include "nsNetUtil.h"
+#include "mozilla/net/ReferrerPolicy.h"
+#include "nsRect.h"
+#include "prenv.h"
+#include "nsIDOMWindow.h"
+#include "nsIGlobalObject.h"
+#include "nsIViewSourceChannel.h"
+#include "nsIWebBrowserChrome.h"
+#include "nsPoint.h"
+#include "nsIObserverService.h"
+#include "nsIPrompt.h"
+#include "nsIAuthPrompt.h"
+#include "nsIAuthPrompt2.h"
+#include "nsIChannelEventSink.h"
+#include "nsIAsyncVerifyRedirectCallback.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsIScriptObjectPrincipal.h"
+#include "nsIScrollableFrame.h"
+#include "nsContentPolicyUtils.h" // NS_CheckContentLoadPolicy(...)
+#include "nsISeekableStream.h"
+#include "nsAutoPtr.h"
+#include "nsQueryObject.h"
+#include "nsIWritablePropertyBag2.h"
+#include "nsIAppShell.h"
+#include "nsWidgetsCID.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsView.h"
+#include "nsViewManager.h"
+#include "nsIScriptChannel.h"
+#include "nsITimedChannel.h"
+#include "nsIPrivacyTransitionObserver.h"
+#include "nsIReflowObserver.h"
+#include "nsIScrollObserver.h"
+#include "nsIDocShellTreeItem.h"
+#include "nsIChannel.h"
+#include "IHistory.h"
+#include "nsViewSourceHandler.h"
+#include "nsWhitespaceTokenizer.h"
+#include "nsICookieService.h"
+#include "nsIConsoleReportCollector.h"
+#include "nsObjectLoadingContent.h"
+
+// we want to explore making the document own the load group
+// so we can associate the document URI with the load group.
+// until this point, we have an evil hack:
+#include "nsIHttpChannelInternal.h"
+#include "nsPILoadGroupInternal.h"
+
+// Local Includes
+#include "nsDocShellLoadInfo.h"
+#include "nsCDefaultURIFixup.h"
+#include "nsDocShellEnumerator.h"
+#include "nsSHistory.h"
+#include "nsDocShellEditorData.h"
+#include "GeckoProfiler.h"
+#include "timeline/JavascriptTimelineMarker.h"
+
+// Helper Classes
+#include "nsError.h"
+#include "nsEscape.h"
+
+// Interfaces Needed
+#include "nsIFormPOSTActionChannel.h"
+#include "nsIUploadChannel.h"
+#include "nsIUploadChannel2.h"
+#include "nsIWebProgress.h"
+#include "nsILayoutHistoryState.h"
+#include "nsITimer.h"
+#include "nsISHistoryInternal.h"
+#include "nsIPrincipal.h"
+#include "nsNullPrincipal.h"
+#include "nsISHEntry.h"
+#include "nsIWindowWatcher.h"
+#include "nsIPromptFactory.h"
+#include "nsITransportSecurityInfo.h"
+#include "nsINode.h"
+#include "nsINSSErrorsService.h"
+#include "nsIApplicationCacheChannel.h"
+#include "nsIApplicationCacheContainer.h"
+#include "nsStreamUtils.h"
+#include "nsIController.h"
+#include "nsPICommandUpdater.h"
+#include "nsIDOMHTMLAnchorElement.h"
+#include "nsIWebBrowserChrome3.h"
+#include "nsITabChild.h"
+#include "nsISiteSecurityService.h"
+#include "nsStructuredCloneContainer.h"
+#include "nsIStructuredCloneContainer.h"
+#ifdef MOZ_PLACES
+#include "nsIFaviconService.h"
+#include "mozIPlacesPendingOperation.h"
+#include "mozIAsyncFavicons.h"
+#endif
+#include "nsINetworkPredictor.h"
+
+// Editor-related
+#include "nsIEditingSession.h"
+
+#include "nsPIDOMWindow.h"
+#include "nsGlobalWindow.h"
+#include "nsPIWindowRoot.h"
+#include "nsICachingChannel.h"
+#include "nsIMultiPartChannel.h"
+#include "nsIWyciwygChannel.h"
+
+// For reporting errors with the console service.
+// These can go away if error reporting is propagated up past nsDocShell.
+#include "nsIScriptError.h"
+
+// used to dispatch urls to default protocol handlers
+#include "nsCExternalHandlerService.h"
+#include "nsIExternalProtocolService.h"
+
+#include "nsFocusManager.h"
+
+#include "nsITextToSubURI.h"
+
+#include "nsIJARChannel.h"
+
+#include "mozilla/Logging.h"
+
+#include "nsISelectionDisplay.h"
+
+#include "nsIGlobalHistory2.h"
+
+#include "nsIFrame.h"
+#include "nsSubDocumentFrame.h"
+
+// for embedding
+#include "nsIWebBrowserChromeFocus.h"
+
+#if NS_PRINT_PREVIEW
+#include "nsIDocumentViewerPrint.h"
+#include "nsIWebBrowserPrint.h"
+#endif
+
+#include "nsContentUtils.h"
+#include "nsIContentSecurityPolicy.h"
+#include "nsILoadInfo.h"
+#include "nsSandboxFlags.h"
+#include "nsXULAppAPI.h"
+#include "nsDOMNavigationTiming.h"
+#include "nsISecurityUITelemetry.h"
+#include "nsIAppsService.h"
+#include "nsDSURIContentListener.h"
+#include "nsDocShellLoadTypes.h"
+#include "nsDocShellTransferableHooks.h"
+#include "nsICommandManager.h"
+#include "nsIDOMNode.h"
+#include "nsIDocShellTreeOwner.h"
+#include "nsIHttpChannel.h"
+#include "nsIIDNService.h"
+#include "nsIInputStreamChannel.h"
+#include "nsINestedURI.h"
+#include "nsISHContainer.h"
+#include "nsISHistory.h"
+#include "nsISecureBrowserUI.h"
+#include "nsISocketProvider.h"
+#include "nsIStringBundle.h"
+#include "nsIURIFixup.h"
+#include "nsIURILoader.h"
+#include "nsIURL.h"
+#include "nsIWebBrowserFind.h"
+#include "nsIWidget.h"
+#include "mozilla/dom/EncodingUtils.h"
+#include "mozilla/dom/PerformanceNavigation.h"
+#include "mozilla/dom/ScriptSettings.h"
+
+#ifdef MOZ_TOOLKIT_SEARCH
+#include "nsIBrowserSearchService.h"
+#endif
+
+#include "mozIThirdPartyUtil.h"
+
+static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
+
+#if defined(DEBUG_bryner) || defined(DEBUG_chb)
+//#define DEBUG_DOCSHELL_FOCUS
+#define DEBUG_PAGE_CACHE
+#endif
+
+#ifdef XP_WIN
+#include <process.h>
+#define getpid _getpid
+#else
+#include <unistd.h> // for getpid()
+#endif
+
+using namespace mozilla;
+using namespace mozilla::dom;
+using mozilla::dom::workers::ServiceWorkerManager;
+
+// True means sUseErrorPages has been added to
+// preferences var cache.
+static bool gAddedPreferencesVarCache = false;
+
+bool nsDocShell::sUseErrorPages = false;
+
+// Number of documents currently loading
+static int32_t gNumberOfDocumentsLoading = 0;
+
+// Global count of existing docshells.
+static int32_t gDocShellCount = 0;
+
+// Global count of docshells with the private attribute set
+static uint32_t gNumberOfPrivateDocShells = 0;
+
+// Global reference to the URI fixup service.
+nsIURIFixup* nsDocShell::sURIFixup = 0;
+
+// True means we validate window targets to prevent frameset
+// spoofing. Initialize this to a non-bolean value so we know to check
+// the pref on the creation of the first docshell.
+static uint32_t gValidateOrigin = 0xffffffff;
+
+// Hint for native dispatch of events on how long to delay after
+// all documents have loaded in milliseconds before favoring normal
+// native event dispatch priorites over performance
+// Can be overridden with docshell.event_starvation_delay_hint pref.
+#define NS_EVENT_STARVATION_DELAY_HINT 2000
+
+#ifdef DEBUG
+static mozilla::LazyLogModule gDocShellLog("nsDocShell");
+#endif
+static mozilla::LazyLogModule gDocShellLeakLog("nsDocShellLeak");;
+
+const char kBrandBundleURL[] = "chrome://branding/locale/brand.properties";
+const char kAppstringsBundleURL[] = "chrome://global/locale/appstrings.properties";
+
+static void
+FavorPerformanceHint(bool aPerfOverStarvation)
+{
+ nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
+ if (appShell) {
+ appShell->FavorPerformanceHint(
+ aPerfOverStarvation,
+ Preferences::GetUint("docshell.event_starvation_delay_hint",
+ NS_EVENT_STARVATION_DELAY_HINT));
+ }
+}
+
+//*****************************************************************************
+// <a ping> support
+//*****************************************************************************
+
+#define PREF_PINGS_ENABLED "browser.send_pings"
+#define PREF_PINGS_MAX_PER_LINK "browser.send_pings.max_per_link"
+#define PREF_PINGS_REQUIRE_SAME_HOST "browser.send_pings.require_same_host"
+
+// Check prefs to see if pings are enabled and if so what restrictions might
+// be applied.
+//
+// @param maxPerLink
+// This parameter returns the number of pings that are allowed per link click
+//
+// @param requireSameHost
+// This parameter returns true if pings are restricted to the same host as
+// the document in which the click occurs. If the same host restriction is
+// imposed, then we still allow for pings to cross over to different
+// protocols and ports for flexibility and because it is not possible to send
+// a ping via FTP.
+//
+// @returns
+// true if pings are enabled and false otherwise.
+//
+static bool
+PingsEnabled(int32_t* aMaxPerLink, bool* aRequireSameHost)
+{
+ bool allow = Preferences::GetBool(PREF_PINGS_ENABLED, false);
+
+ *aMaxPerLink = 1;
+ *aRequireSameHost = true;
+
+ if (allow) {
+ Preferences::GetInt(PREF_PINGS_MAX_PER_LINK, aMaxPerLink);
+ Preferences::GetBool(PREF_PINGS_REQUIRE_SAME_HOST, aRequireSameHost);
+ }
+
+ return allow;
+}
+
+typedef void (*ForEachPingCallback)(void* closure, nsIContent* content,
+ nsIURI* uri, nsIIOService* ios);
+
+static bool
+IsElementAnchor(nsIContent* aContent)
+{
+ // Make sure we are dealing with either an <A> or <AREA> element in the HTML
+ // or XHTML namespace.
+ return aContent->IsAnyOfHTMLElements(nsGkAtoms::a, nsGkAtoms::area);
+}
+
+static void
+ForEachPing(nsIContent* aContent, ForEachPingCallback aCallback, void* aClosure)
+{
+ // NOTE: Using nsIDOMHTMLAnchorElement::GetPing isn't really worth it here
+ // since we'd still need to parse the resulting string. Instead, we
+ // just parse the raw attribute. It might be nice if the content node
+ // implemented an interface that exposed an enumeration of nsIURIs.
+
+ // Make sure we are dealing with either an <A> or <AREA> element in the HTML
+ // or XHTML namespace.
+ if (!IsElementAnchor(aContent)) {
+ return;
+ }
+
+ nsCOMPtr<nsIAtom> pingAtom = NS_Atomize("ping");
+ if (!pingAtom) {
+ return;
+ }
+
+ nsAutoString value;
+ aContent->GetAttr(kNameSpaceID_None, pingAtom, value);
+ if (value.IsEmpty()) {
+ return;
+ }
+
+ nsCOMPtr<nsIIOService> ios = do_GetIOService();
+ if (!ios) {
+ return;
+ }
+
+ nsIDocument* doc = aContent->OwnerDoc();
+
+ nsWhitespaceTokenizer tokenizer(value);
+
+ while (tokenizer.hasMoreTokens()) {
+ nsCOMPtr<nsIURI> uri, baseURI = aContent->GetBaseURI();
+ ios->NewURI(NS_ConvertUTF16toUTF8(tokenizer.nextToken()),
+ doc->GetDocumentCharacterSet().get(),
+ baseURI, getter_AddRefs(uri));
+ // if we can't generate a valid URI, then there is nothing to do
+ if (!uri) {
+ continue;
+ }
+ // Explicitly not allow loading data: URIs
+ bool isDataScheme =
+ (NS_SUCCEEDED(uri->SchemeIs("data", &isDataScheme)) && isDataScheme);
+
+ if (!isDataScheme) {
+ aCallback(aClosure, aContent, uri, ios);
+ }
+ }
+}
+
+//----------------------------------------------------------------------
+
+// We wait this many milliseconds before killing the ping channel...
+#define PING_TIMEOUT 10000
+
+static void
+OnPingTimeout(nsITimer* aTimer, void* aClosure)
+{
+ nsILoadGroup* loadGroup = static_cast<nsILoadGroup*>(aClosure);
+ if (loadGroup) {
+ loadGroup->Cancel(NS_ERROR_ABORT);
+ }
+}
+
+class nsPingListener final
+ : public nsIStreamListener
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIREQUESTOBSERVER
+ NS_DECL_NSISTREAMLISTENER
+
+ nsPingListener()
+ {
+ }
+
+ void SetLoadGroup(nsILoadGroup* aLoadGroup) {
+ mLoadGroup = aLoadGroup;
+ }
+
+ nsresult StartTimeout();
+
+private:
+ ~nsPingListener();
+
+ nsCOMPtr<nsILoadGroup> mLoadGroup;
+ nsCOMPtr<nsITimer> mTimer;
+};
+
+NS_IMPL_ISUPPORTS(nsPingListener, nsIStreamListener, nsIRequestObserver)
+
+nsPingListener::~nsPingListener()
+{
+ if (mTimer) {
+ mTimer->Cancel();
+ mTimer = nullptr;
+ }
+}
+
+nsresult
+nsPingListener::StartTimeout()
+{
+ nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
+
+ if (timer) {
+ nsresult rv = timer->InitWithFuncCallback(OnPingTimeout, mLoadGroup,
+ PING_TIMEOUT,
+ nsITimer::TYPE_ONE_SHOT);
+ if (NS_SUCCEEDED(rv)) {
+ mTimer = timer;
+ return NS_OK;
+ }
+ }
+
+ return NS_ERROR_OUT_OF_MEMORY;
+}
+
+NS_IMETHODIMP
+nsPingListener::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPingListener::OnDataAvailable(nsIRequest* aRequest, nsISupports* aContext,
+ nsIInputStream* aStream, uint64_t aOffset,
+ uint32_t aCount)
+{
+ uint32_t result;
+ return aStream->ReadSegments(NS_DiscardSegment, nullptr, aCount, &result);
+}
+
+NS_IMETHODIMP
+nsPingListener::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext,
+ nsresult aStatus)
+{
+ mLoadGroup = nullptr;
+
+ if (mTimer) {
+ mTimer->Cancel();
+ mTimer = nullptr;
+ }
+
+ return NS_OK;
+}
+
+struct MOZ_STACK_CLASS SendPingInfo
+{
+ int32_t numPings;
+ int32_t maxPings;
+ bool requireSameHost;
+ nsIURI* target;
+ nsIURI* referrer;
+ nsIDocShell* docShell;
+ uint32_t referrerPolicy;
+};
+
+static void
+SendPing(void* aClosure, nsIContent* aContent, nsIURI* aURI,
+ nsIIOService* aIOService)
+{
+ SendPingInfo* info = static_cast<SendPingInfo*>(aClosure);
+ if (info->maxPings > -1 && info->numPings >= info->maxPings) {
+ return;
+ }
+
+ nsIDocument* doc = aContent->OwnerDoc();
+
+ nsCOMPtr<nsIChannel> chan;
+ NS_NewChannel(getter_AddRefs(chan),
+ aURI,
+ doc,
+ info->requireSameHost
+ ? nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED
+ : nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
+ nsIContentPolicy::TYPE_PING,
+ nullptr, // aLoadGroup
+ nullptr, // aCallbacks
+ nsIRequest::LOAD_NORMAL, // aLoadFlags,
+ aIOService);
+
+ if (!chan) {
+ return;
+ }
+
+ // Don't bother caching the result of this URI load, but do not exempt
+ // it from Safe Browsing.
+ chan->SetLoadFlags(nsIRequest::INHIBIT_CACHING | nsIChannel::LOAD_CLASSIFY_URI);
+
+ nsCOMPtr<nsIHttpChannel> httpChan = do_QueryInterface(chan);
+ if (!httpChan) {
+ return;
+ }
+
+ // This is needed in order for 3rd-party cookie blocking to work.
+ nsCOMPtr<nsIHttpChannelInternal> httpInternal = do_QueryInterface(httpChan);
+ if (httpInternal) {
+ httpInternal->SetDocumentURI(doc->GetDocumentURI());
+ }
+
+ httpChan->SetRequestMethod(NS_LITERAL_CSTRING("POST"));
+
+ // Remove extraneous request headers (to reduce request size)
+ httpChan->SetRequestHeader(NS_LITERAL_CSTRING("accept"),
+ EmptyCString(), false);
+ httpChan->SetRequestHeader(NS_LITERAL_CSTRING("accept-language"),
+ EmptyCString(), false);
+ httpChan->SetRequestHeader(NS_LITERAL_CSTRING("accept-encoding"),
+ EmptyCString(), false);
+
+ // Always send a Ping-To header.
+ nsAutoCString pingTo;
+ if (NS_SUCCEEDED(info->target->GetSpec(pingTo))) {
+ httpChan->SetRequestHeader(NS_LITERAL_CSTRING("Ping-To"), pingTo, false);
+ }
+
+ nsCOMPtr<nsIScriptSecurityManager> sm =
+ do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
+
+ if (sm && info->referrer) {
+ bool referrerIsSecure;
+ uint32_t flags = nsIProtocolHandler::URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT;
+ nsresult rv = NS_URIChainHasFlags(info->referrer, flags, &referrerIsSecure);
+
+ // Default to sending less data if NS_URIChainHasFlags() fails.
+ referrerIsSecure = NS_FAILED(rv) || referrerIsSecure;
+
+ bool sameOrigin =
+ NS_SUCCEEDED(sm->CheckSameOriginURI(info->referrer, aURI, false));
+
+ // If both the address of the document containing the hyperlink being
+ // audited and "ping URL" have the same origin or the document containing
+ // the hyperlink being audited was not retrieved over an encrypted
+ // connection, send a Ping-From header.
+ if (sameOrigin || !referrerIsSecure) {
+ nsAutoCString pingFrom;
+ if (NS_SUCCEEDED(info->referrer->GetSpec(pingFrom))) {
+ httpChan->SetRequestHeader(NS_LITERAL_CSTRING("Ping-From"), pingFrom,
+ false);
+ }
+ }
+
+ // If the document containing the hyperlink being audited was not retrieved
+ // over an encrypted connection and its address does not have the same
+ // origin as "ping URL", send a referrer.
+ if (!sameOrigin && !referrerIsSecure) {
+ httpChan->SetReferrerWithPolicy(info->referrer, info->referrerPolicy);
+ }
+ }
+
+ nsCOMPtr<nsIUploadChannel2> uploadChan = do_QueryInterface(httpChan);
+ if (!uploadChan) {
+ return;
+ }
+
+ NS_NAMED_LITERAL_CSTRING(uploadData, "PING");
+
+ nsCOMPtr<nsIInputStream> uploadStream;
+ NS_NewPostDataStream(getter_AddRefs(uploadStream), false, uploadData);
+ if (!uploadStream) {
+ return;
+ }
+
+ uploadChan->ExplicitSetUploadStream(uploadStream,
+ NS_LITERAL_CSTRING("text/ping"),
+ uploadData.Length(),
+ NS_LITERAL_CSTRING("POST"), false);
+
+ // The channel needs to have a loadgroup associated with it, so that we can
+ // cancel the channel and any redirected channels it may create.
+ nsCOMPtr<nsILoadGroup> loadGroup = do_CreateInstance(NS_LOADGROUP_CONTRACTID);
+ if (!loadGroup) {
+ return;
+ }
+ nsCOMPtr<nsIInterfaceRequestor> callbacks = do_QueryInterface(info->docShell);
+ loadGroup->SetNotificationCallbacks(callbacks);
+ chan->SetLoadGroup(loadGroup);
+
+ RefPtr<nsPingListener> pingListener = new nsPingListener();
+ chan->AsyncOpen2(pingListener);
+
+ // Even if AsyncOpen failed, we still count this as a successful ping. It's
+ // possible that AsyncOpen may have failed after triggering some background
+ // process that may have written something to the network.
+ info->numPings++;
+
+ // Prevent ping requests from stalling and never being garbage collected...
+ if (NS_FAILED(pingListener->StartTimeout())) {
+ // If we failed to setup the timer, then we should just cancel the channel
+ // because we won't be able to ensure that it goes away in a timely manner.
+ chan->Cancel(NS_ERROR_ABORT);
+ return;
+ }
+ // if the channel openend successfully, then make the pingListener hold
+ // a strong reference to the loadgroup which is released in ::OnStopRequest
+ pingListener->SetLoadGroup(loadGroup);
+}
+
+// Spec: http://whatwg.org/specs/web-apps/current-work/#ping
+static void
+DispatchPings(nsIDocShell* aDocShell,
+ nsIContent* aContent,
+ nsIURI* aTarget,
+ nsIURI* aReferrer,
+ uint32_t aReferrerPolicy)
+{
+ SendPingInfo info;
+
+ if (!PingsEnabled(&info.maxPings, &info.requireSameHost)) {
+ return;
+ }
+ if (info.maxPings == 0) {
+ return;
+ }
+
+ info.numPings = 0;
+ info.target = aTarget;
+ info.referrer = aReferrer;
+ info.referrerPolicy = aReferrerPolicy;
+ info.docShell = aDocShell;
+
+ ForEachPing(aContent, SendPing, &info);
+}
+
+static nsDOMNavigationTiming::Type
+ConvertLoadTypeToNavigationType(uint32_t aLoadType)
+{
+ // Not initialized, assume it's normal load.
+ if (aLoadType == 0) {
+ aLoadType = LOAD_NORMAL;
+ }
+
+ auto result = nsDOMNavigationTiming::TYPE_RESERVED;
+ switch (aLoadType) {
+ case LOAD_NORMAL:
+ case LOAD_NORMAL_EXTERNAL:
+ case LOAD_NORMAL_BYPASS_CACHE:
+ case LOAD_NORMAL_BYPASS_PROXY:
+ case LOAD_NORMAL_BYPASS_PROXY_AND_CACHE:
+ case LOAD_NORMAL_REPLACE:
+ case LOAD_NORMAL_ALLOW_MIXED_CONTENT:
+ case LOAD_LINK:
+ case LOAD_STOP_CONTENT:
+ case LOAD_REPLACE_BYPASS_CACHE:
+ result = nsDOMNavigationTiming::TYPE_NAVIGATE;
+ break;
+ case LOAD_HISTORY:
+ result = nsDOMNavigationTiming::TYPE_BACK_FORWARD;
+ break;
+ case LOAD_RELOAD_NORMAL:
+ case LOAD_RELOAD_CHARSET_CHANGE:
+ case LOAD_RELOAD_BYPASS_CACHE:
+ case LOAD_RELOAD_BYPASS_PROXY:
+ case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
+ case LOAD_RELOAD_ALLOW_MIXED_CONTENT:
+ result = nsDOMNavigationTiming::TYPE_RELOAD;
+ break;
+ case LOAD_STOP_CONTENT_AND_REPLACE:
+ case LOAD_REFRESH:
+ case LOAD_BYPASS_HISTORY:
+ case LOAD_ERROR_PAGE:
+ case LOAD_PUSHSTATE:
+ result = nsDOMNavigationTiming::TYPE_RESERVED;
+ break;
+ default:
+ // NS_NOTREACHED("Unexpected load type value");
+ result = nsDOMNavigationTiming::TYPE_RESERVED;
+ break;
+ }
+
+ return result;
+}
+
+static nsISHEntry* GetRootSHEntry(nsISHEntry* aEntry);
+
+static void
+IncreasePrivateDocShellCount()
+{
+ gNumberOfPrivateDocShells++;
+ if (gNumberOfPrivateDocShells > 1 ||
+ !XRE_IsContentProcess()) {
+ return;
+ }
+
+ mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton();
+ cc->SendPrivateDocShellsExist(true);
+}
+
+static void
+DecreasePrivateDocShellCount()
+{
+ MOZ_ASSERT(gNumberOfPrivateDocShells > 0);
+ gNumberOfPrivateDocShells--;
+ if (!gNumberOfPrivateDocShells) {
+ if (XRE_IsContentProcess()) {
+ dom::ContentChild* cc = dom::ContentChild::GetSingleton();
+ cc->SendPrivateDocShellsExist(false);
+ return;
+ }
+
+ nsCOMPtr<nsIObserverService> obsvc = services::GetObserverService();
+ if (obsvc) {
+ obsvc->NotifyObservers(nullptr, "last-pb-context-exited", nullptr);
+ }
+ }
+}
+
+static uint64_t gDocshellIDCounter = 0;
+
+nsDocShell::nsDocShell()
+ : nsDocLoader()
+ , mDefaultScrollbarPref(Scrollbar_Auto, Scrollbar_Auto)
+ , mTreeOwner(nullptr)
+ , mChromeEventHandler(nullptr)
+ , mCharsetReloadState(eCharsetReloadInit)
+ , mChildOffset(0)
+ , mBusyFlags(BUSY_FLAGS_NONE)
+ , mAppType(nsIDocShell::APP_TYPE_UNKNOWN)
+ , mLoadType(0)
+ , mMarginWidth(-1)
+ , mMarginHeight(-1)
+ , mItemType(typeContent)
+ , mPreviousTransIndex(-1)
+ , mLoadedTransIndex(-1)
+ , mSandboxFlags(0)
+ , mOrientationLock(eScreenOrientation_None)
+ , mFullscreenAllowed(CHECK_ATTRIBUTES)
+ , mCreated(false)
+ , mAllowSubframes(true)
+ , mAllowPlugins(true)
+ , mAllowJavascript(true)
+ , mAllowMetaRedirects(true)
+ , mAllowImages(true)
+ , mAllowMedia(true)
+ , mAllowDNSPrefetch(true)
+ , mAllowWindowControl(true)
+ , mAllowContentRetargeting(true)
+ , mAllowContentRetargetingOnChildren(true)
+ , mUseErrorPages(false)
+ , mObserveErrorPages(true)
+ , mAllowAuth(true)
+ , mAllowKeywordFixup(false)
+ , mIsOffScreenBrowser(false)
+ , mIsActive(true)
+ , mDisableMetaRefreshWhenInactive(false)
+ , mIsPrerendered(false)
+ , mIsAppTab(false)
+ , mUseGlobalHistory(false)
+ , mUseRemoteTabs(false)
+ , mDeviceSizeIsPageSize(false)
+ , mWindowDraggingAllowed(false)
+ , mInFrameSwap(false)
+ , mInheritPrivateBrowsingId(true)
+ , mCanExecuteScripts(false)
+ , mFiredUnloadEvent(false)
+ , mEODForCurrentDocument(false)
+ , mURIResultedInDocument(false)
+ , mIsBeingDestroyed(false)
+ , mIsExecutingOnLoadHandler(false)
+ , mIsPrintingOrPP(false)
+ , mSavingOldViewer(false)
+ , mAffectPrivateSessionLifetime(true)
+ , mInvisible(false)
+ , mHasLoadedNonBlankURI(false)
+ , mBlankTiming(false)
+ , mCreatingDocument(false)
+#ifdef DEBUG
+ , mInEnsureScriptEnv(false)
+#endif
+ , mDefaultLoadFlags(nsIRequest::LOAD_NORMAL)
+ , mFrameType(FRAME_TYPE_REGULAR)
+ , mPrivateBrowsingId(0)
+ , mParentCharsetSource(0)
+ , mJSRunToCompletionDepth(0)
+ , mTouchEventsOverride(nsIDocShell::TOUCHEVENTS_OVERRIDE_NONE)
+{
+ AssertOriginAttributesMatchPrivateBrowsing();
+ mHistoryID = ++gDocshellIDCounter;
+ if (gDocShellCount++ == 0) {
+ NS_ASSERTION(sURIFixup == nullptr,
+ "Huh, sURIFixup not null in first nsDocShell ctor!");
+
+ CallGetService(NS_URIFIXUP_CONTRACTID, &sURIFixup);
+ }
+
+ MOZ_LOG(gDocShellLeakLog, LogLevel::Debug, ("DOCSHELL %p created\n", this));
+
+#ifdef DEBUG
+ // We're counting the number of |nsDocShells| to help find leaks
+ ++gNumberOfDocShells;
+ if (!PR_GetEnv("MOZ_QUIET")) {
+ printf_stderr("++DOCSHELL %p == %ld [pid = %d] [id = %llu]\n",
+ (void*)this,
+ gNumberOfDocShells,
+ getpid(),
+ AssertedCast<unsigned long long>(mHistoryID));
+ }
+#endif
+}
+
+nsDocShell::~nsDocShell()
+{
+ MOZ_ASSERT(!mObserved);
+
+ // Avoid notifying observers while we're in the dtor.
+ mIsBeingDestroyed = true;
+
+ Destroy();
+
+ nsCOMPtr<nsISHistoryInternal> shPrivate(do_QueryInterface(mSessionHistory));
+ if (shPrivate) {
+ shPrivate->SetRootDocShell(nullptr);
+ }
+
+ if (--gDocShellCount == 0) {
+ NS_IF_RELEASE(sURIFixup);
+ }
+
+ if (gDocShellLeakLog) {
+ MOZ_LOG(gDocShellLeakLog, LogLevel::Debug, ("DOCSHELL %p destroyed\n", this));
+ }
+
+#ifdef DEBUG
+ // We're counting the number of |nsDocShells| to help find leaks
+ --gNumberOfDocShells;
+ if (!PR_GetEnv("MOZ_QUIET")) {
+ printf_stderr("--DOCSHELL %p == %ld [pid = %d] [id = %llu]\n",
+ (void*)this,
+ gNumberOfDocShells,
+ getpid(),
+ AssertedCast<unsigned long long>(mHistoryID));
+ }
+#endif
+}
+
+nsresult
+nsDocShell::Init()
+{
+ nsresult rv = nsDocLoader::Init();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_ASSERTION(mLoadGroup, "Something went wrong!");
+
+ mContentListener = new nsDSURIContentListener(this);
+ rv = mContentListener->Init();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // We want to hold a strong ref to the loadgroup, so it better hold a weak
+ // ref to us... use an InterfaceRequestorProxy to do this.
+ nsCOMPtr<nsIInterfaceRequestor> proxy =
+ new InterfaceRequestorProxy(static_cast<nsIInterfaceRequestor*>(this));
+ mLoadGroup->SetNotificationCallbacks(proxy);
+
+ rv = nsDocLoader::AddDocLoaderAsChildOfRoot(this);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Add as |this| a progress listener to itself. A little weird, but
+ // simpler than reproducing all the listener-notification logic in
+ // overrides of the various methods via which nsDocLoader can be
+ // notified. Note that this holds an nsWeakPtr to ourselves, so it's ok.
+ return AddProgressListener(this, nsIWebProgress::NOTIFY_STATE_DOCUMENT |
+ nsIWebProgress::NOTIFY_STATE_NETWORK);
+}
+
+void
+nsDocShell::DestroyChildren()
+{
+ nsCOMPtr<nsIDocShellTreeItem> shell;
+ nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
+ while (iter.HasMore()) {
+ shell = do_QueryObject(iter.GetNext());
+ NS_ASSERTION(shell, "docshell has null child");
+
+ if (shell) {
+ shell->SetTreeOwner(nullptr);
+ }
+ }
+
+ nsDocLoader::DestroyChildren();
+}
+
+NS_IMPL_ADDREF_INHERITED(nsDocShell, nsDocLoader)
+NS_IMPL_RELEASE_INHERITED(nsDocShell, nsDocLoader)
+
+NS_INTERFACE_MAP_BEGIN(nsDocShell)
+ NS_INTERFACE_MAP_ENTRY(nsIDocShell)
+ NS_INTERFACE_MAP_ENTRY(nsIDocShellTreeItem)
+ NS_INTERFACE_MAP_ENTRY(nsIWebNavigation)
+ NS_INTERFACE_MAP_ENTRY(nsIBaseWindow)
+ NS_INTERFACE_MAP_ENTRY(nsIScrollable)
+ NS_INTERFACE_MAP_ENTRY(nsITextScroll)
+ NS_INTERFACE_MAP_ENTRY(nsIDocCharset)
+ NS_INTERFACE_MAP_ENTRY(nsIRefreshURI)
+ NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
+ NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
+ NS_INTERFACE_MAP_ENTRY(nsIContentViewerContainer)
+ NS_INTERFACE_MAP_ENTRY(nsIWebPageDescriptor)
+ NS_INTERFACE_MAP_ENTRY(nsIAuthPromptProvider)
+ NS_INTERFACE_MAP_ENTRY(nsILoadContext)
+ NS_INTERFACE_MAP_ENTRY(nsIWebShellServices)
+ NS_INTERFACE_MAP_ENTRY(nsILinkHandler)
+ NS_INTERFACE_MAP_ENTRY(nsIClipboardCommands)
+ NS_INTERFACE_MAP_ENTRY(nsIDOMStorageManager)
+ NS_INTERFACE_MAP_ENTRY(nsINetworkInterceptController)
+ NS_INTERFACE_MAP_ENTRY(nsIDeprecationWarner)
+NS_INTERFACE_MAP_END_INHERITING(nsDocLoader)
+
+NS_IMETHODIMP
+nsDocShell::GetInterface(const nsIID& aIID, void** aSink)
+{
+ NS_PRECONDITION(aSink, "null out param");
+
+ *aSink = nullptr;
+
+ if (aIID.Equals(NS_GET_IID(nsICommandManager))) {
+ NS_ENSURE_SUCCESS(EnsureCommandHandler(), NS_ERROR_FAILURE);
+ *aSink = mCommandManager;
+ } else if (aIID.Equals(NS_GET_IID(nsIURIContentListener))) {
+ *aSink = mContentListener;
+ } else if ((aIID.Equals(NS_GET_IID(nsIScriptGlobalObject)) ||
+ aIID.Equals(NS_GET_IID(nsIGlobalObject)) ||
+ aIID.Equals(NS_GET_IID(nsPIDOMWindowOuter)) ||
+ aIID.Equals(NS_GET_IID(mozIDOMWindowProxy)) ||
+ aIID.Equals(NS_GET_IID(nsIDOMWindow)) ||
+ aIID.Equals(NS_GET_IID(nsIDOMWindowInternal))) &&
+ NS_SUCCEEDED(EnsureScriptEnvironment())) {
+ return mScriptGlobal->QueryInterface(aIID, aSink);
+ } else if (aIID.Equals(NS_GET_IID(nsIDOMDocument)) &&
+ NS_SUCCEEDED(EnsureContentViewer())) {
+ mContentViewer->GetDOMDocument((nsIDOMDocument**)aSink);
+ return *aSink ? NS_OK : NS_NOINTERFACE;
+ } else if (aIID.Equals(NS_GET_IID(nsIDocument)) &&
+ NS_SUCCEEDED(EnsureContentViewer())) {
+ nsCOMPtr<nsIDocument> doc = mContentViewer->GetDocument();
+ doc.forget(aSink);
+ return *aSink ? NS_OK : NS_NOINTERFACE;
+ } else if (aIID.Equals(NS_GET_IID(nsIApplicationCacheContainer))) {
+ *aSink = nullptr;
+
+ // Return application cache associated with this docshell, if any
+
+ nsCOMPtr<nsIContentViewer> contentViewer;
+ GetContentViewer(getter_AddRefs(contentViewer));
+ if (!contentViewer) {
+ return NS_ERROR_NO_INTERFACE;
+ }
+
+ nsCOMPtr<nsIDOMDocument> domDoc;
+ contentViewer->GetDOMDocument(getter_AddRefs(domDoc));
+ NS_ASSERTION(domDoc, "Should have a document.");
+ if (!domDoc) {
+ return NS_ERROR_NO_INTERFACE;
+ }
+
+#if defined(DEBUG)
+ MOZ_LOG(gDocShellLog, LogLevel::Debug,
+ ("nsDocShell[%p]: returning app cache container %p",
+ this, domDoc.get()));
+#endif
+ return domDoc->QueryInterface(aIID, aSink);
+ } else if (aIID.Equals(NS_GET_IID(nsIPrompt)) &&
+ NS_SUCCEEDED(EnsureScriptEnvironment())) {
+ nsresult rv;
+ nsCOMPtr<nsIWindowWatcher> wwatch =
+ do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Get the an auth prompter for our window so that the parenting
+ // of the dialogs works as it should when using tabs.
+ nsIPrompt* prompt;
+ rv = wwatch->GetNewPrompter(mScriptGlobal->AsOuter(), &prompt);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ *aSink = prompt;
+ return NS_OK;
+ } else if (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) ||
+ aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) {
+ return NS_SUCCEEDED(GetAuthPrompt(PROMPT_NORMAL, aIID, aSink)) ?
+ NS_OK : NS_NOINTERFACE;
+ } else if (aIID.Equals(NS_GET_IID(nsISHistory))) {
+ nsCOMPtr<nsISHistory> shistory;
+ nsresult rv = GetSessionHistory(getter_AddRefs(shistory));
+ if (NS_SUCCEEDED(rv) && shistory) {
+ shistory.forget(aSink);
+ return NS_OK;
+ }
+ return NS_NOINTERFACE;
+ } else if (aIID.Equals(NS_GET_IID(nsIWebBrowserFind))) {
+ nsresult rv = EnsureFind();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ *aSink = mFind;
+ NS_ADDREF((nsISupports*)*aSink);
+ return NS_OK;
+ } else if (aIID.Equals(NS_GET_IID(nsIEditingSession))) {
+ nsCOMPtr<nsIEditingSession> es;
+ GetEditingSession(getter_AddRefs(es));
+ es.forget(aSink);
+ return *aSink ? NS_OK : NS_NOINTERFACE;
+ } else if (aIID.Equals(NS_GET_IID(nsIClipboardDragDropHookList)) &&
+ NS_SUCCEEDED(EnsureTransferableHookData())) {
+ *aSink = mTransferableHookData;
+ NS_ADDREF((nsISupports*)*aSink);
+ return NS_OK;
+ } else if (aIID.Equals(NS_GET_IID(nsISelectionDisplay))) {
+ nsIPresShell* shell = GetPresShell();
+ if (shell) {
+ return shell->QueryInterface(aIID, aSink);
+ }
+ } else if (aIID.Equals(NS_GET_IID(nsIDocShellTreeOwner))) {
+ nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
+ nsresult rv = GetTreeOwner(getter_AddRefs(treeOwner));
+ if (NS_SUCCEEDED(rv) && treeOwner) {
+ return treeOwner->QueryInterface(aIID, aSink);
+ }
+ } else if (aIID.Equals(NS_GET_IID(nsITabChild))) {
+ *aSink = GetTabChild().take();
+ return *aSink ? NS_OK : NS_ERROR_FAILURE;
+ } else if (aIID.Equals(NS_GET_IID(nsIContentFrameMessageManager))) {
+ nsCOMPtr<nsITabChild> tabChild =
+ do_GetInterface(static_cast<nsIDocShell*>(this));
+ nsCOMPtr<nsIContentFrameMessageManager> mm;
+ if (tabChild) {
+ tabChild->GetMessageManager(getter_AddRefs(mm));
+ } else {
+ if (nsPIDOMWindowOuter* win = GetWindow()) {
+ mm = do_QueryInterface(win->GetParentTarget());
+ }
+ }
+ *aSink = mm.get();
+ } else {
+ return nsDocLoader::GetInterface(aIID, aSink);
+ }
+
+ NS_IF_ADDREF(((nsISupports*)*aSink));
+ return *aSink ? NS_OK : NS_NOINTERFACE;
+}
+
+uint32_t
+nsDocShell::ConvertDocShellLoadInfoToLoadType(
+ nsDocShellInfoLoadType aDocShellLoadType)
+{
+ uint32_t loadType = LOAD_NORMAL;
+
+ switch (aDocShellLoadType) {
+ case nsIDocShellLoadInfo::loadNormal:
+ loadType = LOAD_NORMAL;
+ break;
+ case nsIDocShellLoadInfo::loadNormalReplace:
+ loadType = LOAD_NORMAL_REPLACE;
+ break;
+ case nsIDocShellLoadInfo::loadNormalExternal:
+ loadType = LOAD_NORMAL_EXTERNAL;
+ break;
+ case nsIDocShellLoadInfo::loadHistory:
+ loadType = LOAD_HISTORY;
+ break;
+ case nsIDocShellLoadInfo::loadNormalBypassCache:
+ loadType = LOAD_NORMAL_BYPASS_CACHE;
+ break;
+ case nsIDocShellLoadInfo::loadNormalBypassProxy:
+ loadType = LOAD_NORMAL_BYPASS_PROXY;
+ break;
+ case nsIDocShellLoadInfo::loadNormalBypassProxyAndCache:
+ loadType = LOAD_NORMAL_BYPASS_PROXY_AND_CACHE;
+ break;
+ case nsIDocShellLoadInfo::loadNormalAllowMixedContent:
+ loadType = LOAD_NORMAL_ALLOW_MIXED_CONTENT;
+ break;
+ case nsIDocShellLoadInfo::loadReloadNormal:
+ loadType = LOAD_RELOAD_NORMAL;
+ break;
+ case nsIDocShellLoadInfo::loadReloadCharsetChange:
+ loadType = LOAD_RELOAD_CHARSET_CHANGE;
+ break;
+ case nsIDocShellLoadInfo::loadReloadBypassCache:
+ loadType = LOAD_RELOAD_BYPASS_CACHE;
+ break;
+ case nsIDocShellLoadInfo::loadReloadBypassProxy:
+ loadType = LOAD_RELOAD_BYPASS_PROXY;
+ break;
+ case nsIDocShellLoadInfo::loadReloadBypassProxyAndCache:
+ loadType = LOAD_RELOAD_BYPASS_PROXY_AND_CACHE;
+ break;
+ case nsIDocShellLoadInfo::loadLink:
+ loadType = LOAD_LINK;
+ break;
+ case nsIDocShellLoadInfo::loadRefresh:
+ loadType = LOAD_REFRESH;
+ break;
+ case nsIDocShellLoadInfo::loadBypassHistory:
+ loadType = LOAD_BYPASS_HISTORY;
+ break;
+ case nsIDocShellLoadInfo::loadStopContent:
+ loadType = LOAD_STOP_CONTENT;
+ break;
+ case nsIDocShellLoadInfo::loadStopContentAndReplace:
+ loadType = LOAD_STOP_CONTENT_AND_REPLACE;
+ break;
+ case nsIDocShellLoadInfo::loadPushState:
+ loadType = LOAD_PUSHSTATE;
+ break;
+ case nsIDocShellLoadInfo::loadReplaceBypassCache:
+ loadType = LOAD_REPLACE_BYPASS_CACHE;
+ break;
+ case nsIDocShellLoadInfo::loadReloadMixedContent:
+ loadType = LOAD_RELOAD_ALLOW_MIXED_CONTENT;
+ break;
+ default:
+ NS_NOTREACHED("Unexpected nsDocShellInfoLoadType value");
+ }
+
+ return loadType;
+}
+
+nsDocShellInfoLoadType
+nsDocShell::ConvertLoadTypeToDocShellLoadInfo(uint32_t aLoadType)
+{
+ nsDocShellInfoLoadType docShellLoadType = nsIDocShellLoadInfo::loadNormal;
+ switch (aLoadType) {
+ case LOAD_NORMAL:
+ docShellLoadType = nsIDocShellLoadInfo::loadNormal;
+ break;
+ case LOAD_NORMAL_REPLACE:
+ docShellLoadType = nsIDocShellLoadInfo::loadNormalReplace;
+ break;
+ case LOAD_NORMAL_EXTERNAL:
+ docShellLoadType = nsIDocShellLoadInfo::loadNormalExternal;
+ break;
+ case LOAD_NORMAL_BYPASS_CACHE:
+ docShellLoadType = nsIDocShellLoadInfo::loadNormalBypassCache;
+ break;
+ case LOAD_NORMAL_BYPASS_PROXY:
+ docShellLoadType = nsIDocShellLoadInfo::loadNormalBypassProxy;
+ break;
+ case LOAD_NORMAL_BYPASS_PROXY_AND_CACHE:
+ docShellLoadType = nsIDocShellLoadInfo::loadNormalBypassProxyAndCache;
+ break;
+ case LOAD_NORMAL_ALLOW_MIXED_CONTENT:
+ docShellLoadType = nsIDocShellLoadInfo::loadNormalAllowMixedContent;
+ break;
+ case LOAD_HISTORY:
+ docShellLoadType = nsIDocShellLoadInfo::loadHistory;
+ break;
+ case LOAD_RELOAD_NORMAL:
+ docShellLoadType = nsIDocShellLoadInfo::loadReloadNormal;
+ break;
+ case LOAD_RELOAD_CHARSET_CHANGE:
+ docShellLoadType = nsIDocShellLoadInfo::loadReloadCharsetChange;
+ break;
+ case LOAD_RELOAD_BYPASS_CACHE:
+ docShellLoadType = nsIDocShellLoadInfo::loadReloadBypassCache;
+ break;
+ case LOAD_RELOAD_BYPASS_PROXY:
+ docShellLoadType = nsIDocShellLoadInfo::loadReloadBypassProxy;
+ break;
+ case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
+ docShellLoadType = nsIDocShellLoadInfo::loadReloadBypassProxyAndCache;
+ break;
+ case LOAD_LINK:
+ docShellLoadType = nsIDocShellLoadInfo::loadLink;
+ break;
+ case LOAD_REFRESH:
+ docShellLoadType = nsIDocShellLoadInfo::loadRefresh;
+ break;
+ case LOAD_BYPASS_HISTORY:
+ case LOAD_ERROR_PAGE:
+ docShellLoadType = nsIDocShellLoadInfo::loadBypassHistory;
+ break;
+ case LOAD_STOP_CONTENT:
+ docShellLoadType = nsIDocShellLoadInfo::loadStopContent;
+ break;
+ case LOAD_STOP_CONTENT_AND_REPLACE:
+ docShellLoadType = nsIDocShellLoadInfo::loadStopContentAndReplace;
+ break;
+ case LOAD_PUSHSTATE:
+ docShellLoadType = nsIDocShellLoadInfo::loadPushState;
+ break;
+ case LOAD_REPLACE_BYPASS_CACHE:
+ docShellLoadType = nsIDocShellLoadInfo::loadReplaceBypassCache;
+ break;
+ case LOAD_RELOAD_ALLOW_MIXED_CONTENT:
+ docShellLoadType = nsIDocShellLoadInfo::loadReloadMixedContent;
+ break;
+ default:
+ NS_NOTREACHED("Unexpected load type value");
+ }
+
+ return docShellLoadType;
+}
+
+NS_IMETHODIMP
+nsDocShell::LoadURI(nsIURI* aURI,
+ nsIDocShellLoadInfo* aLoadInfo,
+ uint32_t aLoadFlags,
+ bool aFirstParty)
+{
+ NS_PRECONDITION(aLoadInfo || (aLoadFlags & EXTRA_LOAD_FLAGS) == 0,
+ "Unexpected flags");
+ NS_PRECONDITION((aLoadFlags & 0xf) == 0, "Should not have these flags set");
+
+ // Note: we allow loads to get through here even if mFiredUnloadEvent is
+ // true; that case will get handled in LoadInternal or LoadHistoryEntry,
+ // so we pass false as the second parameter to IsNavigationAllowed.
+ // However, we don't allow the page to change location *in the middle of*
+ // firing beforeunload, so we do need to check if *beforeunload* is currently
+ // firing, so we call IsNavigationAllowed rather than just IsPrintingOrPP.
+ if (!IsNavigationAllowed(true, false)) {
+ return NS_OK; // JS may not handle returning of an error code
+ }
+
+ nsCOMPtr<nsIURI> referrer;
+ nsCOMPtr<nsIURI> originalURI;
+ bool loadReplace = false;
+ nsCOMPtr<nsIInputStream> postStream;
+ nsCOMPtr<nsIInputStream> headersStream;
+ nsCOMPtr<nsIPrincipal> triggeringPrincipal;
+ bool inheritPrincipal = false;
+ bool principalIsExplicit = false;
+ bool sendReferrer = true;
+ uint32_t referrerPolicy = mozilla::net::RP_Default;
+ bool isSrcdoc = false;
+ nsCOMPtr<nsISHEntry> shEntry;
+ nsXPIDLString target;
+ nsAutoString srcdoc;
+ nsCOMPtr<nsIDocShell> sourceDocShell;
+ nsCOMPtr<nsIURI> baseURI;
+
+ uint32_t loadType = MAKE_LOAD_TYPE(LOAD_NORMAL, aLoadFlags);
+
+ NS_ENSURE_ARG(aURI);
+
+ if (!StartupTimeline::HasRecord(StartupTimeline::FIRST_LOAD_URI) &&
+ mItemType == typeContent && !NS_IsAboutBlank(aURI)) {
+ StartupTimeline::RecordOnce(StartupTimeline::FIRST_LOAD_URI);
+ }
+
+ // Extract the info from the DocShellLoadInfo struct...
+ if (aLoadInfo) {
+ aLoadInfo->GetReferrer(getter_AddRefs(referrer));
+ aLoadInfo->GetOriginalURI(getter_AddRefs(originalURI));
+ aLoadInfo->GetLoadReplace(&loadReplace);
+ nsDocShellInfoLoadType lt = nsIDocShellLoadInfo::loadNormal;
+ aLoadInfo->GetLoadType(&lt);
+ // Get the appropriate loadType from nsIDocShellLoadInfo type
+ loadType = ConvertDocShellLoadInfoToLoadType(lt);
+
+ aLoadInfo->GetTriggeringPrincipal(getter_AddRefs(triggeringPrincipal));
+ aLoadInfo->GetInheritPrincipal(&inheritPrincipal);
+ aLoadInfo->GetPrincipalIsExplicit(&principalIsExplicit);
+ aLoadInfo->GetSHEntry(getter_AddRefs(shEntry));
+ aLoadInfo->GetTarget(getter_Copies(target));
+ aLoadInfo->GetPostDataStream(getter_AddRefs(postStream));
+ aLoadInfo->GetHeadersStream(getter_AddRefs(headersStream));
+ aLoadInfo->GetSendReferrer(&sendReferrer);
+ aLoadInfo->GetReferrerPolicy(&referrerPolicy);
+ aLoadInfo->GetIsSrcdocLoad(&isSrcdoc);
+ aLoadInfo->GetSrcdocData(srcdoc);
+ aLoadInfo->GetSourceDocShell(getter_AddRefs(sourceDocShell));
+ aLoadInfo->GetBaseURI(getter_AddRefs(baseURI));
+ }
+
+#if defined(DEBUG)
+ if (MOZ_LOG_TEST(gDocShellLog, LogLevel::Debug)) {
+ nsAutoCString uristr;
+ aURI->GetAsciiSpec(uristr);
+ MOZ_LOG(gDocShellLog, LogLevel::Debug,
+ ("nsDocShell[%p]: loading %s with flags 0x%08x",
+ this, uristr.get(), aLoadFlags));
+ }
+#endif
+
+ if (!shEntry &&
+ !LOAD_TYPE_HAS_FLAGS(loadType, LOAD_FLAGS_REPLACE_HISTORY)) {
+ // First verify if this is a subframe.
+ nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
+ GetSameTypeParent(getter_AddRefs(parentAsItem));
+ nsCOMPtr<nsIDocShell> parentDS(do_QueryInterface(parentAsItem));
+ uint32_t parentLoadType;
+
+ if (parentDS && parentDS != static_cast<nsIDocShell*>(this)) {
+ /* OK. It is a subframe. Checkout the
+ * parent's loadtype. If the parent was loaded thro' a history
+ * mechanism, then get the SH entry for the child from the parent.
+ * This is done to restore frameset navigation while going back/forward.
+ * If the parent was loaded through any other loadType, set the
+ * child's loadType too accordingly, so that session history does not
+ * get confused.
+ */
+
+ // Get the parent's load type
+ parentDS->GetLoadType(&parentLoadType);
+
+ // Get the ShEntry for the child from the parent
+ nsCOMPtr<nsISHEntry> currentSH;
+ bool oshe = false;
+ parentDS->GetCurrentSHEntry(getter_AddRefs(currentSH), &oshe);
+ bool dynamicallyAddedChild = mDynamicallyCreated;
+ if (!dynamicallyAddedChild && !oshe && currentSH) {
+ currentSH->HasDynamicallyAddedChild(&dynamicallyAddedChild);
+ }
+ if (!dynamicallyAddedChild) {
+ // Only use the old SHEntry, if we're sure enough that
+ // it wasn't originally for some other frame.
+ parentDS->GetChildSHEntry(mChildOffset, getter_AddRefs(shEntry));
+ }
+
+ // Make some decisions on the child frame's loadType based on the
+ // parent's loadType.
+ if (!mCurrentURI) {
+ // This is a newly created frame. Check for exception cases first.
+ // By default the subframe will inherit the parent's loadType.
+ if (shEntry && (parentLoadType == LOAD_NORMAL ||
+ parentLoadType == LOAD_LINK ||
+ parentLoadType == LOAD_NORMAL_EXTERNAL)) {
+ // The parent was loaded normally. In this case, this *brand new*
+ // child really shouldn't have a SHEntry. If it does, it could be
+ // because the parent is replacing an existing frame with a new frame,
+ // in the onLoadHandler. We don't want this url to get into session
+ // history. Clear off shEntry, and set load type to
+ // LOAD_BYPASS_HISTORY.
+ bool inOnLoadHandler = false;
+ parentDS->GetIsExecutingOnLoadHandler(&inOnLoadHandler);
+ if (inOnLoadHandler) {
+ loadType = LOAD_NORMAL_REPLACE;
+ shEntry = nullptr;
+ }
+ } else if (parentLoadType == LOAD_REFRESH) {
+ // Clear shEntry. For refresh loads, we have to load
+ // what comes thro' the pipe, not what's in history.
+ shEntry = nullptr;
+ } else if ((parentLoadType == LOAD_BYPASS_HISTORY) ||
+ (shEntry &&
+ ((parentLoadType & LOAD_CMD_HISTORY) ||
+ (parentLoadType == LOAD_RELOAD_NORMAL) ||
+ (parentLoadType == LOAD_RELOAD_CHARSET_CHANGE)))) {
+ // If the parent url, bypassed history or was loaded from
+ // history, pass on the parent's loadType to the new child
+ // frame too, so that the child frame will also
+ // avoid getting into history.
+ loadType = parentLoadType;
+ } else if (parentLoadType == LOAD_ERROR_PAGE) {
+ // If the parent document is an error page, we don't
+ // want to update global/session history. However,
+ // this child frame is not an error page.
+ loadType = LOAD_BYPASS_HISTORY;
+ } else if ((parentLoadType == LOAD_RELOAD_BYPASS_CACHE) ||
+ (parentLoadType == LOAD_RELOAD_BYPASS_PROXY) ||
+ (parentLoadType == LOAD_RELOAD_BYPASS_PROXY_AND_CACHE)) {
+ // the new frame should inherit the parent's load type so that it also
+ // bypasses the cache and/or proxy
+ loadType = parentLoadType;
+ }
+ } else {
+ // This is a pre-existing subframe. If the load was not originally
+ // initiated by session history, (if (!shEntry) condition succeeded) and
+ // mCurrentURI is not null, it is possible that a parent's onLoadHandler
+ // or even self's onLoadHandler is loading a new page in this child.
+ // Check parent's and self's busy flag and if it is set, we don't want
+ // this onLoadHandler load to get in to session history.
+ uint32_t parentBusy = BUSY_FLAGS_NONE;
+ uint32_t selfBusy = BUSY_FLAGS_NONE;
+ parentDS->GetBusyFlags(&parentBusy);
+ GetBusyFlags(&selfBusy);
+ if (parentBusy & BUSY_FLAGS_BUSY ||
+ selfBusy & BUSY_FLAGS_BUSY) {
+ loadType = LOAD_NORMAL_REPLACE;
+ shEntry = nullptr;
+ }
+ }
+ } // parentDS
+ else {
+ // This is the root docshell. If we got here while
+ // executing an onLoad Handler,this load will not go
+ // into session history.
+ bool inOnLoadHandler = false;
+ GetIsExecutingOnLoadHandler(&inOnLoadHandler);
+ if (inOnLoadHandler) {
+ loadType = LOAD_NORMAL_REPLACE;
+ }
+ }
+ } // !shEntry
+
+ if (shEntry) {
+#ifdef DEBUG
+ MOZ_LOG(gDocShellLog, LogLevel::Debug,
+ ("nsDocShell[%p]: loading from session history", this));
+#endif
+
+ return LoadHistoryEntry(shEntry, loadType);
+ }
+
+ // On history navigation via Back/Forward buttons, don't execute
+ // automatic JavaScript redirection such as |location.href = ...| or
+ // |window.open()|
+ //
+ // LOAD_NORMAL: window.open(...) etc.
+ // LOAD_STOP_CONTENT: location.href = ..., location.assign(...)
+ if ((loadType == LOAD_NORMAL || loadType == LOAD_STOP_CONTENT) &&
+ ShouldBlockLoadingForBackButton()) {
+ return NS_OK;
+ }
+
+ // Perform the load...
+
+ // We need a principalToInherit.
+ //
+ // If principalIsExplicit is not set there are 4 possibilities:
+ // (1) If the system principal or an expanded principal was passed
+ // in and we're a typeContent docshell, inherit the principal
+ // from the current document instead.
+ // (2) In all other cases when the principal passed in is not null,
+ // use that principal.
+ // (3) If the caller has allowed inheriting from the current document,
+ // or if we're being called from system code (eg chrome JS or pure
+ // C++) then inheritPrincipal should be true and InternalLoad will get
+ // a principal from the current document. If none of these things are
+ // true, then
+ // (4) we don't pass a principal into the channel, and a principal will be
+ // created later from the channel's internal data.
+ //
+ // If principalIsExplicit *is* set, there are 4 possibilities
+ // (1) If the system principal or an expanded principal was passed in
+ // and we're a typeContent docshell, return an error.
+ // (2) In all other cases when the principal passed in is not null,
+ // use that principal.
+ // (3) If the caller has allowed inheriting from the current document,
+ // then inheritPrincipal should be true and InternalLoad will get
+ // a principal from the current document. If none of these things are
+ // true, then
+ // (4) we dont' pass a principal into the channel, and a principal will be
+ // created later from the channel's internal data.
+ nsCOMPtr<nsIPrincipal> principalToInherit = triggeringPrincipal;
+ if (principalToInherit && mItemType != typeChrome) {
+ if (nsContentUtils::IsSystemPrincipal(principalToInherit)) {
+ if (principalIsExplicit) {
+ return NS_ERROR_DOM_SECURITY_ERR;
+ }
+ principalToInherit = nullptr;
+ inheritPrincipal = true;
+ } else if (nsContentUtils::IsExpandedPrincipal(principalToInherit)) {
+ if (principalIsExplicit) {
+ return NS_ERROR_DOM_SECURITY_ERR;
+ }
+ // Don't inherit from the current page. Just do the safe thing
+ // and pretend that we were loaded by a nullprincipal.
+ //
+ // We didn't inherit OriginAttributes here as ExpandedPrincipal doesn't
+ // have origin attributes.
+ principalToInherit = nsNullPrincipal::CreateWithInheritedAttributes(this);
+ inheritPrincipal = false;
+ }
+ }
+ if (!principalToInherit && !inheritPrincipal && !principalIsExplicit) {
+ // See if there's system or chrome JS code running
+ inheritPrincipal = nsContentUtils::LegacyIsCallerChromeOrNativeCode();
+ }
+
+ if (aLoadFlags & LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL) {
+ inheritPrincipal = false;
+ principalToInherit = nsNullPrincipal::CreateWithInheritedAttributes(this);
+ }
+
+ // If the triggeringPrincipal is not passed explicitly, we first try to create
+ // a principal from the referrer, since the referrer URI reflects the web origin
+ // that triggered the load. If there is no referrer URI, we fall back to using
+ // the SystemPrincipal. It's safe to assume that no provided triggeringPrincipal
+ // and no referrer simulate a load that was triggered by the system.
+ // It's important to note that this block of code needs to appear *after* the block
+ // where we munge the principalToInherit, because otherwise we would never enter
+ // code blocks checking if the principalToInherit is null and we will end up with
+ // a wrong inheritPrincipal flag.
+ if (!triggeringPrincipal) {
+ if (referrer) {
+ nsresult rv = CreatePrincipalFromReferrer(referrer,
+ getter_AddRefs(triggeringPrincipal));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ else {
+ triggeringPrincipal = nsContentUtils::GetSystemPrincipal();
+ }
+ }
+
+ uint32_t flags = 0;
+
+ if (inheritPrincipal) {
+ flags |= INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL;
+ }
+
+ if (!sendReferrer) {
+ flags |= INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER;
+ }
+
+ if (aLoadFlags & LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) {
+ flags |= INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
+ }
+
+ if (aLoadFlags & LOAD_FLAGS_FIRST_LOAD) {
+ flags |= INTERNAL_LOAD_FLAGS_FIRST_LOAD;
+ }
+
+ if (aLoadFlags & LOAD_FLAGS_BYPASS_CLASSIFIER) {
+ flags |= INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER;
+ }
+
+ if (aLoadFlags & LOAD_FLAGS_FORCE_ALLOW_COOKIES) {
+ flags |= INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES;
+ }
+
+ if (isSrcdoc) {
+ flags |= INTERNAL_LOAD_FLAGS_IS_SRCDOC;
+ }
+
+ return InternalLoad(aURI,
+ originalURI,
+ loadReplace,
+ referrer,
+ referrerPolicy,
+ triggeringPrincipal,
+ principalToInherit,
+ flags,
+ target,
+ nullptr, // No type hint
+ NullString(), // No forced download
+ postStream,
+ headersStream,
+ loadType,
+ nullptr, // No SHEntry
+ aFirstParty,
+ srcdoc,
+ sourceDocShell,
+ baseURI,
+ nullptr, // No nsIDocShell
+ nullptr); // No nsIRequest
+}
+
+NS_IMETHODIMP
+nsDocShell::LoadStream(nsIInputStream* aStream, nsIURI* aURI,
+ const nsACString& aContentType,
+ const nsACString& aContentCharset,
+ nsIDocShellLoadInfo* aLoadInfo)
+{
+ NS_ENSURE_ARG(aStream);
+
+ mAllowKeywordFixup = false;
+
+ // if the caller doesn't pass in a URI we need to create a dummy URI. necko
+ // currently requires a URI in various places during the load. Some consumers
+ // do as well.
+ nsCOMPtr<nsIURI> uri = aURI;
+ if (!uri) {
+ // HACK ALERT
+ nsresult rv = NS_OK;
+ uri = do_CreateInstance(NS_SIMPLEURI_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ // Make sure that the URI spec "looks" like a protocol and path...
+ // For now, just use a bogus protocol called "internal"
+ rv = uri->SetSpec(NS_LITERAL_CSTRING("internal:load-stream"));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+
+ uint32_t loadType = LOAD_NORMAL;
+ nsCOMPtr<nsIPrincipal> triggeringPrincipal;
+ if (aLoadInfo) {
+ nsDocShellInfoLoadType lt = nsIDocShellLoadInfo::loadNormal;
+ (void)aLoadInfo->GetLoadType(&lt);
+ // Get the appropriate LoadType from nsIDocShellLoadInfo type
+ loadType = ConvertDocShellLoadInfoToLoadType(lt);
+ aLoadInfo->GetTriggeringPrincipal(getter_AddRefs(triggeringPrincipal));
+ }
+
+ NS_ENSURE_SUCCESS(Stop(nsIWebNavigation::STOP_NETWORK), NS_ERROR_FAILURE);
+
+ mLoadType = loadType;
+
+ if (!triggeringPrincipal) {
+ triggeringPrincipal = nsContentUtils::GetSystemPrincipal();
+ }
+
+ // build up a channel for this stream.
+ nsCOMPtr<nsIChannel> channel;
+ nsresult rv = NS_NewInputStreamChannel(getter_AddRefs(channel),
+ uri,
+ aStream,
+ triggeringPrincipal,
+ nsILoadInfo::SEC_NORMAL,
+ nsIContentPolicy::TYPE_OTHER,
+ aContentType,
+ aContentCharset);
+ NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+
+ nsCOMPtr<nsIURILoader> uriLoader(do_GetService(NS_URI_LOADER_CONTRACTID));
+ NS_ENSURE_TRUE(uriLoader, NS_ERROR_FAILURE);
+
+ NS_ENSURE_SUCCESS(DoChannelLoad(channel, uriLoader, false),
+ NS_ERROR_FAILURE);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::CreateLoadInfo(nsIDocShellLoadInfo** aLoadInfo)
+{
+ nsDocShellLoadInfo* loadInfo = new nsDocShellLoadInfo();
+ nsCOMPtr<nsIDocShellLoadInfo> localRef(loadInfo);
+
+ localRef.forget(aLoadInfo);
+ return NS_OK;
+}
+
+/*
+ * Reset state to a new content model within the current document and the
+ * document viewer. Called by the document before initiating an out of band
+ * document.write().
+ */
+NS_IMETHODIMP
+nsDocShell::PrepareForNewContentModel()
+{
+ mEODForCurrentDocument = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::FirePageHideNotification(bool aIsUnload)
+{
+ if (mContentViewer && !mFiredUnloadEvent) {
+ // Keep an explicit reference since calling PageHide could release
+ // mContentViewer
+ nsCOMPtr<nsIContentViewer> contentViewer(mContentViewer);
+ mFiredUnloadEvent = true;
+
+ if (mTiming) {
+ mTiming->NotifyUnloadEventStart();
+ }
+
+ contentViewer->PageHide(aIsUnload);
+
+ if (mTiming) {
+ mTiming->NotifyUnloadEventEnd();
+ }
+
+ AutoTArray<nsCOMPtr<nsIDocShell>, 8> kids;
+ uint32_t n = mChildList.Length();
+ kids.SetCapacity(n);
+ for (uint32_t i = 0; i < n; i++) {
+ kids.AppendElement(do_QueryInterface(ChildAt(i)));
+ }
+
+ n = kids.Length();
+ for (uint32_t i = 0; i < n; ++i) {
+ if (kids[i]) {
+ kids[i]->FirePageHideNotification(aIsUnload);
+ }
+ }
+ // Now make sure our editor, if any, is detached before we go
+ // any farther.
+ DetachEditorFromWindow();
+ }
+
+ return NS_OK;
+}
+
+void
+nsDocShell::MaybeInitTiming()
+{
+ if (mTiming && !mBlankTiming) {
+ return;
+ }
+
+ if (mScriptGlobal && mBlankTiming) {
+ nsPIDOMWindowInner* innerWin =
+ mScriptGlobal->AsOuter()->GetCurrentInnerWindow();
+ if (innerWin && innerWin->GetPerformance()) {
+ mTiming = innerWin->GetPerformance()->GetDOMTiming();
+ mBlankTiming = false;
+ }
+ }
+
+ if (!mTiming) {
+ mTiming = new nsDOMNavigationTiming();
+ }
+
+ mTiming->NotifyNavigationStart(
+ mIsActive ? nsDOMNavigationTiming::DocShellState::eActive
+ : nsDOMNavigationTiming::DocShellState::eInactive);
+}
+
+//
+// Bug 13871: Prevent frameset spoofing
+//
+// This routine answers: 'Is origin's document from same domain as
+// target's document?'
+//
+// file: uris are considered the same domain for the purpose of
+// frame navigation regardless of script accessibility (bug 420425)
+//
+/* static */ bool
+nsDocShell::ValidateOrigin(nsIDocShellTreeItem* aOriginTreeItem,
+ nsIDocShellTreeItem* aTargetTreeItem)
+{
+ // We want to bypass this check for chrome callers, but only if there's
+ // JS on the stack. System callers still need to do it.
+ if (nsContentUtils::GetCurrentJSContext() &&
+ nsContentUtils::IsCallerChrome()) {
+ return true;
+ }
+
+ MOZ_ASSERT(aOriginTreeItem && aTargetTreeItem, "need two docshells");
+
+ // Get origin document principal
+ nsCOMPtr<nsIDocument> originDocument = aOriginTreeItem->GetDocument();
+ NS_ENSURE_TRUE(originDocument, false);
+
+ // Get target principal
+ nsCOMPtr<nsIDocument> targetDocument = aTargetTreeItem->GetDocument();
+ NS_ENSURE_TRUE(targetDocument, false);
+
+ bool equal;
+ nsresult rv = originDocument->NodePrincipal()->Equals(
+ targetDocument->NodePrincipal(), &equal);
+ if (NS_SUCCEEDED(rv) && equal) {
+ return true;
+ }
+
+ // Not strictly equal, special case if both are file: uris
+ bool originIsFile = false;
+ bool targetIsFile = false;
+ nsCOMPtr<nsIURI> originURI;
+ nsCOMPtr<nsIURI> targetURI;
+ nsCOMPtr<nsIURI> innerOriginURI;
+ nsCOMPtr<nsIURI> innerTargetURI;
+
+ rv = originDocument->NodePrincipal()->GetURI(getter_AddRefs(originURI));
+ if (NS_SUCCEEDED(rv) && originURI) {
+ innerOriginURI = NS_GetInnermostURI(originURI);
+ }
+
+ rv = targetDocument->NodePrincipal()->GetURI(getter_AddRefs(targetURI));
+ if (NS_SUCCEEDED(rv) && targetURI) {
+ innerTargetURI = NS_GetInnermostURI(targetURI);
+ }
+
+ return innerOriginURI && innerTargetURI &&
+ NS_SUCCEEDED(innerOriginURI->SchemeIs("file", &originIsFile)) &&
+ NS_SUCCEEDED(innerTargetURI->SchemeIs("file", &targetIsFile)) &&
+ originIsFile && targetIsFile;
+}
+
+nsresult
+nsDocShell::GetEldestPresContext(nsPresContext** aPresContext)
+{
+ NS_ENSURE_ARG_POINTER(aPresContext);
+ *aPresContext = nullptr;
+
+ nsCOMPtr<nsIContentViewer> viewer = mContentViewer;
+ while (viewer) {
+ nsCOMPtr<nsIContentViewer> prevViewer;
+ viewer->GetPreviousViewer(getter_AddRefs(prevViewer));
+ if (!prevViewer) {
+ return viewer->GetPresContext(aPresContext);
+ }
+ viewer = prevViewer;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetPresContext(nsPresContext** aPresContext)
+{
+ NS_ENSURE_ARG_POINTER(aPresContext);
+ *aPresContext = nullptr;
+
+ if (!mContentViewer) {
+ return NS_OK;
+ }
+
+ return mContentViewer->GetPresContext(aPresContext);
+}
+
+NS_IMETHODIMP_(nsIPresShell*)
+nsDocShell::GetPresShell()
+{
+ RefPtr<nsPresContext> presContext;
+ (void)GetPresContext(getter_AddRefs(presContext));
+ return presContext ? presContext->GetPresShell() : nullptr;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetEldestPresShell(nsIPresShell** aPresShell)
+{
+ nsresult rv = NS_OK;
+
+ NS_ENSURE_ARG_POINTER(aPresShell);
+ *aPresShell = nullptr;
+
+ RefPtr<nsPresContext> presContext;
+ (void)GetEldestPresContext(getter_AddRefs(presContext));
+
+ if (presContext) {
+ NS_IF_ADDREF(*aPresShell = presContext->GetPresShell());
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetContentViewer(nsIContentViewer** aContentViewer)
+{
+ NS_ENSURE_ARG_POINTER(aContentViewer);
+
+ *aContentViewer = mContentViewer;
+ NS_IF_ADDREF(*aContentViewer);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetChromeEventHandler(nsIDOMEventTarget* aChromeEventHandler)
+{
+ // Weak reference. Don't addref.
+ nsCOMPtr<EventTarget> handler = do_QueryInterface(aChromeEventHandler);
+ mChromeEventHandler = handler.get();
+
+ if (mScriptGlobal) {
+ mScriptGlobal->SetChromeEventHandler(mChromeEventHandler);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetChromeEventHandler(nsIDOMEventTarget** aChromeEventHandler)
+{
+ NS_ENSURE_ARG_POINTER(aChromeEventHandler);
+ nsCOMPtr<EventTarget> handler = mChromeEventHandler;
+ handler.forget(aChromeEventHandler);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetCurrentURI(nsIURI* aURI)
+{
+ // Note that securityUI will set STATE_IS_INSECURE, even if
+ // the scheme of |aURI| is "https".
+ SetCurrentURI(aURI, nullptr, true, 0);
+ return NS_OK;
+}
+
+bool
+nsDocShell::SetCurrentURI(nsIURI* aURI, nsIRequest* aRequest,
+ bool aFireOnLocationChange, uint32_t aLocationFlags)
+{
+ if (gDocShellLeakLog && MOZ_LOG_TEST(gDocShellLeakLog, LogLevel::Debug)) {
+ PR_LogPrint("DOCSHELL %p SetCurrentURI %s\n",
+ this, aURI ? aURI->GetSpecOrDefault().get() : "");
+ }
+
+ // We don't want to send a location change when we're displaying an error
+ // page, and we don't want to change our idea of "current URI" either
+ if (mLoadType == LOAD_ERROR_PAGE) {
+ return false;
+ }
+
+ mCurrentURI = NS_TryToMakeImmutable(aURI);
+
+ if (!NS_IsAboutBlank(mCurrentURI)) {
+ mHasLoadedNonBlankURI = true;
+ }
+
+ bool isRoot = false; // Is this the root docshell
+ bool isSubFrame = false; // Is this a subframe navigation?
+
+ nsCOMPtr<nsIDocShellTreeItem> root;
+
+ GetSameTypeRootTreeItem(getter_AddRefs(root));
+ if (root.get() == static_cast<nsIDocShellTreeItem*>(this)) {
+ // This is the root docshell
+ isRoot = true;
+ }
+ if (mLSHE) {
+ mLSHE->GetIsSubFrame(&isSubFrame);
+ }
+
+ if (!isSubFrame && !isRoot) {
+ /*
+ * We don't want to send OnLocationChange notifications when
+ * a subframe is being loaded for the first time, while
+ * visiting a frameset page
+ */
+ return false;
+ }
+
+ if (aFireOnLocationChange) {
+ FireOnLocationChange(this, aRequest, aURI, aLocationFlags);
+ }
+ return !aFireOnLocationChange;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetCharset(nsACString& aCharset)
+{
+ aCharset.Truncate();
+
+ nsIPresShell* presShell = GetPresShell();
+ NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
+ nsIDocument* doc = presShell->GetDocument();
+ NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
+ aCharset = doc->GetDocumentCharacterSet();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GatherCharsetMenuTelemetry()
+{
+ nsCOMPtr<nsIContentViewer> viewer;
+ GetContentViewer(getter_AddRefs(viewer));
+ if (!viewer) {
+ return NS_OK;
+ }
+
+ nsIDocument* doc = viewer->GetDocument();
+ if (!doc || doc->WillIgnoreCharsetOverride()) {
+ return NS_OK;
+ }
+
+ Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_USED, true);
+
+ bool isFileURL = false;
+ nsIURI* url = doc->GetOriginalURI();
+ if (url) {
+ url->SchemeIs("file", &isFileURL);
+ }
+
+ int32_t charsetSource = doc->GetDocumentCharacterSetSource();
+ switch (charsetSource) {
+ case kCharsetFromTopLevelDomain:
+ // Unlabeled doc on a domain that we map to a fallback encoding
+ Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 7);
+ break;
+ case kCharsetFromFallback:
+ case kCharsetFromDocTypeDefault:
+ case kCharsetFromCache:
+ case kCharsetFromParentFrame:
+ case kCharsetFromHintPrevDoc:
+ // Changing charset on an unlabeled doc.
+ if (isFileURL) {
+ Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 0);
+ } else {
+ Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 1);
+ }
+ break;
+ case kCharsetFromAutoDetection:
+ // Changing charset on unlabeled doc where chardet fired
+ if (isFileURL) {
+ Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 2);
+ } else {
+ Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 3);
+ }
+ break;
+ case kCharsetFromMetaPrescan:
+ case kCharsetFromMetaTag:
+ case kCharsetFromChannel:
+ // Changing charset on a doc that had a charset label.
+ Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 4);
+ break;
+ case kCharsetFromParentForced:
+ case kCharsetFromUserForced:
+ // Changing charset on a document that already had an override.
+ Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 5);
+ break;
+ case kCharsetFromIrreversibleAutoDetection:
+ case kCharsetFromOtherComponent:
+ case kCharsetFromByteOrderMark:
+ case kCharsetUninitialized:
+ default:
+ // Bug. This isn't supposed to happen.
+ Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 6);
+ break;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetCharset(const nsACString& aCharset)
+{
+ // set the charset override
+ return SetForcedCharset(aCharset);
+}
+
+NS_IMETHODIMP
+nsDocShell::SetForcedCharset(const nsACString& aCharset)
+{
+ if (aCharset.IsEmpty()) {
+ mForcedCharset.Truncate();
+ return NS_OK;
+ }
+ nsAutoCString encoding;
+ if (!EncodingUtils::FindEncodingForLabel(aCharset, encoding)) {
+ // Reject unknown labels
+ return NS_ERROR_INVALID_ARG;
+ }
+ if (!EncodingUtils::IsAsciiCompatible(encoding)) {
+ // Reject XSS hazards
+ return NS_ERROR_INVALID_ARG;
+ }
+ mForcedCharset = encoding;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetForcedCharset(nsACString& aResult)
+{
+ aResult = mForcedCharset;
+ return NS_OK;
+}
+
+void
+nsDocShell::SetParentCharset(const nsACString& aCharset,
+ int32_t aCharsetSource,
+ nsIPrincipal* aPrincipal)
+{
+ mParentCharset = aCharset;
+ mParentCharsetSource = aCharsetSource;
+ mParentCharsetPrincipal = aPrincipal;
+}
+
+void
+nsDocShell::GetParentCharset(nsACString& aCharset,
+ int32_t* aCharsetSource,
+ nsIPrincipal** aPrincipal)
+{
+ aCharset = mParentCharset;
+ *aCharsetSource = mParentCharsetSource;
+ NS_IF_ADDREF(*aPrincipal = mParentCharsetPrincipal);
+}
+
+NS_IMETHODIMP
+nsDocShell::GetChannelIsUnsafe(bool* aUnsafe)
+{
+ *aUnsafe = false;
+
+ nsIChannel* channel = GetCurrentDocChannel();
+ if (!channel) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(channel);
+ if (!jarChannel) {
+ return NS_OK;
+ }
+
+ return jarChannel->GetIsUnsafe(aUnsafe);
+}
+
+NS_IMETHODIMP
+nsDocShell::GetHasMixedActiveContentLoaded(bool* aHasMixedActiveContentLoaded)
+{
+ nsCOMPtr<nsIDocument> doc(GetDocument());
+ *aHasMixedActiveContentLoaded = doc && doc->GetHasMixedActiveContentLoaded();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetHasMixedActiveContentBlocked(bool* aHasMixedActiveContentBlocked)
+{
+ nsCOMPtr<nsIDocument> doc(GetDocument());
+ *aHasMixedActiveContentBlocked =
+ doc && doc->GetHasMixedActiveContentBlocked();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetHasMixedDisplayContentLoaded(bool* aHasMixedDisplayContentLoaded)
+{
+ nsCOMPtr<nsIDocument> doc(GetDocument());
+ *aHasMixedDisplayContentLoaded =
+ doc && doc->GetHasMixedDisplayContentLoaded();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetHasMixedDisplayContentBlocked(
+ bool* aHasMixedDisplayContentBlocked)
+{
+ nsCOMPtr<nsIDocument> doc(GetDocument());
+ *aHasMixedDisplayContentBlocked =
+ doc && doc->GetHasMixedDisplayContentBlocked();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetHasTrackingContentBlocked(bool* aHasTrackingContentBlocked)
+{
+ nsCOMPtr<nsIDocument> doc(GetDocument());
+ *aHasTrackingContentBlocked = doc && doc->GetHasTrackingContentBlocked();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetHasTrackingContentLoaded(bool* aHasTrackingContentLoaded)
+{
+ nsCOMPtr<nsIDocument> doc(GetDocument());
+ *aHasTrackingContentLoaded = doc && doc->GetHasTrackingContentLoaded();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetAllowPlugins(bool* aAllowPlugins)
+{
+ NS_ENSURE_ARG_POINTER(aAllowPlugins);
+
+ *aAllowPlugins = mAllowPlugins;
+ if (!mAllowPlugins) {
+ return NS_OK;
+ }
+
+ bool unsafe;
+ *aAllowPlugins = NS_SUCCEEDED(GetChannelIsUnsafe(&unsafe)) && !unsafe;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetAllowPlugins(bool aAllowPlugins)
+{
+ mAllowPlugins = aAllowPlugins;
+ // XXX should enable or disable a plugin host
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetAllowJavascript(bool* aAllowJavascript)
+{
+ NS_ENSURE_ARG_POINTER(aAllowJavascript);
+
+ *aAllowJavascript = mAllowJavascript;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetAllowJavascript(bool aAllowJavascript)
+{
+ mAllowJavascript = aAllowJavascript;
+ RecomputeCanExecuteScripts();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetUsePrivateBrowsing(bool* aUsePrivateBrowsing)
+{
+ NS_ENSURE_ARG_POINTER(aUsePrivateBrowsing);
+ AssertOriginAttributesMatchPrivateBrowsing();
+ *aUsePrivateBrowsing = mPrivateBrowsingId > 0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetUsePrivateBrowsing(bool aUsePrivateBrowsing)
+{
+ nsContentUtils::ReportToConsoleNonLocalized(
+ NS_LITERAL_STRING("Only internal code is allowed to set the usePrivateBrowsing attribute"),
+ nsIScriptError::warningFlag,
+ NS_LITERAL_CSTRING("Internal API Used"),
+ mContentViewer ? mContentViewer->GetDocument() : nullptr);
+
+ if (!CanSetOriginAttributes()) {
+ bool changed = aUsePrivateBrowsing != (mPrivateBrowsingId > 0);
+
+ return changed ? NS_ERROR_FAILURE : NS_OK;
+ }
+
+ return SetPrivateBrowsing(aUsePrivateBrowsing);
+}
+
+NS_IMETHODIMP
+nsDocShell::SetPrivateBrowsing(bool aUsePrivateBrowsing)
+{
+ bool changed = aUsePrivateBrowsing != (mPrivateBrowsingId > 0);
+ if (changed) {
+ mPrivateBrowsingId = aUsePrivateBrowsing ? 1 : 0;
+
+ if (mItemType != typeChrome) {
+ mOriginAttributes.SyncAttributesWithPrivateBrowsing(aUsePrivateBrowsing);
+ }
+
+ if (mAffectPrivateSessionLifetime) {
+ if (aUsePrivateBrowsing) {
+ IncreasePrivateDocShellCount();
+ } else {
+ DecreasePrivateDocShellCount();
+ }
+ }
+ }
+
+ nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
+ while (iter.HasMore()) {
+ nsCOMPtr<nsILoadContext> shell = do_QueryObject(iter.GetNext());
+ if (shell) {
+ shell->SetPrivateBrowsing(aUsePrivateBrowsing);
+ }
+ }
+
+ if (changed) {
+ nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mPrivacyObservers);
+ while (iter.HasMore()) {
+ nsWeakPtr ref = iter.GetNext();
+ nsCOMPtr<nsIPrivacyTransitionObserver> obs = do_QueryReferent(ref);
+ if (!obs) {
+ mPrivacyObservers.RemoveElement(ref);
+ } else {
+ obs->PrivateModeChanged(aUsePrivateBrowsing);
+ }
+ }
+ }
+
+ AssertOriginAttributesMatchPrivateBrowsing();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetHasLoadedNonBlankURI(bool* aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ *aResult = mHasLoadedNonBlankURI;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetUseRemoteTabs(bool* aUseRemoteTabs)
+{
+ NS_ENSURE_ARG_POINTER(aUseRemoteTabs);
+
+ *aUseRemoteTabs = mUseRemoteTabs;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetRemoteTabs(bool aUseRemoteTabs)
+{
+#ifdef MOZ_CRASHREPORTER
+ if (aUseRemoteTabs) {
+ CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("DOMIPCEnabled"),
+ NS_LITERAL_CSTRING("1"));
+ }
+#endif
+
+ mUseRemoteTabs = aUseRemoteTabs;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetAffectPrivateSessionLifetime(bool aAffectLifetime)
+{
+ bool change = aAffectLifetime != mAffectPrivateSessionLifetime;
+ if (change && UsePrivateBrowsing()) {
+ AssertOriginAttributesMatchPrivateBrowsing();
+ if (aAffectLifetime) {
+ IncreasePrivateDocShellCount();
+ } else {
+ DecreasePrivateDocShellCount();
+ }
+ }
+ mAffectPrivateSessionLifetime = aAffectLifetime;
+
+ nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
+ while (iter.HasMore()) {
+ nsCOMPtr<nsIDocShell> shell = do_QueryObject(iter.GetNext());
+ if (shell) {
+ shell->SetAffectPrivateSessionLifetime(aAffectLifetime);
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetAffectPrivateSessionLifetime(bool* aAffectLifetime)
+{
+ *aAffectLifetime = mAffectPrivateSessionLifetime;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::AddWeakPrivacyTransitionObserver(
+ nsIPrivacyTransitionObserver* aObserver)
+{
+ nsWeakPtr weakObs = do_GetWeakReference(aObserver);
+ if (!weakObs) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ return mPrivacyObservers.AppendElement(weakObs) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsDocShell::AddWeakReflowObserver(nsIReflowObserver* aObserver)
+{
+ nsWeakPtr weakObs = do_GetWeakReference(aObserver);
+ if (!weakObs) {
+ return NS_ERROR_FAILURE;
+ }
+ return mReflowObservers.AppendElement(weakObs) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsDocShell::RemoveWeakReflowObserver(nsIReflowObserver* aObserver)
+{
+ nsWeakPtr obs = do_GetWeakReference(aObserver);
+ return mReflowObservers.RemoveElement(obs) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsDocShell::NotifyReflowObservers(bool aInterruptible,
+ DOMHighResTimeStamp aStart,
+ DOMHighResTimeStamp aEnd)
+{
+ nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mReflowObservers);
+ while (iter.HasMore()) {
+ nsWeakPtr ref = iter.GetNext();
+ nsCOMPtr<nsIReflowObserver> obs = do_QueryReferent(ref);
+ if (!obs) {
+ mReflowObservers.RemoveElement(ref);
+ } else if (aInterruptible) {
+ obs->ReflowInterruptible(aStart, aEnd);
+ } else {
+ obs->Reflow(aStart, aEnd);
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetAllowMetaRedirects(bool* aReturn)
+{
+ NS_ENSURE_ARG_POINTER(aReturn);
+
+ *aReturn = mAllowMetaRedirects;
+ if (!mAllowMetaRedirects) {
+ return NS_OK;
+ }
+
+ bool unsafe;
+ *aReturn = NS_SUCCEEDED(GetChannelIsUnsafe(&unsafe)) && !unsafe;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetAllowMetaRedirects(bool aValue)
+{
+ mAllowMetaRedirects = aValue;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetAllowSubframes(bool* aAllowSubframes)
+{
+ NS_ENSURE_ARG_POINTER(aAllowSubframes);
+
+ *aAllowSubframes = mAllowSubframes;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetAllowSubframes(bool aAllowSubframes)
+{
+ mAllowSubframes = aAllowSubframes;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetAllowImages(bool* aAllowImages)
+{
+ NS_ENSURE_ARG_POINTER(aAllowImages);
+
+ *aAllowImages = mAllowImages;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetAllowImages(bool aAllowImages)
+{
+ mAllowImages = aAllowImages;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetAllowMedia(bool* aAllowMedia)
+{
+ *aAllowMedia = mAllowMedia;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetAllowMedia(bool aAllowMedia)
+{
+ mAllowMedia = aAllowMedia;
+
+ // Mute or unmute audio contexts attached to the inner window.
+ if (mScriptGlobal) {
+ if (nsPIDOMWindowInner* innerWin =
+ mScriptGlobal->AsOuter()->GetCurrentInnerWindow()) {
+ if (aAllowMedia) {
+ innerWin->UnmuteAudioContexts();
+ } else {
+ innerWin->MuteAudioContexts();
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetAllowDNSPrefetch(bool* aAllowDNSPrefetch)
+{
+ *aAllowDNSPrefetch = mAllowDNSPrefetch;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetAllowDNSPrefetch(bool aAllowDNSPrefetch)
+{
+ mAllowDNSPrefetch = aAllowDNSPrefetch;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetAllowWindowControl(bool* aAllowWindowControl)
+{
+ *aAllowWindowControl = mAllowWindowControl;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetAllowWindowControl(bool aAllowWindowControl)
+{
+ mAllowWindowControl = aAllowWindowControl;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetAllowContentRetargeting(bool* aAllowContentRetargeting)
+{
+ *aAllowContentRetargeting = mAllowContentRetargeting;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetAllowContentRetargeting(bool aAllowContentRetargeting)
+{
+ mAllowContentRetargetingOnChildren = aAllowContentRetargeting;
+ mAllowContentRetargeting = aAllowContentRetargeting;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetAllowContentRetargetingOnChildren(
+ bool* aAllowContentRetargetingOnChildren)
+{
+ *aAllowContentRetargetingOnChildren = mAllowContentRetargetingOnChildren;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetAllowContentRetargetingOnChildren(
+ bool aAllowContentRetargetingOnChildren)
+{
+ mAllowContentRetargetingOnChildren = aAllowContentRetargetingOnChildren;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetInheritPrivateBrowsingId(bool* aInheritPrivateBrowsingId)
+{
+ *aInheritPrivateBrowsingId = mInheritPrivateBrowsingId;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetInheritPrivateBrowsingId(bool aInheritPrivateBrowsingId)
+{
+ mInheritPrivateBrowsingId = aInheritPrivateBrowsingId;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetFullscreenAllowed(bool* aFullscreenAllowed)
+{
+ NS_ENSURE_ARG_POINTER(aFullscreenAllowed);
+
+ // Browsers and apps have their mFullscreenAllowed retrieved from their
+ // corresponding iframe in their parent upon creation.
+ if (mFullscreenAllowed != CHECK_ATTRIBUTES) {
+ *aFullscreenAllowed = (mFullscreenAllowed == PARENT_ALLOWS);
+ return NS_OK;
+ }
+
+ // Assume false until we determine otherwise...
+ *aFullscreenAllowed = false;
+
+ nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
+ if (!win) {
+ return NS_OK;
+ }
+ if (nsCOMPtr<Element> frameElement = win->GetFrameElementInternal()) {
+ if (frameElement->IsXULElement()) {
+ if (frameElement->HasAttr(kNameSpaceID_None,
+ nsGkAtoms::disablefullscreen)) {
+ // Document inside this frame is explicitly disabled.
+ return NS_OK;
+ }
+ } else {
+ // We do not allow document inside any containing element other
+ // than iframe to enter fullscreen.
+ if (frameElement->IsHTMLElement(nsGkAtoms::iframe)) {
+ // If any ancestor iframe does not have allowfullscreen attribute
+ // set, then fullscreen is not allowed.
+ if (!frameElement->HasAttr(kNameSpaceID_None,
+ nsGkAtoms::allowfullscreen) &&
+ !frameElement->HasAttr(kNameSpaceID_None,
+ nsGkAtoms::mozallowfullscreen)) {
+ return NS_OK;
+ }
+ } else if (frameElement->IsHTMLElement(nsGkAtoms::embed)) {
+ // Respect allowfullscreen only if this is a rewritten YouTube embed.
+ nsCOMPtr<nsIObjectLoadingContent> objectLoadingContent =
+ do_QueryInterface(frameElement);
+ if (!objectLoadingContent) {
+ return NS_OK;
+ }
+ nsObjectLoadingContent* olc =
+ static_cast<nsObjectLoadingContent*>(objectLoadingContent.get());
+ if (!olc->IsRewrittenYoutubeEmbed()) {
+ return NS_OK;
+ }
+ // We don't have to check prefixed attributes because Flash does not
+ // support them.
+ if (!frameElement->HasAttr(kNameSpaceID_None,
+ nsGkAtoms::allowfullscreen)) {
+ return NS_OK;
+ }
+ } else {
+ // neither iframe nor embed
+ return NS_OK;
+ }
+ }
+ }
+
+ // If we have no parent then we're the root docshell; no ancestor of the
+ // original docshell doesn't have a allowfullscreen attribute, so
+ // report fullscreen as allowed.
+ RefPtr<nsDocShell> parent = GetParentDocshell();
+ if (!parent) {
+ *aFullscreenAllowed = true;
+ return NS_OK;
+ }
+
+ // Otherwise, we have a parent, continue the checking for
+ // mozFullscreenAllowed in the parent docshell's ancestors.
+ return parent->GetFullscreenAllowed(aFullscreenAllowed);
+}
+
+NS_IMETHODIMP
+nsDocShell::SetFullscreenAllowed(bool aFullscreenAllowed)
+{
+ if (!nsIDocShell::GetIsMozBrowserOrApp()) {
+ // Only allow setting of fullscreenAllowed on content/process boundaries.
+ // At non-boundaries the fullscreenAllowed attribute is calculated based on
+ // whether all enclosing frames have the "mozFullscreenAllowed" attribute
+ // set to "true". fullscreenAllowed is set at the process boundaries to
+ // propagate the value of the parent's "mozFullscreenAllowed" attribute
+ // across process boundaries.
+ return NS_ERROR_UNEXPECTED;
+ }
+ mFullscreenAllowed = (aFullscreenAllowed ? PARENT_ALLOWS : PARENT_PROHIBITS);
+ return NS_OK;
+}
+
+ScreenOrientationInternal
+nsDocShell::OrientationLock()
+{
+ return mOrientationLock;
+}
+
+void
+nsDocShell::SetOrientationLock(ScreenOrientationInternal aOrientationLock)
+{
+ mOrientationLock = aOrientationLock;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetMayEnableCharacterEncodingMenu(
+ bool* aMayEnableCharacterEncodingMenu)
+{
+ *aMayEnableCharacterEncodingMenu = false;
+ if (!mContentViewer) {
+ return NS_OK;
+ }
+ nsIDocument* doc = mContentViewer->GetDocument();
+ if (!doc) {
+ return NS_OK;
+ }
+ if (doc->WillIgnoreCharsetOverride()) {
+ return NS_OK;
+ }
+
+ *aMayEnableCharacterEncodingMenu = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetDocShellEnumerator(int32_t aItemType, int32_t aDirection,
+ nsISimpleEnumerator** aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+ *aResult = nullptr;
+
+ RefPtr<nsDocShellEnumerator> docShellEnum;
+ if (aDirection == ENUMERATE_FORWARDS) {
+ docShellEnum = new nsDocShellForwardsEnumerator;
+ } else {
+ docShellEnum = new nsDocShellBackwardsEnumerator;
+ }
+
+ nsresult rv = docShellEnum->SetEnumDocShellType(aItemType);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ rv = docShellEnum->SetEnumerationRootItem((nsIDocShellTreeItem*)this);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ rv = docShellEnum->First();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ rv = docShellEnum->QueryInterface(NS_GET_IID(nsISimpleEnumerator),
+ (void**)aResult);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetAppType(uint32_t* aAppType)
+{
+ *aAppType = mAppType;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetAppType(uint32_t aAppType)
+{
+ mAppType = aAppType;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetAllowAuth(bool* aAllowAuth)
+{
+ *aAllowAuth = mAllowAuth;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetAllowAuth(bool aAllowAuth)
+{
+ mAllowAuth = aAllowAuth;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetZoom(float* aZoom)
+{
+ NS_ENSURE_ARG_POINTER(aZoom);
+ *aZoom = 1.0f;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetZoom(float aZoom)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetMarginWidth(int32_t* aWidth)
+{
+ NS_ENSURE_ARG_POINTER(aWidth);
+
+ *aWidth = mMarginWidth;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetMarginWidth(int32_t aWidth)
+{
+ mMarginWidth = aWidth;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetMarginHeight(int32_t* aHeight)
+{
+ NS_ENSURE_ARG_POINTER(aHeight);
+
+ *aHeight = mMarginHeight;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetMarginHeight(int32_t aHeight)
+{
+ mMarginHeight = aHeight;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetBusyFlags(uint32_t* aBusyFlags)
+{
+ NS_ENSURE_ARG_POINTER(aBusyFlags);
+
+ *aBusyFlags = mBusyFlags;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::TabToTreeOwner(bool aForward, bool aForDocumentNavigation, bool* aTookFocus)
+{
+ NS_ENSURE_ARG_POINTER(aTookFocus);
+
+ nsCOMPtr<nsIWebBrowserChromeFocus> chromeFocus = do_GetInterface(mTreeOwner);
+ if (chromeFocus) {
+ if (aForward) {
+ *aTookFocus = NS_SUCCEEDED(chromeFocus->FocusNextElement(aForDocumentNavigation));
+ } else {
+ *aTookFocus = NS_SUCCEEDED(chromeFocus->FocusPrevElement(aForDocumentNavigation));
+ }
+ } else {
+ *aTookFocus = false;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetSecurityUI(nsISecureBrowserUI** aSecurityUI)
+{
+ NS_IF_ADDREF(*aSecurityUI = mSecurityUI);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetSecurityUI(nsISecureBrowserUI* aSecurityUI)
+{
+ mSecurityUI = aSecurityUI;
+ mSecurityUI->SetDocShell(this);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetUseErrorPages(bool* aUseErrorPages)
+{
+ *aUseErrorPages = UseErrorPages();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetUseErrorPages(bool aUseErrorPages)
+{
+ // If mUseErrorPages is set explicitly, stop using sUseErrorPages.
+ if (mObserveErrorPages) {
+ mObserveErrorPages = false;
+ }
+ mUseErrorPages = aUseErrorPages;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetPreviousTransIndex(int32_t* aPreviousTransIndex)
+{
+ *aPreviousTransIndex = mPreviousTransIndex;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetLoadedTransIndex(int32_t* aLoadedTransIndex)
+{
+ *aLoadedTransIndex = mLoadedTransIndex;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::HistoryPurged(int32_t aNumEntries)
+{
+ // These indices are used for fastback cache eviction, to determine
+ // which session history entries are candidates for content viewer
+ // eviction. We need to adjust by the number of entries that we
+ // just purged from history, so that we look at the right session history
+ // entries during eviction.
+ mPreviousTransIndex = std::max(-1, mPreviousTransIndex - aNumEntries);
+ mLoadedTransIndex = std::max(0, mLoadedTransIndex - aNumEntries);
+
+ nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
+ while (iter.HasMore()) {
+ nsCOMPtr<nsIDocShell> shell = do_QueryObject(iter.GetNext());
+ if (shell) {
+ shell->HistoryPurged(aNumEntries);
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsDocShell::HistoryTransactionRemoved(int32_t aIndex)
+{
+ // These indices are used for fastback cache eviction, to determine
+ // which session history entries are candidates for content viewer
+ // eviction. We need to adjust by the number of entries that we
+ // just purged from history, so that we look at the right session history
+ // entries during eviction.
+ if (aIndex == mPreviousTransIndex) {
+ mPreviousTransIndex = -1;
+ } else if (aIndex < mPreviousTransIndex) {
+ --mPreviousTransIndex;
+ }
+ if (mLoadedTransIndex == aIndex) {
+ mLoadedTransIndex = 0;
+ } else if (aIndex < mLoadedTransIndex) {
+ --mLoadedTransIndex;
+ }
+
+ nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
+ while (iter.HasMore()) {
+ nsCOMPtr<nsIDocShell> shell = do_QueryObject(iter.GetNext());
+ if (shell) {
+ static_cast<nsDocShell*>(shell.get())->HistoryTransactionRemoved(aIndex);
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetRecordProfileTimelineMarkers(bool aValue)
+{
+ bool currentValue = nsIDocShell::GetRecordProfileTimelineMarkers();
+ if (currentValue == aValue) {
+ return NS_OK;
+ }
+
+ RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
+ if (!timelines) {
+ return NS_OK;
+ }
+
+ if (aValue) {
+ MOZ_ASSERT(!timelines->HasConsumer(this));
+ timelines->AddConsumer(this);
+ MOZ_ASSERT(timelines->HasConsumer(this));
+ UseEntryScriptProfiling();
+ } else {
+ MOZ_ASSERT(timelines->HasConsumer(this));
+ timelines->RemoveConsumer(this);
+ MOZ_ASSERT(!timelines->HasConsumer(this));
+ UnuseEntryScriptProfiling();
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetRecordProfileTimelineMarkers(bool* aValue)
+{
+ *aValue = !!mObserved;
+ return NS_OK;
+}
+
+nsresult
+nsDocShell::PopProfileTimelineMarkers(
+ JSContext* aCx,
+ JS::MutableHandle<JS::Value> aOut)
+{
+ RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
+ if (!timelines) {
+ return NS_OK;
+ }
+
+ nsTArray<dom::ProfileTimelineMarker> store;
+ SequenceRooter<dom::ProfileTimelineMarker> rooter(aCx, &store);
+
+ timelines->PopMarkers(this, aCx, store);
+
+ if (!ToJSValue(aCx, store, aOut)) {
+ JS_ClearPendingException(aCx);
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsDocShell::Now(DOMHighResTimeStamp* aWhen)
+{
+ bool ignore;
+ *aWhen =
+ (TimeStamp::Now() - TimeStamp::ProcessCreation(ignore)).ToMilliseconds();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetWindowDraggingAllowed(bool aValue)
+{
+ RefPtr<nsDocShell> parent = GetParentDocshell();
+ if (!aValue && mItemType == typeChrome && !parent) {
+ // Window dragging is always allowed for top level
+ // chrome docshells.
+ return NS_ERROR_FAILURE;
+ }
+ mWindowDraggingAllowed = aValue;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetWindowDraggingAllowed(bool* aValue)
+{
+ // window dragging regions in CSS (-moz-window-drag:drag)
+ // can be slow. Default behavior is to only allow it for
+ // chrome top level windows.
+ RefPtr<nsDocShell> parent = GetParentDocshell();
+ if (mItemType == typeChrome && !parent) {
+ // Top level chrome window
+ *aValue = true;
+ } else {
+ *aValue = mWindowDraggingAllowed;
+ }
+ return NS_OK;
+}
+
+nsIDOMStorageManager*
+nsDocShell::TopSessionStorageManager()
+{
+ nsresult rv;
+
+ nsCOMPtr<nsIDocShellTreeItem> topItem;
+ rv = GetSameTypeRootTreeItem(getter_AddRefs(topItem));
+ if (NS_FAILED(rv)) {
+ return nullptr;
+ }
+
+ if (!topItem) {
+ return nullptr;
+ }
+
+ nsDocShell* topDocShell = static_cast<nsDocShell*>(topItem.get());
+ if (topDocShell != this) {
+ return topDocShell->TopSessionStorageManager();
+ }
+
+ if (!mSessionStorageManager) {
+ mSessionStorageManager =
+ do_CreateInstance("@mozilla.org/dom/sessionStorage-manager;1");
+ }
+
+ return mSessionStorageManager;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetSessionStorageForPrincipal(nsIPrincipal* aPrincipal,
+ const nsAString& aDocumentURI,
+ bool aCreate,
+ nsIDOMStorage** aStorage)
+{
+ nsCOMPtr<nsIDOMStorageManager> manager = TopSessionStorageManager();
+ if (!manager) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ nsCOMPtr<nsPIDOMWindowOuter> domWin = GetWindow();
+
+ AssertOriginAttributesMatchPrivateBrowsing();
+ if (aCreate) {
+ return manager->CreateStorage(domWin->GetCurrentInnerWindow(), aPrincipal,
+ aDocumentURI, UsePrivateBrowsing(), aStorage);
+ }
+
+ return manager->GetStorage(domWin->GetCurrentInnerWindow(), aPrincipal,
+ UsePrivateBrowsing(), aStorage);
+}
+
+nsresult
+nsDocShell::AddSessionStorage(nsIPrincipal* aPrincipal, nsIDOMStorage* aStorage)
+{
+ RefPtr<DOMStorage> storage = static_cast<DOMStorage*>(aStorage);
+ if (!storage) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ nsIPrincipal* storagePrincipal = storage->GetPrincipal();
+ if (storagePrincipal != aPrincipal) {
+ NS_ERROR("Wanting to add a sessionStorage for different principal");
+ return NS_ERROR_DOM_SECURITY_ERR;
+ }
+
+ nsCOMPtr<nsIDOMStorageManager> manager = TopSessionStorageManager();
+ if (!manager) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ return manager->CloneStorage(aStorage);
+}
+
+NS_IMETHODIMP
+nsDocShell::GetCurrentDocumentChannel(nsIChannel** aResult)
+{
+ NS_IF_ADDREF(*aResult = GetCurrentDocChannel());
+ return NS_OK;
+}
+
+nsIChannel*
+nsDocShell::GetCurrentDocChannel()
+{
+ if (mContentViewer) {
+ nsIDocument* doc = mContentViewer->GetDocument();
+ if (doc) {
+ return doc->GetChannel();
+ }
+ }
+ return nullptr;
+}
+
+NS_IMETHODIMP
+nsDocShell::AddWeakScrollObserver(nsIScrollObserver* aObserver)
+{
+ nsWeakPtr weakObs = do_GetWeakReference(aObserver);
+ if (!weakObs) {
+ return NS_ERROR_FAILURE;
+ }
+ return mScrollObservers.AppendElement(weakObs) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsDocShell::RemoveWeakScrollObserver(nsIScrollObserver* aObserver)
+{
+ nsWeakPtr obs = do_GetWeakReference(aObserver);
+ return mScrollObservers.RemoveElement(obs) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+void
+nsDocShell::NotifyAsyncPanZoomStarted()
+{
+ nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers);
+ while (iter.HasMore()) {
+ nsWeakPtr ref = iter.GetNext();
+ nsCOMPtr<nsIScrollObserver> obs = do_QueryReferent(ref);
+ if (obs) {
+ obs->AsyncPanZoomStarted();
+ } else {
+ mScrollObservers.RemoveElement(ref);
+ }
+ }
+}
+
+void
+nsDocShell::NotifyAsyncPanZoomStopped()
+{
+ nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers);
+ while (iter.HasMore()) {
+ nsWeakPtr ref = iter.GetNext();
+ nsCOMPtr<nsIScrollObserver> obs = do_QueryReferent(ref);
+ if (obs) {
+ obs->AsyncPanZoomStopped();
+ } else {
+ mScrollObservers.RemoveElement(ref);
+ }
+ }
+}
+
+NS_IMETHODIMP
+nsDocShell::NotifyScrollObservers()
+{
+ nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers);
+ while (iter.HasMore()) {
+ nsWeakPtr ref = iter.GetNext();
+ nsCOMPtr<nsIScrollObserver> obs = do_QueryReferent(ref);
+ if (obs) {
+ obs->ScrollPositionChanged();
+ } else {
+ mScrollObservers.RemoveElement(ref);
+ }
+ }
+ return NS_OK;
+}
+
+//*****************************************************************************
+// nsDocShell::nsIDocShellTreeItem
+//*****************************************************************************
+
+NS_IMETHODIMP
+nsDocShell::GetName(nsAString& aName)
+{
+ aName = mName;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetName(const nsAString& aName)
+{
+ mName = aName;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::NameEquals(const nsAString& aName, bool* aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+ *aResult = mName.Equals(aName);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetCustomUserAgent(nsAString& aCustomUserAgent)
+{
+ aCustomUserAgent = mCustomUserAgent;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetCustomUserAgent(const nsAString& aCustomUserAgent)
+{
+ mCustomUserAgent = aCustomUserAgent;
+ RefPtr<nsGlobalWindow> win = mScriptGlobal ?
+ mScriptGlobal->GetCurrentInnerWindowInternal() : nullptr;
+ if (win) {
+ ErrorResult ignored;
+ Navigator* navigator = win->GetNavigator(ignored);
+ ignored.SuppressException();
+ if (navigator) {
+ navigator->ClearUserAgentCache();
+ }
+ }
+
+ uint32_t childCount = mChildList.Length();
+ for (uint32_t i = 0; i < childCount; ++i) {
+ nsCOMPtr<nsIDocShell> childShell = do_QueryInterface(ChildAt(i));
+ if (childShell) {
+ childShell->SetCustomUserAgent(aCustomUserAgent);
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetTouchEventsOverride(uint32_t* aTouchEventsOverride)
+{
+ NS_ENSURE_ARG_POINTER(aTouchEventsOverride);
+
+ *aTouchEventsOverride = mTouchEventsOverride;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetTouchEventsOverride(uint32_t aTouchEventsOverride)
+{
+ if (!(aTouchEventsOverride == nsIDocShell::TOUCHEVENTS_OVERRIDE_NONE ||
+ aTouchEventsOverride == nsIDocShell::TOUCHEVENTS_OVERRIDE_ENABLED ||
+ aTouchEventsOverride == nsIDocShell::TOUCHEVENTS_OVERRIDE_DISABLED)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ mTouchEventsOverride = aTouchEventsOverride;
+
+ uint32_t childCount = mChildList.Length();
+ for (uint32_t i = 0; i < childCount; ++i) {
+ nsCOMPtr<nsIDocShell> childShell = do_QueryInterface(ChildAt(i));
+ if (childShell) {
+ childShell->SetTouchEventsOverride(aTouchEventsOverride);
+ }
+ }
+ return NS_OK;
+}
+
+/* virtual */ int32_t
+nsDocShell::ItemType()
+{
+ return mItemType;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetItemType(int32_t* aItemType)
+{
+ NS_ENSURE_ARG_POINTER(aItemType);
+
+ *aItemType = ItemType();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetItemType(int32_t aItemType)
+{
+ NS_ENSURE_ARG((aItemType == typeChrome) || (typeContent == aItemType));
+
+ // Only allow setting the type on root docshells. Those would be the ones
+ // that have the docloader service as mParent or have no mParent at all.
+ nsCOMPtr<nsIDocumentLoader> docLoaderService =
+ do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID);
+ NS_ENSURE_TRUE(docLoaderService, NS_ERROR_UNEXPECTED);
+
+ NS_ENSURE_STATE(!mParent || mParent == docLoaderService);
+
+ mItemType = aItemType;
+
+ // disable auth prompting for anything but content
+ mAllowAuth = mItemType == typeContent;
+
+ RefPtr<nsPresContext> presContext = nullptr;
+ GetPresContext(getter_AddRefs(presContext));
+ if (presContext) {
+ presContext->UpdateIsChrome();
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetParent(nsIDocShellTreeItem** aParent)
+{
+ if (!mParent) {
+ *aParent = nullptr;
+ } else {
+ CallQueryInterface(mParent, aParent);
+ }
+ // Note that in the case when the parent is not an nsIDocShellTreeItem we
+ // don't want to throw; we just want to return null.
+ return NS_OK;
+}
+
+already_AddRefed<nsDocShell>
+nsDocShell::GetParentDocshell()
+{
+ nsCOMPtr<nsIDocShell> docshell = do_QueryInterface(GetAsSupports(mParent));
+ return docshell.forget().downcast<nsDocShell>();
+}
+
+void
+nsDocShell::RecomputeCanExecuteScripts()
+{
+ bool old = mCanExecuteScripts;
+ RefPtr<nsDocShell> parent = GetParentDocshell();
+
+ // If we have no tree owner, that means that we've been detached from the
+ // docshell tree (this is distinct from having no parent dochshell, which
+ // is the case for root docshells). It would be nice to simply disallow
+ // script in detached docshells, but bug 986542 demonstrates that this
+ // behavior breaks at least one website.
+ //
+ // So instead, we use our previous value, unless mAllowJavascript has been
+ // explicitly set to false.
+ if (!mTreeOwner) {
+ mCanExecuteScripts = mCanExecuteScripts && mAllowJavascript;
+ // If scripting has been explicitly disabled on our docshell, we're done.
+ } else if (!mAllowJavascript) {
+ mCanExecuteScripts = false;
+ // If we have a parent, inherit.
+ } else if (parent) {
+ mCanExecuteScripts = parent->mCanExecuteScripts;
+ // Otherwise, we're the root of the tree, and we haven't explicitly disabled
+ // script. Allow.
+ } else {
+ mCanExecuteScripts = true;
+ }
+
+ // Inform our active DOM window.
+ //
+ // This will pass the outer, which will be in the scope of the active inner.
+ if (mScriptGlobal && mScriptGlobal->GetGlobalJSObject()) {
+ xpc::Scriptability& scriptability =
+ xpc::Scriptability::Get(mScriptGlobal->GetGlobalJSObject());
+ scriptability.SetDocShellAllowsScript(mCanExecuteScripts);
+ }
+
+ // If our value has changed, our children might be affected. Recompute their
+ // value as well.
+ if (old != mCanExecuteScripts) {
+ nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
+ while (iter.HasMore()) {
+ static_cast<nsDocShell*>(iter.GetNext())->RecomputeCanExecuteScripts();
+ }
+ }
+}
+
+nsresult
+nsDocShell::SetDocLoaderParent(nsDocLoader* aParent)
+{
+ bool wasFrame = IsFrame();
+#ifdef DEBUG
+ bool wasPrivate = UsePrivateBrowsing();
+#endif
+
+ nsresult rv = nsDocLoader::SetDocLoaderParent(aParent);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsISupportsPriority> priorityGroup = do_QueryInterface(mLoadGroup);
+ if (wasFrame != IsFrame() && priorityGroup) {
+ priorityGroup->AdjustPriority(wasFrame ? -1 : 1);
+ }
+
+ // Curse ambiguous nsISupports inheritance!
+ nsISupports* parent = GetAsSupports(aParent);
+
+ // If parent is another docshell, we inherit all their flags for
+ // allowing plugins, scripting etc.
+ bool value;
+ nsString customUserAgent;
+ nsCOMPtr<nsIDocShell> parentAsDocShell(do_QueryInterface(parent));
+ if (parentAsDocShell) {
+ if (mAllowPlugins && NS_SUCCEEDED(parentAsDocShell->GetAllowPlugins(&value))) {
+ SetAllowPlugins(value);
+ }
+ if (mAllowJavascript && NS_SUCCEEDED(parentAsDocShell->GetAllowJavascript(&value))) {
+ SetAllowJavascript(value);
+ }
+ if (mAllowMetaRedirects && NS_SUCCEEDED(parentAsDocShell->GetAllowMetaRedirects(&value))) {
+ SetAllowMetaRedirects(value);
+ }
+ if (mAllowSubframes && NS_SUCCEEDED(parentAsDocShell->GetAllowSubframes(&value))) {
+ SetAllowSubframes(value);
+ }
+ if (mAllowImages && NS_SUCCEEDED(parentAsDocShell->GetAllowImages(&value))) {
+ SetAllowImages(value);
+ }
+ SetAllowMedia(parentAsDocShell->GetAllowMedia() && mAllowMedia);
+ if (mAllowWindowControl && NS_SUCCEEDED(parentAsDocShell->GetAllowWindowControl(&value))) {
+ SetAllowWindowControl(value);
+ }
+ SetAllowContentRetargeting(mAllowContentRetargeting &&
+ parentAsDocShell->GetAllowContentRetargetingOnChildren());
+ if (parentAsDocShell->GetIsPrerendered()) {
+ SetIsPrerendered();
+ }
+ if (NS_SUCCEEDED(parentAsDocShell->GetIsActive(&value))) {
+ // a prerendered docshell is not active yet
+ SetIsActive(value && !mIsPrerendered);
+ }
+ if (NS_SUCCEEDED(parentAsDocShell->GetCustomUserAgent(customUserAgent)) &&
+ !customUserAgent.IsEmpty()) {
+ SetCustomUserAgent(customUserAgent);
+ }
+ if (NS_FAILED(parentAsDocShell->GetAllowDNSPrefetch(&value))) {
+ value = false;
+ }
+ SetAllowDNSPrefetch(mAllowDNSPrefetch && value);
+ if (mInheritPrivateBrowsingId) {
+ value = parentAsDocShell->GetAffectPrivateSessionLifetime();
+ SetAffectPrivateSessionLifetime(value);
+ }
+ uint32_t flags;
+ if (NS_SUCCEEDED(parentAsDocShell->GetDefaultLoadFlags(&flags))) {
+ SetDefaultLoadFlags(flags);
+ }
+ uint32_t touchEventsOverride;
+ if (NS_SUCCEEDED(parentAsDocShell->GetTouchEventsOverride(&touchEventsOverride))) {
+ SetTouchEventsOverride(touchEventsOverride);
+ }
+ }
+
+ nsCOMPtr<nsILoadContext> parentAsLoadContext(do_QueryInterface(parent));
+ if (parentAsLoadContext && mInheritPrivateBrowsingId &&
+ NS_SUCCEEDED(parentAsLoadContext->GetUsePrivateBrowsing(&value))) {
+ SetPrivateBrowsing(value);
+ }
+
+ nsCOMPtr<nsIURIContentListener> parentURIListener(do_GetInterface(parent));
+ if (parentURIListener) {
+ mContentListener->SetParentContentListener(parentURIListener);
+ }
+
+ // Our parent has changed. Recompute scriptability.
+ RecomputeCanExecuteScripts();
+
+ NS_ASSERTION(mInheritPrivateBrowsingId || wasPrivate == UsePrivateBrowsing(),
+ "Private browsing state changed while inheritance was disabled");
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetSameTypeParent(nsIDocShellTreeItem** aParent)
+{
+ NS_ENSURE_ARG_POINTER(aParent);
+ *aParent = nullptr;
+
+ if (nsIDocShell::GetIsMozBrowserOrApp()) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIDocShellTreeItem> parent =
+ do_QueryInterface(GetAsSupports(mParent));
+ if (!parent) {
+ return NS_OK;
+ }
+
+ if (parent->ItemType() == mItemType) {
+ parent.swap(*aParent);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetSameTypeParentIgnoreBrowserAndAppBoundaries(nsIDocShell** aParent)
+{
+ NS_ENSURE_ARG_POINTER(aParent);
+ *aParent = nullptr;
+
+ nsCOMPtr<nsIDocShellTreeItem> parent =
+ do_QueryInterface(GetAsSupports(mParent));
+ if (!parent) {
+ return NS_OK;
+ }
+
+ if (parent->ItemType() == mItemType) {
+ nsCOMPtr<nsIDocShell> parentDS = do_QueryInterface(parent);
+ parentDS.forget(aParent);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetRootTreeItem(nsIDocShellTreeItem** aRootTreeItem)
+{
+ NS_ENSURE_ARG_POINTER(aRootTreeItem);
+
+ RefPtr<nsDocShell> root = this;
+ RefPtr<nsDocShell> parent = root->GetParentDocshell();
+ while (parent) {
+ root = parent;
+ parent = root->GetParentDocshell();
+ }
+
+ root.forget(aRootTreeItem);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetSameTypeRootTreeItem(nsIDocShellTreeItem** aRootTreeItem)
+{
+ NS_ENSURE_ARG_POINTER(aRootTreeItem);
+ *aRootTreeItem = static_cast<nsIDocShellTreeItem*>(this);
+
+ nsCOMPtr<nsIDocShellTreeItem> parent;
+ NS_ENSURE_SUCCESS(GetSameTypeParent(getter_AddRefs(parent)),
+ NS_ERROR_FAILURE);
+ while (parent) {
+ *aRootTreeItem = parent;
+ NS_ENSURE_SUCCESS(
+ (*aRootTreeItem)->GetSameTypeParent(getter_AddRefs(parent)),
+ NS_ERROR_FAILURE);
+ }
+ NS_ADDREF(*aRootTreeItem);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetSameTypeRootTreeItemIgnoreBrowserAndAppBoundaries(nsIDocShell ** aRootTreeItem)
+{
+ NS_ENSURE_ARG_POINTER(aRootTreeItem);
+ *aRootTreeItem = static_cast<nsIDocShell *>(this);
+
+ nsCOMPtr<nsIDocShell> parent;
+ NS_ENSURE_SUCCESS(GetSameTypeParentIgnoreBrowserAndAppBoundaries(getter_AddRefs(parent)),
+ NS_ERROR_FAILURE);
+ while (parent) {
+ *aRootTreeItem = parent;
+ NS_ENSURE_SUCCESS((*aRootTreeItem)->
+ GetSameTypeParentIgnoreBrowserAndAppBoundaries(getter_AddRefs(parent)),
+ NS_ERROR_FAILURE);
+ }
+ NS_ADDREF(*aRootTreeItem);
+ return NS_OK;
+}
+
+/* static */
+bool
+nsDocShell::CanAccessItem(nsIDocShellTreeItem* aTargetItem,
+ nsIDocShellTreeItem* aAccessingItem,
+ bool aConsiderOpener)
+{
+ NS_PRECONDITION(aTargetItem, "Must have target item!");
+
+ if (!gValidateOrigin || !aAccessingItem) {
+ // Good to go
+ return true;
+ }
+
+ // XXXbz should we care if aAccessingItem or the document therein is
+ // chrome? Should those get extra privileges?
+
+ // For historical context, see:
+ //
+ // Bug 13871: Prevent frameset spoofing
+ // Bug 103638: Targets with same name in different windows open in wrong
+ // window with javascript
+ // Bug 408052: Adopt "ancestor" frame navigation policy
+
+ // Now do a security check.
+ //
+ // Disallow navigation if the two frames are not part of the same app, or if
+ // they have different is-in-browser-element states.
+ //
+ // Allow navigation if
+ // 1) aAccessingItem can script aTargetItem or one of its ancestors in
+ // the frame hierarchy or
+ // 2) aTargetItem is a top-level frame and aAccessingItem is its descendant
+ // 3) aTargetItem is a top-level frame and aAccessingItem can target
+ // its opener per rule (1) or (2).
+
+ if (aTargetItem == aAccessingItem) {
+ // A frame is allowed to navigate itself.
+ return true;
+ }
+
+ nsCOMPtr<nsIDocShell> targetDS = do_QueryInterface(aTargetItem);
+ nsCOMPtr<nsIDocShell> accessingDS = do_QueryInterface(aAccessingItem);
+ if (!targetDS || !accessingDS) {
+ // We must be able to convert both to nsIDocShell.
+ return false;
+ }
+
+ if (targetDS->GetIsInIsolatedMozBrowserElement() !=
+ accessingDS->GetIsInIsolatedMozBrowserElement() ||
+ targetDS->GetAppId() != accessingDS->GetAppId()) {
+ return false;
+ }
+
+ nsCOMPtr<nsIDocShellTreeItem> accessingRoot;
+ aAccessingItem->GetSameTypeRootTreeItem(getter_AddRefs(accessingRoot));
+ nsCOMPtr<nsIDocShell> accessingRootDS = do_QueryInterface(accessingRoot);
+
+ nsCOMPtr<nsIDocShellTreeItem> targetRoot;
+ aTargetItem->GetSameTypeRootTreeItem(getter_AddRefs(targetRoot));
+ nsCOMPtr<nsIDocShell> targetRootDS = do_QueryInterface(targetRoot);
+
+ DocShellOriginAttributes targetOA =
+ static_cast<nsDocShell*>(targetDS.get())->GetOriginAttributes();
+ DocShellOriginAttributes accessingOA =
+ static_cast<nsDocShell*>(accessingDS.get())->GetOriginAttributes();
+
+ // When the first party isolation is on, the top-level docShell may not have
+ // the firstPartyDomain in its originAttributes, but its document will have
+ // it. So we get the firstPartyDomain from the nodePrincipal of the document
+ // before we compare the originAttributes.
+ if (OriginAttributes::IsFirstPartyEnabled()) {
+ if (accessingDS == accessingRootDS &&
+ aAccessingItem->ItemType() == nsIDocShellTreeItem::typeContent &&
+ !accessingDS->GetIsMozBrowserOrApp()) {
+
+ nsCOMPtr<nsIDocument> accessingDoc = aAccessingItem->GetDocument();
+
+ if (accessingDoc) {
+ nsCOMPtr<nsIPrincipal> accessingPrincipal = accessingDoc->NodePrincipal();
+
+ accessingOA.mFirstPartyDomain =
+ BasePrincipal::Cast(accessingPrincipal)->OriginAttributesRef().mFirstPartyDomain;
+ }
+ }
+
+ if (targetDS == targetRootDS &&
+ aTargetItem->ItemType() == nsIDocShellTreeItem::typeContent &&
+ !targetDS->GetIsMozBrowserOrApp()) {
+
+ nsCOMPtr<nsIDocument> targetDoc = aAccessingItem->GetDocument();
+
+ if (targetDoc) {
+ nsCOMPtr<nsIPrincipal> targetPrincipal = targetDoc->NodePrincipal();
+
+ targetOA.mFirstPartyDomain =
+ BasePrincipal::Cast(targetPrincipal)->OriginAttributesRef().mFirstPartyDomain;
+ }
+ }
+ }
+
+ if (targetOA != accessingOA) {
+ return false;
+ }
+
+ // A private document can't access a non-private one, and vice versa.
+ if (static_cast<nsDocShell*>(targetDS.get())->UsePrivateBrowsing() !=
+ static_cast<nsDocShell*>(accessingDS.get())->UsePrivateBrowsing()) {
+ return false;
+ }
+
+ if (aTargetItem == accessingRoot) {
+ // A frame can navigate its root.
+ return true;
+ }
+
+ // Check if aAccessingItem can navigate one of aTargetItem's ancestors.
+ nsCOMPtr<nsIDocShellTreeItem> target = aTargetItem;
+ do {
+ if (ValidateOrigin(aAccessingItem, target)) {
+ return true;
+ }
+
+ nsCOMPtr<nsIDocShellTreeItem> parent;
+ target->GetSameTypeParent(getter_AddRefs(parent));
+ parent.swap(target);
+ } while (target);
+
+ if (aTargetItem != targetRoot) {
+ // target is a subframe, not in accessor's frame hierarchy, and all its
+ // ancestors have origins different from that of the accessor. Don't
+ // allow access.
+ return false;
+ }
+
+ if (!aConsiderOpener) {
+ // All done here
+ return false;
+ }
+
+ nsCOMPtr<nsPIDOMWindowOuter> targetWindow = aTargetItem->GetWindow();
+ if (!targetWindow) {
+ NS_ERROR("This should not happen, really");
+ return false;
+ }
+
+ nsCOMPtr<mozIDOMWindowProxy> targetOpener = targetWindow->GetOpener();
+ nsCOMPtr<nsIWebNavigation> openerWebNav(do_GetInterface(targetOpener));
+ nsCOMPtr<nsIDocShellTreeItem> openerItem(do_QueryInterface(openerWebNav));
+
+ if (!openerItem) {
+ return false;
+ }
+
+ return CanAccessItem(openerItem, aAccessingItem, false);
+}
+
+static bool
+ItemIsActive(nsIDocShellTreeItem* aItem)
+{
+ if (nsCOMPtr<nsPIDOMWindowOuter> window = aItem->GetWindow()) {
+ auto* win = nsGlobalWindow::Cast(window);
+ MOZ_ASSERT(win->IsOuterWindow());
+ if (!win->GetClosedOuter()) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+NS_IMETHODIMP
+nsDocShell::FindItemWithName(const nsAString& aName,
+ nsISupports* aRequestor,
+ nsIDocShellTreeItem* aOriginalRequestor,
+ nsIDocShellTreeItem** aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ // If we don't find one, we return NS_OK and a null result
+ *aResult = nullptr;
+
+ if (aName.IsEmpty()) {
+ return NS_OK;
+ }
+
+ if (aRequestor) {
+ // If aRequestor is not null we don't need to check special names, so
+ // just hand straight off to the search by actual name function.
+ return DoFindItemWithName(aName, aRequestor, aOriginalRequestor, aResult);
+ } else {
+ // This is the entry point into the target-finding algorithm. Check
+ // for special names. This should only be done once, hence the check
+ // for a null aRequestor.
+
+ nsCOMPtr<nsIDocShellTreeItem> foundItem;
+ if (aName.LowerCaseEqualsLiteral("_self")) {
+ foundItem = this;
+ } else if (aName.LowerCaseEqualsLiteral("_blank")) {
+ // Just return null. Caller must handle creating a new window with
+ // a blank name himself.
+ return NS_OK;
+ } else if (aName.LowerCaseEqualsLiteral("_parent")) {
+ GetSameTypeParent(getter_AddRefs(foundItem));
+ if (!foundItem) {
+ foundItem = this;
+ }
+ } else if (aName.LowerCaseEqualsLiteral("_top")) {
+ GetSameTypeRootTreeItem(getter_AddRefs(foundItem));
+ NS_ASSERTION(foundItem, "Must have this; worst case it's us!");
+ } else {
+ // Do the search for item by an actual name.
+ DoFindItemWithName(aName, aRequestor, aOriginalRequestor,
+ getter_AddRefs(foundItem));
+ }
+
+ if (foundItem && !CanAccessItem(foundItem, aOriginalRequestor)) {
+ foundItem = nullptr;
+ }
+
+ // DoFindItemWithName only returns active items and we don't check if
+ // the item is active for the special cases.
+ if (foundItem) {
+ foundItem.swap(*aResult);
+ }
+ return NS_OK;
+ }
+}
+
+void
+nsDocShell::AssertOriginAttributesMatchPrivateBrowsing() {
+ // Chrome docshells must not have a private browsing OriginAttribute
+ // Content docshells must maintain the equality:
+ // mOriginAttributes.mPrivateBrowsingId == mPrivateBrowsingId
+ if (mItemType == typeChrome) {
+ MOZ_DIAGNOSTIC_ASSERT(mOriginAttributes.mPrivateBrowsingId == 0);
+ } else {
+ MOZ_DIAGNOSTIC_ASSERT(mOriginAttributes.mPrivateBrowsingId == mPrivateBrowsingId);
+ }
+}
+
+nsresult
+nsDocShell::DoFindItemWithName(const nsAString& aName,
+ nsISupports* aRequestor,
+ nsIDocShellTreeItem* aOriginalRequestor,
+ nsIDocShellTreeItem** aResult)
+{
+ // First we check our name.
+ if (mName.Equals(aName) && ItemIsActive(this) &&
+ CanAccessItem(this, aOriginalRequestor)) {
+ NS_ADDREF(*aResult = this);
+ return NS_OK;
+ }
+
+ // This QI may fail, but the places where we want to compare, comparing
+ // against nullptr serves the same purpose.
+ nsCOMPtr<nsIDocShellTreeItem> reqAsTreeItem(do_QueryInterface(aRequestor));
+
+ // Second we check our children making sure not to ask a child if
+ // it is the aRequestor.
+#ifdef DEBUG
+ nsresult rv =
+#endif
+ FindChildWithName(aName, true, true, reqAsTreeItem, aOriginalRequestor,
+ aResult);
+ NS_ASSERTION(NS_SUCCEEDED(rv),
+ "FindChildWithName should not be failing here.");
+ if (*aResult) {
+ return NS_OK;
+ }
+
+ // Third if we have a parent and it isn't the requestor then we
+ // should ask it to do the search. If it is the requestor we
+ // should just stop here and let the parent do the rest. If we
+ // don't have a parent, then we should ask the
+ // docShellTreeOwner to do the search.
+ nsCOMPtr<nsIDocShellTreeItem> parentAsTreeItem =
+ do_QueryInterface(GetAsSupports(mParent));
+ if (parentAsTreeItem) {
+ if (parentAsTreeItem == reqAsTreeItem) {
+ return NS_OK;
+ }
+
+ // If we have a same-type parent, respecting browser and app boundaries.
+ // NOTE: Could use GetSameTypeParent if the issues described in bug 1310344 are fixed.
+ if (!GetIsMozBrowserOrApp() && parentAsTreeItem->ItemType() == mItemType) {
+ return parentAsTreeItem->FindItemWithName(
+ aName,
+ static_cast<nsIDocShellTreeItem*>(this),
+ aOriginalRequestor,
+ aResult);
+ }
+ }
+
+ // If we have a null parent or the parent is not of the same type, we need to
+ // give up on finding it in our tree, and start looking in our TabGroup.
+ nsCOMPtr<nsPIDOMWindowOuter> window = GetWindow();
+ if (window) {
+ RefPtr<mozilla::dom::TabGroup> tabGroup = window->TabGroup();
+ // We don't want to make the request to our TabGroup if they are the ones
+ // which made a request to us.
+ if (tabGroup != aRequestor) {
+ tabGroup->FindItemWithName(aName, this, aOriginalRequestor, aResult);
+ }
+ }
+
+ return NS_OK;
+}
+
+bool
+nsDocShell::IsSandboxedFrom(nsIDocShell* aTargetDocShell)
+{
+ // If no target then not sandboxed.
+ if (!aTargetDocShell) {
+ return false;
+ }
+
+ // We cannot be sandboxed from ourselves.
+ if (aTargetDocShell == this) {
+ return false;
+ }
+
+ // Default the sandbox flags to our flags, so that if we can't retrieve the
+ // active document, we will still enforce our own.
+ uint32_t sandboxFlags = mSandboxFlags;
+ if (mContentViewer) {
+ nsCOMPtr<nsIDocument> doc = mContentViewer->GetDocument();
+ if (doc) {
+ sandboxFlags = doc->GetSandboxFlags();
+ }
+ }
+
+ // If no flags, we are not sandboxed at all.
+ if (!sandboxFlags) {
+ return false;
+ }
+
+ // If aTargetDocShell has an ancestor, it is not top level.
+ nsCOMPtr<nsIDocShellTreeItem> ancestorOfTarget;
+ aTargetDocShell->GetSameTypeParent(getter_AddRefs(ancestorOfTarget));
+ if (ancestorOfTarget) {
+ do {
+ // We are not sandboxed if we are an ancestor of target.
+ if (ancestorOfTarget == this) {
+ return false;
+ }
+ nsCOMPtr<nsIDocShellTreeItem> tempTreeItem;
+ ancestorOfTarget->GetSameTypeParent(getter_AddRefs(tempTreeItem));
+ tempTreeItem.swap(ancestorOfTarget);
+ } while (ancestorOfTarget);
+
+ // Otherwise, we are sandboxed from aTargetDocShell.
+ return true;
+ }
+
+ // aTargetDocShell is top level, are we the "one permitted sandboxed
+ // navigator", i.e. did we open aTargetDocShell?
+ nsCOMPtr<nsIDocShell> permittedNavigator;
+ aTargetDocShell->GetOnePermittedSandboxedNavigator(
+ getter_AddRefs(permittedNavigator));
+ if (permittedNavigator == this) {
+ return false;
+ }
+
+ // If SANDBOXED_TOPLEVEL_NAVIGATION flag is not on, we are not sandboxed
+ // from our top.
+ if (!(sandboxFlags & SANDBOXED_TOPLEVEL_NAVIGATION)) {
+ nsCOMPtr<nsIDocShellTreeItem> rootTreeItem;
+ GetSameTypeRootTreeItem(getter_AddRefs(rootTreeItem));
+ if (SameCOMIdentity(aTargetDocShell, rootTreeItem)) {
+ return false;
+ }
+ }
+
+ // Otherwise, we are sandboxed from aTargetDocShell.
+ return true;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetTreeOwner(nsIDocShellTreeOwner** aTreeOwner)
+{
+ NS_ENSURE_ARG_POINTER(aTreeOwner);
+
+ *aTreeOwner = mTreeOwner;
+ NS_IF_ADDREF(*aTreeOwner);
+ return NS_OK;
+}
+
+#ifdef DEBUG_DOCSHELL_FOCUS
+static void
+PrintDocTree(nsIDocShellTreeItem* aParentNode, int aLevel)
+{
+ for (int32_t i = 0; i < aLevel; i++) {
+ printf(" ");
+ }
+
+ int32_t childWebshellCount;
+ aParentNode->GetChildCount(&childWebshellCount);
+ nsCOMPtr<nsIDocShell> parentAsDocShell(do_QueryInterface(aParentNode));
+ int32_t type = aParentNode->ItemType();
+ nsCOMPtr<nsIPresShell> presShell = parentAsDocShell->GetPresShell();
+ RefPtr<nsPresContext> presContext;
+ parentAsDocShell->GetPresContext(getter_AddRefs(presContext));
+ nsIDocument* doc = presShell->GetDocument();
+
+ nsCOMPtr<nsPIDOMWindowOuter> domwin(doc->GetWindow());
+
+ nsCOMPtr<nsIWidget> widget;
+ nsViewManager* vm = presShell->GetViewManager();
+ if (vm) {
+ vm->GetWidget(getter_AddRefs(widget));
+ }
+ dom::Element* rootElement = doc->GetRootElement();
+
+ printf("DS %p Ty %s Doc %p DW %p EM %p CN %p\n",
+ (void*)parentAsDocShell.get(),
+ type == nsIDocShellTreeItem::typeChrome ? "Chr" : "Con",
+ (void*)doc, (void*)domwin.get(),
+ (void*)presContext->EventStateManager(), (void*)rootElement);
+
+ if (childWebshellCount > 0) {
+ for (int32_t i = 0; i < childWebshellCount; i++) {
+ nsCOMPtr<nsIDocShellTreeItem> child;
+ aParentNode->GetChildAt(i, getter_AddRefs(child));
+ PrintDocTree(child, aLevel + 1);
+ }
+ }
+}
+
+static void
+PrintDocTree(nsIDocShellTreeItem* aParentNode)
+{
+ NS_ASSERTION(aParentNode, "Pointer is null!");
+
+ nsCOMPtr<nsIDocShellTreeItem> parentItem;
+ aParentNode->GetParent(getter_AddRefs(parentItem));
+ while (parentItem) {
+ nsCOMPtr<nsIDocShellTreeItem> tmp;
+ parentItem->GetParent(getter_AddRefs(tmp));
+ if (!tmp) {
+ break;
+ }
+ parentItem = tmp;
+ }
+
+ if (!parentItem) {
+ parentItem = aParentNode;
+ }
+
+ PrintDocTree(parentItem, 0);
+}
+#endif
+
+NS_IMETHODIMP
+nsDocShell::SetTreeOwner(nsIDocShellTreeOwner* aTreeOwner)
+{
+#ifdef DEBUG_DOCSHELL_FOCUS
+ nsCOMPtr<nsIDocShellTreeItem> item(do_QueryInterface(aTreeOwner));
+ if (item) {
+ PrintDocTree(item);
+ }
+#endif
+
+ // Don't automatically set the progress based on the tree owner for frames
+ if (!IsFrame()) {
+ nsCOMPtr<nsIWebProgress> webProgress =
+ do_QueryInterface(GetAsSupports(this));
+
+ if (webProgress) {
+ nsCOMPtr<nsIWebProgressListener> oldListener =
+ do_QueryInterface(mTreeOwner);
+ nsCOMPtr<nsIWebProgressListener> newListener =
+ do_QueryInterface(aTreeOwner);
+
+ if (oldListener) {
+ webProgress->RemoveProgressListener(oldListener);
+ }
+
+ if (newListener) {
+ webProgress->AddProgressListener(newListener,
+ nsIWebProgress::NOTIFY_ALL);
+ }
+ }
+ }
+
+ mTreeOwner = aTreeOwner; // Weak reference per API
+
+ nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
+ while (iter.HasMore()) {
+ nsCOMPtr<nsIDocShellTreeItem> child = do_QueryObject(iter.GetNext());
+ NS_ENSURE_TRUE(child, NS_ERROR_FAILURE);
+
+ if (child->ItemType() == mItemType) {
+ child->SetTreeOwner(aTreeOwner);
+ }
+ }
+
+ // Our tree owner has changed. Recompute scriptability.
+ //
+ // Note that this is near-redundant with the recomputation in
+ // SetDocLoaderParent(), but not so for the root DocShell, where the call to
+ // SetTreeOwner() happens after the initial AddDocLoaderAsChildOfRoot(),
+ // and we never set another parent. Given that this is neither expensive nor
+ // performance-critical, let's be safe and unconditionally recompute this
+ // state whenever dependent state changes.
+ RecomputeCanExecuteScripts();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetChildOffset(uint32_t aChildOffset)
+{
+ mChildOffset = aChildOffset;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetHistoryID(uint64_t* aID)
+{
+ *aID = mHistoryID;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetIsInUnload(bool* aIsInUnload)
+{
+ *aIsInUnload = mFiredUnloadEvent;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetChildCount(int32_t* aChildCount)
+{
+ NS_ENSURE_ARG_POINTER(aChildCount);
+ *aChildCount = mChildList.Length();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::AddChild(nsIDocShellTreeItem* aChild)
+{
+ NS_ENSURE_ARG_POINTER(aChild);
+
+ RefPtr<nsDocLoader> childAsDocLoader = GetAsDocLoader(aChild);
+ NS_ENSURE_TRUE(childAsDocLoader, NS_ERROR_UNEXPECTED);
+
+ // Make sure we're not creating a loop in the docshell tree
+ nsDocLoader* ancestor = this;
+ do {
+ if (childAsDocLoader == ancestor) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ ancestor = ancestor->GetParent();
+ } while (ancestor);
+
+ // Make sure to remove the child from its current parent.
+ nsDocLoader* childsParent = childAsDocLoader->GetParent();
+ if (childsParent) {
+ nsresult rv = childsParent->RemoveChildLoader(childAsDocLoader);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // Make sure to clear the treeowner in case this child is a different type
+ // from us.
+ aChild->SetTreeOwner(nullptr);
+
+ nsresult res = AddChildLoader(childAsDocLoader);
+ NS_ENSURE_SUCCESS(res, res);
+ NS_ASSERTION(!mChildList.IsEmpty(),
+ "child list must not be empty after a successful add");
+
+ nsCOMPtr<nsIDocShell> childDocShell = do_QueryInterface(aChild);
+ bool dynamic = false;
+ childDocShell->GetCreatedDynamically(&dynamic);
+ if (!dynamic) {
+ nsCOMPtr<nsISHEntry> currentSH;
+ bool oshe = false;
+ GetCurrentSHEntry(getter_AddRefs(currentSH), &oshe);
+ if (currentSH) {
+ currentSH->HasDynamicallyAddedChild(&dynamic);
+ }
+ }
+ childDocShell->SetChildOffset(dynamic ? -1 : mChildList.Length() - 1);
+
+ /* Set the child's global history if the parent has one */
+ if (mUseGlobalHistory) {
+ childDocShell->SetUseGlobalHistory(true);
+ }
+
+ if (aChild->ItemType() != mItemType) {
+ return NS_OK;
+ }
+
+ aChild->SetTreeOwner(mTreeOwner);
+
+ nsCOMPtr<nsIDocShell> childAsDocShell(do_QueryInterface(aChild));
+ if (!childAsDocShell) {
+ return NS_OK;
+ }
+
+ // charset, style-disabling, and zoom will be inherited in SetupNewViewer()
+
+ // Now take this document's charset and set the child's parentCharset field
+ // to it. We'll later use that field, in the loading process, for the
+ // charset choosing algorithm.
+ // If we fail, at any point, we just return NS_OK.
+ // This code has some performance impact. But this will be reduced when
+ // the current charset will finally be stored as an Atom, avoiding the
+ // alias resolution extra look-up.
+
+ // we are NOT going to propagate the charset is this Chrome's docshell
+ if (mItemType == nsIDocShellTreeItem::typeChrome) {
+ return NS_OK;
+ }
+
+ // get the parent's current charset
+ if (!mContentViewer) {
+ return NS_OK;
+ }
+ nsIDocument* doc = mContentViewer->GetDocument();
+ if (!doc) {
+ return NS_OK;
+ }
+
+ bool isWyciwyg = false;
+
+ if (mCurrentURI) {
+ // Check if the url is wyciwyg
+ mCurrentURI->SchemeIs("wyciwyg", &isWyciwyg);
+ }
+
+ if (!isWyciwyg) {
+ // If this docshell is loaded from a wyciwyg: URI, don't
+ // advertise our charset since it does not in any way reflect
+ // the actual source charset, which is what we're trying to
+ // expose here.
+
+ const nsACString& parentCS = doc->GetDocumentCharacterSet();
+ int32_t charsetSource = doc->GetDocumentCharacterSetSource();
+ // set the child's parentCharset
+ childAsDocShell->SetParentCharset(parentCS,
+ charsetSource,
+ doc->NodePrincipal());
+ }
+
+ // printf("### 1 >>> Adding child. Parent CS = %s. ItemType = %d.\n",
+ // NS_LossyConvertUTF16toASCII(parentCS).get(), mItemType);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::RemoveChild(nsIDocShellTreeItem* aChild)
+{
+ NS_ENSURE_ARG_POINTER(aChild);
+
+ RefPtr<nsDocLoader> childAsDocLoader = GetAsDocLoader(aChild);
+ NS_ENSURE_TRUE(childAsDocLoader, NS_ERROR_UNEXPECTED);
+
+ nsresult rv = RemoveChildLoader(childAsDocLoader);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aChild->SetTreeOwner(nullptr);
+
+ return nsDocLoader::AddDocLoaderAsChildOfRoot(childAsDocLoader);
+}
+
+NS_IMETHODIMP
+nsDocShell::GetChildAt(int32_t aIndex, nsIDocShellTreeItem** aChild)
+{
+ NS_ENSURE_ARG_POINTER(aChild);
+
+#ifdef DEBUG
+ if (aIndex < 0) {
+ NS_WARNING("Negative index passed to GetChildAt");
+ } else if (static_cast<uint32_t>(aIndex) >= mChildList.Length()) {
+ NS_WARNING("Too large an index passed to GetChildAt");
+ }
+#endif
+
+ nsIDocumentLoader* child = ChildAt(aIndex);
+ NS_ENSURE_TRUE(child, NS_ERROR_UNEXPECTED);
+
+ return CallQueryInterface(child, aChild);
+}
+
+NS_IMETHODIMP
+nsDocShell::FindChildWithName(const nsAString& aName,
+ bool aRecurse, bool aSameType,
+ nsIDocShellTreeItem* aRequestor,
+ nsIDocShellTreeItem* aOriginalRequestor,
+ nsIDocShellTreeItem** aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ // if we don't find one, we return NS_OK and a null result
+ *aResult = nullptr;
+
+ if (aName.IsEmpty()) {
+ return NS_OK;
+ }
+
+ nsXPIDLString childName;
+ nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
+ while (iter.HasMore()) {
+ nsCOMPtr<nsIDocShellTreeItem> child = do_QueryObject(iter.GetNext());
+ NS_ENSURE_TRUE(child, NS_ERROR_FAILURE);
+ int32_t childType = child->ItemType();
+
+ if (aSameType && (childType != mItemType)) {
+ continue;
+ }
+
+ bool childNameEquals = false;
+ child->NameEquals(aName, &childNameEquals);
+ if (childNameEquals && ItemIsActive(child) &&
+ CanAccessItem(child, aOriginalRequestor)) {
+ child.swap(*aResult);
+ break;
+ }
+
+ // Only ask it to check children if it is same type
+ if (childType != mItemType) {
+ continue;
+ }
+
+ // Only ask the child if it isn't the requestor
+ if (aRecurse && (aRequestor != child)) {
+ // See if child contains the shell with the given name
+#ifdef DEBUG
+ nsresult rv =
+#endif
+ child->FindChildWithName(aName, true, aSameType,
+ static_cast<nsIDocShellTreeItem*>(this),
+ aOriginalRequestor, aResult);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "FindChildWithName should not fail here");
+ if (*aResult) {
+ // found it
+ return NS_OK;
+ }
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetChildSHEntry(int32_t aChildOffset, nsISHEntry** aResult)
+{
+ nsresult rv = NS_OK;
+
+ NS_ENSURE_ARG_POINTER(aResult);
+ *aResult = nullptr;
+
+ // A nsISHEntry for a child is *only* available when the parent is in
+ // the progress of loading a document too...
+
+ if (mLSHE) {
+ /* Before looking for the subframe's url, check
+ * the expiration status of the parent. If the parent
+ * has expired from cache, then subframes will not be
+ * loaded from history in certain situations.
+ */
+ bool parentExpired = false;
+ mLSHE->GetExpirationStatus(&parentExpired);
+
+ /* Get the parent's Load Type so that it can be set on the child too.
+ * By default give a loadHistory value
+ */
+ uint32_t loadType = nsIDocShellLoadInfo::loadHistory;
+ mLSHE->GetLoadType(&loadType);
+ // If the user did a shift-reload on this frameset page,
+ // we don't want to load the subframes from history.
+ if (loadType == nsIDocShellLoadInfo::loadReloadBypassCache ||
+ loadType == nsIDocShellLoadInfo::loadReloadBypassProxy ||
+ loadType == nsIDocShellLoadInfo::loadReloadBypassProxyAndCache ||
+ loadType == nsIDocShellLoadInfo::loadRefresh) {
+ return rv;
+ }
+
+ /* If the user pressed reload and the parent frame has expired
+ * from cache, we do not want to load the child frame from history.
+ */
+ if (parentExpired && (loadType == nsIDocShellLoadInfo::loadReloadNormal)) {
+ // The parent has expired. Return null.
+ *aResult = nullptr;
+ return rv;
+ }
+
+ nsCOMPtr<nsISHContainer> container(do_QueryInterface(mLSHE));
+ if (container) {
+ // Get the child subframe from session history.
+ rv = container->GetChildAt(aChildOffset, aResult);
+ if (*aResult) {
+ (*aResult)->SetLoadType(loadType);
+ }
+ }
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDocShell::AddChildSHEntry(nsISHEntry* aCloneRef, nsISHEntry* aNewEntry,
+ int32_t aChildOffset, uint32_t aLoadType,
+ bool aCloneChildren)
+{
+ nsresult rv = NS_OK;
+
+ if (mLSHE && aLoadType != LOAD_PUSHSTATE) {
+ /* You get here if you are currently building a
+ * hierarchy ie.,you just visited a frameset page
+ */
+ nsCOMPtr<nsISHContainer> container(do_QueryInterface(mLSHE, &rv));
+ if (container) {
+ if (NS_FAILED(container->ReplaceChild(aNewEntry))) {
+ rv = container->AddChild(aNewEntry, aChildOffset);
+ }
+ }
+ } else if (!aCloneRef) {
+ /* This is an initial load in some subframe. Just append it if we can */
+ nsCOMPtr<nsISHContainer> container(do_QueryInterface(mOSHE, &rv));
+ if (container) {
+ rv = container->AddChild(aNewEntry, aChildOffset);
+ }
+ } else {
+ rv = AddChildSHEntryInternal(aCloneRef, aNewEntry, aChildOffset,
+ aLoadType, aCloneChildren);
+ }
+ return rv;
+}
+
+nsresult
+nsDocShell::AddChildSHEntryInternal(nsISHEntry* aCloneRef,
+ nsISHEntry* aNewEntry,
+ int32_t aChildOffset,
+ uint32_t aLoadType,
+ bool aCloneChildren)
+{
+ nsresult rv = NS_OK;
+ if (mSessionHistory) {
+ /* You are currently in the rootDocShell.
+ * You will get here when a subframe has a new url
+ * to load and you have walked up the tree all the
+ * way to the top to clone the current SHEntry hierarchy
+ * and replace the subframe where a new url was loaded with
+ * a new entry.
+ */
+ int32_t index = -1;
+ nsCOMPtr<nsISHEntry> currentHE;
+ mSessionHistory->GetIndex(&index);
+ if (index < 0) {
+ return NS_ERROR_FAILURE;
+ }
+
+ rv = mSessionHistory->GetEntryAtIndex(index, false,
+ getter_AddRefs(currentHE));
+ NS_ENSURE_TRUE(currentHE, NS_ERROR_FAILURE);
+
+ nsCOMPtr<nsISHEntry> currentEntry(do_QueryInterface(currentHE));
+ if (currentEntry) {
+ uint32_t cloneID = 0;
+ nsCOMPtr<nsISHEntry> nextEntry;
+ aCloneRef->GetID(&cloneID);
+ rv = CloneAndReplace(currentEntry, this, cloneID, aNewEntry,
+ aCloneChildren, getter_AddRefs(nextEntry));
+
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsISHistoryInternal> shPrivate =
+ do_QueryInterface(mSessionHistory);
+ NS_ENSURE_TRUE(shPrivate, NS_ERROR_FAILURE);
+ rv = shPrivate->AddEntry(nextEntry, true);
+ }
+ }
+ } else {
+ /* Just pass this along */
+ nsCOMPtr<nsIDocShell> parent =
+ do_QueryInterface(GetAsSupports(mParent), &rv);
+ if (parent) {
+ rv = static_cast<nsDocShell*>(parent.get())->AddChildSHEntryInternal(
+ aCloneRef, aNewEntry, aChildOffset, aLoadType, aCloneChildren);
+ }
+ }
+ return rv;
+}
+
+nsresult
+nsDocShell::AddChildSHEntryToParent(nsISHEntry* aNewEntry, int32_t aChildOffset,
+ bool aCloneChildren)
+{
+ /* You will get here when you are in a subframe and
+ * a new url has been loaded on you.
+ * The mOSHE in this subframe will be the previous url's
+ * mOSHE. This mOSHE will be used as the identification
+ * for this subframe in the CloneAndReplace function.
+ */
+
+ // In this case, we will end up calling AddEntry, which increases the
+ // current index by 1
+ nsCOMPtr<nsISHistory> rootSH;
+ GetRootSessionHistory(getter_AddRefs(rootSH));
+ if (rootSH) {
+ rootSH->GetIndex(&mPreviousTransIndex);
+ }
+
+ nsresult rv;
+ nsCOMPtr<nsIDocShell> parent = do_QueryInterface(GetAsSupports(mParent), &rv);
+ if (parent) {
+ rv = parent->AddChildSHEntry(mOSHE, aNewEntry, aChildOffset, mLoadType,
+ aCloneChildren);
+ }
+
+ if (rootSH) {
+ rootSH->GetIndex(&mLoadedTransIndex);
+#ifdef DEBUG_PAGE_CACHE
+ printf("Previous index: %d, Loaded index: %d\n\n", mPreviousTransIndex,
+ mLoadedTransIndex);
+#endif
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetUseGlobalHistory(bool aUseGlobalHistory)
+{
+ nsresult rv;
+
+ mUseGlobalHistory = aUseGlobalHistory;
+
+ if (!aUseGlobalHistory) {
+ mGlobalHistory = nullptr;
+ return NS_OK;
+ }
+
+ // No need to initialize mGlobalHistory if IHistory is available.
+ nsCOMPtr<IHistory> history = services::GetHistoryService();
+ if (history) {
+ return NS_OK;
+ }
+
+ if (mGlobalHistory) {
+ return NS_OK;
+ }
+
+ mGlobalHistory = do_GetService(NS_GLOBALHISTORY2_CONTRACTID, &rv);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetUseGlobalHistory(bool* aUseGlobalHistory)
+{
+ *aUseGlobalHistory = mUseGlobalHistory;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::RemoveFromSessionHistory()
+{
+ nsCOMPtr<nsISHistoryInternal> internalHistory;
+ nsCOMPtr<nsISHistory> sessionHistory;
+ nsCOMPtr<nsIDocShellTreeItem> root;
+ GetSameTypeRootTreeItem(getter_AddRefs(root));
+ if (root) {
+ nsCOMPtr<nsIWebNavigation> rootAsWebnav = do_QueryInterface(root);
+ if (rootAsWebnav) {
+ rootAsWebnav->GetSessionHistory(getter_AddRefs(sessionHistory));
+ internalHistory = do_QueryInterface(sessionHistory);
+ }
+ }
+ if (!internalHistory) {
+ return NS_OK;
+ }
+
+ int32_t index = 0;
+ sessionHistory->GetIndex(&index);
+ AutoTArray<uint64_t, 16> ids({mHistoryID});
+ internalHistory->RemoveEntries(ids, index);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetCreatedDynamically(bool aDynamic)
+{
+ mDynamicallyCreated = aDynamic;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetCreatedDynamically(bool* aDynamic)
+{
+ *aDynamic = mDynamicallyCreated;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetCurrentSHEntry(nsISHEntry** aEntry, bool* aOSHE)
+{
+ *aOSHE = false;
+ *aEntry = nullptr;
+ if (mLSHE) {
+ NS_ADDREF(*aEntry = mLSHE);
+ } else if (mOSHE) {
+ NS_ADDREF(*aEntry = mOSHE);
+ *aOSHE = true;
+ }
+ return NS_OK;
+}
+
+nsIScriptGlobalObject*
+nsDocShell::GetScriptGlobalObject()
+{
+ NS_ENSURE_SUCCESS(EnsureScriptEnvironment(), nullptr);
+ return mScriptGlobal;
+}
+
+nsIDocument*
+nsDocShell::GetDocument()
+{
+ NS_ENSURE_SUCCESS(EnsureContentViewer(), nullptr);
+ return mContentViewer->GetDocument();
+}
+
+nsPIDOMWindowOuter*
+nsDocShell::GetWindow()
+{
+ if (NS_FAILED(EnsureScriptEnvironment())) {
+ return nullptr;
+ }
+ return mScriptGlobal->AsOuter();
+}
+
+NS_IMETHODIMP
+nsDocShell::SetDeviceSizeIsPageSize(bool aValue)
+{
+ if (mDeviceSizeIsPageSize != aValue) {
+ mDeviceSizeIsPageSize = aValue;
+ RefPtr<nsPresContext> presContext;
+ GetPresContext(getter_AddRefs(presContext));
+ if (presContext) {
+ presContext->MediaFeatureValuesChanged(nsRestyleHint(0));
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetDeviceSizeIsPageSize(bool* aValue)
+{
+ *aValue = mDeviceSizeIsPageSize;
+ return NS_OK;
+}
+
+void
+nsDocShell::ClearFrameHistory(nsISHEntry* aEntry)
+{
+ nsCOMPtr<nsISHContainer> shcontainer = do_QueryInterface(aEntry);
+ nsCOMPtr<nsISHistory> rootSH;
+ GetRootSessionHistory(getter_AddRefs(rootSH));
+ nsCOMPtr<nsISHistoryInternal> history = do_QueryInterface(rootSH);
+ if (!history || !shcontainer) {
+ return;
+ }
+
+ int32_t count = 0;
+ shcontainer->GetChildCount(&count);
+ AutoTArray<uint64_t, 16> ids;
+ for (int32_t i = 0; i < count; ++i) {
+ nsCOMPtr<nsISHEntry> child;
+ shcontainer->GetChildAt(i, getter_AddRefs(child));
+ if (child) {
+ uint64_t id = 0;
+ child->GetDocshellID(&id);
+ ids.AppendElement(id);
+ }
+ }
+ int32_t index = 0;
+ rootSH->GetIndex(&index);
+ history->RemoveEntries(ids, index);
+}
+
+//-------------------------------------
+//-- Helper Method for Print discovery
+//-------------------------------------
+bool
+nsDocShell::IsPrintingOrPP(bool aDisplayErrorDialog)
+{
+ if (mIsPrintingOrPP && aDisplayErrorDialog) {
+ DisplayLoadError(NS_ERROR_DOCUMENT_IS_PRINTMODE, nullptr, nullptr, nullptr);
+ }
+
+ return mIsPrintingOrPP;
+}
+
+bool
+nsDocShell::IsNavigationAllowed(bool aDisplayPrintErrorDialog,
+ bool aCheckIfUnloadFired)
+{
+ bool isAllowed = !IsPrintingOrPP(aDisplayPrintErrorDialog) &&
+ (!aCheckIfUnloadFired || !mFiredUnloadEvent);
+ if (!isAllowed) {
+ return false;
+ }
+ if (!mContentViewer) {
+ return true;
+ }
+ bool firingBeforeUnload;
+ mContentViewer->GetBeforeUnloadFiring(&firingBeforeUnload);
+ return !firingBeforeUnload;
+}
+
+//*****************************************************************************
+// nsDocShell::nsIWebNavigation
+//*****************************************************************************
+
+NS_IMETHODIMP
+nsDocShell::GetCanGoBack(bool* aCanGoBack)
+{
+ if (!IsNavigationAllowed(false)) {
+ *aCanGoBack = false;
+ return NS_OK; // JS may not handle returning of an error code
+ }
+ nsresult rv;
+ nsCOMPtr<nsISHistory> rootSH;
+ rv = GetRootSessionHistory(getter_AddRefs(rootSH));
+ nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
+ NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
+ rv = webnav->GetCanGoBack(aCanGoBack);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetCanGoForward(bool* aCanGoForward)
+{
+ if (!IsNavigationAllowed(false)) {
+ *aCanGoForward = false;
+ return NS_OK; // JS may not handle returning of an error code
+ }
+ nsresult rv;
+ nsCOMPtr<nsISHistory> rootSH;
+ rv = GetRootSessionHistory(getter_AddRefs(rootSH));
+ nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
+ NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
+ rv = webnav->GetCanGoForward(aCanGoForward);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDocShell::GoBack()
+{
+ if (!IsNavigationAllowed()) {
+ return NS_OK; // JS may not handle returning of an error code
+ }
+ nsresult rv;
+ nsCOMPtr<nsISHistory> rootSH;
+ rv = GetRootSessionHistory(getter_AddRefs(rootSH));
+ nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
+ NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
+ rv = webnav->GoBack();
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDocShell::GoForward()
+{
+ if (!IsNavigationAllowed()) {
+ return NS_OK; // JS may not handle returning of an error code
+ }
+ nsresult rv;
+ nsCOMPtr<nsISHistory> rootSH;
+ rv = GetRootSessionHistory(getter_AddRefs(rootSH));
+ nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
+ NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
+ rv = webnav->GoForward();
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDocShell::GotoIndex(int32_t aIndex)
+{
+ if (!IsNavigationAllowed()) {
+ return NS_OK; // JS may not handle returning of an error code
+ }
+ nsresult rv;
+ nsCOMPtr<nsISHistory> rootSH;
+ rv = GetRootSessionHistory(getter_AddRefs(rootSH));
+ nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
+ NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
+ rv = webnav->GotoIndex(aIndex);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDocShell::LoadURI(const char16_t* aURI,
+ uint32_t aLoadFlags,
+ nsIURI* aReferringURI,
+ nsIInputStream* aPostStream,
+ nsIInputStream* aHeaderStream)
+{
+ return LoadURIWithOptions(aURI, aLoadFlags, aReferringURI,
+ mozilla::net::RP_Default, aPostStream,
+ aHeaderStream, nullptr);
+}
+
+NS_IMETHODIMP
+nsDocShell::LoadURIWithOptions(const char16_t* aURI,
+ uint32_t aLoadFlags,
+ nsIURI* aReferringURI,
+ uint32_t aReferrerPolicy,
+ nsIInputStream* aPostStream,
+ nsIInputStream* aHeaderStream,
+ nsIURI* aBaseURI)
+{
+ NS_ASSERTION((aLoadFlags & 0xf) == 0, "Unexpected flags");
+
+ if (!IsNavigationAllowed()) {
+ return NS_OK; // JS may not handle returning of an error code
+ }
+ nsCOMPtr<nsIURI> uri;
+ nsCOMPtr<nsIInputStream> postStream(aPostStream);
+ nsresult rv = NS_OK;
+
+ // Create a URI from our string; if that succeeds, we want to
+ // change aLoadFlags to not include the ALLOW_THIRD_PARTY_FIXUP
+ // flag.
+
+ NS_ConvertUTF16toUTF8 uriString(aURI);
+ // Cleanup the empty spaces that might be on each end.
+ uriString.Trim(" ");
+ // Eliminate embedded newlines, which single-line text fields now allow:
+ uriString.StripChars("\r\n");
+ NS_ENSURE_TRUE(!uriString.IsEmpty(), NS_ERROR_FAILURE);
+
+ rv = NS_NewURI(getter_AddRefs(uri), uriString);
+ if (uri) {
+ aLoadFlags &= ~LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
+ }
+
+ nsCOMPtr<nsIURIFixupInfo> fixupInfo;
+ if (sURIFixup) {
+ // Call the fixup object. This will clobber the rv from NS_NewURI
+ // above, but that's fine with us. Note that we need to do this even
+ // if NS_NewURI returned a URI, because fixup handles nested URIs, etc
+ // (things like view-source:mozilla.org for example).
+ uint32_t fixupFlags = 0;
+ if (aLoadFlags & LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) {
+ fixupFlags |= nsIURIFixup::FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP;
+ }
+ if (aLoadFlags & LOAD_FLAGS_FIXUP_SCHEME_TYPOS) {
+ fixupFlags |= nsIURIFixup::FIXUP_FLAG_FIX_SCHEME_TYPOS;
+ }
+ nsCOMPtr<nsIInputStream> fixupStream;
+ rv = sURIFixup->GetFixupURIInfo(uriString, fixupFlags,
+ getter_AddRefs(fixupStream),
+ getter_AddRefs(fixupInfo));
+
+ if (NS_SUCCEEDED(rv)) {
+ fixupInfo->GetPreferredURI(getter_AddRefs(uri));
+ fixupInfo->SetConsumer(GetAsSupports(this));
+ }
+
+ if (fixupStream) {
+ // GetFixupURIInfo only returns a post data stream if it succeeded
+ // and changed the URI, in which case we should override the
+ // passed-in post data.
+ postStream = fixupStream;
+ }
+
+ if (aLoadFlags & LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) {
+ nsCOMPtr<nsIObserverService> serv = services::GetObserverService();
+ if (serv) {
+ serv->NotifyObservers(fixupInfo, "keyword-uri-fixup", aURI);
+ }
+ }
+ }
+ // else no fixup service so just use the URI we created and see
+ // what happens
+
+ if (NS_ERROR_MALFORMED_URI == rv) {
+ if (DisplayLoadError(rv, uri, aURI, nullptr) &&
+ (aLoadFlags & LOAD_FLAGS_ERROR_LOAD_CHANGES_RV) != 0) {
+ return NS_ERROR_LOAD_SHOWED_ERRORPAGE;
+ }
+ }
+
+ if (NS_FAILED(rv) || !uri) {
+ return NS_ERROR_FAILURE;
+ }
+
+ PopupControlState popupState;
+ if (aLoadFlags & LOAD_FLAGS_ALLOW_POPUPS) {
+ popupState = openAllowed;
+ aLoadFlags &= ~LOAD_FLAGS_ALLOW_POPUPS;
+ } else {
+ popupState = openOverridden;
+ }
+ nsAutoPopupStatePusher statePusher(popupState);
+
+ // Don't pass certain flags that aren't needed and end up confusing
+ // ConvertLoadTypeToDocShellLoadInfo. We do need to ensure that they are
+ // passed to LoadURI though, since it uses them.
+ uint32_t extraFlags = (aLoadFlags & EXTRA_LOAD_FLAGS);
+ aLoadFlags &= ~EXTRA_LOAD_FLAGS;
+
+ nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
+ rv = CreateLoadInfo(getter_AddRefs(loadInfo));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ /*
+ * If the user "Disables Protection on This Page", we have to make sure to
+ * remember the users decision when opening links in child tabs [Bug 906190]
+ */
+ uint32_t loadType;
+ if (aLoadFlags & LOAD_FLAGS_ALLOW_MIXED_CONTENT) {
+ loadType = MAKE_LOAD_TYPE(LOAD_NORMAL_ALLOW_MIXED_CONTENT, aLoadFlags);
+ } else {
+ loadType = MAKE_LOAD_TYPE(LOAD_NORMAL, aLoadFlags);
+ }
+
+ loadInfo->SetLoadType(ConvertLoadTypeToDocShellLoadInfo(loadType));
+ loadInfo->SetPostDataStream(postStream);
+ loadInfo->SetReferrer(aReferringURI);
+ loadInfo->SetReferrerPolicy(aReferrerPolicy);
+ loadInfo->SetHeadersStream(aHeaderStream);
+ loadInfo->SetBaseURI(aBaseURI);
+
+ if (fixupInfo) {
+ nsAutoString searchProvider, keyword;
+ fixupInfo->GetKeywordProviderName(searchProvider);
+ fixupInfo->GetKeywordAsSent(keyword);
+ MaybeNotifyKeywordSearchLoading(searchProvider, keyword);
+ }
+
+ rv = LoadURI(uri, loadInfo, extraFlags, true);
+
+ // Save URI string in case it's needed later when
+ // sending to search engine service in EndPageLoad()
+ mOriginalUriString = uriString;
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDocShell::DisplayLoadError(nsresult aError, nsIURI* aURI,
+ const char16_t* aURL,
+ nsIChannel* aFailedChannel,
+ bool* aDisplayedErrorPage)
+{
+ *aDisplayedErrorPage = false;
+ // Get prompt and string bundle servcies
+ nsCOMPtr<nsIPrompt> prompter;
+ nsCOMPtr<nsIStringBundle> stringBundle;
+ GetPromptAndStringBundle(getter_AddRefs(prompter),
+ getter_AddRefs(stringBundle));
+
+ NS_ENSURE_TRUE(stringBundle, NS_ERROR_FAILURE);
+ NS_ENSURE_TRUE(prompter, NS_ERROR_FAILURE);
+
+ nsAutoString error;
+ const uint32_t kMaxFormatStrArgs = 3;
+ nsAutoString formatStrs[kMaxFormatStrArgs];
+ uint32_t formatStrCount = 0;
+ bool addHostPort = false;
+ nsresult rv = NS_OK;
+ nsAutoString messageStr;
+ nsAutoCString cssClass;
+ nsAutoCString errorPage;
+
+ errorPage.AssignLiteral("neterror");
+
+ // Turn the error code into a human readable error message.
+ if (NS_ERROR_UNKNOWN_PROTOCOL == aError) {
+ NS_ENSURE_ARG_POINTER(aURI);
+
+ // Extract the schemes into a comma delimited list.
+ nsAutoCString scheme;
+ aURI->GetScheme(scheme);
+ CopyASCIItoUTF16(scheme, formatStrs[0]);
+ nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(aURI);
+ while (nestedURI) {
+ nsCOMPtr<nsIURI> tempURI;
+ nsresult rv2;
+ rv2 = nestedURI->GetInnerURI(getter_AddRefs(tempURI));
+ if (NS_SUCCEEDED(rv2) && tempURI) {
+ tempURI->GetScheme(scheme);
+ formatStrs[0].AppendLiteral(", ");
+ AppendASCIItoUTF16(scheme, formatStrs[0]);
+ }
+ nestedURI = do_QueryInterface(tempURI);
+ }
+ formatStrCount = 1;
+ error.AssignLiteral("unknownProtocolFound");
+ } else if (NS_ERROR_FILE_NOT_FOUND == aError) {
+ NS_ENSURE_ARG_POINTER(aURI);
+ error.AssignLiteral("fileNotFound");
+ } else if (NS_ERROR_FILE_ACCESS_DENIED == aError) {
+ NS_ENSURE_ARG_POINTER(aURI);
+ error.AssignLiteral("fileAccessDenied");
+ } else if (NS_ERROR_UNKNOWN_HOST == aError) {
+ NS_ENSURE_ARG_POINTER(aURI);
+ // Get the host
+ nsAutoCString host;
+ nsCOMPtr<nsIURI> innermostURI = NS_GetInnermostURI(aURI);
+ innermostURI->GetHost(host);
+ CopyUTF8toUTF16(host, formatStrs[0]);
+ formatStrCount = 1;
+ error.AssignLiteral("dnsNotFound");
+ } else if (NS_ERROR_CONNECTION_REFUSED == aError) {
+ NS_ENSURE_ARG_POINTER(aURI);
+ addHostPort = true;
+ error.AssignLiteral("connectionFailure");
+ } else if (NS_ERROR_NET_INTERRUPT == aError) {
+ NS_ENSURE_ARG_POINTER(aURI);
+ addHostPort = true;
+ error.AssignLiteral("netInterrupt");
+ } else if (NS_ERROR_NET_TIMEOUT == aError) {
+ NS_ENSURE_ARG_POINTER(aURI);
+ // Get the host
+ nsAutoCString host;
+ aURI->GetHost(host);
+ CopyUTF8toUTF16(host, formatStrs[0]);
+ formatStrCount = 1;
+ error.AssignLiteral("netTimeout");
+ } else if (NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION == aError ||
+ NS_ERROR_CSP_FORM_ACTION_VIOLATION == aError) {
+ // CSP error
+ cssClass.AssignLiteral("neterror");
+ error.AssignLiteral("cspBlocked");
+ } else if (NS_ERROR_GET_MODULE(aError) == NS_ERROR_MODULE_SECURITY) {
+ nsCOMPtr<nsINSSErrorsService> nsserr =
+ do_GetService(NS_NSS_ERRORS_SERVICE_CONTRACTID);
+
+ uint32_t errorClass;
+ if (!nsserr || NS_FAILED(nsserr->GetErrorClass(aError, &errorClass))) {
+ errorClass = nsINSSErrorsService::ERROR_CLASS_SSL_PROTOCOL;
+ }
+
+ nsCOMPtr<nsISupports> securityInfo;
+ nsCOMPtr<nsITransportSecurityInfo> tsi;
+ if (aFailedChannel) {
+ aFailedChannel->GetSecurityInfo(getter_AddRefs(securityInfo));
+ }
+ tsi = do_QueryInterface(securityInfo);
+ if (tsi) {
+ uint32_t securityState;
+ tsi->GetSecurityState(&securityState);
+ if (securityState & nsIWebProgressListener::STATE_USES_SSL_3) {
+ error.AssignLiteral("sslv3Used");
+ addHostPort = true;
+ } else if (securityState & nsIWebProgressListener::STATE_USES_WEAK_CRYPTO) {
+ error.AssignLiteral("weakCryptoUsed");
+ addHostPort = true;
+ } else {
+ // Usually we should have aFailedChannel and get a detailed message
+ tsi->GetErrorMessage(getter_Copies(messageStr));
+ }
+ } else {
+ // No channel, let's obtain the generic error message
+ if (nsserr) {
+ nsserr->GetErrorMessage(aError, messageStr);
+ }
+ }
+ if (!messageStr.IsEmpty()) {
+ if (errorClass == nsINSSErrorsService::ERROR_CLASS_BAD_CERT) {
+ error.AssignLiteral("nssBadCert");
+
+ // If this is an HTTP Strict Transport Security host or a pinned host
+ // and the certificate is bad, don't allow overrides (RFC 6797 section
+ // 12.1, HPKP draft spec section 2.6).
+ uint32_t flags =
+ UsePrivateBrowsing() ? nsISocketProvider::NO_PERMANENT_STORAGE : 0;
+ bool isStsHost = false;
+ bool isPinnedHost = false;
+ if (XRE_IsParentProcess()) {
+ nsCOMPtr<nsISiteSecurityService> sss =
+ do_GetService(NS_SSSERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, aURI,
+ flags, nullptr, &isStsHost);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HPKP, aURI,
+ flags, nullptr, &isPinnedHost);
+ NS_ENSURE_SUCCESS(rv, rv);
+ } else {
+ mozilla::dom::ContentChild* cc =
+ mozilla::dom::ContentChild::GetSingleton();
+ mozilla::ipc::URIParams uri;
+ SerializeURI(aURI, uri);
+ cc->SendIsSecureURI(nsISiteSecurityService::HEADER_HSTS, uri, flags,
+ &isStsHost);
+ cc->SendIsSecureURI(nsISiteSecurityService::HEADER_HPKP, uri, flags,
+ &isPinnedHost);
+ }
+
+ if (Preferences::GetBool(
+ "browser.xul.error_pages.expert_bad_cert", false)) {
+ cssClass.AssignLiteral("expertBadCert");
+ }
+
+ // HSTS/pinning takes precedence over the expert bad cert pref. We
+ // never want to show the "Add Exception" button for these sites.
+ // In the future we should differentiate between an HSTS host and a
+ // pinned host and display a more informative message to the user.
+ if (isStsHost || isPinnedHost) {
+ cssClass.AssignLiteral("badStsCert");
+ }
+
+ uint32_t bucketId;
+ if (isStsHost) {
+ // measuring STS separately allows us to measure click through
+ // rates easily
+ bucketId = nsISecurityUITelemetry::WARNING_BAD_CERT_TOP_STS;
+ } else {
+ bucketId = nsISecurityUITelemetry::WARNING_BAD_CERT_TOP;
+ }
+
+ // See if an alternate cert error page is registered
+ nsAdoptingCString alternateErrorPage =
+ Preferences::GetCString("security.alternate_certificate_error_page");
+ if (alternateErrorPage) {
+ errorPage.Assign(alternateErrorPage);
+ }
+
+ if (!IsFrame() && errorPage.EqualsIgnoreCase("certerror")) {
+ Telemetry::Accumulate(mozilla::Telemetry::SECURITY_UI, bucketId);
+ }
+
+ } else {
+ error.AssignLiteral("nssFailure2");
+ }
+ }
+ } else if (NS_ERROR_PHISHING_URI == aError ||
+ NS_ERROR_MALWARE_URI == aError ||
+ NS_ERROR_UNWANTED_URI == aError) {
+ nsAutoCString host;
+ aURI->GetHost(host);
+ CopyUTF8toUTF16(host, formatStrs[0]);
+ formatStrCount = 1;
+
+ // Malware and phishing detectors may want to use an alternate error
+ // page, but if the pref's not set, we'll fall back on the standard page
+ nsAdoptingCString alternateErrorPage =
+ Preferences::GetCString("urlclassifier.alternate_error_page");
+ if (alternateErrorPage) {
+ errorPage.Assign(alternateErrorPage);
+ }
+
+ uint32_t bucketId;
+ bool sendTelemetry = false;
+ if (NS_ERROR_PHISHING_URI == aError) {
+ sendTelemetry = true;
+ error.AssignLiteral("deceptiveBlocked");
+ bucketId = IsFrame() ? nsISecurityUITelemetry::WARNING_PHISHING_PAGE_FRAME
+ : nsISecurityUITelemetry::WARNING_PHISHING_PAGE_TOP;
+ } else if (NS_ERROR_MALWARE_URI == aError) {
+ sendTelemetry = true;
+ error.AssignLiteral("malwareBlocked");
+ bucketId = IsFrame() ? nsISecurityUITelemetry::WARNING_MALWARE_PAGE_FRAME
+ : nsISecurityUITelemetry::WARNING_MALWARE_PAGE_TOP;
+ } else if (NS_ERROR_UNWANTED_URI == aError) {
+ sendTelemetry = true;
+ error.AssignLiteral("unwantedBlocked");
+ bucketId = IsFrame() ? nsISecurityUITelemetry::WARNING_UNWANTED_PAGE_FRAME
+ : nsISecurityUITelemetry::WARNING_UNWANTED_PAGE_TOP;
+ }
+
+ if (sendTelemetry && errorPage.EqualsIgnoreCase("blocked")) {
+ Telemetry::Accumulate(Telemetry::SECURITY_UI, bucketId);
+ }
+
+ cssClass.AssignLiteral("blacklist");
+ } else if (NS_ERROR_CONTENT_CRASHED == aError) {
+ errorPage.AssignLiteral("tabcrashed");
+ error.AssignLiteral("tabcrashed");
+
+ nsCOMPtr<EventTarget> handler = mChromeEventHandler;
+ if (handler) {
+ nsCOMPtr<Element> element = do_QueryInterface(handler);
+ element->GetAttribute(NS_LITERAL_STRING("crashedPageTitle"), messageStr);
+ }
+
+ // DisplayLoadError requires a non-empty messageStr to proceed and call
+ // LoadErrorPage. If the page doesn't have a title, we will use a blank
+ // space which will be trimmed and thus treated as empty by the front-end.
+ if (messageStr.IsEmpty()) {
+ messageStr.AssignLiteral(u" ");
+ }
+ } else {
+ // Errors requiring simple formatting
+ switch (aError) {
+ case NS_ERROR_MALFORMED_URI:
+ // URI is malformed
+ error.AssignLiteral("malformedURI");
+ break;
+ case NS_ERROR_REDIRECT_LOOP:
+ // Doc failed to load because the server generated too many redirects
+ error.AssignLiteral("redirectLoop");
+ break;
+ case NS_ERROR_UNKNOWN_SOCKET_TYPE:
+ // Doc failed to load because PSM is not installed
+ error.AssignLiteral("unknownSocketType");
+ break;
+ case NS_ERROR_NET_RESET:
+ // Doc failed to load because the server kept reseting the connection
+ // before we could read any data from it
+ error.AssignLiteral("netReset");
+ break;
+ case NS_ERROR_DOCUMENT_NOT_CACHED:
+ // Doc failed to load because the cache does not contain a copy of
+ // the document.
+ error.AssignLiteral("notCached");
+ break;
+ case NS_ERROR_OFFLINE:
+ // Doc failed to load because we are offline.
+ error.AssignLiteral("netOffline");
+ break;
+ case NS_ERROR_DOCUMENT_IS_PRINTMODE:
+ // Doc navigation attempted while Printing or Print Preview
+ error.AssignLiteral("isprinting");
+ break;
+ case NS_ERROR_PORT_ACCESS_NOT_ALLOWED:
+ // Port blocked for security reasons
+ addHostPort = true;
+ error.AssignLiteral("deniedPortAccess");
+ break;
+ case NS_ERROR_UNKNOWN_PROXY_HOST:
+ // Proxy hostname could not be resolved.
+ error.AssignLiteral("proxyResolveFailure");
+ break;
+ case NS_ERROR_PROXY_CONNECTION_REFUSED:
+ // Proxy connection was refused.
+ error.AssignLiteral("proxyConnectFailure");
+ break;
+ case NS_ERROR_INVALID_CONTENT_ENCODING:
+ // Bad Content Encoding.
+ error.AssignLiteral("contentEncodingError");
+ break;
+ case NS_ERROR_REMOTE_XUL:
+ error.AssignLiteral("remoteXUL");
+ break;
+ case NS_ERROR_UNSAFE_CONTENT_TYPE:
+ // Channel refused to load from an unrecognized content type.
+ error.AssignLiteral("unsafeContentType");
+ break;
+ case NS_ERROR_CORRUPTED_CONTENT:
+ // Broken Content Detected. e.g. Content-MD5 check failure.
+ error.AssignLiteral("corruptedContentErrorv2");
+ break;
+ case NS_ERROR_INTERCEPTION_FAILED:
+ // ServiceWorker intercepted request, but something went wrong.
+ error.AssignLiteral("corruptedContentErrorv2");
+ break;
+ case NS_ERROR_NET_INADEQUATE_SECURITY:
+ // Server negotiated bad TLS for HTTP/2.
+ error.AssignLiteral("inadequateSecurityError");
+ addHostPort = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ // Test if the error should be displayed
+ if (error.IsEmpty()) {
+ return NS_OK;
+ }
+
+ // Test if the error needs to be formatted
+ if (!messageStr.IsEmpty()) {
+ // already obtained message
+ } else {
+ if (addHostPort) {
+ // Build up the host:port string.
+ nsAutoCString hostport;
+ if (aURI) {
+ aURI->GetHostPort(hostport);
+ } else {
+ hostport.Assign('?');
+ }
+ CopyUTF8toUTF16(hostport, formatStrs[formatStrCount++]);
+ }
+
+ nsAutoCString spec;
+ rv = NS_ERROR_NOT_AVAILABLE;
+ if (aURI) {
+ // displaying "file://" is aesthetically unpleasing and could even be
+ // confusing to the user
+ bool isFileURI = false;
+ rv = aURI->SchemeIs("file", &isFileURI);
+ if (NS_SUCCEEDED(rv) && isFileURI) {
+ aURI->GetPath(spec);
+ } else {
+ aURI->GetSpec(spec);
+ }
+
+ nsAutoCString charset;
+ // unescape and convert from origin charset
+ aURI->GetOriginCharset(charset);
+ nsCOMPtr<nsITextToSubURI> textToSubURI(
+ do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv));
+ if (NS_SUCCEEDED(rv)) {
+ rv = textToSubURI->UnEscapeURIForUI(charset, spec,
+ formatStrs[formatStrCount]);
+ }
+ } else {
+ spec.Assign('?');
+ }
+ if (NS_FAILED(rv)) {
+ CopyUTF8toUTF16(spec, formatStrs[formatStrCount]);
+ }
+ rv = NS_OK;
+ ++formatStrCount;
+
+ const char16_t* strs[kMaxFormatStrArgs];
+ for (uint32_t i = 0; i < formatStrCount; i++) {
+ strs[i] = formatStrs[i].get();
+ }
+ nsXPIDLString str;
+ rv = stringBundle->FormatStringFromName(error.get(), strs, formatStrCount,
+ getter_Copies(str));
+ NS_ENSURE_SUCCESS(rv, rv);
+ messageStr.Assign(str.get());
+ }
+
+ // Display the error as a page or an alert prompt
+ NS_ENSURE_FALSE(messageStr.IsEmpty(), NS_ERROR_FAILURE);
+
+ if (NS_ERROR_NET_INTERRUPT == aError || NS_ERROR_NET_RESET == aError) {
+ bool isSecureURI = false;
+ rv = aURI->SchemeIs("https", &isSecureURI);
+ if (NS_SUCCEEDED(rv) && isSecureURI) {
+ // Maybe TLS intolerant. Treat this as an SSL error.
+ error.AssignLiteral("nssFailure2");
+ }
+ }
+
+ if (UseErrorPages()) {
+ // Display an error page
+ nsresult loadedPage = LoadErrorPage(aURI, aURL, errorPage.get(),
+ error.get(), messageStr.get(),
+ cssClass.get(), aFailedChannel);
+ *aDisplayedErrorPage = NS_SUCCEEDED(loadedPage);
+ } else {
+ // The prompter reqires that our private window has a document (or it
+ // asserts). Satisfy that assertion now since GetDoc will force
+ // creation of one if it hasn't already been created.
+ if (mScriptGlobal) {
+ Unused << mScriptGlobal->GetDoc();
+ }
+
+ // Display a message box
+ prompter->Alert(nullptr, messageStr.get());
+ }
+
+ return NS_OK;
+}
+
+#define PREF_SAFEBROWSING_ALLOWOVERRIDE "browser.safebrowsing.allowOverride"
+
+NS_IMETHODIMP
+nsDocShell::LoadErrorPage(nsIURI* aURI, const char16_t* aURL,
+ const char* aErrorPage,
+ const char16_t* aErrorType,
+ const char16_t* aDescription,
+ const char* aCSSClass,
+ nsIChannel* aFailedChannel)
+{
+#if defined(DEBUG)
+ if (MOZ_LOG_TEST(gDocShellLog, LogLevel::Debug)) {
+ nsAutoCString chanName;
+ if (aFailedChannel) {
+ aFailedChannel->GetName(chanName);
+ } else {
+ chanName.AssignLiteral("<no channel>");
+ }
+
+ MOZ_LOG(gDocShellLog, LogLevel::Debug,
+ ("nsDocShell[%p]::LoadErrorPage(\"%s\", \"%s\", {...}, [%s])\n", this,
+ aURI->GetSpecOrDefault().get(), NS_ConvertUTF16toUTF8(aURL).get(),
+ chanName.get()));
+ }
+#endif
+ mFailedChannel = aFailedChannel;
+ mFailedURI = aURI;
+ mFailedLoadType = mLoadType;
+
+ if (mLSHE) {
+ // Abandon mLSHE's BFCache entry and create a new one. This way, if
+ // we go back or forward to another SHEntry with the same doc
+ // identifier, the error page won't persist.
+ mLSHE->AbandonBFCacheEntry();
+ }
+
+ nsAutoCString url;
+ nsAutoCString charset;
+ if (aURI) {
+ nsresult rv = aURI->GetSpec(url);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = aURI->GetOriginCharset(charset);
+ NS_ENSURE_SUCCESS(rv, rv);
+ } else if (aURL) {
+ CopyUTF16toUTF8(aURL, url);
+ } else {
+ return NS_ERROR_INVALID_POINTER;
+ }
+
+ // Create a URL to pass all the error information through to the page.
+
+#undef SAFE_ESCAPE
+#define SAFE_ESCAPE(output, input, params) \
+ if (NS_WARN_IF(!NS_Escape(input, output, params))) { \
+ return NS_ERROR_OUT_OF_MEMORY; \
+ }
+
+ nsCString escapedUrl, escapedCharset, escapedError, escapedDescription,
+ escapedCSSClass;
+ SAFE_ESCAPE(escapedUrl, url, url_Path);
+ SAFE_ESCAPE(escapedCharset, charset, url_Path);
+ SAFE_ESCAPE(escapedError, NS_ConvertUTF16toUTF8(aErrorType), url_Path);
+ SAFE_ESCAPE(escapedDescription,
+ NS_ConvertUTF16toUTF8(aDescription), url_Path);
+ if (aCSSClass) {
+ nsCString cssClass(aCSSClass);
+ SAFE_ESCAPE(escapedCSSClass, cssClass, url_Path);
+ }
+ nsCString errorPageUrl("about:");
+ errorPageUrl.AppendASCII(aErrorPage);
+ errorPageUrl.AppendLiteral("?e=");
+
+ errorPageUrl.AppendASCII(escapedError.get());
+ errorPageUrl.AppendLiteral("&u=");
+ errorPageUrl.AppendASCII(escapedUrl.get());
+ if ((strcmp(aErrorPage, "blocked") == 0) &&
+ Preferences::GetBool(PREF_SAFEBROWSING_ALLOWOVERRIDE, true)) {
+ errorPageUrl.AppendLiteral("&o=1");
+ }
+ if (!escapedCSSClass.IsEmpty()) {
+ errorPageUrl.AppendLiteral("&s=");
+ errorPageUrl.AppendASCII(escapedCSSClass.get());
+ }
+ errorPageUrl.AppendLiteral("&c=");
+ errorPageUrl.AppendASCII(escapedCharset.get());
+
+ nsAutoCString frameType(FrameTypeToString(mFrameType));
+ errorPageUrl.AppendLiteral("&f=");
+ errorPageUrl.AppendASCII(frameType.get());
+
+ // Append the manifest URL if the error comes from an app.
+ nsString manifestURL;
+ nsresult rv = GetAppManifestURL(manifestURL);
+ if (manifestURL.Length() > 0) {
+ nsCString manifestParam;
+ SAFE_ESCAPE(manifestParam, NS_ConvertUTF16toUTF8(manifestURL), url_Path);
+ errorPageUrl.AppendLiteral("&m=");
+ errorPageUrl.AppendASCII(manifestParam.get());
+ }
+
+ nsCOMPtr<nsICaptivePortalService> cps = do_GetService(NS_CAPTIVEPORTAL_CID);
+ int32_t cpsState;
+ if (cps && NS_SUCCEEDED(cps->GetState(&cpsState)) &&
+ cpsState == nsICaptivePortalService::LOCKED_PORTAL) {
+ errorPageUrl.AppendLiteral("&captive=true");
+ }
+
+ // netError.xhtml's getDescription only handles the "d" parameter at the
+ // end of the URL, so append it last.
+ errorPageUrl.AppendLiteral("&d=");
+ errorPageUrl.AppendASCII(escapedDescription.get());
+
+ nsCOMPtr<nsIURI> errorPageURI;
+ rv = NS_NewURI(getter_AddRefs(errorPageURI), errorPageUrl);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return InternalLoad(errorPageURI, nullptr, false, nullptr,
+ mozilla::net::RP_Default,
+ nsContentUtils::GetSystemPrincipal(), nullptr,
+ INTERNAL_LOAD_FLAGS_NONE, EmptyString(),
+ nullptr, NullString(), nullptr, nullptr, LOAD_ERROR_PAGE,
+ nullptr, true, NullString(), this, nullptr, nullptr,
+ nullptr);
+}
+
+NS_IMETHODIMP
+nsDocShell::Reload(uint32_t aReloadFlags)
+{
+ if (!IsNavigationAllowed()) {
+ return NS_OK; // JS may not handle returning of an error code
+ }
+ nsresult rv;
+ NS_ASSERTION(((aReloadFlags & 0xf) == 0),
+ "Reload command not updated to use load flags!");
+ NS_ASSERTION((aReloadFlags & EXTRA_LOAD_FLAGS) == 0,
+ "Don't pass these flags to Reload");
+
+ uint32_t loadType = MAKE_LOAD_TYPE(LOAD_RELOAD_NORMAL, aReloadFlags);
+ NS_ENSURE_TRUE(IsValidLoadType(loadType), NS_ERROR_INVALID_ARG);
+
+ // Send notifications to the HistoryListener if any, about the impending
+ // reload
+ nsCOMPtr<nsISHistory> rootSH;
+ rv = GetRootSessionHistory(getter_AddRefs(rootSH));
+ nsCOMPtr<nsISHistoryInternal> shistInt(do_QueryInterface(rootSH));
+ bool canReload = true;
+ if (rootSH) {
+ shistInt->NotifyOnHistoryReload(mCurrentURI, aReloadFlags, &canReload);
+ }
+
+ if (!canReload) {
+ return NS_OK;
+ }
+
+ /* If you change this part of code, make sure bug 45297 does not re-occur */
+ if (mOSHE) {
+ rv = LoadHistoryEntry(mOSHE, loadType);
+ } else if (mLSHE) { // In case a reload happened before the current load is done
+ rv = LoadHistoryEntry(mLSHE, loadType);
+ } else {
+ nsCOMPtr<nsIDocument> doc(GetDocument());
+
+ if (!doc) {
+ return NS_OK;
+ }
+
+ // Do not inherit owner from document
+ uint32_t flags = INTERNAL_LOAD_FLAGS_NONE;
+ nsAutoString srcdoc;
+ nsCOMPtr<nsIURI> baseURI;
+ nsCOMPtr<nsIURI> originalURI;
+ bool loadReplace = false;
+
+ nsIPrincipal* triggeringPrincipal = doc->NodePrincipal();
+ nsAutoString contentTypeHint;
+ doc->GetContentType(contentTypeHint);
+
+ if (doc->IsSrcdocDocument()) {
+ doc->GetSrcdocData(srcdoc);
+ flags |= INTERNAL_LOAD_FLAGS_IS_SRCDOC;
+ baseURI = doc->GetBaseURI();
+ }
+ nsCOMPtr<nsIChannel> chan = doc->GetChannel();
+ if (chan) {
+ uint32_t loadFlags;
+ chan->GetLoadFlags(&loadFlags);
+ loadReplace = loadFlags & nsIChannel::LOAD_REPLACE;
+ nsCOMPtr<nsIHttpChannel> httpChan(do_QueryInterface(chan));
+ if (httpChan) {
+ httpChan->GetOriginalURI(getter_AddRefs(originalURI));
+ }
+ }
+
+ MOZ_ASSERT(triggeringPrincipal, "Need a valid triggeringPrincipal");
+
+ // Stack variables to ensure changes to the member variables don't affect to
+ // the call.
+ nsCOMPtr<nsIURI> currentURI = mCurrentURI;
+ nsCOMPtr<nsIURI> referrerURI = mReferrerURI;
+ uint32_t referrerPolicy = mReferrerPolicy;
+ rv = InternalLoad(currentURI,
+ originalURI,
+ loadReplace,
+ referrerURI,
+ referrerPolicy,
+ triggeringPrincipal,
+ triggeringPrincipal,
+ flags,
+ EmptyString(), // No window target
+ NS_LossyConvertUTF16toASCII(contentTypeHint).get(),
+ NullString(), // No forced download
+ nullptr, // No post data
+ nullptr, // No headers data
+ loadType, // Load type
+ nullptr, // No SHEntry
+ true,
+ srcdoc, // srcdoc argument for iframe
+ this, // For reloads we are the source
+ baseURI,
+ nullptr, // No nsIDocShell
+ nullptr); // No nsIRequest
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDocShell::Stop(uint32_t aStopFlags)
+{
+ // Revoke any pending event related to content viewer restoration
+ mRestorePresentationEvent.Revoke();
+
+ if (mLoadType == LOAD_ERROR_PAGE) {
+ if (mLSHE) {
+ // Since error page loads never unset mLSHE, do so now
+ SetHistoryEntry(&mOSHE, mLSHE);
+ SetHistoryEntry(&mLSHE, nullptr);
+ }
+
+ mFailedChannel = nullptr;
+ mFailedURI = nullptr;
+ }
+
+ if (nsIWebNavigation::STOP_CONTENT & aStopFlags) {
+ // Stop the document loading
+ if (mContentViewer) {
+ nsCOMPtr<nsIContentViewer> cv = mContentViewer;
+ cv->Stop();
+ }
+ }
+
+ if (nsIWebNavigation::STOP_NETWORK & aStopFlags) {
+ // Suspend any timers that were set for this loader. We'll clear
+ // them out for good in CreateContentViewer.
+ if (mRefreshURIList) {
+ SuspendRefreshURIs();
+ mSavedRefreshURIList.swap(mRefreshURIList);
+ mRefreshURIList = nullptr;
+ }
+
+ // XXXbz We could also pass |this| to nsIURILoader::Stop. That will
+ // just call Stop() on us as an nsIDocumentLoader... We need fewer
+ // redundant apis!
+ Stop();
+ }
+
+ nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
+ while (iter.HasMore()) {
+ nsCOMPtr<nsIWebNavigation> shellAsNav(do_QueryObject(iter.GetNext()));
+ if (shellAsNav) {
+ shellAsNav->Stop(aStopFlags);
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetDocument(nsIDOMDocument** aDocument)
+{
+ NS_ENSURE_ARG_POINTER(aDocument);
+ NS_ENSURE_SUCCESS(EnsureContentViewer(), NS_ERROR_FAILURE);
+
+ return mContentViewer->GetDOMDocument(aDocument);
+}
+
+NS_IMETHODIMP
+nsDocShell::GetCurrentURI(nsIURI** aURI)
+{
+ NS_ENSURE_ARG_POINTER(aURI);
+
+ if (mCurrentURI) {
+ return NS_EnsureSafeToReturn(mCurrentURI, aURI);
+ }
+
+ *aURI = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetReferringURI(nsIURI** aURI)
+{
+ NS_ENSURE_ARG_POINTER(aURI);
+
+ *aURI = mReferrerURI;
+ NS_IF_ADDREF(*aURI);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetSessionHistory(nsISHistory* aSessionHistory)
+{
+ NS_ENSURE_TRUE(aSessionHistory, NS_ERROR_FAILURE);
+ // make sure that we are the root docshell and
+ // set a handle to root docshell in SH.
+
+ nsCOMPtr<nsIDocShellTreeItem> root;
+ /* Get the root docshell. If *this* is the root docshell
+ * then save a handle to *this* in SH. SH needs it to do
+ * traversions thro' its entries
+ */
+ GetSameTypeRootTreeItem(getter_AddRefs(root));
+ NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
+ if (root.get() == static_cast<nsIDocShellTreeItem*>(this)) {
+ mSessionHistory = aSessionHistory;
+ nsCOMPtr<nsISHistoryInternal> shPrivate =
+ do_QueryInterface(mSessionHistory);
+ NS_ENSURE_TRUE(shPrivate, NS_ERROR_FAILURE);
+ shPrivate->SetRootDocShell(this);
+ return NS_OK;
+ }
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetSessionHistory(nsISHistory** aSessionHistory)
+{
+ NS_ENSURE_ARG_POINTER(aSessionHistory);
+ *aSessionHistory = mSessionHistory;
+ NS_IF_ADDREF(*aSessionHistory);
+ return NS_OK;
+}
+
+//*****************************************************************************
+// nsDocShell::nsIWebPageDescriptor
+//*****************************************************************************
+
+NS_IMETHODIMP
+nsDocShell::LoadPage(nsISupports* aPageDescriptor, uint32_t aDisplayType)
+{
+ nsCOMPtr<nsISHEntry> shEntryIn(do_QueryInterface(aPageDescriptor));
+
+ // Currently, the opaque 'page descriptor' is an nsISHEntry...
+ if (!shEntryIn) {
+ return NS_ERROR_INVALID_POINTER;
+ }
+
+ // Now clone shEntryIn, since we might end up modifying it later on, and we
+ // want a page descriptor to be reusable.
+ nsCOMPtr<nsISHEntry> shEntry;
+ nsresult rv = shEntryIn->Clone(getter_AddRefs(shEntry));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Give our cloned shEntry a new bfcache entry so this load is independent
+ // of all other loads. (This is important, in particular, for bugs 582795
+ // and 585298.)
+ rv = shEntry->AbandonBFCacheEntry();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ //
+ // load the page as view-source
+ //
+ if (nsIWebPageDescriptor::DISPLAY_AS_SOURCE == aDisplayType) {
+ nsCOMPtr<nsIURI> oldUri, newUri;
+ nsCString spec, newSpec;
+
+ // Create a new view-source URI and replace the original.
+ rv = shEntry->GetURI(getter_AddRefs(oldUri));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ oldUri->GetSpec(spec);
+ newSpec.AppendLiteral("view-source:");
+ newSpec.Append(spec);
+
+ rv = NS_NewURI(getter_AddRefs(newUri), newSpec);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ shEntry->SetURI(newUri);
+ shEntry->SetOriginalURI(nullptr);
+ }
+
+ rv = LoadHistoryEntry(shEntry, LOAD_HISTORY);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetCurrentDescriptor(nsISupports** aPageDescriptor)
+{
+ NS_PRECONDITION(aPageDescriptor, "Null out param?");
+
+ *aPageDescriptor = nullptr;
+
+ nsISHEntry* src = mOSHE ? mOSHE : mLSHE;
+ if (src) {
+ nsCOMPtr<nsISHEntry> dest;
+
+ nsresult rv = src->Clone(getter_AddRefs(dest));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // null out inappropriate cloned attributes...
+ dest->SetParent(nullptr);
+ dest->SetIsSubFrame(false);
+
+ return CallQueryInterface(dest, aPageDescriptor);
+ }
+
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+//*****************************************************************************
+// nsDocShell::nsIBaseWindow
+//*****************************************************************************
+
+NS_IMETHODIMP
+nsDocShell::InitWindow(nativeWindow aParentNativeWindow,
+ nsIWidget* aParentWidget, int32_t aX, int32_t aY,
+ int32_t aWidth, int32_t aHeight)
+{
+ SetParentWidget(aParentWidget);
+ SetPositionAndSize(aX, aY, aWidth, aHeight, 0);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::Create()
+{
+ if (mCreated) {
+ // We've already been created
+ return NS_OK;
+ }
+
+ NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome,
+ "Unexpected item type in docshell");
+
+ NS_ENSURE_TRUE(Preferences::GetRootBranch(), NS_ERROR_FAILURE);
+ mCreated = true;
+
+ if (gValidateOrigin == 0xffffffff) {
+ // Check pref to see if we should prevent frameset spoofing
+ gValidateOrigin =
+ Preferences::GetBool("browser.frame.validate_origin", true);
+ }
+
+ // Should we use XUL error pages instead of alerts if possible?
+ mUseErrorPages =
+ Preferences::GetBool("browser.xul.error_pages.enabled", mUseErrorPages);
+
+ if (!gAddedPreferencesVarCache) {
+ Preferences::AddBoolVarCache(&sUseErrorPages,
+ "browser.xul.error_pages.enabled",
+ mUseErrorPages);
+ gAddedPreferencesVarCache = true;
+ }
+
+ mDisableMetaRefreshWhenInactive =
+ Preferences::GetBool("browser.meta_refresh_when_inactive.disabled",
+ mDisableMetaRefreshWhenInactive);
+
+ mDeviceSizeIsPageSize =
+ Preferences::GetBool("docshell.device_size_is_page_size",
+ mDeviceSizeIsPageSize);
+
+ nsCOMPtr<nsIObserverService> serv = services::GetObserverService();
+ if (serv) {
+ const char* msg = mItemType == typeContent ?
+ NS_WEBNAVIGATION_CREATE : NS_CHROME_WEBNAVIGATION_CREATE;
+ serv->NotifyObservers(GetAsSupports(this), msg, nullptr);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::Destroy()
+{
+ NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome,
+ "Unexpected item type in docshell");
+
+ if (!mIsBeingDestroyed) {
+ nsCOMPtr<nsIObserverService> serv = services::GetObserverService();
+ if (serv) {
+ const char* msg = mItemType == typeContent ?
+ NS_WEBNAVIGATION_DESTROY : NS_CHROME_WEBNAVIGATION_DESTROY;
+ serv->NotifyObservers(GetAsSupports(this), msg, nullptr);
+ }
+ }
+
+ mIsBeingDestroyed = true;
+
+ // Make sure we don't record profile timeline markers anymore
+ SetRecordProfileTimelineMarkers(false);
+
+ // Remove our pref observers
+ if (mObserveErrorPages) {
+ mObserveErrorPages = false;
+ }
+
+ // Make sure to blow away our mLoadingURI just in case. No loads
+ // from inside this pagehide.
+ mLoadingURI = nullptr;
+
+ // Fire unload event before we blow anything away.
+ (void)FirePageHideNotification(true);
+
+ // Clear pointers to any detached nsEditorData that's lying
+ // around in shistory entries. Breaks cycle. See bug 430921.
+ if (mOSHE) {
+ mOSHE->SetEditorData(nullptr);
+ }
+ if (mLSHE) {
+ mLSHE->SetEditorData(nullptr);
+ }
+
+ // Note: mContentListener can be null if Init() failed and we're being
+ // called from the destructor.
+ if (mContentListener) {
+ mContentListener->DropDocShellReference();
+ mContentListener->SetParentContentListener(nullptr);
+ // Note that we do NOT set mContentListener to null here; that
+ // way if someone tries to do a load in us after this point
+ // the nsDSURIContentListener will block it. All of which
+ // means that we should do this before calling Stop(), of
+ // course.
+ }
+
+ // Stop any URLs that are currently being loaded...
+ Stop(nsIWebNavigation::STOP_ALL);
+
+ mEditorData = nullptr;
+
+ mTransferableHookData = nullptr;
+
+ // Save the state of the current document, before destroying the window.
+ // This is needed to capture the state of a frameset when the new document
+ // causes the frameset to be destroyed...
+ PersistLayoutHistoryState();
+
+ // Remove this docshell from its parent's child list
+ nsCOMPtr<nsIDocShellTreeItem> docShellParentAsItem =
+ do_QueryInterface(GetAsSupports(mParent));
+ if (docShellParentAsItem) {
+ docShellParentAsItem->RemoveChild(this);
+ }
+
+ if (mContentViewer) {
+ mContentViewer->Close(nullptr);
+ mContentViewer->Destroy();
+ mContentViewer = nullptr;
+ }
+
+ nsDocLoader::Destroy();
+
+ mParentWidget = nullptr;
+ mCurrentURI = nullptr;
+
+ if (mScriptGlobal) {
+ mScriptGlobal->DetachFromDocShell();
+ mScriptGlobal = nullptr;
+ }
+
+ if (mSessionHistory) {
+ // We want to destroy these content viewers now rather than
+ // letting their destruction wait for the session history
+ // entries to get garbage collected. (Bug 488394)
+ nsCOMPtr<nsISHistoryInternal> shPrivate =
+ do_QueryInterface(mSessionHistory);
+ if (shPrivate) {
+ shPrivate->EvictAllContentViewers();
+ }
+ mSessionHistory = nullptr;
+ }
+
+ SetTreeOwner(nullptr);
+
+ mOnePermittedSandboxedNavigator = nullptr;
+
+ // required to break ref cycle
+ mSecurityUI = nullptr;
+
+ // Cancel any timers that were set for this docshell; this is needed
+ // to break the cycle between us and the timers.
+ CancelRefreshURITimers();
+
+ if (UsePrivateBrowsing()) {
+ mPrivateBrowsingId = 0;
+ mOriginAttributes.SyncAttributesWithPrivateBrowsing(false);
+ if (mAffectPrivateSessionLifetime) {
+ DecreasePrivateDocShellCount();
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetUnscaledDevicePixelsPerCSSPixel(double* aScale)
+{
+ if (mParentWidget) {
+ *aScale = mParentWidget->GetDefaultScale().scale;
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIBaseWindow> ownerWindow(do_QueryInterface(mTreeOwner));
+ if (ownerWindow) {
+ return ownerWindow->GetUnscaledDevicePixelsPerCSSPixel(aScale);
+ }
+
+ *aScale = 1.0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetDevicePixelsPerDesktopPixel(double* aScale)
+{
+ if (mParentWidget) {
+ *aScale = mParentWidget->GetDesktopToDeviceScale().scale;
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIBaseWindow> ownerWindow(do_QueryInterface(mTreeOwner));
+ if (ownerWindow) {
+ return ownerWindow->GetDevicePixelsPerDesktopPixel(aScale);
+ }
+
+ *aScale = 1.0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetPosition(int32_t aX, int32_t aY)
+{
+ mBounds.x = aX;
+ mBounds.y = aY;
+
+ if (mContentViewer) {
+ NS_ENSURE_SUCCESS(mContentViewer->Move(aX, aY), NS_ERROR_FAILURE);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetPositionDesktopPix(int32_t aX, int32_t aY)
+{
+ nsCOMPtr<nsIBaseWindow> ownerWindow(do_QueryInterface(mTreeOwner));
+ if (ownerWindow) {
+ return ownerWindow->SetPositionDesktopPix(aX, aY);
+ }
+
+ double scale = 1.0;
+ GetDevicePixelsPerDesktopPixel(&scale);
+ return SetPosition(NSToIntRound(aX * scale), NSToIntRound(aY * scale));
+}
+
+NS_IMETHODIMP
+nsDocShell::GetPosition(int32_t* aX, int32_t* aY)
+{
+ return GetPositionAndSize(aX, aY, nullptr, nullptr);
+}
+
+NS_IMETHODIMP
+nsDocShell::SetSize(int32_t aWidth, int32_t aHeight, bool aRepaint)
+{
+ int32_t x = 0, y = 0;
+ GetPosition(&x, &y);
+ return SetPositionAndSize(x, y, aWidth, aHeight,
+ aRepaint ? nsIBaseWindow::eRepaint : 0);
+}
+
+NS_IMETHODIMP
+nsDocShell::GetSize(int32_t* aWidth, int32_t* aHeight)
+{
+ return GetPositionAndSize(nullptr, nullptr, aWidth, aHeight);
+}
+
+NS_IMETHODIMP
+nsDocShell::SetPositionAndSize(int32_t aX, int32_t aY, int32_t aWidth,
+ int32_t aHeight, uint32_t aFlags)
+{
+ mBounds.x = aX;
+ mBounds.y = aY;
+ mBounds.width = aWidth;
+ mBounds.height = aHeight;
+
+ // Hold strong ref, since SetBounds can make us null out mContentViewer
+ nsCOMPtr<nsIContentViewer> viewer = mContentViewer;
+ if (viewer) {
+ uint32_t cvflags = (aFlags & nsIBaseWindow::eDelayResize) ?
+ nsIContentViewer::eDelayResize : 0;
+ // XXX Border figured in here or is that handled elsewhere?
+ nsresult rv = viewer->SetBoundsWithFlags(mBounds, cvflags);
+ NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetPositionAndSize(int32_t* aX, int32_t* aY, int32_t* aWidth,
+ int32_t* aHeight)
+{
+ if (mParentWidget) {
+ // ensure size is up-to-date if window has changed resolution
+ LayoutDeviceIntRect r = mParentWidget->GetClientBounds();
+ SetPositionAndSize(mBounds.x, mBounds.y, r.width, r.height, 0);
+ }
+
+ // We should really consider just getting this information from
+ // our window instead of duplicating the storage and code...
+ if (aWidth || aHeight) {
+ // Caller wants to know our size; make sure to give them up to
+ // date information.
+ nsCOMPtr<nsIDocument> doc(do_GetInterface(GetAsSupports(mParent)));
+ if (doc) {
+ doc->FlushPendingNotifications(Flush_Layout);
+ }
+ }
+
+ DoGetPositionAndSize(aX, aY, aWidth, aHeight);
+ return NS_OK;
+}
+
+void
+nsDocShell::DoGetPositionAndSize(int32_t* aX, int32_t* aY, int32_t* aWidth,
+ int32_t* aHeight)
+{
+ if (aX) {
+ *aX = mBounds.x;
+ }
+ if (aY) {
+ *aY = mBounds.y;
+ }
+ if (aWidth) {
+ *aWidth = mBounds.width;
+ }
+ if (aHeight) {
+ *aHeight = mBounds.height;
+ }
+}
+
+NS_IMETHODIMP
+nsDocShell::Repaint(bool aForce)
+{
+ nsCOMPtr<nsIPresShell> presShell = GetPresShell();
+ NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
+
+ nsViewManager* viewManager = presShell->GetViewManager();
+ NS_ENSURE_TRUE(viewManager, NS_ERROR_FAILURE);
+
+ viewManager->InvalidateAllViews();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetParentWidget(nsIWidget** aParentWidget)
+{
+ NS_ENSURE_ARG_POINTER(aParentWidget);
+
+ *aParentWidget = mParentWidget;
+ NS_IF_ADDREF(*aParentWidget);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetParentWidget(nsIWidget* aParentWidget)
+{
+ mParentWidget = aParentWidget;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetParentNativeWindow(nativeWindow* aParentNativeWindow)
+{
+ NS_ENSURE_ARG_POINTER(aParentNativeWindow);
+
+ if (mParentWidget) {
+ *aParentNativeWindow = mParentWidget->GetNativeData(NS_NATIVE_WIDGET);
+ } else {
+ *aParentNativeWindow = nullptr;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetParentNativeWindow(nativeWindow aParentNativeWindow)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetNativeHandle(nsAString& aNativeHandle)
+{
+ // the nativeHandle should be accessed from nsIXULWindow
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetVisibility(bool* aVisibility)
+{
+ NS_ENSURE_ARG_POINTER(aVisibility);
+
+ *aVisibility = false;
+
+ if (!mContentViewer) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIPresShell> presShell = GetPresShell();
+ if (!presShell) {
+ return NS_OK;
+ }
+
+ // get the view manager
+ nsViewManager* vm = presShell->GetViewManager();
+ NS_ENSURE_TRUE(vm, NS_ERROR_FAILURE);
+
+ // get the root view
+ nsView* view = vm->GetRootView(); // views are not ref counted
+ NS_ENSURE_TRUE(view, NS_ERROR_FAILURE);
+
+ // if our root view is hidden, we are not visible
+ if (view->GetVisibility() == nsViewVisibility_kHide) {
+ return NS_OK;
+ }
+
+ // otherwise, we must walk up the document and view trees checking
+ // for a hidden view, unless we're an off screen browser, which
+ // would make this test meaningless.
+
+ RefPtr<nsDocShell> docShell = this;
+ RefPtr<nsDocShell> parentItem = docShell->GetParentDocshell();
+ while (parentItem) {
+ presShell = docShell->GetPresShell();
+
+ nsCOMPtr<nsIPresShell> pPresShell = parentItem->GetPresShell();
+
+ // Null-check for crash in bug 267804
+ if (!pPresShell) {
+ NS_NOTREACHED("parent docshell has null pres shell");
+ return NS_OK;
+ }
+
+ vm = presShell->GetViewManager();
+ if (vm) {
+ view = vm->GetRootView();
+ }
+
+ if (view) {
+ view = view->GetParent(); // anonymous inner view
+ if (view) {
+ view = view->GetParent(); // subdocumentframe's view
+ }
+ }
+
+ nsIFrame* frame = view ? view->GetFrame() : nullptr;
+ bool isDocShellOffScreen = false;
+ docShell->GetIsOffScreenBrowser(&isDocShellOffScreen);
+ if (frame &&
+ !frame->IsVisibleConsideringAncestors(
+ nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY) &&
+ !isDocShellOffScreen) {
+ return NS_OK;
+ }
+
+ docShell = parentItem;
+ parentItem = docShell->GetParentDocshell();
+ }
+
+ nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_QueryInterface(mTreeOwner));
+ if (!treeOwnerAsWin) {
+ *aVisibility = true;
+ return NS_OK;
+ }
+
+ // Check with the tree owner as well to give embedders a chance to
+ // expose visibility as well.
+ return treeOwnerAsWin->GetVisibility(aVisibility);
+}
+
+NS_IMETHODIMP
+nsDocShell::SetIsOffScreenBrowser(bool aIsOffScreen)
+{
+ mIsOffScreenBrowser = aIsOffScreen;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetIsOffScreenBrowser(bool* aIsOffScreen)
+{
+ *aIsOffScreen = mIsOffScreenBrowser;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetIsActive(bool aIsActive)
+{
+ // We disallow setting active on chrome docshells.
+ if (mItemType == nsIDocShellTreeItem::typeChrome) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ // Keep track ourselves.
+ mIsActive = aIsActive;
+
+ // Clear prerender flag if necessary.
+ mIsPrerendered &= !aIsActive;
+
+ // Tell the PresShell about it.
+ nsCOMPtr<nsIPresShell> pshell = GetPresShell();
+ if (pshell) {
+ pshell->SetIsActive(aIsActive);
+ }
+
+ // Tell the window about it
+ if (mScriptGlobal) {
+ mScriptGlobal->SetIsBackground(!aIsActive);
+ if (nsCOMPtr<nsIDocument> doc = mScriptGlobal->GetExtantDoc()) {
+ // Update orientation when the top-level browsing context becomes active.
+ // We make an exception for apps because they currently rely on
+ // orientation locks persisting across browsing contexts.
+ if (aIsActive && !GetIsApp()) {
+ nsCOMPtr<nsIDocShellTreeItem> parent;
+ GetSameTypeParent(getter_AddRefs(parent));
+ if (!parent) {
+ // We only care about the top-level browsing context.
+ uint16_t orientation = OrientationLock();
+ ScreenOrientation::UpdateActiveOrientationLock(orientation);
+ }
+ }
+
+ doc->PostVisibilityUpdateEvent();
+ }
+ }
+
+ // Tell the nsDOMNavigationTiming about it
+ RefPtr<nsDOMNavigationTiming> timing = mTiming;
+ if (!timing && mContentViewer) {
+ nsIDocument* doc = mContentViewer->GetDocument();
+ if (doc) {
+ timing = doc->GetNavigationTiming();
+ }
+ }
+ if (timing) {
+ timing->NotifyDocShellStateChanged(
+ aIsActive ? nsDOMNavigationTiming::DocShellState::eActive
+ : nsDOMNavigationTiming::DocShellState::eInactive);
+ }
+
+ // Recursively tell all of our children, but don't tell <iframe mozbrowser>
+ // children; they handle their state separately.
+ nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
+ while (iter.HasMore()) {
+ nsCOMPtr<nsIDocShell> docshell = do_QueryObject(iter.GetNext());
+ if (!docshell) {
+ continue;
+ }
+
+ if (!docshell->GetIsMozBrowserOrApp()) {
+ docshell->SetIsActive(aIsActive);
+ }
+ }
+
+ // Restart or stop meta refresh timers if necessary
+ if (mDisableMetaRefreshWhenInactive) {
+ if (mIsActive) {
+ ResumeRefreshURIs();
+ } else {
+ SuspendRefreshURIs();
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetIsActive(bool* aIsActive)
+{
+ *aIsActive = mIsActive;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetIsPrerendered()
+{
+ MOZ_ASSERT(!mIsPrerendered,
+ "SetIsPrerendered() called on already prerendered docshell");
+ SetIsActive(false);
+ mIsPrerendered = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetIsPrerendered(bool* aIsPrerendered)
+{
+ *aIsPrerendered = mIsPrerendered;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetIsAppTab(bool aIsAppTab)
+{
+ mIsAppTab = aIsAppTab;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetIsAppTab(bool* aIsAppTab)
+{
+ *aIsAppTab = mIsAppTab;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetSandboxFlags(uint32_t aSandboxFlags)
+{
+ mSandboxFlags = aSandboxFlags;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetSandboxFlags(uint32_t* aSandboxFlags)
+{
+ *aSandboxFlags = mSandboxFlags;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetOnePermittedSandboxedNavigator(nsIDocShell* aSandboxedNavigator)
+{
+ if (mOnePermittedSandboxedNavigator) {
+ NS_ERROR("One Permitted Sandboxed Navigator should only be set once.");
+ return NS_OK;
+ }
+
+ mOnePermittedSandboxedNavigator = do_GetWeakReference(aSandboxedNavigator);
+ NS_ASSERTION(mOnePermittedSandboxedNavigator,
+ "One Permitted Sandboxed Navigator must support weak references.");
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetOnePermittedSandboxedNavigator(nsIDocShell** aSandboxedNavigator)
+{
+ NS_ENSURE_ARG_POINTER(aSandboxedNavigator);
+ nsCOMPtr<nsIDocShell> permittedNavigator =
+ do_QueryReferent(mOnePermittedSandboxedNavigator);
+ permittedNavigator.forget(aSandboxedNavigator);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetDefaultLoadFlags(uint32_t aDefaultLoadFlags)
+{
+ mDefaultLoadFlags = aDefaultLoadFlags;
+
+ // Tell the load group to set these flags all requests in the group
+ if (mLoadGroup) {
+ mLoadGroup->SetDefaultLoadFlags(aDefaultLoadFlags);
+ } else {
+ NS_WARNING("nsDocShell::SetDefaultLoadFlags has no loadGroup to propagate the flags to");
+ }
+
+ // Recursively tell all of our children. We *do not* skip
+ // <iframe mozbrowser> children - if someone sticks custom flags in this
+ // docShell then they too get the same flags.
+ nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
+ while (iter.HasMore()) {
+ nsCOMPtr<nsIDocShell> docshell = do_QueryObject(iter.GetNext());
+ if (!docshell) {
+ continue;
+ }
+ docshell->SetDefaultLoadFlags(aDefaultLoadFlags);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetDefaultLoadFlags(uint32_t* aDefaultLoadFlags)
+{
+ *aDefaultLoadFlags = mDefaultLoadFlags;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetMixedContentChannel(nsIChannel* aMixedContentChannel)
+{
+#ifdef DEBUG
+ // if the channel is non-null
+ if (aMixedContentChannel) {
+ // Get the root docshell.
+ nsCOMPtr<nsIDocShellTreeItem> root;
+ GetSameTypeRootTreeItem(getter_AddRefs(root));
+ NS_WARNING_ASSERTION(root.get() == static_cast<nsIDocShellTreeItem*>(this),
+ "Setting mMixedContentChannel on a docshell that is "
+ "not the root docshell");
+ }
+#endif
+ mMixedContentChannel = aMixedContentChannel;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetFailedChannel(nsIChannel** aFailedChannel)
+{
+ NS_ENSURE_ARG_POINTER(aFailedChannel);
+ nsIDocument* doc = GetDocument();
+ if (!doc) {
+ *aFailedChannel = nullptr;
+ return NS_OK;
+ }
+ NS_IF_ADDREF(*aFailedChannel = doc->GetFailedChannel());
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetMixedContentChannel(nsIChannel** aMixedContentChannel)
+{
+ NS_ENSURE_ARG_POINTER(aMixedContentChannel);
+ NS_IF_ADDREF(*aMixedContentChannel = mMixedContentChannel);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetAllowMixedContentAndConnectionData(bool* aRootHasSecureConnection,
+ bool* aAllowMixedContent,
+ bool* aIsRootDocShell)
+{
+ *aRootHasSecureConnection = true;
+ *aAllowMixedContent = false;
+ *aIsRootDocShell = false;
+
+ nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
+ GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
+ NS_ASSERTION(sameTypeRoot,
+ "No document shell root tree item from document shell tree item!");
+ *aIsRootDocShell =
+ sameTypeRoot.get() == static_cast<nsIDocShellTreeItem*>(this);
+
+ // now get the document from sameTypeRoot
+ nsCOMPtr<nsIDocument> rootDoc = sameTypeRoot->GetDocument();
+ if (rootDoc) {
+ nsCOMPtr<nsIPrincipal> rootPrincipal = rootDoc->NodePrincipal();
+
+ // For things with system principal (e.g. scratchpad) there is no uri
+ // aRootHasSecureConnection should be false.
+ nsCOMPtr<nsIURI> rootUri;
+ if (nsContentUtils::IsSystemPrincipal(rootPrincipal) ||
+ NS_FAILED(rootPrincipal->GetURI(getter_AddRefs(rootUri))) || !rootUri ||
+ NS_FAILED(rootUri->SchemeIs("https", aRootHasSecureConnection))) {
+ *aRootHasSecureConnection = false;
+ }
+
+ // Check the root doc's channel against the root docShell's
+ // mMixedContentChannel to see if they are the same. If they are the same,
+ // the user has overriden the block.
+ nsCOMPtr<nsIDocShell> rootDocShell = do_QueryInterface(sameTypeRoot);
+ nsCOMPtr<nsIChannel> mixedChannel;
+ rootDocShell->GetMixedContentChannel(getter_AddRefs(mixedChannel));
+ *aAllowMixedContent =
+ mixedChannel && (mixedChannel == rootDoc->GetChannel());
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetVisibility(bool aVisibility)
+{
+ // Show()/Hide() may change mContentViewer.
+ nsCOMPtr<nsIContentViewer> cv = mContentViewer;
+ if (!cv) {
+ return NS_OK;
+ }
+ if (aVisibility) {
+ cv->Show();
+ } else {
+ cv->Hide();
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetEnabled(bool* aEnabled)
+{
+ NS_ENSURE_ARG_POINTER(aEnabled);
+ *aEnabled = true;
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetEnabled(bool aEnabled)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetFocus()
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetMainWidget(nsIWidget** aMainWidget)
+{
+ // We don't create our own widget, so simply return the parent one.
+ return GetParentWidget(aMainWidget);
+}
+
+NS_IMETHODIMP
+nsDocShell::GetTitle(char16_t** aTitle)
+{
+ NS_ENSURE_ARG_POINTER(aTitle);
+
+ *aTitle = ToNewUnicode(mTitle);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetTitle(const char16_t* aTitle)
+{
+ // Store local title
+ mTitle = aTitle;
+
+ nsCOMPtr<nsIDocShellTreeItem> parent;
+ GetSameTypeParent(getter_AddRefs(parent));
+
+ // When title is set on the top object it should then be passed to the
+ // tree owner.
+ if (!parent) {
+ nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_QueryInterface(mTreeOwner));
+ if (treeOwnerAsWin) {
+ treeOwnerAsWin->SetTitle(aTitle);
+ }
+ }
+
+ AssertOriginAttributesMatchPrivateBrowsing();
+ if (mCurrentURI && mLoadType != LOAD_ERROR_PAGE && mUseGlobalHistory &&
+ !UsePrivateBrowsing()) {
+ nsCOMPtr<IHistory> history = services::GetHistoryService();
+ if (history) {
+ history->SetURITitle(mCurrentURI, mTitle);
+ } else if (mGlobalHistory) {
+ mGlobalHistory->SetPageTitle(mCurrentURI, nsString(mTitle));
+ }
+ }
+
+ // Update SessionHistory with the document's title.
+ if (mOSHE && mLoadType != LOAD_BYPASS_HISTORY &&
+ mLoadType != LOAD_ERROR_PAGE) {
+ mOSHE->SetTitle(mTitle);
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsDocShell::GetCurScrollPos(int32_t aScrollOrientation, int32_t* aCurPos)
+{
+ NS_ENSURE_ARG_POINTER(aCurPos);
+
+ nsIScrollableFrame* sf = GetRootScrollFrame();
+ if (!sf) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsPoint pt = sf->GetScrollPosition();
+
+ switch (aScrollOrientation) {
+ case ScrollOrientation_X:
+ *aCurPos = pt.x;
+ return NS_OK;
+
+ case ScrollOrientation_Y:
+ *aCurPos = pt.y;
+ return NS_OK;
+
+ default:
+ NS_ENSURE_TRUE(false, NS_ERROR_INVALID_ARG);
+ }
+}
+
+nsresult
+nsDocShell::SetCurScrollPosEx(int32_t aCurHorizontalPos,
+ int32_t aCurVerticalPos)
+{
+ nsIScrollableFrame* sf = GetRootScrollFrame();
+ NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
+
+ sf->ScrollTo(nsPoint(aCurHorizontalPos, aCurVerticalPos),
+ nsIScrollableFrame::INSTANT);
+ return NS_OK;
+}
+
+//*****************************************************************************
+// nsDocShell::nsIScrollable
+//*****************************************************************************
+
+NS_IMETHODIMP
+nsDocShell::GetDefaultScrollbarPreferences(int32_t aScrollOrientation,
+ int32_t* aScrollbarPref)
+{
+ NS_ENSURE_ARG_POINTER(aScrollbarPref);
+ switch (aScrollOrientation) {
+ case ScrollOrientation_X:
+ *aScrollbarPref = mDefaultScrollbarPref.x;
+ return NS_OK;
+
+ case ScrollOrientation_Y:
+ *aScrollbarPref = mDefaultScrollbarPref.y;
+ return NS_OK;
+
+ default:
+ NS_ENSURE_TRUE(false, NS_ERROR_INVALID_ARG);
+ }
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetDefaultScrollbarPreferences(int32_t aScrollOrientation,
+ int32_t aScrollbarPref)
+{
+ switch (aScrollOrientation) {
+ case ScrollOrientation_X:
+ mDefaultScrollbarPref.x = aScrollbarPref;
+ return NS_OK;
+
+ case ScrollOrientation_Y:
+ mDefaultScrollbarPref.y = aScrollbarPref;
+ return NS_OK;
+
+ default:
+ NS_ENSURE_TRUE(false, NS_ERROR_INVALID_ARG);
+ }
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetScrollbarVisibility(bool* aVerticalVisible,
+ bool* aHorizontalVisible)
+{
+ nsIScrollableFrame* sf = GetRootScrollFrame();
+ NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
+
+ uint32_t scrollbarVisibility = sf->GetScrollbarVisibility();
+ if (aVerticalVisible) {
+ *aVerticalVisible =
+ (scrollbarVisibility & nsIScrollableFrame::VERTICAL) != 0;
+ }
+ if (aHorizontalVisible) {
+ *aHorizontalVisible =
+ (scrollbarVisibility & nsIScrollableFrame::HORIZONTAL) != 0;
+ }
+
+ return NS_OK;
+}
+
+//*****************************************************************************
+// nsDocShell::nsITextScroll
+//*****************************************************************************
+
+NS_IMETHODIMP
+nsDocShell::ScrollByLines(int32_t aNumLines)
+{
+ nsIScrollableFrame* sf = GetRootScrollFrame();
+ NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
+
+ sf->ScrollBy(nsIntPoint(0, aNumLines), nsIScrollableFrame::LINES,
+ nsIScrollableFrame::SMOOTH);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::ScrollByPages(int32_t aNumPages)
+{
+ nsIScrollableFrame* sf = GetRootScrollFrame();
+ NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
+
+ sf->ScrollBy(nsIntPoint(0, aNumPages), nsIScrollableFrame::PAGES,
+ nsIScrollableFrame::SMOOTH);
+ return NS_OK;
+}
+
+//*****************************************************************************
+// nsDocShell::nsIRefreshURI
+//*****************************************************************************
+
+NS_IMETHODIMP
+nsDocShell::RefreshURI(nsIURI* aURI,
+ int32_t aDelay, bool aRepeat,
+ bool aMetaRefresh,
+ nsIPrincipal* aPrincipal)
+{
+ NS_ENSURE_ARG(aURI);
+
+ /* Check if Meta refresh/redirects are permitted. Some
+ * embedded applications may not want to do this.
+ * Must do this before sending out NOTIFY_REFRESH events
+ * because listeners may have side effects (e.g. displaying a
+ * button to manually trigger the refresh later).
+ */
+ bool allowRedirects = true;
+ GetAllowMetaRedirects(&allowRedirects);
+ if (!allowRedirects) {
+ return NS_OK;
+ }
+
+ // If any web progress listeners are listening for NOTIFY_REFRESH events,
+ // give them a chance to block this refresh.
+ bool sameURI;
+ nsresult rv = aURI->Equals(mCurrentURI, &sameURI);
+ if (NS_FAILED(rv)) {
+ sameURI = false;
+ }
+ if (!RefreshAttempted(this, aURI, aDelay, sameURI)) {
+ return NS_OK;
+ }
+
+ nsRefreshTimer* refreshTimer = new nsRefreshTimer();
+ uint32_t busyFlags = 0;
+ GetBusyFlags(&busyFlags);
+
+ nsCOMPtr<nsISupports> dataRef = refreshTimer; // Get the ref count to 1
+
+ refreshTimer->mDocShell = this;
+ refreshTimer->mPrincipal = aPrincipal;
+ refreshTimer->mURI = aURI;
+ refreshTimer->mDelay = aDelay;
+ refreshTimer->mRepeat = aRepeat;
+ refreshTimer->mMetaRefresh = aMetaRefresh;
+
+ if (!mRefreshURIList) {
+ mRefreshURIList = nsArray::Create();
+ }
+
+ if (busyFlags & BUSY_FLAGS_BUSY || (!mIsActive && mDisableMetaRefreshWhenInactive)) {
+ // We don't want to create the timer right now. Instead queue up the request
+ // and trigger the timer in EndPageLoad() or whenever we become active.
+ mRefreshURIList->AppendElement(refreshTimer, /*weak =*/ false);
+ } else {
+ // There is no page loading going on right now. Create the
+ // timer and fire it right away.
+ nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1");
+ NS_ENSURE_TRUE(timer, NS_ERROR_FAILURE);
+
+ mRefreshURIList->AppendElement(timer, /*weak =*/ false); // owning timer ref
+ timer->InitWithCallback(refreshTimer, aDelay, nsITimer::TYPE_ONE_SHOT);
+ }
+ return NS_OK;
+}
+
+nsresult
+nsDocShell::ForceRefreshURIFromTimer(nsIURI* aURI,
+ int32_t aDelay,
+ bool aMetaRefresh,
+ nsITimer* aTimer,
+ nsIPrincipal* aPrincipal)
+{
+ NS_PRECONDITION(aTimer, "Must have a timer here");
+
+ // Remove aTimer from mRefreshURIList if needed
+ if (mRefreshURIList) {
+ uint32_t n = 0;
+ mRefreshURIList->GetLength(&n);
+
+ for (uint32_t i = 0; i < n; ++i) {
+ nsCOMPtr<nsITimer> timer = do_QueryElementAt(mRefreshURIList, i);
+ if (timer == aTimer) {
+ mRefreshURIList->RemoveElementAt(i);
+ break;
+ }
+ }
+ }
+
+ return ForceRefreshURI(aURI, aDelay, aMetaRefresh, aPrincipal);
+}
+
+NS_IMETHODIMP
+nsDocShell::ForceRefreshURI(nsIURI* aURI, int32_t aDelay, bool aMetaRefresh, nsIPrincipal* aPrincipal)
+{
+ NS_ENSURE_ARG(aURI);
+
+ nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
+ CreateLoadInfo(getter_AddRefs(loadInfo));
+ NS_ENSURE_TRUE(loadInfo, NS_ERROR_OUT_OF_MEMORY);
+
+ /* We do need to pass in a referrer, but we don't want it to
+ * be sent to the server.
+ */
+ loadInfo->SetSendReferrer(false);
+
+ /* for most refreshes the current URI is an appropriate
+ * internal referrer
+ */
+ loadInfo->SetReferrer(mCurrentURI);
+
+ /* Don't ever "guess" on which principal to use to avoid picking
+ * the current principal.
+ */
+ loadInfo->SetPrincipalIsExplicit(true);
+
+ /* Check if this META refresh causes a redirection
+ * to another site.
+ */
+ bool equalUri = false;
+ nsresult rv = aURI->Equals(mCurrentURI, &equalUri);
+ if (NS_SUCCEEDED(rv) && (!equalUri) && aMetaRefresh &&
+ aDelay <= REFRESH_REDIRECT_TIMER) {
+ /* It is a META refresh based redirection within the threshold time
+ * we have in mind (15000 ms as defined by REFRESH_REDIRECT_TIMER).
+ * Pass a REPLACE flag to LoadURI().
+ */
+ loadInfo->SetLoadType(nsIDocShellLoadInfo::loadNormalReplace);
+
+ /* for redirects we mimic HTTP, which passes the
+ * original referrer
+ */
+ nsCOMPtr<nsIURI> internalReferrer;
+ GetReferringURI(getter_AddRefs(internalReferrer));
+ if (internalReferrer) {
+ loadInfo->SetReferrer(internalReferrer);
+ }
+ } else {
+ loadInfo->SetLoadType(nsIDocShellLoadInfo::loadRefresh);
+ }
+
+ // If the principal is null, the refresh will have a triggeringPrincipal
+ // derived from the referrer URI, or will be set to the system principal
+ // if there is no refererrer. See LoadURI()
+ if (aPrincipal) {
+ loadInfo->SetTriggeringPrincipal(aPrincipal);
+ }
+
+ /*
+ * LoadURI(...) will cancel all refresh timers... This causes the
+ * Timer and its refreshData instance to be released...
+ */
+ LoadURI(aURI, loadInfo, nsIWebNavigation::LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL, true);
+
+ return NS_OK;
+}
+
+nsresult
+nsDocShell::SetupRefreshURIFromHeader(nsIURI* aBaseURI,
+ nsIPrincipal* aPrincipal,
+ const nsACString& aHeader)
+{
+ // Refresh headers are parsed with the following format in mind
+ // <META HTTP-EQUIV=REFRESH CONTENT="5; URL=http://uri">
+ // By the time we are here, the following is true:
+ // header = "REFRESH"
+ // content = "5; URL=http://uri" // note the URL attribute is
+ // optional, if it is absent, the currently loaded url is used.
+ // Also note that the seconds and URL separator can be either
+ // a ';' or a ','. The ',' separator should be illegal but CNN
+ // is using it.
+ //
+ // We need to handle the following strings, where
+ // - X is a set of digits
+ // - URI is either a relative or absolute URI
+ //
+ // Note that URI should start with "url=" but we allow omission
+ //
+ // "" || ";" || ","
+ // empty string. use the currently loaded URI
+ // and refresh immediately.
+ // "X" || "X;" || "X,"
+ // Refresh the currently loaded URI in X seconds.
+ // "X; URI" || "X, URI"
+ // Refresh using URI as the destination in X seconds.
+ // "URI" || "; URI" || ", URI"
+ // Refresh immediately using URI as the destination.
+ //
+ // Currently, anything immediately following the URI, if
+ // separated by any char in the set "'\"\t\r\n " will be
+ // ignored. So "10; url=go.html ; foo=bar" will work,
+ // and so will "10; url='go.html'; foo=bar". However,
+ // "10; url=go.html; foo=bar" will result in the uri
+ // "go.html;" since ';' and ',' are valid uri characters.
+ //
+ // Note that we need to remove any tokens wrapping the URI.
+ // These tokens currently include spaces, double and single
+ // quotes.
+
+ // when done, seconds is 0 or the given number of seconds
+ // uriAttrib is empty or the URI specified
+ MOZ_ASSERT(aPrincipal);
+
+ nsAutoCString uriAttrib;
+ int32_t seconds = 0;
+ bool specifiesSeconds = false;
+
+ nsACString::const_iterator iter, tokenStart, doneIterating;
+
+ aHeader.BeginReading(iter);
+ aHeader.EndReading(doneIterating);
+
+ // skip leading whitespace
+ while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter)) {
+ ++iter;
+ }
+
+ tokenStart = iter;
+
+ // skip leading + and -
+ if (iter != doneIterating && (*iter == '-' || *iter == '+')) {
+ ++iter;
+ }
+
+ // parse number
+ while (iter != doneIterating && (*iter >= '0' && *iter <= '9')) {
+ seconds = seconds * 10 + (*iter - '0');
+ specifiesSeconds = true;
+ ++iter;
+ }
+
+ if (iter != doneIterating) {
+ // if we started with a '-', number is negative
+ if (*tokenStart == '-') {
+ seconds = -seconds;
+ }
+
+ // skip to next ';' or ','
+ nsACString::const_iterator iterAfterDigit = iter;
+ while (iter != doneIterating && !(*iter == ';' || *iter == ',')) {
+ if (specifiesSeconds) {
+ // Non-whitespace characters here mean that the string is
+ // malformed but tolerate sites that specify a decimal point,
+ // even though meta refresh only works on whole seconds.
+ if (iter == iterAfterDigit &&
+ !nsCRT::IsAsciiSpace(*iter) && *iter != '.') {
+ // The characters between the seconds and the next
+ // section are just garbage!
+ // e.g. content="2a0z+,URL=http://www.mozilla.org/"
+ // Just ignore this redirect.
+ return NS_ERROR_FAILURE;
+ } else if (nsCRT::IsAsciiSpace(*iter)) {
+ // We've had at least one whitespace so tolerate the mistake
+ // and drop through.
+ // e.g. content="10 foo"
+ ++iter;
+ break;
+ }
+ }
+ ++iter;
+ }
+
+ // skip any remaining whitespace
+ while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter)) {
+ ++iter;
+ }
+
+ // skip ';' or ','
+ if (iter != doneIterating && (*iter == ';' || *iter == ',')) {
+ ++iter;
+ }
+
+ // skip whitespace
+ while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter)) {
+ ++iter;
+ }
+ }
+
+ // possible start of URI
+ tokenStart = iter;
+
+ // skip "url = " to real start of URI
+ if (iter != doneIterating && (*iter == 'u' || *iter == 'U')) {
+ ++iter;
+ if (iter != doneIterating && (*iter == 'r' || *iter == 'R')) {
+ ++iter;
+ if (iter != doneIterating && (*iter == 'l' || *iter == 'L')) {
+ ++iter;
+
+ // skip whitespace
+ while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter)) {
+ ++iter;
+ }
+
+ if (iter != doneIterating && *iter == '=') {
+ ++iter;
+
+ // skip whitespace
+ while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter)) {
+ ++iter;
+ }
+
+ // found real start of URI
+ tokenStart = iter;
+ }
+ }
+ }
+ }
+
+ // skip a leading '"' or '\''.
+
+ bool isQuotedURI = false;
+ if (tokenStart != doneIterating &&
+ (*tokenStart == '"' || *tokenStart == '\'')) {
+ isQuotedURI = true;
+ ++tokenStart;
+ }
+
+ // set iter to start of URI
+ iter = tokenStart;
+
+ // tokenStart here points to the beginning of URI
+
+ // grab the rest of the URI
+ while (iter != doneIterating) {
+ if (isQuotedURI && (*iter == '"' || *iter == '\'')) {
+ break;
+ }
+ ++iter;
+ }
+
+ // move iter one back if the last character is a '"' or '\''
+ if (iter != tokenStart && isQuotedURI) {
+ --iter;
+ if (!(*iter == '"' || *iter == '\'')) {
+ ++iter;
+ }
+ }
+
+ // URI is whatever's contained from tokenStart to iter.
+ // note: if tokenStart == doneIterating, so is iter.
+
+ nsresult rv = NS_OK;
+
+ nsCOMPtr<nsIURI> uri;
+ bool specifiesURI = false;
+ if (tokenStart == iter) {
+ uri = aBaseURI;
+ } else {
+ uriAttrib = Substring(tokenStart, iter);
+ // NS_NewURI takes care of any whitespace surrounding the URL
+ rv = NS_NewURI(getter_AddRefs(uri), uriAttrib, nullptr, aBaseURI);
+ specifiesURI = true;
+ }
+
+ // No URI or seconds were specified
+ if (!specifiesSeconds && !specifiesURI) {
+ // Do nothing because the alternative is to spin around in a refresh
+ // loop forever!
+ return NS_ERROR_FAILURE;
+ }
+
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsIScriptSecurityManager> securityManager(
+ do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv));
+ if (NS_SUCCEEDED(rv)) {
+ rv = securityManager->CheckLoadURIWithPrincipal(
+ aPrincipal, uri,
+ nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT);
+
+ if (NS_SUCCEEDED(rv)) {
+ bool isjs = true;
+ rv = NS_URIChainHasFlags(
+ uri, nsIProtocolHandler::URI_OPENING_EXECUTES_SCRIPT, &isjs);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (isjs) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ if (NS_SUCCEEDED(rv)) {
+ // Since we can't travel back in time yet, just pretend
+ // negative numbers do nothing at all.
+ if (seconds < 0) {
+ return NS_ERROR_FAILURE;
+ }
+
+ rv = RefreshURI(uri, seconds * 1000, false, true, aPrincipal);
+ }
+ }
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetupRefreshURI(nsIChannel* aChannel)
+{
+ nsresult rv;
+ nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel, &rv));
+ if (NS_SUCCEEDED(rv)) {
+ nsAutoCString refreshHeader;
+ rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("refresh"),
+ refreshHeader);
+
+ if (!refreshHeader.IsEmpty()) {
+ nsCOMPtr<nsIScriptSecurityManager> secMan =
+ do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIPrincipal> principal;
+ rv = secMan->GetChannelResultPrincipal(aChannel,
+ getter_AddRefs(principal));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ SetupReferrerFromChannel(aChannel);
+ rv = SetupRefreshURIFromHeader(mCurrentURI, principal, refreshHeader);
+ if (NS_SUCCEEDED(rv)) {
+ return NS_REFRESHURI_HEADER_FOUND;
+ }
+ }
+ }
+ return rv;
+}
+
+static void
+DoCancelRefreshURITimers(nsIMutableArray* aTimerList)
+{
+ if (!aTimerList) {
+ return;
+ }
+
+ uint32_t n = 0;
+ aTimerList->GetLength(&n);
+
+ while (n) {
+ nsCOMPtr<nsITimer> timer(do_QueryElementAt(aTimerList, --n));
+
+ aTimerList->RemoveElementAt(n); // bye bye owning timer ref
+
+ if (timer) {
+ timer->Cancel();
+ }
+ }
+}
+
+NS_IMETHODIMP
+nsDocShell::CancelRefreshURITimers()
+{
+ DoCancelRefreshURITimers(mRefreshURIList);
+ DoCancelRefreshURITimers(mSavedRefreshURIList);
+ mRefreshURIList = nullptr;
+ mSavedRefreshURIList = nullptr;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetRefreshPending(bool* aResult)
+{
+ if (!mRefreshURIList) {
+ *aResult = false;
+ return NS_OK;
+ }
+
+ uint32_t count;
+ nsresult rv = mRefreshURIList->GetLength(&count);
+ if (NS_SUCCEEDED(rv)) {
+ *aResult = (count != 0);
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDocShell::SuspendRefreshURIs()
+{
+ if (mRefreshURIList) {
+ uint32_t n = 0;
+ mRefreshURIList->GetLength(&n);
+
+ for (uint32_t i = 0; i < n; ++i) {
+ nsCOMPtr<nsITimer> timer = do_QueryElementAt(mRefreshURIList, i);
+ if (!timer) {
+ continue; // this must be a nsRefreshURI already
+ }
+
+ // Replace this timer object with a nsRefreshTimer object.
+ nsCOMPtr<nsITimerCallback> callback;
+ timer->GetCallback(getter_AddRefs(callback));
+
+ timer->Cancel();
+
+ nsCOMPtr<nsITimerCallback> rt = do_QueryInterface(callback);
+ NS_ASSERTION(rt,
+ "RefreshURIList timer callbacks should only be RefreshTimer objects");
+
+ mRefreshURIList->ReplaceElementAt(rt, i, /*weak =*/ false);
+ }
+ }
+
+ // Suspend refresh URIs for our child shells as well.
+ nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
+ while (iter.HasMore()) {
+ nsCOMPtr<nsIDocShell> shell = do_QueryObject(iter.GetNext());
+ if (shell) {
+ shell->SuspendRefreshURIs();
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::ResumeRefreshURIs()
+{
+ RefreshURIFromQueue();
+
+ // Resume refresh URIs for our child shells as well.
+ nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
+ while (iter.HasMore()) {
+ nsCOMPtr<nsIDocShell> shell = do_QueryObject(iter.GetNext());
+ if (shell) {
+ shell->ResumeRefreshURIs();
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsDocShell::RefreshURIFromQueue()
+{
+ if (!mRefreshURIList) {
+ return NS_OK;
+ }
+ uint32_t n = 0;
+ mRefreshURIList->GetLength(&n);
+
+ while (n) {
+ nsCOMPtr<nsITimerCallback> refreshInfo =
+ do_QueryElementAt(mRefreshURIList, --n);
+
+ if (refreshInfo) {
+ // This is the nsRefreshTimer object, waiting to be
+ // setup in a timer object and fired.
+ // Create the timer and trigger it.
+ uint32_t delay =
+ static_cast<nsRefreshTimer*>(
+ static_cast<nsITimerCallback*>(refreshInfo))->GetDelay();
+ nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1");
+ if (timer) {
+ // Replace the nsRefreshTimer element in the queue with
+ // its corresponding timer object, so that in case another
+ // load comes through before the timer can go off, the timer will
+ // get cancelled in CancelRefreshURITimer()
+ mRefreshURIList->ReplaceElementAt(timer, n, /*weak =*/ false);
+ timer->InitWithCallback(refreshInfo, delay, nsITimer::TYPE_ONE_SHOT);
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+//*****************************************************************************
+// nsDocShell::nsIContentViewerContainer
+//*****************************************************************************
+
+NS_IMETHODIMP
+nsDocShell::Embed(nsIContentViewer* aContentViewer,
+ const char* aCommand, nsISupports* aExtraInfo)
+{
+ // Save the LayoutHistoryState of the previous document, before
+ // setting up new document
+ PersistLayoutHistoryState();
+
+ nsresult rv = SetupNewViewer(aContentViewer);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // If we are loading a wyciwyg url from history, change the base URI for
+ // the document to the original http url that created the document.write().
+ // This makes sure that all relative urls in a document.written page loaded
+ // via history work properly.
+ if (mCurrentURI &&
+ (mLoadType & LOAD_CMD_HISTORY ||
+ mLoadType == LOAD_RELOAD_NORMAL ||
+ mLoadType == LOAD_RELOAD_CHARSET_CHANGE)) {
+ bool isWyciwyg = false;
+ // Check if the url is wyciwyg
+ rv = mCurrentURI->SchemeIs("wyciwyg", &isWyciwyg);
+ if (isWyciwyg && NS_SUCCEEDED(rv)) {
+ SetBaseUrlForWyciwyg(aContentViewer);
+ }
+ }
+ // XXX What if SetupNewViewer fails?
+ if (mLSHE) {
+ // Restore the editing state, if it's stored in session history.
+ if (mLSHE->HasDetachedEditor()) {
+ ReattachEditorToWindow(mLSHE);
+ }
+ // Set history.state
+ SetDocCurrentStateObj(mLSHE);
+
+ SetHistoryEntry(&mOSHE, mLSHE);
+ }
+
+ bool updateHistory = true;
+
+ // Determine if this type of load should update history
+ switch (mLoadType) {
+ case LOAD_NORMAL_REPLACE:
+ case LOAD_STOP_CONTENT_AND_REPLACE:
+ case LOAD_RELOAD_BYPASS_CACHE:
+ case LOAD_RELOAD_BYPASS_PROXY:
+ case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
+ case LOAD_REPLACE_BYPASS_CACHE:
+ updateHistory = false;
+ break;
+ default:
+ break;
+ }
+
+ if (!updateHistory) {
+ SetLayoutHistoryState(nullptr);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetIsPrinting(bool aIsPrinting)
+{
+ mIsPrintingOrPP = aIsPrinting;
+ return NS_OK;
+}
+
+//*****************************************************************************
+// nsDocShell::nsIWebProgressListener
+//*****************************************************************************
+
+NS_IMETHODIMP
+nsDocShell::OnProgressChange(nsIWebProgress* aProgress,
+ nsIRequest* aRequest,
+ int32_t aCurSelfProgress,
+ int32_t aMaxSelfProgress,
+ int32_t aCurTotalProgress,
+ int32_t aMaxTotalProgress)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::OnStateChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
+ uint32_t aStateFlags, nsresult aStatus)
+{
+ if ((~aStateFlags & (STATE_START | STATE_IS_NETWORK)) == 0) {
+ // Save timing statistics.
+ nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
+ nsCOMPtr<nsIURI> uri;
+ channel->GetURI(getter_AddRefs(uri));
+ nsAutoCString aURI;
+ uri->GetAsciiSpec(aURI);
+
+ nsCOMPtr<nsIWyciwygChannel> wcwgChannel(do_QueryInterface(aRequest));
+ nsCOMPtr<nsIWebProgress> webProgress =
+ do_QueryInterface(GetAsSupports(this));
+
+ // We don't update navigation timing for wyciwyg channels
+ if (this == aProgress && !wcwgChannel) {
+ MaybeInitTiming();
+ mTiming->NotifyFetchStart(uri,
+ ConvertLoadTypeToNavigationType(mLoadType));
+ }
+
+ // Was the wyciwyg document loaded on this docshell?
+ if (wcwgChannel && !mLSHE && (mItemType == typeContent) &&
+ aProgress == webProgress.get()) {
+ bool equalUri = true;
+ // Store the wyciwyg url in session history, only if it is
+ // being loaded fresh for the first time. We don't want
+ // multiple entries for successive loads
+ if (mCurrentURI &&
+ NS_SUCCEEDED(uri->Equals(mCurrentURI, &equalUri)) &&
+ !equalUri) {
+ nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
+ GetSameTypeParent(getter_AddRefs(parentAsItem));
+ nsCOMPtr<nsIDocShell> parentDS(do_QueryInterface(parentAsItem));
+ bool inOnLoadHandler = false;
+ if (parentDS) {
+ parentDS->GetIsExecutingOnLoadHandler(&inOnLoadHandler);
+ }
+ if (inOnLoadHandler) {
+ // We're handling parent's load event listener, which causes
+ // document.write in a subdocument.
+ // Need to clear the session history for all child
+ // docshells so that we can handle them like they would
+ // all be added dynamically.
+ nsCOMPtr<nsIDocShell> parent = do_QueryInterface(parentAsItem);
+ if (parent) {
+ bool oshe = false;
+ nsCOMPtr<nsISHEntry> entry;
+ parent->GetCurrentSHEntry(getter_AddRefs(entry), &oshe);
+ static_cast<nsDocShell*>(parent.get())->ClearFrameHistory(entry);
+ }
+ }
+
+ // This is a document.write(). Get the made-up url
+ // from the channel and store it in session history.
+ // Pass false for aCloneChildren, since we're creating
+ // a new DOM here.
+ AddToSessionHistory(uri, wcwgChannel, nullptr, nullptr, false,
+ getter_AddRefs(mLSHE));
+ SetCurrentURI(uri, aRequest, true, 0);
+ // Save history state of the previous page
+ PersistLayoutHistoryState();
+ // We'll never get an Embed() for this load, so just go ahead
+ // and SetHistoryEntry now.
+ SetHistoryEntry(&mOSHE, mLSHE);
+ }
+ }
+ // Page has begun to load
+ mBusyFlags = BUSY_FLAGS_BUSY | BUSY_FLAGS_BEFORE_PAGE_LOAD;
+
+ if ((aStateFlags & STATE_RESTORING) == 0) {
+ // Show the progress cursor if the pref is set
+ if (nsContentUtils::UseActivityCursor()) {
+ nsCOMPtr<nsIWidget> mainWidget;
+ GetMainWidget(getter_AddRefs(mainWidget));
+ if (mainWidget) {
+ mainWidget->SetCursor(eCursor_spinning);
+ }
+ }
+ }
+ } else if ((~aStateFlags & (STATE_TRANSFERRING | STATE_IS_DOCUMENT)) == 0) {
+ // Page is loading
+ mBusyFlags = BUSY_FLAGS_BUSY | BUSY_FLAGS_PAGE_LOADING;
+ } else if ((aStateFlags & STATE_STOP) && (aStateFlags & STATE_IS_NETWORK)) {
+ // Page has finished loading
+ mBusyFlags = BUSY_FLAGS_NONE;
+
+ // Hide the progress cursor if the pref is set
+ if (nsContentUtils::UseActivityCursor()) {
+ nsCOMPtr<nsIWidget> mainWidget;
+ GetMainWidget(getter_AddRefs(mainWidget));
+ if (mainWidget) {
+ mainWidget->SetCursor(eCursor_standard);
+ }
+ }
+ }
+ if ((~aStateFlags & (STATE_IS_DOCUMENT | STATE_STOP)) == 0) {
+ nsCOMPtr<nsIWebProgress> webProgress =
+ do_QueryInterface(GetAsSupports(this));
+ // Is the document stop notification for this document?
+ if (aProgress == webProgress.get()) {
+ nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
+ EndPageLoad(aProgress, channel, aStatus);
+ }
+ }
+ // note that redirect state changes will go through here as well, but it
+ // is better to handle those in OnRedirectStateChange where more
+ // information is available.
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::OnLocationChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
+ nsIURI* aURI, uint32_t aFlags)
+{
+ NS_NOTREACHED("notification excluded in AddProgressListener(...)");
+ return NS_OK;
+}
+
+void
+nsDocShell::OnRedirectStateChange(nsIChannel* aOldChannel,
+ nsIChannel* aNewChannel,
+ uint32_t aRedirectFlags,
+ uint32_t aStateFlags)
+{
+ NS_ASSERTION(aStateFlags & STATE_REDIRECTING,
+ "Calling OnRedirectStateChange when there is no redirect");
+
+ // If mixed content is allowed for the old channel, we forward
+ // the permission to the new channel if it has the same origin
+ // as the old one.
+ if (mMixedContentChannel && mMixedContentChannel == aOldChannel) {
+ nsresult rv = nsContentUtils::CheckSameOrigin(mMixedContentChannel, aNewChannel);
+ if (NS_SUCCEEDED(rv)) {
+ SetMixedContentChannel(aNewChannel); // Same origin: forward permission.
+ } else {
+ SetMixedContentChannel(nullptr); // Different origin: clear mMixedContentChannel.
+ }
+ }
+
+ if (!(aStateFlags & STATE_IS_DOCUMENT)) {
+ return; // not a toplevel document
+ }
+
+ nsCOMPtr<nsIURI> oldURI, newURI;
+ aOldChannel->GetURI(getter_AddRefs(oldURI));
+ aNewChannel->GetURI(getter_AddRefs(newURI));
+ if (!oldURI || !newURI) {
+ return;
+ }
+
+ // Below a URI visit is saved (see AddURIVisit method doc).
+ // The visit chain looks something like:
+ // ...
+ // Site N - 1
+ // => Site N
+ // (redirect to =>) Site N + 1 (we are here!)
+
+ // Get N - 1 and transition type
+ nsCOMPtr<nsIURI> previousURI;
+ uint32_t previousFlags = 0;
+ ExtractLastVisit(aOldChannel, getter_AddRefs(previousURI), &previousFlags);
+
+ if (aRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL ||
+ ChannelIsPost(aOldChannel)) {
+ // 1. Internal redirects are ignored because they are specific to the
+ // channel implementation.
+ // 2. POSTs are not saved by global history.
+ //
+ // Regardless, we need to propagate the previous visit to the new
+ // channel.
+ SaveLastVisit(aNewChannel, previousURI, previousFlags);
+ } else {
+ nsCOMPtr<nsIURI> referrer;
+ // Treat referrer as null if there is an error getting it.
+ (void)NS_GetReferrerFromChannel(aOldChannel, getter_AddRefs(referrer));
+
+ // Get the HTTP response code, if available.
+ uint32_t responseStatus = 0;
+ nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aOldChannel);
+ if (httpChannel) {
+ (void)httpChannel->GetResponseStatus(&responseStatus);
+ }
+
+ // Add visit N -1 => N
+ AddURIVisit(oldURI, referrer, previousURI, previousFlags, responseStatus);
+
+ // Since N + 1 could be the final destination, we will not save N => N + 1
+ // here. OnNewURI will do that, so we will cache it.
+ SaveLastVisit(aNewChannel, oldURI, aRedirectFlags);
+ }
+
+ // check if the new load should go through the application cache.
+ nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
+ do_QueryInterface(aNewChannel);
+ if (appCacheChannel) {
+ if (GeckoProcessType_Default != XRE_GetProcessType()) {
+ // Permission will be checked in the parent process.
+ appCacheChannel->SetChooseApplicationCache(true);
+ } else {
+ nsCOMPtr<nsIScriptSecurityManager> secMan =
+ do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
+
+ if (secMan) {
+ nsCOMPtr<nsIPrincipal> principal;
+ secMan->GetDocShellCodebasePrincipal(newURI, this,
+ getter_AddRefs(principal));
+ appCacheChannel->SetChooseApplicationCache(
+ NS_ShouldCheckAppCache(principal, UsePrivateBrowsing()));
+ }
+ }
+ }
+
+ if (!(aRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL) &&
+ mLoadType & (LOAD_CMD_RELOAD | LOAD_CMD_HISTORY)) {
+ mLoadType = LOAD_NORMAL_REPLACE;
+ SetHistoryEntry(&mLSHE, nullptr);
+ }
+}
+
+NS_IMETHODIMP
+nsDocShell::OnStatusChange(nsIWebProgress* aWebProgress,
+ nsIRequest* aRequest,
+ nsresult aStatus, const char16_t* aMessage)
+{
+ NS_NOTREACHED("notification excluded in AddProgressListener(...)");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::OnSecurityChange(nsIWebProgress* aWebProgress,
+ nsIRequest* aRequest, uint32_t aState)
+{
+ NS_NOTREACHED("notification excluded in AddProgressListener(...)");
+ return NS_OK;
+}
+
+nsresult
+nsDocShell::EndPageLoad(nsIWebProgress* aProgress,
+ nsIChannel* aChannel, nsresult aStatus)
+{
+ if (!aChannel) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ nsCOMPtr<nsIConsoleReportCollector> reporter = do_QueryInterface(aChannel);
+ if (reporter) {
+ reporter->FlushConsoleReports(GetDocument());
+ }
+
+ nsCOMPtr<nsIURI> url;
+ nsresult rv = aChannel->GetURI(getter_AddRefs(url));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ nsCOMPtr<nsITimedChannel> timingChannel = do_QueryInterface(aChannel);
+ if (timingChannel) {
+ TimeStamp channelCreationTime;
+ rv = timingChannel->GetChannelCreation(&channelCreationTime);
+ if (NS_SUCCEEDED(rv) && !channelCreationTime.IsNull()) {
+ Telemetry::AccumulateTimeDelta(Telemetry::TOTAL_CONTENT_PAGE_LOAD_TIME,
+ channelCreationTime);
+ nsCOMPtr<nsPILoadGroupInternal> internalLoadGroup =
+ do_QueryInterface(mLoadGroup);
+ if (internalLoadGroup) {
+ internalLoadGroup->OnEndPageLoad(aChannel);
+ }
+ }
+ }
+
+ // Timing is picked up by the window, we don't need it anymore
+ mTiming = nullptr;
+
+ // clean up reload state for meta charset
+ if (eCharsetReloadRequested == mCharsetReloadState) {
+ mCharsetReloadState = eCharsetReloadStopOrigional;
+ } else {
+ mCharsetReloadState = eCharsetReloadInit;
+ }
+
+ // Save a pointer to the currently-loading history entry.
+ // nsDocShell::EndPageLoad will clear mLSHE, but we may need this history
+ // entry further down in this method.
+ nsCOMPtr<nsISHEntry> loadingSHE = mLSHE;
+ mozilla::Unused << loadingSHE; // XXX: Not sure if we need this anymore
+
+ //
+ // one of many safeguards that prevent death and destruction if
+ // someone is so very very rude as to bring this window down
+ // during this load handler.
+ //
+ nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
+
+ // Notify the ContentViewer that the Document has finished loading. This
+ // will cause any OnLoad(...) and PopState(...) handlers to fire.
+ if (!mEODForCurrentDocument && mContentViewer) {
+ mIsExecutingOnLoadHandler = true;
+ mContentViewer->LoadComplete(aStatus);
+ mIsExecutingOnLoadHandler = false;
+
+ mEODForCurrentDocument = true;
+
+ // If all documents have completed their loading
+ // favor native event dispatch priorities
+ // over performance
+ if (--gNumberOfDocumentsLoading == 0) {
+ // Hint to use normal native event dispatch priorities
+ FavorPerformanceHint(false);
+ }
+ }
+ /* Check if the httpChannel has any cache-control related response headers,
+ * like no-store, no-cache. If so, update SHEntry so that
+ * when a user goes back/forward to this page, we appropriately do
+ * form value restoration or load from server.
+ */
+ nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
+ if (!httpChannel) {
+ // HttpChannel could be hiding underneath a Multipart channel.
+ GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
+ }
+
+ if (httpChannel) {
+ // figure out if SH should be saving layout state.
+ bool discardLayoutState = ShouldDiscardLayoutState(httpChannel);
+ if (mLSHE && discardLayoutState && (mLoadType & LOAD_CMD_NORMAL) &&
+ (mLoadType != LOAD_BYPASS_HISTORY) && (mLoadType != LOAD_ERROR_PAGE)) {
+ mLSHE->SetSaveLayoutStateFlag(false);
+ }
+ }
+
+ // Clear mLSHE after calling the onLoadHandlers. This way, if the
+ // onLoadHandler tries to load something different in
+ // itself or one of its children, we can deal with it appropriately.
+ if (mLSHE) {
+ mLSHE->SetLoadType(nsIDocShellLoadInfo::loadHistory);
+
+ // Clear the mLSHE reference to indicate document loading is done one
+ // way or another.
+ SetHistoryEntry(&mLSHE, nullptr);
+ }
+ // if there's a refresh header in the channel, this method
+ // will set it up for us.
+ if (mIsActive || !mDisableMetaRefreshWhenInactive)
+ RefreshURIFromQueue();
+
+ // Test whether this is the top frame or a subframe
+ bool isTopFrame = true;
+ nsCOMPtr<nsIDocShellTreeItem> targetParentTreeItem;
+ rv = GetSameTypeParent(getter_AddRefs(targetParentTreeItem));
+ if (NS_SUCCEEDED(rv) && targetParentTreeItem) {
+ isTopFrame = false;
+ }
+
+ //
+ // If the page load failed, then deal with the error condition...
+ // Errors are handled as follows:
+ // 1. Check to see if it's a file not found error or bad content
+ // encoding error.
+ // 2. Send the URI to a keyword server (if enabled)
+ // 3. If the error was DNS failure, then add www and .com to the URI
+ // (if appropriate).
+ // 4. Throw an error dialog box...
+ //
+ if (url && NS_FAILED(aStatus)) {
+ if (aStatus == NS_ERROR_FILE_NOT_FOUND ||
+ aStatus == NS_ERROR_FILE_ACCESS_DENIED ||
+ aStatus == NS_ERROR_CORRUPTED_CONTENT ||
+ aStatus == NS_ERROR_INVALID_CONTENT_ENCODING) {
+ DisplayLoadError(aStatus, url, nullptr, aChannel);
+ return NS_OK;
+ } else if (aStatus == NS_ERROR_INVALID_SIGNATURE) {
+ // NS_ERROR_INVALID_SIGNATURE indicates a content-signature error.
+ // This currently only happens in case a remote about page fails.
+ // We have to load a fallback in this case.
+ // XXX: We always load about blank here, firefox has to overwrite this if
+ // it wants to display something else.
+ return LoadURI(u"about:blank", // URI string
+ nsIChannel::LOAD_NORMAL, // Load flags
+ nullptr, // Referring URI
+ nullptr, // Post data stream
+ nullptr); // Headers stream
+ }
+
+ // Handle iframe document not loading error because source was
+ // a tracking URL. We make a note of this iframe node by including
+ // it in a dedicated array of blocked tracking nodes under its parent
+ // document. (document of parent window of blocked document)
+ if (isTopFrame == false && aStatus == NS_ERROR_TRACKING_URI) {
+ // frameElement is our nsIContent to be annotated
+ nsCOMPtr<nsIDOMElement> frameElement;
+ nsPIDOMWindowOuter* thisWindow = GetWindow();
+ if (!thisWindow) {
+ return NS_OK;
+ }
+
+ frameElement = thisWindow->GetFrameElement();
+ if (!frameElement) {
+ return NS_OK;
+ }
+
+ // Parent window
+ nsCOMPtr<nsIDocShellTreeItem> parentItem;
+ GetSameTypeParent(getter_AddRefs(parentItem));
+ if (!parentItem) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIDocument> parentDoc;
+ parentDoc = parentItem->GetDocument();
+ if (!parentDoc) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIContent> cont = do_QueryInterface(frameElement);
+ parentDoc->AddBlockedTrackingNode(cont);
+
+ return NS_OK;
+ }
+
+ if (sURIFixup) {
+ //
+ // Try and make an alternative URI from the old one
+ //
+ nsCOMPtr<nsIURI> newURI;
+ nsCOMPtr<nsIInputStream> newPostData;
+
+ nsAutoCString oldSpec;
+ url->GetSpec(oldSpec);
+
+ //
+ // First try keyword fixup
+ //
+ nsAutoString keywordProviderName, keywordAsSent;
+ if (aStatus == NS_ERROR_UNKNOWN_HOST && mAllowKeywordFixup) {
+ bool keywordsEnabled = Preferences::GetBool("keyword.enabled", false);
+
+ nsAutoCString host;
+ url->GetHost(host);
+
+ nsAutoCString scheme;
+ url->GetScheme(scheme);
+
+ int32_t dotLoc = host.FindChar('.');
+
+ // we should only perform a keyword search under the following
+ // conditions:
+ // (0) Pref keyword.enabled is true
+ // (1) the url scheme is http (or https)
+ // (2) the url does not have a protocol scheme
+ // If we don't enforce such a policy, then we end up doing
+ // keyword searchs on urls we don't intend like imap, file,
+ // mailbox, etc. This could lead to a security problem where we
+ // send data to the keyword server that we shouldn't be.
+ // Someone needs to clean up keywords in general so we can
+ // determine on a per url basis if we want keywords
+ // enabled...this is just a bandaid...
+ if (keywordsEnabled && !scheme.IsEmpty() &&
+ (scheme.Find("http") != 0)) {
+ keywordsEnabled = false;
+ }
+
+ if (keywordsEnabled && (kNotFound == dotLoc)) {
+ nsCOMPtr<nsIURIFixupInfo> info;
+ // only send non-qualified hosts to the keyword server
+ if (!mOriginalUriString.IsEmpty()) {
+ sURIFixup->KeywordToURI(mOriginalUriString,
+ getter_AddRefs(newPostData),
+ getter_AddRefs(info));
+ } else {
+ //
+ // If this string was passed through nsStandardURL by
+ // chance, then it may have been converted from UTF-8 to
+ // ACE, which would result in a completely bogus keyword
+ // query. Here we try to recover the original Unicode
+ // value, but this is not 100% correct since the value may
+ // have been normalized per the IDN normalization rules.
+ //
+ // Since we don't have access to the exact original string
+ // that was entered by the user, this will just have to do.
+ bool isACE;
+ nsAutoCString utf8Host;
+ nsCOMPtr<nsIIDNService> idnSrv =
+ do_GetService(NS_IDNSERVICE_CONTRACTID);
+ if (idnSrv &&
+ NS_SUCCEEDED(idnSrv->IsACE(host, &isACE)) && isACE &&
+ NS_SUCCEEDED(idnSrv->ConvertACEtoUTF8(host, utf8Host))) {
+ sURIFixup->KeywordToURI(utf8Host,
+ getter_AddRefs(newPostData),
+ getter_AddRefs(info));
+ } else {
+ sURIFixup->KeywordToURI(host,
+ getter_AddRefs(newPostData),
+ getter_AddRefs(info));
+ }
+ }
+
+ info->GetPreferredURI(getter_AddRefs(newURI));
+ if (newURI) {
+ info->GetKeywordAsSent(keywordAsSent);
+ info->GetKeywordProviderName(keywordProviderName);
+ }
+ } // end keywordsEnabled
+ }
+
+ //
+ // Now try change the address, e.g. turn http://foo into
+ // http://www.foo.com
+ //
+ if (aStatus == NS_ERROR_UNKNOWN_HOST ||
+ aStatus == NS_ERROR_NET_RESET) {
+ bool doCreateAlternate = true;
+
+ // Skip fixup for anything except a normal document load
+ // operation on the topframe.
+
+ if (mLoadType != LOAD_NORMAL || !isTopFrame) {
+ doCreateAlternate = false;
+ } else {
+ // Test if keyword lookup produced a new URI or not
+ if (newURI) {
+ bool sameURI = false;
+ url->Equals(newURI, &sameURI);
+ if (!sameURI) {
+ // Keyword lookup made a new URI so no need to try
+ // an alternate one.
+ doCreateAlternate = false;
+ }
+ }
+
+ if (doCreateAlternate) {
+ // Skip doing this if our channel was redirected, because we
+ // shouldn't be guessing things about the post-redirect URI.
+ nsLoadFlags loadFlags = 0;
+ if (NS_FAILED(aChannel->GetLoadFlags(&loadFlags)) ||
+ (loadFlags & nsIChannel::LOAD_REPLACE)) {
+ doCreateAlternate = false;
+ }
+ }
+ }
+ if (doCreateAlternate) {
+ newURI = nullptr;
+ newPostData = nullptr;
+ keywordProviderName.Truncate();
+ keywordAsSent.Truncate();
+ sURIFixup->CreateFixupURI(oldSpec,
+ nsIURIFixup::FIXUP_FLAGS_MAKE_ALTERNATE_URI,
+ getter_AddRefs(newPostData),
+ getter_AddRefs(newURI));
+ }
+ }
+
+ // Did we make a new URI that is different to the old one? If so
+ // load it.
+ //
+ if (newURI) {
+ // Make sure the new URI is different from the old one,
+ // otherwise there's little point trying to load it again.
+ bool sameURI = false;
+ url->Equals(newURI, &sameURI);
+ if (!sameURI) {
+ nsAutoCString newSpec;
+ newURI->GetSpec(newSpec);
+ NS_ConvertUTF8toUTF16 newSpecW(newSpec);
+
+ // This notification is meant for Firefox Health Report so it
+ // can increment counts from the search engine
+ MaybeNotifyKeywordSearchLoading(keywordProviderName, keywordAsSent);
+
+ return LoadURI(newSpecW.get(), // URI string
+ LOAD_FLAGS_NONE, // Load flags
+ nullptr, // Referring URI
+ newPostData, // Post data stream
+ nullptr); // Headers stream
+ }
+ }
+ }
+
+ // Well, fixup didn't work :-(
+ // It is time to throw an error dialog box, and be done with it...
+
+ // Errors to be shown only on top-level frames
+ if ((aStatus == NS_ERROR_UNKNOWN_HOST ||
+ aStatus == NS_ERROR_CONNECTION_REFUSED ||
+ aStatus == NS_ERROR_UNKNOWN_PROXY_HOST ||
+ aStatus == NS_ERROR_PROXY_CONNECTION_REFUSED) &&
+ (isTopFrame || UseErrorPages())) {
+ DisplayLoadError(aStatus, url, nullptr, aChannel);
+ } else if (aStatus == NS_ERROR_NET_TIMEOUT ||
+ aStatus == NS_ERROR_REDIRECT_LOOP ||
+ aStatus == NS_ERROR_UNKNOWN_SOCKET_TYPE ||
+ aStatus == NS_ERROR_NET_INTERRUPT ||
+ aStatus == NS_ERROR_NET_RESET ||
+ aStatus == NS_ERROR_OFFLINE ||
+ aStatus == NS_ERROR_MALWARE_URI ||
+ aStatus == NS_ERROR_PHISHING_URI ||
+ aStatus == NS_ERROR_UNWANTED_URI ||
+ aStatus == NS_ERROR_UNSAFE_CONTENT_TYPE ||
+ aStatus == NS_ERROR_REMOTE_XUL ||
+ aStatus == NS_ERROR_INTERCEPTION_FAILED ||
+ aStatus == NS_ERROR_NET_INADEQUATE_SECURITY ||
+ NS_ERROR_GET_MODULE(aStatus) == NS_ERROR_MODULE_SECURITY) {
+ // Errors to be shown for any frame
+ DisplayLoadError(aStatus, url, nullptr, aChannel);
+ } else if (aStatus == NS_ERROR_DOCUMENT_NOT_CACHED) {
+ // Non-caching channels will simply return NS_ERROR_OFFLINE.
+ // Caching channels would have to look at their flags to work
+ // out which error to return. Or we can fix up the error here.
+ if (!(mLoadType & LOAD_CMD_HISTORY)) {
+ aStatus = NS_ERROR_OFFLINE;
+ }
+ DisplayLoadError(aStatus, url, nullptr, aChannel);
+ }
+ } else if (url && NS_SUCCEEDED(aStatus)) {
+ // If we have a host
+ mozilla::net::PredictorLearnRedirect(url, aChannel, this);
+ }
+
+ return NS_OK;
+}
+
+//*****************************************************************************
+// nsDocShell: Content Viewer Management
+//*****************************************************************************
+
+nsresult
+nsDocShell::EnsureContentViewer()
+{
+ if (mContentViewer) {
+ return NS_OK;
+ }
+ if (mIsBeingDestroyed) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIURI> baseURI;
+ nsIPrincipal* principal = GetInheritedPrincipal(false);
+ nsCOMPtr<nsIDocShellTreeItem> parentItem;
+ GetSameTypeParent(getter_AddRefs(parentItem));
+ if (parentItem) {
+ if (nsCOMPtr<nsPIDOMWindowOuter> domWin = GetWindow()) {
+ nsCOMPtr<Element> parentElement = domWin->GetFrameElementInternal();
+ if (parentElement) {
+ baseURI = parentElement->GetBaseURI();
+ }
+ }
+ }
+
+ nsresult rv = CreateAboutBlankContentViewer(principal, baseURI);
+
+ NS_ENSURE_STATE(mContentViewer);
+
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsIDocument> doc(GetDocument());
+ NS_ASSERTION(doc,
+ "Should have doc if CreateAboutBlankContentViewer "
+ "succeeded!");
+
+ doc->SetIsInitialDocument(true);
+ }
+
+ return rv;
+}
+
+nsresult
+nsDocShell::CreateAboutBlankContentViewer(nsIPrincipal* aPrincipal,
+ nsIURI* aBaseURI,
+ bool aTryToSaveOldPresentation)
+{
+ nsCOMPtr<nsIDocument> blankDoc;
+ nsCOMPtr<nsIContentViewer> viewer;
+ nsresult rv = NS_ERROR_FAILURE;
+
+ /* mCreatingDocument should never be true at this point. However, it's
+ a theoretical possibility. We want to know about it and make it stop,
+ and this sounds like a job for an assertion. */
+ NS_ASSERTION(!mCreatingDocument,
+ "infinite(?) loop creating document averted");
+ if (mCreatingDocument) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // mContentViewer->PermitUnload may release |this| docshell.
+ nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
+
+ AutoRestore<bool> creatingDocument(mCreatingDocument);
+ mCreatingDocument = true;
+
+ if (aPrincipal && !nsContentUtils::IsSystemPrincipal(aPrincipal) &&
+ mItemType != typeChrome) {
+ MOZ_ASSERT(ChromeUtils::IsOriginAttributesEqualIgnoringAddonId(
+ BasePrincipal::Cast(aPrincipal)->OriginAttributesRef(),
+ mOriginAttributes));
+ }
+
+ // Make sure timing is created. But first record whether we had it
+ // already, so we don't clobber the timing for an in-progress load.
+ bool hadTiming = mTiming;
+ MaybeInitTiming();
+ if (mContentViewer) {
+ // We've got a content viewer already. Make sure the user
+ // permits us to discard the current document and replace it
+ // with about:blank. And also ensure we fire the unload events
+ // in the current document.
+
+ // Unload gets fired first for
+ // document loaded from the session history.
+ mTiming->NotifyBeforeUnload();
+
+ bool okToUnload;
+ rv = mContentViewer->PermitUnload(&okToUnload);
+
+ if (NS_SUCCEEDED(rv) && !okToUnload) {
+ // The user chose not to unload the page, interrupt the load.
+ return NS_ERROR_FAILURE;
+ }
+
+ mSavingOldViewer = aTryToSaveOldPresentation &&
+ CanSavePresentation(LOAD_NORMAL, nullptr, nullptr);
+
+ if (mTiming) {
+ mTiming->NotifyUnloadAccepted(mCurrentURI);
+ }
+
+ // Make sure to blow away our mLoadingURI just in case. No loads
+ // from inside this pagehide.
+ mLoadingURI = nullptr;
+
+ // Stop any in-progress loading, so that we don't accidentally trigger any
+ // PageShow notifications from Embed() interrupting our loading below.
+ Stop();
+
+ // Notify the current document that it is about to be unloaded!!
+ //
+ // It is important to fire the unload() notification *before* any state
+ // is changed within the DocShell - otherwise, javascript will get the
+ // wrong information :-(
+ //
+ (void)FirePageHideNotification(!mSavingOldViewer);
+ }
+
+ // Now make sure we don't think we're in the middle of firing unload after
+ // this point. This will make us fire unload when the about:blank document
+ // unloads... but that's ok, more or less. Would be nice if it fired load
+ // too, of course.
+ mFiredUnloadEvent = false;
+
+ nsCOMPtr<nsIDocumentLoaderFactory> docFactory =
+ nsContentUtils::FindInternalContentViewer(NS_LITERAL_CSTRING("text/html"));
+
+ if (docFactory) {
+ nsCOMPtr<nsIPrincipal> principal;
+ if (mSandboxFlags & SANDBOXED_ORIGIN) {
+ if (aPrincipal) {
+ principal = nsNullPrincipal::CreateWithInheritedAttributes(aPrincipal);
+ } else {
+ principal = nsNullPrincipal::CreateWithInheritedAttributes(this);
+ }
+ } else {
+ principal = aPrincipal;
+ }
+ // generate (about:blank) document to load
+ docFactory->CreateBlankDocument(mLoadGroup, principal,
+ getter_AddRefs(blankDoc));
+ if (blankDoc) {
+ // Hack: set the base URI manually, since this document never
+ // got Reset() with a channel.
+ blankDoc->SetBaseURI(aBaseURI);
+
+ blankDoc->SetContainer(this);
+
+ // Copy our sandbox flags to the document. These are immutable
+ // after being set here.
+ blankDoc->SetSandboxFlags(mSandboxFlags);
+
+ // create a content viewer for us and the new document
+ docFactory->CreateInstanceForDocument(
+ NS_ISUPPORTS_CAST(nsIDocShell*, this), blankDoc, "view",
+ getter_AddRefs(viewer));
+
+ // hook 'em up
+ if (viewer) {
+ viewer->SetContainer(this);
+ rv = Embed(viewer, "", 0);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ SetCurrentURI(blankDoc->GetDocumentURI(), nullptr, true, 0);
+ rv = mIsBeingDestroyed ? NS_ERROR_NOT_AVAILABLE : NS_OK;
+ }
+ }
+ }
+
+ // The transient about:blank viewer doesn't have a session history entry.
+ SetHistoryEntry(&mOSHE, nullptr);
+
+ // Clear out our mTiming like we would in EndPageLoad, if we didn't
+ // have one before entering this function.
+ if (!hadTiming) {
+ mTiming = nullptr;
+ mBlankTiming = true;
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDocShell::CreateAboutBlankContentViewer(nsIPrincipal* aPrincipal)
+{
+ return CreateAboutBlankContentViewer(aPrincipal, nullptr);
+}
+
+bool
+nsDocShell::CanSavePresentation(uint32_t aLoadType,
+ nsIRequest* aNewRequest,
+ nsIDocument* aNewDocument)
+{
+ if (!mOSHE) {
+ return false; // no entry to save into
+ }
+
+ nsCOMPtr<nsIContentViewer> viewer;
+ mOSHE->GetContentViewer(getter_AddRefs(viewer));
+ if (viewer) {
+ NS_WARNING("mOSHE already has a content viewer!");
+ return false;
+ }
+
+ // Only save presentation for "normal" loads and link loads. Anything else
+ // probably wants to refetch the page, so caching the old presentation
+ // would be incorrect.
+ if (aLoadType != LOAD_NORMAL &&
+ aLoadType != LOAD_HISTORY &&
+ aLoadType != LOAD_LINK &&
+ aLoadType != LOAD_STOP_CONTENT &&
+ aLoadType != LOAD_STOP_CONTENT_AND_REPLACE &&
+ aLoadType != LOAD_ERROR_PAGE) {
+ return false;
+ }
+
+ // If the session history entry has the saveLayoutState flag set to false,
+ // then we should not cache the presentation.
+ bool canSaveState;
+ mOSHE->GetSaveLayoutStateFlag(&canSaveState);
+ if (!canSaveState) {
+ return false;
+ }
+
+ // If the document is not done loading, don't cache it.
+ if (!mScriptGlobal || mScriptGlobal->IsLoading()) {
+ return false;
+ }
+
+ if (mScriptGlobal->WouldReuseInnerWindow(aNewDocument)) {
+ return false;
+ }
+
+ // Avoid doing the work of saving the presentation state in the case where
+ // the content viewer cache is disabled.
+ if (nsSHistory::GetMaxTotalViewers() == 0) {
+ return false;
+ }
+
+ // Don't cache the content viewer if we're in a subframe and the subframe
+ // pref is disabled.
+ bool cacheFrames =
+ Preferences::GetBool("browser.sessionhistory.cache_subframes", false);
+ if (!cacheFrames) {
+ nsCOMPtr<nsIDocShellTreeItem> root;
+ GetSameTypeParent(getter_AddRefs(root));
+ if (root && root != this) {
+ return false; // this is a subframe load
+ }
+ }
+
+ // If the document does not want its presentation cached, then don't.
+ nsCOMPtr<nsIDocument> doc = mScriptGlobal->GetExtantDoc();
+ return doc && doc->CanSavePresentation(aNewRequest);
+}
+
+void
+nsDocShell::ReattachEditorToWindow(nsISHEntry* aSHEntry)
+{
+ NS_ASSERTION(!mEditorData,
+ "Why reattach an editor when we already have one?");
+ NS_ASSERTION(aSHEntry && aSHEntry->HasDetachedEditor(),
+ "Reattaching when there's not a detached editor.");
+
+ if (mEditorData || !aSHEntry) {
+ return;
+ }
+
+ mEditorData = aSHEntry->ForgetEditorData();
+ if (mEditorData) {
+#ifdef DEBUG
+ nsresult rv =
+#endif
+ mEditorData->ReattachToWindow(this);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to reattach editing session");
+ }
+}
+
+void
+nsDocShell::DetachEditorFromWindow()
+{
+ if (!mEditorData || mEditorData->WaitingForLoad()) {
+ // If there's nothing to detach, or if the editor data is actually set
+ // up for the _new_ page that's coming in, don't detach.
+ return;
+ }
+
+ NS_ASSERTION(!mOSHE || !mOSHE->HasDetachedEditor(),
+ "Detaching editor when it's already detached.");
+
+ nsresult res = mEditorData->DetachFromWindow();
+ NS_ASSERTION(NS_SUCCEEDED(res), "Failed to detach editor");
+
+ if (NS_SUCCEEDED(res)) {
+ // Make mOSHE hold the owning ref to the editor data.
+ if (mOSHE) {
+ mOSHE->SetEditorData(mEditorData.forget());
+ } else {
+ mEditorData = nullptr;
+ }
+ }
+
+#ifdef DEBUG
+ {
+ bool isEditable;
+ GetEditable(&isEditable);
+ NS_ASSERTION(!isEditable,
+ "Window is still editable after detaching editor.");
+ }
+#endif // DEBUG
+}
+
+nsresult
+nsDocShell::CaptureState()
+{
+ if (!mOSHE || mOSHE == mLSHE) {
+ // No entry to save into, or we're replacing the existing entry.
+ return NS_ERROR_FAILURE;
+ }
+
+ if (!mScriptGlobal) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsISupports> windowState = mScriptGlobal->SaveWindowState();
+ NS_ENSURE_TRUE(windowState, NS_ERROR_FAILURE);
+
+#ifdef DEBUG_PAGE_CACHE
+ nsCOMPtr<nsIURI> uri;
+ mOSHE->GetURI(getter_AddRefs(uri));
+ nsAutoCString spec;
+ if (uri) {
+ uri->GetSpec(spec);
+ }
+ printf("Saving presentation into session history\n");
+ printf(" SH URI: %s\n", spec.get());
+#endif
+
+ nsresult rv = mOSHE->SetWindowState(windowState);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Suspend refresh URIs and save off the timer queue
+ rv = mOSHE->SetRefreshURIList(mSavedRefreshURIList);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Capture the current content viewer bounds.
+ if (mContentViewer) {
+ nsIntRect bounds;
+ mContentViewer->GetBounds(bounds);
+ rv = mOSHE->SetViewerBounds(bounds);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // Capture the docshell hierarchy.
+ mOSHE->ClearChildShells();
+
+ uint32_t childCount = mChildList.Length();
+ for (uint32_t i = 0; i < childCount; ++i) {
+ nsCOMPtr<nsIDocShellTreeItem> childShell = do_QueryInterface(ChildAt(i));
+ NS_ASSERTION(childShell, "null child shell");
+
+ mOSHE->AddChildShell(childShell);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::RestorePresentationEvent::Run()
+{
+ if (mDocShell && NS_FAILED(mDocShell->RestoreFromHistory())) {
+ NS_WARNING("RestoreFromHistory failed");
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::BeginRestore(nsIContentViewer* aContentViewer, bool aTop)
+{
+ nsresult rv;
+ if (!aContentViewer) {
+ rv = EnsureContentViewer();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aContentViewer = mContentViewer;
+ }
+
+ // Dispatch events for restoring the presentation. We try to simulate
+ // the progress notifications loading the document would cause, so we add
+ // the document's channel to the loadgroup to initiate stateChange
+ // notifications.
+
+ nsCOMPtr<nsIDOMDocument> domDoc;
+ aContentViewer->GetDOMDocument(getter_AddRefs(domDoc));
+ nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
+ if (doc) {
+ nsIChannel* channel = doc->GetChannel();
+ if (channel) {
+ mEODForCurrentDocument = false;
+ mIsRestoringDocument = true;
+ mLoadGroup->AddRequest(channel, nullptr);
+ mIsRestoringDocument = false;
+ }
+ }
+
+ if (!aTop) {
+ // This point corresponds to us having gotten OnStartRequest or
+ // STATE_START, so do the same thing that CreateContentViewer does at
+ // this point to ensure that unload/pagehide events for this document
+ // will fire when it's unloaded again.
+ mFiredUnloadEvent = false;
+
+ // For non-top frames, there is no notion of making sure that the
+ // previous document is in the domwindow when STATE_START notifications
+ // happen. We can just call BeginRestore for all of the child shells
+ // now.
+ rv = BeginRestoreChildren();
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsDocShell::BeginRestoreChildren()
+{
+ nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
+ while (iter.HasMore()) {
+ nsCOMPtr<nsIDocShell> child = do_QueryObject(iter.GetNext());
+ if (child) {
+ nsresult rv = child->BeginRestore(nullptr, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::FinishRestore()
+{
+ // First we call finishRestore() on our children. In the simulated load,
+ // all of the child frames finish loading before the main document.
+
+ nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
+ while (iter.HasMore()) {
+ nsCOMPtr<nsIDocShell> child = do_QueryObject(iter.GetNext());
+ if (child) {
+ child->FinishRestore();
+ }
+ }
+
+ if (mOSHE && mOSHE->HasDetachedEditor()) {
+ ReattachEditorToWindow(mOSHE);
+ }
+
+ nsCOMPtr<nsIDocument> doc = GetDocument();
+ if (doc) {
+ // Finally, we remove the request from the loadgroup. This will
+ // cause onStateChange(STATE_STOP) to fire, which will fire the
+ // pageshow event to the chrome.
+
+ nsIChannel* channel = doc->GetChannel();
+ if (channel) {
+ mIsRestoringDocument = true;
+ mLoadGroup->RemoveRequest(channel, nullptr, NS_OK);
+ mIsRestoringDocument = false;
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetRestoringDocument(bool* aRestoring)
+{
+ *aRestoring = mIsRestoringDocument;
+ return NS_OK;
+}
+
+nsresult
+nsDocShell::RestorePresentation(nsISHEntry* aSHEntry, bool* aRestoring)
+{
+ NS_ASSERTION(mLoadType & LOAD_CMD_HISTORY,
+ "RestorePresentation should only be called for history loads");
+
+ nsCOMPtr<nsIContentViewer> viewer;
+ aSHEntry->GetContentViewer(getter_AddRefs(viewer));
+
+#ifdef DEBUG_PAGE_CACHE
+ nsCOMPtr<nsIURI> uri;
+ aSHEntry->GetURI(getter_AddRefs(uri));
+
+ nsAutoCString spec;
+ if (uri) {
+ uri->GetSpec(spec);
+ }
+#endif
+
+ *aRestoring = false;
+
+ if (!viewer) {
+#ifdef DEBUG_PAGE_CACHE
+ printf("no saved presentation for uri: %s\n", spec.get());
+#endif
+ return NS_OK;
+ }
+
+ // We need to make sure the content viewer's container is this docshell.
+ // In subframe navigation, it's possible for the docshell that the
+ // content viewer was originally loaded into to be replaced with a
+ // different one. We don't currently support restoring the presentation
+ // in that case.
+
+ nsCOMPtr<nsIDocShell> container;
+ viewer->GetContainer(getter_AddRefs(container));
+ if (!::SameCOMIdentity(container, GetAsSupports(this))) {
+#ifdef DEBUG_PAGE_CACHE
+ printf("No valid container, clearing presentation\n");
+#endif
+ aSHEntry->SetContentViewer(nullptr);
+ return NS_ERROR_FAILURE;
+ }
+
+ NS_ASSERTION(mContentViewer != viewer, "Restoring existing presentation");
+
+#ifdef DEBUG_PAGE_CACHE
+ printf("restoring presentation from session history: %s\n", spec.get());
+#endif
+
+ SetHistoryEntry(&mLSHE, aSHEntry);
+
+ // Post an event that will remove the request after we've returned
+ // to the event loop. This mimics the way it is called by nsIChannel
+ // implementations.
+
+ // Revoke any pending restore (just in case)
+ NS_ASSERTION(!mRestorePresentationEvent.IsPending(),
+ "should only have one RestorePresentationEvent");
+ mRestorePresentationEvent.Revoke();
+
+ RefPtr<RestorePresentationEvent> evt = new RestorePresentationEvent(this);
+ nsresult rv = NS_DispatchToCurrentThread(evt);
+ if (NS_SUCCEEDED(rv)) {
+ mRestorePresentationEvent = evt.get();
+ // The rest of the restore processing will happen on our event
+ // callback.
+ *aRestoring = true;
+ }
+
+ return rv;
+}
+
+namespace {
+class MOZ_STACK_CLASS PresentationEventForgetter
+{
+public:
+ explicit PresentationEventForgetter(
+ nsRevocableEventPtr<nsDocShell::RestorePresentationEvent>&
+ aRestorePresentationEvent)
+ : mRestorePresentationEvent(aRestorePresentationEvent)
+ , mEvent(aRestorePresentationEvent.get())
+ {
+ }
+
+ ~PresentationEventForgetter()
+ {
+ Forget();
+ }
+
+ void Forget()
+ {
+ if (mRestorePresentationEvent.get() == mEvent) {
+ mRestorePresentationEvent.Forget();
+ mEvent = nullptr;
+ }
+ }
+
+private:
+ nsRevocableEventPtr<nsDocShell::RestorePresentationEvent>&
+ mRestorePresentationEvent;
+ RefPtr<nsDocShell::RestorePresentationEvent> mEvent;
+};
+
+} // namespace
+
+nsresult
+nsDocShell::RestoreFromHistory()
+{
+ MOZ_ASSERT(mRestorePresentationEvent.IsPending());
+ PresentationEventForgetter forgetter(mRestorePresentationEvent);
+
+ // This section of code follows the same ordering as CreateContentViewer.
+ if (!mLSHE) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIContentViewer> viewer;
+ mLSHE->GetContentViewer(getter_AddRefs(viewer));
+ if (!viewer) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (mSavingOldViewer) {
+ // We determined that it was safe to cache the document presentation
+ // at the time we initiated the new load. We need to check whether
+ // it's still safe to do so, since there may have been DOM mutations
+ // or new requests initiated.
+ nsCOMPtr<nsIDOMDocument> domDoc;
+ viewer->GetDOMDocument(getter_AddRefs(domDoc));
+ nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
+ nsIRequest* request = nullptr;
+ if (doc) {
+ request = doc->GetChannel();
+ }
+ mSavingOldViewer = CanSavePresentation(mLoadType, request, doc);
+ }
+
+ nsCOMPtr<nsIContentViewer> oldCv(mContentViewer);
+ nsCOMPtr<nsIContentViewer> newCv(viewer);
+ int32_t minFontSize = 0;
+ float textZoom = 1.0f;
+ float pageZoom = 1.0f;
+ float overrideDPPX = 0.0f;
+
+ bool styleDisabled = false;
+ if (oldCv && newCv) {
+ oldCv->GetMinFontSize(&minFontSize);
+ oldCv->GetTextZoom(&textZoom);
+ oldCv->GetFullZoom(&pageZoom);
+ oldCv->GetOverrideDPPX(&overrideDPPX);
+ oldCv->GetAuthorStyleDisabled(&styleDisabled);
+ }
+
+ // Protect against mLSHE going away via a load triggered from
+ // pagehide or unload.
+ nsCOMPtr<nsISHEntry> origLSHE = mLSHE;
+
+ // Make sure to blow away our mLoadingURI just in case. No loads
+ // from inside this pagehide.
+ mLoadingURI = nullptr;
+
+ // Notify the old content viewer that it's being hidden.
+ FirePageHideNotification(!mSavingOldViewer);
+
+ // If mLSHE was changed as a result of the pagehide event, then
+ // something else was loaded. Don't finish restoring.
+ if (mLSHE != origLSHE) {
+ return NS_OK;
+ }
+
+ // Add the request to our load group. We do this before swapping out
+ // the content viewers so that consumers of STATE_START can access
+ // the old document. We only deal with the toplevel load at this time --
+ // to be consistent with normal document loading, subframes cannot start
+ // loading until after data arrives, which is after STATE_START completes.
+
+ RefPtr<RestorePresentationEvent> currentPresentationRestoration =
+ mRestorePresentationEvent.get();
+ Stop();
+ // Make sure we're still restoring the same presentation.
+ // If we aren't, docshell is in process doing another load already.
+ NS_ENSURE_STATE(currentPresentationRestoration ==
+ mRestorePresentationEvent.get());
+ BeginRestore(viewer, true);
+ NS_ENSURE_STATE(currentPresentationRestoration ==
+ mRestorePresentationEvent.get());
+ forgetter.Forget();
+
+ // Set mFiredUnloadEvent = false so that the unload handler for the
+ // *new* document will fire.
+ mFiredUnloadEvent = false;
+
+ mURIResultedInDocument = true;
+ nsCOMPtr<nsISHistory> rootSH;
+ GetRootSessionHistory(getter_AddRefs(rootSH));
+ if (rootSH) {
+ nsCOMPtr<nsISHistoryInternal> hist = do_QueryInterface(rootSH);
+ rootSH->GetIndex(&mPreviousTransIndex);
+ hist->UpdateIndex();
+ rootSH->GetIndex(&mLoadedTransIndex);
+#ifdef DEBUG_PAGE_CACHE
+ printf("Previous index: %d, Loaded index: %d\n\n", mPreviousTransIndex,
+ mLoadedTransIndex);
+#endif
+ }
+
+ // Rather than call Embed(), we will retrieve the viewer from the session
+ // history entry and swap it in.
+ // XXX can we refactor this so that we can just call Embed()?
+ PersistLayoutHistoryState();
+ nsresult rv;
+ if (mContentViewer) {
+ if (mSavingOldViewer && NS_FAILED(CaptureState())) {
+ if (mOSHE) {
+ mOSHE->SyncPresentationState();
+ }
+ mSavingOldViewer = false;
+ }
+ }
+
+ mSavedRefreshURIList = nullptr;
+
+ // In cases where we use a transient about:blank viewer between loads,
+ // we never show the transient viewer, so _its_ previous viewer is never
+ // unhooked from the view hierarchy. Destroy any such previous viewer now,
+ // before we grab the root view sibling, so that we don't grab a view
+ // that's about to go away.
+
+ if (mContentViewer) {
+ nsCOMPtr<nsIContentViewer> previousViewer;
+ mContentViewer->GetPreviousViewer(getter_AddRefs(previousViewer));
+ if (previousViewer) {
+ mContentViewer->SetPreviousViewer(nullptr);
+ previousViewer->Destroy();
+ }
+ }
+
+ // Save off the root view's parent and sibling so that we can insert the
+ // new content viewer's root view at the same position. Also save the
+ // bounds of the root view's widget.
+
+ nsView* rootViewSibling = nullptr;
+ nsView* rootViewParent = nullptr;
+ nsIntRect newBounds(0, 0, 0, 0);
+
+ nsCOMPtr<nsIPresShell> oldPresShell = GetPresShell();
+ if (oldPresShell) {
+ nsViewManager* vm = oldPresShell->GetViewManager();
+ if (vm) {
+ nsView* oldRootView = vm->GetRootView();
+
+ if (oldRootView) {
+ rootViewSibling = oldRootView->GetNextSibling();
+ rootViewParent = oldRootView->GetParent();
+
+ mContentViewer->GetBounds(newBounds);
+ }
+ }
+ }
+
+ nsCOMPtr<nsIContent> container;
+ nsCOMPtr<nsIDocument> sibling;
+ if (rootViewParent && rootViewParent->GetParent()) {
+ nsIFrame* frame = rootViewParent->GetParent()->GetFrame();
+ container = frame ? frame->GetContent() : nullptr;
+ }
+ if (rootViewSibling) {
+ nsIFrame* frame = rootViewSibling->GetFrame();
+ sibling =
+ frame ? frame->PresContext()->PresShell()->GetDocument() : nullptr;
+ }
+
+ // Transfer ownership to mContentViewer. By ensuring that either the
+ // docshell or the session history, but not both, have references to the
+ // content viewer, we prevent the viewer from being torn down after
+ // Destroy() is called.
+
+ if (mContentViewer) {
+ mContentViewer->Close(mSavingOldViewer ? mOSHE.get() : nullptr);
+ viewer->SetPreviousViewer(mContentViewer);
+ }
+ if (mOSHE && (!mContentViewer || !mSavingOldViewer)) {
+ // We don't plan to save a viewer in mOSHE; tell it to drop
+ // any other state it's holding.
+ mOSHE->SyncPresentationState();
+ }
+
+ // Order the mContentViewer setup just like Embed does.
+ mContentViewer = nullptr;
+
+ // Now that we're about to switch documents, forget all of our children.
+ // Note that we cached them as needed up in CaptureState above.
+ DestroyChildren();
+
+ mContentViewer.swap(viewer);
+
+ // Grab all of the related presentation from the SHEntry now.
+ // Clearing the viewer from the SHEntry will clear all of this state.
+ nsCOMPtr<nsISupports> windowState;
+ mLSHE->GetWindowState(getter_AddRefs(windowState));
+ mLSHE->SetWindowState(nullptr);
+
+ bool sticky;
+ mLSHE->GetSticky(&sticky);
+
+ nsCOMPtr<nsIDOMDocument> domDoc;
+ mContentViewer->GetDOMDocument(getter_AddRefs(domDoc));
+
+ nsCOMArray<nsIDocShellTreeItem> childShells;
+ int32_t i = 0;
+ nsCOMPtr<nsIDocShellTreeItem> child;
+ while (NS_SUCCEEDED(mLSHE->ChildShellAt(i++, getter_AddRefs(child))) &&
+ child) {
+ childShells.AppendObject(child);
+ }
+
+ // get the previous content viewer size
+ nsIntRect oldBounds(0, 0, 0, 0);
+ mLSHE->GetViewerBounds(oldBounds);
+
+ // Restore the refresh URI list. The refresh timers will be restarted
+ // when EndPageLoad() is called.
+ nsCOMPtr<nsIMutableArray> refreshURIList;
+ mLSHE->GetRefreshURIList(getter_AddRefs(refreshURIList));
+
+ // Reattach to the window object.
+ mIsRestoringDocument = true; // for MediaDocument::BecomeInteractive
+ rv = mContentViewer->Open(windowState, mLSHE);
+ mIsRestoringDocument = false;
+
+ // Hack to keep nsDocShellEditorData alive across the
+ // SetContentViewer(nullptr) call below.
+ nsAutoPtr<nsDocShellEditorData> data(mLSHE->ForgetEditorData());
+
+ // Now remove it from the cached presentation.
+ mLSHE->SetContentViewer(nullptr);
+ mEODForCurrentDocument = false;
+
+ mLSHE->SetEditorData(data.forget());
+
+#ifdef DEBUG
+ {
+ nsCOMPtr<nsIMutableArray> refreshURIs;
+ mLSHE->GetRefreshURIList(getter_AddRefs(refreshURIs));
+ nsCOMPtr<nsIDocShellTreeItem> childShell;
+ mLSHE->ChildShellAt(0, getter_AddRefs(childShell));
+ NS_ASSERTION(!refreshURIs && !childShell,
+ "SHEntry should have cleared presentation state");
+ }
+#endif
+
+ // Restore the sticky state of the viewer. The viewer has set this state
+ // on the history entry in Destroy() just before marking itself non-sticky,
+ // to avoid teardown of the presentation.
+ mContentViewer->SetSticky(sticky);
+
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // mLSHE is now our currently-loaded document.
+ SetHistoryEntry(&mOSHE, mLSHE);
+
+ // XXX special wyciwyg handling in Embed()?
+
+ // We aren't going to restore any items from the LayoutHistoryState,
+ // but we don't want them to stay around in case the page is reloaded.
+ SetLayoutHistoryState(nullptr);
+
+ // This is the end of our Embed() replacement
+
+ mSavingOldViewer = false;
+ mEODForCurrentDocument = false;
+
+ // Tell the event loop to favor plevents over user events, see comments
+ // in CreateContentViewer.
+ if (++gNumberOfDocumentsLoading == 1) {
+ FavorPerformanceHint(true);
+ }
+
+ if (oldCv && newCv) {
+ newCv->SetMinFontSize(minFontSize);
+ newCv->SetTextZoom(textZoom);
+ newCv->SetFullZoom(pageZoom);
+ newCv->SetOverrideDPPX(overrideDPPX);
+ newCv->SetAuthorStyleDisabled(styleDisabled);
+ }
+
+ nsCOMPtr<nsIDocument> document = do_QueryInterface(domDoc);
+ if (document) {
+ RefPtr<nsDocShell> parent = GetParentDocshell();
+ if (parent) {
+ nsCOMPtr<nsIDocument> d = parent->GetDocument();
+ if (d) {
+ if (d->EventHandlingSuppressed()) {
+ document->SuppressEventHandling(nsIDocument::eEvents,
+ d->EventHandlingSuppressed());
+ }
+
+ // Ick, it'd be nicer to not rewalk all of the subdocs here.
+ if (d->AnimationsPaused()) {
+ document->SuppressEventHandling(nsIDocument::eAnimationsOnly,
+ d->AnimationsPaused());
+ }
+ }
+ }
+
+ // Use the uri from the mLSHE we had when we entered this function
+ // (which need not match the document's URI if anchors are involved),
+ // since that's the history entry we're loading. Note that if we use
+ // origLSHE we don't have to worry about whether the entry in question
+ // is still mLSHE or whether it's now mOSHE.
+ nsCOMPtr<nsIURI> uri;
+ origLSHE->GetURI(getter_AddRefs(uri));
+ SetCurrentURI(uri, document->GetChannel(), true, 0);
+ }
+
+ // This is the end of our CreateContentViewer() replacement.
+ // Now we simulate a load. First, we restore the state of the javascript
+ // window object.
+ nsCOMPtr<nsPIDOMWindowOuter> privWin = GetWindow();
+ NS_ASSERTION(privWin, "could not get nsPIDOMWindow interface");
+
+ // Now, dispatch a title change event which would happen as the
+ // <head> is parsed.
+ document->NotifyPossibleTitleChange(false);
+
+ // Now we simulate appending child docshells for subframes.
+ for (i = 0; i < childShells.Count(); ++i) {
+ nsIDocShellTreeItem* childItem = childShells.ObjectAt(i);
+ nsCOMPtr<nsIDocShell> childShell = do_QueryInterface(childItem);
+
+ // Make sure to not clobber the state of the child. Since AddChild
+ // always clobbers it, save it off first.
+ bool allowPlugins;
+ childShell->GetAllowPlugins(&allowPlugins);
+
+ bool allowJavascript;
+ childShell->GetAllowJavascript(&allowJavascript);
+
+ bool allowRedirects;
+ childShell->GetAllowMetaRedirects(&allowRedirects);
+
+ bool allowSubframes;
+ childShell->GetAllowSubframes(&allowSubframes);
+
+ bool allowImages;
+ childShell->GetAllowImages(&allowImages);
+
+ bool allowMedia = childShell->GetAllowMedia();
+
+ bool allowDNSPrefetch;
+ childShell->GetAllowDNSPrefetch(&allowDNSPrefetch);
+
+ bool allowContentRetargeting = childShell->GetAllowContentRetargeting();
+ bool allowContentRetargetingOnChildren =
+ childShell->GetAllowContentRetargetingOnChildren();
+
+ uint32_t defaultLoadFlags;
+ childShell->GetDefaultLoadFlags(&defaultLoadFlags);
+
+ // this.AddChild(child) calls child.SetDocLoaderParent(this), meaning that
+ // the child inherits our state. Among other things, this means that the
+ // child inherits our mIsActive, mIsPrerendered and mPrivateBrowsingId,
+ // which is what we want.
+ AddChild(childItem);
+
+ childShell->SetAllowPlugins(allowPlugins);
+ childShell->SetAllowJavascript(allowJavascript);
+ childShell->SetAllowMetaRedirects(allowRedirects);
+ childShell->SetAllowSubframes(allowSubframes);
+ childShell->SetAllowImages(allowImages);
+ childShell->SetAllowMedia(allowMedia);
+ childShell->SetAllowDNSPrefetch(allowDNSPrefetch);
+ childShell->SetAllowContentRetargeting(allowContentRetargeting);
+ childShell->SetAllowContentRetargetingOnChildren(
+ allowContentRetargetingOnChildren);
+ childShell->SetDefaultLoadFlags(defaultLoadFlags);
+
+ rv = childShell->BeginRestore(nullptr, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // Make sure to restore the window state after adding the child shells back
+ // to the tree. This is necessary for Thaw() and Resume() to propagate
+ // properly.
+ rv = privWin->RestoreWindowState(windowState);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIPresShell> shell = GetPresShell();
+
+ // We may be displayed on a different monitor (or in a different
+ // HiDPI mode) than when we got into the history list. So we need
+ // to check if this has happened. See bug 838239.
+
+ // Because the prescontext normally handles resolution changes via
+ // a runnable (see nsPresContext::UIResolutionChanged), its device
+ // context won't be -immediately- updated as a result of calling
+ // shell->BackingScaleFactorChanged().
+
+ // But we depend on that device context when adjusting the view size
+ // via mContentViewer->SetBounds(newBounds) below. So we need to
+ // explicitly tell it to check for changed resolution here.
+ if (shell && shell->GetPresContext()->DeviceContext()->CheckDPIChange()) {
+ shell->BackingScaleFactorChanged();
+ }
+
+ nsViewManager* newVM = shell ? shell->GetViewManager() : nullptr;
+ nsView* newRootView = newVM ? newVM->GetRootView() : nullptr;
+
+ // Insert the new root view at the correct location in the view tree.
+ if (container) {
+ nsSubDocumentFrame* subDocFrame =
+ do_QueryFrame(container->GetPrimaryFrame());
+ rootViewParent = subDocFrame ? subDocFrame->EnsureInnerView() : nullptr;
+ } else {
+ rootViewParent = nullptr;
+ }
+ if (sibling &&
+ sibling->GetShell() &&
+ sibling->GetShell()->GetViewManager()) {
+ rootViewSibling = sibling->GetShell()->GetViewManager()->GetRootView();
+ } else {
+ rootViewSibling = nullptr;
+ }
+ if (rootViewParent && newRootView &&
+ newRootView->GetParent() != rootViewParent) {
+ nsViewManager* parentVM = rootViewParent->GetViewManager();
+ if (parentVM) {
+ // InsertChild(parent, child, sib, true) inserts the child after
+ // sib in content order, which is before sib in view order. BUT
+ // when sib is null it inserts at the end of the the document
+ // order, i.e., first in view order. But when oldRootSibling is
+ // null, the old root as at the end of the view list --- last in
+ // content order --- and we want to call InsertChild(parent, child,
+ // nullptr, false) in that case.
+ parentVM->InsertChild(rootViewParent, newRootView,
+ rootViewSibling,
+ rootViewSibling ? true : false);
+
+ NS_ASSERTION(newRootView->GetNextSibling() == rootViewSibling,
+ "error in InsertChild");
+ }
+ }
+
+ nsCOMPtr<nsPIDOMWindowInner> privWinInner = privWin->GetCurrentInnerWindow();
+
+ // If parent is suspended, increase suspension count.
+ // This can't be done as early as event suppression since this
+ // depends on docshell tree.
+ privWinInner->SyncStateFromParentWindow();
+
+ // Now that all of the child docshells have been put into place, we can
+ // restart the timers for the window and all of the child frames.
+ privWinInner->Resume();
+
+ // Restore the refresh URI list. The refresh timers will be restarted
+ // when EndPageLoad() is called.
+ mRefreshURIList = refreshURIList;
+
+ // Meta-refresh timers have been restarted for this shell, but not
+ // for our children. Walk the child shells and restart their timers.
+ nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
+ while (iter.HasMore()) {
+ nsCOMPtr<nsIDocShell> child = do_QueryObject(iter.GetNext());
+ if (child) {
+ child->ResumeRefreshURIs();
+ }
+ }
+
+ // Make sure this presentation is the same size as the previous
+ // presentation. If this is not the same size we showed it at last time,
+ // then we need to resize the widget.
+
+ // XXXbryner This interacts poorly with Firefox's infobar. If the old
+ // presentation had the infobar visible, then we will resize the new
+ // presentation to that smaller size. However, firing the locationchanged
+ // event will hide the infobar, which will immediately resize the window
+ // back to the larger size. A future optimization might be to restore
+ // the presentation at the "wrong" size, then fire the locationchanged
+ // event and check whether the docshell's new size is the same as the
+ // cached viewer size (skipping the resize if they are equal).
+
+ if (newRootView) {
+ if (!newBounds.IsEmpty() && !newBounds.IsEqualEdges(oldBounds)) {
+#ifdef DEBUG_PAGE_CACHE
+ printf("resize widget(%d, %d, %d, %d)\n", newBounds.x,
+ newBounds.y, newBounds.width, newBounds.height);
+#endif
+ mContentViewer->SetBounds(newBounds);
+ } else {
+ nsIScrollableFrame* rootScrollFrame =
+ shell->GetRootScrollFrameAsScrollableExternal();
+ if (rootScrollFrame) {
+ rootScrollFrame->PostScrolledAreaEventForCurrentArea();
+ }
+ }
+ }
+
+ // The FinishRestore call below can kill these, null them out so we don't
+ // have invalid pointer lying around.
+ newRootView = rootViewSibling = rootViewParent = nullptr;
+ newVM = nullptr;
+
+ // Simulate the completion of the load.
+ nsDocShell::FinishRestore();
+
+ // Restart plugins, and paint the content.
+ if (shell) {
+ shell->Thaw();
+ }
+
+ return privWin->FireDelayedDOMEvents();
+}
+
+nsresult
+nsDocShell::CreateContentViewer(const nsACString& aContentType,
+ nsIRequest* aRequest,
+ nsIStreamListener** aContentHandler)
+{
+ *aContentHandler = nullptr;
+
+ // Can we check the content type of the current content viewer
+ // and reuse it without destroying it and re-creating it?
+
+ NS_ASSERTION(mLoadGroup, "Someone ignored return from Init()?");
+
+ // Instantiate the content viewer object
+ nsCOMPtr<nsIContentViewer> viewer;
+ nsresult rv = NewContentViewerObj(aContentType, aRequest, mLoadGroup,
+ aContentHandler, getter_AddRefs(viewer));
+
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // Notify the current document that it is about to be unloaded!!
+ //
+ // It is important to fire the unload() notification *before* any state
+ // is changed within the DocShell - otherwise, javascript will get the
+ // wrong information :-(
+ //
+
+ if (mSavingOldViewer) {
+ // We determined that it was safe to cache the document presentation
+ // at the time we initiated the new load. We need to check whether
+ // it's still safe to do so, since there may have been DOM mutations
+ // or new requests initiated.
+ nsCOMPtr<nsIDOMDocument> domDoc;
+ viewer->GetDOMDocument(getter_AddRefs(domDoc));
+ nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
+ mSavingOldViewer = CanSavePresentation(mLoadType, aRequest, doc);
+ }
+
+ NS_ASSERTION(!mLoadingURI, "Re-entering unload?");
+
+ nsCOMPtr<nsIChannel> aOpenedChannel = do_QueryInterface(aRequest);
+ if (aOpenedChannel) {
+ aOpenedChannel->GetURI(getter_AddRefs(mLoadingURI));
+ }
+ FirePageHideNotification(!mSavingOldViewer);
+ mLoadingURI = nullptr;
+
+ // Set mFiredUnloadEvent = false so that the unload handler for the
+ // *new* document will fire.
+ mFiredUnloadEvent = false;
+
+ // we've created a new document so go ahead and call
+ // OnLoadingSite(), but don't fire OnLocationChange()
+ // notifications before we've called Embed(). See bug 284993.
+ mURIResultedInDocument = true;
+
+ if (mLoadType == LOAD_ERROR_PAGE) {
+ // We need to set the SH entry and our current URI here and not
+ // at the moment we load the page. We want the same behavior
+ // of Stop() as for a normal page load. See bug 514232 for details.
+
+ // Revert mLoadType to load type to state the page load failed,
+ // following function calls need it.
+ mLoadType = mFailedLoadType;
+
+ nsCOMPtr<nsIChannel> failedChannel = mFailedChannel;
+
+ nsIDocument* doc = viewer->GetDocument();
+ if (doc) {
+ doc->SetFailedChannel(failedChannel);
+ }
+
+ // Make sure we have a URI to set currentURI.
+ nsCOMPtr<nsIURI> failedURI;
+ if (failedChannel) {
+ NS_GetFinalChannelURI(failedChannel, getter_AddRefs(failedURI));
+ }
+
+ if (!failedURI) {
+ failedURI = mFailedURI;
+ }
+ if (!failedURI) {
+ // We need a URI object to store a session history entry, so make up a URI
+ NS_NewURI(getter_AddRefs(failedURI), "about:blank");
+ }
+
+ // When we don't have failedURI, something wrong will happen. See
+ // bug 291876.
+ MOZ_ASSERT(failedURI, "We don't have a URI for history APIs.");
+
+ mFailedChannel = nullptr;
+ mFailedURI = nullptr;
+
+ // Create an shistory entry for the old load.
+ if (failedURI) {
+ bool errorOnLocationChangeNeeded = OnNewURI(
+ failedURI, failedChannel, nullptr, nullptr, mLoadType, false, false, false);
+
+ if (errorOnLocationChangeNeeded) {
+ FireOnLocationChange(this, failedChannel, failedURI,
+ LOCATION_CHANGE_ERROR_PAGE);
+ }
+ }
+
+ // Be sure to have a correct mLSHE, it may have been cleared by
+ // EndPageLoad. See bug 302115.
+ if (mSessionHistory && !mLSHE) {
+ int32_t idx;
+ mSessionHistory->GetRequestedIndex(&idx);
+ if (idx == -1) {
+ mSessionHistory->GetIndex(&idx);
+ }
+ mSessionHistory->GetEntryAtIndex(idx, false, getter_AddRefs(mLSHE));
+ }
+
+ mLoadType = LOAD_ERROR_PAGE;
+ }
+
+ bool onLocationChangeNeeded = OnLoadingSite(aOpenedChannel, false);
+
+ // let's try resetting the load group if we need to...
+ nsCOMPtr<nsILoadGroup> currentLoadGroup;
+ NS_ENSURE_SUCCESS(
+ aOpenedChannel->GetLoadGroup(getter_AddRefs(currentLoadGroup)),
+ NS_ERROR_FAILURE);
+
+ if (currentLoadGroup != mLoadGroup) {
+ nsLoadFlags loadFlags = 0;
+
+ // Cancel any URIs that are currently loading...
+ // XXX: Need to do this eventually Stop();
+ //
+ // Retarget the document to this loadgroup...
+ //
+ /* First attach the channel to the right loadgroup
+ * and then remove from the old loadgroup. This
+ * puts the notifications in the right order and
+ * we don't null-out mLSHE in OnStateChange() for
+ * all redirected urls
+ */
+ aOpenedChannel->SetLoadGroup(mLoadGroup);
+
+ // Mark the channel as being a document URI...
+ aOpenedChannel->GetLoadFlags(&loadFlags);
+ loadFlags |= nsIChannel::LOAD_DOCUMENT_URI;
+
+ aOpenedChannel->SetLoadFlags(loadFlags);
+
+ mLoadGroup->AddRequest(aRequest, nullptr);
+ if (currentLoadGroup) {
+ currentLoadGroup->RemoveRequest(aRequest, nullptr, NS_BINDING_RETARGETED);
+ }
+
+ // Update the notification callbacks, so that progress and
+ // status information are sent to the right docshell...
+ aOpenedChannel->SetNotificationCallbacks(this);
+ }
+
+ NS_ENSURE_SUCCESS(Embed(viewer, "", nullptr), NS_ERROR_FAILURE);
+
+ mSavedRefreshURIList = nullptr;
+ mSavingOldViewer = false;
+ mEODForCurrentDocument = false;
+
+ // if this document is part of a multipart document,
+ // the ID can be used to distinguish it from the other parts.
+ nsCOMPtr<nsIMultiPartChannel> multiPartChannel(do_QueryInterface(aRequest));
+ if (multiPartChannel) {
+ nsCOMPtr<nsIPresShell> shell = GetPresShell();
+ if (NS_SUCCEEDED(rv) && shell) {
+ nsIDocument* doc = shell->GetDocument();
+ if (doc) {
+ uint32_t partID;
+ multiPartChannel->GetPartID(&partID);
+ doc->SetPartID(partID);
+ }
+ }
+ }
+
+ // Give hint to native plevent dispatch mechanism. If a document
+ // is loading the native plevent dispatch mechanism should favor
+ // performance over normal native event dispatch priorities.
+ if (++gNumberOfDocumentsLoading == 1) {
+ // Hint to favor performance for the plevent notification mechanism.
+ // We want the pages to load as fast as possible even if its means
+ // native messages might be starved.
+ FavorPerformanceHint(true);
+ }
+
+ if (onLocationChangeNeeded) {
+ FireOnLocationChange(this, aRequest, mCurrentURI, 0);
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsDocShell::NewContentViewerObj(const nsACString& aContentType,
+ nsIRequest* aRequest, nsILoadGroup* aLoadGroup,
+ nsIStreamListener** aContentHandler,
+ nsIContentViewer** aViewer)
+{
+ nsCOMPtr<nsIChannel> aOpenedChannel = do_QueryInterface(aRequest);
+
+ nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
+ nsContentUtils::FindInternalContentViewer(aContentType);
+ if (!docLoaderFactory) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Now create an instance of the content viewer nsLayoutDLF makes the
+ // determination if it should be a "view-source" instead of "view"
+ nsresult rv = docLoaderFactory->CreateInstance("view",
+ aOpenedChannel,
+ aLoadGroup, aContentType,
+ this,
+ nullptr,
+ aContentHandler,
+ aViewer);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ (*aViewer)->SetContainer(this);
+ return NS_OK;
+}
+
+nsresult
+nsDocShell::SetupNewViewer(nsIContentViewer* aNewViewer)
+{
+ //
+ // Copy content viewer state from previous or parent content viewer.
+ //
+ // The following logic is mirrored in nsHTMLDocument::StartDocumentLoad!
+ //
+ // Do NOT to maintain a reference to the old content viewer outside
+ // of this "copying" block, or it will not be destroyed until the end of
+ // this routine and all <SCRIPT>s and event handlers fail! (bug 20315)
+ //
+ // In this block of code, if we get an error result, we return it
+ // but if we get a null pointer, that's perfectly legal for parent
+ // and parentContentViewer.
+ //
+
+ int32_t x = 0;
+ int32_t y = 0;
+ int32_t cx = 0;
+ int32_t cy = 0;
+
+ // This will get the size from the current content viewer or from the
+ // Init settings
+ DoGetPositionAndSize(&x, &y, &cx, &cy);
+
+ nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
+ NS_ENSURE_SUCCESS(GetSameTypeParent(getter_AddRefs(parentAsItem)),
+ NS_ERROR_FAILURE);
+ nsCOMPtr<nsIDocShell> parent(do_QueryInterface(parentAsItem));
+
+ nsAutoCString forceCharset;
+ nsAutoCString hintCharset;
+ int32_t hintCharsetSource;
+ int32_t minFontSize;
+ float textZoom;
+ float pageZoom;
+ float overrideDPPX;
+ bool styleDisabled;
+ // |newMUDV| also serves as a flag to set the data from the above vars
+ nsCOMPtr<nsIContentViewer> newCv;
+
+ if (mContentViewer || parent) {
+ nsCOMPtr<nsIContentViewer> oldCv;
+ if (mContentViewer) {
+ // Get any interesting state from old content viewer
+ // XXX: it would be far better to just reuse the document viewer ,
+ // since we know we're just displaying the same document as before
+ oldCv = mContentViewer;
+
+ // Tell the old content viewer to hibernate in session history when
+ // it is destroyed.
+
+ if (mSavingOldViewer && NS_FAILED(CaptureState())) {
+ if (mOSHE) {
+ mOSHE->SyncPresentationState();
+ }
+ mSavingOldViewer = false;
+ }
+ } else {
+ // No old content viewer, so get state from parent's content viewer
+ parent->GetContentViewer(getter_AddRefs(oldCv));
+ }
+
+ if (oldCv) {
+ newCv = aNewViewer;
+ if (newCv) {
+ NS_ENSURE_SUCCESS(oldCv->GetForceCharacterSet(forceCharset),
+ NS_ERROR_FAILURE);
+ NS_ENSURE_SUCCESS(oldCv->GetHintCharacterSet(hintCharset),
+ NS_ERROR_FAILURE);
+ NS_ENSURE_SUCCESS(oldCv->GetHintCharacterSetSource(&hintCharsetSource),
+ NS_ERROR_FAILURE);
+ NS_ENSURE_SUCCESS(oldCv->GetMinFontSize(&minFontSize),
+ NS_ERROR_FAILURE);
+ NS_ENSURE_SUCCESS(oldCv->GetTextZoom(&textZoom),
+ NS_ERROR_FAILURE);
+ NS_ENSURE_SUCCESS(oldCv->GetFullZoom(&pageZoom),
+ NS_ERROR_FAILURE);
+ NS_ENSURE_SUCCESS(oldCv->GetOverrideDPPX(&overrideDPPX),
+ NS_ERROR_FAILURE);
+ NS_ENSURE_SUCCESS(oldCv->GetAuthorStyleDisabled(&styleDisabled),
+ NS_ERROR_FAILURE);
+ }
+ }
+ }
+
+ nscolor bgcolor = NS_RGBA(0, 0, 0, 0);
+ // Ensure that the content viewer is destroyed *after* the GC - bug 71515
+ nsCOMPtr<nsIContentViewer> contentViewer = mContentViewer;
+ if (contentViewer) {
+ // Stop any activity that may be happening in the old document before
+ // releasing it...
+ contentViewer->Stop();
+
+ // Try to extract the canvas background color from the old
+ // presentation shell, so we can use it for the next document.
+ nsCOMPtr<nsIPresShell> shell;
+ contentViewer->GetPresShell(getter_AddRefs(shell));
+
+ if (shell) {
+ bgcolor = shell->GetCanvasBackground();
+ }
+
+ contentViewer->Close(mSavingOldViewer ? mOSHE.get() : nullptr);
+ aNewViewer->SetPreviousViewer(contentViewer);
+ }
+ if (mOSHE && (!mContentViewer || !mSavingOldViewer)) {
+ // We don't plan to save a viewer in mOSHE; tell it to drop
+ // any other state it's holding.
+ mOSHE->SyncPresentationState();
+ }
+
+ mContentViewer = nullptr;
+
+ // Now that we're about to switch documents, forget all of our children.
+ // Note that we cached them as needed up in CaptureState above.
+ DestroyChildren();
+
+ mContentViewer = aNewViewer;
+
+ nsCOMPtr<nsIWidget> widget;
+ NS_ENSURE_SUCCESS(GetMainWidget(getter_AddRefs(widget)), NS_ERROR_FAILURE);
+
+ nsIntRect bounds(x, y, cx, cy);
+
+ mContentViewer->SetNavigationTiming(mTiming);
+
+ if (NS_FAILED(mContentViewer->Init(widget, bounds))) {
+ mContentViewer = nullptr;
+ NS_WARNING("ContentViewer Initialization failed");
+ return NS_ERROR_FAILURE;
+ }
+
+ // If we have old state to copy, set the old state onto the new content
+ // viewer
+ if (newCv) {
+ NS_ENSURE_SUCCESS(newCv->SetForceCharacterSet(forceCharset),
+ NS_ERROR_FAILURE);
+ NS_ENSURE_SUCCESS(newCv->SetHintCharacterSet(hintCharset),
+ NS_ERROR_FAILURE);
+ NS_ENSURE_SUCCESS(newCv->SetHintCharacterSetSource(hintCharsetSource),
+ NS_ERROR_FAILURE);
+ NS_ENSURE_SUCCESS(newCv->SetMinFontSize(minFontSize),
+ NS_ERROR_FAILURE);
+ NS_ENSURE_SUCCESS(newCv->SetTextZoom(textZoom),
+ NS_ERROR_FAILURE);
+ NS_ENSURE_SUCCESS(newCv->SetFullZoom(pageZoom),
+ NS_ERROR_FAILURE);
+ NS_ENSURE_SUCCESS(newCv->SetOverrideDPPX(overrideDPPX),
+ NS_ERROR_FAILURE);
+ NS_ENSURE_SUCCESS(newCv->SetAuthorStyleDisabled(styleDisabled),
+ NS_ERROR_FAILURE);
+ }
+
+ // Stuff the bgcolor from the old pres shell into the new
+ // pres shell. This improves page load continuity.
+ nsCOMPtr<nsIPresShell> shell;
+ mContentViewer->GetPresShell(getter_AddRefs(shell));
+
+ if (shell) {
+ shell->SetCanvasBackground(bgcolor);
+ }
+
+ // XXX: It looks like the LayoutState gets restored again in Embed()
+ // right after the call to SetupNewViewer(...)
+
+ // We don't show the mContentViewer yet, since we want to draw the old page
+ // until we have enough of the new page to show. Just return with the new
+ // viewer still set to hidden.
+
+ return NS_OK;
+}
+
+nsresult
+nsDocShell::SetDocCurrentStateObj(nsISHEntry* aShEntry)
+{
+ NS_ENSURE_STATE(mContentViewer);
+ nsCOMPtr<nsIDocument> document = GetDocument();
+ NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
+
+ nsCOMPtr<nsIStructuredCloneContainer> scContainer;
+ if (aShEntry) {
+ nsresult rv = aShEntry->GetStateData(getter_AddRefs(scContainer));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // If aShEntry is null, just set the document's state object to null.
+ }
+
+ // It's OK for scContainer too be null here; that just means there's no
+ // state data associated with this history entry.
+ document->SetStateObject(scContainer);
+
+ return NS_OK;
+}
+
+nsresult
+nsDocShell::CheckLoadingPermissions()
+{
+ // This method checks whether the caller may load content into
+ // this docshell. Even though we've done our best to hide windows
+ // from code that doesn't have the right to access them, it's
+ // still possible for an evil site to open a window and access
+ // frames in the new window through window.frames[] (which is
+ // allAccess for historic reasons), so we still need to do this
+ // check on load.
+ nsresult rv = NS_OK;
+
+ if (!gValidateOrigin || !IsFrame()) {
+ // Origin validation was turned off, or we're not a frame.
+ // Permit all loads.
+
+ return rv;
+ }
+
+ // Note - The check for a current JSContext here isn't necessarily sensical.
+ // It's just designed to preserve the old semantics during a mass-conversion
+ // patch.
+ if (!nsContentUtils::GetCurrentJSContext()) {
+ return NS_OK;
+ }
+
+ // Check if the caller is from the same origin as this docshell,
+ // or any of its ancestors.
+ nsCOMPtr<nsIDocShellTreeItem> item(this);
+ do {
+ nsCOMPtr<nsIScriptGlobalObject> sgo = do_GetInterface(item);
+ nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(sgo));
+
+ nsIPrincipal* p;
+ if (!sop || !(p = sop->GetPrincipal())) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ if (nsContentUtils::SubjectPrincipal()->Subsumes(p)) {
+ // Same origin, permit load
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIDocShellTreeItem> tmp;
+ item->GetSameTypeParent(getter_AddRefs(tmp));
+ item.swap(tmp);
+ } while (item);
+
+ return NS_ERROR_DOM_PROP_ACCESS_DENIED;
+}
+
+//*****************************************************************************
+// nsDocShell: Site Loading
+//*****************************************************************************
+
+namespace {
+
+#ifdef MOZ_PLACES
+// Callback used by CopyFavicon to inform the favicon service that one URI
+// (mNewURI) has the same favicon URI (OnComplete's aFaviconURI) as another.
+class nsCopyFaviconCallback final : public nsIFaviconDataCallback
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ nsCopyFaviconCallback(mozIAsyncFavicons* aSvc,
+ nsIURI* aNewURI,
+ nsIPrincipal* aLoadingPrincipal,
+ bool aInPrivateBrowsing)
+ : mSvc(aSvc)
+ , mNewURI(aNewURI)
+ , mLoadingPrincipal(aLoadingPrincipal)
+ , mInPrivateBrowsing(aInPrivateBrowsing)
+ {
+ }
+
+ NS_IMETHOD
+ OnComplete(nsIURI* aFaviconURI, uint32_t aDataLen,
+ const uint8_t* aData, const nsACString& aMimeType) override
+ {
+ // Continue only if there is an associated favicon.
+ if (!aFaviconURI) {
+ return NS_OK;
+ }
+
+ MOZ_ASSERT(aDataLen == 0,
+ "We weren't expecting the callback to deliver data.");
+
+ nsCOMPtr<mozIPlacesPendingOperation> po;
+ return mSvc->SetAndFetchFaviconForPage(
+ mNewURI, aFaviconURI, false,
+ mInPrivateBrowsing ? nsIFaviconService::FAVICON_LOAD_PRIVATE :
+ nsIFaviconService::FAVICON_LOAD_NON_PRIVATE,
+ nullptr, mLoadingPrincipal, getter_AddRefs(po));
+ }
+
+private:
+ ~nsCopyFaviconCallback() {}
+
+ nsCOMPtr<mozIAsyncFavicons> mSvc;
+ nsCOMPtr<nsIURI> mNewURI;
+ nsCOMPtr<nsIPrincipal> mLoadingPrincipal;
+ bool mInPrivateBrowsing;
+};
+
+NS_IMPL_ISUPPORTS(nsCopyFaviconCallback, nsIFaviconDataCallback)
+#endif
+
+} // namespace
+
+void
+nsDocShell::CopyFavicon(nsIURI* aOldURI,
+ nsIURI* aNewURI,
+ nsIPrincipal* aLoadingPrincipal,
+ bool aInPrivateBrowsing)
+{
+ if (XRE_IsContentProcess()) {
+ dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
+ if (contentChild) {
+ mozilla::ipc::URIParams oldURI, newURI;
+ SerializeURI(aOldURI, oldURI);
+ SerializeURI(aNewURI, newURI);
+ contentChild->SendCopyFavicon(oldURI, newURI,
+ IPC::Principal(aLoadingPrincipal),
+ aInPrivateBrowsing);
+ }
+ return;
+ }
+
+#ifdef MOZ_PLACES
+ nsCOMPtr<mozIAsyncFavicons> favSvc =
+ do_GetService("@mozilla.org/browser/favicon-service;1");
+ if (favSvc) {
+ nsCOMPtr<nsIFaviconDataCallback> callback =
+ new nsCopyFaviconCallback(favSvc, aNewURI,
+ aLoadingPrincipal,
+ aInPrivateBrowsing);
+ favSvc->GetFaviconURLForPage(aOldURI, callback);
+ }
+#endif
+}
+
+class InternalLoadEvent : public Runnable
+{
+public:
+ InternalLoadEvent(nsDocShell* aDocShell, nsIURI* aURI,
+ nsIURI* aOriginalURI, bool aLoadReplace,
+ nsIURI* aReferrer, uint32_t aReferrerPolicy,
+ nsIPrincipal* aTriggeringPrincipal,
+ nsIPrincipal* aPrincipalToInherit, uint32_t aFlags,
+ const char* aTypeHint, nsIInputStream* aPostData,
+ nsIInputStream* aHeadersData, uint32_t aLoadType,
+ nsISHEntry* aSHEntry, bool aFirstParty,
+ const nsAString& aSrcdoc, nsIDocShell* aSourceDocShell,
+ nsIURI* aBaseURI)
+ : mSrcdoc(aSrcdoc)
+ , mDocShell(aDocShell)
+ , mURI(aURI)
+ , mOriginalURI(aOriginalURI)
+ , mLoadReplace(aLoadReplace)
+ , mReferrer(aReferrer)
+ , mReferrerPolicy(aReferrerPolicy)
+ , mTriggeringPrincipal(aTriggeringPrincipal)
+ , mPrincipalToInherit(aPrincipalToInherit)
+ , mPostData(aPostData)
+ , mHeadersData(aHeadersData)
+ , mSHEntry(aSHEntry)
+ , mFlags(aFlags)
+ , mLoadType(aLoadType)
+ , mFirstParty(aFirstParty)
+ , mSourceDocShell(aSourceDocShell)
+ , mBaseURI(aBaseURI)
+ {
+ // Make sure to keep null things null as needed
+ if (aTypeHint) {
+ mTypeHint = aTypeHint;
+ }
+ }
+
+ NS_IMETHOD
+ Run() override
+ {
+ return mDocShell->InternalLoad(mURI, mOriginalURI,
+ mLoadReplace,
+ mReferrer,
+ mReferrerPolicy,
+ mTriggeringPrincipal, mPrincipalToInherit,
+ mFlags, EmptyString(), mTypeHint.get(),
+ NullString(), mPostData, mHeadersData,
+ mLoadType, mSHEntry, mFirstParty,
+ mSrcdoc, mSourceDocShell, mBaseURI,
+ nullptr, nullptr);
+ }
+
+private:
+ // Use IDL strings so .get() returns null by default
+ nsXPIDLString mWindowTarget;
+ nsXPIDLCString mTypeHint;
+ nsString mSrcdoc;
+
+ RefPtr<nsDocShell> mDocShell;
+ nsCOMPtr<nsIURI> mURI;
+ nsCOMPtr<nsIURI> mOriginalURI;
+ bool mLoadReplace;
+ nsCOMPtr<nsIURI> mReferrer;
+ uint32_t mReferrerPolicy;
+ nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
+ nsCOMPtr<nsIPrincipal> mPrincipalToInherit;
+ nsCOMPtr<nsIInputStream> mPostData;
+ nsCOMPtr<nsIInputStream> mHeadersData;
+ nsCOMPtr<nsISHEntry> mSHEntry;
+ uint32_t mFlags;
+ uint32_t mLoadType;
+ bool mFirstParty;
+ nsCOMPtr<nsIDocShell> mSourceDocShell;
+ nsCOMPtr<nsIURI> mBaseURI;
+};
+
+/**
+ * Returns true if we started an asynchronous load (i.e., from the network), but
+ * the document we're loading there hasn't yet become this docshell's active
+ * document.
+ *
+ * When JustStartedNetworkLoad is true, you should be careful about modifying
+ * mLoadType and mLSHE. These are both set when the asynchronous load first
+ * starts, and the load expects that, when it eventually runs InternalLoad,
+ * mLoadType and mLSHE will have their original values.
+ */
+bool
+nsDocShell::JustStartedNetworkLoad()
+{
+ return mDocumentRequest && mDocumentRequest != GetCurrentDocChannel();
+}
+
+nsresult
+nsDocShell::CreatePrincipalFromReferrer(nsIURI* aReferrer,
+ nsIPrincipal** aResult)
+{
+ PrincipalOriginAttributes attrs;
+ attrs.InheritFromDocShellToDoc(mOriginAttributes, aReferrer);
+ nsCOMPtr<nsIPrincipal> prin =
+ BasePrincipal::CreateCodebasePrincipal(aReferrer, attrs);
+ prin.forget(aResult);
+
+ return *aResult ? NS_OK : NS_ERROR_FAILURE;
+}
+
+bool
+nsDocShell::IsAboutNewtab(nsIURI* aURI)
+{
+ if (!aURI) {
+ return false;
+ }
+ bool isAbout;
+ if (NS_WARN_IF(NS_FAILED(aURI->SchemeIs("about", &isAbout)))) {
+ return false;
+ }
+ if (!isAbout) {
+ return false;
+ }
+
+ nsAutoCString module;
+ if (NS_WARN_IF(NS_FAILED(NS_GetAboutModuleName(aURI, module)))) {
+ return false;
+ }
+ return module.Equals("newtab");
+}
+
+NS_IMETHODIMP
+nsDocShell::InternalLoad(nsIURI* aURI,
+ nsIURI* aOriginalURI,
+ bool aLoadReplace,
+ nsIURI* aReferrer,
+ uint32_t aReferrerPolicy,
+ nsIPrincipal* aTriggeringPrincipal,
+ nsIPrincipal* aPrincipalToInherit,
+ uint32_t aFlags,
+ const nsAString& aWindowTarget,
+ const char* aTypeHint,
+ const nsAString& aFileName,
+ nsIInputStream* aPostData,
+ nsIInputStream* aHeadersData,
+ uint32_t aLoadType,
+ nsISHEntry* aSHEntry,
+ bool aFirstParty,
+ const nsAString& aSrcdoc,
+ nsIDocShell* aSourceDocShell,
+ nsIURI* aBaseURI,
+ nsIDocShell** aDocShell,
+ nsIRequest** aRequest)
+{
+ MOZ_ASSERT(aTriggeringPrincipal, "need a valid TriggeringPrincipal");
+
+ nsresult rv = NS_OK;
+ mOriginalUriString.Truncate();
+
+ if (gDocShellLeakLog && MOZ_LOG_TEST(gDocShellLeakLog, LogLevel::Debug)) {
+ PR_LogPrint("DOCSHELL %p InternalLoad %s\n",
+ this, aURI ? aURI->GetSpecOrDefault().get() : "");
+ }
+ // Initialize aDocShell/aRequest
+ if (aDocShell) {
+ *aDocShell = nullptr;
+ }
+ if (aRequest) {
+ *aRequest = nullptr;
+ }
+
+ if (!aURI) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ NS_ENSURE_TRUE(IsValidLoadType(aLoadType), NS_ERROR_INVALID_ARG);
+
+ NS_ENSURE_TRUE(!mIsBeingDestroyed, NS_ERROR_NOT_AVAILABLE);
+
+ rv = EnsureScriptEnvironment();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // wyciwyg urls can only be loaded through history. Any normal load of
+ // wyciwyg through docshell is illegal. Disallow such loads.
+ if (aLoadType & LOAD_CMD_NORMAL) {
+ bool isWyciwyg = false;
+ rv = aURI->SchemeIs("wyciwyg", &isWyciwyg);
+ if ((isWyciwyg && NS_SUCCEEDED(rv)) || NS_FAILED(rv)) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ bool isJavaScript = false;
+ if (NS_FAILED(aURI->SchemeIs("javascript", &isJavaScript))) {
+ isJavaScript = false;
+ }
+
+ bool isTargetTopLevelDocShell = false;
+ nsCOMPtr<nsIDocShell> targetDocShell;
+ if (!aWindowTarget.IsEmpty()) {
+ // Locate the target DocShell.
+ nsCOMPtr<nsIDocShellTreeItem> targetItem;
+ // Only _self, _parent, and _top are supported in noopener case. But we
+ // have to be careful to not apply that to the noreferrer case. See bug
+ // 1358469.
+ bool allowNamedTarget = !(aFlags & INTERNAL_LOAD_FLAGS_NO_OPENER) ||
+ (aFlags & INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER);
+ if (allowNamedTarget ||
+ aWindowTarget.LowerCaseEqualsLiteral("_self") ||
+ aWindowTarget.LowerCaseEqualsLiteral("_parent") ||
+ aWindowTarget.LowerCaseEqualsLiteral("_top")) {
+ rv = FindItemWithName(aWindowTarget, nullptr, this,
+ getter_AddRefs(targetItem));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ targetDocShell = do_QueryInterface(targetItem);
+ if (targetDocShell) {
+ // If the targetDocShell and the rootDocShell are the same, then the
+ // targetDocShell is the top level document and hence we should
+ // consider this TYPE_DOCUMENT
+ //
+ // For example:
+ // 1. target="_top"
+ // 2. target="_parent", where this docshell is in the 2nd level of
+ // docshell tree.
+ nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
+ targetDocShell->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
+ NS_ASSERTION(sameTypeRoot,
+ "No document shell root tree item from targetDocShell!");
+ nsCOMPtr<nsIDocShell> rootShell = do_QueryInterface(sameTypeRoot);
+ NS_ASSERTION(rootShell,
+ "No root docshell from document shell root tree item.");
+ isTargetTopLevelDocShell = targetDocShell == rootShell;
+ } else {
+ // If the targetDocShell doesn't exist, then this is a new docShell
+ // and we should consider this a TYPE_DOCUMENT load
+ //
+ // For example, when target="_blank"
+ isTargetTopLevelDocShell = true;
+ }
+ }
+
+ // The contentType will be INTERNAL_(I)FRAME if:
+ // 1. This docshell is for iframe.
+ // 2. AND aWindowTarget is not a new window, nor a top-level window.
+ //
+ // This variable will be used when we call NS_CheckContentLoadPolicy, and
+ // later when we call DoURILoad.
+ uint32_t contentType;
+ if (IsFrame() && !isTargetTopLevelDocShell) {
+ nsCOMPtr<Element> requestingElement =
+ mScriptGlobal->AsOuter()->GetFrameElementInternal();
+ if (requestingElement) {
+ contentType = requestingElement->IsHTMLElement(nsGkAtoms::iframe) ?
+ nsIContentPolicy::TYPE_INTERNAL_IFRAME : nsIContentPolicy::TYPE_INTERNAL_FRAME;
+ } else {
+ // If we have lost our frame element by now, just assume we're
+ // an iframe since that's more common.
+ contentType = nsIContentPolicy::TYPE_INTERNAL_IFRAME;
+ }
+ } else {
+ contentType = nsIContentPolicy::TYPE_DOCUMENT;
+ }
+
+ // If there's no targetDocShell, that means we are about to create a new window,
+ // perform a content policy check before creating the window.
+ if (!targetDocShell) {
+ nsCOMPtr<Element> requestingElement;
+ nsISupports* requestingContext = nullptr;
+
+ if (contentType == nsIContentPolicy::TYPE_DOCUMENT) {
+ if (XRE_IsContentProcess()) {
+ // In e10s the child process doesn't have access to the element that
+ // contains the browsing context (because that element is in the chrome
+ // process). So we just pass mScriptGlobal.
+ requestingContext = ToSupports(mScriptGlobal);
+ } else {
+ // This is for loading non-e10s tabs and toplevel windows of various
+ // sorts.
+ // For the toplevel window cases, requestingElement will be null.
+ requestingElement = mScriptGlobal->AsOuter()->GetFrameElementInternal();
+ requestingContext = requestingElement;
+ }
+ } else {
+ requestingElement = mScriptGlobal->AsOuter()->GetFrameElementInternal();
+ requestingContext = requestingElement;
+
+#ifdef DEBUG
+ if (requestingElement) {
+ // Get the docshell type for requestingElement.
+ nsCOMPtr<nsIDocument> requestingDoc = requestingElement->OwnerDoc();
+ nsCOMPtr<nsIDocShell> elementDocShell = requestingDoc->GetDocShell();
+
+ // requestingElement docshell type = current docshell type.
+ MOZ_ASSERT(mItemType == elementDocShell->ItemType(),
+ "subframes should have the same docshell type as their parent");
+ }
+#endif
+ }
+
+ int16_t shouldLoad = nsIContentPolicy::ACCEPT;
+ rv = NS_CheckContentLoadPolicy(contentType,
+ aURI,
+ aTriggeringPrincipal,
+ requestingContext,
+ EmptyCString(), // mime guess
+ nullptr, // extra
+ &shouldLoad);
+
+ if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
+ if (NS_SUCCEEDED(rv) && shouldLoad == nsIContentPolicy::REJECT_TYPE) {
+ return NS_ERROR_CONTENT_BLOCKED_SHOW_ALT;
+ }
+
+ return NS_ERROR_CONTENT_BLOCKED;
+ }
+
+ // If HSTS priming was set by nsMixedContentBlocker::ShouldLoad, and we
+ // would block due to mixed content, go ahead and block here. If we try to
+ // proceed with priming, we will error out later on.
+ nsCOMPtr<nsIDocShell> docShell = NS_CP_GetDocShellFromContext(requestingContext);
+ // When loading toplevel windows, requestingContext can be null. We don't
+ // really care about HSTS in that situation, though; loads in toplevel
+ // windows should all be browser UI.
+ if (docShell) {
+ nsIDocument* document = docShell->GetDocument();
+ NS_ENSURE_TRUE(document, NS_OK);
+
+ HSTSPrimingState state = document->GetHSTSPrimingStateForLocation(aURI);
+ if (state == HSTSPrimingState::eHSTS_PRIMING_BLOCK) {
+ // HSTS Priming currently disabled for InternalLoad, so we need to clear
+ // the location that was added by nsMixedContentBlocker::ShouldLoad
+ // Bug 1269815 will address images loaded via InternalLoad
+ document->ClearHSTSPrimingLocation(aURI);
+ return NS_ERROR_CONTENT_BLOCKED;
+ }
+ }
+ }
+
+ nsCOMPtr<nsIPrincipal> principalToInherit = aPrincipalToInherit;
+ //
+ // Get a principal from the current document if necessary. Note that we only
+ // do this for URIs that inherit a security context and local file URIs;
+ // in particular we do NOT do this for about:blank. This way, random
+ // about:blank loads that have no principal (which basically means they were
+ // done by someone from chrome manually messing with our nsIWebNavigation
+ // or by C++ setting document.location) don't get a funky principal. If
+ // callers want something interesting to happen with the about:blank
+ // principal in this case, they should pass aPrincipalToInherit in.
+ //
+ {
+ bool inherits;
+ // One more twist: Don't inherit the principal for external loads.
+ if (aLoadType != LOAD_NORMAL_EXTERNAL && !principalToInherit &&
+ (aFlags & INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL) &&
+ NS_SUCCEEDED(nsContentUtils::URIInheritsSecurityContext(aURI,
+ &inherits)) &&
+ inherits) {
+ principalToInherit = GetInheritedPrincipal(true);
+ }
+ }
+
+ // Don't allow loads that would inherit our security context
+ // if this document came from an unsafe channel.
+ {
+ bool willInherit;
+ // This condition needs to match the one in
+ // nsContentUtils::ChannelShouldInheritPrincipal.
+ // Except we reverse the rv check to be safe in case
+ // nsContentUtils::URIInheritsSecurityContext fails here and
+ // succeeds there.
+ rv = nsContentUtils::URIInheritsSecurityContext(aURI, &willInherit);
+ if (NS_FAILED(rv) || willInherit || NS_IsAboutBlank(aURI)) {
+ nsCOMPtr<nsIDocShellTreeItem> treeItem = this;
+ do {
+ nsCOMPtr<nsIDocShell> itemDocShell = do_QueryInterface(treeItem);
+ bool isUnsafe;
+ if (itemDocShell &&
+ NS_SUCCEEDED(itemDocShell->GetChannelIsUnsafe(&isUnsafe)) &&
+ isUnsafe) {
+ return NS_ERROR_DOM_SECURITY_ERR;
+ }
+
+ nsCOMPtr<nsIDocShellTreeItem> parent;
+ treeItem->GetSameTypeParent(getter_AddRefs(parent));
+ parent.swap(treeItem);
+ } while (treeItem);
+ }
+ }
+
+ //
+ // Resolve the window target before going any further...
+ // If the load has been targeted to another DocShell, then transfer the
+ // load to it...
+ //
+ if (!aWindowTarget.IsEmpty()) {
+ // We've already done our owner-inheriting. Mask out that bit, so we
+ // don't try inheriting an owner from the target window if we came up
+ // with a null owner above.
+ aFlags = aFlags & ~INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL;
+
+ bool isNewWindow = false;
+ if (!targetDocShell) {
+ // If the docshell's document is sandboxed, only open a new window
+ // if the document's SANDBOXED_AUXILLARY_NAVIGATION flag is not set.
+ // (i.e. if allow-popups is specified)
+ NS_ENSURE_TRUE(mContentViewer, NS_ERROR_FAILURE);
+ nsIDocument* doc = mContentViewer->GetDocument();
+ uint32_t sandboxFlags = 0;
+
+ if (doc) {
+ sandboxFlags = doc->GetSandboxFlags();
+ if (sandboxFlags & SANDBOXED_AUXILIARY_NAVIGATION) {
+ return NS_ERROR_DOM_INVALID_ACCESS_ERR;
+ }
+ }
+
+ nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
+ NS_ENSURE_TRUE(win, NS_ERROR_NOT_AVAILABLE);
+
+ nsCOMPtr<nsPIDOMWindowOuter> newWin;
+ nsAutoCString spec;
+ if (aURI) {
+ aURI->GetSpec(spec);
+ }
+ // If we are a noopener load, we just hand the whole thing over to our
+ // window.
+ if (aFlags & INTERNAL_LOAD_FLAGS_NO_OPENER) {
+ // Various asserts that we know to hold because NO_OPENER loads can only
+ // happen for links.
+ MOZ_ASSERT(!aLoadReplace);
+ MOZ_ASSERT(aPrincipalToInherit == aTriggeringPrincipal);
+ MOZ_ASSERT(aFlags == INTERNAL_LOAD_FLAGS_NO_OPENER ||
+ aFlags == (INTERNAL_LOAD_FLAGS_NO_OPENER |
+ INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER));
+ MOZ_ASSERT(!aPostData);
+ MOZ_ASSERT(!aHeadersData);
+ MOZ_ASSERT(aLoadType == LOAD_LINK);
+ MOZ_ASSERT(!aSHEntry);
+ MOZ_ASSERT(aFirstParty); // Windowwatcher will assume this.
+
+ nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
+ rv = CreateLoadInfo(getter_AddRefs(loadInfo));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // Set up our loadinfo so it will do the load as much like we would have
+ // as possible.
+ loadInfo->SetReferrer(aReferrer);
+ loadInfo->SetReferrerPolicy(aReferrerPolicy);
+ loadInfo->SetSendReferrer(!(aFlags &
+ INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER));
+ loadInfo->SetOriginalURI(aOriginalURI);
+ loadInfo->SetLoadReplace(aLoadReplace);
+ loadInfo->SetTriggeringPrincipal(aTriggeringPrincipal);
+ loadInfo->SetInheritPrincipal(
+ aFlags & INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL);
+ // Explicit principal because we do not want any guesses as to what the
+ // principal to inherit is: it should be aTriggeringPrincipal.
+ loadInfo->SetPrincipalIsExplicit(true);
+ loadInfo->SetLoadType(ConvertLoadTypeToDocShellLoadInfo(LOAD_LINK));
+
+ rv = win->Open(NS_ConvertUTF8toUTF16(spec),
+ aWindowTarget, // window name
+ EmptyString(), // Features
+ loadInfo,
+ true, // aForceNoOpener
+ getter_AddRefs(newWin));
+ MOZ_ASSERT(!newWin);
+ return rv;
+ }
+
+ rv = win->OpenNoNavigate(NS_ConvertUTF8toUTF16(spec),
+ aWindowTarget, // window name
+ EmptyString(), // Features
+ getter_AddRefs(newWin));
+
+ // In some cases the Open call doesn't actually result in a new
+ // window being opened. We can detect these cases by examining the
+ // document in |newWin|, if any.
+ nsCOMPtr<nsPIDOMWindowOuter> piNewWin = do_QueryInterface(newWin);
+ if (piNewWin) {
+ nsCOMPtr<nsIDocument> newDoc = piNewWin->GetExtantDoc();
+ if (!newDoc || newDoc->IsInitialDocument()) {
+ isNewWindow = true;
+ aFlags |= INTERNAL_LOAD_FLAGS_FIRST_LOAD;
+ }
+ }
+
+ nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(newWin);
+ targetDocShell = do_QueryInterface(webNav);
+ }
+
+ //
+ // Transfer the load to the target DocShell... Pass nullptr as the
+ // window target name from to prevent recursive retargeting!
+ //
+ if (NS_SUCCEEDED(rv) && targetDocShell) {
+ rv = targetDocShell->InternalLoad(aURI,
+ aOriginalURI,
+ aLoadReplace,
+ aReferrer,
+ aReferrerPolicy,
+ aTriggeringPrincipal,
+ principalToInherit,
+ aFlags,
+ EmptyString(), // No window target
+ aTypeHint,
+ NullString(), // No forced download
+ aPostData,
+ aHeadersData,
+ aLoadType,
+ aSHEntry,
+ aFirstParty,
+ aSrcdoc,
+ aSourceDocShell,
+ aBaseURI,
+ aDocShell,
+ aRequest);
+ if (rv == NS_ERROR_NO_CONTENT) {
+ // XXXbz except we never reach this code!
+ if (isNewWindow) {
+ //
+ // At this point, a new window has been created, but the
+ // URI did not have any data associated with it...
+ //
+ // So, the best we can do, is to tear down the new window
+ // that was just created!
+ //
+ if (nsCOMPtr<nsPIDOMWindowOuter> domWin = targetDocShell->GetWindow()) {
+ domWin->Close();
+ }
+ }
+ //
+ // NS_ERROR_NO_CONTENT should not be returned to the
+ // caller... This is an internal error code indicating that
+ // the URI had no data associated with it - probably a
+ // helper-app style protocol (ie. mailto://)
+ //
+ rv = NS_OK;
+ } else if (isNewWindow) {
+ // XXX: Once new windows are created hidden, the new
+ // window will need to be made visible... For now,
+ // do nothing.
+ }
+ }
+
+ // Else we ran out of memory, or were a popup and got blocked,
+ // or something.
+
+ return rv;
+ }
+
+ //
+ // Load is being targetted at this docshell so return an error if the
+ // docshell is in the process of being destroyed.
+ //
+ if (mIsBeingDestroyed) {
+ return NS_ERROR_FAILURE;
+ }
+
+ NS_ENSURE_STATE(!HasUnloadedParent());
+
+ rv = CheckLoadingPermissions();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ if (mFiredUnloadEvent) {
+ if (IsOKToLoadURI(aURI)) {
+ NS_PRECONDITION(aWindowTarget.IsEmpty(),
+ "Shouldn't have a window target here!");
+
+ // If this is a replace load, make whatever load triggered
+ // the unload event also a replace load, so we don't
+ // create extra history entries.
+ if (LOAD_TYPE_HAS_FLAGS(aLoadType, LOAD_FLAGS_REPLACE_HISTORY)) {
+ mLoadType = LOAD_NORMAL_REPLACE;
+ }
+
+ // Do this asynchronously
+ nsCOMPtr<nsIRunnable> ev =
+ new InternalLoadEvent(this, aURI, aOriginalURI, aLoadReplace,
+ aReferrer, aReferrerPolicy,
+ aTriggeringPrincipal, principalToInherit,
+ aFlags, aTypeHint, aPostData, aHeadersData,
+ aLoadType, aSHEntry, aFirstParty, aSrcdoc,
+ aSourceDocShell, aBaseURI);
+ return NS_DispatchToCurrentThread(ev);
+ }
+
+ // Just ignore this load attempt
+ return NS_OK;
+ }
+
+ // If a source docshell has been passed, check to see if we are sandboxed
+ // from it as the result of an iframe or CSP sandbox.
+ if (aSourceDocShell && aSourceDocShell->IsSandboxedFrom(this)) {
+ return NS_ERROR_DOM_INVALID_ACCESS_ERR;
+ }
+
+ // If this docshell is owned by a frameloader, make sure to cancel
+ // possible frameloader initialization before loading a new page.
+ nsCOMPtr<nsIDocShellTreeItem> parent = GetParentDocshell();
+ if (parent) {
+ nsCOMPtr<nsIDocument> doc = parent->GetDocument();
+ if (doc) {
+ doc->TryCancelFrameLoaderInitialization(this);
+ }
+ }
+
+ // Before going any further vet loads initiated by external programs.
+ if (aLoadType == LOAD_NORMAL_EXTERNAL) {
+ // Disallow external chrome: loads targetted at content windows
+ bool isChrome = false;
+ if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &isChrome)) && isChrome) {
+ NS_WARNING("blocked external chrome: url -- use '--chrome' option");
+ return NS_ERROR_FAILURE;
+ }
+
+ // clear the decks to prevent context bleed-through (bug 298255)
+ rv = CreateAboutBlankContentViewer(nullptr, nullptr);
+ if (NS_FAILED(rv)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // reset loadType so we don't have to add lots of tests for
+ // LOAD_NORMAL_EXTERNAL after this point
+ aLoadType = LOAD_NORMAL;
+ }
+
+ mAllowKeywordFixup =
+ (aFlags & INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) != 0;
+ mURIResultedInDocument = false; // reset the clock...
+
+ if (aLoadType == LOAD_NORMAL ||
+ aLoadType == LOAD_STOP_CONTENT ||
+ LOAD_TYPE_HAS_FLAGS(aLoadType, LOAD_FLAGS_REPLACE_HISTORY) ||
+ aLoadType == LOAD_HISTORY ||
+ aLoadType == LOAD_LINK) {
+ nsCOMPtr<nsIURI> currentURI = mCurrentURI;
+
+ nsAutoCString curHash, newHash;
+ bool curURIHasRef = false, newURIHasRef = false;
+
+ nsresult rvURINew = aURI->GetRef(newHash);
+ if (NS_SUCCEEDED(rvURINew)) {
+ rvURINew = aURI->GetHasRef(&newURIHasRef);
+ }
+
+ bool sameExceptHashes = false;
+ if (currentURI && NS_SUCCEEDED(rvURINew)) {
+ nsresult rvURIOld = currentURI->GetRef(curHash);
+ if (NS_SUCCEEDED(rvURIOld)) {
+ rvURIOld = currentURI->GetHasRef(&curURIHasRef);
+ }
+ if (NS_SUCCEEDED(rvURIOld)) {
+ if (NS_FAILED(currentURI->EqualsExceptRef(aURI, &sameExceptHashes))) {
+ sameExceptHashes = false;
+ }
+ }
+ }
+
+ if (!sameExceptHashes && sURIFixup && currentURI &&
+ NS_SUCCEEDED(rvURINew)) {
+ // Maybe aURI came from the exposable form of currentURI?
+ nsCOMPtr<nsIURI> currentExposableURI;
+ rv = sURIFixup->CreateExposableURI(currentURI,
+ getter_AddRefs(currentExposableURI));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsresult rvURIOld = currentExposableURI->GetRef(curHash);
+ if (NS_SUCCEEDED(rvURIOld)) {
+ rvURIOld = currentExposableURI->GetHasRef(&curURIHasRef);
+ }
+ if (NS_SUCCEEDED(rvURIOld)) {
+ if (NS_FAILED(currentExposableURI->EqualsExceptRef(aURI, &sameExceptHashes))) {
+ sameExceptHashes = false;
+ }
+ }
+ }
+
+ bool historyNavBetweenSameDoc = false;
+ if (mOSHE && aSHEntry) {
+ // We're doing a history load.
+
+ mOSHE->SharesDocumentWith(aSHEntry, &historyNavBetweenSameDoc);
+
+#ifdef DEBUG
+ if (historyNavBetweenSameDoc) {
+ nsCOMPtr<nsIInputStream> currentPostData;
+ mOSHE->GetPostData(getter_AddRefs(currentPostData));
+ NS_ASSERTION(currentPostData == aPostData,
+ "Different POST data for entries for the same page?");
+ }
+#endif
+ }
+
+ // A short-circuited load happens when we navigate between two SHEntries
+ // for the same document. We do a short-circuited load under two
+ // circumstances. Either
+ //
+ // a) we're navigating between two different SHEntries which share a
+ // document, or
+ //
+ // b) we're navigating to a new shentry whose URI differs from the
+ // current URI only in its hash, the new hash is non-empty, and
+ // we're not doing a POST.
+ //
+ // The restriction tha the SHEntries in (a) must be different ensures
+ // that history.go(0) and the like trigger full refreshes, rather than
+ // short-circuited loads.
+ bool doShortCircuitedLoad =
+ (historyNavBetweenSameDoc && mOSHE != aSHEntry) ||
+ (!aSHEntry && !aPostData &&
+ sameExceptHashes && newURIHasRef);
+
+ if (doShortCircuitedLoad) {
+ // Save the position of the scrollers.
+ nscoord cx = 0, cy = 0;
+ GetCurScrollPos(ScrollOrientation_X, &cx);
+ GetCurScrollPos(ScrollOrientation_Y, &cy);
+
+ // Reset mLoadType to its original value once we exit this block,
+ // because this short-circuited load might have started after a
+ // normal, network load, and we don't want to clobber its load type.
+ // See bug 737307.
+ AutoRestore<uint32_t> loadTypeResetter(mLoadType);
+
+ // If a non-short-circuit load (i.e., a network load) is pending,
+ // make this a replacement load, so that we don't add a SHEntry here
+ // and the network load goes into the SHEntry it expects to.
+ if (JustStartedNetworkLoad() && (aLoadType & LOAD_CMD_NORMAL)) {
+ mLoadType = LOAD_NORMAL_REPLACE;
+ } else {
+ mLoadType = aLoadType;
+ }
+
+ mURIResultedInDocument = true;
+
+ nsCOMPtr<nsISHEntry> oldLSHE = mLSHE;
+
+ /* we need to assign mLSHE to aSHEntry right here, so that on History
+ * loads, SetCurrentURI() called from OnNewURI() will send proper
+ * onLocationChange() notifications to the browser to update
+ * back/forward buttons.
+ */
+ SetHistoryEntry(&mLSHE, aSHEntry);
+
+ // Set the doc's URI according to the new history entry's URI.
+ nsCOMPtr<nsIDocument> doc = GetDocument();
+ NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
+ doc->SetDocumentURI(aURI);
+
+ /* This is a anchor traversal with in the same page.
+ * call OnNewURI() so that, this traversal will be
+ * recorded in session and global history.
+ */
+ nsCOMPtr<nsIPrincipal> triggeringPrincipal, principalToInherit;
+ if (mOSHE) {
+ mOSHE->GetTriggeringPrincipal(getter_AddRefs(triggeringPrincipal));
+ mOSHE->GetPrincipalToInherit(getter_AddRefs(principalToInherit));
+ }
+ // Pass true for aCloneSHChildren, since we're not
+ // changing documents here, so all of our subframes are
+ // still relevant to the new session history entry.
+ //
+ // It also makes OnNewURI(...) set LOCATION_CHANGE_SAME_DOCUMENT
+ // flag on firing onLocationChange(...).
+ // Anyway, aCloneSHChildren param is simply reflecting
+ // doShortCircuitedLoad in this scope.
+ OnNewURI(aURI, nullptr, triggeringPrincipal, principalToInherit,
+ mLoadType, true, true, true);
+
+ nsCOMPtr<nsIInputStream> postData;
+ nsCOMPtr<nsISupports> cacheKey;
+
+ bool scrollRestorationIsManual = false;
+ if (mOSHE) {
+ /* save current position of scroller(s) (bug 59774) */
+ mOSHE->SetScrollPosition(cx, cy);
+ mOSHE->GetScrollRestorationIsManual(&scrollRestorationIsManual);
+ // Get the postdata and page ident from the current page, if
+ // the new load is being done via normal means. Note that
+ // "normal means" can be checked for just by checking for
+ // LOAD_CMD_NORMAL, given the loadType and allowScroll check
+ // above -- it filters out some LOAD_CMD_NORMAL cases that we
+ // wouldn't want here.
+ if (aLoadType & LOAD_CMD_NORMAL) {
+ mOSHE->GetPostData(getter_AddRefs(postData));
+ mOSHE->GetCacheKey(getter_AddRefs(cacheKey));
+
+ // Link our new SHEntry to the old SHEntry's back/forward
+ // cache data, since the two SHEntries correspond to the
+ // same document.
+ if (mLSHE) {
+ if (!aSHEntry) {
+ // If we're not doing a history load, scroll restoration
+ // should be inherited from the previous session history entry.
+ mLSHE->SetScrollRestorationIsManual(scrollRestorationIsManual);
+ }
+ mLSHE->AdoptBFCacheEntry(mOSHE);
+ }
+ }
+ }
+
+ // If we're doing a history load, use its scroll restoration state.
+ if (aSHEntry) {
+ aSHEntry->GetScrollRestorationIsManual(&scrollRestorationIsManual);
+ }
+
+ /* Assign mOSHE to mLSHE. This will either be a new entry created
+ * by OnNewURI() for normal loads or aSHEntry for history loads.
+ */
+ if (mLSHE) {
+ SetHistoryEntry(&mOSHE, mLSHE);
+ // Save the postData obtained from the previous page
+ // in to the session history entry created for the
+ // anchor page, so that any history load of the anchor
+ // page will restore the appropriate postData.
+ if (postData) {
+ mOSHE->SetPostData(postData);
+ }
+
+ // Make sure we won't just repost without hitting the
+ // cache first
+ if (cacheKey) {
+ mOSHE->SetCacheKey(cacheKey);
+ }
+ }
+
+ /* Restore the original LSHE if we were loading something
+ * while short-circuited load was initiated.
+ */
+ SetHistoryEntry(&mLSHE, oldLSHE);
+ /* Set the title for the SH entry for this target url. so that
+ * SH menus in go/back/forward buttons won't be empty for this.
+ */
+ if (mSessionHistory) {
+ int32_t index = -1;
+ mSessionHistory->GetIndex(&index);
+ nsCOMPtr<nsISHEntry> shEntry;
+ mSessionHistory->GetEntryAtIndex(index, false, getter_AddRefs(shEntry));
+ NS_ENSURE_TRUE(shEntry, NS_ERROR_FAILURE);
+ shEntry->SetTitle(mTitle);
+ }
+
+ /* Set the title for the Global History entry for this anchor url.
+ */
+ if (mUseGlobalHistory && !UsePrivateBrowsing()) {
+ nsCOMPtr<IHistory> history = services::GetHistoryService();
+ if (history) {
+ history->SetURITitle(aURI, mTitle);
+ } else if (mGlobalHistory) {
+ mGlobalHistory->SetPageTitle(aURI, mTitle);
+ }
+ }
+
+ SetDocCurrentStateObj(mOSHE);
+
+ // Inform the favicon service that the favicon for oldURI also
+ // applies to aURI.
+ CopyFavicon(currentURI, aURI, doc->NodePrincipal(), UsePrivateBrowsing());
+
+ RefPtr<nsGlobalWindow> scriptGlobal = mScriptGlobal;
+ RefPtr<nsGlobalWindow> win = scriptGlobal ?
+ scriptGlobal->GetCurrentInnerWindowInternal() : nullptr;
+
+ // ScrollToAnchor doesn't necessarily cause us to scroll the window;
+ // the function decides whether a scroll is appropriate based on the
+ // arguments it receives. But even if we don't end up scrolling,
+ // ScrollToAnchor performs other important tasks, such as informing
+ // the presShell that we have a new hash. See bug 680257.
+ rv = ScrollToAnchor(curURIHasRef, newURIHasRef, newHash, aLoadType);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ /* restore previous position of scroller(s), if we're moving
+ * back in history (bug 59774)
+ */
+ nscoord bx = 0;
+ nscoord by = 0;
+ bool needsScrollPosUpdate = false;
+ if (mOSHE && (aLoadType == LOAD_HISTORY ||
+ aLoadType == LOAD_RELOAD_NORMAL) &&
+ !scrollRestorationIsManual) {
+ needsScrollPosUpdate = true;
+ mOSHE->GetScrollPosition(&bx, &by);
+ }
+
+ // Dispatch the popstate and hashchange events, as appropriate.
+ //
+ // The event dispatch below can cause us to re-enter script and
+ // destroy the docshell, nulling out mScriptGlobal. Hold a stack
+ // reference to avoid null derefs. See bug 914521.
+ if (win) {
+ // Fire a hashchange event URIs differ, and only in their hashes.
+ bool doHashchange = sameExceptHashes &&
+ (curURIHasRef != newURIHasRef || !curHash.Equals(newHash));
+
+ if (historyNavBetweenSameDoc || doHashchange) {
+ win->DispatchSyncPopState();
+ }
+
+ if (needsScrollPosUpdate && win->AsInner()->HasActiveDocument()) {
+ SetCurScrollPosEx(bx, by);
+ }
+
+ if (doHashchange) {
+ // Note that currentURI hasn't changed because it's on the
+ // stack, so we can just use it directly as the old URI.
+ win->DispatchAsyncHashchange(currentURI, aURI);
+ }
+ }
+
+ return NS_OK;
+ }
+ }
+
+ // mContentViewer->PermitUnload can destroy |this| docShell, which
+ // causes the next call of CanSavePresentation to crash.
+ // Hold onto |this| until we return, to prevent a crash from happening.
+ // (bug#331040)
+ nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
+
+ // Don't init timing for javascript:, since it generally doesn't
+ // actually start a load or anything. If it does, we'll init
+ // timing then, from OnStateChange.
+
+ // XXXbz mTiming should know what channel it's for, so we don't
+ // need this hackery. Note that this is still broken in cases
+ // when we're loading something that's not javascript: and the
+ // beforeunload handler denies the load. That will screw up
+ // timing for the next load!
+ if (!isJavaScript) {
+ MaybeInitTiming();
+ }
+ bool timeBeforeUnload = aFileName.IsVoid();
+ if (mTiming && timeBeforeUnload) {
+ mTiming->NotifyBeforeUnload();
+ }
+ // Check if the page doesn't want to be unloaded. The javascript:
+ // protocol handler deals with this for javascript: URLs.
+ if (!isJavaScript && aFileName.IsVoid() && mContentViewer) {
+ bool okToUnload;
+ rv = mContentViewer->PermitUnload(&okToUnload);
+
+ if (NS_SUCCEEDED(rv) && !okToUnload) {
+ // The user chose not to unload the page, interrupt the
+ // load.
+ return NS_OK;
+ }
+ }
+
+ if (mTiming && timeBeforeUnload) {
+ mTiming->NotifyUnloadAccepted(mCurrentURI);
+ }
+
+ // Check if the webbrowser chrome wants the load to proceed; this can be
+ // used to cancel attempts to load URIs in the wrong process.
+ nsCOMPtr<nsIWebBrowserChrome3> browserChrome3 = do_GetInterface(mTreeOwner);
+ if (browserChrome3) {
+ // In case this is a remote newtab load, set aURI to aOriginalURI (newtab).
+ // This ensures that the verifySignedContent flag is set on loadInfo in
+ // DoURILoad.
+ nsIURI* uriForShouldLoadCheck = aURI;
+ if (IsAboutNewtab(aOriginalURI)) {
+ uriForShouldLoadCheck = aOriginalURI;
+ }
+ bool shouldLoad;
+ rv = browserChrome3->ShouldLoadURI(this, uriForShouldLoadCheck, aReferrer,
+ &shouldLoad);
+ if (NS_SUCCEEDED(rv) && !shouldLoad) {
+ return NS_OK;
+ }
+ }
+
+ // Whenever a top-level browsing context is navigated, the user agent MUST
+ // lock the orientation of the document to the document's default
+ // orientation. We don't explicitly check for a top-level browsing context
+ // here because orientation is only set on top-level browsing contexts.
+ // We make an exception for apps because they currently rely on
+ // orientation locks persisting across browsing contexts.
+ if (OrientationLock() != eScreenOrientation_None && !GetIsApp()) {
+#ifdef DEBUG
+ nsCOMPtr<nsIDocShellTreeItem> parent;
+ GetSameTypeParent(getter_AddRefs(parent));
+ MOZ_ASSERT(!parent);
+#endif
+ SetOrientationLock(eScreenOrientation_None);
+ if (mIsActive) {
+ ScreenOrientation::UpdateActiveOrientationLock(eScreenOrientation_None);
+ }
+ }
+
+ // Check for saving the presentation here, before calling Stop().
+ // This is necessary so that we can catch any pending requests.
+ // Since the new request has not been created yet, we pass null for the
+ // new request parameter.
+ // Also pass nullptr for the document, since it doesn't affect the return
+ // value for our purposes here.
+ bool savePresentation = CanSavePresentation(aLoadType, nullptr, nullptr);
+
+ // Don't stop current network activity for javascript: URL's since
+ // they might not result in any data, and thus nothing should be
+ // stopped in those cases. In the case where they do result in
+ // data, the javascript: URL channel takes care of stopping
+ // current network activity.
+ if (!isJavaScript && aFileName.IsVoid()) {
+ // Stop any current network activity.
+ // Also stop content if this is a zombie doc. otherwise
+ // the onload will be delayed by other loads initiated in the
+ // background by the first document that
+ // didn't fully load before the next load was initiated.
+ // If not a zombie, don't stop content until data
+ // starts arriving from the new URI...
+
+ nsCOMPtr<nsIContentViewer> zombieViewer;
+ if (mContentViewer) {
+ mContentViewer->GetPreviousViewer(getter_AddRefs(zombieViewer));
+ }
+
+ if (zombieViewer ||
+ LOAD_TYPE_HAS_FLAGS(aLoadType, LOAD_FLAGS_STOP_CONTENT)) {
+ rv = Stop(nsIWebNavigation::STOP_ALL);
+ } else {
+ rv = Stop(nsIWebNavigation::STOP_NETWORK);
+ }
+
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+
+ mLoadType = aLoadType;
+
+ // mLSHE should be assigned to aSHEntry, only after Stop() has
+ // been called. But when loading an error page, do not clear the
+ // mLSHE for the real page.
+ if (mLoadType != LOAD_ERROR_PAGE) {
+ SetHistoryEntry(&mLSHE, aSHEntry);
+ }
+
+ mSavingOldViewer = savePresentation;
+
+ // If we have a saved content viewer in history, restore and show it now.
+ if (aSHEntry && (mLoadType & LOAD_CMD_HISTORY)) {
+ // Make sure our history ID points to the same ID as
+ // SHEntry's docshell ID.
+ aSHEntry->GetDocshellID(&mHistoryID);
+
+ // It's possible that the previous viewer of mContentViewer is the
+ // viewer that will end up in aSHEntry when it gets closed. If that's
+ // the case, we need to go ahead and force it into its shentry so we
+ // can restore it.
+ if (mContentViewer) {
+ nsCOMPtr<nsIContentViewer> prevViewer;
+ mContentViewer->GetPreviousViewer(getter_AddRefs(prevViewer));
+ if (prevViewer) {
+#ifdef DEBUG
+ nsCOMPtr<nsIContentViewer> prevPrevViewer;
+ prevViewer->GetPreviousViewer(getter_AddRefs(prevPrevViewer));
+ NS_ASSERTION(!prevPrevViewer, "Should never have viewer chain here");
+#endif
+ nsCOMPtr<nsISHEntry> viewerEntry;
+ prevViewer->GetHistoryEntry(getter_AddRefs(viewerEntry));
+ if (viewerEntry == aSHEntry) {
+ // Make sure this viewer ends up in the right place
+ mContentViewer->SetPreviousViewer(nullptr);
+ prevViewer->Destroy();
+ }
+ }
+ }
+ nsCOMPtr<nsISHEntry> oldEntry = mOSHE;
+ bool restoring;
+ rv = RestorePresentation(aSHEntry, &restoring);
+ if (restoring) {
+ return rv;
+ }
+
+ // We failed to restore the presentation, so clean up.
+ // Both the old and new history entries could potentially be in
+ // an inconsistent state.
+ if (NS_FAILED(rv)) {
+ if (oldEntry) {
+ oldEntry->SyncPresentationState();
+ }
+
+ aSHEntry->SyncPresentationState();
+ }
+ }
+
+ nsAutoString srcdoc;
+ if (aFlags & INTERNAL_LOAD_FLAGS_IS_SRCDOC) {
+ srcdoc = aSrcdoc;
+ } else {
+ srcdoc = NullString();
+ }
+
+ net::PredictorLearn(aURI, nullptr,
+ nsINetworkPredictor::LEARN_LOAD_TOPLEVEL, this);
+ net::PredictorPredict(aURI, nullptr,
+ nsINetworkPredictor::PREDICT_LOAD, this, nullptr);
+
+ nsCOMPtr<nsIRequest> req;
+ rv = DoURILoad(aURI, aOriginalURI, aLoadReplace, aReferrer,
+ !(aFlags & INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER),
+ aReferrerPolicy,
+ aTriggeringPrincipal, principalToInherit, aTypeHint,
+ aFileName, aPostData, aHeadersData,
+ aFirstParty, aDocShell, getter_AddRefs(req),
+ (aFlags & INTERNAL_LOAD_FLAGS_FIRST_LOAD) != 0,
+ (aFlags & INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER) != 0,
+ (aFlags & INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES) != 0,
+ srcdoc, aBaseURI, contentType);
+ if (req && aRequest) {
+ NS_ADDREF(*aRequest = req);
+ }
+
+ if (NS_FAILED(rv)) {
+ nsCOMPtr<nsIChannel> chan(do_QueryInterface(req));
+ if (DisplayLoadError(rv, aURI, nullptr, chan) &&
+ (aFlags & LOAD_FLAGS_ERROR_LOAD_CHANGES_RV) != 0) {
+ return NS_ERROR_LOAD_SHOWED_ERRORPAGE;
+ }
+ }
+
+ return rv;
+}
+
+nsIPrincipal*
+nsDocShell::GetInheritedPrincipal(bool aConsiderCurrentDocument)
+{
+ nsCOMPtr<nsIDocument> document;
+ bool inheritedFromCurrent = false;
+
+ if (aConsiderCurrentDocument && mContentViewer) {
+ document = mContentViewer->GetDocument();
+ inheritedFromCurrent = true;
+ }
+
+ if (!document) {
+ nsCOMPtr<nsIDocShellTreeItem> parentItem;
+ GetSameTypeParent(getter_AddRefs(parentItem));
+ if (parentItem) {
+ document = parentItem->GetDocument();
+ }
+ }
+
+ if (!document) {
+ if (!aConsiderCurrentDocument) {
+ return nullptr;
+ }
+
+ // Make sure we end up with _something_ as the principal no matter
+ // what.If this fails, we'll just get a null docViewer and bail.
+ EnsureContentViewer();
+ if (!mContentViewer) {
+ return nullptr;
+ }
+ document = mContentViewer->GetDocument();
+ }
+
+ //-- Get the document's principal
+ if (document) {
+ nsIPrincipal* docPrincipal = document->NodePrincipal();
+
+ // Don't allow loads in typeContent docShells to inherit the system
+ // principal from existing documents.
+ if (inheritedFromCurrent &&
+ mItemType == typeContent &&
+ nsContentUtils::IsSystemPrincipal(docPrincipal)) {
+ return nullptr;
+ }
+
+ return docPrincipal;
+ }
+
+ return nullptr;
+}
+
+nsresult
+nsDocShell::DoURILoad(nsIURI* aURI,
+ nsIURI* aOriginalURI,
+ bool aLoadReplace,
+ nsIURI* aReferrerURI,
+ bool aSendReferrer,
+ uint32_t aReferrerPolicy,
+ nsIPrincipal* aTriggeringPrincipal,
+ nsIPrincipal* aPrincipalToInherit,
+ const char* aTypeHint,
+ const nsAString& aFileName,
+ nsIInputStream* aPostData,
+ nsIInputStream* aHeadersData,
+ bool aFirstParty,
+ nsIDocShell** aDocShell,
+ nsIRequest** aRequest,
+ bool aIsNewWindowTarget,
+ bool aBypassClassifier,
+ bool aForceAllowCookies,
+ const nsAString& aSrcdoc,
+ nsIURI* aBaseURI,
+ nsContentPolicyType aContentPolicyType)
+{
+ // Double-check that we're still around to load this URI.
+ if (mIsBeingDestroyed) {
+ // Return NS_OK despite not doing anything to avoid throwing exceptions from
+ // nsLocation::SetHref if the unload handler of the existing page tears us
+ // down.
+ return NS_OK;
+ }
+
+ nsresult rv;
+ nsCOMPtr<nsIURILoader> uriLoader = do_GetService(NS_URI_LOADER_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ if (IsFrame()) {
+
+ MOZ_ASSERT(aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_IFRAME ||
+ aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_FRAME,
+ "DoURILoad thinks this is a frame and InternalLoad does not");
+
+ // Only allow view-source scheme in top-level docshells. view-source is
+ // the only scheme to which this applies at the moment due to potential
+ // timing attacks to read data from cross-origin iframes. If this widens
+ // we should add a protocol flag for whether the scheme is allowed in
+ // frames and use something like nsNetUtil::NS_URIChainHasFlags.
+ nsCOMPtr<nsIURI> tempURI = aURI;
+ nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(tempURI);
+ while (nestedURI) {
+ // view-source should always be an nsINestedURI, loop and check the
+ // scheme on this and all inner URIs that are also nested URIs.
+ bool isViewSource = false;
+ rv = tempURI->SchemeIs("view-source", &isViewSource);
+ if (NS_FAILED(rv) || isViewSource) {
+ return NS_ERROR_UNKNOWN_PROTOCOL;
+ }
+ nestedURI->GetInnerURI(getter_AddRefs(tempURI));
+ nestedURI = do_QueryInterface(tempURI);
+ }
+ } else {
+ MOZ_ASSERT(aContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT,
+ "DoURILoad thinks this is a document and InternalLoad does not");
+ }
+
+ // open a channel for the url
+ nsCOMPtr<nsIChannel> channel;
+
+ bool isSrcdoc = !aSrcdoc.IsVoid();
+
+ // There are two cases we care about:
+ // * Top-level load: In this case, loadingNode is null, but loadingWindow
+ // is our mScriptGlobal. We pass null for loadingPrincipal in this case.
+ // * Subframe load: loadingWindow is null, but loadingNode is the frame
+ // element for the load. loadingPrincipal is the NodePrincipal of the frame
+ // element.
+ nsCOMPtr<nsINode> loadingNode;
+ nsCOMPtr<nsPIDOMWindowOuter> loadingWindow;
+ nsCOMPtr<nsIPrincipal> loadingPrincipal;
+
+ if (aContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT) {
+ loadingNode = nullptr;
+ loadingPrincipal = nullptr;
+ loadingWindow = mScriptGlobal->AsOuter();
+ } else {
+ loadingWindow = nullptr;
+ loadingNode = mScriptGlobal->AsOuter()->GetFrameElementInternal();
+ if (loadingNode) {
+ // If we have a loading node, then use that as our loadingPrincipal.
+ loadingPrincipal = loadingNode->NodePrincipal();
+ } else {
+ // If this isn't a top-level load and mScriptGlobal's frame element is
+ // null, then the element got removed from the DOM while we were trying
+ // to load this resource. This docshell is scheduled for destruction
+ // already, so bail out here.
+ return NS_OK;
+ }
+ }
+
+ // Getting the right triggeringPrincipal needs to be updated and is only
+ // ready for use once bug 1182569 landed. Until then, we cannot rely on
+ // the triggeringPrincipal for TYPE_DOCUMENT loads.
+ MOZ_ASSERT(aTriggeringPrincipal, "Need a valid triggeringPrincipal");
+
+ bool isSandBoxed = mSandboxFlags & SANDBOXED_ORIGIN;
+ // only inherit if we have a aPrincipalToInherit
+ bool inherit = false;
+
+ if (aPrincipalToInherit) {
+ inherit = nsContentUtils::ChannelShouldInheritPrincipal(
+ aPrincipalToInherit,
+ aURI,
+ true, // aInheritForAboutBlank
+ isSrcdoc);
+ }
+
+ nsLoadFlags loadFlags = mDefaultLoadFlags;
+ nsSecurityFlags securityFlags = nsILoadInfo::SEC_NORMAL;
+
+ if (aFirstParty) {
+ // tag first party URL loads
+ loadFlags |= nsIChannel::LOAD_INITIAL_DOCUMENT_URI;
+ }
+
+ if (mLoadType == LOAD_ERROR_PAGE) {
+ // Error pages are LOAD_BACKGROUND
+ loadFlags |= nsIChannel::LOAD_BACKGROUND;
+ securityFlags |= nsILoadInfo::SEC_LOAD_ERROR_PAGE;
+ }
+
+ if (inherit) {
+ securityFlags |= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
+ }
+ if (isSandBoxed) {
+ securityFlags |= nsILoadInfo::SEC_SANDBOXED;
+ }
+
+ nsCOMPtr<nsILoadInfo> loadInfo =
+ (aContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT) ?
+ new LoadInfo(loadingWindow, aTriggeringPrincipal,
+ securityFlags) :
+ new LoadInfo(loadingPrincipal, aTriggeringPrincipal, loadingNode,
+ securityFlags, aContentPolicyType);
+
+ if (aPrincipalToInherit) {
+ loadInfo->SetPrincipalToInherit(aPrincipalToInherit);
+ }
+
+ // We have to do this in case our OriginAttributes are different from the
+ // OriginAttributes of the parent document. Or in case there isn't a
+ // parent document.
+ NeckoOriginAttributes neckoAttrs;
+ bool isTopLevelDoc = aContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT &&
+ mItemType == typeContent &&
+ !GetIsMozBrowserOrApp();
+ neckoAttrs.InheritFromDocShellToNecko(GetOriginAttributes(), isTopLevelDoc, aURI);
+ rv = loadInfo->SetOriginAttributes(neckoAttrs);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (!isSrcdoc) {
+ rv = NS_NewChannelInternal(getter_AddRefs(channel),
+ aURI,
+ loadInfo,
+ nullptr, // loadGroup
+ static_cast<nsIInterfaceRequestor*>(this),
+ loadFlags);
+
+ if (NS_FAILED(rv)) {
+ if (rv == NS_ERROR_UNKNOWN_PROTOCOL) {
+ // This is a uri with a protocol scheme we don't know how
+ // to handle. Embedders might still be interested in
+ // handling the load, though, so we fire a notification
+ // before throwing the load away.
+ bool abort = false;
+ nsresult rv2 = mContentListener->OnStartURIOpen(aURI, &abort);
+ if (NS_SUCCEEDED(rv2) && abort) {
+ // Hey, they're handling the load for us! How convenient!
+ return NS_OK;
+ }
+ }
+ return rv;
+ }
+
+ if (aBaseURI) {
+ nsCOMPtr<nsIViewSourceChannel> vsc = do_QueryInterface(channel);
+ if (vsc) {
+ vsc->SetBaseURI(aBaseURI);
+ }
+ }
+ } else {
+ nsAutoCString scheme;
+ rv = aURI->GetScheme(scheme);
+ NS_ENSURE_SUCCESS(rv, rv);
+ bool isViewSource;
+ aURI->SchemeIs("view-source", &isViewSource);
+
+ if (isViewSource) {
+ nsViewSourceHandler* vsh = nsViewSourceHandler::GetInstance();
+ NS_ENSURE_TRUE(vsh, NS_ERROR_FAILURE);
+
+ rv = vsh->NewSrcdocChannel(aURI, aBaseURI, aSrcdoc,
+ loadInfo, getter_AddRefs(channel));
+ } else {
+ rv = NS_NewInputStreamChannelInternal(getter_AddRefs(channel),
+ aURI,
+ aSrcdoc,
+ NS_LITERAL_CSTRING("text/html"),
+ loadInfo,
+ true);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIInputStreamChannel> isc = do_QueryInterface(channel);
+ MOZ_ASSERT(isc);
+ isc->SetBaseURI(aBaseURI);
+ }
+ }
+
+ nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
+ do_QueryInterface(channel);
+ if (appCacheChannel) {
+ // Any document load should not inherit application cache.
+ appCacheChannel->SetInheritApplicationCache(false);
+
+ // Loads with the correct permissions should check for a matching
+ // application cache.
+ if (GeckoProcessType_Default != XRE_GetProcessType()) {
+ // Permission will be checked in the parent process
+ appCacheChannel->SetChooseApplicationCache(true);
+ } else {
+ nsCOMPtr<nsIScriptSecurityManager> secMan =
+ do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
+
+ if (secMan) {
+ nsCOMPtr<nsIPrincipal> principal;
+ secMan->GetDocShellCodebasePrincipal(aURI, this,
+ getter_AddRefs(principal));
+ appCacheChannel->SetChooseApplicationCache(
+ NS_ShouldCheckAppCache(principal, UsePrivateBrowsing()));
+ }
+ }
+ }
+
+ // Make sure to give the caller a channel if we managed to create one
+ // This is important for correct error page/session history interaction
+ if (aRequest) {
+ NS_ADDREF(*aRequest = channel);
+ }
+
+ if (aOriginalURI) {
+ channel->SetOriginalURI(aOriginalURI);
+ if (aLoadReplace) {
+ uint32_t loadFlags;
+ channel->GetLoadFlags(&loadFlags);
+ NS_ENSURE_SUCCESS(rv, rv);
+ channel->SetLoadFlags(loadFlags | nsIChannel::LOAD_REPLACE);
+ }
+ } else {
+ channel->SetOriginalURI(aURI);
+ }
+
+ if (aTypeHint && *aTypeHint) {
+ channel->SetContentType(nsDependentCString(aTypeHint));
+ mContentTypeHint = aTypeHint;
+ } else {
+ mContentTypeHint.Truncate();
+ }
+
+ if (!aFileName.IsVoid()) {
+ rv = channel->SetContentDisposition(nsIChannel::DISPOSITION_ATTACHMENT);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!aFileName.IsEmpty()) {
+ rv = channel->SetContentDispositionFilename(aFileName);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ }
+
+ if (mLoadType == LOAD_NORMAL_ALLOW_MIXED_CONTENT ||
+ mLoadType == LOAD_RELOAD_ALLOW_MIXED_CONTENT) {
+ rv = SetMixedContentChannel(channel);
+ NS_ENSURE_SUCCESS(rv, rv);
+ } else if (mMixedContentChannel) {
+ /*
+ * If the user "Disables Protection on This Page", we call
+ * SetMixedContentChannel for the first time, otherwise
+ * mMixedContentChannel is still null.
+ * Later, if the new channel passes a same orign check, we remember the
+ * users decision by calling SetMixedContentChannel using the new channel.
+ * This way, the user does not have to click the disable protection button
+ * over and over for browsing the same site.
+ */
+ rv = nsContentUtils::CheckSameOrigin(mMixedContentChannel, channel);
+ if (NS_FAILED(rv) || NS_FAILED(SetMixedContentChannel(channel))) {
+ SetMixedContentChannel(nullptr);
+ }
+ }
+
+ // hack
+ nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
+ nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal(
+ do_QueryInterface(channel));
+ if (httpChannelInternal) {
+ if (aForceAllowCookies) {
+ httpChannelInternal->SetThirdPartyFlags(
+ nsIHttpChannelInternal::THIRD_PARTY_FORCE_ALLOW);
+ }
+ if (aFirstParty) {
+ httpChannelInternal->SetDocumentURI(aURI);
+ } else {
+ httpChannelInternal->SetDocumentURI(aReferrerURI);
+ }
+ httpChannelInternal->SetRedirectMode(
+ nsIHttpChannelInternal::REDIRECT_MODE_MANUAL);
+ }
+
+ nsCOMPtr<nsIWritablePropertyBag2> props(do_QueryInterface(channel));
+ if (props) {
+ // save true referrer for those who need it (e.g. xpinstall whitelisting)
+ // Currently only http and ftp channels support this.
+ props->SetPropertyAsInterface(NS_LITERAL_STRING("docshell.internalReferrer"),
+ aReferrerURI);
+ }
+
+ nsCOMPtr<nsICacheInfoChannel> cacheChannel(do_QueryInterface(channel));
+ /* Get the cache Key from SH */
+ nsCOMPtr<nsISupports> cacheKey;
+ if (cacheChannel) {
+ if (mLSHE) {
+ mLSHE->GetCacheKey(getter_AddRefs(cacheKey));
+ } else if (mOSHE) { // for reload cases
+ mOSHE->GetCacheKey(getter_AddRefs(cacheKey));
+ }
+ }
+
+ // figure out if we need to set the post data stream on the channel...
+ if (aPostData) {
+ nsCOMPtr<nsIFormPOSTActionChannel> postChannel(do_QueryInterface(channel));
+ if (postChannel) {
+ // XXX it's a bit of a hack to rewind the postdata stream here but
+ // it has to be done in case the post data is being reused multiple
+ // times.
+ nsCOMPtr<nsISeekableStream> postDataSeekable =
+ do_QueryInterface(aPostData);
+ if (postDataSeekable) {
+ rv = postDataSeekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // we really need to have a content type associated with this stream!!
+ postChannel->SetUploadStream(aPostData, EmptyCString(), -1);
+ }
+
+ /* If there is a valid postdata *and* it is a History Load,
+ * set up the cache key on the channel, to retrieve the
+ * data *only* from the cache. If it is a normal reload, the
+ * cache is free to go to the server for updated postdata.
+ */
+ if (cacheChannel && cacheKey) {
+ if (mLoadType == LOAD_HISTORY ||
+ mLoadType == LOAD_RELOAD_CHARSET_CHANGE) {
+ cacheChannel->SetCacheKey(cacheKey);
+ uint32_t loadFlags;
+ if (NS_SUCCEEDED(channel->GetLoadFlags(&loadFlags))) {
+ channel->SetLoadFlags(
+ loadFlags | nsICachingChannel::LOAD_ONLY_FROM_CACHE);
+ }
+ } else if (mLoadType == LOAD_RELOAD_NORMAL) {
+ cacheChannel->SetCacheKey(cacheKey);
+ }
+ }
+ } else {
+ /* If there is no postdata, set the cache key on the channel, and
+ * do not set the LOAD_ONLY_FROM_CACHE flag, so that the channel
+ * will be free to get it from net if it is not found in cache.
+ * New cache may use it creatively on CGI pages with GET
+ * method and even on those that say "no-cache"
+ */
+ if (mLoadType == LOAD_HISTORY ||
+ mLoadType == LOAD_RELOAD_NORMAL ||
+ mLoadType == LOAD_RELOAD_CHARSET_CHANGE) {
+ if (cacheChannel && cacheKey) {
+ cacheChannel->SetCacheKey(cacheKey);
+ }
+ }
+ }
+
+ if (httpChannel) {
+ if (aHeadersData) {
+ rv = AddHeadersToChannel(aHeadersData, httpChannel);
+ }
+ // Set the referrer explicitly
+ if (aReferrerURI && aSendReferrer) {
+ // Referrer is currenly only set for link clicks here.
+ httpChannel->SetReferrerWithPolicy(aReferrerURI, aReferrerPolicy);
+ }
+ // set Content-Signature enforcing bit if aOriginalURI == about:newtab
+ if (aOriginalURI && httpChannel) {
+ if (IsAboutNewtab(aOriginalURI)) {
+ nsCOMPtr<nsILoadInfo> loadInfo = httpChannel->GetLoadInfo();
+ if (loadInfo) {
+ loadInfo->SetVerifySignedContent(true);
+ }
+ }
+ }
+ }
+
+ nsCOMPtr<nsIScriptChannel> scriptChannel = do_QueryInterface(channel);
+ if (scriptChannel) {
+ // Allow execution against our context if the principals match
+ scriptChannel->SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL);
+ }
+
+ if (aIsNewWindowTarget) {
+ nsCOMPtr<nsIWritablePropertyBag2> props = do_QueryInterface(channel);
+ if (props) {
+ props->SetPropertyAsBool(NS_LITERAL_STRING("docshell.newWindowTarget"),
+ true);
+ }
+ }
+
+ nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(channel));
+ if (timedChannel) {
+ timedChannel->SetTimingEnabled(true);
+
+ nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
+ if (IsFrame() && win) {
+ nsCOMPtr<Element> frameElement = win->GetFrameElementInternal();
+ if (frameElement) {
+ timedChannel->SetInitiatorType(frameElement->LocalName());
+ }
+ }
+ }
+
+ rv = DoChannelLoad(channel, uriLoader, aBypassClassifier);
+
+ //
+ // If the channel load failed, we failed and nsIWebProgress just ain't
+ // gonna happen.
+ //
+ if (NS_SUCCEEDED(rv)) {
+ if (aDocShell) {
+ *aDocShell = this;
+ NS_ADDREF(*aDocShell);
+ }
+ }
+
+ return rv;
+}
+
+static nsresult
+AppendSegmentToString(nsIInputStream* aIn,
+ void* aClosure,
+ const char* aFromRawSegment,
+ uint32_t aToOffset,
+ uint32_t aCount,
+ uint32_t* aWriteCount)
+{
+ // aFromSegment now contains aCount bytes of data.
+
+ nsAutoCString* buf = static_cast<nsAutoCString*>(aClosure);
+ buf->Append(aFromRawSegment, aCount);
+
+ // Indicate that we have consumed all of aFromSegment
+ *aWriteCount = aCount;
+ return NS_OK;
+}
+
+nsresult
+nsDocShell::AddHeadersToChannel(nsIInputStream* aHeadersData,
+ nsIChannel* aGenericChannel)
+{
+ nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aGenericChannel);
+ NS_ENSURE_STATE(httpChannel);
+
+ uint32_t numRead;
+ nsAutoCString headersString;
+ nsresult rv = aHeadersData->ReadSegments(AppendSegmentToString,
+ &headersString,
+ UINT32_MAX,
+ &numRead);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // used during the manipulation of the String from the InputStream
+ nsAutoCString headerName;
+ nsAutoCString headerValue;
+ int32_t crlf;
+ int32_t colon;
+
+ //
+ // Iterate over the headersString: for each "\r\n" delimited chunk,
+ // add the value as a header to the nsIHttpChannel
+ //
+
+ static const char kWhitespace[] = "\b\t\r\n ";
+ while (true) {
+ crlf = headersString.Find("\r\n");
+ if (crlf == kNotFound) {
+ return NS_OK;
+ }
+
+ const nsCSubstring& oneHeader = StringHead(headersString, crlf);
+
+ colon = oneHeader.FindChar(':');
+ if (colon == kNotFound) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ headerName = StringHead(oneHeader, colon);
+ headerValue = Substring(oneHeader, colon + 1);
+
+ headerName.Trim(kWhitespace);
+ headerValue.Trim(kWhitespace);
+
+ headersString.Cut(0, crlf + 2);
+
+ //
+ // FINALLY: we can set the header!
+ //
+
+ rv = httpChannel->SetRequestHeader(headerName, headerValue, true);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ NS_NOTREACHED("oops");
+ return NS_ERROR_UNEXPECTED;
+}
+
+nsresult
+nsDocShell::DoChannelLoad(nsIChannel* aChannel,
+ nsIURILoader* aURILoader,
+ bool aBypassClassifier)
+{
+ nsresult rv;
+ // Mark the channel as being a document URI and allow content sniffing...
+ nsLoadFlags loadFlags = 0;
+ (void)aChannel->GetLoadFlags(&loadFlags);
+ loadFlags |= nsIChannel::LOAD_DOCUMENT_URI |
+ nsIChannel::LOAD_CALL_CONTENT_SNIFFERS;
+
+ // Load attributes depend on load type...
+ switch (mLoadType) {
+ case LOAD_HISTORY: {
+ // Only send VALIDATE_NEVER if mLSHE's URI was never changed via
+ // push/replaceState (bug 669671).
+ bool uriModified = false;
+ if (mLSHE) {
+ mLSHE->GetURIWasModified(&uriModified);
+ }
+
+ if (!uriModified) {
+ loadFlags |= nsIRequest::VALIDATE_NEVER;
+ }
+ break;
+ }
+
+ case LOAD_RELOAD_CHARSET_CHANGE: {
+ // Use SetAllowStaleCacheContent (not LOAD_FROM_CACHE flag) since we only want
+ // to force cache load for this channel, not the whole loadGroup.
+ nsCOMPtr<nsICacheInfoChannel> cachingChannel = do_QueryInterface(aChannel);
+ if (cachingChannel) {
+ cachingChannel->SetAllowStaleCacheContent(true);
+ }
+ break;
+ }
+
+ case LOAD_RELOAD_NORMAL:
+ case LOAD_REFRESH:
+ loadFlags |= nsIRequest::VALIDATE_ALWAYS;
+ break;
+
+ case LOAD_NORMAL_BYPASS_CACHE:
+ case LOAD_NORMAL_BYPASS_PROXY:
+ case LOAD_NORMAL_BYPASS_PROXY_AND_CACHE:
+ case LOAD_NORMAL_ALLOW_MIXED_CONTENT:
+ case LOAD_RELOAD_BYPASS_CACHE:
+ case LOAD_RELOAD_BYPASS_PROXY:
+ case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
+ case LOAD_RELOAD_ALLOW_MIXED_CONTENT:
+ case LOAD_REPLACE_BYPASS_CACHE:
+ loadFlags |= nsIRequest::LOAD_BYPASS_CACHE |
+ nsIRequest::LOAD_FRESH_CONNECTION;
+ break;
+
+ case LOAD_NORMAL:
+ case LOAD_LINK:
+ // Set cache checking flags
+ switch (Preferences::GetInt("browser.cache.check_doc_frequency", -1)) {
+ case 0:
+ loadFlags |= nsIRequest::VALIDATE_ONCE_PER_SESSION;
+ break;
+ case 1:
+ loadFlags |= nsIRequest::VALIDATE_ALWAYS;
+ break;
+ case 2:
+ loadFlags |= nsIRequest::VALIDATE_NEVER;
+ break;
+ }
+ break;
+ }
+
+ if (!aBypassClassifier) {
+ loadFlags |= nsIChannel::LOAD_CLASSIFY_URI;
+ }
+
+ // If the user pressed shift-reload, then do not allow ServiceWorker
+ // interception to occur. See step 12.1 of the SW HandleFetch algorithm.
+ if (IsForceReloadType(mLoadType)) {
+ loadFlags |= nsIChannel::LOAD_BYPASS_SERVICE_WORKER;
+ }
+
+ (void)aChannel->SetLoadFlags(loadFlags);
+
+ uint32_t openFlags = 0;
+ if (mLoadType == LOAD_LINK) {
+ openFlags |= nsIURILoader::IS_CONTENT_PREFERRED;
+ }
+ if (!mAllowContentRetargeting) {
+ openFlags |= nsIURILoader::DONT_RETARGET;
+ }
+ rv = aURILoader->OpenURI(aChannel, openFlags, this);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+nsresult
+nsDocShell::ScrollToAnchor(bool aCurHasRef, bool aNewHasRef,
+ nsACString& aNewHash, uint32_t aLoadType)
+{
+ if (!mCurrentURI) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIPresShell> shell = GetPresShell();
+ if (!shell) {
+ // If we failed to get the shell, or if there is no shell,
+ // nothing left to do here.
+ return NS_OK;
+ }
+
+ nsIScrollableFrame* rootScroll = shell->GetRootScrollFrameAsScrollable();
+ if (rootScroll) {
+ rootScroll->ClearDidHistoryRestore();
+ }
+
+ // If we have no new anchor, we do not want to scroll, unless there is a
+ // current anchor and we are doing a history load. So return if we have no
+ // new anchor, and there is no current anchor or the load is not a history
+ // load.
+ if ((!aCurHasRef || aLoadType != LOAD_HISTORY) && !aNewHasRef) {
+ return NS_OK;
+ }
+
+ // Both the new and current URIs refer to the same page. We can now
+ // browse to the hash stored in the new URI.
+
+ if (!aNewHash.IsEmpty()) {
+ // anchor is there, but if it's a load from history,
+ // we don't have any anchor jumping to do
+ bool scroll = aLoadType != LOAD_HISTORY &&
+ aLoadType != LOAD_RELOAD_NORMAL;
+
+ char* str = ToNewCString(aNewHash);
+ if (!str) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // nsUnescape modifies the string that is passed into it.
+ nsUnescape(str);
+
+ // We assume that the bytes are in UTF-8, as it says in the
+ // spec:
+ // http://www.w3.org/TR/html4/appendix/notes.html#h-B.2.1
+
+ // We try the UTF-8 string first, and then try the document's
+ // charset (see below). If the string is not UTF-8,
+ // conversion will fail and give us an empty Unicode string.
+ // In that case, we should just fall through to using the
+ // page's charset.
+ nsresult rv = NS_ERROR_FAILURE;
+ NS_ConvertUTF8toUTF16 uStr(str);
+ if (!uStr.IsEmpty()) {
+ rv = shell->GoToAnchor(NS_ConvertUTF8toUTF16(str), scroll,
+ nsIPresShell::SCROLL_SMOOTH_AUTO);
+ }
+ free(str);
+
+ // Above will fail if the anchor name is not UTF-8. Need to
+ // convert from document charset to unicode.
+ if (NS_FAILED(rv)) {
+ // Get a document charset
+ NS_ENSURE_TRUE(mContentViewer, NS_ERROR_FAILURE);
+ nsIDocument* doc = mContentViewer->GetDocument();
+ NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
+ const nsACString& aCharset = doc->GetDocumentCharacterSet();
+
+ nsCOMPtr<nsITextToSubURI> textToSubURI =
+ do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Unescape and convert to unicode
+ nsXPIDLString uStr;
+
+ rv = textToSubURI->UnEscapeAndConvert(PromiseFlatCString(aCharset).get(),
+ PromiseFlatCString(aNewHash).get(),
+ getter_Copies(uStr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Ignore return value of GoToAnchor, since it will return an error
+ // if there is no such anchor in the document, which is actually a
+ // success condition for us (we want to update the session history
+ // with the new URI no matter whether we actually scrolled
+ // somewhere).
+ //
+ // When aNewHash contains "%00", unescaped string may be empty.
+ // And GoToAnchor asserts if we ask it to scroll to an empty ref.
+ shell->GoToAnchor(uStr, scroll && !uStr.IsEmpty(),
+ nsIPresShell::SCROLL_SMOOTH_AUTO);
+ }
+ } else {
+ // Tell the shell it's at an anchor, without scrolling.
+ shell->GoToAnchor(EmptyString(), false);
+
+ // An empty anchor was found, but if it's a load from history,
+ // we don't have to jump to the top of the page. Scrollbar
+ // position will be restored by the caller, based on positions
+ // stored in session history.
+ if (aLoadType == LOAD_HISTORY || aLoadType == LOAD_RELOAD_NORMAL) {
+ return NS_OK;
+ }
+ // An empty anchor. Scroll to the top of the page. Ignore the
+ // return value; failure to scroll here (e.g. if there is no
+ // root scrollframe) is not grounds for canceling the load!
+ SetCurScrollPosEx(0, 0);
+ }
+
+ return NS_OK;
+}
+
+void
+nsDocShell::SetupReferrerFromChannel(nsIChannel* aChannel)
+{
+ nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
+ if (httpChannel) {
+ nsCOMPtr<nsIURI> referrer;
+ nsresult rv = httpChannel->GetReferrer(getter_AddRefs(referrer));
+ if (NS_SUCCEEDED(rv)) {
+ SetReferrerURI(referrer);
+ }
+ uint32_t referrerPolicy;
+ rv = httpChannel->GetReferrerPolicy(&referrerPolicy);
+ if (NS_SUCCEEDED(rv)) {
+ SetReferrerPolicy(referrerPolicy);
+ }
+ }
+}
+
+bool
+nsDocShell::OnNewURI(nsIURI* aURI, nsIChannel* aChannel,
+ nsIPrincipal* aTriggeringPrincipal,
+ nsIPrincipal* aPrincipalToInherit,
+ uint32_t aLoadType, bool aFireOnLocationChange,
+ bool aAddToGlobalHistory, bool aCloneSHChildren)
+{
+ NS_PRECONDITION(aURI, "uri is null");
+ NS_PRECONDITION(!aChannel || !aTriggeringPrincipal, "Shouldn't have both set");
+
+ MOZ_ASSERT(!aPrincipalToInherit || (aPrincipalToInherit && aTriggeringPrincipal));
+
+#if defined(DEBUG)
+ if (MOZ_LOG_TEST(gDocShellLog, LogLevel::Debug)) {
+ nsAutoCString chanName;
+ if (aChannel) {
+ aChannel->GetName(chanName);
+ } else {
+ chanName.AssignLiteral("<no channel>");
+ }
+
+ MOZ_LOG(gDocShellLog, LogLevel::Debug,
+ ("nsDocShell[%p]::OnNewURI(\"%s\", [%s], 0x%x)\n",
+ this, aURI->GetSpecOrDefault().get(), chanName.get(), aLoadType));
+ }
+#endif
+
+ bool equalUri = false;
+
+ // Get the post data and the HTTP response code from the channel.
+ uint32_t responseStatus = 0;
+ nsCOMPtr<nsIInputStream> inputStream;
+ if (aChannel) {
+ nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
+
+ // Check if the HTTPChannel is hiding under a multiPartChannel
+ if (!httpChannel) {
+ GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
+ }
+
+ if (httpChannel) {
+ nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
+ if (uploadChannel) {
+ uploadChannel->GetUploadStream(getter_AddRefs(inputStream));
+ }
+
+ // If the response status indicates an error, unlink this session
+ // history entry from any entries sharing its document.
+ nsresult rv = httpChannel->GetResponseStatus(&responseStatus);
+ if (mLSHE && NS_SUCCEEDED(rv) && responseStatus >= 400) {
+ mLSHE->AbandonBFCacheEntry();
+ }
+ }
+ }
+
+ // Determine if this type of load should update history.
+ bool updateGHistory = !(aLoadType == LOAD_BYPASS_HISTORY ||
+ aLoadType == LOAD_ERROR_PAGE ||
+ aLoadType & LOAD_CMD_HISTORY);
+
+ // We don't update session history on reload unless we're loading
+ // an iframe in shift-reload case.
+ bool updateSHistory = updateGHistory &&
+ (!(aLoadType & LOAD_CMD_RELOAD) ||
+ (IsForceReloadType(aLoadType) && IsFrame()));
+
+ // Create SH Entry (mLSHE) only if there is a SessionHistory object in the
+ // current frame or in the root docshell.
+ nsCOMPtr<nsISHistory> rootSH = mSessionHistory;
+ if (!rootSH) {
+ // Get the handle to SH from the root docshell
+ GetRootSessionHistory(getter_AddRefs(rootSH));
+ if (!rootSH) {
+ updateSHistory = false;
+ updateGHistory = false; // XXX Why global history too?
+ }
+ }
+
+ // Check if the url to be loaded is the same as the one already loaded.
+ if (mCurrentURI) {
+ aURI->Equals(mCurrentURI, &equalUri);
+ }
+
+#ifdef DEBUG
+ bool shAvailable = (rootSH != nullptr);
+
+ // XXX This log message is almost useless because |updateSHistory|
+ // and |updateGHistory| are not correct at this point.
+
+ MOZ_LOG(gDocShellLog, LogLevel::Debug,
+ (" shAvailable=%i updateSHistory=%i updateGHistory=%i"
+ " equalURI=%i\n",
+ shAvailable, updateSHistory, updateGHistory, equalUri));
+
+ if (shAvailable && mCurrentURI && !mOSHE && aLoadType != LOAD_ERROR_PAGE) {
+ NS_ASSERTION(NS_IsAboutBlank(mCurrentURI),
+ "no SHEntry for a non-transient viewer?");
+ }
+#endif
+
+ /* If the url to be loaded is the same as the one already there,
+ * and the original loadType is LOAD_NORMAL, LOAD_LINK, or
+ * LOAD_STOP_CONTENT, set loadType to LOAD_NORMAL_REPLACE so that
+ * AddToSessionHistory() won't mess with the current SHEntry and
+ * if this page has any frame children, it also will be handled
+ * properly. see bug 83684
+ *
+ * NB: If mOSHE is null but we have a current URI, then it means
+ * that we must be at the transient about:blank content viewer
+ * (asserted above) and we should let the normal load continue,
+ * since there's nothing to replace.
+ *
+ * XXX Hopefully changing the loadType at this time will not hurt
+ * anywhere. The other way to take care of sequentially repeating
+ * frameset pages is to add new methods to nsIDocShellTreeItem.
+ * Hopefully I don't have to do that.
+ */
+ if (equalUri &&
+ mOSHE &&
+ (mLoadType == LOAD_NORMAL ||
+ mLoadType == LOAD_LINK ||
+ mLoadType == LOAD_STOP_CONTENT) &&
+ !inputStream) {
+ mLoadType = LOAD_NORMAL_REPLACE;
+ }
+
+ // If this is a refresh to the currently loaded url, we don't
+ // have to update session or global history.
+ if (mLoadType == LOAD_REFRESH && !inputStream && equalUri) {
+ SetHistoryEntry(&mLSHE, mOSHE);
+ }
+
+ /* If the user pressed shift-reload, cache will create a new cache key
+ * for the page. Save the new cacheKey in Session History.
+ * see bug 90098
+ */
+ if (aChannel && IsForceReloadType(aLoadType)) {
+ MOZ_ASSERT(!updateSHistory || IsFrame(),
+ "We shouldn't be updating session history for forced"
+ " reloads unless we're in a newly created iframe!");
+
+ nsCOMPtr<nsICacheInfoChannel> cacheChannel(do_QueryInterface(aChannel));
+ nsCOMPtr<nsISupports> cacheKey;
+ // Get the Cache Key and store it in SH.
+ if (cacheChannel) {
+ cacheChannel->GetCacheKey(getter_AddRefs(cacheKey));
+ }
+ // If we already have a loading history entry, store the new cache key
+ // in it. Otherwise, since we're doing a reload and won't be updating
+ // our history entry, store the cache key in our current history entry.
+ if (mLSHE) {
+ mLSHE->SetCacheKey(cacheKey);
+ } else if (mOSHE) {
+ mOSHE->SetCacheKey(cacheKey);
+ }
+
+ // Since we're force-reloading, clear all the sub frame history.
+ ClearFrameHistory(mLSHE);
+ ClearFrameHistory(mOSHE);
+ }
+
+ if (aLoadType == LOAD_RELOAD_NORMAL) {
+ nsCOMPtr<nsISHEntry> currentSH;
+ bool oshe = false;
+ GetCurrentSHEntry(getter_AddRefs(currentSH), &oshe);
+ bool dynamicallyAddedChild = false;
+ if (currentSH) {
+ currentSH->HasDynamicallyAddedChild(&dynamicallyAddedChild);
+ }
+ if (dynamicallyAddedChild) {
+ ClearFrameHistory(currentSH);
+ }
+ }
+
+ if (aLoadType == LOAD_REFRESH) {
+ ClearFrameHistory(mLSHE);
+ ClearFrameHistory(mOSHE);
+ }
+
+ if (updateSHistory) {
+ // Update session history if necessary...
+ if (!mLSHE && (mItemType == typeContent) && mURIResultedInDocument) {
+ /* This is a fresh page getting loaded for the first time
+ *.Create a Entry for it and add it to SH, if this is the
+ * rootDocShell
+ */
+ (void)AddToSessionHistory(aURI, aChannel, aTriggeringPrincipal,
+ aPrincipalToInherit, aCloneSHChildren,
+ getter_AddRefs(mLSHE));
+ }
+ } else if (mSessionHistory && mLSHE && mURIResultedInDocument) {
+ // Even if we don't add anything to SHistory, ensure the current index
+ // points to the same SHEntry as our mLSHE.
+ int32_t index = 0;
+ mSessionHistory->GetRequestedIndex(&index);
+ if (index == -1) {
+ mSessionHistory->GetIndex(&index);
+ }
+ nsCOMPtr<nsISHEntry> currentSH;
+ mSessionHistory->GetEntryAtIndex(index, false, getter_AddRefs(currentSH));
+ if (currentSH != mLSHE) {
+ nsCOMPtr<nsISHistoryInternal> shPrivate =
+ do_QueryInterface(mSessionHistory);
+ shPrivate->ReplaceEntry(index, mLSHE);
+ }
+ }
+
+ // If this is a POST request, we do not want to include this in global
+ // history.
+ if (updateGHistory && aAddToGlobalHistory && !ChannelIsPost(aChannel)) {
+ nsCOMPtr<nsIURI> previousURI;
+ uint32_t previousFlags = 0;
+
+ if (aLoadType & LOAD_CMD_RELOAD) {
+ // On a reload request, we don't set redirecting flags.
+ previousURI = aURI;
+ } else {
+ ExtractLastVisit(aChannel, getter_AddRefs(previousURI), &previousFlags);
+ }
+
+ // Note: We don't use |referrer| when our global history is
+ // based on IHistory.
+ nsCOMPtr<nsIURI> referrer;
+ // Treat referrer as null if there is an error getting it.
+ (void)NS_GetReferrerFromChannel(aChannel, getter_AddRefs(referrer));
+
+ AddURIVisit(aURI, referrer, previousURI, previousFlags, responseStatus);
+ }
+
+ // If this was a history load or a refresh, or it was a history load but
+ // later changed to LOAD_NORMAL_REPLACE due to redirection, update the index
+ // in session history.
+ if (rootSH &&
+ ((mLoadType & (LOAD_CMD_HISTORY | LOAD_CMD_RELOAD)) ||
+ mLoadType == LOAD_NORMAL_REPLACE)) {
+ nsCOMPtr<nsISHistoryInternal> shInternal(do_QueryInterface(rootSH));
+ if (shInternal) {
+ rootSH->GetIndex(&mPreviousTransIndex);
+ shInternal->UpdateIndex();
+ rootSH->GetIndex(&mLoadedTransIndex);
+#ifdef DEBUG_PAGE_CACHE
+ printf("Previous index: %d, Loaded index: %d\n\n",
+ mPreviousTransIndex, mLoadedTransIndex);
+#endif
+ }
+ }
+
+ // aCloneSHChildren exactly means "we are not loading a new document".
+ uint32_t locationFlags =
+ aCloneSHChildren ? uint32_t(LOCATION_CHANGE_SAME_DOCUMENT) : 0;
+
+ bool onLocationChangeNeeded = SetCurrentURI(aURI, aChannel,
+ aFireOnLocationChange,
+ locationFlags);
+ // Make sure to store the referrer from the channel, if any
+ SetupReferrerFromChannel(aChannel);
+ return onLocationChangeNeeded;
+}
+
+bool
+nsDocShell::OnLoadingSite(nsIChannel* aChannel, bool aFireOnLocationChange,
+ bool aAddToGlobalHistory)
+{
+ nsCOMPtr<nsIURI> uri;
+ // If this a redirect, use the final url (uri)
+ // else use the original url
+ //
+ // Note that this should match what documents do (see nsDocument::Reset).
+ NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
+ NS_ENSURE_TRUE(uri, false);
+
+ // Pass false for aCloneSHChildren, since we're loading a new page here.
+ return OnNewURI(uri, aChannel, nullptr, nullptr, mLoadType, aFireOnLocationChange,
+ aAddToGlobalHistory, false);
+}
+
+void
+nsDocShell::SetReferrerURI(nsIURI* aURI)
+{
+ mReferrerURI = aURI; // This assigment addrefs
+}
+
+void
+nsDocShell::SetReferrerPolicy(uint32_t aReferrerPolicy)
+{
+ mReferrerPolicy = aReferrerPolicy;
+}
+
+//*****************************************************************************
+// nsDocShell: Session History
+//*****************************************************************************
+
+NS_IMETHODIMP
+nsDocShell::AddState(JS::Handle<JS::Value> aData, const nsAString& aTitle,
+ const nsAString& aURL, bool aReplace, JSContext* aCx)
+{
+ // Implements History.pushState and History.replaceState
+
+ // Here's what we do, roughly in the order specified by HTML5:
+ // 1. Serialize aData using structured clone.
+ // 2. If the third argument is present,
+ // a. Resolve the url, relative to the first script's base URL
+ // b. If (a) fails, raise a SECURITY_ERR
+ // c. Compare the resulting absolute URL to the document's address. If
+ // any part of the URLs difer other than the <path>, <query>, and
+ // <fragment> components, raise a SECURITY_ERR and abort.
+ // 3. If !aReplace:
+ // Remove from the session history all entries after the current entry,
+ // as we would after a regular navigation, and save the current
+ // entry's scroll position (bug 590573).
+ // 4. As apropriate, either add a state object entry to the session history
+ // after the current entry with the following properties, or modify the
+ // current session history entry to set
+ // a. cloned data as the state object,
+ // b. if the third argument was present, the absolute URL found in
+ // step 2
+ // Also clear the new history entry's POST data (see bug 580069).
+ // 5. If aReplace is false (i.e. we're doing a pushState instead of a
+ // replaceState), notify bfcache that we've navigated to a new page.
+ // 6. If the third argument is present, set the document's current address
+ // to the absolute URL found in step 2.
+ //
+ // It's important that this function not run arbitrary scripts after step 1
+ // and before completing step 5. For example, if a script called
+ // history.back() before we completed step 5, bfcache might destroy an
+ // active content viewer. Since EvictOutOfRangeContentViewers at the end of
+ // step 5 might run script, we can't just put a script blocker around the
+ // critical section.
+ //
+ // Note that we completely ignore the aTitle parameter.
+
+ nsresult rv;
+
+ // Don't clobber the load type of an existing network load.
+ AutoRestore<uint32_t> loadTypeResetter(mLoadType);
+
+ // pushState effectively becomes replaceState when we've started a network
+ // load but haven't adopted its document yet. This mirrors what we do with
+ // changes to the hash at this stage of the game.
+ if (JustStartedNetworkLoad()) {
+ aReplace = true;
+ }
+
+ nsCOMPtr<nsIDocument> document = GetDocument();
+ NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
+
+ // Step 1: Serialize aData using structured clone.
+ nsCOMPtr<nsIStructuredCloneContainer> scContainer;
+
+ // scContainer->Init might cause arbitrary JS to run, and this code might
+ // navigate the page we're on, potentially to a different origin! (bug
+ // 634834) To protect against this, we abort if our principal changes due
+ // to the InitFromJSVal() call.
+ {
+ nsCOMPtr<nsIDocument> origDocument = GetDocument();
+ if (!origDocument) {
+ return NS_ERROR_DOM_SECURITY_ERR;
+ }
+ nsCOMPtr<nsIPrincipal> origPrincipal = origDocument->NodePrincipal();
+
+ scContainer = new nsStructuredCloneContainer();
+ rv = scContainer->InitFromJSVal(aData, aCx);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIDocument> newDocument = GetDocument();
+ if (!newDocument) {
+ return NS_ERROR_DOM_SECURITY_ERR;
+ }
+ nsCOMPtr<nsIPrincipal> newPrincipal = newDocument->NodePrincipal();
+
+ bool principalsEqual = false;
+ origPrincipal->Equals(newPrincipal, &principalsEqual);
+ NS_ENSURE_TRUE(principalsEqual, NS_ERROR_DOM_SECURITY_ERR);
+ }
+
+ // Check that the state object isn't too long.
+ // Default max length: 640k bytes.
+ int32_t maxStateObjSize =
+ Preferences::GetInt("browser.history.maxStateObjectSize", 0xA0000);
+ if (maxStateObjSize < 0) {
+ maxStateObjSize = 0;
+ }
+
+ uint64_t scSize;
+ rv = scContainer->GetSerializedNBytes(&scSize);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_ENSURE_TRUE(scSize <= (uint32_t)maxStateObjSize, NS_ERROR_ILLEGAL_VALUE);
+
+ // Step 2: Resolve aURL
+ bool equalURIs = true;
+ nsCOMPtr<nsIURI> currentURI;
+ if (sURIFixup && mCurrentURI) {
+ rv = sURIFixup->CreateExposableURI(mCurrentURI, getter_AddRefs(currentURI));
+ NS_ENSURE_SUCCESS(rv, rv);
+ } else {
+ currentURI = mCurrentURI;
+ }
+ nsCOMPtr<nsIURI> oldURI = currentURI;
+ nsCOMPtr<nsIURI> newURI;
+ if (aURL.Length() == 0) {
+ newURI = currentURI;
+ } else {
+ // 2a: Resolve aURL relative to mURI
+
+ nsIURI* docBaseURI = document->GetDocBaseURI();
+ if (!docBaseURI) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsAutoCString spec;
+ docBaseURI->GetSpec(spec);
+
+ nsAutoCString charset;
+ rv = docBaseURI->GetOriginCharset(charset);
+ NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+
+ rv = NS_NewURI(getter_AddRefs(newURI), aURL, charset.get(), docBaseURI);
+
+ // 2b: If 2a fails, raise a SECURITY_ERR
+ if (NS_FAILED(rv)) {
+ return NS_ERROR_DOM_SECURITY_ERR;
+ }
+
+ // 2c: Same-origin check.
+ if (!nsContentUtils::URIIsLocalFile(newURI)) {
+ // In addition to checking that the security manager says that
+ // the new URI has the same origin as our current URI, we also
+ // check that the two URIs have the same userpass. (The
+ // security manager says that |http://foo.com| and
+ // |http://me@foo.com| have the same origin.) currentURI
+ // won't contain the password part of the userpass, so this
+ // means that it's never valid to specify a password in a
+ // pushState or replaceState URI.
+
+ nsCOMPtr<nsIScriptSecurityManager> secMan =
+ do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
+ NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE);
+
+ // It's very important that we check that newURI is of the same
+ // origin as currentURI, not docBaseURI, because a page can
+ // set docBaseURI arbitrarily to any domain.
+ nsAutoCString currentUserPass, newUserPass;
+ NS_ENSURE_SUCCESS(currentURI->GetUserPass(currentUserPass),
+ NS_ERROR_FAILURE);
+ NS_ENSURE_SUCCESS(newURI->GetUserPass(newUserPass), NS_ERROR_FAILURE);
+ if (NS_FAILED(secMan->CheckSameOriginURI(currentURI, newURI, true)) ||
+ !currentUserPass.Equals(newUserPass)) {
+ return NS_ERROR_DOM_SECURITY_ERR;
+ }
+ } else {
+ // It's a file:// URI
+ nsCOMPtr<nsIScriptObjectPrincipal> docScriptObj =
+ do_QueryInterface(document);
+
+ if (!docScriptObj) {
+ return NS_ERROR_DOM_SECURITY_ERR;
+ }
+
+ nsCOMPtr<nsIPrincipal> principal = docScriptObj->GetPrincipal();
+
+ if (!principal ||
+ NS_FAILED(principal->CheckMayLoad(newURI, true, false))) {
+ return NS_ERROR_DOM_SECURITY_ERR;
+ }
+ }
+
+ if (currentURI) {
+ currentURI->Equals(newURI, &equalURIs);
+ } else {
+ equalURIs = false;
+ }
+
+ } // end of same-origin check
+
+ // Step 3: Create a new entry in the session history. This will erase
+ // all SHEntries after the new entry and make this entry the current
+ // one. This operation may modify mOSHE, which we need later, so we
+ // keep a reference here.
+ NS_ENSURE_TRUE(mOSHE, NS_ERROR_FAILURE);
+ nsCOMPtr<nsISHEntry> oldOSHE = mOSHE;
+
+ mLoadType = LOAD_PUSHSTATE;
+
+ nsCOMPtr<nsISHEntry> newSHEntry;
+ if (!aReplace) {
+ // Save the current scroll position (bug 590573).
+ nscoord cx = 0, cy = 0;
+ GetCurScrollPos(ScrollOrientation_X, &cx);
+ GetCurScrollPos(ScrollOrientation_Y, &cy);
+ mOSHE->SetScrollPosition(cx, cy);
+
+ bool scrollRestorationIsManual = false;
+ mOSHE->GetScrollRestorationIsManual(&scrollRestorationIsManual);
+
+ // Since we're not changing which page we have loaded, pass
+ // true for aCloneChildren.
+ rv = AddToSessionHistory(newURI, nullptr, nullptr, nullptr, true,
+ getter_AddRefs(newSHEntry));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_ENSURE_TRUE(newSHEntry, NS_ERROR_FAILURE);
+
+ // Session history entries created by pushState inherit scroll restoration
+ // mode from the current entry.
+ newSHEntry->SetScrollRestorationIsManual(scrollRestorationIsManual);
+
+ // Link the new SHEntry to the old SHEntry's BFCache entry, since the
+ // two entries correspond to the same document.
+ NS_ENSURE_SUCCESS(newSHEntry->AdoptBFCacheEntry(oldOSHE), NS_ERROR_FAILURE);
+
+ // Set the new SHEntry's title (bug 655273).
+ nsString title;
+ mOSHE->GetTitle(getter_Copies(title));
+ newSHEntry->SetTitle(title);
+
+ // AddToSessionHistory may not modify mOSHE. In case it doesn't,
+ // we'll just set mOSHE here.
+ mOSHE = newSHEntry;
+
+ } else {
+ newSHEntry = mOSHE;
+ newSHEntry->SetURI(newURI);
+ newSHEntry->SetOriginalURI(newURI);
+ newSHEntry->SetLoadReplace(false);
+ }
+
+ // Step 4: Modify new/original session history entry and clear its POST
+ // data, if there is any.
+ newSHEntry->SetStateData(scContainer);
+ newSHEntry->SetPostData(nullptr);
+
+ // If this push/replaceState changed the document's current URI and the new
+ // URI differs from the old URI in more than the hash, or if the old
+ // SHEntry's URI was modified in this way by a push/replaceState call
+ // set URIWasModified to true for the current SHEntry (bug 669671).
+ bool sameExceptHashes = true, oldURIWasModified = false;
+ newURI->EqualsExceptRef(currentURI, &sameExceptHashes);
+ oldOSHE->GetURIWasModified(&oldURIWasModified);
+ newSHEntry->SetURIWasModified(!sameExceptHashes || oldURIWasModified);
+
+ // Step 5: If aReplace is false, indicating that we're doing a pushState
+ // rather than a replaceState, notify bfcache that we've added a page to
+ // the history so it can evict content viewers if appropriate. Otherwise
+ // call ReplaceEntry so that we notify nsIHistoryListeners that an entry
+ // was replaced.
+ nsCOMPtr<nsISHistory> rootSH;
+ GetRootSessionHistory(getter_AddRefs(rootSH));
+ NS_ENSURE_TRUE(rootSH, NS_ERROR_UNEXPECTED);
+
+ nsCOMPtr<nsISHistoryInternal> internalSH = do_QueryInterface(rootSH);
+ NS_ENSURE_TRUE(internalSH, NS_ERROR_UNEXPECTED);
+
+ if (!aReplace) {
+ int32_t curIndex = -1;
+ rv = rootSH->GetIndex(&curIndex);
+ if (NS_SUCCEEDED(rv) && curIndex > -1) {
+ internalSH->EvictOutOfRangeContentViewers(curIndex);
+ }
+ } else {
+ nsCOMPtr<nsISHEntry> rootSHEntry = GetRootSHEntry(newSHEntry);
+
+ int32_t index = -1;
+ rv = rootSH->GetIndexOfEntry(rootSHEntry, &index);
+ if (NS_SUCCEEDED(rv) && index > -1) {
+ internalSH->ReplaceEntry(index, rootSHEntry);
+ }
+ }
+
+ // Step 6: If the document's URI changed, update document's URI and update
+ // global history.
+ //
+ // We need to call FireOnLocationChange so that the browser's address bar
+ // gets updated and the back button is enabled, but we only need to
+ // explicitly call FireOnLocationChange if we're not calling SetCurrentURI,
+ // since SetCurrentURI will call FireOnLocationChange for us.
+ //
+ // Both SetCurrentURI(...) and FireDummyOnLocationChange() pass
+ // nullptr for aRequest param to FireOnLocationChange(...). Such an update
+ // notification is allowed only when we know docshell is not loading a new
+ // document and it requires LOCATION_CHANGE_SAME_DOCUMENT flag. Otherwise,
+ // FireOnLocationChange(...) breaks security UI.
+ if (!equalURIs) {
+ document->SetDocumentURI(newURI);
+ // We can't trust SetCurrentURI to do always fire locationchange events
+ // when we expect it to, so we hack around that by doing it ourselves...
+ SetCurrentURI(newURI, nullptr, false, LOCATION_CHANGE_SAME_DOCUMENT);
+ if (mLoadType != LOAD_ERROR_PAGE) {
+ FireDummyOnLocationChange();
+ }
+
+ AddURIVisit(newURI, oldURI, oldURI, 0);
+
+ // AddURIVisit doesn't set the title for the new URI in global history,
+ // so do that here.
+ if (mUseGlobalHistory && !UsePrivateBrowsing()) {
+ nsCOMPtr<IHistory> history = services::GetHistoryService();
+ if (history) {
+ history->SetURITitle(newURI, mTitle);
+ } else if (mGlobalHistory) {
+ mGlobalHistory->SetPageTitle(newURI, mTitle);
+ }
+ }
+
+ // Inform the favicon service that our old favicon applies to this new
+ // URI.
+ CopyFavicon(oldURI, newURI, document->NodePrincipal(), UsePrivateBrowsing());
+ } else {
+ FireDummyOnLocationChange();
+ }
+ document->SetStateObject(scContainer);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetCurrentScrollRestorationIsManual(bool* aIsManual)
+{
+ *aIsManual = false;
+ if (mOSHE) {
+ mOSHE->GetScrollRestorationIsManual(aIsManual);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetCurrentScrollRestorationIsManual(bool aIsManual)
+{
+ if (mOSHE) {
+ mOSHE->SetScrollRestorationIsManual(aIsManual);
+ }
+
+ return NS_OK;
+}
+
+bool
+nsDocShell::ShouldAddToSessionHistory(nsIURI* aURI)
+{
+ // I believe none of the about: urls should go in the history. But then
+ // that could just be me... If the intent is only deny about:blank then we
+ // should just do a spec compare, rather than two gets of the scheme and
+ // then the path. -Gagan
+ nsresult rv;
+ nsAutoCString buf;
+
+ rv = aURI->GetScheme(buf);
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+
+ if (buf.EqualsLiteral("about")) {
+ rv = aURI->GetPath(buf);
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+
+ if (buf.EqualsLiteral("blank") || buf.EqualsLiteral("newtab")) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+nsresult
+nsDocShell::AddToSessionHistory(nsIURI* aURI, nsIChannel* aChannel,
+ nsIPrincipal* aTriggeringPrincipal,
+ nsIPrincipal* aPrincipalToInherit,
+ bool aCloneChildren,
+ nsISHEntry** aNewEntry)
+{
+ NS_PRECONDITION(aURI, "uri is null");
+ NS_PRECONDITION(!aChannel || !aTriggeringPrincipal, "Shouldn't have both set");
+
+#if defined(DEBUG)
+ if (MOZ_LOG_TEST(gDocShellLog, LogLevel::Debug)) {
+ nsAutoCString chanName;
+ if (aChannel) {
+ aChannel->GetName(chanName);
+ } else {
+ chanName.AssignLiteral("<no channel>");
+ }
+
+ MOZ_LOG(gDocShellLog, LogLevel::Debug,
+ ("nsDocShell[%p]::AddToSessionHistory(\"%s\", [%s])\n",
+ this, aURI->GetSpecOrDefault().get(), chanName.get()));
+ }
+#endif
+
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsISHEntry> entry;
+ bool shouldPersist;
+
+ shouldPersist = ShouldAddToSessionHistory(aURI);
+
+ // Get a handle to the root docshell
+ nsCOMPtr<nsIDocShellTreeItem> root;
+ GetSameTypeRootTreeItem(getter_AddRefs(root));
+ /*
+ * If this is a LOAD_FLAGS_REPLACE_HISTORY in a subframe, we use
+ * the existing SH entry in the page and replace the url and
+ * other vitalities.
+ */
+ if (LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY) &&
+ root != static_cast<nsIDocShellTreeItem*>(this)) {
+ // This is a subframe
+ entry = mOSHE;
+ nsCOMPtr<nsISHContainer> shContainer(do_QueryInterface(entry));
+ if (shContainer) {
+ int32_t childCount = 0;
+ shContainer->GetChildCount(&childCount);
+ // Remove all children of this entry
+ for (int32_t i = childCount - 1; i >= 0; i--) {
+ nsCOMPtr<nsISHEntry> child;
+ shContainer->GetChildAt(i, getter_AddRefs(child));
+ shContainer->RemoveChild(child);
+ }
+ entry->AbandonBFCacheEntry();
+ }
+ }
+
+ // Create a new entry if necessary.
+ if (!entry) {
+ entry = do_CreateInstance(NS_SHENTRY_CONTRACTID);
+
+ if (!entry) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+
+ // Get the post data & referrer
+ nsCOMPtr<nsIInputStream> inputStream;
+ nsCOMPtr<nsIURI> originalURI;
+ bool loadReplace = false;
+ nsCOMPtr<nsIURI> referrerURI;
+ uint32_t referrerPolicy = mozilla::net::RP_Default;
+ nsCOMPtr<nsISupports> cacheKey;
+ nsCOMPtr<nsIPrincipal> triggeringPrincipal = aTriggeringPrincipal;
+ nsCOMPtr<nsIPrincipal> principalToInherit = aPrincipalToInherit;
+ bool expired = false;
+ bool discardLayoutState = false;
+ nsCOMPtr<nsICacheInfoChannel> cacheChannel;
+ if (aChannel) {
+ cacheChannel = do_QueryInterface(aChannel);
+
+ /* If there is a caching channel, get the Cache Key and store it
+ * in SH.
+ */
+ if (cacheChannel) {
+ cacheChannel->GetCacheKey(getter_AddRefs(cacheKey));
+ }
+ nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
+
+ // Check if the httpChannel is hiding under a multipartChannel
+ if (!httpChannel) {
+ GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
+ }
+ if (httpChannel) {
+ nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
+ if (uploadChannel) {
+ uploadChannel->GetUploadStream(getter_AddRefs(inputStream));
+ }
+ httpChannel->GetOriginalURI(getter_AddRefs(originalURI));
+ uint32_t loadFlags;
+ aChannel->GetLoadFlags(&loadFlags);
+ loadReplace = loadFlags & nsIChannel::LOAD_REPLACE;
+ httpChannel->GetReferrer(getter_AddRefs(referrerURI));
+ httpChannel->GetReferrerPolicy(&referrerPolicy);
+
+ discardLayoutState = ShouldDiscardLayoutState(httpChannel);
+ }
+
+ // XXX Bug 1286838: Replace channel owner with loadInfo triggeringPrincipal
+ nsCOMPtr<nsISupports> owner;
+ aChannel->GetOwner(getter_AddRefs(owner));
+ triggeringPrincipal = do_QueryInterface(owner);
+
+ nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
+ if (loadInfo) {
+ if (!triggeringPrincipal) {
+ triggeringPrincipal = loadInfo->TriggeringPrincipal();
+ }
+
+ // For now keep storing just the principal in the SHEntry.
+ if (!principalToInherit) {
+ if (loadInfo->GetLoadingSandboxed()) {
+ if (loadInfo->LoadingPrincipal()) {
+ principalToInherit = nsNullPrincipal::CreateWithInheritedAttributes(
+ loadInfo->LoadingPrincipal());
+ } else {
+ // get the OriginAttributes
+ NeckoOriginAttributes nAttrs;
+ loadInfo->GetOriginAttributes(&nAttrs);
+ PrincipalOriginAttributes pAttrs;
+ pAttrs.InheritFromNecko(nAttrs);
+ principalToInherit = nsNullPrincipal::Create(pAttrs);
+ }
+ } else {
+ principalToInherit = loadInfo->PrincipalToInherit();
+ }
+ }
+ }
+ }
+
+ // Title is set in nsDocShell::SetTitle()
+ entry->Create(aURI, // uri
+ EmptyString(), // Title
+ inputStream, // Post data stream
+ nullptr, // LayoutHistory state
+ cacheKey, // CacheKey
+ mContentTypeHint, // Content-type
+ triggeringPrincipal, // Channel or provided principal
+ principalToInherit,
+ mHistoryID,
+ mDynamicallyCreated);
+
+ entry->SetOriginalURI(originalURI);
+ entry->SetLoadReplace(loadReplace);
+ entry->SetReferrerURI(referrerURI);
+ entry->SetReferrerPolicy(referrerPolicy);
+ nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(aChannel);
+ if (inStrmChan) {
+ bool isSrcdocChannel;
+ inStrmChan->GetIsSrcdocChannel(&isSrcdocChannel);
+ if (isSrcdocChannel) {
+ nsAutoString srcdoc;
+ inStrmChan->GetSrcdocData(srcdoc);
+ entry->SetSrcdocData(srcdoc);
+ nsCOMPtr<nsIURI> baseURI;
+ inStrmChan->GetBaseURI(getter_AddRefs(baseURI));
+ entry->SetBaseURI(baseURI);
+ }
+ }
+ /* If cache got a 'no-store', ask SH not to store
+ * HistoryLayoutState. By default, SH will set this
+ * flag to true and save HistoryLayoutState.
+ */
+ if (discardLayoutState) {
+ entry->SetSaveLayoutStateFlag(false);
+ }
+ if (cacheChannel) {
+ // Check if the page has expired from cache
+ uint32_t expTime = 0;
+ cacheChannel->GetCacheTokenExpirationTime(&expTime);
+ uint32_t now = PRTimeToSeconds(PR_Now());
+ if (expTime <= now) {
+ expired = true;
+ }
+ }
+ if (expired) {
+ entry->SetExpirationStatus(true);
+ }
+
+ if (root == static_cast<nsIDocShellTreeItem*>(this) && mSessionHistory) {
+ // If we need to clone our children onto the new session
+ // history entry, do so now.
+ if (aCloneChildren && mOSHE) {
+ uint32_t cloneID;
+ mOSHE->GetID(&cloneID);
+ nsCOMPtr<nsISHEntry> newEntry;
+ CloneAndReplace(mOSHE, this, cloneID, entry, true,
+ getter_AddRefs(newEntry));
+ NS_ASSERTION(entry == newEntry,
+ "The new session history should be in the new entry");
+ }
+
+ // This is the root docshell
+ bool addToSHistory = !LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY);
+ if (!addToSHistory) {
+ // Replace current entry in session history; If the requested index is
+ // valid, it indicates the loading was triggered by a history load, and
+ // we should replace the entry at requested index instead.
+ int32_t index = 0;
+ mSessionHistory->GetRequestedIndex(&index);
+ if (index == -1) {
+ mSessionHistory->GetIndex(&index);
+ }
+ nsCOMPtr<nsISHistoryInternal> shPrivate =
+ do_QueryInterface(mSessionHistory);
+ // Replace the current entry with the new entry
+ if (index >= 0) {
+ if (shPrivate) {
+ rv = shPrivate->ReplaceEntry(index, entry);
+ }
+ } else {
+ // If we're trying to replace an inexistant shistory entry, append.
+ addToSHistory = true;
+ }
+ }
+
+ if (addToSHistory) {
+ // Add to session history
+ nsCOMPtr<nsISHistoryInternal> shPrivate =
+ do_QueryInterface(mSessionHistory);
+ NS_ENSURE_TRUE(shPrivate, NS_ERROR_FAILURE);
+ mSessionHistory->GetIndex(&mPreviousTransIndex);
+ rv = shPrivate->AddEntry(entry, shouldPersist);
+ mSessionHistory->GetIndex(&mLoadedTransIndex);
+#ifdef DEBUG_PAGE_CACHE
+ printf("Previous index: %d, Loaded index: %d\n\n",
+ mPreviousTransIndex, mLoadedTransIndex);
+#endif
+ }
+ } else {
+ // This is a subframe.
+ if (!mOSHE || !LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY)) {
+ rv = AddChildSHEntryToParent(entry, mChildOffset, aCloneChildren);
+ }
+ }
+
+ // Return the new SH entry...
+ if (aNewEntry) {
+ *aNewEntry = nullptr;
+ if (NS_SUCCEEDED(rv)) {
+ entry.forget(aNewEntry);
+ }
+ }
+
+ return rv;
+}
+
+nsresult
+nsDocShell::LoadHistoryEntry(nsISHEntry* aEntry, uint32_t aLoadType)
+{
+ if (!IsNavigationAllowed()) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIURI> uri;
+ nsCOMPtr<nsIURI> originalURI;
+ bool loadReplace = false;
+ nsCOMPtr<nsIInputStream> postData;
+ nsCOMPtr<nsIURI> referrerURI;
+ uint32_t referrerPolicy;
+ nsAutoCString contentType;
+ nsCOMPtr<nsIPrincipal> triggeringPrincipal;
+ nsCOMPtr<nsIPrincipal> principalToInherit;
+
+ NS_ENSURE_TRUE(aEntry, NS_ERROR_FAILURE);
+
+ NS_ENSURE_SUCCESS(aEntry->GetURI(getter_AddRefs(uri)), NS_ERROR_FAILURE);
+ NS_ENSURE_SUCCESS(aEntry->GetOriginalURI(getter_AddRefs(originalURI)),
+ NS_ERROR_FAILURE);
+ NS_ENSURE_SUCCESS(aEntry->GetLoadReplace(&loadReplace),
+ NS_ERROR_FAILURE);
+ NS_ENSURE_SUCCESS(aEntry->GetReferrerURI(getter_AddRefs(referrerURI)),
+ NS_ERROR_FAILURE);
+ NS_ENSURE_SUCCESS(aEntry->GetReferrerPolicy(&referrerPolicy),
+ NS_ERROR_FAILURE);
+ NS_ENSURE_SUCCESS(aEntry->GetPostData(getter_AddRefs(postData)),
+ NS_ERROR_FAILURE);
+ NS_ENSURE_SUCCESS(aEntry->GetContentType(contentType), NS_ERROR_FAILURE);
+ NS_ENSURE_SUCCESS(aEntry->GetTriggeringPrincipal(getter_AddRefs(triggeringPrincipal)),
+ NS_ERROR_FAILURE);
+ NS_ENSURE_SUCCESS(aEntry->GetPrincipalToInherit(getter_AddRefs(principalToInherit)),
+ NS_ERROR_FAILURE);
+
+ // Calling CreateAboutBlankContentViewer can set mOSHE to null, and if
+ // that's the only thing holding a ref to aEntry that will cause aEntry to
+ // die while we're loading it. So hold a strong ref to aEntry here, just
+ // in case.
+ nsCOMPtr<nsISHEntry> kungFuDeathGrip(aEntry);
+ bool isJS;
+ nsresult rv = uri->SchemeIs("javascript", &isJS);
+ if (NS_FAILED(rv) || isJS) {
+ // We're loading a URL that will execute script from inside asyncOpen.
+ // Replace the current document with about:blank now to prevent
+ // anything from the current document from leaking into any JavaScript
+ // code in the URL.
+ // Don't cache the presentation if we're going to just reload the
+ // current entry. Caching would lead to trying to save the different
+ // content viewers in the same nsISHEntry object.
+ rv = CreateAboutBlankContentViewer(principalToInherit, nullptr,
+ aEntry != mOSHE);
+
+ if (NS_FAILED(rv)) {
+ // The creation of the intermittent about:blank content
+ // viewer failed for some reason (potentially because the
+ // user prevented it). Interrupt the history load.
+ return NS_OK;
+ }
+
+ if (!triggeringPrincipal) {
+ // Ensure that we have a triggeringPrincipal. Otherwise javascript:
+ // URIs will pick it up from the about:blank page we just loaded,
+ // and we don't really want even that in this case.
+ triggeringPrincipal = nsNullPrincipal::CreateWithInheritedAttributes(this);
+ }
+ }
+
+ /* If there is a valid postdata *and* the user pressed
+ * reload or shift-reload, take user's permission before we
+ * repost the data to the server.
+ */
+ if ((aLoadType & LOAD_CMD_RELOAD) && postData) {
+ bool repost;
+ rv = ConfirmRepost(&repost);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // If the user pressed cancel in the dialog, return. We're done here.
+ if (!repost) {
+ return NS_BINDING_ABORTED;
+ }
+ }
+
+ // Do not inherit principal from document (security-critical!);
+ uint32_t flags = INTERNAL_LOAD_FLAGS_NONE;
+
+ nsAutoString srcdoc;
+ bool isSrcdoc;
+ nsCOMPtr<nsIURI> baseURI;
+ aEntry->GetIsSrcdocEntry(&isSrcdoc);
+ if (isSrcdoc) {
+ aEntry->GetSrcdocData(srcdoc);
+ aEntry->GetBaseURI(getter_AddRefs(baseURI));
+ flags |= INTERNAL_LOAD_FLAGS_IS_SRCDOC;
+ } else {
+ srcdoc = NullString();
+ }
+
+ // If there is no triggeringPrincipal we can fall back to using the
+ // SystemPrincipal as the triggeringPrincipal for loading the history
+ // entry, since the history entry can only end up in history if security
+ // checks passed in the initial loading phase.
+ if (!triggeringPrincipal) {
+ triggeringPrincipal = nsContentUtils::GetSystemPrincipal();
+ }
+
+ // Passing nullptr as aSourceDocShell gives the same behaviour as before
+ // aSourceDocShell was introduced. According to spec we should be passing
+ // the source browsing context that was used when the history entry was
+ // first created. bug 947716 has been created to address this issue.
+ rv = InternalLoad(uri,
+ originalURI,
+ loadReplace,
+ referrerURI,
+ referrerPolicy,
+ triggeringPrincipal,
+ principalToInherit,
+ flags,
+ EmptyString(), // No window target
+ contentType.get(), // Type hint
+ NullString(), // No forced file download
+ postData, // Post data stream
+ nullptr, // No headers stream
+ aLoadType, // Load type
+ aEntry, // SHEntry
+ true,
+ srcdoc,
+ nullptr, // Source docshell, see comment above
+ baseURI,
+ nullptr, // No nsIDocShell
+ nullptr); // No nsIRequest
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetShouldSaveLayoutState(bool* aShould)
+{
+ *aShould = false;
+ if (mOSHE) {
+ // Don't capture historystate and save it in history
+ // if the page asked not to do so.
+ mOSHE->GetSaveLayoutStateFlag(aShould);
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsDocShell::PersistLayoutHistoryState()
+{
+ nsresult rv = NS_OK;
+
+ if (mOSHE) {
+ bool scrollRestorationIsManual = false;
+ mOSHE->GetScrollRestorationIsManual(&scrollRestorationIsManual);
+
+ nsCOMPtr<nsIPresShell> shell = GetPresShell();
+ nsCOMPtr<nsILayoutHistoryState> layoutState;
+ if (shell) {
+ rv = shell->CaptureHistoryState(getter_AddRefs(layoutState));
+ } else if (scrollRestorationIsManual) {
+ // Even if we don't have layout anymore, we may want to reset the current
+ // scroll state in layout history.
+ GetLayoutHistoryState(getter_AddRefs(layoutState));
+ }
+
+ if (scrollRestorationIsManual && layoutState) {
+ layoutState->ResetScrollState();
+ }
+ }
+
+ return rv;
+}
+
+/* static */ nsresult
+nsDocShell::WalkHistoryEntries(nsISHEntry* aRootEntry,
+ nsDocShell* aRootShell,
+ WalkHistoryEntriesFunc aCallback,
+ void* aData)
+{
+ NS_ENSURE_TRUE(aRootEntry, NS_ERROR_FAILURE);
+
+ nsCOMPtr<nsISHContainer> container(do_QueryInterface(aRootEntry));
+ if (!container) {
+ return NS_ERROR_FAILURE;
+ }
+
+ int32_t childCount;
+ container->GetChildCount(&childCount);
+ for (int32_t i = 0; i < childCount; i++) {
+ nsCOMPtr<nsISHEntry> childEntry;
+ container->GetChildAt(i, getter_AddRefs(childEntry));
+ if (!childEntry) {
+ // childEntry can be null for valid reasons, for example if the
+ // docshell at index i never loaded anything useful.
+ // Remember to clone also nulls in the child array (bug 464064).
+ aCallback(nullptr, nullptr, i, aData);
+ continue;
+ }
+
+ nsDocShell* childShell = nullptr;
+ if (aRootShell) {
+ // Walk the children of aRootShell and see if one of them
+ // has srcChild as a SHEntry.
+ nsTObserverArray<nsDocLoader*>::ForwardIterator iter(
+ aRootShell->mChildList);
+ while (iter.HasMore()) {
+ nsDocShell* child = static_cast<nsDocShell*>(iter.GetNext());
+
+ if (child->HasHistoryEntry(childEntry)) {
+ childShell = child;
+ break;
+ }
+ }
+ }
+ nsresult rv = aCallback(childEntry, childShell, i, aData);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return NS_OK;
+}
+
+// callback data for WalkHistoryEntries
+struct MOZ_STACK_CLASS CloneAndReplaceData
+{
+ CloneAndReplaceData(uint32_t aCloneID, nsISHEntry* aReplaceEntry,
+ bool aCloneChildren, nsISHEntry* aDestTreeParent)
+ : cloneID(aCloneID)
+ , cloneChildren(aCloneChildren)
+ , replaceEntry(aReplaceEntry)
+ , destTreeParent(aDestTreeParent)
+ {
+ }
+
+ uint32_t cloneID;
+ bool cloneChildren;
+ nsISHEntry* replaceEntry;
+ nsISHEntry* destTreeParent;
+ nsCOMPtr<nsISHEntry> resultEntry;
+};
+
+/* static */ nsresult
+nsDocShell::CloneAndReplaceChild(nsISHEntry* aEntry, nsDocShell* aShell,
+ int32_t aEntryIndex, void* aData)
+{
+ nsCOMPtr<nsISHEntry> dest;
+
+ CloneAndReplaceData* data = static_cast<CloneAndReplaceData*>(aData);
+ uint32_t cloneID = data->cloneID;
+ nsISHEntry* replaceEntry = data->replaceEntry;
+
+ nsCOMPtr<nsISHContainer> container = do_QueryInterface(data->destTreeParent);
+ if (!aEntry) {
+ if (container) {
+ container->AddChild(nullptr, aEntryIndex);
+ }
+ return NS_OK;
+ }
+
+ uint32_t srcID;
+ aEntry->GetID(&srcID);
+
+ nsresult rv = NS_OK;
+ if (srcID == cloneID) {
+ // Replace the entry
+ dest = replaceEntry;
+ } else {
+ // Clone the SHEntry...
+ rv = aEntry->Clone(getter_AddRefs(dest));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ dest->SetIsSubFrame(true);
+
+ if (srcID != cloneID || data->cloneChildren) {
+ // Walk the children
+ CloneAndReplaceData childData(cloneID, replaceEntry,
+ data->cloneChildren, dest);
+ rv = WalkHistoryEntries(aEntry, aShell,
+ CloneAndReplaceChild, &childData);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ if (srcID != cloneID && aShell) {
+ aShell->SwapHistoryEntries(aEntry, dest);
+ }
+
+ if (container) {
+ container->AddChild(dest, aEntryIndex);
+ }
+
+ data->resultEntry = dest;
+ return rv;
+}
+
+/* static */ nsresult
+nsDocShell::CloneAndReplace(nsISHEntry* aSrcEntry,
+ nsDocShell* aSrcShell,
+ uint32_t aCloneID,
+ nsISHEntry* aReplaceEntry,
+ bool aCloneChildren,
+ nsISHEntry** aResultEntry)
+{
+ NS_ENSURE_ARG_POINTER(aResultEntry);
+ NS_ENSURE_TRUE(aReplaceEntry, NS_ERROR_FAILURE);
+
+ CloneAndReplaceData data(aCloneID, aReplaceEntry, aCloneChildren, nullptr);
+ nsresult rv = CloneAndReplaceChild(aSrcEntry, aSrcShell, 0, &data);
+
+ data.resultEntry.swap(*aResultEntry);
+ return rv;
+}
+
+void
+nsDocShell::SwapHistoryEntries(nsISHEntry* aOldEntry, nsISHEntry* aNewEntry)
+{
+ if (aOldEntry == mOSHE) {
+ mOSHE = aNewEntry;
+ }
+
+ if (aOldEntry == mLSHE) {
+ mLSHE = aNewEntry;
+ }
+}
+
+struct SwapEntriesData
+{
+ nsDocShell* ignoreShell; // constant; the shell to ignore
+ nsISHEntry* destTreeRoot; // constant; the root of the dest tree
+ nsISHEntry* destTreeParent; // constant; the node under destTreeRoot
+ // whose children will correspond to aEntry
+};
+
+nsresult
+nsDocShell::SetChildHistoryEntry(nsISHEntry* aEntry, nsDocShell* aShell,
+ int32_t aEntryIndex, void* aData)
+{
+ SwapEntriesData* data = static_cast<SwapEntriesData*>(aData);
+ nsDocShell* ignoreShell = data->ignoreShell;
+
+ if (!aShell || aShell == ignoreShell) {
+ return NS_OK;
+ }
+
+ nsISHEntry* destTreeRoot = data->destTreeRoot;
+
+ nsCOMPtr<nsISHEntry> destEntry;
+ nsCOMPtr<nsISHContainer> container = do_QueryInterface(data->destTreeParent);
+
+ if (container) {
+ // aEntry is a clone of some child of destTreeParent, but since the
+ // trees aren't necessarily in sync, we'll have to locate it.
+ // Note that we could set aShell's entry to null if we don't find a
+ // corresponding entry under destTreeParent.
+
+ uint32_t targetID, id;
+ aEntry->GetID(&targetID);
+
+ // First look at the given index, since this is the common case.
+ nsCOMPtr<nsISHEntry> entry;
+ container->GetChildAt(aEntryIndex, getter_AddRefs(entry));
+ if (entry && NS_SUCCEEDED(entry->GetID(&id)) && id == targetID) {
+ destEntry.swap(entry);
+ } else {
+ int32_t childCount;
+ container->GetChildCount(&childCount);
+ for (int32_t i = 0; i < childCount; ++i) {
+ container->GetChildAt(i, getter_AddRefs(entry));
+ if (!entry) {
+ continue;
+ }
+
+ entry->GetID(&id);
+ if (id == targetID) {
+ destEntry.swap(entry);
+ break;
+ }
+ }
+ }
+ } else {
+ destEntry = destTreeRoot;
+ }
+
+ aShell->SwapHistoryEntries(aEntry, destEntry);
+
+ // Now handle the children of aEntry.
+ SwapEntriesData childData = { ignoreShell, destTreeRoot, destEntry };
+ return WalkHistoryEntries(aEntry, aShell, SetChildHistoryEntry, &childData);
+}
+
+static nsISHEntry*
+GetRootSHEntry(nsISHEntry* aEntry)
+{
+ nsCOMPtr<nsISHEntry> rootEntry = aEntry;
+ nsISHEntry* result = nullptr;
+ while (rootEntry) {
+ result = rootEntry;
+ result->GetParent(getter_AddRefs(rootEntry));
+ }
+
+ return result;
+}
+
+void
+nsDocShell::SetHistoryEntry(nsCOMPtr<nsISHEntry>* aPtr, nsISHEntry* aEntry)
+{
+ // We need to sync up the docshell and session history trees for
+ // subframe navigation. If the load was in a subframe, we forward up to
+ // the root docshell, which will then recursively sync up all docshells
+ // to their corresponding entries in the new session history tree.
+ // If we don't do this, then we can cache a content viewer on the wrong
+ // cloned entry, and subsequently restore it at the wrong time.
+
+ nsISHEntry* newRootEntry = GetRootSHEntry(aEntry);
+ if (newRootEntry) {
+ // newRootEntry is now the new root entry.
+ // Find the old root entry as well.
+
+ // Need a strong ref. on |oldRootEntry| so it isn't destroyed when
+ // SetChildHistoryEntry() does SwapHistoryEntries() (bug 304639).
+ nsCOMPtr<nsISHEntry> oldRootEntry = GetRootSHEntry(*aPtr);
+ if (oldRootEntry) {
+ nsCOMPtr<nsIDocShellTreeItem> rootAsItem;
+ GetSameTypeRootTreeItem(getter_AddRefs(rootAsItem));
+ nsCOMPtr<nsIDocShell> rootShell = do_QueryInterface(rootAsItem);
+ if (rootShell) { // if we're the root just set it, nothing to swap
+ SwapEntriesData data = { this, newRootEntry };
+ nsIDocShell* rootIDocShell = static_cast<nsIDocShell*>(rootShell);
+ nsDocShell* rootDocShell = static_cast<nsDocShell*>(rootIDocShell);
+
+#ifdef DEBUG
+ nsresult rv =
+#endif
+ SetChildHistoryEntry(oldRootEntry, rootDocShell, 0, &data);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "SetChildHistoryEntry failed");
+ }
+ }
+ }
+
+ *aPtr = aEntry;
+}
+
+nsresult
+nsDocShell::GetRootSessionHistory(nsISHistory** aReturn)
+{
+ nsresult rv;
+
+ nsCOMPtr<nsIDocShellTreeItem> root;
+ // Get the root docshell
+ rv = GetSameTypeRootTreeItem(getter_AddRefs(root));
+ // QI to nsIWebNavigation
+ nsCOMPtr<nsIWebNavigation> rootAsWebnav(do_QueryInterface(root));
+ if (rootAsWebnav) {
+ // Get the handle to SH from the root docshell
+ rv = rootAsWebnav->GetSessionHistory(aReturn);
+ }
+ return rv;
+}
+
+nsresult
+nsDocShell::GetHttpChannel(nsIChannel* aChannel, nsIHttpChannel** aReturn)
+{
+ NS_ENSURE_ARG_POINTER(aReturn);
+ if (!aChannel) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIMultiPartChannel> multiPartChannel(do_QueryInterface(aChannel));
+ if (multiPartChannel) {
+ nsCOMPtr<nsIChannel> baseChannel;
+ multiPartChannel->GetBaseChannel(getter_AddRefs(baseChannel));
+ nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(baseChannel));
+ *aReturn = httpChannel;
+ NS_IF_ADDREF(*aReturn);
+ }
+ return NS_OK;
+}
+
+bool
+nsDocShell::ShouldDiscardLayoutState(nsIHttpChannel* aChannel)
+{
+ // By default layout State will be saved.
+ if (!aChannel) {
+ return false;
+ }
+
+ // figure out if SH should be saving layout state
+ bool noStore = false;
+ aChannel->IsNoStoreResponse(&noStore);
+ return noStore;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetEditor(nsIEditor** aEditor)
+{
+ NS_ENSURE_ARG_POINTER(aEditor);
+
+ if (!mEditorData) {
+ *aEditor = nullptr;
+ return NS_OK;
+ }
+
+ return mEditorData->GetEditor(aEditor);
+}
+
+NS_IMETHODIMP
+nsDocShell::SetEditor(nsIEditor* aEditor)
+{
+ nsresult rv = EnsureEditorData();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ return mEditorData->SetEditor(aEditor);
+}
+
+NS_IMETHODIMP
+nsDocShell::GetEditable(bool* aEditable)
+{
+ NS_ENSURE_ARG_POINTER(aEditable);
+ *aEditable = mEditorData && mEditorData->GetEditable();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetHasEditingSession(bool* aHasEditingSession)
+{
+ NS_ENSURE_ARG_POINTER(aHasEditingSession);
+
+ if (mEditorData) {
+ nsCOMPtr<nsIEditingSession> editingSession;
+ mEditorData->GetEditingSession(getter_AddRefs(editingSession));
+ *aHasEditingSession = (editingSession.get() != nullptr);
+ } else {
+ *aHasEditingSession = false;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::MakeEditable(bool aInWaitForUriLoad)
+{
+ nsresult rv = EnsureEditorData();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ return mEditorData->MakeEditable(aInWaitForUriLoad);
+}
+
+bool
+nsDocShell::ChannelIsPost(nsIChannel* aChannel)
+{
+ nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
+ if (!httpChannel) {
+ return false;
+ }
+
+ nsAutoCString method;
+ httpChannel->GetRequestMethod(method);
+ return method.EqualsLiteral("POST");
+}
+
+void
+nsDocShell::ExtractLastVisit(nsIChannel* aChannel,
+ nsIURI** aURI,
+ uint32_t* aChannelRedirectFlags)
+{
+ nsCOMPtr<nsIPropertyBag2> props(do_QueryInterface(aChannel));
+ if (!props) {
+ return;
+ }
+
+ nsresult rv = props->GetPropertyAsInterface(
+ NS_LITERAL_STRING("docshell.previousURI"),
+ NS_GET_IID(nsIURI),
+ reinterpret_cast<void**>(aURI));
+
+ if (NS_FAILED(rv)) {
+ // There is no last visit for this channel, so this must be the first
+ // link. Link the visit to the referrer of this request, if any.
+ // Treat referrer as null if there is an error getting it.
+ (void)NS_GetReferrerFromChannel(aChannel, aURI);
+ } else {
+ rv = props->GetPropertyAsUint32(NS_LITERAL_STRING("docshell.previousFlags"),
+ aChannelRedirectFlags);
+
+ NS_WARNING_ASSERTION(
+ NS_SUCCEEDED(rv),
+ "Could not fetch previous flags, URI will be treated like referrer");
+ }
+}
+
+void
+nsDocShell::SaveLastVisit(nsIChannel* aChannel,
+ nsIURI* aURI,
+ uint32_t aChannelRedirectFlags)
+{
+ nsCOMPtr<nsIWritablePropertyBag2> props(do_QueryInterface(aChannel));
+ if (!props || !aURI) {
+ return;
+ }
+
+ props->SetPropertyAsInterface(NS_LITERAL_STRING("docshell.previousURI"),
+ aURI);
+ props->SetPropertyAsUint32(NS_LITERAL_STRING("docshell.previousFlags"),
+ aChannelRedirectFlags);
+}
+
+void
+nsDocShell::AddURIVisit(nsIURI* aURI,
+ nsIURI* aReferrerURI,
+ nsIURI* aPreviousURI,
+ uint32_t aChannelRedirectFlags,
+ uint32_t aResponseStatus)
+{
+ MOZ_ASSERT(aURI, "Visited URI is null!");
+ MOZ_ASSERT(mLoadType != LOAD_ERROR_PAGE &&
+ mLoadType != LOAD_BYPASS_HISTORY,
+ "Do not add error or bypass pages to global history");
+
+ // Only content-type docshells save URI visits. Also don't do
+ // anything here if we're not supposed to use global history.
+ if (mItemType != typeContent || !mUseGlobalHistory || UsePrivateBrowsing()) {
+ return;
+ }
+
+ nsCOMPtr<IHistory> history = services::GetHistoryService();
+
+ if (history) {
+ uint32_t visitURIFlags = 0;
+
+ if (!IsFrame()) {
+ visitURIFlags |= IHistory::TOP_LEVEL;
+ }
+
+ if (aChannelRedirectFlags & nsIChannelEventSink::REDIRECT_TEMPORARY) {
+ visitURIFlags |= IHistory::REDIRECT_TEMPORARY;
+ } else if (aChannelRedirectFlags & nsIChannelEventSink::REDIRECT_PERMANENT) {
+ visitURIFlags |= IHistory::REDIRECT_PERMANENT;
+ }
+
+ if (aResponseStatus >= 300 && aResponseStatus < 400) {
+ visitURIFlags |= IHistory::REDIRECT_SOURCE;
+ }
+ // Errors 400-501 and 505 are considered unrecoverable, in the sense a
+ // simple retry attempt by the user is unlikely to solve them.
+ // 408 is special cased, since may actually indicate a temporary
+ // connection problem.
+ else if (aResponseStatus != 408 &&
+ ((aResponseStatus >= 400 && aResponseStatus <= 501) ||
+ aResponseStatus == 505)) {
+ visitURIFlags |= IHistory::UNRECOVERABLE_ERROR;
+ }
+
+ (void)history->VisitURI(aURI, aPreviousURI, visitURIFlags);
+ } else if (mGlobalHistory) {
+ // Falls back to sync global history interface.
+ (void)mGlobalHistory->AddURI(aURI,
+ !!aChannelRedirectFlags,
+ !IsFrame(),
+ aReferrerURI);
+ }
+}
+
+//*****************************************************************************
+// nsDocShell: Helper Routines
+//*****************************************************************************
+
+NS_IMETHODIMP
+nsDocShell::SetLoadType(uint32_t aLoadType)
+{
+ mLoadType = aLoadType;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetLoadType(uint32_t* aLoadType)
+{
+ *aLoadType = mLoadType;
+ return NS_OK;
+}
+
+nsresult
+nsDocShell::ConfirmRepost(bool* aRepost)
+{
+ nsCOMPtr<nsIPrompt> prompter;
+ CallGetInterface(this, static_cast<nsIPrompt**>(getter_AddRefs(prompter)));
+ if (!prompter) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ nsCOMPtr<nsIStringBundleService> stringBundleService =
+ mozilla::services::GetStringBundleService();
+ if (!stringBundleService) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIStringBundle> appBundle;
+ nsresult rv = stringBundleService->CreateBundle(kAppstringsBundleURL,
+ getter_AddRefs(appBundle));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIStringBundle> brandBundle;
+ rv = stringBundleService->CreateBundle(kBrandBundleURL,
+ getter_AddRefs(brandBundle));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_ASSERTION(prompter && brandBundle && appBundle,
+ "Unable to set up repost prompter.");
+
+ nsXPIDLString brandName;
+ rv = brandBundle->GetStringFromName(u"brandShortName",
+ getter_Copies(brandName));
+
+ nsXPIDLString msgString, button0Title;
+ if (NS_FAILED(rv)) { // No brand, use the generic version.
+ rv = appBundle->GetStringFromName(u"confirmRepostPrompt",
+ getter_Copies(msgString));
+ } else {
+ // Brand available - if the app has an override file with formatting, the
+ // app name will be included. Without an override, the prompt will look
+ // like the generic version.
+ const char16_t* formatStrings[] = { brandName.get() };
+ rv = appBundle->FormatStringFromName(u"confirmRepostPrompt",
+ formatStrings,
+ ArrayLength(formatStrings),
+ getter_Copies(msgString));
+ }
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ rv = appBundle->GetStringFromName(u"resendButton.label",
+ getter_Copies(button0Title));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ int32_t buttonPressed;
+ // The actual value here is irrelevant, but we can't pass an invalid
+ // bool through XPConnect.
+ bool checkState = false;
+ rv = prompter->ConfirmEx(
+ nullptr, msgString.get(),
+ (nsIPrompt::BUTTON_POS_0 * nsIPrompt::BUTTON_TITLE_IS_STRING) +
+ (nsIPrompt::BUTTON_POS_1 * nsIPrompt::BUTTON_TITLE_CANCEL),
+ button0Title.get(), nullptr, nullptr, nullptr, &checkState, &buttonPressed);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ *aRepost = (buttonPressed == 0);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetPromptAndStringBundle(nsIPrompt** aPrompt,
+ nsIStringBundle** aStringBundle)
+{
+ NS_ENSURE_SUCCESS(GetInterface(NS_GET_IID(nsIPrompt), (void**)aPrompt),
+ NS_ERROR_FAILURE);
+
+ nsCOMPtr<nsIStringBundleService> stringBundleService =
+ mozilla::services::GetStringBundleService();
+ NS_ENSURE_TRUE(stringBundleService, NS_ERROR_FAILURE);
+
+ NS_ENSURE_SUCCESS(
+ stringBundleService->CreateBundle(kAppstringsBundleURL, aStringBundle),
+ NS_ERROR_FAILURE);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetChildOffset(nsIDOMNode* aChild, nsIDOMNode* aParent,
+ int32_t* aOffset)
+{
+ NS_ENSURE_ARG_POINTER(aChild || aParent);
+
+ nsCOMPtr<nsIDOMNodeList> childNodes;
+ NS_ENSURE_SUCCESS(aParent->GetChildNodes(getter_AddRefs(childNodes)),
+ NS_ERROR_FAILURE);
+ NS_ENSURE_TRUE(childNodes, NS_ERROR_FAILURE);
+
+ int32_t i = 0;
+
+ for (; true; i++) {
+ nsCOMPtr<nsIDOMNode> childNode;
+ NS_ENSURE_SUCCESS(childNodes->Item(i, getter_AddRefs(childNode)),
+ NS_ERROR_FAILURE);
+ NS_ENSURE_TRUE(childNode, NS_ERROR_FAILURE);
+
+ if (childNode.get() == aChild) {
+ *aOffset = i;
+ return NS_OK;
+ }
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+nsIScrollableFrame*
+nsDocShell::GetRootScrollFrame()
+{
+ nsCOMPtr<nsIPresShell> shell = GetPresShell();
+ NS_ENSURE_TRUE(shell, nullptr);
+
+ return shell->GetRootScrollFrameAsScrollableExternal();
+}
+
+NS_IMETHODIMP
+nsDocShell::EnsureScriptEnvironment()
+{
+ if (mScriptGlobal) {
+ return NS_OK;
+ }
+
+ if (mIsBeingDestroyed) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+#ifdef DEBUG
+ NS_ASSERTION(!mInEnsureScriptEnv,
+ "Infinite loop! Calling EnsureScriptEnvironment() from "
+ "within EnsureScriptEnvironment()!");
+
+ // Yeah, this isn't re-entrant safe, but that's ok since if we
+ // re-enter this method, we'll infinitely loop...
+ AutoRestore<bool> boolSetter(mInEnsureScriptEnv);
+ mInEnsureScriptEnv = true;
+#endif
+
+ nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(mTreeOwner));
+ NS_ENSURE_TRUE(browserChrome, NS_ERROR_NOT_AVAILABLE);
+
+ uint32_t chromeFlags;
+ browserChrome->GetChromeFlags(&chromeFlags);
+
+ bool isModalContentWindow =
+ (mItemType == typeContent) &&
+ (chromeFlags & nsIWebBrowserChrome::CHROME_MODAL_CONTENT_WINDOW);
+ // There can be various other content docshells associated with the
+ // top-level window, like sidebars. Make sure that we only create an
+ // nsGlobalModalWindow for the primary content shell.
+ if (isModalContentWindow) {
+ nsCOMPtr<nsIDocShellTreeItem> primaryItem;
+ nsresult rv =
+ mTreeOwner->GetPrimaryContentShell(getter_AddRefs(primaryItem));
+ NS_ENSURE_SUCCESS(rv, rv);
+ isModalContentWindow = (primaryItem == this);
+ }
+
+ // If our window is modal and we're not opened as chrome, make
+ // this window a modal content window.
+ mScriptGlobal =
+ NS_NewScriptGlobalObject(mItemType == typeChrome, isModalContentWindow);
+ MOZ_ASSERT(mScriptGlobal);
+
+ mScriptGlobal->SetDocShell(this);
+
+ // Ensure the script object is set up to run script.
+ return mScriptGlobal->EnsureScriptEnvironment();
+}
+
+NS_IMETHODIMP
+nsDocShell::EnsureEditorData()
+{
+ bool openDocHasDetachedEditor = mOSHE && mOSHE->HasDetachedEditor();
+ if (!mEditorData && !mIsBeingDestroyed && !openDocHasDetachedEditor) {
+ // We shouldn't recreate the editor data if it already exists, or
+ // we're shutting down, or we already have a detached editor data
+ // stored in the session history. We should only have one editordata
+ // per docshell.
+ mEditorData = new nsDocShellEditorData(this);
+ }
+
+ return mEditorData ? NS_OK : NS_ERROR_NOT_AVAILABLE;
+}
+
+nsresult
+nsDocShell::EnsureTransferableHookData()
+{
+ if (!mTransferableHookData) {
+ mTransferableHookData = new nsTransferableHookData();
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::EnsureFind()
+{
+ nsresult rv;
+ if (!mFind) {
+ mFind = do_CreateInstance("@mozilla.org/embedcomp/find;1", &rv);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+
+ // we promise that the nsIWebBrowserFind that we return has been set
+ // up to point to the focused, or content window, so we have to
+ // set that up each time.
+
+ nsIScriptGlobalObject* scriptGO = GetScriptGlobalObject();
+ NS_ENSURE_TRUE(scriptGO, NS_ERROR_UNEXPECTED);
+
+ // default to our window
+ nsCOMPtr<nsPIDOMWindowOuter> ourWindow = do_QueryInterface(scriptGO);
+ nsCOMPtr<nsPIDOMWindowOuter> windowToSearch;
+ nsFocusManager::GetFocusedDescendant(ourWindow, true,
+ getter_AddRefs(windowToSearch));
+
+ nsCOMPtr<nsIWebBrowserFindInFrames> findInFrames = do_QueryInterface(mFind);
+ if (!findInFrames) {
+ return NS_ERROR_NO_INTERFACE;
+ }
+
+ rv = findInFrames->SetRootSearchFrame(ourWindow);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ rv = findInFrames->SetCurrentSearchFrame(windowToSearch);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+bool
+nsDocShell::IsFrame()
+{
+ nsCOMPtr<nsIDocShellTreeItem> parent;
+ GetSameTypeParent(getter_AddRefs(parent));
+ return !!parent;
+}
+
+NS_IMETHODIMP
+nsDocShell::IsBeingDestroyed(bool* aDoomed)
+{
+ NS_ENSURE_ARG(aDoomed);
+ *aDoomed = mIsBeingDestroyed;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetIsExecutingOnLoadHandler(bool* aResult)
+{
+ NS_ENSURE_ARG(aResult);
+ *aResult = mIsExecutingOnLoadHandler;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetLayoutHistoryState(nsILayoutHistoryState** aLayoutHistoryState)
+{
+ if (mOSHE) {
+ mOSHE->GetLayoutHistoryState(aLayoutHistoryState);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetLayoutHistoryState(nsILayoutHistoryState* aLayoutHistoryState)
+{
+ if (mOSHE) {
+ mOSHE->SetLayoutHistoryState(aLayoutHistoryState);
+ }
+ return NS_OK;
+}
+
+nsRefreshTimer::nsRefreshTimer()
+ : mDelay(0), mRepeat(false), mMetaRefresh(false)
+{
+}
+
+nsRefreshTimer::~nsRefreshTimer()
+{
+}
+
+NS_IMPL_ADDREF(nsRefreshTimer)
+NS_IMPL_RELEASE(nsRefreshTimer)
+
+NS_INTERFACE_MAP_BEGIN(nsRefreshTimer)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITimerCallback)
+ NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
+NS_INTERFACE_MAP_END_THREADSAFE
+
+NS_IMETHODIMP
+nsRefreshTimer::Notify(nsITimer* aTimer)
+{
+ NS_ASSERTION(mDocShell, "DocShell is somehow null");
+
+ if (mDocShell && aTimer) {
+ // Get the delay count to determine load type
+ uint32_t delay = 0;
+ aTimer->GetDelay(&delay);
+ mDocShell->ForceRefreshURIFromTimer(mURI, delay, mMetaRefresh, aTimer, mPrincipal);
+ }
+ return NS_OK;
+}
+
+nsDocShell::InterfaceRequestorProxy::InterfaceRequestorProxy(
+ nsIInterfaceRequestor* aRequestor)
+{
+ if (aRequestor) {
+ mWeakPtr = do_GetWeakReference(aRequestor);
+ }
+}
+
+nsDocShell::InterfaceRequestorProxy::~InterfaceRequestorProxy()
+{
+ mWeakPtr = nullptr;
+}
+
+NS_IMPL_ISUPPORTS(nsDocShell::InterfaceRequestorProxy, nsIInterfaceRequestor)
+
+NS_IMETHODIMP
+nsDocShell::InterfaceRequestorProxy::GetInterface(const nsIID& aIID,
+ void** aSink)
+{
+ NS_ENSURE_ARG_POINTER(aSink);
+ nsCOMPtr<nsIInterfaceRequestor> ifReq = do_QueryReferent(mWeakPtr);
+ if (ifReq) {
+ return ifReq->GetInterface(aIID, aSink);
+ }
+ *aSink = nullptr;
+ return NS_NOINTERFACE;
+}
+
+nsresult
+nsDocShell::SetBaseUrlForWyciwyg(nsIContentViewer* aContentViewer)
+{
+ if (!aContentViewer) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIURI> baseURI;
+ nsresult rv = NS_ERROR_NOT_AVAILABLE;
+
+ if (sURIFixup) {
+ rv = sURIFixup->CreateExposableURI(mCurrentURI, getter_AddRefs(baseURI));
+ }
+
+ // Get the current document and set the base uri
+ if (baseURI) {
+ nsIDocument* document = aContentViewer->GetDocument();
+ if (document) {
+ document->SetBaseURI(baseURI);
+ }
+ }
+ return rv;
+}
+
+//*****************************************************************************
+// nsDocShell::nsIAuthPromptProvider
+//*****************************************************************************
+
+NS_IMETHODIMP
+nsDocShell::GetAuthPrompt(uint32_t aPromptReason, const nsIID& aIID,
+ void** aResult)
+{
+ // a priority prompt request will override a false mAllowAuth setting
+ bool priorityPrompt = (aPromptReason == PROMPT_PROXY);
+
+ if (!mAllowAuth && !priorityPrompt) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ // we're either allowing auth, or it's a proxy request
+ nsresult rv;
+ nsCOMPtr<nsIPromptFactory> wwatch =
+ do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = EnsureScriptEnvironment();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Get the an auth prompter for our window so that the parenting
+ // of the dialogs works as it should when using tabs.
+
+ return wwatch->GetPrompt(mScriptGlobal->AsOuter(), aIID,
+ reinterpret_cast<void**>(aResult));
+}
+
+//*****************************************************************************
+// nsDocShell::nsILoadContext
+//*****************************************************************************
+
+NS_IMETHODIMP
+nsDocShell::GetAssociatedWindow(mozIDOMWindowProxy** aWindow)
+{
+ CallGetInterface(this, aWindow);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetTopWindow(mozIDOMWindowProxy** aWindow)
+{
+ nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
+ if (win) {
+ win = win->GetTop();
+ }
+ win.forget(aWindow);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetTopFrameElement(nsIDOMElement** aElement)
+{
+ *aElement = nullptr;
+ nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
+ if (!win) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsPIDOMWindowOuter> top = win->GetScriptableTop();
+ NS_ENSURE_TRUE(top, NS_ERROR_FAILURE);
+
+ // GetFrameElementInternal, /not/ GetScriptableFrameElement -- if |top| is
+ // inside <iframe mozbrowser>, we want to return the iframe, not null.
+ // And we want to cross the content/chrome boundary.
+ nsCOMPtr<nsIDOMElement> elt =
+ do_QueryInterface(top->GetFrameElementInternal());
+ elt.forget(aElement);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetNestedFrameId(uint64_t* aId)
+{
+ *aId = 0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::IsTrackingProtectionOn(bool* aIsTrackingProtectionOn)
+{
+ if (Preferences::GetBool("privacy.trackingprotection.enabled", false)) {
+ *aIsTrackingProtectionOn = true;
+ } else if (UsePrivateBrowsing() &&
+ Preferences::GetBool("privacy.trackingprotection.pbmode.enabled", false)) {
+ *aIsTrackingProtectionOn = true;
+ } else {
+ *aIsTrackingProtectionOn = false;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetIsContent(bool* aIsContent)
+{
+ *aIsContent = (mItemType == typeContent);
+ return NS_OK;
+}
+
+bool
+nsDocShell::IsOKToLoadURI(nsIURI* aURI)
+{
+ NS_PRECONDITION(aURI, "Must have a URI!");
+
+ if (!mFiredUnloadEvent) {
+ return true;
+ }
+
+ if (!mLoadingURI) {
+ return false;
+ }
+
+ nsCOMPtr<nsIScriptSecurityManager> secMan =
+ do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
+ return secMan &&
+ NS_SUCCEEDED(secMan->CheckSameOriginURI(aURI, mLoadingURI, false));
+}
+
+//
+// Routines for selection and clipboard
+//
+nsresult
+nsDocShell::GetControllerForCommand(const char* aCommand,
+ nsIController** aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+ *aResult = nullptr;
+
+ NS_ENSURE_TRUE(mScriptGlobal, NS_ERROR_FAILURE);
+
+ nsCOMPtr<nsPIWindowRoot> root = mScriptGlobal->GetTopWindowRoot();
+ NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
+
+ return root->GetControllerForCommand(aCommand, aResult);
+}
+
+NS_IMETHODIMP
+nsDocShell::IsCommandEnabled(const char* aCommand, bool* aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+ *aResult = false;
+
+ nsresult rv = NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIController> controller;
+ rv = GetControllerForCommand(aCommand, getter_AddRefs(controller));
+ if (controller) {
+ rv = controller->IsCommandEnabled(aCommand, aResult);
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDocShell::DoCommand(const char* aCommand)
+{
+ nsresult rv = NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIController> controller;
+ rv = GetControllerForCommand(aCommand, getter_AddRefs(controller));
+ if (controller) {
+ rv = controller->DoCommand(aCommand);
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDocShell::DoCommandWithParams(const char* aCommand, nsICommandParams* aParams)
+{
+ nsCOMPtr<nsIController> controller;
+ nsresult rv = GetControllerForCommand(aCommand, getter_AddRefs(controller));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsCOMPtr<nsICommandController> commandController =
+ do_QueryInterface(controller, &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return commandController->DoCommandWithParams(aCommand, aParams);
+}
+
+nsresult
+nsDocShell::EnsureCommandHandler()
+{
+ if (!mCommandManager) {
+ nsCOMPtr<nsPICommandUpdater> commandUpdater =
+ do_CreateInstance("@mozilla.org/embedcomp/command-manager;1");
+ if (!commandUpdater) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ nsCOMPtr<nsPIDOMWindowOuter> domWindow = GetWindow();
+ nsresult rv = commandUpdater->Init(domWindow);
+ if (NS_SUCCEEDED(rv)) {
+ mCommandManager = do_QueryInterface(commandUpdater);
+ }
+ }
+
+ return mCommandManager ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsDocShell::CanCutSelection(bool* aResult)
+{
+ return IsCommandEnabled("cmd_cut", aResult);
+}
+
+NS_IMETHODIMP
+nsDocShell::CanCopySelection(bool* aResult)
+{
+ return IsCommandEnabled("cmd_copy", aResult);
+}
+
+NS_IMETHODIMP
+nsDocShell::CanCopyLinkLocation(bool* aResult)
+{
+ return IsCommandEnabled("cmd_copyLink", aResult);
+}
+
+NS_IMETHODIMP
+nsDocShell::CanCopyImageLocation(bool* aResult)
+{
+ return IsCommandEnabled("cmd_copyImageLocation", aResult);
+}
+
+NS_IMETHODIMP
+nsDocShell::CanCopyImageContents(bool* aResult)
+{
+ return IsCommandEnabled("cmd_copyImageContents", aResult);
+}
+
+NS_IMETHODIMP
+nsDocShell::CanPaste(bool* aResult)
+{
+ return IsCommandEnabled("cmd_paste", aResult);
+}
+
+NS_IMETHODIMP
+nsDocShell::CutSelection(void)
+{
+ return DoCommand("cmd_cut");
+}
+
+NS_IMETHODIMP
+nsDocShell::CopySelection(void)
+{
+ return DoCommand("cmd_copy");
+}
+
+NS_IMETHODIMP
+nsDocShell::CopyLinkLocation(void)
+{
+ return DoCommand("cmd_copyLink");
+}
+
+NS_IMETHODIMP
+nsDocShell::CopyImageLocation(void)
+{
+ return DoCommand("cmd_copyImageLocation");
+}
+
+NS_IMETHODIMP
+nsDocShell::CopyImageContents(void)
+{
+ return DoCommand("cmd_copyImageContents");
+}
+
+NS_IMETHODIMP
+nsDocShell::Paste(void)
+{
+ return DoCommand("cmd_paste");
+}
+
+NS_IMETHODIMP
+nsDocShell::SelectAll(void)
+{
+ return DoCommand("cmd_selectAll");
+}
+
+//
+// SelectNone
+//
+// Collapses the current selection, insertion point ends up at beginning
+// of previous selection.
+//
+NS_IMETHODIMP
+nsDocShell::SelectNone(void)
+{
+ return DoCommand("cmd_selectNone");
+}
+
+// link handling
+
+class OnLinkClickEvent : public Runnable
+{
+public:
+ OnLinkClickEvent(nsDocShell* aHandler, nsIContent* aContent,
+ nsIURI* aURI,
+ const char16_t* aTargetSpec,
+ const nsAString& aFileName,
+ nsIInputStream* aPostDataStream,
+ nsIInputStream* aHeadersDataStream,
+ bool aIsTrusted);
+
+ NS_IMETHOD Run() override
+ {
+ nsAutoPopupStatePusher popupStatePusher(mPopupState);
+
+ // We need to set up an AutoJSAPI here for the following reason: When we do
+ // OnLinkClickSync we'll eventually end up in nsGlobalWindow::OpenInternal
+ // which only does popup blocking if !LegacyIsCallerChromeOrNativeCode().
+ // So we need to fake things so that we don't look like native code as far
+ // as LegacyIsCallerNativeCode() is concerned.
+ AutoJSAPI jsapi;
+ if (mIsTrusted || jsapi.Init(mContent->OwnerDoc()->GetScopeObject())) {
+ mHandler->OnLinkClickSync(mContent, mURI,
+ mTargetSpec.get(), mFileName,
+ mPostDataStream, mHeadersDataStream,
+ nullptr, nullptr);
+ }
+ return NS_OK;
+ }
+
+private:
+ RefPtr<nsDocShell> mHandler;
+ nsCOMPtr<nsIURI> mURI;
+ nsString mTargetSpec;
+ nsString mFileName;
+ nsCOMPtr<nsIInputStream> mPostDataStream;
+ nsCOMPtr<nsIInputStream> mHeadersDataStream;
+ nsCOMPtr<nsIContent> mContent;
+ PopupControlState mPopupState;
+ bool mIsTrusted;
+};
+
+OnLinkClickEvent::OnLinkClickEvent(nsDocShell* aHandler,
+ nsIContent* aContent,
+ nsIURI* aURI,
+ const char16_t* aTargetSpec,
+ const nsAString& aFileName,
+ nsIInputStream* aPostDataStream,
+ nsIInputStream* aHeadersDataStream,
+ bool aIsTrusted)
+ : mHandler(aHandler)
+ , mURI(aURI)
+ , mTargetSpec(aTargetSpec)
+ , mFileName(aFileName)
+ , mPostDataStream(aPostDataStream)
+ , mHeadersDataStream(aHeadersDataStream)
+ , mContent(aContent)
+ , mPopupState(mHandler->mScriptGlobal->GetPopupControlState())
+ , mIsTrusted(aIsTrusted)
+{
+}
+
+NS_IMETHODIMP
+nsDocShell::OnLinkClick(nsIContent* aContent,
+ nsIURI* aURI,
+ const char16_t* aTargetSpec,
+ const nsAString& aFileName,
+ nsIInputStream* aPostDataStream,
+ nsIInputStream* aHeadersDataStream,
+ bool aIsTrusted)
+{
+ NS_ASSERTION(NS_IsMainThread(), "wrong thread");
+
+ if (!IsNavigationAllowed() || !IsOKToLoadURI(aURI)) {
+ return NS_OK;
+ }
+
+ // On history navigation through Back/Forward buttons, don't execute
+ // automatic JavaScript redirection such as |anchorElement.click()| or
+ // |formElement.submit()|.
+ //
+ // XXX |formElement.submit()| bypasses this checkpoint because it calls
+ // nsDocShell::OnLinkClickSync(...) instead.
+ if (ShouldBlockLoadingForBackButton()) {
+ return NS_OK;
+ }
+
+ if (aContent->IsEditable()) {
+ return NS_OK;
+ }
+
+ nsresult rv = NS_ERROR_FAILURE;
+ nsAutoString target;
+
+ nsCOMPtr<nsIWebBrowserChrome3> browserChrome3 = do_GetInterface(mTreeOwner);
+ if (browserChrome3) {
+ nsCOMPtr<nsIDOMNode> linkNode = do_QueryInterface(aContent);
+ nsAutoString oldTarget(aTargetSpec);
+ rv = browserChrome3->OnBeforeLinkTraversal(oldTarget, aURI,
+ linkNode, mIsAppTab, target);
+ }
+
+ if (NS_FAILED(rv)) {
+ target = aTargetSpec;
+ }
+
+ nsCOMPtr<nsIRunnable> ev =
+ new OnLinkClickEvent(this, aContent, aURI, target.get(), aFileName,
+ aPostDataStream, aHeadersDataStream, aIsTrusted);
+ return NS_DispatchToCurrentThread(ev);
+}
+
+NS_IMETHODIMP
+nsDocShell::OnLinkClickSync(nsIContent* aContent,
+ nsIURI* aURI,
+ const char16_t* aTargetSpec,
+ const nsAString& aFileName,
+ nsIInputStream* aPostDataStream,
+ nsIInputStream* aHeadersDataStream,
+ nsIDocShell** aDocShell,
+ nsIRequest** aRequest)
+{
+ // Initialize the DocShell / Request
+ if (aDocShell) {
+ *aDocShell = nullptr;
+ }
+ if (aRequest) {
+ *aRequest = nullptr;
+ }
+
+ if (!IsNavigationAllowed() || !IsOKToLoadURI(aURI)) {
+ return NS_OK;
+ }
+
+ // XXX When the linking node was HTMLFormElement, it is synchronous event.
+ // That is, the caller of this method is not |OnLinkClickEvent::Run()|
+ // but |HTMLFormElement::SubmitSubmission(...)|.
+ if (aContent->IsHTMLElement(nsGkAtoms::form) &&
+ ShouldBlockLoadingForBackButton()) {
+ return NS_OK;
+ }
+
+ if (aContent->IsEditable()) {
+ return NS_OK;
+ }
+
+ {
+ // defer to an external protocol handler if necessary...
+ nsCOMPtr<nsIExternalProtocolService> extProtService =
+ do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID);
+ if (extProtService) {
+ nsAutoCString scheme;
+ aURI->GetScheme(scheme);
+ if (!scheme.IsEmpty()) {
+ // if the URL scheme does not correspond to an exposed protocol, then we
+ // need to hand this link click over to the external protocol handler.
+ bool isExposed;
+ nsresult rv =
+ extProtService->IsExposedProtocol(scheme.get(), &isExposed);
+ if (NS_SUCCEEDED(rv) && !isExposed) {
+ return extProtService->LoadURI(aURI, this);
+ }
+ }
+ }
+ }
+
+ uint32_t flags = INTERNAL_LOAD_FLAGS_NONE;
+ if (IsElementAnchor(aContent)) {
+ MOZ_ASSERT(aContent->IsHTMLElement());
+ nsAutoString referrer;
+ aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::rel, referrer);
+ nsWhitespaceTokenizerTemplate<nsContentUtils::IsHTMLWhitespace> tok(referrer);
+ while (tok.hasMoreTokens()) {
+ const nsAString& token = tok.nextToken();
+ if (token.LowerCaseEqualsLiteral("noreferrer")) {
+ flags |= INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER |
+ INTERNAL_LOAD_FLAGS_NO_OPENER;
+ // We now have all the flags we could possibly have, so just stop.
+ break;
+ }
+ if (token.LowerCaseEqualsLiteral("noopener")) {
+ flags |= INTERNAL_LOAD_FLAGS_NO_OPENER;
+ }
+ }
+ }
+
+ // Get the owner document of the link that was clicked, this will be
+ // the document that the link is in, or the last document that the
+ // link was in. From that document, we'll get the URI to use as the
+ // referer, since the current URI in this docshell may be a
+ // new document that we're in the process of loading.
+ nsCOMPtr<nsIDocument> refererDoc = aContent->OwnerDoc();
+ NS_ENSURE_TRUE(refererDoc, NS_ERROR_UNEXPECTED);
+
+ // Now check that the refererDoc's inner window is the current inner
+ // window for mScriptGlobal. If it's not, then we don't want to
+ // follow this link.
+ nsPIDOMWindowInner* refererInner = refererDoc->GetInnerWindow();
+ NS_ENSURE_TRUE(refererInner, NS_ERROR_UNEXPECTED);
+ if (!mScriptGlobal ||
+ mScriptGlobal->AsOuter()->GetCurrentInnerWindow() != refererInner) {
+ // We're no longer the current inner window
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIURI> referer = refererDoc->GetDocumentURI();
+ uint32_t refererPolicy = refererDoc->GetReferrerPolicy();
+
+ // get referrer attribute from clicked link and parse it
+ // if per element referrer is enabled, the element referrer overrules
+ // the document wide referrer
+ if (IsElementAnchor(aContent)) {
+ net::ReferrerPolicy refPolEnum = aContent->AsElement()->GetReferrerPolicyAsEnum();
+ if (refPolEnum != net::RP_Unset) {
+ refererPolicy = refPolEnum;
+ }
+ }
+
+ // referer could be null here in some odd cases, but that's ok,
+ // we'll just load the link w/o sending a referer in those cases.
+
+ nsAutoString target(aTargetSpec);
+
+ // If this is an anchor element, grab its type property to use as a hint
+ nsAutoString typeHint;
+ nsCOMPtr<nsIDOMHTMLAnchorElement> anchor(do_QueryInterface(aContent));
+ if (anchor) {
+ anchor->GetType(typeHint);
+ NS_ConvertUTF16toUTF8 utf8Hint(typeHint);
+ nsAutoCString type, dummy;
+ NS_ParseRequestContentType(utf8Hint, type, dummy);
+ CopyUTF8toUTF16(type, typeHint);
+ }
+
+ // Clone the URI now, in case a content policy or something messes
+ // with it under InternalLoad; we do _not_ want to change the URI
+ // our caller passed in.
+ nsCOMPtr<nsIURI> clonedURI;
+ aURI->Clone(getter_AddRefs(clonedURI));
+ if (!clonedURI) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ nsresult rv = InternalLoad(clonedURI, // New URI
+ nullptr, // Original URI
+ false, // LoadReplace
+ referer, // Referer URI
+ refererPolicy, // Referer policy
+ aContent->NodePrincipal(), // Triggering is our node's
+ // principal
+ aContent->NodePrincipal(),
+ flags,
+ target, // Window target
+ NS_LossyConvertUTF16toASCII(typeHint).get(),
+ aFileName, // Download as file
+ aPostDataStream, // Post data stream
+ aHeadersDataStream, // Headers stream
+ LOAD_LINK, // Load type
+ nullptr, // No SHEntry
+ true, // first party site
+ NullString(), // No srcdoc
+ this, // We are the source
+ nullptr, // baseURI not needed
+ aDocShell, // DocShell out-param
+ aRequest); // Request out-param
+ if (NS_SUCCEEDED(rv)) {
+ DispatchPings(this, aContent, aURI, referer, refererPolicy);
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDocShell::OnOverLink(nsIContent* aContent,
+ nsIURI* aURI,
+ const char16_t* aTargetSpec)
+{
+ if (aContent->IsEditable()) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIWebBrowserChrome2> browserChrome2 = do_GetInterface(mTreeOwner);
+ nsresult rv = NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIWebBrowserChrome> browserChrome;
+ if (!browserChrome2) {
+ browserChrome = do_GetInterface(mTreeOwner);
+ if (!browserChrome) {
+ return rv;
+ }
+ }
+
+ nsCOMPtr<nsITextToSubURI> textToSubURI =
+ do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // use url origin charset to unescape the URL
+ nsAutoCString charset;
+ rv = aURI->GetOriginCharset(charset);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString spec;
+ rv = aURI->GetSpec(spec);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString uStr;
+ rv = textToSubURI->UnEscapeURIForUI(charset, spec, uStr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mozilla::net::PredictorPredict(aURI, mCurrentURI,
+ nsINetworkPredictor::PREDICT_LINK,
+ this, nullptr);
+
+ if (browserChrome2) {
+ nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aContent);
+ rv = browserChrome2->SetStatusWithContext(nsIWebBrowserChrome::STATUS_LINK,
+ uStr, element);
+ } else {
+ rv = browserChrome->SetStatus(nsIWebBrowserChrome::STATUS_LINK, uStr.get());
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDocShell::OnLeaveLink()
+{
+ nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(mTreeOwner));
+ nsresult rv = NS_ERROR_FAILURE;
+
+ if (browserChrome) {
+ rv = browserChrome->SetStatus(nsIWebBrowserChrome::STATUS_LINK,
+ EmptyString().get());
+ }
+ return rv;
+}
+
+bool
+nsDocShell::ShouldBlockLoadingForBackButton()
+{
+ if (!(mLoadType & LOAD_CMD_HISTORY) ||
+ EventStateManager::IsHandlingUserInput() ||
+ !Preferences::GetBool("accessibility.blockjsredirection")) {
+ return false;
+ }
+
+ bool canGoForward = false;
+ GetCanGoForward(&canGoForward);
+ return canGoForward;
+}
+
+bool
+nsDocShell::PluginsAllowedInCurrentDoc()
+{
+ bool pluginsAllowed = false;
+
+ if (!mContentViewer) {
+ return false;
+ }
+
+ nsIDocument* doc = mContentViewer->GetDocument();
+ if (!doc) {
+ return false;
+ }
+
+ doc->GetAllowPlugins(&pluginsAllowed);
+ return pluginsAllowed;
+}
+
+//----------------------------------------------------------------------
+// Web Shell Services API
+
+// This functions is only called when a new charset is detected in loading a
+// document. Its name should be changed to "CharsetReloadDocument"
+NS_IMETHODIMP
+nsDocShell::ReloadDocument(const char* aCharset, int32_t aSource)
+{
+ // XXX hack. keep the aCharset and aSource wait to pick it up
+ nsCOMPtr<nsIContentViewer> cv;
+ NS_ENSURE_SUCCESS(GetContentViewer(getter_AddRefs(cv)), NS_ERROR_FAILURE);
+ if (cv) {
+ int32_t hint;
+ cv->GetHintCharacterSetSource(&hint);
+ if (aSource > hint) {
+ nsCString charset(aCharset);
+ cv->SetHintCharacterSet(charset);
+ cv->SetHintCharacterSetSource(aSource);
+ if (eCharsetReloadRequested != mCharsetReloadState) {
+ mCharsetReloadState = eCharsetReloadRequested;
+ return Reload(LOAD_FLAGS_CHARSET_CHANGE);
+ }
+ }
+ }
+ // return failure if this request is not accepted due to mCharsetReloadState
+ return NS_ERROR_DOCSHELL_REQUEST_REJECTED;
+}
+
+NS_IMETHODIMP
+nsDocShell::StopDocumentLoad(void)
+{
+ if (eCharsetReloadRequested != mCharsetReloadState) {
+ Stop(nsIWebNavigation::STOP_ALL);
+ return NS_OK;
+ }
+ // return failer if this request is not accepted due to mCharsetReloadState
+ return NS_ERROR_DOCSHELL_REQUEST_REJECTED;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetPrintPreview(nsIWebBrowserPrint** aPrintPreview)
+{
+ *aPrintPreview = nullptr;
+#if NS_PRINT_PREVIEW
+ nsCOMPtr<nsIDocumentViewerPrint> print = do_QueryInterface(mContentViewer);
+ if (!print || !print->IsInitializedForPrintPreview()) {
+ Stop(nsIWebNavigation::STOP_ALL);
+ nsCOMPtr<nsIPrincipal> principal = nsNullPrincipal::CreateWithInheritedAttributes(this);
+ nsresult rv = CreateAboutBlankContentViewer(principal, nullptr);
+ NS_ENSURE_SUCCESS(rv, rv);
+ print = do_QueryInterface(mContentViewer);
+ NS_ENSURE_STATE(print);
+ print->InitializeForPrintPreview();
+ }
+ nsCOMPtr<nsIWebBrowserPrint> result = do_QueryInterface(print);
+ result.forget(aPrintPreview);
+ return NS_OK;
+#else
+ return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+#ifdef DEBUG
+unsigned long nsDocShell::gNumberOfDocShells = 0;
+#endif
+
+NS_IMETHODIMP
+nsDocShell::GetCanExecuteScripts(bool* aResult)
+{
+ *aResult = mCanExecuteScripts;
+ return NS_OK;
+}
+
+/* [infallible] */ NS_IMETHODIMP
+nsDocShell::SetFrameType(uint32_t aFrameType)
+{
+ mFrameType = aFrameType;
+ return NS_OK;
+}
+
+/* [infallible] */ NS_IMETHODIMP
+nsDocShell::GetFrameType(uint32_t* aFrameType)
+{
+ *aFrameType = mFrameType;
+ return NS_OK;
+}
+
+/* [infallible] */ NS_IMETHODIMP
+nsDocShell::GetIsApp(bool* aIsApp)
+{
+ *aIsApp = (mFrameType == FRAME_TYPE_APP);
+ return NS_OK;
+}
+
+/* [infallible] */ NS_IMETHODIMP
+nsDocShell::GetIsMozBrowserOrApp(bool* aIsMozBrowserOrApp)
+{
+ *aIsMozBrowserOrApp = (mFrameType != FRAME_TYPE_REGULAR);
+ return NS_OK;
+}
+
+uint32_t
+nsDocShell::GetInheritedFrameType()
+{
+ if (mFrameType != FRAME_TYPE_REGULAR) {
+ return mFrameType;
+ }
+
+ nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
+ GetSameTypeParent(getter_AddRefs(parentAsItem));
+
+ nsCOMPtr<nsIDocShell> parent = do_QueryInterface(parentAsItem);
+ if (!parent) {
+ return FRAME_TYPE_REGULAR;
+ }
+
+ return static_cast<nsDocShell*>(parent.get())->GetInheritedFrameType();
+}
+
+/* [infallible] */ NS_IMETHODIMP
+nsDocShell::GetIsIsolatedMozBrowserElement(bool* aIsIsolatedMozBrowserElement)
+{
+ bool result = mFrameType == FRAME_TYPE_BROWSER &&
+ mOriginAttributes.mInIsolatedMozBrowser;
+ *aIsIsolatedMozBrowserElement = result;
+ return NS_OK;
+}
+
+/* [infallible] */ NS_IMETHODIMP
+nsDocShell::GetIsInIsolatedMozBrowserElement(bool* aIsInIsolatedMozBrowserElement)
+{
+ MOZ_ASSERT(!mOriginAttributes.mInIsolatedMozBrowser ||
+ (GetInheritedFrameType() == FRAME_TYPE_BROWSER),
+ "Isolated mozbrowser should only be true inside browser frames");
+ bool result = (GetInheritedFrameType() == FRAME_TYPE_BROWSER) &&
+ mOriginAttributes.mInIsolatedMozBrowser;
+ *aIsInIsolatedMozBrowserElement = result;
+ return NS_OK;
+}
+
+/* [infallible] */ NS_IMETHODIMP
+nsDocShell::GetIsInMozBrowserOrApp(bool* aIsInMozBrowserOrApp)
+{
+ *aIsInMozBrowserOrApp = (GetInheritedFrameType() != FRAME_TYPE_REGULAR);
+ return NS_OK;
+}
+
+/* [infallible] */ NS_IMETHODIMP
+nsDocShell::GetIsTopLevelContentDocShell(bool* aIsTopLevelContentDocShell)
+{
+ *aIsTopLevelContentDocShell = false;
+
+ if (mItemType == typeContent) {
+ nsCOMPtr<nsIDocShellTreeItem> root;
+ GetSameTypeRootTreeItem(getter_AddRefs(root));
+ *aIsTopLevelContentDocShell = root.get() == static_cast<nsIDocShellTreeItem*>(this);
+ }
+
+ return NS_OK;
+}
+
+/* [infallible] */ NS_IMETHODIMP
+nsDocShell::GetAppId(uint32_t* aAppId)
+{
+ if (mOriginAttributes.mAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID) {
+ *aAppId = mOriginAttributes.mAppId;
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIDocShell> parent;
+ GetSameTypeParentIgnoreBrowserAndAppBoundaries(getter_AddRefs(parent));
+
+ if (!parent) {
+ *aAppId = nsIScriptSecurityManager::NO_APP_ID;
+ return NS_OK;
+ }
+
+ return parent->GetAppId(aAppId);
+}
+
+// Implements nsILoadContext.originAttributes
+NS_IMETHODIMP
+nsDocShell::GetOriginAttributes(JS::MutableHandle<JS::Value> aVal)
+{
+ JSContext* cx = nsContentUtils::GetCurrentJSContext();
+ MOZ_ASSERT(cx);
+
+ return GetOriginAttributes(cx, aVal);
+}
+
+// Implements nsIDocShell.GetOriginAttributes()
+NS_IMETHODIMP
+nsDocShell::GetOriginAttributes(JSContext* aCx,
+ JS::MutableHandle<JS::Value> aVal)
+{
+ bool ok = ToJSValue(aCx, mOriginAttributes, aVal);
+ NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
+ return NS_OK;
+}
+
+bool
+nsDocShell::CanSetOriginAttributes()
+{
+ MOZ_ASSERT(mChildList.IsEmpty());
+ if (!mChildList.IsEmpty()) {
+ return false;
+ }
+
+ // TODO: Bug 1273058 - mContentViewer should be null when setting origin
+ // attributes.
+ if (mContentViewer) {
+ nsIDocument* doc = mContentViewer->GetDocument();
+ if (doc) {
+ nsIURI* uri = doc->GetDocumentURI();
+ if (!uri) {
+ return false;
+ }
+ nsCString uriSpec = uri->GetSpecOrDefault();
+ MOZ_ASSERT(uriSpec.EqualsLiteral("about:blank"));
+ if (!uriSpec.EqualsLiteral("about:blank")) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+nsresult
+nsDocShell::SetOriginAttributes(const DocShellOriginAttributes& aAttrs)
+{
+ if (!CanSetOriginAttributes()) {
+ return NS_ERROR_FAILURE;
+ }
+
+ AssertOriginAttributesMatchPrivateBrowsing();
+ mOriginAttributes = aAttrs;
+
+ bool isPrivate = mOriginAttributes.mPrivateBrowsingId > 0;
+ // Chrome docshell can not contain OriginAttributes.mPrivateBrowsingId
+ if (mItemType == typeChrome && isPrivate) {
+ mOriginAttributes.mPrivateBrowsingId = 0;
+ }
+
+ SetPrivateBrowsing(isPrivate);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetOriginAttributesBeforeLoading(JS::Handle<JS::Value> aOriginAttributes)
+{
+ if (!aOriginAttributes.isObject()) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ AutoJSAPI jsapi;
+ if (!jsapi.Init(&aOriginAttributes.toObject())) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ JSContext* cx = jsapi.cx();
+ if (NS_WARN_IF(!cx)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ DocShellOriginAttributes attrs;
+ if (!aOriginAttributes.isObject() || !attrs.Init(cx, aOriginAttributes)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ return SetOriginAttributes(attrs);
+}
+
+NS_IMETHODIMP
+nsDocShell::SetOriginAttributes(JS::Handle<JS::Value> aOriginAttributes,
+ JSContext* aCx)
+{
+ DocShellOriginAttributes attrs;
+ if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ return SetOriginAttributes(attrs);
+}
+
+NS_IMETHODIMP
+nsDocShell::GetAppManifestURL(nsAString& aAppManifestURL)
+{
+ uint32_t appId = nsIDocShell::GetAppId();
+ if (appId != nsIScriptSecurityManager::NO_APP_ID &&
+ appId != nsIScriptSecurityManager::UNKNOWN_APP_ID) {
+ nsCOMPtr<nsIAppsService> appsService =
+ do_GetService(APPS_SERVICE_CONTRACTID);
+ NS_ASSERTION(appsService, "No AppsService available");
+ appsService->GetManifestURLByLocalId(appId, aAppManifestURL);
+ } else {
+ aAppManifestURL.SetLength(0);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetAsyncPanZoomEnabled(bool* aOut)
+{
+ if (nsIPresShell* presShell = GetPresShell()) {
+ *aOut = presShell->AsyncPanZoomEnabled();
+ return NS_OK;
+ }
+
+ // If we don't have a presShell, fall back to the default platform value of
+ // whether or not APZ is enabled.
+ *aOut = gfxPlatform::AsyncPanZoomEnabled();
+ return NS_OK;
+}
+
+bool
+nsDocShell::HasUnloadedParent()
+{
+ RefPtr<nsDocShell> parent = GetParentDocshell();
+ while (parent) {
+ bool inUnload = false;
+ parent->GetIsInUnload(&inUnload);
+ if (inUnload) {
+ return true;
+ }
+ parent = parent->GetParentDocshell();
+ }
+ return false;
+}
+
+bool
+nsDocShell::IsInvisible()
+{
+ return mInvisible;
+}
+
+void
+nsDocShell::SetInvisible(bool aInvisible)
+{
+ mInvisible = aInvisible;
+}
+
+void
+nsDocShell::SetOpener(nsITabParent* aOpener)
+{
+ mOpener = do_GetWeakReference(aOpener);
+}
+
+nsITabParent*
+nsDocShell::GetOpener()
+{
+ nsCOMPtr<nsITabParent> opener(do_QueryReferent(mOpener));
+ return opener;
+}
+
+// The caller owns |aAsyncCause| here.
+void
+nsDocShell::NotifyJSRunToCompletionStart(const char* aReason,
+ const char16_t* aFunctionName,
+ const char16_t* aFilename,
+ const uint32_t aLineNumber,
+ JS::Handle<JS::Value> aAsyncStack,
+ const char* aAsyncCause)
+{
+ // If first start, mark interval start.
+ if (mJSRunToCompletionDepth == 0) {
+ RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
+ if (timelines && timelines->HasConsumer(this)) {
+ timelines->AddMarkerForDocShell(this, Move(
+ mozilla::MakeUnique<JavascriptTimelineMarker>(
+ aReason, aFunctionName, aFilename, aLineNumber, MarkerTracingType::START,
+ aAsyncStack, aAsyncCause)));
+ }
+ }
+
+ mJSRunToCompletionDepth++;
+}
+
+void
+nsDocShell::NotifyJSRunToCompletionStop()
+{
+ mJSRunToCompletionDepth--;
+
+ // If last stop, mark interval end.
+ if (mJSRunToCompletionDepth == 0) {
+ RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
+ if (timelines && timelines->HasConsumer(this)) {
+ timelines->AddMarkerForDocShell(this, "Javascript", MarkerTracingType::END);
+ }
+ }
+}
+
+void
+nsDocShell::MaybeNotifyKeywordSearchLoading(const nsString& aProvider,
+ const nsString& aKeyword)
+{
+ if (aProvider.IsEmpty()) {
+ return;
+ }
+
+ if (XRE_IsContentProcess()) {
+ dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
+ if (contentChild) {
+ contentChild->SendNotifyKeywordSearchLoading(aProvider, aKeyword);
+ }
+ return;
+ }
+
+#ifdef MOZ_TOOLKIT_SEARCH
+ nsCOMPtr<nsIBrowserSearchService> searchSvc =
+ do_GetService("@mozilla.org/browser/search-service;1");
+ if (searchSvc) {
+ nsCOMPtr<nsISearchEngine> searchEngine;
+ searchSvc->GetEngineByName(aProvider, getter_AddRefs(searchEngine));
+ if (searchEngine) {
+ nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
+ if (obsSvc) {
+ // Note that "keyword-search" refers to a search via the url
+ // bar, not a bookmarks keyword search.
+ obsSvc->NotifyObservers(searchEngine, "keyword-search", aKeyword.get());
+ }
+ }
+ }
+#endif
+}
+
+NS_IMETHODIMP
+nsDocShell::ShouldPrepareForIntercept(nsIURI* aURI, bool aIsNonSubresourceRequest,
+ bool* aShouldIntercept)
+{
+ *aShouldIntercept = false;
+ // No in private browsing
+ if (UsePrivateBrowsing()) {
+ return NS_OK;
+ }
+
+ if (mSandboxFlags) {
+ // If we're sandboxed, don't intercept.
+ return NS_OK;
+ }
+
+ RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+ if (!swm) {
+ return NS_OK;
+ }
+
+ nsresult result;
+ nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
+ do_GetService(THIRDPARTYUTIL_CONTRACTID, &result);
+ NS_ENSURE_SUCCESS(result, result);
+
+ if (mCurrentURI &&
+ nsContentUtils::CookiesBehavior() == nsICookieService::BEHAVIOR_REJECT_FOREIGN) {
+ nsAutoCString uriSpec;
+ if (!(mCurrentURI->GetSpecOrDefault().EqualsLiteral("about:blank"))) {
+ // Reject the interception of third-party iframes if the cookie behaviour
+ // is set to reject all third-party cookies (1). In case that this pref
+ // is not set or can't be read, we default to allow all cookies (0) as
+ // this is the default value in all.js.
+ bool isThirdPartyURI = true;
+ result = thirdPartyUtil->IsThirdPartyURI(mCurrentURI, aURI,
+ &isThirdPartyURI);
+ if (NS_FAILED(result)) {
+ return result;
+ }
+
+ if (isThirdPartyURI) {
+ return NS_OK;
+ }
+ }
+ }
+
+ if (aIsNonSubresourceRequest) {
+ PrincipalOriginAttributes attrs;
+ attrs.InheritFromDocShellToDoc(mOriginAttributes, aURI);
+ nsCOMPtr<nsIPrincipal> principal =
+ BasePrincipal::CreateCodebasePrincipal(aURI, attrs);
+ *aShouldIntercept = swm->IsAvailable(principal, aURI);
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIDocument> doc = GetDocument();
+ if (!doc) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ ErrorResult rv;
+ *aShouldIntercept = swm->IsControlled(doc, rv);
+ if (NS_WARN_IF(rv.Failed())) {
+ return rv.StealNSResult();
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::ChannelIntercepted(nsIInterceptedChannel* aChannel)
+{
+ RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+ if (!swm) {
+ aChannel->Cancel(NS_ERROR_INTERCEPTION_FAILED);
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIChannel> channel;
+ nsresult rv = aChannel->GetChannel(getter_AddRefs(channel));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIDocument> doc;
+
+ bool isSubresourceLoad = !nsContentUtils::IsNonSubresourceRequest(channel);
+ if (isSubresourceLoad) {
+ doc = GetDocument();
+ if (!doc) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ } else {
+ // For top-level navigations, save a document ID which will be passed to
+ // the FetchEvent as the clientId later on.
+ rv = nsIDocument::GenerateDocumentId(mInterceptedDocumentId);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ bool isReload = mLoadType & LOAD_CMD_RELOAD;
+
+ nsCOMPtr<nsIURI> uri;
+ rv = channel->GetURI(getter_AddRefs(uri));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ PrincipalOriginAttributes attrs;
+ attrs.InheritFromDocShellToDoc(mOriginAttributes, uri);
+
+ ErrorResult error;
+ swm->DispatchFetchEvent(attrs, doc, mInterceptedDocumentId, aChannel,
+ isReload, isSubresourceLoad, error);
+ if (NS_WARN_IF(error.Failed())) {
+ return error.StealNSResult();
+ }
+
+ return NS_OK;
+}
+
+bool
+nsDocShell::InFrameSwap()
+{
+ RefPtr<nsDocShell> shell = this;
+ do {
+ if (shell->mInFrameSwap) {
+ return true;
+ }
+ shell = shell->GetParentDocshell();
+ } while (shell);
+ return false;
+}
+
+NS_IMETHODIMP
+nsDocShell::IssueWarning(uint32_t aWarning, bool aAsError)
+{
+ if (mContentViewer) {
+ nsCOMPtr<nsIDocument> doc = mContentViewer->GetDocument();
+ if (doc) {
+ doc->WarnOnceAbout(nsIDocument::DeprecatedOperations(aWarning), aAsError);
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetEditingSession(nsIEditingSession** aEditSession)
+{
+ if (!NS_SUCCEEDED(EnsureEditorData())) {
+ return NS_ERROR_FAILURE;
+ }
+
+ mEditorData->GetEditingSession(aEditSession);
+ return *aEditSession ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetScriptableTabChild(nsITabChild** aTabChild)
+{
+ *aTabChild = GetTabChild().take();
+ return *aTabChild ? NS_OK : NS_ERROR_FAILURE;
+}
+
+already_AddRefed<nsITabChild>
+nsDocShell::GetTabChild()
+{
+ nsCOMPtr<nsIDocShellTreeOwner> owner(mTreeOwner);
+ nsCOMPtr<nsITabChild> tc = do_GetInterface(owner);
+ return tc.forget();
+}
+
+nsICommandManager*
+nsDocShell::GetCommandManager()
+{
+ NS_ENSURE_SUCCESS(EnsureCommandHandler(), nullptr);
+ return mCommandManager;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetProcessLockReason(uint32_t* aReason)
+{
+ MOZ_ASSERT(aReason);
+
+ nsPIDOMWindowOuter* outer = GetWindow();
+ MOZ_ASSERT(outer);
+
+ // Check if we are a toplevel window
+ if (outer->GetScriptableParentOrNull()) {
+ *aReason = PROCESS_LOCK_IFRAME;
+ return NS_OK;
+ }
+
+ // If we have any other toplevel windows in our tab group, then we cannot
+ // perform the navigation.
+ nsTArray<nsPIDOMWindowOuter*> toplevelWindows =
+ outer->TabGroup()->GetTopLevelWindows();
+ if (toplevelWindows.Length() > 1) {
+ *aReason = PROCESS_LOCK_RELATED_CONTEXTS;
+ return NS_OK;
+ }
+ MOZ_ASSERT(toplevelWindows.Length() == 1);
+ MOZ_ASSERT(toplevelWindows[0] == outer);
+
+ // If we aren't in a content process, we cannot perform a cross-process load.
+ if (!XRE_IsContentProcess()) {
+ *aReason = PROCESS_LOCK_NON_CONTENT;
+ return NS_OK;
+ }
+
+ *aReason = PROCESS_LOCK_NONE;
+ return NS_OK;
+}
diff --git a/docshell/base/nsDocShell.h b/docshell/base/nsDocShell.h
new file mode 100644
index 000000000..3ca9e0b34
--- /dev/null
+++ b/docshell/base/nsDocShell.h
@@ -0,0 +1,1083 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsDocShell_h__
+#define nsDocShell_h__
+
+#include "nsITimer.h"
+#include "nsContentPolicyUtils.h"
+#include "nsIDocShell.h"
+#include "nsIDocShellTreeItem.h"
+#include "nsIBaseWindow.h"
+#include "nsINetworkInterceptController.h"
+#include "nsIScrollable.h"
+#include "nsITextScroll.h"
+#include "nsIContentViewerContainer.h"
+#include "nsIDOMStorageManager.h"
+#include "nsDocLoader.h"
+#include "mozilla/BasePrincipal.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/WeakPtr.h"
+#include "mozilla/TimeStamp.h"
+#include "GeckoProfiler.h"
+#include "mozilla/dom/ProfileTimelineMarkerBinding.h"
+#include "mozilla/LinkedList.h"
+#include "jsapi.h"
+
+// Helper Classes
+#include "nsCOMPtr.h"
+#include "nsPoint.h" // mCurrent/mDefaultScrollbarPreferences
+#include "nsString.h"
+#include "nsAutoPtr.h"
+#include "nsThreadUtils.h"
+#include "nsContentUtils.h"
+#include "timeline/ObservedDocShell.h"
+#include "timeline/TimelineConsumers.h"
+#include "timeline/TimelineMarker.h"
+
+// Threshold value in ms for META refresh based redirects
+#define REFRESH_REDIRECT_TIMER 15000
+
+// Interfaces Needed
+#include "nsIDocCharset.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIRefreshURI.h"
+#include "nsIWebNavigation.h"
+#include "nsIWebPageDescriptor.h"
+#include "nsIWebProgressListener.h"
+#include "nsIDocShellLoadInfo.h"
+#include "nsIAuthPromptProvider.h"
+#include "nsILoadContext.h"
+#include "nsIWebShellServices.h"
+#include "nsILinkHandler.h"
+#include "nsIClipboardCommands.h"
+#include "nsITabParent.h"
+#include "nsCRT.h"
+#include "prtime.h"
+#include "nsRect.h"
+#include "Units.h"
+#include "nsIDeprecationWarner.h"
+
+namespace mozilla {
+namespace dom {
+class EventTarget;
+typedef uint32_t ScreenOrientationInternal;
+} // namespace dom
+} // namespace mozilla
+
+class nsDocShell;
+class nsDOMNavigationTiming;
+class nsGlobalWindow;
+class nsIController;
+class nsIScrollableFrame;
+class OnLinkClickEvent;
+class nsDSURIContentListener;
+class nsDocShellEditorData;
+class nsIClipboardDragDropHookList;
+class nsICommandManager;
+class nsIContentViewer;
+class nsIDocument;
+class nsIDOMNode;
+class nsIDocShellTreeOwner;
+class nsIGlobalHistory2;
+class nsIHttpChannel;
+class nsIMutableArray;
+class nsIPrompt;
+class nsISHistory;
+class nsISecureBrowserUI;
+class nsIStringBundle;
+class nsIURIFixup;
+class nsIURILoader;
+class nsIWebBrowserFind;
+class nsIWidget;
+
+/* internally used ViewMode types */
+enum ViewMode
+{
+ viewNormal = 0x0,
+ viewSource = 0x1
+};
+
+class nsRefreshTimer : public nsITimerCallback
+{
+public:
+ nsRefreshTimer();
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSITIMERCALLBACK
+
+ int32_t GetDelay() { return mDelay ;}
+
+ RefPtr<nsDocShell> mDocShell;
+ nsCOMPtr<nsIURI> mURI;
+ nsCOMPtr<nsIPrincipal> mPrincipal;
+ int32_t mDelay;
+ bool mRepeat;
+ bool mMetaRefresh;
+
+protected:
+ virtual ~nsRefreshTimer();
+};
+
+enum eCharsetReloadState
+{
+ eCharsetReloadInit,
+ eCharsetReloadRequested,
+ eCharsetReloadStopOrigional
+};
+
+class nsDocShell final
+ : public nsDocLoader
+ , public nsIDocShell
+ , public nsIWebNavigation
+ , public nsIBaseWindow
+ , public nsIScrollable
+ , public nsITextScroll
+ , public nsIDocCharset
+ , public nsIContentViewerContainer
+ , public nsIRefreshURI
+ , public nsIWebProgressListener
+ , public nsIWebPageDescriptor
+ , public nsIAuthPromptProvider
+ , public nsILoadContext
+ , public nsIWebShellServices
+ , public nsILinkHandler
+ , public nsIClipboardCommands
+ , public nsIDOMStorageManager
+ , public nsINetworkInterceptController
+ , public nsIDeprecationWarner
+ , public mozilla::SupportsWeakPtr<nsDocShell>
+{
+ friend class nsDSURIContentListener;
+
+public:
+ MOZ_DECLARE_WEAKREFERENCE_TYPENAME(nsDocShell)
+
+ nsDocShell();
+
+ NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW
+
+ virtual nsresult Init() override;
+
+ NS_DECL_ISUPPORTS_INHERITED
+
+ NS_DECL_NSIDOCSHELL
+ NS_DECL_NSIDOCSHELLTREEITEM
+ NS_DECL_NSIWEBNAVIGATION
+ NS_DECL_NSIBASEWINDOW
+ NS_DECL_NSISCROLLABLE
+ NS_DECL_NSITEXTSCROLL
+ NS_DECL_NSIDOCCHARSET
+ NS_DECL_NSIINTERFACEREQUESTOR
+ NS_DECL_NSIWEBPROGRESSLISTENER
+ NS_DECL_NSIREFRESHURI
+ NS_DECL_NSICONTENTVIEWERCONTAINER
+ NS_DECL_NSIWEBPAGEDESCRIPTOR
+ NS_DECL_NSIAUTHPROMPTPROVIDER
+ NS_DECL_NSICLIPBOARDCOMMANDS
+ NS_DECL_NSIWEBSHELLSERVICES
+ NS_DECL_NSINETWORKINTERCEPTCONTROLLER
+ NS_DECL_NSIDEPRECATIONWARNER
+ NS_FORWARD_SAFE_NSIDOMSTORAGEMANAGER(TopSessionStorageManager())
+
+ NS_IMETHOD Stop() override
+ {
+ // Need this here because otherwise nsIWebNavigation::Stop
+ // overrides the docloader's Stop()
+ return nsDocLoader::Stop();
+ }
+
+ // Need to implement (and forward) nsISecurityEventSink, because
+ // nsIWebProgressListener has methods with identical names...
+ NS_FORWARD_NSISECURITYEVENTSINK(nsDocLoader::)
+
+ // nsILinkHandler
+ NS_IMETHOD OnLinkClick(nsIContent* aContent,
+ nsIURI* aURI,
+ const char16_t* aTargetSpec,
+ const nsAString& aFileName,
+ nsIInputStream* aPostDataStream,
+ nsIInputStream* aHeadersDataStream,
+ bool aIsTrusted) override;
+ NS_IMETHOD OnLinkClickSync(nsIContent* aContent,
+ nsIURI* aURI,
+ const char16_t* aTargetSpec,
+ const nsAString& aFileName,
+ nsIInputStream* aPostDataStream = 0,
+ nsIInputStream* aHeadersDataStream = 0,
+ nsIDocShell** aDocShell = 0,
+ nsIRequest** aRequest = 0) override;
+ NS_IMETHOD OnOverLink(nsIContent* aContent,
+ nsIURI* aURI,
+ const char16_t* aTargetSpec) override;
+ NS_IMETHOD OnLeaveLink() override;
+
+ nsDocShellInfoLoadType ConvertLoadTypeToDocShellLoadInfo(uint32_t aLoadType);
+ uint32_t ConvertDocShellLoadInfoToLoadType(
+ nsDocShellInfoLoadType aDocShellLoadType);
+
+ // Don't use NS_DECL_NSILOADCONTEXT because some of nsILoadContext's methods
+ // are shared with nsIDocShell (appID, etc.) and can't be declared twice.
+ NS_IMETHOD GetAssociatedWindow(mozIDOMWindowProxy**) override;
+ NS_IMETHOD GetTopWindow(mozIDOMWindowProxy**) override;
+ NS_IMETHOD GetTopFrameElement(nsIDOMElement**) override;
+ NS_IMETHOD GetNestedFrameId(uint64_t*) override;
+ NS_IMETHOD GetIsContent(bool*) override;
+ NS_IMETHOD GetUsePrivateBrowsing(bool*) override;
+ NS_IMETHOD SetUsePrivateBrowsing(bool) override;
+ NS_IMETHOD SetPrivateBrowsing(bool) override;
+ NS_IMETHOD GetUseRemoteTabs(bool*) override;
+ NS_IMETHOD SetRemoteTabs(bool) override;
+ NS_IMETHOD GetOriginAttributes(JS::MutableHandle<JS::Value>) override;
+ NS_IMETHOD IsTrackingProtectionOn(bool*) override;
+
+ // Restores a cached presentation from history (mLSHE).
+ // This method swaps out the content viewer and simulates loads for
+ // subframes. It then simulates the completion of the toplevel load.
+ nsresult RestoreFromHistory();
+
+ // Perform a URI load from a refresh timer. This is just like the
+ // ForceRefreshURI method on nsIRefreshURI, but makes sure to take
+ // the timer involved out of mRefreshURIList if it's there.
+ // aTimer must not be null.
+ nsresult ForceRefreshURIFromTimer(nsIURI* aURI, int32_t aDelay,
+ bool aMetaRefresh, nsITimer* aTimer,
+ nsIPrincipal* aPrincipal);
+
+ friend class OnLinkClickEvent;
+
+ // We need dummy OnLocationChange in some cases to update the UI without
+ // updating security info.
+ void FireDummyOnLocationChange()
+ {
+ FireOnLocationChange(this, nullptr, mCurrentURI,
+ LOCATION_CHANGE_SAME_DOCUMENT);
+ }
+
+ nsresult HistoryTransactionRemoved(int32_t aIndex);
+
+ // Notify Scroll observers when an async panning/zooming transform
+ // has started being applied
+ void NotifyAsyncPanZoomStarted();
+ // Notify Scroll observers when an async panning/zooming transform
+ // is no longer applied
+ void NotifyAsyncPanZoomStopped();
+
+ void SetInFrameSwap(bool aInSwap)
+ {
+ mInFrameSwap = aInSwap;
+ }
+ bool InFrameSwap();
+
+private:
+ bool CanSetOriginAttributes();
+
+public:
+ const mozilla::DocShellOriginAttributes&
+ GetOriginAttributes()
+ {
+ return mOriginAttributes;
+ }
+
+ nsresult SetOriginAttributes(const mozilla::DocShellOriginAttributes& aAttrs);
+
+ void GetInterceptedDocumentId(nsAString& aId)
+ {
+ aId = mInterceptedDocumentId;
+ }
+
+private:
+ // An observed docshell wrapper is created when recording markers is enabled.
+ mozilla::UniquePtr<mozilla::ObservedDocShell> mObserved;
+
+ // It is necessary to allow adding a timeline marker wherever a docshell
+ // instance is available. This operation happens frequently and needs to
+ // be very fast, so instead of using a Map or having to search for some
+ // docshell-specific markers storage, a pointer to an `ObservedDocShell` is
+ // is stored on docshells directly.
+ friend void mozilla::TimelineConsumers::AddConsumer(nsDocShell*);
+ friend void mozilla::TimelineConsumers::RemoveConsumer(nsDocShell*);
+ friend void mozilla::TimelineConsumers::AddMarkerForDocShell(
+ nsDocShell*, const char*, MarkerTracingType, MarkerStackRequest);
+ friend void mozilla::TimelineConsumers::AddMarkerForDocShell(
+ nsDocShell*, const char*, const TimeStamp&, MarkerTracingType,
+ MarkerStackRequest);
+ friend void mozilla::TimelineConsumers::AddMarkerForDocShell(
+ nsDocShell*, UniquePtr<AbstractTimelineMarker>&&);
+ friend void mozilla::TimelineConsumers::PopMarkers(nsDocShell*,
+ JSContext*, nsTArray<dom::ProfileTimelineMarker>&);
+
+public:
+ // Tell the favicon service that aNewURI has the same favicon as aOldURI.
+ static void CopyFavicon(nsIURI* aOldURI,
+ nsIURI* aNewURI,
+ nsIPrincipal* aLoadingPrincipal,
+ bool aInPrivateBrowsing);
+
+ static nsDocShell* Cast(nsIDocShell* aDocShell)
+ {
+ return static_cast<nsDocShell*>(aDocShell);
+ }
+
+protected:
+ virtual ~nsDocShell();
+ virtual void DestroyChildren() override;
+
+ // Content Viewer Management
+ nsresult EnsureContentViewer();
+ // aPrincipal can be passed in if the caller wants. If null is
+ // passed in, the about:blank principal will end up being used.
+ nsresult CreateAboutBlankContentViewer(nsIPrincipal* aPrincipal,
+ nsIURI* aBaseURI,
+ bool aTryToSaveOldPresentation = true);
+ nsresult CreateContentViewer(const nsACString& aContentType,
+ nsIRequest* aRequest,
+ nsIStreamListener** aContentHandler);
+ nsresult NewContentViewerObj(const nsACString& aContentType,
+ nsIRequest* aRequest, nsILoadGroup* aLoadGroup,
+ nsIStreamListener** aContentHandler,
+ nsIContentViewer** aViewer);
+ nsresult SetupNewViewer(nsIContentViewer* aNewViewer);
+
+ void SetupReferrerFromChannel(nsIChannel* aChannel);
+
+ nsresult GetEldestPresContext(nsPresContext** aPresContext);
+
+ // Get the principal that we'll set on the channel if we're inheriting. If
+ // aConsiderCurrentDocument is true, we try to use the current document if
+ // at all possible. If that fails, we fall back on the parent document.
+ // If that fails too, we force creation of a content viewer and use the
+ // resulting principal. If aConsiderCurrentDocument is false, we just look
+ // at the parent.
+ nsIPrincipal* GetInheritedPrincipal(bool aConsiderCurrentDocument);
+
+ // Actually open a channel and perform a URI load. Callers need to pass a
+ // non-null aTriggeringPrincipal which initiated the URI load. Please note
+ // that aTriggeringPrincipal will be used for performing security checks.
+ // If the argument aURI is provided by the web, then please do not pass a
+ // SystemPrincipal as the triggeringPrincipal. If principalToInherit is
+ // null, then no inheritance of any sort will happen and the load will
+ // get a principal based on the URI being loaded.
+ // If aSrcdoc is not void, the load will be considered as a srcdoc load,
+ // and the contents of aSrcdoc will be loaded instead of aURI.
+ // aOriginalURI will be set as the originalURI on the channel that does the
+ // load. If aOriginalURI is null, aURI will be set as the originalURI.
+ // If aLoadReplace is true, LOAD_REPLACE flag will be set to the nsIChannel.
+ nsresult DoURILoad(nsIURI* aURI,
+ nsIURI* aOriginalURI,
+ bool aLoadReplace,
+ nsIURI* aReferrer,
+ bool aSendReferrer,
+ uint32_t aReferrerPolicy,
+ nsIPrincipal* aTriggeringPrincipal,
+ nsIPrincipal* aPrincipalToInherit,
+ const char* aTypeHint,
+ const nsAString& aFileName,
+ nsIInputStream* aPostData,
+ nsIInputStream* aHeadersData,
+ bool aFirstParty,
+ nsIDocShell** aDocShell,
+ nsIRequest** aRequest,
+ bool aIsNewWindowTarget,
+ bool aBypassClassifier,
+ bool aForceAllowCookies,
+ const nsAString& aSrcdoc,
+ nsIURI* aBaseURI,
+ nsContentPolicyType aContentPolicyType);
+ nsresult AddHeadersToChannel(nsIInputStream* aHeadersData,
+ nsIChannel* aChannel);
+ nsresult DoChannelLoad(nsIChannel* aChannel,
+ nsIURILoader* aURILoader,
+ bool aBypassClassifier);
+
+ nsresult ScrollToAnchor(bool aCurHasRef,
+ bool aNewHasRef,
+ nsACString& aNewHash,
+ uint32_t aLoadType);
+
+ // Returns true if would have called FireOnLocationChange,
+ // but did not because aFireOnLocationChange was false on entry.
+ // In this case it is the caller's responsibility to ensure
+ // FireOnLocationChange is called.
+ // In all other cases false is returned.
+ bool OnLoadingSite(nsIChannel* aChannel,
+ bool aFireOnLocationChange,
+ bool aAddToGlobalHistory = true);
+
+ // Returns true if would have called FireOnLocationChange,
+ // but did not because aFireOnLocationChange was false on entry.
+ // In this case it is the caller's responsibility to ensure
+ // FireOnLocationChange is called.
+ // In all other cases false is returned.
+ // Either aChannel or aTriggeringPrincipal must be null. If aChannel is
+ // present, the owner should be gotten from it.
+ // If OnNewURI calls AddToSessionHistory, it will pass its
+ // aCloneSHChildren argument as aCloneChildren.
+ bool OnNewURI(nsIURI* aURI, nsIChannel* aChannel,
+ nsIPrincipal* aTriggeringPrincipal,
+ nsIPrincipal* aPrincipalToInherit,
+ uint32_t aLoadType,
+ bool aFireOnLocationChange,
+ bool aAddToGlobalHistory,
+ bool aCloneSHChildren);
+
+ void SetReferrerURI(nsIURI* aURI);
+ void SetReferrerPolicy(uint32_t aReferrerPolicy);
+
+ // Session History
+ bool ShouldAddToSessionHistory(nsIURI* aURI);
+ // Either aChannel or aOwner must be null. If aChannel is
+ // present, the owner should be gotten from it.
+ // If aCloneChildren is true, then our current session history's
+ // children will be cloned onto the new entry. This should be
+ // used when we aren't actually changing the document while adding
+ // the new session history entry.
+ nsresult AddToSessionHistory(nsIURI* aURI, nsIChannel* aChannel,
+ nsIPrincipal* aTriggeringPrincipal,
+ nsIPrincipal* aPrincipalToInherit,
+ bool aCloneChildren,
+ nsISHEntry** aNewEntry);
+ nsresult AddChildSHEntryToParent(nsISHEntry* aNewEntry, int32_t aChildOffset,
+ bool aCloneChildren);
+
+ nsresult AddChildSHEntryInternal(nsISHEntry* aCloneRef, nsISHEntry* aNewEntry,
+ int32_t aChildOffset, uint32_t aLoadType,
+ bool aCloneChildren);
+
+ nsresult LoadHistoryEntry(nsISHEntry* aEntry, uint32_t aLoadType);
+ nsresult PersistLayoutHistoryState();
+
+ // Clone a session history tree for subframe navigation.
+ // The tree rooted at |aSrcEntry| will be cloned into |aDestEntry|, except
+ // for the entry with id |aCloneID|, which will be replaced with
+ // |aReplaceEntry|. |aSrcShell| is a (possibly null) docshell which
+ // corresponds to |aSrcEntry| via its mLSHE or mOHE pointers, and will
+ // have that pointer updated to point to the cloned history entry.
+ // If aCloneChildren is true then the children of the entry with id
+ // |aCloneID| will be cloned into |aReplaceEntry|.
+ static nsresult CloneAndReplace(nsISHEntry* aSrcEntry,
+ nsDocShell* aSrcShell,
+ uint32_t aCloneID,
+ nsISHEntry* aReplaceEntry,
+ bool aCloneChildren,
+ nsISHEntry** aDestEntry);
+
+ // Child-walking callback for CloneAndReplace
+ static nsresult CloneAndReplaceChild(nsISHEntry* aEntry, nsDocShell* aShell,
+ int32_t aChildIndex, void* aData);
+
+ nsresult GetRootSessionHistory(nsISHistory** aReturn);
+ nsresult GetHttpChannel(nsIChannel* aChannel, nsIHttpChannel** aReturn);
+ bool ShouldDiscardLayoutState(nsIHttpChannel* aChannel);
+
+ // Determine whether this docshell corresponds to the given history entry,
+ // via having a pointer to it in mOSHE or mLSHE.
+ bool HasHistoryEntry(nsISHEntry* aEntry) const
+ {
+ return aEntry && (aEntry == mOSHE || aEntry == mLSHE);
+ }
+
+ // Update any pointers (mOSHE or mLSHE) to aOldEntry to point to aNewEntry
+ void SwapHistoryEntries(nsISHEntry* aOldEntry, nsISHEntry* aNewEntry);
+
+ // Call this method to swap in a new history entry to m[OL]SHE, rather than
+ // setting it directly. This completes the navigation in all docshells
+ // in the case of a subframe navigation.
+ void SetHistoryEntry(nsCOMPtr<nsISHEntry>* aPtr, nsISHEntry* aEntry);
+
+ // Child-walking callback for SetHistoryEntry
+ static nsresult SetChildHistoryEntry(nsISHEntry* aEntry, nsDocShell* aShell,
+ int32_t aEntryIndex, void* aData);
+
+ // Callback prototype for WalkHistoryEntries.
+ // aEntry is the child history entry, aShell is its corresponding docshell,
+ // aChildIndex is the child's index in its parent entry, and aData is
+ // the opaque pointer passed to WalkHistoryEntries.
+ typedef nsresult(*WalkHistoryEntriesFunc)(nsISHEntry* aEntry,
+ nsDocShell* aShell,
+ int32_t aChildIndex,
+ void* aData);
+
+ // For each child of aRootEntry, find the corresponding docshell which is
+ // a child of aRootShell, and call aCallback. The opaque pointer aData
+ // is passed to the callback.
+ static nsresult WalkHistoryEntries(nsISHEntry* aRootEntry,
+ nsDocShell* aRootShell,
+ WalkHistoryEntriesFunc aCallback,
+ void* aData);
+
+ // overridden from nsDocLoader, this provides more information than the
+ // normal OnStateChange with flags STATE_REDIRECTING
+ virtual void OnRedirectStateChange(nsIChannel* aOldChannel,
+ nsIChannel* aNewChannel,
+ uint32_t aRedirectFlags,
+ uint32_t aStateFlags) override;
+
+ /**
+ * Helper function that determines if channel is an HTTP POST.
+ *
+ * @param aChannel
+ * The channel to test
+ *
+ * @return True iff channel is an HTTP post.
+ */
+ bool ChannelIsPost(nsIChannel* aChannel);
+
+ /**
+ * Helper function that finds the last URI and its transition flags for a
+ * channel.
+ *
+ * This method first checks the channel's property bag to see if previous
+ * info has been saved. If not, it gives back the referrer of the channel.
+ *
+ * @param aChannel
+ * The channel we are transitioning to
+ * @param aURI
+ * Output parameter with the previous URI, not addref'd
+ * @param aChannelRedirectFlags
+ * If a redirect, output parameter with the previous redirect flags
+ * from nsIChannelEventSink
+ */
+ void ExtractLastVisit(nsIChannel* aChannel,
+ nsIURI** aURI,
+ uint32_t* aChannelRedirectFlags);
+
+ /**
+ * Helper function that caches a URI and a transition for saving later.
+ *
+ * @param aChannel
+ * Channel that will have these properties saved
+ * @param aURI
+ * The URI to save for later
+ * @param aChannelRedirectFlags
+ * The nsIChannelEventSink redirect flags to save for later
+ */
+ void SaveLastVisit(nsIChannel* aChannel,
+ nsIURI* aURI,
+ uint32_t aChannelRedirectFlags);
+
+ /**
+ * Helper function for adding a URI visit using IHistory. If IHistory is
+ * not available, the method tries nsIGlobalHistory2.
+ *
+ * The IHistory API maintains chains of visits, tracking both HTTP referrers
+ * and redirects for a user session. VisitURI requires the current URI and
+ * the previous URI in the chain.
+ *
+ * Visits can be saved either during a redirect or when the request has
+ * reached its final destination. The previous URI in the visit may be
+ * from another redirect or it may be the referrer.
+ *
+ * @pre aURI is not null.
+ *
+ * @param aURI
+ * The URI that was just visited
+ * @param aReferrerURI
+ * The referrer URI of this request
+ * @param aPreviousURI
+ * The previous URI of this visit (may be the same as aReferrerURI)
+ * @param aChannelRedirectFlags
+ * For redirects, the redirect flags from nsIChannelEventSink
+ * (0 otherwise)
+ * @param aResponseStatus
+ * For HTTP channels, the response code (0 otherwise).
+ */
+ void AddURIVisit(nsIURI* aURI,
+ nsIURI* aReferrerURI,
+ nsIURI* aPreviousURI,
+ uint32_t aChannelRedirectFlags,
+ uint32_t aResponseStatus = 0);
+
+ // Helper Routines
+ nsresult ConfirmRepost(bool* aRepost);
+ NS_IMETHOD GetPromptAndStringBundle(nsIPrompt** aPrompt,
+ nsIStringBundle** aStringBundle);
+ NS_IMETHOD GetChildOffset(nsIDOMNode* aChild, nsIDOMNode* aParent,
+ int32_t* aOffset);
+ nsIScrollableFrame* GetRootScrollFrame();
+ NS_IMETHOD EnsureScriptEnvironment();
+ NS_IMETHOD EnsureEditorData();
+ nsresult EnsureTransferableHookData();
+ NS_IMETHOD EnsureFind();
+ nsresult RefreshURIFromQueue();
+ NS_IMETHOD LoadErrorPage(nsIURI* aURI, const char16_t* aURL,
+ const char* aErrorPage,
+ const char16_t* aErrorType,
+ const char16_t* aDescription,
+ const char* aCSSClass,
+ nsIChannel* aFailedChannel);
+ bool IsPrintingOrPP(bool aDisplayErrorDialog = true);
+ bool IsNavigationAllowed(bool aDisplayPrintErrorDialog = true,
+ bool aCheckIfUnloadFired = true);
+
+ nsresult SetBaseUrlForWyciwyg(nsIContentViewer* aContentViewer);
+
+ static inline uint32_t PRTimeToSeconds(PRTime aTimeUsec)
+ {
+ PRTime usecPerSec = PR_USEC_PER_SEC;
+ return uint32_t(aTimeUsec /= usecPerSec);
+ }
+
+ inline bool UseErrorPages()
+ {
+ return (mObserveErrorPages ? sUseErrorPages : mUseErrorPages);
+ }
+
+ bool IsFrame();
+
+ //
+ // Helper method that is called when a new document (including any
+ // sub-documents - ie. frames) has been completely loaded.
+ //
+ virtual nsresult EndPageLoad(nsIWebProgress* aProgress,
+ nsIChannel* aChannel,
+ nsresult aResult);
+
+ // Sets the current document's current state object to the given SHEntry's
+ // state object. The current state object is eventually given to the page
+ // in the PopState event.
+ nsresult SetDocCurrentStateObj(nsISHEntry* aShEntry);
+
+ nsresult CheckLoadingPermissions();
+
+ // Security checks to prevent frameset spoofing. See comments at
+ // implementation sites.
+ static bool CanAccessItem(nsIDocShellTreeItem* aTargetItem,
+ nsIDocShellTreeItem* aAccessingItem,
+ bool aConsiderOpener = true);
+ static bool ValidateOrigin(nsIDocShellTreeItem* aOriginTreeItem,
+ nsIDocShellTreeItem* aTargetTreeItem);
+
+ // Returns true if would have called FireOnLocationChange,
+ // but did not because aFireOnLocationChange was false on entry.
+ // In this case it is the caller's responsibility to ensure
+ // FireOnLocationChange is called.
+ // In all other cases false is returned.
+ bool SetCurrentURI(nsIURI* aURI, nsIRequest* aRequest,
+ bool aFireOnLocationChange,
+ uint32_t aLocationFlags);
+
+ // The following methods deal with saving and restoring content viewers
+ // in session history.
+
+ // mContentViewer points to the current content viewer associated with
+ // this docshell. When loading a new document, the content viewer is
+ // either destroyed or stored into a session history entry. To make sure
+ // that destruction happens in a controlled fashion, a given content viewer
+ // is always owned in exactly one of these ways:
+ // 1) The content viewer is active and owned by a docshell's
+ // mContentViewer.
+ // 2) The content viewer is still being displayed while we begin loading
+ // a new document. The content viewer is owned by the _new_
+ // content viewer's mPreviousViewer, and has a pointer to the
+ // nsISHEntry where it will eventually be stored. The content viewer
+ // has been close()d by the docshell, which detaches the document from
+ // the window object.
+ // 3) The content viewer is cached in session history. The nsISHEntry
+ // has the only owning reference to the content viewer. The viewer
+ // has released its nsISHEntry pointer to prevent circular ownership.
+ //
+ // When restoring a content viewer from session history, open() is called
+ // to reattach the document to the window object. The content viewer is
+ // then placed into mContentViewer and removed from the history entry.
+ // (mContentViewer is put into session history as described above, if
+ // applicable).
+
+ // Determines whether we can safely cache the current mContentViewer in
+ // session history. This checks a number of factors such as cache policy,
+ // pending requests, and unload handlers.
+ // |aLoadType| should be the load type that will replace the current
+ // presentation. |aNewRequest| should be the request for the document to
+ // be loaded in place of the current document, or null if such a request
+ // has not been created yet. |aNewDocument| should be the document that will
+ // replace the current document.
+ bool CanSavePresentation(uint32_t aLoadType,
+ nsIRequest* aNewRequest,
+ nsIDocument* aNewDocument);
+
+ // Captures the state of the supporting elements of the presentation
+ // (the "window" object, docshell tree, meta-refresh loads, and security
+ // state) and stores them on |mOSHE|.
+ nsresult CaptureState();
+
+ // Begin the toplevel restore process for |aSHEntry|.
+ // This simulates a channel open, and defers the real work until
+ // RestoreFromHistory is called from a PLEvent.
+ nsresult RestorePresentation(nsISHEntry* aSHEntry, bool* aRestoring);
+
+ // Call BeginRestore(nullptr, false) for each child of this shell.
+ nsresult BeginRestoreChildren();
+
+ // Method to get our current position and size without flushing
+ void DoGetPositionAndSize(int32_t* aX, int32_t* aY, int32_t* aWidth,
+ int32_t* aHeight);
+
+ // Call this when a URI load is handed to us (via OnLinkClick or
+ // InternalLoad). This makes sure that we're not inside unload, or that if
+ // we are it's still OK to load this URI.
+ bool IsOKToLoadURI(nsIURI* aURI);
+
+ void ReattachEditorToWindow(nsISHEntry* aSHEntry);
+
+ nsCOMPtr<nsIDOMStorageManager> mSessionStorageManager;
+ nsIDOMStorageManager* TopSessionStorageManager();
+
+ // helpers for executing commands
+ nsresult GetControllerForCommand(const char* aCommand,
+ nsIController** aResult);
+ nsresult EnsureCommandHandler();
+
+ nsIChannel* GetCurrentDocChannel();
+
+ bool ShouldBlockLoadingForBackButton();
+
+ // Convenience method for getting our parent docshell. Can return null
+ already_AddRefed<nsDocShell> GetParentDocshell();
+
+ // Check if aURI is about:newtab.
+ bool IsAboutNewtab(nsIURI* aURI);
+
+protected:
+ nsresult GetCurScrollPos(int32_t aScrollOrientation, int32_t* aCurPos);
+ nsresult SetCurScrollPosEx(int32_t aCurHorizontalPos,
+ int32_t aCurVerticalPos);
+
+ // Override the parent setter from nsDocLoader
+ virtual nsresult SetDocLoaderParent(nsDocLoader* aLoader) override;
+
+ void ClearFrameHistory(nsISHEntry* aEntry);
+
+ /**
+ * Initializes mTiming if it isn't yet.
+ * After calling this, mTiming is non-null.
+ */
+ void MaybeInitTiming();
+
+ bool DisplayLoadError(nsresult aError, nsIURI* aURI, const char16_t* aURL,
+ nsIChannel* aFailedChannel)
+ {
+ bool didDisplayLoadError = false;
+ DisplayLoadError(aError, aURI, aURL, aFailedChannel, &didDisplayLoadError);
+ return didDisplayLoadError;
+ }
+
+public:
+ // Event type dispatched by RestorePresentation
+ class RestorePresentationEvent : public mozilla::Runnable
+ {
+ public:
+ NS_DECL_NSIRUNNABLE
+ explicit RestorePresentationEvent(nsDocShell* aDs) : mDocShell(aDs) {}
+ void Revoke() { mDocShell = nullptr; }
+ private:
+ RefPtr<nsDocShell> mDocShell;
+ };
+
+protected:
+ bool JustStartedNetworkLoad();
+
+ nsresult CreatePrincipalFromReferrer(nsIURI* aReferrer,
+ nsIPrincipal** aResult);
+
+ static const nsCString FrameTypeToString(uint32_t aFrameType)
+ {
+ switch (aFrameType) {
+ case FRAME_TYPE_APP:
+ return NS_LITERAL_CSTRING("app");
+ case FRAME_TYPE_BROWSER:
+ return NS_LITERAL_CSTRING("browser");
+ case FRAME_TYPE_REGULAR:
+ return NS_LITERAL_CSTRING("regular");
+ default:
+ NS_ERROR("Unknown frame type");
+ return EmptyCString();
+ }
+ }
+
+ uint32_t GetInheritedFrameType();
+
+ bool HasUnloadedParent();
+
+ // Dimensions of the docshell
+ nsIntRect mBounds;
+ nsString mName;
+ nsString mTitle;
+ nsString mCustomUserAgent;
+
+ /**
+ * Content-Type Hint of the most-recently initiated load. Used for
+ * session history entries.
+ */
+ nsCString mContentTypeHint;
+ nsIntPoint mDefaultScrollbarPref; // persistent across doc loads
+
+ nsCOMPtr<nsIMutableArray> mRefreshURIList;
+ nsCOMPtr<nsIMutableArray> mSavedRefreshURIList;
+ RefPtr<nsDSURIContentListener> mContentListener;
+ nsCOMPtr<nsIContentViewer> mContentViewer;
+ nsCOMPtr<nsIWidget> mParentWidget;
+
+ // mCurrentURI should be marked immutable on set if possible.
+ nsCOMPtr<nsIURI> mCurrentURI;
+ nsCOMPtr<nsIURI> mReferrerURI;
+ uint32_t mReferrerPolicy;
+ RefPtr<nsGlobalWindow> mScriptGlobal;
+ nsCOMPtr<nsISHistory> mSessionHistory;
+ nsCOMPtr<nsIGlobalHistory2> mGlobalHistory;
+ nsCOMPtr<nsIWebBrowserFind> mFind;
+ nsCOMPtr<nsICommandManager> mCommandManager;
+ // Reference to the SHEntry for this docshell until the page is destroyed.
+ // Somebody give me better name
+ nsCOMPtr<nsISHEntry> mOSHE;
+ // Reference to the SHEntry for this docshell until the page is loaded
+ // Somebody give me better name.
+ // If mLSHE is non-null, non-pushState subframe loads don't create separate
+ // root history entries. That is, frames loaded during the parent page
+ // load don't generate history entries the way frame navigation after the
+ // parent has loaded does. (This isn't the only purpose of mLSHE.)
+ nsCOMPtr<nsISHEntry> mLSHE;
+
+ // Holds a weak pointer to a RestorePresentationEvent object if any that
+ // holds a weak pointer back to us. We use this pointer to possibly revoke
+ // the event whenever necessary.
+ nsRevocableEventPtr<RestorePresentationEvent> mRestorePresentationEvent;
+
+ // Editor data, if this document is designMode or contentEditable.
+ nsAutoPtr<nsDocShellEditorData> mEditorData;
+
+ // Transferable hooks/callbacks
+ nsCOMPtr<nsIClipboardDragDropHookList> mTransferableHookData;
+
+ // Secure browser UI object
+ nsCOMPtr<nsISecureBrowserUI> mSecurityUI;
+
+ // The URI we're currently loading. This is only relevant during the
+ // firing of a pagehide/unload. The caller of FirePageHideNotification()
+ // is responsible for setting it and unsetting it. It may be null if the
+ // pagehide/unload is happening for some reason other than just loading a
+ // new URI.
+ nsCOMPtr<nsIURI> mLoadingURI;
+
+ // Set in LoadErrorPage from the method argument and used later
+ // in CreateContentViewer. We have to delay an shistory entry creation
+ // for which these objects are needed.
+ nsCOMPtr<nsIURI> mFailedURI;
+ nsCOMPtr<nsIChannel> mFailedChannel;
+ uint32_t mFailedLoadType;
+
+ // Set in DoURILoad when either the LOAD_RELOAD_ALLOW_MIXED_CONTENT flag or
+ // the LOAD_NORMAL_ALLOW_MIXED_CONTENT flag is set.
+ // Checked in nsMixedContentBlocker, to see if the channels match.
+ nsCOMPtr<nsIChannel> mMixedContentChannel;
+
+ // WEAK REFERENCES BELOW HERE.
+ // Note these are intentionally not addrefd. Doing so will create a cycle.
+ // For that reasons don't use nsCOMPtr.
+
+ nsIDocShellTreeOwner* mTreeOwner; // Weak Reference
+ mozilla::dom::EventTarget* mChromeEventHandler; // Weak Reference
+
+ eCharsetReloadState mCharsetReloadState;
+
+ // Offset in the parent's child list.
+ // -1 if the docshell is added dynamically to the parent shell.
+ uint32_t mChildOffset;
+ uint32_t mBusyFlags;
+ uint32_t mAppType;
+ uint32_t mLoadType;
+
+ int32_t mMarginWidth;
+ int32_t mMarginHeight;
+
+ // This can either be a content docshell or a chrome docshell. After
+ // Create() is called, the type is not expected to change.
+ int32_t mItemType;
+
+ // Index into the SHTransaction list, indicating the previous and current
+ // transaction at the time that this DocShell begins to load
+ int32_t mPreviousTransIndex;
+ int32_t mLoadedTransIndex;
+
+ uint32_t mSandboxFlags;
+ nsWeakPtr mOnePermittedSandboxedNavigator;
+
+ // The orientation lock as described by
+ // https://w3c.github.io/screen-orientation/
+ mozilla::dom::ScreenOrientationInternal mOrientationLock;
+
+ // mFullscreenAllowed stores how we determine whether fullscreen is allowed
+ // when GetFullscreenAllowed() is called. Fullscreen is allowed in a
+ // docshell when all containing iframes have the allowfullscreen
+ // attribute set to true. When mFullscreenAllowed is CHECK_ATTRIBUTES
+ // we check this docshell's containing frame for the allowfullscreen
+ // attribute, and recurse onto the parent docshell to ensure all containing
+ // frames also have the allowfullscreen attribute. If we find an ancestor
+ // docshell with mFullscreenAllowed not equal to CHECK_ATTRIBUTES, we've
+ // reached a content boundary, and mFullscreenAllowed denotes whether the
+ // parent across the content boundary has allowfullscreen=true in all its
+ // containing iframes. mFullscreenAllowed defaults to CHECK_ATTRIBUTES and
+ // is set otherwise when docshells which are content boundaries are created.
+ enum FullscreenAllowedState : uint8_t
+ {
+ CHECK_ATTRIBUTES,
+ PARENT_ALLOWS,
+ PARENT_PROHIBITS
+ };
+ FullscreenAllowedState mFullscreenAllowed;
+
+ // Cached value of the "browser.xul.error_pages.enabled" preference.
+ static bool sUseErrorPages;
+
+ bool mCreated : 1;
+ bool mAllowSubframes : 1;
+ bool mAllowPlugins : 1;
+ bool mAllowJavascript : 1;
+ bool mAllowMetaRedirects : 1;
+ bool mAllowImages : 1;
+ bool mAllowMedia : 1;
+ bool mAllowDNSPrefetch : 1;
+ bool mAllowWindowControl : 1;
+ bool mAllowContentRetargeting : 1;
+ bool mAllowContentRetargetingOnChildren : 1;
+ bool mUseErrorPages : 1;
+ bool mObserveErrorPages : 1;
+ bool mAllowAuth : 1;
+ bool mAllowKeywordFixup : 1;
+ bool mIsOffScreenBrowser : 1;
+ bool mIsActive : 1;
+ bool mDisableMetaRefreshWhenInactive : 1;
+ bool mIsPrerendered : 1;
+ bool mIsAppTab : 1;
+ bool mUseGlobalHistory : 1;
+ bool mUseRemoteTabs : 1;
+ bool mDeviceSizeIsPageSize : 1;
+ bool mWindowDraggingAllowed : 1;
+ bool mInFrameSwap : 1;
+ bool mInheritPrivateBrowsingId : 1;
+
+ // Because scriptability depends on the mAllowJavascript values of our
+ // ancestors, we cache the effective scriptability and recompute it when
+ // it might have changed;
+ bool mCanExecuteScripts : 1;
+ void RecomputeCanExecuteScripts();
+
+ // This boolean is set to true right before we fire pagehide and generally
+ // unset when we embed a new content viewer. While it's true no navigation
+ // is allowed in this docshell.
+ bool mFiredUnloadEvent : 1;
+
+ // this flag is for bug #21358. a docshell may load many urls
+ // which don't result in new documents being created (i.e. a new
+ // content viewer) we want to make sure we don't call a on load
+ // event more than once for a given content viewer.
+ bool mEODForCurrentDocument : 1;
+ bool mURIResultedInDocument : 1;
+
+ bool mIsBeingDestroyed : 1;
+
+ bool mIsExecutingOnLoadHandler : 1;
+
+ // Indicates that a DocShell in this "docshell tree" is printing
+ bool mIsPrintingOrPP : 1;
+
+ // Indicates to CreateContentViewer() that it is safe to cache the old
+ // presentation of the page, and to SetupNewViewer() that the old viewer
+ // should be passed a SHEntry to save itself into.
+ bool mSavingOldViewer : 1;
+
+ // @see nsIDocShellHistory::createdDynamically
+ bool mDynamicallyCreated : 1;
+ bool mAffectPrivateSessionLifetime : 1;
+ bool mInvisible : 1;
+ bool mHasLoadedNonBlankURI : 1;
+
+ // This flag means that mTiming has been initialized but nulled out.
+ // We will check the innerWin's timing before creating a new one
+ // in MaybeInitTiming()
+ bool mBlankTiming : 1;
+
+ // The following two fields cannot be declared as bit fields
+ // because of uses with AutoRestore.
+ bool mCreatingDocument; // (should be) debugging only
+#ifdef DEBUG
+ bool mInEnsureScriptEnv;
+#endif
+
+ uint64_t mHistoryID;
+ uint32_t mDefaultLoadFlags;
+
+ static nsIURIFixup* sURIFixup;
+
+ RefPtr<nsDOMNavigationTiming> mTiming;
+
+ // Are we a regular frame, a browser frame, or an app frame?
+ uint32_t mFrameType;
+
+ // This represents the state of private browsing in the docshell.
+ // Currently treated as a binary value: 1 - in private mode, 0 - not private mode
+ // On content docshells mPrivateBrowsingId == mOriginAttributes.mPrivateBrowsingId
+ // On chrome docshells this value will be set, but not have the corresponding
+ // origin attribute set.
+ uint32_t mPrivateBrowsingId;
+
+ nsString mInterceptedDocumentId;
+
+private:
+ nsCString mForcedCharset;
+ nsCString mParentCharset;
+ int32_t mParentCharsetSource;
+ nsCOMPtr<nsIPrincipal> mParentCharsetPrincipal;
+ nsTObserverArray<nsWeakPtr> mPrivacyObservers;
+ nsTObserverArray<nsWeakPtr> mReflowObservers;
+ nsTObserverArray<nsWeakPtr> mScrollObservers;
+ nsCString mOriginalUriString;
+ nsWeakPtr mOpener;
+ mozilla::DocShellOriginAttributes mOriginAttributes;
+
+ // A depth count of how many times NotifyRunToCompletionStart
+ // has been called without a matching NotifyRunToCompletionStop.
+ uint32_t mJSRunToCompletionDepth;
+
+ // Whether or not touch events are overridden. Possible values are defined
+ // as constants in the nsIDocShell.idl file.
+ uint32_t mTouchEventsOverride;
+
+ // Separate function to do the actual name (i.e. not _top, _self etc.)
+ // searching for FindItemWithName.
+ nsresult DoFindItemWithName(const nsAString& aName,
+ nsISupports* aRequestor,
+ nsIDocShellTreeItem* aOriginalRequestor,
+ nsIDocShellTreeItem** aResult);
+
+ // Helper assertion to enforce that mInPrivateBrowsing is in sync with
+ // OriginAttributes.mPrivateBrowsingId
+ void AssertOriginAttributesMatchPrivateBrowsing();
+
+ // Notify consumers of a search being loaded through the observer service:
+ void MaybeNotifyKeywordSearchLoading(const nsString& aProvider,
+ const nsString& aKeyword);
+
+#ifdef DEBUG
+ // We're counting the number of |nsDocShells| to help find leaks
+ static unsigned long gNumberOfDocShells;
+#endif /* DEBUG */
+
+public:
+ class InterfaceRequestorProxy : public nsIInterfaceRequestor
+ {
+ public:
+ explicit InterfaceRequestorProxy(nsIInterfaceRequestor* aRequestor);
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIINTERFACEREQUESTOR
+
+ protected:
+ virtual ~InterfaceRequestorProxy();
+ InterfaceRequestorProxy() {}
+ nsWeakPtr mWeakPtr;
+ };
+};
+
+#endif /* nsDocShell_h__ */
diff --git a/docshell/base/nsDocShellEditorData.cpp b/docshell/base/nsDocShellEditorData.cpp
new file mode 100644
index 000000000..7e4068eb9
--- /dev/null
+++ b/docshell/base/nsDocShellEditorData.cpp
@@ -0,0 +1,192 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsDocShellEditorData.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "nsPIDOMWindow.h"
+#include "nsIDOMDocument.h"
+#include "nsIEditor.h"
+#include "nsIEditingSession.h"
+#include "nsIDocShell.h"
+
+nsDocShellEditorData::nsDocShellEditorData(nsIDocShell* aOwningDocShell)
+ : mDocShell(aOwningDocShell)
+ , mMakeEditable(false)
+ , mIsDetached(false)
+ , mDetachedMakeEditable(false)
+ , mDetachedEditingState(nsIHTMLDocument::eOff)
+{
+ NS_ASSERTION(mDocShell, "Where is my docShell?");
+}
+
+nsDocShellEditorData::~nsDocShellEditorData()
+{
+ TearDownEditor();
+}
+
+void
+nsDocShellEditorData::TearDownEditor()
+{
+ if (mEditor) {
+ mEditor->PreDestroy(false);
+ mEditor = nullptr;
+ }
+ mEditingSession = nullptr;
+ mIsDetached = false;
+}
+
+nsresult
+nsDocShellEditorData::MakeEditable(bool aInWaitForUriLoad)
+{
+ if (mMakeEditable) {
+ return NS_OK;
+ }
+
+ // if we are already editable, and are getting turned off,
+ // nuke the editor.
+ if (mEditor) {
+ NS_WARNING("Destroying existing editor on frame");
+
+ mEditor->PreDestroy(false);
+ mEditor = nullptr;
+ }
+
+ if (aInWaitForUriLoad) {
+ mMakeEditable = true;
+ }
+ return NS_OK;
+}
+
+bool
+nsDocShellEditorData::GetEditable()
+{
+ return mMakeEditable || (mEditor != nullptr);
+}
+
+nsresult
+nsDocShellEditorData::CreateEditor()
+{
+ nsCOMPtr<nsIEditingSession> editingSession;
+ nsresult rv = GetEditingSession(getter_AddRefs(editingSession));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ nsCOMPtr<nsPIDOMWindowOuter> domWindow =
+ mDocShell ? mDocShell->GetWindow() : nullptr;
+ rv = editingSession->SetupEditorOnWindow(domWindow);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsDocShellEditorData::GetEditingSession(nsIEditingSession** aResult)
+{
+ nsresult rv = EnsureEditingSession();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_ADDREF(*aResult = mEditingSession);
+
+ return NS_OK;
+}
+
+nsresult
+nsDocShellEditorData::GetEditor(nsIEditor** aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+ NS_IF_ADDREF(*aResult = mEditor);
+ return NS_OK;
+}
+
+nsresult
+nsDocShellEditorData::SetEditor(nsIEditor* aEditor)
+{
+ // destroy any editor that we have. Checks for equality are
+ // necessary to ensure that assigment into the nsCOMPtr does
+ // not temporarily reduce the refCount of the editor to zero
+ if (mEditor.get() != aEditor) {
+ if (mEditor) {
+ mEditor->PreDestroy(false);
+ mEditor = nullptr;
+ }
+
+ mEditor = aEditor; // owning addref
+ if (!mEditor) {
+ mMakeEditable = false;
+ }
+ }
+
+ return NS_OK;
+}
+
+// This creates the editing session on the content docShell that owns 'this'.
+nsresult
+nsDocShellEditorData::EnsureEditingSession()
+{
+ NS_ASSERTION(mDocShell, "Should have docShell here");
+ NS_ASSERTION(!mIsDetached, "This will stomp editing session!");
+
+ nsresult rv = NS_OK;
+
+ if (!mEditingSession) {
+ mEditingSession =
+ do_CreateInstance("@mozilla.org/editor/editingsession;1", &rv);
+ }
+
+ return rv;
+}
+
+nsresult
+nsDocShellEditorData::DetachFromWindow()
+{
+ NS_ASSERTION(mEditingSession,
+ "Can't detach when we don't have a session to detach!");
+
+ nsCOMPtr<nsPIDOMWindowOuter> domWindow =
+ mDocShell ? mDocShell->GetWindow() : nullptr;
+ nsresult rv = mEditingSession->DetachFromWindow(domWindow);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mIsDetached = true;
+ mDetachedMakeEditable = mMakeEditable;
+ mMakeEditable = false;
+
+ nsCOMPtr<nsIDocument> doc = domWindow->GetDoc();
+ nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(doc);
+ if (htmlDoc) {
+ mDetachedEditingState = htmlDoc->GetEditingState();
+ }
+
+ mDocShell = nullptr;
+
+ return NS_OK;
+}
+
+nsresult
+nsDocShellEditorData::ReattachToWindow(nsIDocShell* aDocShell)
+{
+ mDocShell = aDocShell;
+
+ nsCOMPtr<nsPIDOMWindowOuter> domWindow =
+ mDocShell ? mDocShell->GetWindow() : nullptr;
+ nsresult rv = mEditingSession->ReattachToWindow(domWindow);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mIsDetached = false;
+ mMakeEditable = mDetachedMakeEditable;
+
+ nsCOMPtr<nsIDocument> doc = domWindow->GetDoc();
+ nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(doc);
+ if (htmlDoc) {
+ htmlDoc->SetEditingState(mDetachedEditingState);
+ }
+
+ return NS_OK;
+}
diff --git a/docshell/base/nsDocShellEditorData.h b/docshell/base/nsDocShellEditorData.h
new file mode 100644
index 000000000..272190503
--- /dev/null
+++ b/docshell/base/nsDocShellEditorData.h
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef nsDocShellEditorData_h__
+#define nsDocShellEditorData_h__
+
+#ifndef nsCOMPtr_h___
+#include "nsCOMPtr.h"
+#endif
+
+#include "nsIHTMLDocument.h"
+
+class nsIDocShell;
+class nsIEditingSession;
+class nsIEditor;
+
+class nsDocShellEditorData
+{
+public:
+ explicit nsDocShellEditorData(nsIDocShell* aOwningDocShell);
+ ~nsDocShellEditorData();
+
+ nsresult MakeEditable(bool aWaitForUriLoad);
+ bool GetEditable();
+ nsresult CreateEditor();
+ nsresult GetEditingSession(nsIEditingSession** aResult);
+ nsresult GetEditor(nsIEditor** aResult);
+ nsresult SetEditor(nsIEditor* aEditor);
+ void TearDownEditor();
+ nsresult DetachFromWindow();
+ nsresult ReattachToWindow(nsIDocShell* aDocShell);
+ bool WaitingForLoad() const { return mMakeEditable; }
+
+protected:
+ nsresult EnsureEditingSession();
+
+ // The doc shell that owns us. Weak ref, since it always outlives us.
+ nsIDocShell* mDocShell;
+
+ // Only present for the content root docShell. Session is owned here.
+ nsCOMPtr<nsIEditingSession> mEditingSession;
+
+ // Indicates whether to make an editor after a url load.
+ bool mMakeEditable;
+
+ // If this frame is editable, store editor here. Editor is owned here.
+ nsCOMPtr<nsIEditor> mEditor;
+
+ // Denotes if the editor is detached from its window. The editor is detached
+ // while it's stored in the session history bfcache.
+ bool mIsDetached;
+
+ // Backup for mMakeEditable while the editor is detached.
+ bool mDetachedMakeEditable;
+
+ // Backup for the corresponding nsIHTMLDocument's editing state while
+ // the editor is detached.
+ nsIHTMLDocument::EditingState mDetachedEditingState;
+};
+
+#endif // nsDocShellEditorData_h__
diff --git a/docshell/base/nsDocShellEnumerator.cpp b/docshell/base/nsDocShellEnumerator.cpp
new file mode 100644
index 000000000..1f870cd01
--- /dev/null
+++ b/docshell/base/nsDocShellEnumerator.cpp
@@ -0,0 +1,205 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsDocShellEnumerator.h"
+
+#include "nsIDocShellTreeItem.h"
+
+nsDocShellEnumerator::nsDocShellEnumerator(int32_t aEnumerationDirection)
+ : mRootItem(nullptr)
+ , mCurIndex(0)
+ , mDocShellType(nsIDocShellTreeItem::typeAll)
+ , mArrayValid(false)
+ , mEnumerationDirection(aEnumerationDirection)
+{
+}
+
+nsDocShellEnumerator::~nsDocShellEnumerator()
+{
+}
+
+NS_IMPL_ISUPPORTS(nsDocShellEnumerator, nsISimpleEnumerator)
+
+NS_IMETHODIMP
+nsDocShellEnumerator::GetNext(nsISupports** aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+ *aResult = nullptr;
+
+ nsresult rv = EnsureDocShellArray();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ if (mCurIndex >= mItemArray.Length()) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // post-increment is important here
+ nsCOMPtr<nsISupports> item = do_QueryReferent(mItemArray[mCurIndex++], &rv);
+ item.forget(aResult);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDocShellEnumerator::HasMoreElements(bool* aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+ *aResult = false;
+
+ nsresult rv = EnsureDocShellArray();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ *aResult = (mCurIndex < mItemArray.Length());
+ return NS_OK;
+}
+
+nsresult
+nsDocShellEnumerator::GetEnumerationRootItem(
+ nsIDocShellTreeItem** aEnumerationRootItem)
+{
+ NS_ENSURE_ARG_POINTER(aEnumerationRootItem);
+ nsCOMPtr<nsIDocShellTreeItem> item = do_QueryReferent(mRootItem);
+ item.forget(aEnumerationRootItem);
+ return NS_OK;
+}
+
+nsresult
+nsDocShellEnumerator::SetEnumerationRootItem(
+ nsIDocShellTreeItem* aEnumerationRootItem)
+{
+ mRootItem = do_GetWeakReference(aEnumerationRootItem);
+ ClearState();
+ return NS_OK;
+}
+
+nsresult
+nsDocShellEnumerator::GetEnumDocShellType(int32_t* aEnumerationItemType)
+{
+ NS_ENSURE_ARG_POINTER(aEnumerationItemType);
+ *aEnumerationItemType = mDocShellType;
+ return NS_OK;
+}
+
+nsresult
+nsDocShellEnumerator::SetEnumDocShellType(int32_t aEnumerationItemType)
+{
+ mDocShellType = aEnumerationItemType;
+ ClearState();
+ return NS_OK;
+}
+
+nsresult
+nsDocShellEnumerator::First()
+{
+ mCurIndex = 0;
+ return EnsureDocShellArray();
+}
+
+nsresult
+nsDocShellEnumerator::EnsureDocShellArray()
+{
+ if (!mArrayValid) {
+ mArrayValid = true;
+ return BuildDocShellArray(mItemArray);
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsDocShellEnumerator::ClearState()
+{
+ mItemArray.Clear();
+ mArrayValid = false;
+ mCurIndex = 0;
+ return NS_OK;
+}
+
+nsresult
+nsDocShellEnumerator::BuildDocShellArray(nsTArray<nsWeakPtr>& aItemArray)
+{
+ NS_ENSURE_TRUE(mRootItem, NS_ERROR_NOT_INITIALIZED);
+ aItemArray.Clear();
+ nsCOMPtr<nsIDocShellTreeItem> item = do_QueryReferent(mRootItem);
+ return BuildArrayRecursive(item, aItemArray);
+}
+
+nsresult
+nsDocShellForwardsEnumerator::BuildArrayRecursive(
+ nsIDocShellTreeItem* aItem,
+ nsTArray<nsWeakPtr>& aItemArray)
+{
+ nsresult rv;
+
+ // add this item to the array
+ if (mDocShellType == nsIDocShellTreeItem::typeAll ||
+ aItem->ItemType() == mDocShellType) {
+ if (!aItemArray.AppendElement(do_GetWeakReference(aItem))) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+
+ int32_t numChildren;
+ rv = aItem->GetChildCount(&numChildren);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ for (int32_t i = 0; i < numChildren; ++i) {
+ nsCOMPtr<nsIDocShellTreeItem> curChild;
+ rv = aItem->GetChildAt(i, getter_AddRefs(curChild));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ rv = BuildArrayRecursive(curChild, aItemArray);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsDocShellBackwardsEnumerator::BuildArrayRecursive(
+ nsIDocShellTreeItem* aItem,
+ nsTArray<nsWeakPtr>& aItemArray)
+{
+ nsresult rv;
+
+ int32_t numChildren;
+ rv = aItem->GetChildCount(&numChildren);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ for (int32_t i = numChildren - 1; i >= 0; --i) {
+ nsCOMPtr<nsIDocShellTreeItem> curChild;
+ rv = aItem->GetChildAt(i, getter_AddRefs(curChild));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ rv = BuildArrayRecursive(curChild, aItemArray);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+
+ // add this item to the array
+ if (mDocShellType == nsIDocShellTreeItem::typeAll ||
+ aItem->ItemType() == mDocShellType) {
+ if (!aItemArray.AppendElement(do_GetWeakReference(aItem))) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+
+ return NS_OK;
+}
diff --git a/docshell/base/nsDocShellEnumerator.h b/docshell/base/nsDocShellEnumerator.h
new file mode 100644
index 000000000..87de74f0f
--- /dev/null
+++ b/docshell/base/nsDocShellEnumerator.h
@@ -0,0 +1,107 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsDocShellEnumerator_h___
+#define nsDocShellEnumerator_h___
+
+#include "nsISimpleEnumerator.h"
+#include "nsTArray.h"
+#include "nsIWeakReferenceUtils.h"
+
+class nsIDocShellTreeItem;
+
+/*
+// {13cbc281-35ae-11d5-be5b-bde0edece43c}
+#define NS_DOCSHELL_FORWARDS_ENUMERATOR_CID \
+{ 0x13cbc281, 0x35ae, 0x11d5, { 0xbe, 0x5b, 0xbd, 0xe0, 0xed, 0xec, 0xe4, 0x3c } }
+
+#define NS_DOCSHELL_FORWARDS_ENUMERATOR_CONTRACTID \
+"@mozilla.org/docshell/enumerator-forwards;1"
+
+// {13cbc282-35ae-11d5-be5b-bde0edece43c}
+#define NS_DOCSHELL_BACKWARDS_ENUMERATOR_CID \
+{ 0x13cbc282, 0x35ae, 0x11d5, { 0xbe, 0x5b, 0xbd, 0xe0, 0xed, 0xec, 0xe4, 0x3c } }
+
+#define NS_DOCSHELL_BACKWARDS_ENUMERATOR_CONTRACTID \
+"@mozilla.org/docshell/enumerator-backwards;1"
+*/
+
+class nsDocShellEnumerator : public nsISimpleEnumerator
+{
+protected:
+ enum
+ {
+ enumerateForwards,
+ enumerateBackwards
+ };
+
+ virtual ~nsDocShellEnumerator();
+
+public:
+ explicit nsDocShellEnumerator(int32_t aEnumerationDirection);
+
+ // nsISupports
+ NS_DECL_ISUPPORTS
+
+ // nsISimpleEnumerator
+ NS_DECL_NSISIMPLEENUMERATOR
+
+public:
+ nsresult GetEnumerationRootItem(nsIDocShellTreeItem** aEnumerationRootItem);
+ nsresult SetEnumerationRootItem(nsIDocShellTreeItem* aEnumerationRootItem);
+
+ nsresult GetEnumDocShellType(int32_t* aEnumerationItemType);
+ nsresult SetEnumDocShellType(int32_t aEnumerationItemType);
+
+ nsresult First();
+
+protected:
+ nsresult EnsureDocShellArray();
+ nsresult ClearState();
+
+ nsresult BuildDocShellArray(nsTArray<nsWeakPtr>& aItemArray);
+ virtual nsresult BuildArrayRecursive(nsIDocShellTreeItem* aItem,
+ nsTArray<nsWeakPtr>& aItemArray) = 0;
+
+protected:
+ nsWeakPtr mRootItem; // weak ref!
+
+ nsTArray<nsWeakPtr> mItemArray; // flattened list of items with matching type
+ uint32_t mCurIndex;
+
+ int32_t mDocShellType; // only want shells of this type
+ bool mArrayValid; // is mItemArray up to date?
+
+ const int8_t mEnumerationDirection;
+};
+
+class nsDocShellForwardsEnumerator : public nsDocShellEnumerator
+{
+public:
+ nsDocShellForwardsEnumerator()
+ : nsDocShellEnumerator(enumerateForwards)
+ {
+ }
+
+protected:
+ virtual nsresult BuildArrayRecursive(nsIDocShellTreeItem* aItem,
+ nsTArray<nsWeakPtr>& aItemArray);
+};
+
+class nsDocShellBackwardsEnumerator : public nsDocShellEnumerator
+{
+public:
+ nsDocShellBackwardsEnumerator()
+ : nsDocShellEnumerator(enumerateBackwards)
+ {
+ }
+
+protected:
+ virtual nsresult BuildArrayRecursive(nsIDocShellTreeItem* aItem,
+ nsTArray<nsWeakPtr>& aItemArray);
+};
+
+#endif // nsDocShellEnumerator_h___
diff --git a/docshell/base/nsDocShellLoadInfo.cpp b/docshell/base/nsDocShellLoadInfo.cpp
new file mode 100644
index 000000000..7d0034b04
--- /dev/null
+++ b/docshell/base/nsDocShellLoadInfo.cpp
@@ -0,0 +1,297 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsDocShellLoadInfo.h"
+#include "nsISHEntry.h"
+#include "nsIInputStream.h"
+#include "nsIURI.h"
+#include "nsIDocShell.h"
+#include "mozilla/net/ReferrerPolicy.h"
+
+nsDocShellLoadInfo::nsDocShellLoadInfo()
+ : mLoadReplace(false)
+ , mInheritPrincipal(false)
+ , mPrincipalIsExplicit(false)
+ , mSendReferrer(true)
+ , mReferrerPolicy(mozilla::net::RP_Default)
+ , mLoadType(nsIDocShellLoadInfo::loadNormal)
+ , mIsSrcdocLoad(false)
+{
+}
+
+nsDocShellLoadInfo::~nsDocShellLoadInfo()
+{
+}
+
+NS_IMPL_ADDREF(nsDocShellLoadInfo)
+NS_IMPL_RELEASE(nsDocShellLoadInfo)
+
+NS_INTERFACE_MAP_BEGIN(nsDocShellLoadInfo)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDocShellLoadInfo)
+ NS_INTERFACE_MAP_ENTRY(nsIDocShellLoadInfo)
+NS_INTERFACE_MAP_END
+
+NS_IMETHODIMP
+nsDocShellLoadInfo::GetReferrer(nsIURI** aReferrer)
+{
+ NS_ENSURE_ARG_POINTER(aReferrer);
+
+ *aReferrer = mReferrer;
+ NS_IF_ADDREF(*aReferrer);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShellLoadInfo::SetReferrer(nsIURI* aReferrer)
+{
+ mReferrer = aReferrer;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShellLoadInfo::GetOriginalURI(nsIURI** aOriginalURI)
+{
+ NS_ENSURE_ARG_POINTER(aOriginalURI);
+
+ *aOriginalURI = mOriginalURI;
+ NS_IF_ADDREF(*aOriginalURI);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShellLoadInfo::SetOriginalURI(nsIURI* aOriginalURI)
+{
+ mOriginalURI = aOriginalURI;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShellLoadInfo::GetLoadReplace(bool* aLoadReplace)
+{
+ *aLoadReplace = mLoadReplace;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShellLoadInfo::SetLoadReplace(bool aLoadReplace)
+{
+ mLoadReplace = aLoadReplace;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShellLoadInfo::GetTriggeringPrincipal(nsIPrincipal** aTriggeringPrincipal)
+{
+ NS_ENSURE_ARG_POINTER(aTriggeringPrincipal);
+ NS_IF_ADDREF(*aTriggeringPrincipal = mTriggeringPrincipal);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShellLoadInfo::SetTriggeringPrincipal(nsIPrincipal* aTriggeringPrincipal)
+{
+ mTriggeringPrincipal = aTriggeringPrincipal;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShellLoadInfo::GetInheritPrincipal(bool* aInheritPrincipal)
+{
+ NS_ENSURE_ARG_POINTER(aInheritPrincipal);
+ *aInheritPrincipal = mInheritPrincipal;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShellLoadInfo::SetInheritPrincipal(bool aInheritPrincipal)
+{
+ mInheritPrincipal = aInheritPrincipal;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShellLoadInfo::GetPrincipalIsExplicit(bool* aPrincipalIsExplicit)
+{
+ *aPrincipalIsExplicit = mPrincipalIsExplicit;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShellLoadInfo::SetPrincipalIsExplicit(bool aPrincipalIsExplicit)
+{
+ mPrincipalIsExplicit = aPrincipalIsExplicit;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShellLoadInfo::GetLoadType(nsDocShellInfoLoadType* aLoadType)
+{
+ NS_ENSURE_ARG_POINTER(aLoadType);
+
+ *aLoadType = mLoadType;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShellLoadInfo::SetLoadType(nsDocShellInfoLoadType aLoadType)
+{
+ mLoadType = aLoadType;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShellLoadInfo::GetSHEntry(nsISHEntry** aSHEntry)
+{
+ NS_ENSURE_ARG_POINTER(aSHEntry);
+
+ *aSHEntry = mSHEntry;
+ NS_IF_ADDREF(*aSHEntry);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShellLoadInfo::SetSHEntry(nsISHEntry* aSHEntry)
+{
+ mSHEntry = aSHEntry;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShellLoadInfo::GetTarget(char16_t** aTarget)
+{
+ NS_ENSURE_ARG_POINTER(aTarget);
+
+ *aTarget = ToNewUnicode(mTarget);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShellLoadInfo::SetTarget(const char16_t* aTarget)
+{
+ mTarget.Assign(aTarget);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShellLoadInfo::GetPostDataStream(nsIInputStream** aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ *aResult = mPostDataStream;
+
+ NS_IF_ADDREF(*aResult);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShellLoadInfo::SetPostDataStream(nsIInputStream* aStream)
+{
+ mPostDataStream = aStream;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShellLoadInfo::GetHeadersStream(nsIInputStream** aHeadersStream)
+{
+ NS_ENSURE_ARG_POINTER(aHeadersStream);
+ *aHeadersStream = mHeadersStream;
+ NS_IF_ADDREF(*aHeadersStream);
+ return NS_OK;
+}
+NS_IMETHODIMP
+nsDocShellLoadInfo::SetHeadersStream(nsIInputStream* aHeadersStream)
+{
+ mHeadersStream = aHeadersStream;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShellLoadInfo::GetSendReferrer(bool* aSendReferrer)
+{
+ NS_ENSURE_ARG_POINTER(aSendReferrer);
+
+ *aSendReferrer = mSendReferrer;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShellLoadInfo::SetSendReferrer(bool aSendReferrer)
+{
+ mSendReferrer = aSendReferrer;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShellLoadInfo::GetReferrerPolicy(
+ nsDocShellInfoReferrerPolicy* aReferrerPolicy)
+{
+ *aReferrerPolicy = mReferrerPolicy;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShellLoadInfo::SetReferrerPolicy(
+ nsDocShellInfoReferrerPolicy aReferrerPolicy)
+{
+ mReferrerPolicy = aReferrerPolicy;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShellLoadInfo::GetIsSrcdocLoad(bool* aIsSrcdocLoad)
+{
+ *aIsSrcdocLoad = mIsSrcdocLoad;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShellLoadInfo::GetSrcdocData(nsAString& aSrcdocData)
+{
+ aSrcdocData = mSrcdocData;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShellLoadInfo::SetSrcdocData(const nsAString& aSrcdocData)
+{
+ mSrcdocData = aSrcdocData;
+ mIsSrcdocLoad = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShellLoadInfo::GetSourceDocShell(nsIDocShell** aSourceDocShell)
+{
+ MOZ_ASSERT(aSourceDocShell);
+ nsCOMPtr<nsIDocShell> result = mSourceDocShell;
+ result.forget(aSourceDocShell);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShellLoadInfo::SetSourceDocShell(nsIDocShell* aSourceDocShell)
+{
+ mSourceDocShell = aSourceDocShell;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShellLoadInfo::GetBaseURI(nsIURI** aBaseURI)
+{
+ NS_ENSURE_ARG_POINTER(aBaseURI);
+
+ *aBaseURI = mBaseURI;
+ NS_IF_ADDREF(*aBaseURI);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShellLoadInfo::SetBaseURI(nsIURI* aBaseURI)
+{
+ mBaseURI = aBaseURI;
+ return NS_OK;
+}
diff --git a/docshell/base/nsDocShellLoadInfo.h b/docshell/base/nsDocShellLoadInfo.h
new file mode 100644
index 000000000..b7eaed832
--- /dev/null
+++ b/docshell/base/nsDocShellLoadInfo.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsDocShellLoadInfo_h__
+#define nsDocShellLoadInfo_h__
+
+// Helper Classes
+#include "nsCOMPtr.h"
+#include "nsString.h"
+
+// Interfaces Needed
+#include "nsIDocShellLoadInfo.h"
+
+class nsIInputStream;
+class nsISHEntry;
+class nsIURI;
+class nsIDocShell;
+
+class nsDocShellLoadInfo : public nsIDocShellLoadInfo
+{
+public:
+ nsDocShellLoadInfo();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIDOCSHELLLOADINFO
+
+protected:
+ virtual ~nsDocShellLoadInfo();
+
+protected:
+ nsCOMPtr<nsIURI> mReferrer;
+ nsCOMPtr<nsIURI> mOriginalURI;
+ nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
+ bool mLoadReplace;
+ bool mInheritPrincipal;
+ bool mPrincipalIsExplicit;
+ bool mSendReferrer;
+ nsDocShellInfoReferrerPolicy mReferrerPolicy;
+ nsDocShellInfoLoadType mLoadType;
+ nsCOMPtr<nsISHEntry> mSHEntry;
+ nsString mTarget;
+ nsCOMPtr<nsIInputStream> mPostDataStream;
+ nsCOMPtr<nsIInputStream> mHeadersStream;
+ bool mIsSrcdocLoad;
+ nsString mSrcdocData;
+ nsCOMPtr<nsIDocShell> mSourceDocShell;
+ nsCOMPtr<nsIURI> mBaseURI;
+};
+
+#endif /* nsDocShellLoadInfo_h__ */
diff --git a/docshell/base/nsDocShellLoadTypes.h b/docshell/base/nsDocShellLoadTypes.h
new file mode 100644
index 000000000..67304476d
--- /dev/null
+++ b/docshell/base/nsDocShellLoadTypes.h
@@ -0,0 +1,117 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsDocShellLoadTypes_h_
+#define nsDocShellLoadTypes_h_
+
+#ifdef MOZILLA_INTERNAL_API
+
+#include "nsIDocShell.h"
+#include "nsIWebNavigation.h"
+
+/**
+ * Load flag for error pages. This uses one of the reserved flag
+ * values from nsIWebNavigation.
+ */
+#define LOAD_FLAGS_ERROR_PAGE 0x0001U
+
+#define MAKE_LOAD_TYPE(type, flags) ((type) | ((flags) << 16))
+#define LOAD_TYPE_HAS_FLAGS(type, flags) ((type) & ((flags) << 16))
+
+/**
+ * These are flags that confuse ConvertLoadTypeToDocShellLoadInfo and should
+ * not be passed to MAKE_LOAD_TYPE. In particular this includes all flags
+ * above 0xffff (e.g. LOAD_FLAGS_BYPASS_CLASSIFIER), since MAKE_LOAD_TYPE would
+ * just shift them out anyway.
+ */
+#define EXTRA_LOAD_FLAGS (LOAD_FLAGS_FIRST_LOAD | \
+ LOAD_FLAGS_ALLOW_POPUPS | \
+ 0xffff0000)
+
+/* load types are legal combinations of load commands and flags
+ *
+ * NOTE:
+ * Remember to update the IsValidLoadType function below if you change this
+ * enum to ensure bad flag combinations will be rejected.
+ */
+enum LoadType
+{
+ LOAD_NORMAL = MAKE_LOAD_TYPE(nsIDocShell::LOAD_CMD_NORMAL, nsIWebNavigation::LOAD_FLAGS_NONE),
+ LOAD_NORMAL_REPLACE = MAKE_LOAD_TYPE(nsIDocShell::LOAD_CMD_NORMAL, nsIWebNavigation::LOAD_FLAGS_REPLACE_HISTORY),
+ LOAD_NORMAL_EXTERNAL = MAKE_LOAD_TYPE(nsIDocShell::LOAD_CMD_NORMAL, nsIWebNavigation::LOAD_FLAGS_FROM_EXTERNAL),
+ LOAD_HISTORY = MAKE_LOAD_TYPE(nsIDocShell::LOAD_CMD_HISTORY, nsIWebNavigation::LOAD_FLAGS_NONE),
+ LOAD_NORMAL_BYPASS_CACHE = MAKE_LOAD_TYPE(nsIDocShell::LOAD_CMD_NORMAL, nsIWebNavigation::LOAD_FLAGS_BYPASS_CACHE),
+ LOAD_NORMAL_BYPASS_PROXY = MAKE_LOAD_TYPE(nsIDocShell::LOAD_CMD_NORMAL, nsIWebNavigation::LOAD_FLAGS_BYPASS_PROXY),
+ LOAD_NORMAL_BYPASS_PROXY_AND_CACHE = MAKE_LOAD_TYPE(nsIDocShell::LOAD_CMD_NORMAL, nsIWebNavigation::LOAD_FLAGS_BYPASS_CACHE | nsIWebNavigation::LOAD_FLAGS_BYPASS_PROXY),
+ LOAD_NORMAL_ALLOW_MIXED_CONTENT = MAKE_LOAD_TYPE(nsIDocShell::LOAD_CMD_NORMAL, nsIWebNavigation::LOAD_FLAGS_ALLOW_MIXED_CONTENT | nsIWebNavigation::LOAD_FLAGS_BYPASS_CACHE),
+ LOAD_RELOAD_NORMAL = MAKE_LOAD_TYPE(nsIDocShell::LOAD_CMD_RELOAD, nsIWebNavigation::LOAD_FLAGS_NONE),
+ LOAD_RELOAD_BYPASS_CACHE = MAKE_LOAD_TYPE(nsIDocShell::LOAD_CMD_RELOAD, nsIWebNavigation::LOAD_FLAGS_BYPASS_CACHE),
+ LOAD_RELOAD_BYPASS_PROXY = MAKE_LOAD_TYPE(nsIDocShell::LOAD_CMD_RELOAD, nsIWebNavigation::LOAD_FLAGS_BYPASS_PROXY),
+ LOAD_RELOAD_ALLOW_MIXED_CONTENT = MAKE_LOAD_TYPE(nsIDocShell::LOAD_CMD_RELOAD, nsIWebNavigation::LOAD_FLAGS_ALLOW_MIXED_CONTENT | nsIWebNavigation::LOAD_FLAGS_BYPASS_CACHE),
+ LOAD_RELOAD_BYPASS_PROXY_AND_CACHE = MAKE_LOAD_TYPE(nsIDocShell::LOAD_CMD_RELOAD, nsIWebNavigation::LOAD_FLAGS_BYPASS_CACHE | nsIWebNavigation::LOAD_FLAGS_BYPASS_PROXY),
+ LOAD_LINK = MAKE_LOAD_TYPE(nsIDocShell::LOAD_CMD_NORMAL, nsIWebNavigation::LOAD_FLAGS_IS_LINK),
+ LOAD_REFRESH = MAKE_LOAD_TYPE(nsIDocShell::LOAD_CMD_NORMAL, nsIWebNavigation::LOAD_FLAGS_IS_REFRESH),
+ LOAD_RELOAD_CHARSET_CHANGE = MAKE_LOAD_TYPE(nsIDocShell::LOAD_CMD_RELOAD, nsIWebNavigation::LOAD_FLAGS_CHARSET_CHANGE),
+ LOAD_BYPASS_HISTORY = MAKE_LOAD_TYPE(nsIDocShell::LOAD_CMD_NORMAL, nsIWebNavigation::LOAD_FLAGS_BYPASS_HISTORY),
+ LOAD_STOP_CONTENT = MAKE_LOAD_TYPE(nsIDocShell::LOAD_CMD_NORMAL, nsIWebNavigation::LOAD_FLAGS_STOP_CONTENT),
+ LOAD_STOP_CONTENT_AND_REPLACE = MAKE_LOAD_TYPE(nsIDocShell::LOAD_CMD_NORMAL, nsIWebNavigation::LOAD_FLAGS_STOP_CONTENT | nsIWebNavigation::LOAD_FLAGS_REPLACE_HISTORY),
+ LOAD_PUSHSTATE = MAKE_LOAD_TYPE(nsIDocShell::LOAD_CMD_PUSHSTATE, nsIWebNavigation::LOAD_FLAGS_NONE),
+ LOAD_REPLACE_BYPASS_CACHE = MAKE_LOAD_TYPE(nsIDocShell::LOAD_CMD_NORMAL, nsIWebNavigation::LOAD_FLAGS_REPLACE_HISTORY | nsIWebNavigation::LOAD_FLAGS_BYPASS_CACHE),
+ /**
+ * Load type for an error page. These loads are never triggered by users of
+ * Docshell. Instead, Docshell triggers the load itself when a
+ * consumer-triggered load failed.
+ */
+ LOAD_ERROR_PAGE = MAKE_LOAD_TYPE(nsIDocShell::LOAD_CMD_NORMAL,
+ LOAD_FLAGS_ERROR_PAGE)
+
+ // NOTE: Adding a new value? Remember to update IsValidLoadType!
+};
+static inline bool
+IsValidLoadType(uint32_t aLoadType)
+{
+ switch (aLoadType) {
+ case LOAD_NORMAL:
+ case LOAD_NORMAL_REPLACE:
+ case LOAD_NORMAL_EXTERNAL:
+ case LOAD_NORMAL_BYPASS_CACHE:
+ case LOAD_NORMAL_BYPASS_PROXY:
+ case LOAD_NORMAL_BYPASS_PROXY_AND_CACHE:
+ case LOAD_NORMAL_ALLOW_MIXED_CONTENT:
+ case LOAD_HISTORY:
+ case LOAD_RELOAD_NORMAL:
+ case LOAD_RELOAD_BYPASS_CACHE:
+ case LOAD_RELOAD_BYPASS_PROXY:
+ case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
+ case LOAD_RELOAD_ALLOW_MIXED_CONTENT:
+ case LOAD_LINK:
+ case LOAD_REFRESH:
+ case LOAD_RELOAD_CHARSET_CHANGE:
+ case LOAD_BYPASS_HISTORY:
+ case LOAD_STOP_CONTENT:
+ case LOAD_STOP_CONTENT_AND_REPLACE:
+ case LOAD_PUSHSTATE:
+ case LOAD_REPLACE_BYPASS_CACHE:
+ case LOAD_ERROR_PAGE:
+ return true;
+ }
+ return false;
+}
+
+static inline bool
+IsForceReloadType(uint32_t aLoadType) {
+ switch (aLoadType) {
+ case LOAD_RELOAD_BYPASS_CACHE:
+ case LOAD_RELOAD_BYPASS_PROXY:
+ case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
+ case LOAD_RELOAD_ALLOW_MIXED_CONTENT:
+ return true;
+ }
+ return false;
+}
+
+#endif // MOZILLA_INTERNAL_API
+#endif
diff --git a/docshell/base/nsDocShellTransferableHooks.cpp b/docshell/base/nsDocShellTransferableHooks.cpp
new file mode 100644
index 000000000..441ade128
--- /dev/null
+++ b/docshell/base/nsDocShellTransferableHooks.cpp
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsDocShellTransferableHooks.h"
+#include "nsIClipboardDragDropHooks.h"
+#include "nsIClipboardDragDropHookList.h"
+#include "nsArrayEnumerator.h"
+
+nsTransferableHookData::nsTransferableHookData()
+{
+}
+
+nsTransferableHookData::~nsTransferableHookData()
+{
+}
+
+NS_IMPL_ISUPPORTS(nsTransferableHookData, nsIClipboardDragDropHookList)
+
+NS_IMETHODIMP
+nsTransferableHookData::AddClipboardDragDropHooks(
+ nsIClipboardDragDropHooks* aOverrides)
+{
+ NS_ENSURE_ARG(aOverrides);
+
+ // don't let a hook be added more than once
+ if (mHookList.IndexOfObject(aOverrides) == -1) {
+ if (!mHookList.AppendObject(aOverrides)) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsTransferableHookData::RemoveClipboardDragDropHooks(
+ nsIClipboardDragDropHooks* aOverrides)
+{
+ NS_ENSURE_ARG(aOverrides);
+ if (!mHookList.RemoveObject(aOverrides)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsTransferableHookData::GetHookEnumerator(nsISimpleEnumerator** aResult)
+{
+ return NS_NewArrayEnumerator(aResult, mHookList);
+}
diff --git a/docshell/base/nsDocShellTransferableHooks.h b/docshell/base/nsDocShellTransferableHooks.h
new file mode 100644
index 000000000..58d764529
--- /dev/null
+++ b/docshell/base/nsDocShellTransferableHooks.h
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsDocShellTransferableHooks_h__
+#define nsDocShellTransferableHooks_h__
+
+#include "nsIClipboardDragDropHookList.h"
+#include "nsCOMArray.h"
+
+class nsIClipboardDragDropHooks;
+
+class nsTransferableHookData : public nsIClipboardDragDropHookList
+{
+public:
+ nsTransferableHookData();
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSICLIPBOARDDRAGDROPHOOKLIST
+
+protected:
+ virtual ~nsTransferableHookData();
+
+ nsCOMArray<nsIClipboardDragDropHooks> mHookList;
+};
+
+#endif // nsDocShellTransferableHooks_h__
diff --git a/docshell/base/nsDownloadHistory.cpp b/docshell/base/nsDownloadHistory.cpp
new file mode 100644
index 000000000..32513ef00
--- /dev/null
+++ b/docshell/base/nsDownloadHistory.cpp
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsDownloadHistory.h"
+#include "nsCOMPtr.h"
+#include "nsServiceManagerUtils.h"
+#include "nsIGlobalHistory2.h"
+#include "nsIObserverService.h"
+#include "nsIURI.h"
+#include "mozilla/Services.h"
+
+NS_IMPL_ISUPPORTS(nsDownloadHistory, nsIDownloadHistory)
+
+NS_IMETHODIMP
+nsDownloadHistory::AddDownload(nsIURI* aSource,
+ nsIURI* aReferrer,
+ PRTime aStartTime,
+ nsIURI* aDestination)
+{
+ NS_ENSURE_ARG_POINTER(aSource);
+
+ nsCOMPtr<nsIGlobalHistory2> history =
+ do_GetService("@mozilla.org/browser/global-history;2");
+ if (!history) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ bool visited;
+ nsresult rv = history->IsVisited(aSource, &visited);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = history->AddURI(aSource, false, true, aReferrer);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!visited) {
+ nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+ if (os) {
+ os->NotifyObservers(aSource, NS_LINK_VISITED_EVENT_TOPIC, nullptr);
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDownloadHistory::RemoveAllDownloads()
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
diff --git a/docshell/base/nsDownloadHistory.h b/docshell/base/nsDownloadHistory.h
new file mode 100644
index 000000000..4a4c20ddf
--- /dev/null
+++ b/docshell/base/nsDownloadHistory.h
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __nsDownloadHistory_h__
+#define __nsDownloadHistory_h__
+
+#include "nsIDownloadHistory.h"
+#include "mozilla/Attributes.h"
+
+#define NS_DOWNLOADHISTORY_CID \
+ {0x2ee83680, 0x2af0, 0x4bcb, {0xbf, 0xa0, 0xc9, 0x70, 0x5f, 0x65, 0x54, 0xf1}}
+
+class nsDownloadHistory final : public nsIDownloadHistory
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIDOWNLOADHISTORY
+
+ NS_DEFINE_STATIC_CID_ACCESSOR(NS_DOWNLOADHISTORY_CID)
+
+private:
+ ~nsDownloadHistory() {}
+};
+
+#endif // __nsDownloadHistory_h__
diff --git a/docshell/base/nsIClipboardCommands.idl b/docshell/base/nsIClipboardCommands.idl
new file mode 100644
index 000000000..9dcb1401f
--- /dev/null
+++ b/docshell/base/nsIClipboardCommands.idl
@@ -0,0 +1,111 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+/**
+ * An interface for embedding clients who wish to interact with
+ * the system-wide OS clipboard. Mozilla does not use a private
+ * clipboard, instead it places its data directly onto the system
+ * clipboard. The webshell implements this interface.
+ */
+
+[scriptable, uuid(b8100c90-73be-11d2-92a5-00105a1b0d64)]
+interface nsIClipboardCommands : nsISupports {
+
+ /**
+ * Returns whether there is a selection and it is not read-only.
+ *
+ * @return <code>true</code> if the current selection can be cut,
+ * <code>false</code> otherwise.
+ */
+ boolean canCutSelection();
+
+ /**
+ * Returns whether there is a selection and it is copyable.
+ *
+ * @return <code>true</code> if there is a selection,
+ * <code>false</code> otherwise.
+ */
+ boolean canCopySelection();
+
+ /**
+ * Returns whether we can copy a link location.
+ *
+ * @return <code>true</code> if a link is selected,
+ * <code>false</code> otherwise.
+ */
+ boolean canCopyLinkLocation();
+
+ /**
+ * Returns whether we can copy an image location.
+ *
+ * @return <code>true</code> if an image is selected,
+ <code>false</code> otherwise.
+ */
+ boolean canCopyImageLocation();
+
+ /**
+ * Returns whether we can copy an image's contents.
+ *
+ * @return <code>true</code> if an image is selected,
+ * <code>false</code> otherwise
+ */
+ boolean canCopyImageContents();
+
+ /**
+ * Returns whether the current contents of the clipboard can be
+ * pasted and if the current selection is not read-only.
+ *
+ * @return <code>true</code> there is data to paste on the clipboard
+ * and the current selection is not read-only,
+ * <code>false</code> otherwise
+ */
+ boolean canPaste();
+
+ /**
+ * Cut the current selection onto the clipboard.
+ */
+ void cutSelection();
+
+ /**
+ * Copy the current selection onto the clipboard.
+ */
+ void copySelection();
+
+ /**
+ * Copy the link location of the current selection (e.g.,
+ * the |href| attribute of a selected |a| tag).
+ */
+ void copyLinkLocation();
+
+ /**
+ * Copy the location of the selected image.
+ */
+ void copyImageLocation();
+
+ /**
+ * Copy the contents of the selected image.
+ */
+ void copyImageContents();
+
+ /**
+ * Paste the contents of the clipboard into the current selection.
+ */
+ void paste();
+
+ /**
+ * Select the entire contents.
+ */
+ void selectAll();
+
+ /**
+ * Clear the current selection (if any). Insertion point ends up
+ * at beginning of current selection.
+ */
+ void selectNone();
+
+};
diff --git a/docshell/base/nsIContentViewer.idl b/docshell/base/nsIContentViewer.idl
new file mode 100644
index 000000000..d7d58ad69
--- /dev/null
+++ b/docshell/base/nsIContentViewer.idl
@@ -0,0 +1,280 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIDocShell;
+interface nsIDocument;
+interface nsIDOMDocument;
+interface nsIDOMNode;
+interface nsISHEntry;
+interface nsIPrintSettings;
+
+
+%{ C++
+#include "nsTArray.h"
+#include "nsRect.h"
+
+class nsIWidget;
+class nsIPresShell;
+class nsPresContext;
+class nsView;
+class nsDOMNavigationTiming;
+%}
+
+[ptr] native nsIWidgetPtr(nsIWidget);
+[ref] native nsIntRectRef(nsIntRect);
+[ptr] native nsIPresShellPtr(nsIPresShell);
+[ptr] native nsPresContextPtr(nsPresContext);
+[ptr] native nsViewPtr(nsView);
+[ptr] native nsDOMNavigationTimingPtr(nsDOMNavigationTiming);
+[ref] native nsIContentViewerTArray(nsTArray<nsCOMPtr<nsIContentViewer> >);
+
+[scriptable, builtinclass, uuid(2da17016-7851-4a45-a7a8-00b360e01595)]
+interface nsIContentViewer : nsISupports
+{
+ [noscript] void init(in nsIWidgetPtr aParentWidget,
+ [const] in nsIntRectRef aBounds);
+
+ attribute nsIDocShell container;
+
+ [noscript,notxpcom,nostdcall] void loadStart(in nsIDocument aDoc);
+ void loadComplete(in nsresult aStatus);
+ [noscript] readonly attribute boolean loadCompleted;
+
+ /**
+ * Checks if the document wants to prevent unloading by firing beforeunload on
+ * the document, and if it does, prompts the user. The result is returned.
+ */
+ boolean permitUnload();
+
+ /**
+ * Exposes whether we're blocked in a call to permitUnload.
+ */
+ readonly attribute boolean inPermitUnload;
+
+ /**
+ * As above, but this passes around the aShouldPrompt argument to keep
+ * track of whether the user has responded to a prompt.
+ * Used internally by the scriptable version to ensure we only prompt once.
+ */
+ [noscript,nostdcall] boolean permitUnloadInternal(inout boolean aShouldPrompt);
+
+ /**
+ * Exposes whether we're in the process of firing the beforeunload event.
+ * In this case, the corresponding docshell will not allow navigation.
+ */
+ readonly attribute boolean beforeUnloadFiring;
+
+ void pageHide(in boolean isUnload);
+
+ /**
+ * All users of a content viewer are responsible for calling both
+ * close() and destroy(), in that order.
+ *
+ * close() should be called when the load of a new page for the next
+ * content viewer begins, and destroy() should be called when the next
+ * content viewer replaces this one.
+ *
+ * |historyEntry| sets the session history entry for the content viewer. If
+ * this is null, then Destroy() will be called on the document by close().
+ * If it is non-null, the document will not be destroyed, and the following
+ * actions will happen when destroy() is called (*):
+ * - Sanitize() will be called on the viewer's document
+ * - The content viewer will set the contentViewer property on the
+ * history entry, and release its reference (ownership reversal).
+ * - hide() will be called, and no further destruction will happen.
+ *
+ * (*) unless the document is currently being printed, in which case
+ * it will never be saved in session history.
+ *
+ */
+ void close(in nsISHEntry historyEntry);
+ void destroy();
+
+ void stop();
+
+ attribute nsIDOMDocument DOMDocument;
+
+ /**
+ * Returns DOMDocument as nsIDocument and without addrefing.
+ */
+ [noscript,notxpcom] nsIDocument getDocument();
+
+ [noscript] void getBounds(in nsIntRectRef aBounds);
+ [noscript] void setBounds([const] in nsIntRectRef aBounds);
+ /**
+ * The 'aFlags' argument to setBoundsWithFlags is a set of these bits.
+ */
+ const unsigned long eDelayResize = 1;
+ [noscript] void setBoundsWithFlags([const] in nsIntRectRef aBounds,
+ in unsigned long aFlags);
+
+ /**
+ * The previous content viewer, which has been |close|d but not
+ * |destroy|ed.
+ */
+ [noscript] attribute nsIContentViewer previousViewer;
+
+ void move(in long aX, in long aY);
+
+ void show();
+ void hide();
+
+ attribute boolean sticky;
+
+ /*
+ * This is called when the DOM window wants to be closed. Returns true
+ * if the window can close immediately. Otherwise, returns false and will
+ * close the DOM window as soon as practical.
+ */
+
+ boolean requestWindowClose();
+
+ /**
+ * Attach the content viewer to its DOM window and docshell.
+ * @param aState A state object that might be useful in attaching the DOM
+ * window.
+ * @param aSHEntry The history entry that the content viewer was stored in.
+ * The entry must have the docshells for all of the child
+ * documents stored in its child shell list.
+ */
+ void open(in nsISupports aState, in nsISHEntry aSHEntry);
+
+ /**
+ * Clears the current history entry. This is used if we need to clear out
+ * the saved presentation state.
+ */
+ void clearHistoryEntry();
+
+ /**
+ * Change the layout to view the document with page layout (like print preview), but
+ * dynamic and editable (like Galley layout).
+ */
+ void setPageMode(in boolean aPageMode, in nsIPrintSettings aPrintSettings);
+
+ /**
+ * Get the history entry that this viewer will save itself into when
+ * destroyed. Can return null
+ */
+ readonly attribute nsISHEntry historyEntry;
+
+ /**
+ * Indicates when we're in a state where content shouldn't be allowed to
+ * trigger a tab-modal prompt (as opposed to a window-modal prompt) because
+ * we're part way through some operation (eg beforeunload) that shouldn't be
+ * rentrant if the user closes the tab while the prompt is showing.
+ * See bug 613800.
+ */
+ readonly attribute boolean isTabModalPromptAllowed;
+
+ /**
+ * Returns whether this content viewer is in a hidden state.
+ *
+ * @note Only Gecko internal code should set the attribute!
+ */
+ attribute boolean isHidden;
+
+ [noscript] readonly attribute nsIPresShellPtr presShell;
+ [noscript] readonly attribute nsPresContextPtr presContext;
+ // aDocument must not be null.
+ [noscript] void setDocumentInternal(in nsIDocument aDocument,
+ in boolean aForceReuseInnerWindow);
+ /**
+ * Find the view to use as the container view for MakeWindow. Returns
+ * null if this will be the root of a view manager hierarchy. In that
+ * case, if mParentWidget is null then this document should not even
+ * be displayed.
+ */
+ [noscript,notxpcom,nostdcall] nsViewPtr findContainerView();
+ /**
+ * Set collector for navigation timing data (load, unload events).
+ */
+ [noscript,notxpcom,nostdcall] void setNavigationTiming(in nsDOMNavigationTimingPtr aTiming);
+ /*
+ Scrolls to a given DOM content node.
+ */
+ void scrollToNode(in nsIDOMNode node);
+
+ /** The amount by which to scale all text. Default is 1.0. */
+ attribute float textZoom;
+
+ /** The amount by which to scale all lengths. Default is 1.0. */
+ attribute float fullZoom;
+
+ /**
+ * The value used to override devicePixelRatio and media queries dppx.
+ * Default is 0.0, that means no overriding is done (only a positive value
+ * is applied).
+ */
+ attribute float overrideDPPX;
+
+ /** Disable entire author style level (including HTML presentation hints) */
+ attribute boolean authorStyleDisabled;
+
+ /**
+ * XXX comm-central only: bug 829543. Not the Character Encoding menu in
+ * browser!
+ */
+ attribute ACString forceCharacterSet;
+
+ /**
+ * XXX comm-central only: bug 829543.
+ */
+ attribute ACString hintCharacterSet;
+
+ /**
+ * XXX comm-central only: bug 829543.
+ */
+ attribute int32_t hintCharacterSetSource;
+
+ /**
+ * Requests the size of the content to the container.
+ */
+ void getContentSize(out long width, out long height);
+
+ /**
+ * Returns the preferred width and height of the content, constrained to the
+ * given maximum values. If either maxWidth or maxHeight is less than zero,
+ * that dimension is not constrained.
+ *
+ * All input and output values are in device pixels, rather than CSS pixels.
+ */
+ void getContentSizeConstrained(in long maxWidth, in long maxHeight,
+ out long width, out long height);
+
+ /** The minimum font size */
+ attribute long minFontSize;
+
+ /**
+ * Append |this| and all of its descendants to the given array,
+ * in depth-first pre-order traversal.
+ */
+ [noscript] void appendSubtree(in nsIContentViewerTArray array);
+
+ /**
+ * Instruct the refresh driver to discontinue painting until further
+ * notice.
+ */
+ void pausePainting();
+
+ /**
+ * Instruct the refresh driver to resume painting after a previous call to
+ * pausePainting().
+ */
+ void resumePainting();
+
+ /*
+ * Render the document as if being viewed on a device with the specified
+ * media type. This will cause a reflow.
+ *
+ * @param mediaType The media type to be emulated
+ */
+ void emulateMedium(in AString aMediaType);
+
+ /*
+ * Restore the viewer's natural media type
+ */
+ void stopEmulatingMedium();
+};
diff --git a/docshell/base/nsIContentViewerContainer.idl b/docshell/base/nsIContentViewerContainer.idl
new file mode 100644
index 000000000..c94b37ea7
--- /dev/null
+++ b/docshell/base/nsIContentViewerContainer.idl
@@ -0,0 +1,20 @@
+/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIContentViewer;
+
+[scriptable, uuid(ea2ce7a0-5c3d-11d4-90c2-0050041caf44)]
+interface nsIContentViewerContainer : nsISupports {
+ void embed(in nsIContentViewer aDocViewer, in string aCommand, in nsISupports aExtraInfo);
+
+ /**
+ * Allows the PrintEngine to make this call on
+ * an internal interface to the DocShell
+ */
+ void setIsPrinting(in boolean aIsPrinting);
+};
diff --git a/docshell/base/nsIContentViewerEdit.idl b/docshell/base/nsIContentViewerEdit.idl
new file mode 100644
index 000000000..c8b6b8a02
--- /dev/null
+++ b/docshell/base/nsIContentViewerEdit.idl
@@ -0,0 +1,36 @@
+/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIDOMNode;
+
+[scriptable, uuid(35BE2D7E-F29B-48EC-BF7E-80A30A724DE3)]
+interface nsIContentViewerEdit : nsISupports
+{
+ void clearSelection();
+ void selectAll();
+
+ void copySelection();
+ readonly attribute boolean copyable;
+
+ void copyLinkLocation();
+ readonly attribute boolean inLink;
+
+ const long COPY_IMAGE_TEXT = 0x0001;
+ const long COPY_IMAGE_HTML = 0x0002;
+ const long COPY_IMAGE_DATA = 0x0004;
+ const long COPY_IMAGE_ALL = -1;
+ void copyImage(in long aCopyFlags);
+ readonly attribute boolean inImage;
+
+ AString getContents(in string aMimeType, in boolean aSelectionOnly);
+ readonly attribute boolean canGetContents;
+
+ // Set the node that will be the subject of the editing commands above.
+ // Usually this will be the node that was context-clicked.
+ void setCommandNode(in nsIDOMNode aNode);
+};
diff --git a/docshell/base/nsIContentViewerFile.idl b/docshell/base/nsIContentViewerFile.idl
new file mode 100644
index 000000000..dd2adbf30
--- /dev/null
+++ b/docshell/base/nsIContentViewerFile.idl
@@ -0,0 +1,31 @@
+/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIDOMWindow;
+interface nsIPrintSettings;
+interface nsIWebProgressListener;
+
+%{ C++
+#include <stdio.h>
+%}
+
+[ptr] native FILE(FILE);
+
+/**
+ * The nsIDocShellFile
+ */
+
+[scriptable, uuid(564a3276-6228-401e-9b5c-d82cb382a60f)]
+interface nsIContentViewerFile : nsISupports
+{
+ readonly attribute boolean printable;
+
+ [noscript] void print(in boolean aSilent,
+ in FILE aDebugFile,
+ in nsIPrintSettings aPrintSettings);
+};
diff --git a/docshell/base/nsIDocCharset.idl b/docshell/base/nsIDocCharset.idl
new file mode 100644
index 000000000..5b1b5a8dc
--- /dev/null
+++ b/docshell/base/nsIDocCharset.idl
@@ -0,0 +1,19 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+/**
+ * The functionality of the nsIDocCharset interface has been incorporated into
+ * nsIDocShell.
+ *
+ * This is an empty interface for backwards compatibility that will go away at
+ * some point in the future
+ *
+ */
+
+[scriptable, uuid(c3faaf6e-40f0-11e1-95fc-6c626d69675c)]
+interface nsIDocCharset : nsISupports
+{};
diff --git a/docshell/base/nsIDocShell.idl b/docshell/base/nsIDocShell.idl
new file mode 100644
index 000000000..8261c45dc
--- /dev/null
+++ b/docshell/base/nsIDocShell.idl
@@ -0,0 +1,1186 @@
+/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "domstubs.idl"
+#include "nsIDocShellTreeItem.idl"
+#include "nsIRequest.idl"
+
+%{ C++
+#include "js/TypeDecls.h"
+class nsPresContext;
+class nsIPresShell;
+%}
+
+/**
+ * The nsIDocShell interface.
+ */
+
+[ptr] native nsPresContext(nsPresContext);
+[ptr] native nsIPresShell(nsIPresShell);
+
+interface nsIURI;
+interface nsIChannel;
+interface nsIContentViewer;
+interface nsIDOMEventTarget;
+interface nsIDocShellLoadInfo;
+interface nsIEditor;
+interface nsIEditingSession;
+interface nsISimpleEnumerator;
+interface nsIInputStream;
+interface nsIRequest;
+interface nsISHEntry;
+interface nsILayoutHistoryState;
+interface nsISecureBrowserUI;
+interface nsIScriptGlobalObject;
+interface nsIDOMStorage;
+interface nsIPrincipal;
+interface nsIWebBrowserPrint;
+interface nsIPrivacyTransitionObserver;
+interface nsIReflowObserver;
+interface nsIScrollObserver;
+interface nsITabParent;
+interface nsITabChild;
+interface nsICommandManager;
+interface nsICommandParams;
+native TabChildRef(already_AddRefed<nsITabChild>);
+
+[scriptable, builtinclass, uuid(049234fe-da10-478b-bc5d-bc6f9a1ba63d)]
+interface nsIDocShell : nsIDocShellTreeItem
+{
+ /**
+ * Loads a given URI. This will give priority to loading the requested URI
+ * in the object implementing this interface. If it can't be loaded here
+ * however, the URL dispatcher will go through its normal process of content
+ * loading.
+ *
+ * @param uri - The URI to load.
+ * @param loadInfo - This is the extended load info for this load. This
+ * most often will be null, but if you need to do
+ * additional setup for this load you can get a loadInfo
+ * object by calling createLoadInfo. Once you have this
+ * object you can set the needed properties on it and
+ * then pass it to loadURI.
+ * @param aLoadFlags - Flags to modify load behaviour. Flags are defined in
+ * nsIWebNavigation. Note that using flags outside
+ * LOAD_FLAGS_MASK is only allowed if passing in a
+ * non-null loadInfo. And even some of those might not
+ * be allowed. Use at your own risk.
+ */
+ [noscript]void loadURI(in nsIURI uri,
+ in nsIDocShellLoadInfo loadInfo,
+ in unsigned long aLoadFlags,
+ in boolean firstParty);
+
+ /**
+ * Loads a given stream. This will give priority to loading the requested
+ * stream in the object implementing this interface. If it can't be loaded
+ * here however, the URL dispatched will go through its normal process of
+ * content loading.
+ *
+ * @param aStream - The input stream that provides access to the data
+ * to be loaded. This must be a blocking, threadsafe
+ * stream implementation.
+ * @param aURI - The URI representing the stream, or null.
+ * @param aContentType - The type (MIME) of data being loaded (empty if unknown).
+ * @param aContentCharset - The charset of the data being loaded (empty if unknown).
+ * @param aLoadInfo - This is the extended load info for this load. This
+ * most often will be null, but if you need to do
+ * additional setup for this load you can get a
+ * loadInfo object by calling createLoadInfo. Once
+ * you have this object you can set the needed
+ * properties on it and then pass it to loadStream.
+ */
+ [noscript]void loadStream(in nsIInputStream aStream,
+ in nsIURI aURI,
+ in ACString aContentType,
+ in ACString aContentCharset,
+ in nsIDocShellLoadInfo aLoadInfo);
+
+ const long INTERNAL_LOAD_FLAGS_NONE = 0x0;
+ const long INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL = 0x1;
+ const long INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER = 0x2;
+ const long INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP = 0x4;
+
+ // This flag marks the first load in this object
+ // @see nsIWebNavigation::LOAD_FLAGS_FIRST_LOAD
+ const long INTERNAL_LOAD_FLAGS_FIRST_LOAD = 0x8;
+
+ const long INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER = 0x10;
+ const long INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES = 0x20;
+
+ // Whether the load should be treated as srcdoc load, rather than a URI one.
+ const long INTERNAL_LOAD_FLAGS_IS_SRCDOC = 0x40;
+
+ const long INTERNAL_LOAD_FLAGS_NO_OPENER = 0x100;
+
+ // NB: 0x80 is available.
+
+ /**
+ * Loads the given URI. This method is identical to loadURI(...) except
+ * that its parameter list is broken out instead of being packaged inside
+ * of an nsIDocShellLoadInfo object...
+ *
+ * @param aURI - The URI to load.
+ * @param aOriginalURI - The URI to set as the originalURI on the channel
+ * that does the load. If null, aURI will be set as
+ * the originalURI.
+ * @param aLoadReplace - If set LOAD_REPLACE flag will be set on the
+ * channel. aOriginalURI is null, this argument is
+ * ignored.
+ * @param aReferrer - Referring URI
+ * @param aReferrerPolicy - Referrer policy
+ * @param aTriggeringPrincipal - A non-null principal that initiated that load.
+ * Please note that this is the principal that is
+ * used for security checks. If the argument aURI
+ * is provided by the web, then please do not pass
+ * a SystemPrincipal as the triggeringPrincipal.
+ * @param aPrincipalToInherit - Principal to be inherited for that load. If this
+ * argument is null then principalToInherit is
+ * computed as follows:
+ * a) If INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL, and
+ * aLoadType is not LOAD_NORMAL_EXTERNAL, and the
+ * URI would normally inherit a principal, then
+ * principalToInherit is set to the current
+ * document's principal, or parent document if
+ * there is not a current document.
+ * b) If principalToInherit is still null (e.g. if
+ * some of the conditions of (a) were not satisfied),
+ * then no inheritance of any sort will happen: the
+ * load will just get a principal based on the URI
+ * being loaded.
+ * @param aFlags - Any of the load flags defined within above.
+ * @param aStopActiveDoc - Flag indicating whether loading the current
+ * document should be stopped.
+ * @param aWindowTarget - Window target for the load.
+ * @param aTypeHint - A hint as to the content-type of the resulting
+ * data. May be null or empty if no hint.
+ * @param aFileName - Non-null when the link should be downloaded as
+ the given filename.
+ * @param aPostDataStream - Post data stream (if POSTing)
+ * @param aHeadersStream - Stream containing "extra" request headers...
+ * @param aLoadFlags - Flags to modify load behaviour. Flags are defined
+ * in nsIWebNavigation.
+ * @param aSHEntry - Active Session History entry (if loading from SH)
+ * @param aSrcdoc When INTERNAL_LOAD_FLAGS_IS_SRCDOC is set, the
+ * contents of this parameter will be loaded instead
+ * of aURI.
+ * @param aSourceDocShell - The source browsing context for the navigation.
+ * @param aBaseURI - The base URI to be used for the load. Set in
+ * srcdoc loads as it cannot otherwise be inferred
+ * in certain situations such as view-source.
+ */
+ [noscript]void internalLoad(in nsIURI aURI,
+ in nsIURI aOriginalURI,
+ in boolean aLoadReplace,
+ in nsIURI aReferrer,
+ in unsigned long aReferrerPolicy,
+ in nsIPrincipal aTriggeringPrincipal,
+ in nsIPrincipal aPrincipalToInherit,
+ in uint32_t aFlags,
+ in AString aWindowTarget,
+ in string aTypeHint,
+ in AString aFileName,
+ in nsIInputStream aPostDataStream,
+ in nsIInputStream aHeadersStream,
+ in unsigned long aLoadFlags,
+ in nsISHEntry aSHEntry,
+ in boolean firstParty,
+ in AString aSrcdoc,
+ in nsIDocShell aSourceDocShell,
+ in nsIURI aBaseURI,
+ out nsIDocShell aDocShell,
+ out nsIRequest aRequest);
+
+ /**
+ * Do either a history.pushState() or history.replaceState() operation,
+ * depending on the value of aReplace.
+ */
+ [implicit_jscontext]
+ void addState(in jsval aData, in DOMString aTitle,
+ in DOMString aURL, in boolean aReplace);
+
+ /**
+ * Creates a DocShellLoadInfo object that you can manipulate and then pass
+ * to loadURI.
+ */
+ void createLoadInfo(out nsIDocShellLoadInfo loadInfo);
+
+ /**
+ * Reset state to a new content model within the current document and the document
+ * viewer. Called by the document before initiating an out of band document.write().
+ */
+ void prepareForNewContentModel();
+
+ /**
+ * For editors and suchlike who wish to change the URI associated with the
+ * document. Note if you want to get the current URI, use the read-only
+ * property on nsIWebNavigation.
+ */
+ void setCurrentURI(in nsIURI aURI);
+
+ /**
+ * Notify the associated content viewer and all child docshells that they are
+ * about to be hidden. If |isUnload| is true, then the document is being
+ * unloaded as well.
+ *
+ * @param isUnload if true, fire the unload event in addition to the pagehide
+ * event.
+ */
+ [noscript] void firePageHideNotification(in boolean isUnload);
+
+ /**
+ * Presentation context for the currently loaded document. This may be null.
+ */
+ [noscript] readonly attribute nsPresContext presContext;
+
+ /**
+ * Presentation shell for the currently loaded document. This may be null.
+ */
+ [noscript,notxpcom] nsIPresShell GetPresShell();
+
+ /**
+ * Presentation shell for the oldest document, if this docshell is
+ * currently transitioning between documents.
+ */
+ [noscript] readonly attribute nsIPresShell eldestPresShell;
+
+ /**
+ * Content Viewer that is currently loaded for this DocShell. This may
+ * change as the underlying content changes.
+ */
+ readonly attribute nsIContentViewer contentViewer;
+
+ /**
+ * This attribute allows chrome to tie in to handle DOM events that may
+ * be of interest to chrome.
+ */
+ attribute nsIDOMEventTarget chromeEventHandler;
+
+ /**
+ * This allows chrome to set a custom User agent on a specific docshell
+ */
+ attribute DOMString customUserAgent;
+
+ /**
+ * Whether to allow plugin execution
+ */
+ attribute boolean allowPlugins;
+
+ /**
+ * Whether to allow Javascript execution
+ */
+ attribute boolean allowJavascript;
+
+ /**
+ * Attribute stating if refresh based redirects can be allowed
+ */
+ attribute boolean allowMetaRedirects;
+
+ /**
+ * Attribute stating if it should allow subframes (framesets/iframes) or not
+ */
+ attribute boolean allowSubframes;
+
+ /**
+ * Attribute stating whether or not images should be loaded.
+ */
+ attribute boolean allowImages;
+
+ /**
+ * Attribute stating whether or not media (audio/video) should be loaded.
+ */
+ [infallible] attribute boolean allowMedia;
+
+ /**
+ * Attribute that determines whether DNS prefetch is allowed for this subtree
+ * of the docshell tree. Defaults to true. Setting this will make it take
+ * effect starting with the next document loaded in the docshell.
+ */
+ attribute boolean allowDNSPrefetch;
+
+ /**
+ * Attribute that determines whether window control (move/resize) is allowed.
+ */
+ attribute boolean allowWindowControl;
+
+ /**
+ * True if the docshell allows its content to be handled by a content listener
+ * other than the docshell itself, including the external helper app service,
+ * and false otherwise. Defaults to true.
+ */
+ [infallible] attribute boolean allowContentRetargeting;
+
+ /**
+ * True if new child docshells should allow content retargeting.
+ * Setting allowContentRetargeting also overwrites this value.
+ */
+ [infallible] attribute boolean allowContentRetargetingOnChildren;
+
+ /**
+ * True if this docShell should inherit the private browsing ID from
+ * its parent when reparented.
+ *
+ * NOTE: This should *not* be set false in new code, or for docShells
+ * inserted anywhere other than as children of panels.
+ */
+ [infallible] attribute boolean inheritPrivateBrowsingId;
+
+ /**
+ * Get an enumerator over this docShell and its children.
+ *
+ * @param aItemType - Only include docShells of this type, or if typeAll,
+ * include all child shells.
+ * Uses types from nsIDocShellTreeItem.
+ * @param aDirection - Whether to enumerate forwards or backwards.
+ */
+
+ const long ENUMERATE_FORWARDS = 0;
+ const long ENUMERATE_BACKWARDS = 1;
+
+ nsISimpleEnumerator getDocShellEnumerator(in long aItemType,
+ in long aDirection);
+
+ /**
+ * The type of application that created this window
+ */
+ const unsigned long APP_TYPE_UNKNOWN = 0;
+ const unsigned long APP_TYPE_MAIL = 1;
+ const unsigned long APP_TYPE_EDITOR = 2;
+
+ attribute unsigned long appType;
+
+ /**
+ * certain dochshells (like the message pane)
+ * should not throw up auth dialogs
+ * because it can act as a password trojan
+ */
+ attribute boolean allowAuth;
+
+ /**
+ * Set/Get the document scale factor. When setting this attribute, a
+ * NS_ERROR_NOT_IMPLEMENTED error may be returned by implementations
+ * not supporting zoom. Implementations not supporting zoom should return
+ * 1.0 all the time for the Get operation. 1.0 by the way is the default
+ * of zoom. This means 100% of normal scaling or in other words normal size
+ * no zoom.
+ */
+ attribute float zoom;
+
+ /*
+ * The size, in CSS pixels, of the horizontal margins for the <body> of an
+ * HTML document in this docshel; used to implement the marginwidth attribute
+ * on HTML <frame>/<iframe> elements. A value smaller than zero indicates
+ * that the attribute was not set.
+ */
+ attribute long marginWidth;
+
+ /*
+ * The size, in CSS pixels, of the vertical margins for the <body> of an HTML
+ * document in this docshel; used to implement the marginheight attribute on
+ * HTML <frame>/<iframe> elements. A value smaller than zero indicates that
+ * the attribute was not set.
+ */
+ attribute long marginHeight;
+
+ /*
+ * Tells the docshell to offer focus to its tree owner.
+ * This is currently only necessary for embedding chrome.
+ * If forDocumentNavigation is true, then document navigation should be
+ * performed, where only the root of documents are selected. Otherwise, the
+ * next element in the parent should be returned. Returns true if focus was
+ * successfully taken by the tree owner.
+ */
+ bool tabToTreeOwner(in boolean forward, in boolean forDocumentNavigation);
+
+ /**
+ * Current busy state for DocShell
+ */
+ const unsigned long BUSY_FLAGS_NONE = 0;
+ const unsigned long BUSY_FLAGS_BUSY = 1;
+ const unsigned long BUSY_FLAGS_BEFORE_PAGE_LOAD = 2;
+ const unsigned long BUSY_FLAGS_PAGE_LOADING = 4;
+
+ /**
+ * Load commands for the document
+ */
+ const unsigned long LOAD_CMD_NORMAL = 0x1; // Normal load
+ const unsigned long LOAD_CMD_RELOAD = 0x2; // Reload
+ const unsigned long LOAD_CMD_HISTORY = 0x4; // Load from history
+ const unsigned long LOAD_CMD_PUSHSTATE = 0x8; // History.pushState()
+
+ readonly attribute unsigned long busyFlags;
+
+ /*
+ * attribute to access the loadtype for the document
+ */
+ attribute unsigned long loadType;
+
+ /*
+ * Default load flags (as defined in nsIRequest) that will be set on all
+ * requests made by this docShell and propagated to all child docShells and
+ * to nsILoadGroup::defaultLoadFlags for the docShell's loadGroup.
+ * Default is no flags. Once set, only future requests initiated by the
+ * docShell are affected, so in general, these flags should be set before
+ * the docShell loads any content.
+ */
+ attribute nsLoadFlags defaultLoadFlags;
+
+ /*
+ * returns true if the docshell is being destroyed, false otherwise
+ */
+ boolean isBeingDestroyed();
+
+ /*
+ * Returns true if the docshell is currently executing the onLoad Handler
+ */
+ readonly attribute boolean isExecutingOnLoadHandler;
+
+ attribute nsILayoutHistoryState layoutHistoryState;
+
+ readonly attribute boolean shouldSaveLayoutState;
+
+ /**
+ * The SecureBrowserUI object for this docshell. This is set by XUL
+ * <browser> or nsWebBrowser for their root docshell.
+ */
+ attribute nsISecureBrowserUI securityUI;
+
+ /**
+ * Cancel the XPCOM timers for each meta-refresh URI in this docshell,
+ * and this docshell's children, recursively. The meta-refresh timers can be
+ * restarted using resumeRefreshURIs(). If the timers are already suspended,
+ * this has no effect.
+ */
+ void suspendRefreshURIs();
+
+ /**
+ * Restart the XPCOM timers for each meta-refresh URI in this docshell,
+ * and this docshell's children, recursively. If the timers are already
+ * running, this has no effect.
+ */
+ void resumeRefreshURIs();
+
+ /**
+ * Begin firing WebProgressListener notifications for restoring a page
+ * presentation. |viewer| is the content viewer whose document we are
+ * starting to load. If null, it defaults to the docshell's current content
+ * viewer, creating one if necessary. |top| should be true for the toplevel
+ * docshell that is being restored; it will be set to false when this method
+ * is called for child docshells. This method will post an event to
+ * complete the simulated load after returning to the event loop.
+ */
+ void beginRestore(in nsIContentViewer viewer, in boolean top);
+
+ /**
+ * Finish firing WebProgressListener notifications and DOM events for
+ * restoring a page presentation. This should only be called via
+ * beginRestore().
+ */
+ void finishRestore();
+
+ /* Track whether we're currently restoring a document presentation. */
+ readonly attribute boolean restoringDocument;
+
+ /* attribute to access whether error pages are enabled */
+ attribute boolean useErrorPages;
+
+ /**
+ * Display a load error in a frame while keeping that frame's currentURI
+ * pointing correctly to the page where the error ocurred, rather than to
+ * the error document page. You must provide either the aURI or aURL parameter.
+ *
+ * @param aError The error code to be displayed
+ * @param aURI nsIURI of the page where the error happened
+ * @param aURL wstring of the page where the error happened
+ * @param aFailedChannel The channel related to this error
+ *
+ * Returns whether or not we displayed an error page (note: will always
+ * return false if in-content error pages are disabled!)
+ */
+ boolean displayLoadError(in nsresult aError,
+ in nsIURI aURI,
+ in wstring aURL,
+ [optional] in nsIChannel aFailedChannel);
+
+ /**
+ * The channel that failed to load and resulted in an error page.
+ * May be null. Relevant only to error pages.
+ */
+ readonly attribute nsIChannel failedChannel;
+
+ /**
+ * Keeps track of the previous SHTransaction index and the current
+ * SHTransaction index at the time that the doc shell begins to load.
+ * Used for ContentViewer eviction.
+ */
+ readonly attribute long previousTransIndex;
+ readonly attribute long loadedTransIndex;
+
+ /**
+ * Notification that entries have been removed from the beginning of a
+ * nsSHistory which has this as its rootDocShell.
+ *
+ * @param numEntries - The number of entries removed
+ */
+ void historyPurged(in long numEntries);
+
+ /*
+ * @deprecated, use nsIDocShell.QueryInterface(nsIDOMStorageManager) instead.
+ *
+ * Retrieves the WebApps session storage object for the supplied principal.
+ *
+ * @param principal returns a storage for this principal
+ * @param documentURI new storage will be created with reference to this
+ * document.documentURI that will appear in storage event
+ * @param create If true and a session storage object doesn't
+ * already exist, a new one will be created.
+ */
+ nsIDOMStorage getSessionStorageForPrincipal(in nsIPrincipal principal,
+ in DOMString documentURI,
+ in boolean create);
+
+ /*
+ * @deprecated, use nsIDocShell.QueryInterface(nsIDOMStorageManager) instead.
+ *
+ * Add a WebApps session storage object to the docshell.
+ *
+ * @param principal the principal the storage object is associated with
+ * @param storage the storage object to add
+ */
+ void addSessionStorage(in nsIPrincipal principal, in nsIDOMStorage storage);
+
+ /**
+ * Gets the channel for the currently loaded document, if any.
+ * For a new document load, this will be the channel of the previous document
+ * until after OnLocationChange fires.
+ */
+ readonly attribute nsIChannel currentDocumentChannel;
+
+ /**
+ * Set the offset of this child in its container.
+ */
+ [noscript] void setChildOffset(in unsigned long offset);
+
+ /**
+ * Find out whether the docshell is currently in the middle of a page
+ * transition. This is set just before the pagehide/unload events fire.
+ */
+ readonly attribute boolean isInUnload;
+
+ /**
+ * Find out if the currently loaded document came from a suspicious channel
+ * (such as a JAR channel where the server-returned content type isn't a
+ * known JAR type).
+ */
+ readonly attribute boolean channelIsUnsafe;
+
+ /**
+ * This attribute determines whether Mixed Active Content is loaded on the
+ * document. When it is true, mixed active content was not blocked and has
+ * loaded (or is about to load) on the page. When it is false, mixed active content
+ * has not loaded on the page, either because there was no mixed active content
+ * requests on the page or such requests were blocked by nsMixedContentBlocker.
+ * This boolean is set to true in nsMixedContentBlocker if Mixed Active Content
+ * is allowed (either explicitly on the page by the user or when the about:config
+ * setting security.mixed_content.block_active_content is set to false).
+ */
+ [infallible] readonly attribute boolean hasMixedActiveContentLoaded;
+
+ /**
+ * This attribute determines whether a document has Mixed Active Content
+ * that has been blocked from loading. When it is true, there is definitely
+ * mixed active content on a page that has been blocked by
+ * nsMixedContentBlocker. When it is false, there may or may not be mixed
+ * active content on a page, but if there is, it will load. Note that if the
+ * about:config setting security.mixed_content.block_active_content is set
+ * false, this boolean will be false, since blocking active content has been
+ * disabled.
+ */
+ [infallible] readonly attribute boolean hasMixedActiveContentBlocked;
+
+ /**
+ * This attribute determines whether Mixed Display Content is loaded on the
+ * document. When it is true, mixed display content was not blocked and has
+ * loaded (or is about to load) on the page. Similar behavior to
+ * hasMixedActiveContentLoaded.
+ */
+ [infallible] readonly attribute boolean hasMixedDisplayContentLoaded;
+
+ /**
+ * This attribute determines whether a document has Mixed Display Content
+ * that has been blocked from loading. Similar behavior to
+ * hasMixedActiveContentBlocked.
+ */
+ [infallible] readonly attribute boolean hasMixedDisplayContentBlocked;
+
+ /**
+ * This attribute determines whether a document has Tracking Content
+ * that has been blocked from loading.
+ */
+ [infallible] readonly attribute boolean hasTrackingContentBlocked;
+
+ /**
+ * This attribute determines whether Tracking Content is loaded on the
+ * document. When it is true, tracking content was not blocked and has
+ * loaded (or is about to load) on the page.
+ */
+ [infallible] readonly attribute boolean hasTrackingContentLoaded;
+
+ /**
+ * Disconnects this docshell's editor from its window, and stores the
+ * editor data in the open document's session history entry. This
+ * should be called only during page transitions.
+ */
+ [noscript, notxpcom] void DetachEditorFromWindow();
+
+ /**
+ * If true, this browser is not visible in the traditional sense, but
+ * is actively being rendered to the screen (ex. painted on a canvas)
+ * and should be treated accordingly.
+ **/
+ attribute boolean isOffScreenBrowser;
+
+ /**
+ * If the current content viewer isn't initialized for print preview,
+ * it is replaced with one which is and to which an about:blank document
+ * is loaded.
+ */
+ readonly attribute nsIWebBrowserPrint printPreview;
+
+ /**
+ * Whether this docshell can execute scripts based on its hierarchy.
+ * The rule of thumb here is that we disable js if this docshell or any
+ * of its parents disallow scripting.
+ */
+ [infallible] readonly attribute boolean canExecuteScripts;
+
+ /**
+ * Sets whether a docshell is active. An active docshell is one that is
+ * visible, and thus is not a good candidate for certain optimizations
+ * like image frame discarding. Docshells are active unless told otherwise.
+ */
+ attribute boolean isActive;
+
+ /**
+ * Puts the docshell in prerendering mode. noscript because we want only
+ * native code to be able to put a docshell in prerendering.
+ */
+ [noscript] void SetIsPrerendered();
+
+ /**
+ * Whether this docshell is in prerender mode.
+ */
+ [infallible] readonly attribute boolean isPrerendered;
+
+ /**
+ * The ID of the docshell in the session history.
+ */
+ readonly attribute unsigned long long historyID;
+
+ /**
+ * Sets whether a docshell is an app tab. An app tab docshell may behave
+ * differently than a non-app tab docshell in some cases, such as when
+ * handling link clicks. Docshells are not app tabs unless told otherwise.
+ */
+ attribute boolean isAppTab;
+
+ /**
+ * Create a new about:blank document and content viewer.
+ * @param aPrincipal the principal to use for the new document.
+ */
+ void createAboutBlankContentViewer(in nsIPrincipal aPrincipal);
+
+ /**
+ * Upon getting, returns the canonical encoding label of the document
+ * currently loaded into this docshell.
+ *
+ * Upon setting, sets forcedCharset for compatibility with legacy callers.
+ */
+ attribute ACString charset;
+
+ /**
+ * Called when the user chose an encoding override from the character
+ * encoding menu. Separate from the setter for the charset property to avoid
+ * extensions adding noise to the data.
+ */
+ void gatherCharsetMenuTelemetry();
+
+ /**
+ * The charset forced by the user.
+ */
+ attribute ACString forcedCharset;
+
+ /**
+ * In a child docshell, this is the charset of the parent docshell
+ */
+ [noscript, notxpcom, nostdcall] void setParentCharset(
+ in ACString parentCharset,
+ in int32_t parentCharsetSource,
+ in nsIPrincipal parentCharsetPrincipal);
+ [noscript, notxpcom, nostdcall] void getParentCharset(
+ out ACString parentCharset,
+ out int32_t parentCharsetSource,
+ out nsIPrincipal parentCharsetPrincipal);
+
+ /**
+ * Whether the docShell records profile timeline markers at the moment
+ */
+ [infallible] attribute boolean recordProfileTimelineMarkers;
+
+ /**
+ * Return a DOMHighResTimeStamp representing the number of
+ * milliseconds from an arbitrary point in time. The reference
+ * point is shared by all DocShells and is also used by timestamps
+ * on markers.
+ */
+ DOMHighResTimeStamp now();
+
+ /**
+ * Returns and flushes the profile timeline markers gathered by the docShell
+ */
+ [implicit_jscontext]
+ jsval popProfileTimelineMarkers();
+
+ /**
+ * Add an observer to the list of parties to be notified when this docshell's
+ * private browsing status is changed. |obs| must support weak references.
+ */
+ void addWeakPrivacyTransitionObserver(in nsIPrivacyTransitionObserver obs);
+
+ /**
+ * Add an observer to the list of parties to be notified when reflows are
+ * occurring. |obs| must support weak references.
+ */
+ void addWeakReflowObserver(in nsIReflowObserver obs);
+
+ /**
+ * Remove an observer from the list of parties to be notified about reflows.
+ */
+ void removeWeakReflowObserver(in nsIReflowObserver obs);
+
+ /**
+ * Notify all attached observers that a reflow has just occurred.
+ *
+ * @param interruptible if true, the reflow was interruptible.
+ * @param start timestamp when reflow started, in milliseconds since
+ * navigationStart (accurate to 1/1000 of a ms)
+ * @param end timestamp when reflow ended, in milliseconds since
+ * navigationStart (accurate to 1/1000 of a ms)
+ */
+ [noscript] void notifyReflowObservers(in bool interruptible,
+ in DOMHighResTimeStamp start,
+ in DOMHighResTimeStamp end);
+
+ /**
+ * Add an observer to the list of parties to be notified when scroll position
+ * of some elements is changed.
+ */
+ [noscript] void addWeakScrollObserver(in nsIScrollObserver obs);
+
+ /**
+ * Add an observer to the list of parties to be notified when scroll position
+ * of some elements is changed.
+ */
+ [noscript] void removeWeakScrollObserver(in nsIScrollObserver obs);
+
+ /**
+ * Notify all attached observers that the scroll position of some element
+ * has changed.
+ */
+ [noscript] void notifyScrollObservers();
+
+ /**
+ * Returns true iff the docshell corresponds to an <iframe mozapp>.
+ */
+ [infallible] readonly attribute boolean isApp;
+
+ /**
+ * The type of iframe that this docshell lives.
+ */
+ const unsigned long FRAME_TYPE_REGULAR = 0;
+ const unsigned long FRAME_TYPE_BROWSER = 1;
+ const unsigned long FRAME_TYPE_APP = 2;
+
+ [infallible] attribute unsigned long frameType;
+
+ /**
+ * Returns true if this docshell corresponds to an <iframe mozbrowser> or
+ * <iframe mozapp>. <xul:browser> returns false here.
+ */
+ [infallible] readonly attribute boolean isMozBrowserOrApp;
+
+ /**
+ * Returns true if this docshell corresponds to an isolated <iframe
+ * mozbrowser>.
+ *
+ * <iframe mozbrowser mozapp> and <xul:browser> are not considered to be
+ * mozbrowser elements. <iframe mozbrowser noisolation> does not count as
+ * isolated since isolation is disabled. Isolation can only be disabled if
+ * the containing document is chrome.
+ */
+ [infallible] readonly attribute boolean isIsolatedMozBrowserElement;
+
+ /**
+ * Returns true if this docshell corresponds to an isolated <iframe
+ * mozbrowser> or if the docshell is contained in an isolated <iframe
+ * mozbrowser>.
+ *
+ * <iframe mozbrowser mozapp> and <xul:browser> are not considered to be
+ * mozbrowser elements. <iframe mozbrowser noisolation> does not count as
+ * isolated since isolation is disabled. Isolation can only be disabled if
+ * the containing document is chrome.
+ *
+ * Our notion here of "contained in" means: Walk up the docshell hierarchy in
+ * this process until we hit an <iframe mozapp> or <iframe mozbrowser> (or
+ * until the hierarchy ends). Return true iff the docshell we stopped on has
+ * isIsolatedMozBrowserElement == true.
+ */
+ [infallible] readonly attribute boolean isInIsolatedMozBrowserElement;
+
+ /**
+ * Returns true if this docshell corresponds to an <iframe mozbrowser> or
+ * <iframe mozapp>, or if this docshell is contained in an <iframe mozbrowser>
+ * or <iframe mozapp>. <xul:browser> returns false here.
+ *
+ * To compute this value, we walk up the docshell hierarchy. If we encounter
+ * a docshell with isMozBrowserOrApp before we hit the end of the hierarchy,
+ * we return true. Otherwise, we return false.
+ */
+ [infallible] readonly attribute boolean isInMozBrowserOrApp;
+
+ /**
+ * Returns true if this docshell is the top level content docshell.
+ */
+ [infallible] readonly attribute boolean isTopLevelContentDocShell;
+
+ /**
+ * Returns the id of the app associated with this docshell. If this docshell
+ * is an <iframe mozbrowser> inside an <iframe mozapp>, we return the app's
+ * appId.
+ *
+ * We compute this value by walking up the docshell hierarchy until we find a
+ * docshell on which origin attributes was set. (ignoring those docshells
+ * where x == UNKNOWN_APP_ID). We return the app id x.
+ *
+ * If we don't find a docshell with an associated app id in our hierarchy, we
+ * return NO_APP_ID. We never return UNKNOWN_APP_ID.
+ *
+ * Notice that a docshell may have an associated app even if it returns true
+ * for isBrowserElement!
+ */
+ [infallible] readonly attribute unsigned long appId;
+
+ /**
+ * Return the manifest URL of the app associated with this docshell.
+ *
+ * If there is no associated app in our hierarchy, we return empty string.
+ */
+ readonly attribute DOMString appManifestURL;
+
+ /**
+ * Like nsIDocShellTreeItem::GetSameTypeParent, except this ignores <iframe
+ * mozbrowser> and <iframe mozapp> boundaries.
+ */
+ nsIDocShell getSameTypeParentIgnoreBrowserAndAppBoundaries();
+
+ /**
+ * Like nsIDocShellTreeItem::GetSameTypeRootTreeItem, except this ignores
+ * <iframe mozbrowser> and <iframe mozapp> boundaries.
+ */
+ nsIDocShell getSameTypeRootTreeItemIgnoreBrowserAndAppBoundaries();
+
+ /**
+ * True iff asynchronous panning and zooming is enabled for this
+ * docshell.
+ */
+ readonly attribute bool asyncPanZoomEnabled;
+
+ /**
+ * The sandbox flags on the docshell. These reflect the value of the sandbox
+ * attribute of the associated IFRAME or CSP-protectable content, if
+ * existent. See the HTML5 spec for more details.
+ * These flags on the docshell reflect the current state of the sandbox
+ * attribute, which is modifiable. They are only used when loading new
+ * content, sandbox flags are also immutably set on the document when it is
+ * loaded.
+ * The sandbox flags of a document depend on the sandbox flags on its
+ * docshell and of its parent document, if any.
+ * See nsSandboxFlags.h for the possible flags.
+ */
+ attribute unsigned long sandboxFlags;
+
+ /**
+ * When a new browsing context is opened by a sandboxed document, it needs to
+ * keep track of the browsing context that opened it, so that it can be
+ * navigated by it. This is the "one permitted sandboxed navigator".
+ */
+ attribute nsIDocShell onePermittedSandboxedNavigator;
+
+ /**
+ * Returns true if we are sandboxed from aTargetDocShell.
+ * aTargetDocShell - the browsing context we are attempting to navigate.
+ */
+ [noscript,notxpcom,nostdcall] bool isSandboxedFrom(in nsIDocShell aTargetDocShell);
+
+ /**
+ * This member variable determines whether a document has Mixed Active Content that
+ * was initially blocked from loading, but the user has choosen to override the
+ * block and allow the content to load. mMixedContentChannel is set to the document's
+ * channel when the user allows mixed content. The nsMixedContentBlocker content policy
+ * checks if the document's root channel matches the mMixedContentChannel. If it matches,
+ * then Mixed Content is loaded. If it does match, mixed content is blocked.
+ *
+ * A match implies that there is definitely mixed active content on a page that was
+ * initially blocked by nsMixedContentBlocker and then allowed and loaded by the user.
+ * A miss imples that IF there is mixed active content on the page AND it was
+ * blocked by nsMixedContentBlocker.cpp, the user has not choosen to override
+ * the block. Note that if the about:config setting
+ * security.mixed_content.block_active_content is set to false, this boolean
+ * will be false, mMixedContentChannel will remain null since blocking active content has
+ * been disabled and hence mMixedContentChannel will never be set.
+ */
+ attribute nsIChannel mixedContentChannel;
+
+ /**
+ * Checks whether the channel associated with the root docShell is equal to
+ * mMixedContentChannel. If they are the same, allowMixedContent is set to true.
+ * Checks if the root document has a secure connection. If it is, sets
+ * rootHasSecureConnection to true. If the docShell is the root doc shell,
+ * isRootDocShell is set to true.
+ */
+ void GetAllowMixedContentAndConnectionData(out boolean rootHasSecureConnection, out boolean allowMixedContent, out boolean isRootDocShell);
+
+
+ /**
+ * Are plugins allowed in the current document loaded in this docshell ?
+ * (if there is one). This depends on whether plugins are allowed by this
+ * docshell itself or if the document is sandboxed and hence plugins should
+ * not be allowed.
+ */
+ [noscript, notxpcom] bool pluginsAllowedInCurrentDoc();
+
+
+ /**
+ * Attribute that determines whether fullscreen is allowed to be entered for
+ * this subtree of the docshell tree. This is true when all iframes containing
+ * this docshell have their "allowfullscreen" attribute set to "true".
+ * fullscreenAllowed is only writable at content boundaries, where it is used
+ * to propagate the value of the cross process parent's iframe's
+ * "allowfullscreen" attribute to the child process. Setting
+ * fullscreenAllowed on docshells which aren't content boundaries throws an
+ * exception.
+ */
+ [infallible] readonly attribute boolean fullscreenAllowed;
+
+ void setFullscreenAllowed(in boolean allowed);
+
+ [notxpcom] uint32_t orientationLock();
+ [notxpcom] void setOrientationLock(in uint32_t orientationLock);
+
+ [noscript, infallible] attribute boolean affectPrivateSessionLifetime;
+
+ /**
+ * Indicates whether the UI may enable the character encoding menu. The UI
+ * must disable the menu when this property is false.
+ */
+ [infallible] readonly attribute boolean mayEnableCharacterEncodingMenu;
+
+ attribute nsIEditor editor;
+ readonly attribute boolean editable; /* this docShell is editable */
+ readonly attribute boolean hasEditingSession; /* this docShell has an editing session */
+
+ /**
+ * Make this docShell editable, setting a flag that causes
+ * an editor to get created, either immediately, or after
+ * a url has been loaded.
+ * @param inWaitForUriLoad true to wait for a URI before
+ * creating the editor.
+ */
+ void makeEditable(in boolean inWaitForUriLoad);
+
+ /**
+ * Get the SHEntry associated with a child docshell
+ */
+ nsISHEntry getChildSHEntry(in long aChildOffset);
+
+ /**
+ * Add a Child SHEntry for a frameset page, given the child's loadtype.
+ * If aCloneChildren is true, then aCloneReference's children will be
+ * cloned onto aHistoryEntry.
+ */
+ void addChildSHEntry(in nsISHEntry aCloneReference,
+ in nsISHEntry aHistoryEntry,
+ in long aChildOffset,
+ in unsigned long aLoadType,
+ in boolean aCloneChilden);
+
+ /**
+ * Whether this docshell should save entries in global history.
+ */
+ attribute boolean useGlobalHistory;
+
+ /**
+ * Removes nsISHEntry objects related to this docshell from session history.
+ * Use this only with subdocuments, like iframes.
+ */
+ void removeFromSessionHistory();
+
+ /**
+ * Set when an iframe/frame is added dynamically.
+ */
+ attribute boolean createdDynamically;
+
+ /**
+ * Returns false for mLSHE, true for mOSHE
+ */
+ boolean getCurrentSHEntry(out nsISHEntry aEntry);
+
+ /**
+ * Cherry picked parts of nsIController.
+ * They are here, because we want to call these functions
+ * from JS.
+ */
+ boolean isCommandEnabled(in string command);
+ void doCommand(in string command);
+ void doCommandWithParams(in string command, in nsICommandParams aParams);
+
+ /**
+ * Invisible DocShell are dummy construct to simulate DOM windows
+ * without any actual visual representation. They have to be marked
+ * at construction time, to avoid any painting activity.
+ */
+ [noscript, notxpcom] bool IsInvisible();
+ [noscript, notxpcom] void SetInvisible(in bool aIsInvisibleDochsell);
+
+/**
+ * Get the script global for the document in this docshell.
+*/
+ [noscript,notxpcom,nostdcall] nsIScriptGlobalObject GetScriptGlobalObject();
+
+ /**
+ * If deviceSizeIsPageSize is set to true, device-width/height media queries
+ * will be calculated from the page size, not the device size.
+ *
+ * Used by the Responsive Design Mode and B2G Simulator.
+ *
+ * Default is False.
+ * Default value can be overriden with
+ * docshell.device_size_is_page_size pref.
+ */
+ [infallible] attribute boolean deviceSizeIsPageSize;
+
+ /**
+ * Regarding setOpener / getOpener - We can't use XPIDL's "attribute"
+ * for notxpcom, so we're relegated to using explicit gets / sets. This
+ * should be fine, considering that these methods should only ever be
+ * called from native code.
+ */
+ [noscript,notxpcom,nostdcall] void setOpener(in nsITabParent aOpener);
+ [noscript,notxpcom,nostdcall] nsITabParent getOpener();
+
+ /**
+ * Notify DocShell when the browser is about to start executing JS, and after
+ * that execution has stopped. This only occurs when the Timeline devtool
+ * is collecting information.
+ */
+ [noscript,notxpcom,nostdcall] void notifyJSRunToCompletionStart(in string aReason,
+ in wstring functionName,
+ in wstring fileName,
+ in unsigned long lineNumber,
+ in jsval asyncStack,
+ in string asyncCause);
+ [noscript,notxpcom,nostdcall] void notifyJSRunToCompletionStop();
+
+ /**
+ * This attribute determines whether a document which is not about:blank has
+ * already be loaded by this docShell.
+ */
+ [infallible] readonly attribute boolean hasLoadedNonBlankURI;
+
+ /**
+ * Allow usage of -moz-window-dragging:drag for content docshells.
+ * True for top level chrome docshells. Throws if set to false with
+ * top level chrome docshell.
+ */
+ attribute boolean windowDraggingAllowed;
+
+ /**
+ * Sets/gets the current scroll restoration mode.
+ * @see https://html.spec.whatwg.org/#dom-history-scroll-restoration
+ */
+ attribute boolean currentScrollRestorationIsManual;
+
+ /**
+ * Setter and getter for the origin attributes living on this docshell.
+ */
+ [implicit_jscontext]
+ jsval getOriginAttributes();
+
+ [implicit_jscontext]
+ void setOriginAttributes(in jsval aAttrs);
+
+ /**
+ * The editing session for this docshell.
+ */
+ readonly attribute nsIEditingSession editingSession;
+
+ /**
+ * The tab child for this docshell.
+ */
+ [binaryname(ScriptableTabChild)] readonly attribute nsITabChild tabChild;
+ [noscript,notxpcom,nostdcall] TabChildRef GetTabChild();
+
+ [noscript,nostdcall,notxpcom] nsICommandManager GetCommandManager();
+
+ /**
+ * This allows chrome to override the default choice of whether touch events
+ * are available on a specific docshell. Possible values are listed below.
+ */
+ attribute unsigned long touchEventsOverride;
+ /**
+ * Override platform/pref default behaviour and force-disable touch events.
+ */
+ const unsigned long TOUCHEVENTS_OVERRIDE_DISABLED = 0;
+ /**
+ * Override platform/pref default behaviour and force-enable touch events.
+ */
+ const unsigned long TOUCHEVENTS_OVERRIDE_ENABLED = 1;
+ /**
+ * Don't override the platform/pref default behaviour for touch events.
+ */
+ const unsigned long TOUCHEVENTS_OVERRIDE_NONE = 2;
+
+ /**
+ * A DocShell is locked to the current process if it would be
+ * content-observable for a process switch to occur before performing a
+ * navigation load. It is important to ensure that a DocShell is not process
+ * locked before perfoming process changing loads.
+ */
+ [infallible] readonly attribute unsigned long processLockReason;
+ /**
+ * The DocShell is not locked to the current process, and a navigation may
+ * proceed in a new process.
+ */
+ const unsigned long PROCESS_LOCK_NONE = 0;
+ /**
+ * The DocShell is locked to the current process because it is not a
+ * toplevel browsing context.
+ */
+ const unsigned long PROCESS_LOCK_IFRAME = 1;
+ /**
+ * The DocShell is locked to the current process because there exist other
+ * related browsing contexts which may be holding a reference.
+ */
+ const unsigned long PROCESS_LOCK_RELATED_CONTEXTS = 2;
+ /**
+ * The DocShell is locked to the current process because the current
+ * process is not a content process.
+ *
+ * NOTE: Some loads may not consider this a hard process lock, and may wish to
+ * ignore this reason.
+ */
+ const unsigned long PROCESS_LOCK_NON_CONTENT = 3;
+};
diff --git a/docshell/base/nsIDocShellLoadInfo.idl b/docshell/base/nsIDocShellLoadInfo.idl
new file mode 100644
index 000000000..113c0a4c1
--- /dev/null
+++ b/docshell/base/nsIDocShellLoadInfo.idl
@@ -0,0 +1,125 @@
+/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+/**
+ * The nsIDocShellLoadInfo interface defines an interface for specifying
+ * setup information used in a nsIDocShell::loadURI call.
+ */
+
+interface nsIURI;
+interface nsIInputStream;
+interface nsISHEntry;
+interface nsIDocShell;
+interface nsIPrincipal;
+
+typedef long nsDocShellInfoLoadType;
+typedef unsigned long nsDocShellInfoReferrerPolicy;
+
+[scriptable, uuid(e7570e5a-f1d6-452d-b4f8-b35fdc63aa03)]
+interface nsIDocShellLoadInfo : nsISupports
+{
+ /** This is the referrer for the load. */
+ attribute nsIURI referrer;
+
+ /**
+ * The originalURI to be passed to nsIDocShell.internalLoad. May be null.
+ */
+ attribute nsIURI originalURI;
+
+ /**
+ * loadReplace flag to be passed to nsIDocShell.internalLoad.
+ */
+ attribute boolean loadReplace;
+
+ /** The principal of the load, that is, the entity responsible for
+ * causing the load to occur. In most cases the referrer and
+ * the triggeringPrincipal's URI will be identical.
+ */
+ attribute nsIPrincipal triggeringPrincipal;
+
+ /** If this attribute is true and no triggeringPrincipal is specified,
+ * copy the principal from the referring document.
+ */
+ attribute boolean inheritPrincipal;
+
+ /** If this attribute is true only ever use the principal specified
+ * by the triggeringPrincipal and inheritPrincipal attributes.
+ * If there are security reasons for why this is unsafe, such
+ * as trying to use a systemprincipal as the triggeringPrincipal
+ * for a content docshell the load fails.
+ */
+ attribute boolean principalIsExplicit;
+
+ /* these are load type enums... */
+ const long loadNormal = 0; // Normal Load
+ const long loadNormalReplace = 1; // Normal Load but replaces current history slot
+ const long loadHistory = 2; // Load from history
+ const long loadReloadNormal = 3; // Reload
+ const long loadReloadBypassCache = 4;
+ const long loadReloadBypassProxy = 5;
+ const long loadReloadBypassProxyAndCache = 6;
+ const long loadLink = 7;
+ const long loadRefresh = 8;
+ const long loadReloadCharsetChange = 9;
+ const long loadBypassHistory = 10;
+ const long loadStopContent = 11;
+ const long loadStopContentAndReplace = 12;
+ const long loadNormalExternal = 13;
+ const long loadNormalBypassCache = 14;
+ const long loadNormalBypassProxy = 15;
+ const long loadNormalBypassProxyAndCache = 16;
+ const long loadPushState = 17; // history.pushState or replaceState
+ const long loadReplaceBypassCache = 18;
+ const long loadReloadMixedContent = 19;
+ const long loadNormalAllowMixedContent = 20;
+
+ /** Contains a load type as specified by the load* constants */
+ attribute nsDocShellInfoLoadType loadType;
+
+ /** SHEntry for this page */
+ attribute nsISHEntry SHEntry;
+
+ /** Target for load, like _content, _blank etc. */
+ attribute wstring target;
+
+ /** Post data */
+ attribute nsIInputStream postDataStream;
+
+ /** Additional headers */
+ attribute nsIInputStream headersStream;
+
+ /** True if the referrer should be sent, false if it shouldn't be
+ * sent, even if it's available. This attribute defaults to true.
+ */
+ attribute boolean sendReferrer;
+
+ /** Referrer policy for the load. This attribute holds one of
+ * the values (REFERRER_POLICY_*) defined in nsIHttpChannel.
+ */
+ attribute nsDocShellInfoReferrerPolicy referrerPolicy;
+
+ /** True if the docshell has been created to load an iframe where the
+ * srcdoc attribute has been set. Set when srcdocData is specified.
+ */
+ readonly attribute boolean isSrcdocLoad;
+
+ /** When set, the load will be interpreted as a srcdoc load, where contents
+ * of this string will be loaded instead of the URI. Setting srcdocData
+ * sets isSrcdocLoad to true
+ */
+ attribute AString srcdocData;
+
+ /** When set, this is the Source Browsing Context for the navigation. */
+ attribute nsIDocShell sourceDocShell;
+
+ /**
+ * Used for srcdoc loads to give view-source knowledge of the load's base
+ * URI as this information isn't embedded in the load's URI.
+ */
+ attribute nsIURI baseURI;
+};
diff --git a/docshell/base/nsIDocShellTreeItem.idl b/docshell/base/nsIDocShellTreeItem.idl
new file mode 100644
index 000000000..431b16c79
--- /dev/null
+++ b/docshell/base/nsIDocShellTreeItem.idl
@@ -0,0 +1,184 @@
+/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIDocShellTreeOwner;
+interface nsIDocument;
+interface nsPIDOMWindowOuter;
+
+
+/**
+ * The nsIDocShellTreeItem supplies the methods that are required of any item
+ * that wishes to be able to live within the docshell tree either as a middle
+ * node or a leaf.
+ */
+
+[scriptable, uuid(9b7c586f-9214-480c-a2c4-49b526fff1a6)]
+interface nsIDocShellTreeItem : nsISupports
+{
+ /*
+ name of the DocShellTreeItem
+ */
+ attribute AString name;
+
+ /**
+ * Compares the provided name against the item's name and
+ * returns the appropriate result.
+ *
+ * @return <CODE>PR_TRUE</CODE> if names match;
+ * <CODE>PR_FALSE</CODE> otherwise.
+ */
+ boolean nameEquals(in AString name);
+
+ /*
+ Definitions for the item types.
+ */
+ const long typeChrome=0; // typeChrome must equal 0
+ const long typeContent=1; // typeContent must equal 1
+ const long typeContentWrapper=2; // typeContentWrapper must equal 2
+ const long typeChromeWrapper=3; // typeChromeWrapper must equal 3
+
+ const long typeAll=0x7FFFFFFF;
+
+ /*
+ The type this item is.
+ */
+ attribute long itemType;
+ [noscript,notxpcom,nostdcall] long ItemType();
+
+ /*
+ Parent DocShell.
+ */
+ readonly attribute nsIDocShellTreeItem parent;
+
+ /*
+ This getter returns the same thing parent does however if the parent
+ is of a different itemType, or if the parent is an <iframe mozbrowser>
+ or <iframe mozapp>, it will instead return nullptr. This call is a
+ convience function for those wishing to not cross the boundaries at
+ which item types change.
+ */
+ readonly attribute nsIDocShellTreeItem sameTypeParent;
+
+ /*
+ Returns the root DocShellTreeItem. This is a convience equivalent to
+ getting the parent and its parent until there isn't a parent.
+ */
+ readonly attribute nsIDocShellTreeItem rootTreeItem;
+
+ /*
+ Returns the root DocShellTreeItem of the same type. This is a convience
+ equivalent to getting the parent of the same type and its parent until
+ there isn't a parent.
+ */
+ readonly attribute nsIDocShellTreeItem sameTypeRootTreeItem;
+
+ /*
+ Returns the docShellTreeItem with the specified name. Search order is as
+ follows...
+ 1.) Check name of self, if it matches return it.
+ 2.) For each immediate child.
+ a.) Check name of child and if it matches return it.
+ b.) Ask the child to perform the check
+ i.) Do not ask a child if it is the aRequestor
+ ii.) Do not ask a child if it is of a different item type.
+ 3.) If there is a parent of the same item type ask parent to perform the check
+ a.) Do not ask parent if it is the aRequestor
+ 4.) If there is a tree owner ask the tree owner to perform the check
+ a.) Do not ask the tree owner if it is the aRequestor
+ b.) This should only be done if there is no parent of the same type.
+
+ Return the child DocShellTreeItem with the specified name.
+ name - This is the name of the item that is trying to be found.
+ aRequestor - This is the object that is requesting the find. This
+ parameter is used to identify when the child is asking its parent to find
+ a child with the specific name. The parent uses this parameter to ensure
+ a resursive state does not occur by not again asking the requestor to find
+ a shell by the specified name. Inversely the child uses it to ensure it
+ does not ask its parent to do the search if its parent is the one that
+ asked it to search. Children also use this to test against the treeOwner;
+ aOriginalRequestor - The original treeitem that made the request, if any.
+ This is used to ensure that we don't run into cross-site issues.
+ */
+ nsIDocShellTreeItem findItemWithName(in AString name,
+ in nsISupports aRequestor,
+ in nsIDocShellTreeItem aOriginalRequestor);
+
+ /*
+ The owner of the DocShell Tree. This interface will be called upon when
+ the docshell has things it needs to tell to the owner of the docshell.
+ Note that docShell tree ownership does not cross tree types. Meaning
+ setting ownership on a chrome tree does not set ownership on the content
+ sub-trees. A given tree's boundaries are identified by the type changes.
+ Trees of different types may be connected, but should not be traversed
+ for things such as ownership.
+
+ Note implementers of this interface should NOT effect the lifetime of the
+ parent DocShell by holding this reference as it creates a cycle. Owners
+ when releasing this interface should set the treeOwner to nullptr.
+ Implementers of this interface are guaranteed that when treeOwner is
+ set that the poitner is valid without having to addref.
+
+ Further note however when others try to get the interface it should be
+ addref'd before handing it to them.
+ */
+ readonly attribute nsIDocShellTreeOwner treeOwner;
+ [noscript] void setTreeOwner(in nsIDocShellTreeOwner treeOwner);
+
+ /*
+ The current number of DocShells which are immediate children of the
+ this object.
+ */
+ readonly attribute long childCount;
+
+ /*
+ Add a new child DocShellTreeItem. Adds to the end of the list.
+ Note that this does NOT take a reference to the child. The child stays
+ alive only as long as it's referenced from outside the docshell tree.
+ @throws NS_ERROR_ILLEGAL_VALUE if child corresponds to the same
+ object as this treenode or an ancestor of this treenode
+ @throws NS_ERROR_UNEXPECTED if this node is a leaf in the tree.
+ */
+ void addChild(in nsIDocShellTreeItem child);
+
+ /*
+ Removes a child DocShellTreeItem.
+ @throws NS_ERROR_UNEXPECTED if this node is a leaf in the tree.
+ */
+ void removeChild(in nsIDocShellTreeItem child);
+
+ /**
+ * Return the child at the index requested. This is 0-based.
+ *
+ * @throws NS_ERROR_UNEXPECTED if the index is out of range
+ */
+ nsIDocShellTreeItem getChildAt(in long index);
+
+ /*
+ Return the child DocShellTreeItem with the specified name.
+ aName - This is the name of the item that is trying to be found.
+ aRecurse - Is used to tell the function to recurse through children.
+ Note, recursion will only happen through items of the same type.
+ aSameType - If this is set only children of the same type will be returned.
+ aRequestor - This is the docshellTreeItem that is requesting the find. This
+ parameter is used when recursion is being used to avoid searching the same
+ tree again when a child has asked a parent to search for children.
+ aOriginalRequestor - The original treeitem that made the request, if any.
+ This is used to ensure that we don't run into cross-site issues.
+
+ Note the search is depth first when recursing.
+ */
+ nsIDocShellTreeItem findChildWithName(in AString aName,
+ in boolean aRecurse,
+ in boolean aSameType,
+ in nsIDocShellTreeItem aRequestor,
+ in nsIDocShellTreeItem aOriginalRequestor);
+
+ [noscript,nostdcall,notxpcom] nsIDocument getDocument();
+ [noscript,nostdcall,notxpcom] nsPIDOMWindowOuter getWindow();
+};
+
diff --git a/docshell/base/nsIDocShellTreeOwner.idl b/docshell/base/nsIDocShellTreeOwner.idl
new file mode 100644
index 000000000..3610bd969
--- /dev/null
+++ b/docshell/base/nsIDocShellTreeOwner.idl
@@ -0,0 +1,109 @@
+/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+/**
+ * The nsIDocShellTreeOwner
+ */
+
+interface nsIDocShellTreeItem;
+interface nsITabParent;
+
+[scriptable, uuid(0e3dc4b1-4cea-4a37-af71-79f0afd07574)]
+interface nsIDocShellTreeOwner : nsISupports
+{
+ /**
+ * Called when a content shell is added to the docshell tree. This is
+ * _only_ called for "root" content shells (that is, ones whose parent is a
+ * chrome shell).
+ *
+ * @param aContentShell the shell being added.
+ * @param aPrimary whether the shell is primary.
+ * @param aTargetable whether the shell can be a target for named window
+ * targeting.
+ * @param aID the "id" of the shell. What this actually means is
+ * undefined. Don't rely on this for anything.
+ */
+ void contentShellAdded(in nsIDocShellTreeItem aContentShell,
+ in boolean aPrimary, in boolean aTargetable,
+ in AString aID);
+
+ /**
+ * Called when a content shell is removed from the docshell tree. This is
+ * _only_ called for "root" content shells (that is, ones whose parent is a
+ * chrome shell). Note that if aContentShell was never added,
+ * contentShellRemoved should just do nothing.
+ *
+ * @param aContentShell the shell being removed.
+ */
+ void contentShellRemoved(in nsIDocShellTreeItem aContentShell);
+
+ /*
+ Returns the Primary Content Shell
+ */
+ readonly attribute nsIDocShellTreeItem primaryContentShell;
+
+ void tabParentAdded(in nsITabParent aTab, in boolean aPrimary);
+ void tabParentRemoved(in nsITabParent aTab);
+
+ /*
+ In multiprocess case we may not have primaryContentShell but
+ primaryTabParent.
+ */
+ readonly attribute nsITabParent primaryTabParent;
+
+ /*
+ Tells the tree owner to size its window or parent window in such a way
+ that the shell passed along will be the size specified.
+ */
+ void sizeShellTo(in nsIDocShellTreeItem shell, in long cx, in long cy);
+
+ /*
+ Gets the size of the primary content area in CSS pixels. This should work
+ for both in-process and out-of-process content areas.
+ */
+ void getPrimaryContentSize(out long width, out long height);
+ /*
+ Sets the size of the primary content area in CSS pixels. This should work
+ for both in-process and out-of-process content areas.
+ */
+ void setPrimaryContentSize(in long width, in long height);
+
+ /*
+ Gets the size of the root docshell in CSS pixels.
+ */
+ void getRootShellSize(out long width, out long height);
+ /*
+ Sets the size of the root docshell in CSS pixels.
+ */
+ void setRootShellSize(in long width, in long height);
+
+ /*
+ Sets the persistence of different attributes of the window.
+ */
+ void setPersistence(in boolean aPersistPosition,
+ in boolean aPersistSize,
+ in boolean aPersistSizeMode);
+
+ /*
+ Gets the current persistence states of the window.
+ */
+ void getPersistence(out boolean aPersistPosition,
+ out boolean aPersistSize,
+ out boolean aPersistSizeMode);
+
+ /*
+ Gets the number of targettable docshells.
+ */
+ readonly attribute unsigned long targetableShellCount;
+
+ /*
+ Returns true if there is a primary content shell or a primary
+ tab parent.
+ */
+ readonly attribute bool hasPrimaryContent;
+};
diff --git a/docshell/base/nsIDocumentLoaderFactory.idl b/docshell/base/nsIDocumentLoaderFactory.idl
new file mode 100644
index 000000000..7e0df1fd7
--- /dev/null
+++ b/docshell/base/nsIDocumentLoaderFactory.idl
@@ -0,0 +1,46 @@
+/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIChannel;
+interface nsIContentViewer;
+interface nsIStreamListener;
+interface nsIDocShell;
+interface nsIDocument;
+interface nsILoadGroup;
+interface nsIPrincipal;
+
+/**
+ * To get a component that implements nsIDocumentLoaderFactory
+ * for a given mimetype, use nsICategoryManager to find an entry
+ * with the mimetype as its name in the category "Gecko-Content-Viewers".
+ * The value of the entry is the contractid of the component.
+ * The component is a service, so use GetService, not CreateInstance to get it.
+ */
+
+[scriptable, uuid(e795239e-9d3c-47c4-b063-9e600fb3b287)]
+interface nsIDocumentLoaderFactory : nsISupports {
+ nsIContentViewer createInstance(in string aCommand,
+ in nsIChannel aChannel,
+ in nsILoadGroup aLoadGroup,
+ in ACString aContentType,
+ in nsIDocShell aContainer,
+ in nsISupports aExtraInfo,
+ out nsIStreamListener aDocListenerResult);
+
+ nsIContentViewer createInstanceForDocument(in nsISupports aContainer,
+ in nsIDocument aDocument,
+ in string aCommand);
+
+ /**
+ * Create a blank document using the given loadgroup and given
+ * principal. aPrincipal is allowed to be null, in which case the
+ * new document will get the about:blank codebase principal.
+ */
+ nsIDocument createBlankDocument(in nsILoadGroup aLoadGroup,
+ in nsIPrincipal aPrincipal);
+};
diff --git a/docshell/base/nsIDownloadHistory.idl b/docshell/base/nsIDownloadHistory.idl
new file mode 100644
index 000000000..ed2a19deb
--- /dev/null
+++ b/docshell/base/nsIDownloadHistory.idl
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 sts=2
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIURI;
+
+/**
+ * This interface can be used to add a download to history. There is a separate
+ * interface specifically for downloads in case embedders choose to track
+ * downloads differently from other types of history.
+ */
+[scriptable, uuid(4dcd6a12-a091-4f38-8360-022929635746)]
+interface nsIDownloadHistory : nsISupports {
+ /**
+ * Adds a download to history. This will also notify observers that the
+ * URI aSource is visited with the topic NS_LINK_VISITED_EVENT_TOPIC if
+ * aSource has not yet been visited.
+ *
+ * @param aSource
+ * The source of the download we are adding to history. This cannot be
+ * null.
+ * @param aReferrer
+ * [optional] The referrer of source URI.
+ * @param aStartTime
+ * [optional] The time the download was started. If the start time
+ * is not given, the current time is used.
+ * @param aDestination
+ * [optional] The target where the download is to be saved on the local
+ * filesystem.
+ * @throws NS_ERROR_NOT_AVAILABLE
+ * In a situation where a history implementation is not available,
+ * where 'history implementation' refers to something like
+ * nsIGlobalHistory and friends.
+ * @note This addition is not guaranteed to be synchronous, since it delegates
+ * the actual addition to the underlying history implementation. If you
+ * need to observe the completion of the addition, use the underlying
+ * history implementation's notifications system (e.g. nsINavHistoryObserver
+ * for toolkit's implementation of this interface).
+ */
+ void addDownload(in nsIURI aSource, [optional] in nsIURI aReferrer,
+ [optional] in PRTime aStartTime,
+ [optional] in nsIURI aDestination);
+
+ /**
+ * Remove all downloads from history.
+ *
+ * @note This removal is not guaranteed to be synchronous, since it delegates
+ * the actual removal to the underlying history implementation. If you
+ * need to observe the completion of the removal, use the underlying
+ * history implementation's notifications system (e.g. nsINavHistoryObserver
+ * for toolkit's implementation of this interface).
+ */
+ void removeAllDownloads();
+};
diff --git a/docshell/base/nsIGlobalHistory2.idl b/docshell/base/nsIGlobalHistory2.idl
new file mode 100644
index 000000000..aa8b44620
--- /dev/null
+++ b/docshell/base/nsIGlobalHistory2.idl
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Provides information about global history to gecko.
+ *
+ * @note This interface replaces and deprecates nsIGlobalHistory.
+ */
+
+#include "nsISupports.idl"
+interface nsIURI;
+
+%{ C++
+
+// nsIObserver topic to fire when you add new visited URIs to the history;
+// the nsIURI is the subject
+#define NS_LINK_VISITED_EVENT_TOPIC "link-visited"
+
+%}
+
+[scriptable, uuid(cf777d42-1270-4b34-be7b-2931c93feda5)]
+interface nsIGlobalHistory2 : nsISupports
+{
+ /**
+ * Add a URI to global history
+ *
+ * @param aURI the URI of the page
+ * @param aRedirect whether the URI was redirected to another location;
+ * this is 'true' for the original URI which is
+ * redirected.
+ * @param aToplevel whether the URI is loaded in a top-level window
+ * @param aReferrer the URI of the referring page
+ *
+ * @note Docshell will not filter out URI schemes like chrome: data:
+ * about: and view-source:. Embedders should consider filtering out
+ * these schemes and others, e.g. mailbox: for the main URI and the
+ * referrer.
+ */
+ void addURI(in nsIURI aURI, in boolean aRedirect, in boolean aToplevel, in nsIURI aReferrer);
+
+ /**
+ * Checks to see whether the given URI is in history.
+ *
+ * @param aURI the uri to the page
+ * @return true if a URI has been visited
+ */
+ boolean isVisited(in nsIURI aURI);
+
+ /**
+ * Set the page title for the given uri. URIs that are not already in
+ * global history will not be added.
+ *
+ * @param aURI the URI for which to set to the title
+ * @param aTitle the page title
+ */
+ void setPageTitle(in nsIURI aURI, in AString aTitle);
+};
diff --git a/docshell/base/nsILinkHandler.h b/docshell/base/nsILinkHandler.h
new file mode 100644
index 000000000..7cdcd566d
--- /dev/null
+++ b/docshell/base/nsILinkHandler.h
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef nsILinkHandler_h___
+#define nsILinkHandler_h___
+
+#include "nsISupports.h"
+#include "mozilla/EventForwards.h"
+
+class nsIContent;
+class nsIDocShell;
+class nsIInputStream;
+class nsIRequest;
+
+#define NS_ILINKHANDLER_IID \
+ { 0xceb9aade, 0x43da, 0x4f1a, \
+ { 0xac, 0x8a, 0xc7, 0x09, 0xfb, 0x22, 0x46, 0x64 } }
+
+/**
+ * Interface used for handling clicks on links
+ */
+class nsILinkHandler : public nsISupports
+{
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_ILINKHANDLER_IID)
+
+ /**
+ * Process a click on a link.
+ *
+ * @param aContent the content for the frame that generated the trigger
+ * @param aURI a URI object that defines the destination for the link
+ * @param aTargetSpec indicates where the link is targeted (may be an empty
+ * string)
+ * @param aPostDataStream the POST data to send
+ * @param aFileName non-null when the link should be downloaded as the given file
+ * @param aHeadersDataStream ???
+ * @param aIsTrusted false if the triggerer is an untrusted DOM event.
+ */
+ NS_IMETHOD OnLinkClick(nsIContent* aContent,
+ nsIURI* aURI,
+ const char16_t* aTargetSpec,
+ const nsAString& aFileName,
+ nsIInputStream* aPostDataStream,
+ nsIInputStream* aHeadersDataStream,
+ bool aIsTrusted) = 0;
+
+ /**
+ * Process a click on a link.
+ *
+ * Works the same as OnLinkClick() except it happens immediately rather than
+ * through an event.
+ *
+ * @param aContent the content for the frame that generated the trigger
+ * @param aURI a URI obect that defines the destination for the link
+ * @param aTargetSpec indicates where the link is targeted (may be an empty
+ * string)
+ * @param aFileName non-null when the link should be downloaded as the given file
+ * @param aPostDataStream the POST data to send
+ * @param aHeadersDataStream ???
+ * @param aDocShell (out-param) the DocShell that the request was opened on
+ * @param aRequest the request that was opened
+ */
+ NS_IMETHOD OnLinkClickSync(nsIContent* aContent,
+ nsIURI* aURI,
+ const char16_t* aTargetSpec,
+ const nsAString& aFileName,
+ nsIInputStream* aPostDataStream = 0,
+ nsIInputStream* aHeadersDataStream = 0,
+ nsIDocShell** aDocShell = 0,
+ nsIRequest** aRequest = 0) = 0;
+
+ /**
+ * Process a mouse-over a link.
+ *
+ * @param aContent the linked content.
+ * @param aURI an URI object that defines the destination for the link
+ * @param aTargetSpec indicates where the link is targeted (it may be an empty
+ * string)
+ */
+ NS_IMETHOD OnOverLink(nsIContent* aContent,
+ nsIURI* aURLSpec,
+ const char16_t* aTargetSpec) = 0;
+
+ /**
+ * Process the mouse leaving a link.
+ */
+ NS_IMETHOD OnLeaveLink() = 0;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsILinkHandler, NS_ILINKHANDLER_IID)
+
+#endif /* nsILinkHandler_h___ */
diff --git a/docshell/base/nsILoadContext.idl b/docshell/base/nsILoadContext.idl
new file mode 100644
index 000000000..b0da7597b
--- /dev/null
+++ b/docshell/base/nsILoadContext.idl
@@ -0,0 +1,155 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: ft=cpp tw=78 sw=2 et ts=2 sts=2 cin
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface mozIDOMWindowProxy;
+interface nsIDOMElement;
+
+%{C++
+#ifdef MOZILLA_INTERNAL_API
+#include "mozilla/BasePrincipal.h" // for DocShellOriginAttributes
+#endif
+%}
+
+/**
+ * An nsILoadContext represents the context of a load. This interface
+ * can be queried for various information about where the load is
+ * happening.
+ */
+[scriptable, uuid(2813a7a3-d084-4d00-acd0-f76620315c02)]
+interface nsILoadContext : nsISupports
+{
+ /**
+ * associatedWindow is the window with which the load is associated, if any.
+ * Note that the load may be triggered by a document which is different from
+ * the document in associatedWindow, and in fact the source of the load need
+ * not be same-origin with the document in associatedWindow. This attribute
+ * may be null if there is no associated window.
+ */
+ readonly attribute mozIDOMWindowProxy associatedWindow;
+
+ /**
+ * topWindow is the top window which is of same type as associatedWindow.
+ * This is equivalent to associatedWindow.top, but is provided here as a
+ * convenience. All the same caveats as associatedWindow of apply, of
+ * course. This attribute may be null if there is no associated window.
+ */
+ readonly attribute mozIDOMWindowProxy topWindow;
+
+ /**
+ * topFrameElement is the <iframe>, <frame>, or <browser> element which
+ * contains the topWindow with which the load is associated.
+ *
+ * Note that we may have a topFrameElement even when we don't have an
+ * associatedWindow, if the topFrameElement's content lives out of process.
+ * topFrameElement is available in single-process and multiprocess contexts.
+ * Note that topFrameElement may be in chrome even when the nsILoadContext is
+ * associated with content.
+ */
+ readonly attribute nsIDOMElement topFrameElement;
+
+ /**
+ * If this LoadContext corresponds to a nested remote iframe, we don't have
+ * access to the topFrameElement. Instead, we must use this id to send
+ * messages. A return value of 0 signifies that this load context is not for
+ * a nested frame.
+ */
+ readonly attribute unsigned long long nestedFrameId;
+
+ /**
+ * True if the load context is content (as opposed to chrome). This is
+ * determined based on the type of window the load is performed in, NOT based
+ * on any URIs that might be around.
+ */
+ readonly attribute boolean isContent;
+
+ /*
+ * Attribute that determines if private browsing should be used. May not be
+ * changed after a document has been loaded in this context.
+ */
+ attribute boolean usePrivateBrowsing;
+
+ /**
+ * Attribute that determines if remote (out-of-process) tabs should be used.
+ */
+ readonly attribute boolean useRemoteTabs;
+
+%{C++
+ /**
+ * De-XPCOMed getter to make call-sites cleaner.
+ */
+ bool UsePrivateBrowsing() {
+ bool usingPB;
+ GetUsePrivateBrowsing(&usingPB);
+ return usingPB;
+ }
+
+ bool UseRemoteTabs() {
+ bool usingRT;
+ GetUseRemoteTabs(&usingRT);
+ return usingRT;
+ }
+%}
+
+ /**
+ * Set the private browsing state of the load context, meant to be used internally.
+ */
+ [noscript] void SetPrivateBrowsing(in boolean aInPrivateBrowsing);
+
+ /**
+ * Set the remote tabs state of the load context, meant to be used internally.
+ */
+ [noscript] void SetRemoteTabs(in boolean aUseRemoteTabs);
+
+ /**
+ * Returns true iff the load is occurring inside an isolated mozbrowser
+ * element. <iframe mozbrowser mozapp> and <xul:browser> are not considered to
+ * be mozbrowser elements. <iframe mozbrowser noisolation> does not count as
+ * isolated since isolation is disabled. Isolation can only be disabled if
+ * the containing document is chrome.
+ */
+ readonly attribute boolean isInIsolatedMozBrowserElement;
+
+ /**
+ * Returns the app id of the app the load is occurring is in. Returns
+ * nsIScriptSecurityManager::NO_APP_ID if the load is not part of an app.
+ */
+ readonly attribute unsigned long appId;
+
+ /**
+ * A dictionary of the non-default origin attributes associated with this
+ * nsILoadContext.
+ */
+ readonly attribute jsval originAttributes;
+
+%{C++
+#ifdef MOZILLA_INTERNAL_API
+ /**
+ * The C++ getter for origin attributes.
+ *
+ * Defined in LoadContext.cpp
+ */
+ bool GetOriginAttributes(mozilla::DocShellOriginAttributes& aAttrs);
+#endif
+%}
+
+ /**
+ * Returns true if tracking protection is enabled for the load context.
+ */
+ boolean IsTrackingProtectionOn();
+
+%{C++
+ /**
+ * De-XPCOMed getter to make call-sites cleaner.
+ */
+ bool UseTrackingProtection() {
+ bool usingTP;
+ IsTrackingProtectionOn(&usingTP);
+ return usingTP;
+ }
+%}
+};
diff --git a/docshell/base/nsIPrivacyTransitionObserver.idl b/docshell/base/nsIPrivacyTransitionObserver.idl
new file mode 100644
index 000000000..c85d468d3
--- /dev/null
+++ b/docshell/base/nsIPrivacyTransitionObserver.idl
@@ -0,0 +1,11 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+[scriptable, function, uuid(b4b1449d-0ef0-47f5-b62e-adc57fd49702)]
+interface nsIPrivacyTransitionObserver : nsISupports
+{
+ void privateModeChanged(in bool enabled);
+};
diff --git a/docshell/base/nsIReflowObserver.idl b/docshell/base/nsIReflowObserver.idl
new file mode 100644
index 000000000..fb602e260
--- /dev/null
+++ b/docshell/base/nsIReflowObserver.idl
@@ -0,0 +1,31 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "domstubs.idl"
+
+[scriptable, uuid(832e692c-c4a6-11e2-8fd1-dce678957a39)]
+interface nsIReflowObserver : nsISupports
+{
+ /**
+ * Called when an uninterruptible reflow has occurred.
+ *
+ * @param start timestamp when reflow ended, in milliseconds since
+ * navigationStart (accurate to 1/1000 of a ms)
+ * @param end timestamp when reflow ended, in milliseconds since
+ * navigationStart (accurate to 1/1000 of a ms)
+ */
+ void reflow(in DOMHighResTimeStamp start,
+ in DOMHighResTimeStamp end);
+
+ /**
+ * Called when an interruptible reflow has occurred.
+ *
+ * @param start timestamp when reflow ended, in milliseconds since
+ * navigationStart (accurate to 1/1000 of a ms)
+ * @param end timestamp when reflow ended, in milliseconds since
+ * navigationStart (accurate to 1/1000 of a ms)
+ */
+ void reflowInterruptible(in DOMHighResTimeStamp start,
+ in DOMHighResTimeStamp end);
+};
diff --git a/docshell/base/nsIRefreshURI.idl b/docshell/base/nsIRefreshURI.idl
new file mode 100644
index 000000000..5abd829da
--- /dev/null
+++ b/docshell/base/nsIRefreshURI.idl
@@ -0,0 +1,91 @@
+/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIChannel;
+interface nsIPrincipal;
+interface nsIURI;
+
+[scriptable, uuid(a5e61a3c-51bd-45be-ac0c-e87b71860656)]
+interface nsIRefreshURI : nsISupports {
+ /**
+ * Load a uri after waiting for aMillis milliseconds. If the docshell
+ * is busy loading a page currently, the refresh request will be
+ * queued and executed when the current load finishes.
+ *
+ * @param aUri The uri to refresh.
+ * @param aPrincipal The triggeringPrincipal for the refresh load
+ * May be null, in which case a principal will be built based on the
+ * referrer URI of the previous docshell load, or will use the system
+ * principal when there is no referrer.
+ * @param aMillis The number of milliseconds to wait.
+ * @param aRepeat Flag to indicate if the uri is to be
+ * repeatedly refreshed every aMillis milliseconds.
+ * @param aMetaRefresh Flag to indicate if this is a Meta refresh.
+ */
+ void refreshURI(in nsIURI aURI,
+ in long aMillis, in boolean aRepeat,
+ in boolean aMetaRefresh,
+ [optional] in nsIPrincipal aPrincipal);
+
+ /**
+ * Loads a URI immediately as if it were a refresh.
+ *
+ * @param aURI The URI to refresh.
+ * @param aPrincipal The triggeringPrincipal for the refresh load
+ * May be null, in which case a principal will be built based on the
+ * referrer URI of the previous docshell load, or will use the system
+ * principal when there is no referrer.
+ * @param aMillis The number of milliseconds by which this refresh would
+ * be delayed if it were not being forced.
+ * @param aMetaRefresh Flag to indicate if this is a meta refresh.
+ */
+ void forceRefreshURI(in nsIURI aURI,
+ in long aMillis, in boolean aMetaRefresh,
+ [optional] in nsIPrincipal aPrincipal);
+
+ /**
+ * Checks the passed in channel to see if there is a refresh header,
+ * if there is, will setup a timer to refresh the uri found
+ * in the header. If docshell is busy loading a page currently, the
+ * request will be queued and executed when the current page
+ * finishes loading.
+ *
+ * Returns the NS_REFRESHURI_HEADER_FOUND success code if a refresh
+ * header was found and successfully setup.
+ *
+ * @param aChannel The channel to be parsed.
+ */
+ void setupRefreshURI(in nsIChannel aChannel);
+
+ /**
+ * Parses the passed in header string and sets up a refreshURI if
+ * a "refresh" header is found. If docshell is busy loading a page
+ * currently, the request will be queued and executed when
+ * the current page finishes loading.
+ *
+ * @param aBaseURI base URI to resolve refresh uri with.
+ * @param aPrincipal The triggeringPrincipal for the refresh load
+ * May be null, in which case a principal will be built based on the
+ * referrer URI of the previous docshell load, or will use the system
+ * principal when there is no referrer.
+ * @param aHeader The meta refresh header string.
+ */
+ void setupRefreshURIFromHeader(in nsIURI aBaseURI,
+ in nsIPrincipal principal,
+ in ACString aHeader);
+
+ /**
+ * Cancels all timer loads.
+ */
+ void cancelRefreshURITimers();
+
+ /**
+ * True when there are pending refreshes, false otherwise.
+ */
+ readonly attribute boolean refreshPending;
+};
diff --git a/docshell/base/nsIScrollObserver.h b/docshell/base/nsIScrollObserver.h
new file mode 100644
index 000000000..82c7cabe9
--- /dev/null
+++ b/docshell/base/nsIScrollObserver.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsIScrollObserver_h___
+#define nsIScrollObserver_h___
+
+#include "nsISupports.h"
+#include "Units.h"
+
+#define NS_ISCROLLOBSERVER_IID \
+ { 0xaa5026eb, 0x2f88, 0x4026, \
+ { 0xa4, 0x6b, 0xf4, 0x59, 0x6b, 0x4e, 0xdf, 0x00 } }
+
+class nsIScrollObserver : public nsISupports
+{
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_ISCROLLOBSERVER_IID)
+
+ /**
+ * Called when the scroll position of some element has changed.
+ */
+ virtual void ScrollPositionChanged() = 0;
+
+ /**
+ * Called when an async panning/zooming transform has started being applied
+ * and passed the scroll offset
+ */
+ virtual void AsyncPanZoomStarted() {};
+
+ /**
+ * Called when an async panning/zooming transform is no longer applied
+ * and passed the scroll offset
+ */
+ virtual void AsyncPanZoomStopped() {};
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIScrollObserver, NS_ISCROLLOBSERVER_IID)
+
+#endif /* nsIScrollObserver_h___ */
diff --git a/docshell/base/nsIScrollable.idl b/docshell/base/nsIScrollable.idl
new file mode 100644
index 000000000..931469528
--- /dev/null
+++ b/docshell/base/nsIScrollable.idl
@@ -0,0 +1,55 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=2 sw=2 et tw=78:
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+/**
+ * The nsIScrollable is an interface that can be implemented by a control that
+ * supports scrolling. This is a generic interface without concern for the
+ * type of content that may be inside.
+ */
+[scriptable, uuid(3507fc93-313e-4a4c-8ca8-4d0ea0f97315)]
+interface nsIScrollable : nsISupports
+{
+ /**
+ * Constants declaring the two scroll orientations a scrollbar can be in.
+ * ScrollOrientation_X - Horizontal scrolling. When passing this
+ * in to a method you are requesting or setting data for the
+ * horizontal scrollbar.
+ * ScrollOrientation_Y - Vertical scrolling. When passing this
+ * in to a method you are requesting or setting data for the
+ * vertical scrollbar.
+ */
+ const long ScrollOrientation_X = 1;
+ const long ScrollOrientation_Y = 2;
+
+ /**
+ * Constants declaring the states of the scrollbars.
+ * ScrollPref_Auto - bars visible only when needed.
+ * ScrollPref_Never - bars never visible, even when scrolling still possible.
+ * ScrollPref_Always - bars always visible, even when scrolling is not possible
+ */
+ const long Scrollbar_Auto = 1;
+ const long Scrollbar_Never = 2;
+ const long Scrollbar_Always = 3;
+
+ /**
+ * Get or set the default scrollbar state for all documents in
+ * this shell.
+ */
+ long getDefaultScrollbarPreferences(in long scrollOrientation);
+ void setDefaultScrollbarPreferences(in long scrollOrientation,
+ in long scrollbarPref);
+
+ /**
+ * Get information about whether the vertical and horizontal scrollbars are
+ * currently visible. If you are only interested in one of the visibility
+ * settings pass nullptr in for the one you aren't interested in.
+ */
+ void getScrollbarVisibility(out boolean verticalVisible,
+ out boolean horizontalVisible);
+};
diff --git a/docshell/base/nsITextScroll.idl b/docshell/base/nsITextScroll.idl
new file mode 100644
index 000000000..87338c491
--- /dev/null
+++ b/docshell/base/nsITextScroll.idl
@@ -0,0 +1,33 @@
+/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+/**
+ * The nsITextScroll is an interface that can be implemented by a control that
+ * supports text scrolling.
+ */
+
+[scriptable, uuid(067B28A0-877F-11d3-AF7E-00A024FFC08C)]
+interface nsITextScroll : nsISupports
+{
+ /**
+ * Scroll the view up or down by aNumLines lines. positive
+ * values move down in the view. Prevents scrolling off the
+ * end of the view.
+ * @param numLines number of lines to scroll the view by
+ */
+ void scrollByLines(in long numLines);
+
+ /**
+ * Scroll the view up or down by numPages pages. a page
+ * is considered to be the amount displayed by the clip view.
+ * positive values move down in the view. Prevents scrolling
+ * off the end of the view.
+ * @param numPages number of pages to scroll the view by
+ */
+ void scrollByPages(in long numPages);
+}; \ No newline at end of file
diff --git a/docshell/base/nsIURIFixup.idl b/docshell/base/nsIURIFixup.idl
new file mode 100644
index 000000000..0145aca00
--- /dev/null
+++ b/docshell/base/nsIURIFixup.idl
@@ -0,0 +1,166 @@
+/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIURI;
+interface nsIInputStream;
+
+/**
+ * Interface indicating what we found/corrected when fixing up a URI
+ */
+[scriptable, uuid(4819f183-b532-4932-ac09-b309cd853be7)]
+interface nsIURIFixupInfo : nsISupports
+{
+ /**
+ * Consumer that asked for fixed up URI.
+ */
+ attribute nsISupports consumer;
+
+ /**
+ * Our best guess as to what URI the consumer will want. Might
+ * be null if we couldn't salvage anything (for instance, because
+ * the input was invalid as a URI and FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP
+ * was not passed)
+ */
+ readonly attribute nsIURI preferredURI;
+
+ /**
+ * The fixed-up original input, *never* using a keyword search.
+ * (might be null if the original input was not recoverable as
+ * a URL, e.g. "foo bar"!)
+ */
+ readonly attribute nsIURI fixedURI;
+
+ /**
+ * The name of the keyword search provider used to provide a keyword search;
+ * empty string if no keyword search was done.
+ */
+ readonly attribute AString keywordProviderName;
+
+ /**
+ * The keyword as used for the search (post trimming etc.)
+ * empty string if no keyword search was done.
+ */
+ readonly attribute AString keywordAsSent;
+
+ /**
+ * Whether we changed the protocol instead of using one from the input as-is.
+ */
+ readonly attribute boolean fixupChangedProtocol;
+
+ /**
+ * Whether we created an alternative URI. We might have added a prefix and/or
+ * suffix, the contents of which are controlled by the
+ * browser.fixup.alternate.prefix and .suffix prefs, with the defaults being
+ * "www." and ".com", respectively.
+ */
+ readonly attribute boolean fixupCreatedAlternateURI;
+
+ /**
+ * The original input
+ */
+ readonly attribute AUTF8String originalInput;
+};
+
+
+/**
+ * Interface implemented by objects capable of fixing up strings into URIs
+ */
+[scriptable, uuid(1da7e9d4-620b-4949-849a-1cd6077b1b2d)]
+interface nsIURIFixup : nsISupports
+{
+ /** No fixup flags. */
+ const unsigned long FIXUP_FLAG_NONE = 0;
+
+ /**
+ * Allow the fixup to use a keyword lookup service to complete the URI.
+ * The fixup object implementer should honour this flag and only perform
+ * any lengthy keyword (or search) operation if it is set.
+ */
+ const unsigned long FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP = 1;
+
+ /**
+ * Tell the fixup to make an alternate URI from the input URI, for example
+ * to turn foo into www.foo.com.
+ */
+ const unsigned long FIXUP_FLAGS_MAKE_ALTERNATE_URI = 2;
+
+ /*
+ * Fix common scheme typos.
+ */
+ const unsigned long FIXUP_FLAG_FIX_SCHEME_TYPOS = 8;
+
+ /* NB: If adding an extra flag, 4 is free (again) */
+
+ /**
+ * Converts an internal URI (e.g. a wyciwyg URI) into one which we can
+ * expose to the user, for example on the URL bar.
+ *
+ * @param aURI The URI to be converted
+ * @return nsIURI The converted, exposable URI
+ * @throws NS_ERROR_MALFORMED_URI when the exposable portion of aURI is malformed
+ * @throws NS_ERROR_UNKNOWN_PROTOCOL when we can't get a protocol handler service
+ * for the URI scheme.
+ */
+ nsIURI createExposableURI(in nsIURI aURI);
+
+ /**
+ * Converts the specified string into a URI, first attempting
+ * to correct any errors in the syntax or other vagaries. Returns
+ * a wellformed URI or nullptr if it can't.
+ *
+ * @param aURIText Candidate URI.
+ * @param aFixupFlags Flags that govern ways the URI may be fixed up.
+ * @param aPostData The POST data to submit with the returned
+ * URI (see nsISearchSubmission).
+ */
+ nsIURI createFixupURI(in AUTF8String aURIText, in unsigned long aFixupFlags,
+ [optional] out nsIInputStream aPostData);
+
+ /**
+ * Same as createFixupURI, but returns information about what it corrected
+ * (e.g. whether we could rescue the URI or "just" generated a keyword
+ * search URI instead).
+ *
+ * @param aURIText Candidate URI.
+ * @param aFixupFlags Flags that govern ways the URI may be fixed up.
+ * @param aPostData The POST data to submit with the returned
+ * URI (see nsISearchSubmission).
+ */
+ nsIURIFixupInfo getFixupURIInfo(in AUTF8String aURIText,
+ in unsigned long aFixupFlags,
+ [optional] out nsIInputStream aPostData);
+
+ /**
+ * Converts the specified keyword string into a URI. Note that it's the
+ * caller's responsibility to check whether keywords are enabled and
+ * whether aKeyword is a sensible keyword.
+ *
+ * @param aKeyword The keyword string to convert into a URI
+ * @param aPostData The POST data to submit to the returned URI
+ * (see nsISearchSubmission).
+ *
+ * @throws NS_ERROR_FAILURE if the resulting URI requires submission of POST
+ * data and aPostData is null.
+ */
+ nsIURIFixupInfo keywordToURI(in AUTF8String aKeyword,
+ [optional] out nsIInputStream aPostData);
+
+ /**
+ * Returns true if the specified domain is whitelisted and false otherwise.
+ * A whitelisted domain is relevant when we have a single word and can't be
+ * sure whether to treat the word as a host name or should instead be
+ * treated as a search term.
+ *
+ * @param aDomain A domain name to query.
+ * @param aDotPos The position of the first '.' character in aDomain, or
+ * -1 if no '.' character exists.
+ */
+ bool isDomainWhitelisted(in AUTF8String aDomain,
+ in uint32_t aDotPos);
+};
+
diff --git a/docshell/base/nsIWebNavigation.idl b/docshell/base/nsIWebNavigation.idl
new file mode 100644
index 000000000..042b1c547
--- /dev/null
+++ b/docshell/base/nsIWebNavigation.idl
@@ -0,0 +1,367 @@
+/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIDOMDocument;
+interface nsIInputStream;
+interface nsISHistory;
+interface nsIURI;
+
+/**
+ * The nsIWebNavigation interface defines an interface for navigating the web.
+ * It provides methods and attributes to direct an object to navigate to a new
+ * location, stop or restart an in process load, or determine where the object
+ * has previously gone.
+ */
+[scriptable, uuid(3ade79d4-8cb9-4952-b18d-4f9b63ca0d31)]
+interface nsIWebNavigation : nsISupports
+{
+ /**
+ * Indicates if the object can go back. If true this indicates that
+ * there is back session history available for navigation.
+ */
+ readonly attribute boolean canGoBack;
+
+ /**
+ * Indicates if the object can go forward. If true this indicates that
+ * there is forward session history available for navigation
+ */
+ readonly attribute boolean canGoForward;
+
+ /**
+ * Tells the object to navigate to the previous session history item. When a
+ * page is loaded from session history, all content is loaded from the cache
+ * (if available) and page state (such as form values and scroll position) is
+ * restored.
+ *
+ * @throw NS_ERROR_UNEXPECTED
+ * Indicates that the call was unexpected at this time, which implies
+ * that canGoBack is false.
+ */
+ void goBack();
+
+ /**
+ * Tells the object to navigate to the next session history item. When a
+ * page is loaded from session history, all content is loaded from the cache
+ * (if available) and page state (such as form values and scroll position) is
+ * restored.
+ *
+ * @throw NS_ERROR_UNEXPECTED
+ * Indicates that the call was unexpected at this time, which implies
+ * that canGoForward is false.
+ */
+ void goForward();
+
+ /**
+ * Tells the object to navigate to the session history item at a given index.
+ *
+ * @throw NS_ERROR_UNEXPECTED
+ * Indicates that the call was unexpected at this time, which implies
+ * that session history entry at the given index does not exist.
+ */
+ void gotoIndex(in long index);
+
+ /****************************************************************************
+ * The following flags may be bitwise combined to form the load flags
+ * parameter passed to either the loadURI or reload method. Some of these
+ * flags are only applicable to loadURI.
+ */
+
+ /**
+ * This flags defines the range of bits that may be specified. Flags
+ * outside this range may be used, but may not be passed to Reload().
+ */
+ const unsigned long LOAD_FLAGS_MASK = 0xffff;
+
+ /**
+ * This is the default value for the load flags parameter.
+ */
+ const unsigned long LOAD_FLAGS_NONE = 0x0000;
+
+ /**
+ * Flags 0x1, 0x2, 0x4, 0x8 are reserved for internal use by
+ * nsIWebNavigation implementations for now.
+ */
+
+ /**
+ * This flag specifies that the load should have the semantics of an HTML
+ * Meta-refresh tag (i.e., that the cache should be bypassed). This flag
+ * is only applicable to loadURI.
+ * XXX the meaning of this flag is poorly defined.
+ * XXX no one uses this, so we should probably deprecate and remove it.
+ */
+ const unsigned long LOAD_FLAGS_IS_REFRESH = 0x0010;
+
+ /**
+ * This flag specifies that the load should have the semantics of a link
+ * click. This flag is only applicable to loadURI.
+ * XXX the meaning of this flag is poorly defined.
+ */
+ const unsigned long LOAD_FLAGS_IS_LINK = 0x0020;
+
+ /**
+ * This flag specifies that history should not be updated. This flag is only
+ * applicable to loadURI.
+ */
+ const unsigned long LOAD_FLAGS_BYPASS_HISTORY = 0x0040;
+
+ /**
+ * This flag specifies that any existing history entry should be replaced.
+ * This flag is only applicable to loadURI.
+ */
+ const unsigned long LOAD_FLAGS_REPLACE_HISTORY = 0x0080;
+
+ /**
+ * This flag specifies that the local web cache should be bypassed, but an
+ * intermediate proxy cache could still be used to satisfy the load.
+ */
+ const unsigned long LOAD_FLAGS_BYPASS_CACHE = 0x0100;
+
+ /**
+ * This flag specifies that any intermediate proxy caches should be bypassed
+ * (i.e., that the content should be loaded from the origin server).
+ */
+ const unsigned long LOAD_FLAGS_BYPASS_PROXY = 0x0200;
+
+ /**
+ * This flag specifies that a reload was triggered as a result of detecting
+ * an incorrect character encoding while parsing a previously loaded
+ * document.
+ */
+ const unsigned long LOAD_FLAGS_CHARSET_CHANGE = 0x0400;
+
+ /**
+ * If this flag is set, Stop() will be called before the load starts
+ * and will stop both content and network activity (the default is to
+ * only stop network activity). Effectively, this passes the
+ * STOP_CONTENT flag to Stop(), in addition to the STOP_NETWORK flag.
+ */
+ const unsigned long LOAD_FLAGS_STOP_CONTENT = 0x0800;
+
+ /**
+ * A hint this load was prompted by an external program: take care!
+ */
+ const unsigned long LOAD_FLAGS_FROM_EXTERNAL = 0x1000;
+
+ /**
+ This flag is set when a user explicitly disables the Mixed Content
+ Blocker, and allows Mixed Content to load on an https page.
+ */
+ const unsigned long LOAD_FLAGS_ALLOW_MIXED_CONTENT = 0x2000;
+
+ /**
+ * This flag specifies that this is the first load in this object.
+ * Set with care, since setting incorrectly can cause us to assume that
+ * nothing was actually loaded in this object if the load ends up being
+ * handled by an external application. This flag must not be passed to
+ * Reload.
+ */
+ const unsigned long LOAD_FLAGS_FIRST_LOAD = 0x4000;
+
+ /**
+ * This flag specifies that the load should not be subject to popup
+ * blocking checks. This flag must not be passed to Reload.
+ */
+ const unsigned long LOAD_FLAGS_ALLOW_POPUPS = 0x8000;
+
+ /**
+ * This flag specifies that the URI classifier should not be checked for
+ * this load. This flag must not be passed to Reload.
+ */
+ const unsigned long LOAD_FLAGS_BYPASS_CLASSIFIER = 0x10000;
+
+ /**
+ * Force relevant cookies to be sent with this load even if normally they
+ * wouldn't be.
+ */
+ const unsigned long LOAD_FLAGS_FORCE_ALLOW_COOKIES = 0x20000;
+
+ /**
+ * Prevent the owner principal from being inherited for this load.
+ * Note: Within Gecko we use the term principal rather than owners
+ * but some legacy addons might still rely on the outdated term.
+ */
+ const unsigned long LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL = 0x40000;
+ const unsigned long LOAD_FLAGS_DISALLOW_INHERIT_OWNER = 0x40000;
+
+ /**
+ * Overwrite the returned error code with a specific result code
+ * when an error page is displayed.
+ */
+ const unsigned long LOAD_FLAGS_ERROR_LOAD_CHANGES_RV = 0x80000;
+
+ /**
+ * This flag specifies that the URI may be submitted to a third-party
+ * server for correction. This should only be applied to non-sensitive
+ * URIs entered by users. This flag must not be passed to Reload.
+ */
+ const unsigned long LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP = 0x100000;
+
+ /**
+ * This flag specifies that common scheme typos should be corrected.
+ */
+ const unsigned long LOAD_FLAGS_FIXUP_SCHEME_TYPOS = 0x200000;
+
+ /**
+ * Loads a given URI. This will give priority to loading the requested URI
+ * in the object implementing this interface. If it can't be loaded here
+ * however, the URI dispatcher will go through its normal process of content
+ * loading.
+ *
+ * @param aURI
+ * The URI string to load. For HTTP and FTP URLs and possibly others,
+ * characters above U+007F will be converted to UTF-8 and then URL-
+ * escaped per the rules of RFC 2396.
+ * @param aLoadFlags
+ * Flags modifying load behaviour. This parameter is a bitwise
+ * combination of the load flags defined above. (Undefined bits are
+ * reserved for future use.) Generally you will pass LOAD_FLAGS_NONE
+ * for this parameter.
+ * @param aReferrer
+ * The referring URI. If this argument is null, then the referring
+ * URI will be inferred internally.
+ * @param aPostData
+ * If the URI corresponds to a HTTP request, then this stream is
+ * appended directly to the HTTP request headers. It may be prefixed
+ * with additional HTTP headers. This stream must contain a "\r\n"
+ * sequence separating any HTTP headers from the HTTP request body.
+ * This parameter is optional and may be null.
+ * @param aHeaders
+ * If the URI corresponds to a HTTP request, then any HTTP headers
+ * contained in this stream are set on the HTTP request. The HTTP
+ * header stream is formatted as:
+ * ( HEADER "\r\n" )*
+ * This parameter is optional and may be null.
+ */
+ void loadURI(in wstring aURI,
+ in unsigned long aLoadFlags,
+ in nsIURI aReferrer,
+ in nsIInputStream aPostData,
+ in nsIInputStream aHeaders);
+
+ /**
+ * Loads a given URI. This will give priority to loading the requested URI
+ * in the object implementing this interface. If it can't be loaded here
+ * however, the URI dispatcher will go through its normal process of content
+ * loading.
+ *
+ * Behaves like loadURI, but allows passing of additional parameters.
+ *
+ * @param aURI
+ * The URI string to load. For HTTP and FTP URLs and possibly others,
+ * characters above U+007F will be converted to UTF-8 and then URL-
+ * escaped per the rules of RFC 2396.
+ * @param aLoadFlags
+ * Flags modifying load behaviour. This parameter is a bitwise
+ * combination of the load flags defined above. (Undefined bits are
+ * reserved for future use.) Generally you will pass LOAD_FLAGS_NONE
+ * for this parameter.
+ * @param aReferrer
+ * The referring URI. If this argument is null, then the referring
+ * URI will be inferred internally.
+ * @param aReferrerPolicy
+ * One of the REFERRER_POLICY_* constants from nsIHttpChannel.
+ * Normal case is REFERRER_POLICY_DEFAULT.
+ * @param aPostData
+ * If the URI corresponds to a HTTP request, then this stream is
+ * appended directly to the HTTP request headers. It may be prefixed
+ * with additional HTTP headers. This stream must contain a "\r\n"
+ * sequence separating any HTTP headers from the HTTP request body.
+ * This parameter is optional and may be null.
+ * @param aHeaders
+ * If the URI corresponds to a HTTP request, then any HTTP headers
+ * contained in this stream are set on the HTTP request. The HTTP
+ * header stream is formatted as:
+ * ( HEADER "\r\n" )*
+ * This parameter is optional and may be null.
+ * @param aBaseURI
+ * Set to indicate a base URI to be associated with the load. Note
+ * that at present this argument is only used with view-source aURIs
+ * and cannot be used to resolve aURI.
+ * This parameter is optional and may be null.
+ */
+ void loadURIWithOptions(in wstring aURI,
+ in unsigned long aLoadFlags,
+ in nsIURI aReferrer,
+ in unsigned long aReferrerPolicy,
+ in nsIInputStream aPostData,
+ in nsIInputStream aHeaders,
+ in nsIURI aBaseURI);
+
+ /**
+ * Tells the Object to reload the current page. There may be cases where the
+ * user will be asked to confirm the reload (for example, when it is
+ * determined that the request is non-idempotent).
+ *
+ * @param aReloadFlags
+ * Flags modifying load behaviour. This parameter is a bitwise
+ * combination of the Load Flags defined above. (Undefined bits are
+ * reserved for future use.) Generally you will pass LOAD_FLAGS_NONE
+ * for this parameter.
+ *
+ * @throw NS_BINDING_ABORTED
+ * Indicating that the user canceled the reload.
+ */
+ void reload(in unsigned long aReloadFlags);
+
+ /****************************************************************************
+ * The following flags may be passed as the stop flags parameter to the stop
+ * method defined on this interface.
+ */
+
+ /**
+ * This flag specifies that all network activity should be stopped. This
+ * includes both active network loads and pending META-refreshes.
+ */
+ const unsigned long STOP_NETWORK = 0x01;
+
+ /**
+ * This flag specifies that all content activity should be stopped. This
+ * includes animated images, plugins and pending Javascript timeouts.
+ */
+ const unsigned long STOP_CONTENT = 0x02;
+
+ /**
+ * This flag specifies that all activity should be stopped.
+ */
+ const unsigned long STOP_ALL = 0x03;
+
+ /**
+ * Stops a load of a URI.
+ *
+ * @param aStopFlags
+ * This parameter is one of the stop flags defined above.
+ */
+ void stop(in unsigned long aStopFlags);
+
+ /**
+ * Retrieves the current DOM document for the frame, or lazily creates a
+ * blank document if there is none. This attribute never returns null except
+ * for unexpected error situations.
+ */
+ readonly attribute nsIDOMDocument document;
+
+ /**
+ * The currently loaded URI or null.
+ */
+ readonly attribute nsIURI currentURI;
+
+ /**
+ * The referring URI for the currently loaded URI or null.
+ */
+ readonly attribute nsIURI referringURI;
+
+ /**
+ * The session history object used by this web navigation instance.
+ */
+ attribute nsISHistory sessionHistory;
+
+ /**
+ * Set an OriginAttributes dictionary in the docShell. This can be done only
+ * before loading any content.
+ */
+ void setOriginAttributesBeforeLoading(in jsval originAttributes);
+};
diff --git a/docshell/base/nsIWebNavigationInfo.idl b/docshell/base/nsIWebNavigationInfo.idl
new file mode 100644
index 000000000..cb6035650
--- /dev/null
+++ b/docshell/base/nsIWebNavigationInfo.idl
@@ -0,0 +1,63 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIWebNavigation;
+
+/**
+ * The nsIWebNavigationInfo interface exposes a way to get information
+ * on the capabilities of Gecko webnavigation objects.
+ */
+[scriptable, uuid(62a93afb-93a1-465c-84c8-0432264229de)]
+interface nsIWebNavigationInfo : nsISupports
+{
+ /**
+ * Returned by isTypeSupported to indicate lack of support for a type.
+ * @note this is guaranteed not to change, so that boolean tests can be done
+ * on the return value if isTypeSupported to detect whether a type is
+ * supported at all.
+ */
+ const unsigned long UNSUPPORTED = 0;
+
+ /**
+ * Returned by isTypeSupported to indicate that a type is supported as an
+ * image.
+ */
+ const unsigned long IMAGE = 1;
+
+ /**
+ * Returned by isTypeSupported to indicate that a type is supported via an
+ * NPAPI ("Netscape 4 API") plug-in. This is not the value returned for
+ * "XPCOM plug-ins".
+ */
+ const unsigned long PLUGIN = 2;
+
+ /**
+ * @note Other return types may be added here in the future as they become
+ * relevant.
+ */
+
+ /**
+ * Returned by isTypeSupported to indicate that a type is supported via some
+ * other means.
+ */
+ const unsigned long OTHER = 1 << 15;
+
+ /**
+ * Query whether aType is supported.
+ * @param aType the MIME type in question.
+ * @param aWebNav the nsIWebNavigation object for which the request
+ * is being made. This is allowed to be null. If it is non-null,
+ * the return value of this method may depend on the exact state of
+ * aWebNav and the values set through nsIWebBrowserSetup; otherwise
+ * the method will assume that the caller is interested in information
+ * about nsIWebNavigation objects in their default state.
+ * @return an enum value indicating whether and how aType is supported.
+ * @note This method may rescan plugins to ensure that they're properly
+ * registered for the types they support.
+ */
+ unsigned long isTypeSupported(in ACString aType, in nsIWebNavigation aWebNav);
+};
diff --git a/docshell/base/nsIWebPageDescriptor.idl b/docshell/base/nsIWebPageDescriptor.idl
new file mode 100644
index 000000000..96db7f34a
--- /dev/null
+++ b/docshell/base/nsIWebPageDescriptor.idl
@@ -0,0 +1,30 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "nsISupports.idl"
+
+/**
+ * The nsIWebPageDescriptor interface allows content being displayed in one
+ * window to be loaded into another window without refetching it from the
+ * network.
+ */
+
+[scriptable, uuid(6f30b676-3710-4c2c-80b1-0395fb26516e)]
+interface nsIWebPageDescriptor : nsISupports
+{
+ const unsigned long DISPLAY_AS_SOURCE = 0x0001;
+ const unsigned long DISPLAY_NORMAL = 0x0002;
+
+ /**
+ * Tells the object to load the page specified by the page descriptor
+ *
+ * @throws NS_ERROR_FAILURE -
+ */
+ void loadPage(in nsISupports aPageDescriptor, in unsigned long aDisplayType);
+
+
+ /**
+ * Retrieves the page descriptor for the curent document.
+ */
+ readonly attribute nsISupports currentDescriptor;
+};
diff --git a/docshell/base/nsIWebShellServices.h b/docshell/base/nsIWebShellServices.h
new file mode 100644
index 000000000..c67de0ff7
--- /dev/null
+++ b/docshell/base/nsIWebShellServices.h
@@ -0,0 +1,34 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef nsIWebShellServices_h___
+#define nsIWebShellServices_h___
+
+#include "nsISupports.h"
+#include "nsCharsetSource.h"
+
+/* 0c628af0-5638-4703-8f99-ed6134c9de18 */
+#define NS_IWEB_SHELL_SERVICES_IID \
+{ 0x0c628af0, 0x5638, 0x4703, {0x8f, 0x99, 0xed, 0x61, 0x34, 0xc9, 0xde, 0x18} }
+
+class nsIWebShellServices : public nsISupports
+{
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_IWEB_SHELL_SERVICES_IID)
+
+ NS_IMETHOD ReloadDocument(const char* aCharset = nullptr,
+ int32_t aSource = kCharsetUninitialized) = 0;
+ NS_IMETHOD StopDocumentLoad(void) = 0;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIWebShellServices, NS_IWEB_SHELL_SERVICES_IID)
+
+/* Use this macro when declaring classes that implement this interface. */
+#define NS_DECL_NSIWEBSHELLSERVICES \
+ NS_IMETHOD ReloadDocument(const char* aCharset = nullptr, \
+ int32_t aSource = kCharsetUninitialized) override; \
+ NS_IMETHOD StopDocumentLoad(void) override;
+
+#endif /* nsIWebShellServices_h___ */
diff --git a/docshell/base/nsWebNavigationInfo.cpp b/docshell/base/nsWebNavigationInfo.cpp
new file mode 100644
index 000000000..2d1e19665
--- /dev/null
+++ b/docshell/base/nsWebNavigationInfo.cpp
@@ -0,0 +1,134 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsWebNavigationInfo.h"
+#include "nsIWebNavigation.h"
+#include "nsServiceManagerUtils.h"
+#include "nsIDocumentLoaderFactory.h"
+#include "nsIPluginHost.h"
+#include "nsIDocShell.h"
+#include "nsContentUtils.h"
+#include "imgLoader.h"
+
+NS_IMPL_ISUPPORTS(nsWebNavigationInfo, nsIWebNavigationInfo)
+
+#define CONTENT_DLF_CONTRACT "@mozilla.org/content/document-loader-factory;1"
+#define PLUGIN_DLF_CONTRACT \
+ "@mozilla.org/content/plugin/document-loader-factory;1"
+
+nsresult
+nsWebNavigationInfo::Init()
+{
+ nsresult rv;
+ mCategoryManager = do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWebNavigationInfo::IsTypeSupported(const nsACString& aType,
+ nsIWebNavigation* aWebNav,
+ uint32_t* aIsTypeSupported)
+{
+ NS_PRECONDITION(aIsTypeSupported, "null out param?");
+
+ // Note to self: aWebNav could be an nsWebBrowser or an nsDocShell here (or
+ // an nsSHistory, but not much we can do with that). So if we start using
+ // it here, we need to be careful to get to the docshell correctly.
+
+ // For now just report what the Gecko-Content-Viewers category has
+ // to say for itself.
+ *aIsTypeSupported = nsIWebNavigationInfo::UNSUPPORTED;
+
+ // We want to claim that the type for PDF documents is unsupported,
+ // so that the internal PDF viewer's stream converted will get used.
+ if (aType.LowerCaseEqualsLiteral("application/pdf") &&
+ nsContentUtils::IsPDFJSEnabled()) {
+ return NS_OK;
+ }
+
+ // We want to claim that the type for SWF movies is unsupported,
+ // so that the internal SWF player's stream converter will get used.
+ if (aType.LowerCaseEqualsLiteral("application/x-shockwave-flash") &&
+ nsContentUtils::IsSWFPlayerEnabled()) {
+ return NS_OK;
+ }
+
+ const nsCString& flatType = PromiseFlatCString(aType);
+ nsresult rv = IsTypeSupportedInternal(flatType, aIsTypeSupported);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (*aIsTypeSupported) {
+ return rv;
+ }
+
+ // If this request is for a docShell that isn't going to allow plugins,
+ // there's no need to try and find a plugin to handle it.
+ nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(aWebNav));
+ bool allowed;
+ if (docShell &&
+ NS_SUCCEEDED(docShell->GetAllowPlugins(&allowed)) && !allowed) {
+ return NS_OK;
+ }
+
+ // Try reloading plugins in case they've changed.
+ nsCOMPtr<nsIPluginHost> pluginHost =
+ do_GetService(MOZ_PLUGIN_HOST_CONTRACTID);
+ if (pluginHost) {
+ // false will ensure that currently running plugins will not
+ // be shut down
+ rv = pluginHost->ReloadPlugins();
+ if (NS_SUCCEEDED(rv)) {
+ // OK, we reloaded plugins and there were new ones
+ // (otherwise NS_ERROR_PLUGINS_PLUGINSNOTCHANGED would have
+ // been returned). Try checking whether we can handle the
+ // content now.
+ return IsTypeSupportedInternal(flatType, aIsTypeSupported);
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsWebNavigationInfo::IsTypeSupportedInternal(const nsCString& aType,
+ uint32_t* aIsSupported)
+{
+ NS_PRECONDITION(aIsSupported, "Null out param?");
+
+ nsContentUtils::ContentViewerType vtype = nsContentUtils::TYPE_UNSUPPORTED;
+
+ nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
+ nsContentUtils::FindInternalContentViewer(aType, &vtype);
+
+ switch (vtype) {
+ case nsContentUtils::TYPE_UNSUPPORTED:
+ *aIsSupported = nsIWebNavigationInfo::UNSUPPORTED;
+ break;
+
+ case nsContentUtils::TYPE_PLUGIN:
+ *aIsSupported = nsIWebNavigationInfo::PLUGIN;
+ break;
+
+ case nsContentUtils::TYPE_UNKNOWN:
+ *aIsSupported = nsIWebNavigationInfo::OTHER;
+ break;
+
+ case nsContentUtils::TYPE_CONTENT:
+ // XXXbz we only need this because images register for the same
+ // contractid as documents, so we can't tell them apart based on
+ // contractid.
+ if (imgLoader::SupportImageWithMimeType(aType.get())) {
+ *aIsSupported = nsIWebNavigationInfo::IMAGE;
+ } else {
+ *aIsSupported = nsIWebNavigationInfo::OTHER;
+ }
+ break;
+ }
+
+ return NS_OK;
+}
diff --git a/docshell/base/nsWebNavigationInfo.h b/docshell/base/nsWebNavigationInfo.h
new file mode 100644
index 000000000..2b54156f7
--- /dev/null
+++ b/docshell/base/nsWebNavigationInfo.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsWebNavigationInfo_h__
+#define nsWebNavigationInfo_h__
+
+#include "nsIWebNavigationInfo.h"
+#include "nsCOMPtr.h"
+#include "nsICategoryManager.h"
+#include "mozilla/Attributes.h"
+
+class nsCString;
+
+#define NS_WEBNAVIGATION_INFO_CID \
+ { 0xf30bc0a2, 0x958b, 0x4287,{0xbf, 0x62, 0xce, 0x38, 0xba, 0x0c, 0x81, 0x1e}}
+
+class nsWebNavigationInfo final : public nsIWebNavigationInfo
+{
+public:
+ nsWebNavigationInfo() {}
+
+ NS_DECL_ISUPPORTS
+
+ NS_DECL_NSIWEBNAVIGATIONINFO
+
+ nsresult Init();
+
+private:
+ ~nsWebNavigationInfo() {}
+
+ // Check whether aType is supported. If this method throws, the
+ // value of aIsSupported is not changed.
+ nsresult IsTypeSupportedInternal(const nsCString& aType,
+ uint32_t* aIsSupported);
+
+ nsCOMPtr<nsICategoryManager> mCategoryManager;
+};
+
+#endif // nsWebNavigationInfo_h__
diff --git a/docshell/base/timeline/AbstractTimelineMarker.cpp b/docshell/base/timeline/AbstractTimelineMarker.cpp
new file mode 100644
index 000000000..aeeab8207
--- /dev/null
+++ b/docshell/base/timeline/AbstractTimelineMarker.cpp
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "AbstractTimelineMarker.h"
+
+#include "mozilla/TimeStamp.h"
+#include "MainThreadUtils.h"
+#include "nsAppRunner.h"
+
+namespace mozilla {
+
+AbstractTimelineMarker::AbstractTimelineMarker(const char* aName,
+ MarkerTracingType aTracingType)
+ : mName(aName)
+ , mTracingType(aTracingType)
+ , mProcessType(XRE_GetProcessType())
+ , mIsOffMainThread(!NS_IsMainThread())
+{
+ MOZ_COUNT_CTOR(AbstractTimelineMarker);
+ SetCurrentTime();
+}
+
+AbstractTimelineMarker::AbstractTimelineMarker(const char* aName,
+ const TimeStamp& aTime,
+ MarkerTracingType aTracingType)
+ : mName(aName)
+ , mTracingType(aTracingType)
+ , mProcessType(XRE_GetProcessType())
+ , mIsOffMainThread(!NS_IsMainThread())
+{
+ MOZ_COUNT_CTOR(AbstractTimelineMarker);
+ SetCustomTime(aTime);
+}
+
+UniquePtr<AbstractTimelineMarker>
+AbstractTimelineMarker::Clone()
+{
+ MOZ_ASSERT(false, "Clone method not yet implemented on this marker type.");
+ return nullptr;
+}
+
+bool
+AbstractTimelineMarker::Equals(const AbstractTimelineMarker& aOther)
+{
+ // Check whether two markers should be considered the same, for the purpose
+ // of pairing start and end markers. Normally this definition suffices.
+ return strcmp(mName, aOther.mName) == 0;
+}
+
+AbstractTimelineMarker::~AbstractTimelineMarker()
+{
+ MOZ_COUNT_DTOR(AbstractTimelineMarker);
+}
+
+void
+AbstractTimelineMarker::SetCurrentTime()
+{
+ TimeStamp now = TimeStamp::Now();
+ SetCustomTime(now);
+}
+
+void
+AbstractTimelineMarker::SetCustomTime(const TimeStamp& aTime)
+{
+ bool isInconsistent = false;
+ mTime = (aTime - TimeStamp::ProcessCreation(isInconsistent)).ToMilliseconds();
+}
+
+void
+AbstractTimelineMarker::SetCustomTime(DOMHighResTimeStamp aTime)
+{
+ mTime = aTime;
+}
+
+void
+AbstractTimelineMarker::SetProcessType(GeckoProcessType aProcessType)
+{
+ mProcessType = aProcessType;
+}
+
+void
+AbstractTimelineMarker::SetOffMainThread(bool aIsOffMainThread)
+{
+ mIsOffMainThread = aIsOffMainThread;
+}
+
+} // namespace mozilla
diff --git a/docshell/base/timeline/AbstractTimelineMarker.h b/docshell/base/timeline/AbstractTimelineMarker.h
new file mode 100644
index 000000000..516b44eb0
--- /dev/null
+++ b/docshell/base/timeline/AbstractTimelineMarker.h
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_AbstractTimelineMarker_h_
+#define mozilla_AbstractTimelineMarker_h_
+
+#include "TimelineMarkerEnums.h" // for MarkerTracingType
+#include "nsDOMNavigationTiming.h" // for DOMHighResTimeStamp
+#include "nsXULAppAPI.h" // for GeckoProcessType
+#include "mozilla/UniquePtr.h"
+
+struct JSContext;
+class JSObject;
+
+namespace mozilla {
+class TimeStamp;
+
+namespace dom {
+struct ProfileTimelineMarker;
+}
+
+class AbstractTimelineMarker
+{
+private:
+ AbstractTimelineMarker() = delete;
+ AbstractTimelineMarker(const AbstractTimelineMarker& aOther) = delete;
+ void operator=(const AbstractTimelineMarker& aOther) = delete;
+
+public:
+ AbstractTimelineMarker(const char* aName,
+ MarkerTracingType aTracingType);
+
+ AbstractTimelineMarker(const char* aName,
+ const TimeStamp& aTime,
+ MarkerTracingType aTracingType);
+
+ virtual ~AbstractTimelineMarker();
+
+ virtual UniquePtr<AbstractTimelineMarker> Clone();
+ virtual bool Equals(const AbstractTimelineMarker& aOther);
+
+ virtual void AddDetails(JSContext* aCx, dom::ProfileTimelineMarker& aMarker) = 0;
+ virtual JSObject* GetStack() = 0;
+
+ const char* GetName() const { return mName; }
+ DOMHighResTimeStamp GetTime() const { return mTime; }
+ MarkerTracingType GetTracingType() const { return mTracingType; }
+
+ uint8_t GetProcessType() const { return mProcessType; };
+ bool IsOffMainThread() const { return mIsOffMainThread; };
+
+private:
+ const char* mName;
+ DOMHighResTimeStamp mTime;
+ MarkerTracingType mTracingType;
+
+ uint8_t mProcessType; // @see `enum GeckoProcessType`.
+ bool mIsOffMainThread;
+
+protected:
+ void SetCurrentTime();
+ void SetCustomTime(const TimeStamp& aTime);
+ void SetCustomTime(DOMHighResTimeStamp aTime);
+ void SetProcessType(GeckoProcessType aProcessType);
+ void SetOffMainThread(bool aIsOffMainThread);
+};
+
+} // namespace mozilla
+
+#endif /* mozilla_AbstractTimelineMarker_h_ */
diff --git a/docshell/base/timeline/AutoGlobalTimelineMarker.cpp b/docshell/base/timeline/AutoGlobalTimelineMarker.cpp
new file mode 100644
index 000000000..44b7c750b
--- /dev/null
+++ b/docshell/base/timeline/AutoGlobalTimelineMarker.cpp
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "AutoGlobalTimelineMarker.h"
+
+#include "TimelineConsumers.h"
+#include "MainThreadUtils.h"
+
+namespace mozilla {
+
+AutoGlobalTimelineMarker::AutoGlobalTimelineMarker(const char* aName,
+ MarkerStackRequest aStackRequest /* = STACK */
+ MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
+ : mName(aName)
+ , mStackRequest(aStackRequest)
+{
+ MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+ MOZ_ASSERT(NS_IsMainThread());
+
+ RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
+ if (!timelines || timelines->IsEmpty()) {
+ return;
+ }
+
+ timelines->AddMarkerForAllObservedDocShells(mName, MarkerTracingType::START, mStackRequest);
+}
+
+AutoGlobalTimelineMarker::~AutoGlobalTimelineMarker()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
+ if (!timelines || timelines->IsEmpty()) {
+ return;
+ }
+
+ timelines->AddMarkerForAllObservedDocShells(mName, MarkerTracingType::END, mStackRequest);
+}
+
+} // namespace mozilla
diff --git a/docshell/base/timeline/AutoGlobalTimelineMarker.h b/docshell/base/timeline/AutoGlobalTimelineMarker.h
new file mode 100644
index 000000000..2937cf3d7
--- /dev/null
+++ b/docshell/base/timeline/AutoGlobalTimelineMarker.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_AutoGlobalTimelineMarker_h_
+#define mozilla_AutoGlobalTimelineMarker_h_
+
+#include "mozilla/GuardObjects.h"
+#include "TimelineMarkerEnums.h"
+
+namespace mozilla {
+
+// # AutoGlobalTimelineMarker
+//
+// Similar to `AutoTimelineMarker`, but adds its traced marker to all docshells,
+// not a single particular one. This is useful for operations that aren't
+// associated with any one particular doc shell, or when it isn't clear which
+// docshell triggered the operation.
+//
+// Example usage:
+//
+// {
+// AutoGlobalTimelineMarker marker("Cycle Collection");
+// nsCycleCollector* cc = GetCycleCollector();
+// cc->Collect();
+// ...
+// }
+class MOZ_RAII AutoGlobalTimelineMarker
+{
+ MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER;
+
+ // The name of the marker we are adding.
+ const char* mName;
+ // Whether to capture the JS stack or not.
+ MarkerStackRequest mStackRequest;
+
+public:
+ explicit AutoGlobalTimelineMarker(const char* aName,
+ MarkerStackRequest aStackRequest = MarkerStackRequest::STACK
+ MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
+ ~AutoGlobalTimelineMarker();
+
+ AutoGlobalTimelineMarker(const AutoGlobalTimelineMarker& aOther) = delete;
+ void operator=(const AutoGlobalTimelineMarker& aOther) = delete;
+};
+
+} // namespace mozilla
+
+#endif /* mozilla_AutoGlobalTimelineMarker_h_ */
diff --git a/docshell/base/timeline/AutoTimelineMarker.cpp b/docshell/base/timeline/AutoTimelineMarker.cpp
new file mode 100644
index 000000000..dbf4bd596
--- /dev/null
+++ b/docshell/base/timeline/AutoTimelineMarker.cpp
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "AutoTimelineMarker.h"
+
+#include "TimelineConsumers.h"
+#include "MainThreadUtils.h"
+
+namespace mozilla {
+
+AutoTimelineMarker::AutoTimelineMarker(nsIDocShell* aDocShell, const char* aName
+ MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
+ : mName(aName)
+ , mDocShell(nullptr)
+{
+ MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!aDocShell) {
+ return;
+ }
+
+ RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
+ if (!timelines || !timelines->HasConsumer(aDocShell)) {
+ return;
+ }
+
+ mDocShell = aDocShell;
+ timelines->AddMarkerForDocShell(mDocShell, mName, MarkerTracingType::START);
+}
+
+AutoTimelineMarker::~AutoTimelineMarker()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!mDocShell) {
+ return;
+ }
+
+ RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
+ if (!timelines || !timelines->HasConsumer(mDocShell)) {
+ return;
+ }
+
+ timelines->AddMarkerForDocShell(mDocShell, mName, MarkerTracingType::END);
+}
+
+} // namespace mozilla
diff --git a/docshell/base/timeline/AutoTimelineMarker.h b/docshell/base/timeline/AutoTimelineMarker.h
new file mode 100644
index 000000000..47bb25046
--- /dev/null
+++ b/docshell/base/timeline/AutoTimelineMarker.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_AutoTimelineMarker_h_
+#define mozilla_AutoTimelineMarker_h_
+
+#include "mozilla/GuardObjects.h"
+#include "mozilla/RefPtr.h"
+
+class nsIDocShell;
+
+namespace mozilla {
+
+// # AutoTimelineMarker
+//
+// An RAII class to trace some task in the platform by adding a start and end
+// timeline marker pair. These markers are then rendered in the devtools'
+// performance tool's waterfall graph.
+//
+// Example usage:
+//
+// {
+// AutoTimelineMarker marker(mDocShell, "Parse CSS");
+// nsresult rv = ParseTheCSSFile(mFile);
+// ...
+// }
+class MOZ_RAII AutoTimelineMarker
+{
+ MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER;
+
+ // The name of the marker we are adding.
+ const char* mName;
+
+ // The docshell that is associated with this marker.
+ RefPtr<nsIDocShell> mDocShell;
+
+public:
+ AutoTimelineMarker(nsIDocShell* aDocShell,
+ const char* aName MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
+ ~AutoTimelineMarker();
+
+ AutoTimelineMarker(const AutoTimelineMarker& aOther) = delete;
+ void operator=(const AutoTimelineMarker& aOther) = delete;
+};
+
+} // namespace mozilla
+
+#endif /* mozilla_AutoTimelineMarker_h_ */
diff --git a/docshell/base/timeline/CompositeTimelineMarker.h b/docshell/base/timeline/CompositeTimelineMarker.h
new file mode 100644
index 000000000..45faefbd6
--- /dev/null
+++ b/docshell/base/timeline/CompositeTimelineMarker.h
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_CompositeTimelineMarker_h_
+#define mozilla_CompositeTimelineMarker_h_
+
+#include "TimelineMarker.h"
+#include "mozilla/dom/ProfileTimelineMarkerBinding.h"
+
+namespace mozilla {
+
+class CompositeTimelineMarker : public TimelineMarker
+{
+public:
+ CompositeTimelineMarker(const TimeStamp& aTime,
+ MarkerTracingType aTracingType)
+ : TimelineMarker("Composite", aTime, aTracingType)
+ {
+ // Even though these markers end up being created on the main thread in the
+ // content or chrome processes, they actually trace down code in the
+ // compositor parent process. All the information for creating these markers
+ // is sent along via IPC to an nsView when a composite finishes.
+ // Mark this as 'off the main thread' to style it differently in frontends.
+ SetOffMainThread(true);
+ }
+};
+
+} // namespace mozilla
+
+#endif // mozilla_CompositeTimelineMarker_h_
diff --git a/docshell/base/timeline/ConsoleTimelineMarker.h b/docshell/base/timeline/ConsoleTimelineMarker.h
new file mode 100644
index 000000000..f397830c2
--- /dev/null
+++ b/docshell/base/timeline/ConsoleTimelineMarker.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_ConsoleTimelineMarker_h_
+#define mozilla_ConsoleTimelineMarker_h_
+
+#include "TimelineMarker.h"
+#include "mozilla/dom/ProfileTimelineMarkerBinding.h"
+
+namespace mozilla {
+
+class ConsoleTimelineMarker : public TimelineMarker
+{
+public:
+ ConsoleTimelineMarker(const nsAString& aCause,
+ MarkerTracingType aTracingType)
+ : TimelineMarker("ConsoleTime", aTracingType)
+ , mCause(aCause)
+ {
+ // Stack is captured by default on the "start" marker. Explicitly also
+ // capture stack on the "end" marker.
+ if (aTracingType == MarkerTracingType::END) {
+ CaptureStack();
+ }
+ }
+
+ virtual bool Equals(const AbstractTimelineMarker& aOther) override
+ {
+ if (!TimelineMarker::Equals(aOther)) {
+ return false;
+ }
+ // Console markers must have matching causes as well. It is safe to perform
+ // a static_cast here as the previous equality check ensures that this is
+ // a console marker instance.
+ return mCause == static_cast<const ConsoleTimelineMarker*>(&aOther)->mCause;
+ }
+
+ virtual void AddDetails(JSContext* aCx, dom::ProfileTimelineMarker& aMarker) override
+ {
+ TimelineMarker::AddDetails(aCx, aMarker);
+
+ if (GetTracingType() == MarkerTracingType::START) {
+ aMarker.mCauseName.Construct(mCause);
+ } else {
+ aMarker.mEndStack = GetStack();
+ }
+ }
+
+private:
+ nsString mCause;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_ConsoleTimelineMarker_h_
diff --git a/docshell/base/timeline/DocLoadingTimelineMarker.h b/docshell/base/timeline/DocLoadingTimelineMarker.h
new file mode 100644
index 000000000..af2ac17fd
--- /dev/null
+++ b/docshell/base/timeline/DocLoadingTimelineMarker.h
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_DocLoadingTimelineMarker_h_
+#define mozilla_DocLoadingTimelineMarker_h_
+
+#include "TimelineMarker.h"
+#include "mozilla/dom/ProfileTimelineMarkerBinding.h"
+
+namespace mozilla {
+
+class DocLoadingTimelineMarker : public TimelineMarker
+{
+public:
+ explicit DocLoadingTimelineMarker(const char* aName)
+ : TimelineMarker(aName, MarkerTracingType::TIMESTAMP)
+ , mUnixTime(PR_Now())
+ {}
+
+ virtual void AddDetails(JSContext* aCx, dom::ProfileTimelineMarker& aMarker) override
+ {
+ TimelineMarker::AddDetails(aCx, aMarker);
+ aMarker.mUnixTime.Construct(mUnixTime);
+ }
+
+private:
+ // Certain consumers might use Date.now() or similar for tracing time.
+ // However, TimelineMarkers use process creation as an epoch, which provides
+ // more precision. To allow syncing, attach an additional unix timestamp.
+ // Using this instead of `AbstractTimelineMarker::GetTime()'s` timestamp
+ // is strongly discouraged.
+ PRTime mUnixTime;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_DocLoadingTimelineMarker_h_
diff --git a/docshell/base/timeline/EventTimelineMarker.h b/docshell/base/timeline/EventTimelineMarker.h
new file mode 100644
index 000000000..095bc528e
--- /dev/null
+++ b/docshell/base/timeline/EventTimelineMarker.h
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_EventTimelineMarker_h_
+#define mozilla_EventTimelineMarker_h_
+
+#include "TimelineMarker.h"
+#include "mozilla/dom/ProfileTimelineMarkerBinding.h"
+
+namespace mozilla {
+
+class EventTimelineMarker : public TimelineMarker
+{
+public:
+ EventTimelineMarker(const nsAString& aType,
+ uint16_t aPhase,
+ MarkerTracingType aTracingType)
+ : TimelineMarker("DOMEvent", aTracingType)
+ , mType(aType)
+ , mPhase(aPhase)
+ {}
+
+ virtual void AddDetails(JSContext* aCx, dom::ProfileTimelineMarker& aMarker) override
+ {
+ TimelineMarker::AddDetails(aCx, aMarker);
+
+ if (GetTracingType() == MarkerTracingType::START) {
+ aMarker.mType.Construct(mType);
+ aMarker.mEventPhase.Construct(mPhase);
+ }
+ }
+
+private:
+ nsString mType;
+ uint16_t mPhase;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_EventTimelineMarker_h_
diff --git a/docshell/base/timeline/JavascriptTimelineMarker.h b/docshell/base/timeline/JavascriptTimelineMarker.h
new file mode 100644
index 000000000..5a9924283
--- /dev/null
+++ b/docshell/base/timeline/JavascriptTimelineMarker.h
@@ -0,0 +1,95 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_JavascriptTimelineMarker_h_
+#define mozilla_JavascriptTimelineMarker_h_
+
+#include "TimelineMarker.h"
+#include "mozilla/dom/ProfileTimelineMarkerBinding.h"
+#include "mozilla/dom/RootedDictionary.h"
+#include "mozilla/dom/ToJSValue.h"
+
+namespace mozilla {
+
+class JavascriptTimelineMarker : public TimelineMarker
+{
+public:
+ // The caller owns |aAsyncCause| here, so we must copy it into a separate
+ // string for use later on.
+ JavascriptTimelineMarker(const char* aReason,
+ const char16_t* aFunctionName,
+ const char16_t* aFileName,
+ uint32_t aLineNumber,
+ MarkerTracingType aTracingType,
+ JS::Handle<JS::Value> aAsyncStack,
+ const char* aAsyncCause)
+ : TimelineMarker("Javascript", aTracingType, MarkerStackRequest::NO_STACK)
+ , mCause(NS_ConvertUTF8toUTF16(aReason))
+ , mFunctionName(aFunctionName)
+ , mFileName(aFileName)
+ , mLineNumber(aLineNumber)
+ , mAsyncCause(aAsyncCause)
+ {
+ JSContext* ctx = nsContentUtils::GetCurrentJSContext();
+ if (ctx) {
+ mAsyncStack.init(ctx, aAsyncStack);
+ }
+ }
+
+ virtual void AddDetails(JSContext* aCx, dom::ProfileTimelineMarker& aMarker) override
+ {
+ TimelineMarker::AddDetails(aCx, aMarker);
+
+ aMarker.mCauseName.Construct(mCause);
+
+ if (!mFunctionName.IsEmpty() || !mFileName.IsEmpty()) {
+ dom::RootedDictionary<dom::ProfileTimelineStackFrame> stackFrame(aCx);
+ stackFrame.mLine.Construct(mLineNumber);
+ stackFrame.mSource.Construct(mFileName);
+ stackFrame.mFunctionDisplayName.Construct(mFunctionName);
+
+ if (mAsyncStack.isObject() && !mAsyncStack.isNullOrUndefined() &&
+ !mAsyncCause.IsEmpty()) {
+ JS::Rooted<JSObject*> asyncStack(aCx, mAsyncStack.toObjectOrNull());
+ JS::Rooted<JSObject*> parentFrame(aCx);
+ JS::Rooted<JSString*> asyncCause(aCx, JS_NewUCStringCopyN(aCx, mAsyncCause.BeginReading(),
+ mAsyncCause.Length()));
+ if (!asyncCause) {
+ JS_ClearPendingException(aCx);
+ return;
+ }
+
+ if (JS::IsSavedFrame(asyncStack) &&
+ !JS::CopyAsyncStack(aCx, asyncStack, asyncCause, &parentFrame, 0)) {
+ JS_ClearPendingException(aCx);
+ } else {
+ stackFrame.mAsyncParent = parentFrame;
+ }
+ }
+
+ JS::Rooted<JS::Value> newStack(aCx);
+ if (ToJSValue(aCx, stackFrame, &newStack)) {
+ if (newStack.isObject()) {
+ aMarker.mStack = &newStack.toObject();
+ }
+ } else {
+ JS_ClearPendingException(aCx);
+ }
+ }
+ }
+
+private:
+ nsString mCause;
+ nsString mFunctionName;
+ nsString mFileName;
+ uint32_t mLineNumber;
+ JS::PersistentRooted<JS::Value> mAsyncStack;
+ NS_ConvertUTF8toUTF16 mAsyncCause;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_JavascriptTimelineMarker_h_
diff --git a/docshell/base/timeline/LayerTimelineMarker.h b/docshell/base/timeline/LayerTimelineMarker.h
new file mode 100644
index 000000000..8293f3558
--- /dev/null
+++ b/docshell/base/timeline/LayerTimelineMarker.h
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_LayerTimelineMarker_h_
+#define mozilla_LayerTimelineMarker_h_
+
+#include "TimelineMarker.h"
+#include "mozilla/dom/ProfileTimelineMarkerBinding.h"
+#include "nsRegion.h"
+
+namespace mozilla {
+
+class LayerTimelineMarker : public TimelineMarker
+{
+public:
+ explicit LayerTimelineMarker(const nsIntRegion& aRegion)
+ : TimelineMarker("Layer", MarkerTracingType::HELPER_EVENT)
+ , mRegion(aRegion)
+ {}
+
+ void AddLayerRectangles(dom::Sequence<dom::ProfileTimelineLayerRect>& aRectangles)
+ {
+ for (auto iter = mRegion.RectIter(); !iter.Done(); iter.Next()) {
+ const nsIntRect& iterRect = iter.Get();
+ dom::ProfileTimelineLayerRect rect;
+ rect.mX = iterRect.X();
+ rect.mY = iterRect.Y();
+ rect.mWidth = iterRect.Width();
+ rect.mHeight = iterRect.Height();
+ aRectangles.AppendElement(rect, fallible);
+ }
+ }
+
+private:
+ nsIntRegion mRegion;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_LayerTimelineMarker_h_
diff --git a/docshell/base/timeline/MarkersStorage.cpp b/docshell/base/timeline/MarkersStorage.cpp
new file mode 100644
index 000000000..cc56998fb
--- /dev/null
+++ b/docshell/base/timeline/MarkersStorage.cpp
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "MarkersStorage.h"
+#include "MainThreadUtils.h"
+
+namespace mozilla {
+
+MarkersStorage::MarkersStorage(const char* aMutexName)
+ : mLock(aMutexName)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+}
+
+MarkersStorage::~MarkersStorage()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+}
+
+Mutex&
+MarkersStorage::GetLock()
+{
+ return mLock;
+}
+
+} // namespace mozilla
diff --git a/docshell/base/timeline/MarkersStorage.h b/docshell/base/timeline/MarkersStorage.h
new file mode 100644
index 000000000..8728ea586
--- /dev/null
+++ b/docshell/base/timeline/MarkersStorage.h
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_MarkersStorage_h_
+#define mozilla_MarkersStorage_h_
+
+#include "TimelineMarkerEnums.h" // for MarkerReleaseRequest
+#include "mozilla/Mutex.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/LinkedList.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+class AbstractTimelineMarker;
+
+namespace dom {
+struct ProfileTimelineMarker;
+}
+
+class MarkersStorage : public LinkedListElement<MarkersStorage>
+{
+private:
+ MarkersStorage() = delete;
+ MarkersStorage(const MarkersStorage& aOther) = delete;
+ void operator=(const MarkersStorage& aOther) = delete;
+
+public:
+ explicit MarkersStorage(const char* aMutexName);
+ virtual ~MarkersStorage();
+
+ virtual void AddMarker(UniquePtr<AbstractTimelineMarker>&& aMarker) = 0;
+ virtual void AddOTMTMarker(UniquePtr<AbstractTimelineMarker>&& aMarker) = 0;
+ virtual void ClearMarkers() = 0;
+ virtual void PopMarkers(JSContext* aCx, nsTArray<dom::ProfileTimelineMarker>& aStore) = 0;
+
+protected:
+ Mutex& GetLock();
+
+private:
+ Mutex mLock;
+};
+
+} // namespace mozilla
+
+#endif /* mozilla_MarkersStorage_h_ */
diff --git a/docshell/base/timeline/MessagePortTimelineMarker.h b/docshell/base/timeline/MessagePortTimelineMarker.h
new file mode 100644
index 000000000..f578cf128
--- /dev/null
+++ b/docshell/base/timeline/MessagePortTimelineMarker.h
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_MessagePortTimelineMarker_h_
+#define mozilla_MessagePortTimelineMarker_h_
+
+#include "TimelineMarker.h"
+#include "mozilla/dom/ProfileTimelineMarkerBinding.h"
+
+namespace mozilla {
+
+class MessagePortTimelineMarker : public TimelineMarker
+{
+public:
+ MessagePortTimelineMarker(dom::ProfileTimelineMessagePortOperationType aOperationType,
+ MarkerTracingType aTracingType)
+ : TimelineMarker("MessagePort", aTracingType, MarkerStackRequest::NO_STACK)
+ , mOperationType(aOperationType)
+ {}
+
+ virtual UniquePtr<AbstractTimelineMarker> Clone() override
+ {
+ MessagePortTimelineMarker* clone =
+ new MessagePortTimelineMarker(mOperationType, GetTracingType());
+ clone->SetCustomTime(GetTime());
+ return UniquePtr<AbstractTimelineMarker>(clone);
+ }
+
+ virtual void AddDetails(JSContext* aCx, dom::ProfileTimelineMarker& aMarker) override
+ {
+ TimelineMarker::AddDetails(aCx, aMarker);
+
+ if (GetTracingType() == MarkerTracingType::START) {
+ aMarker.mMessagePortOperation.Construct(mOperationType);
+ }
+ }
+
+private:
+ dom::ProfileTimelineMessagePortOperationType mOperationType;
+};
+
+} // namespace mozilla
+
+#endif /* mozilla_MessagePortTimelineMarker_h_ */
diff --git a/docshell/base/timeline/ObservedDocShell.cpp b/docshell/base/timeline/ObservedDocShell.cpp
new file mode 100644
index 000000000..b394f37b3
--- /dev/null
+++ b/docshell/base/timeline/ObservedDocShell.cpp
@@ -0,0 +1,171 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ObservedDocShell.h"
+
+#include "AbstractTimelineMarker.h"
+#include "LayerTimelineMarker.h"
+#include "MainThreadUtils.h"
+#include "mozilla/Move.h"
+#include "mozilla/AutoRestore.h"
+
+namespace mozilla {
+
+ObservedDocShell::ObservedDocShell(nsIDocShell* aDocShell)
+ : MarkersStorage("ObservedDocShellMutex")
+ , mDocShell(aDocShell)
+ , mPopping(false)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+}
+
+void
+ObservedDocShell::AddMarker(UniquePtr<AbstractTimelineMarker>&& aMarker)
+{
+ // Only allow main thread markers to go into this list. No need to lock
+ // here since `mTimelineMarkers` will only be accessed or modified on the
+ // main thread only.
+ MOZ_ASSERT(NS_IsMainThread());
+ // Don't accept any markers generated by the process of popping
+ // markers.
+ if (!mPopping) {
+ mTimelineMarkers.AppendElement(Move(aMarker));
+ }
+}
+
+void
+ObservedDocShell::AddOTMTMarker(UniquePtr<AbstractTimelineMarker>&& aMarker)
+{
+ // Only allow off the main thread markers to go into this list. Since most
+ // of our markers come from the main thread, be a little more efficient and
+ // avoid dealing with multithreading scenarios until all the markers are
+ // actually cleared or popped in `ClearMarkers` or `PopMarkers`.
+ MOZ_ASSERT(!NS_IsMainThread());
+ MutexAutoLock lock(GetLock()); // for `mOffTheMainThreadTimelineMarkers`.
+ mOffTheMainThreadTimelineMarkers.AppendElement(Move(aMarker));
+}
+
+void
+ObservedDocShell::ClearMarkers()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MutexAutoLock lock(GetLock()); // for `mOffTheMainThreadTimelineMarkers`.
+ mTimelineMarkers.Clear();
+ mOffTheMainThreadTimelineMarkers.Clear();
+}
+
+void
+ObservedDocShell::PopMarkers(JSContext* aCx,
+ nsTArray<dom::ProfileTimelineMarker>& aStore)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MutexAutoLock lock(GetLock()); // for `mOffTheMainThreadTimelineMarkers`.
+
+ MOZ_RELEASE_ASSERT(!mPopping);
+ AutoRestore<bool> resetPopping(mPopping);
+ mPopping = true;
+
+ // First, move all of our markers into a single array. We'll chose
+ // the `mTimelineMarkers` store because that's where we expect most of
+ // our markers to be.
+ mTimelineMarkers.AppendElements(Move(mOffTheMainThreadTimelineMarkers));
+
+ // If we see an unpaired START, we keep it around for the next call
+ // to ObservedDocShell::PopMarkers. We store the kept START objects here.
+ nsTArray<UniquePtr<AbstractTimelineMarker>> keptStartMarkers;
+
+ for (uint32_t i = 0; i < mTimelineMarkers.Length(); ++i) {
+ UniquePtr<AbstractTimelineMarker>& startPayload = mTimelineMarkers.ElementAt(i);
+
+ // If this is a TIMESTAMP marker, there's no corresponding END,
+ // as it's a single unit of time, not a duration.
+ if (startPayload->GetTracingType() == MarkerTracingType::TIMESTAMP) {
+ dom::ProfileTimelineMarker* marker = aStore.AppendElement();
+ marker->mName = NS_ConvertUTF8toUTF16(startPayload->GetName());
+ marker->mStart = startPayload->GetTime();
+ marker->mEnd = startPayload->GetTime();
+ marker->mStack = startPayload->GetStack();
+ startPayload->AddDetails(aCx, *marker);
+ continue;
+ }
+
+ // Whenever a START marker is found, look for the corresponding END
+ // and build a {name,start,end} JS object.
+ if (startPayload->GetTracingType() == MarkerTracingType::START) {
+ bool hasSeenEnd = false;
+
+ // "Paint" markers are different because painting is handled at root
+ // docshell level. The information that a paint was done is stored at
+ // sub-docshell level, but we can only be sure that a paint did actually
+ // happen in if a "Layer" marker was recorded too.
+ bool startIsPaintType = strcmp(startPayload->GetName(), "Paint") == 0;
+ bool hasSeenLayerType = false;
+
+ // If we are processing a "Paint" marker, we append information from
+ // all the embedded "Layer" markers to this array.
+ dom::Sequence<dom::ProfileTimelineLayerRect> layerRectangles;
+
+ // DOM events can be nested, so we must take care when searching
+ // for the matching end. It doesn't hurt to apply this logic to
+ // all event types.
+ uint32_t markerDepth = 0;
+
+ // The assumption is that the devtools timeline flushes markers frequently
+ // enough for the amount of markers to always be small enough that the
+ // nested for loop isn't going to be a performance problem.
+ for (uint32_t j = i + 1; j < mTimelineMarkers.Length(); ++j) {
+ UniquePtr<AbstractTimelineMarker>& endPayload = mTimelineMarkers.ElementAt(j);
+ bool endIsLayerType = strcmp(endPayload->GetName(), "Layer") == 0;
+
+ // Look for "Layer" markers to stream out "Paint" markers.
+ if (startIsPaintType && endIsLayerType) {
+ AbstractTimelineMarker* raw = endPayload.get();
+ LayerTimelineMarker* layerPayload = static_cast<LayerTimelineMarker*>(raw);
+ layerPayload->AddLayerRectangles(layerRectangles);
+ hasSeenLayerType = true;
+ }
+ if (!startPayload->Equals(*endPayload)) {
+ continue;
+ }
+ if (endPayload->GetTracingType() == MarkerTracingType::START) {
+ ++markerDepth;
+ continue;
+ }
+ if (endPayload->GetTracingType() == MarkerTracingType::END) {
+ if (markerDepth > 0) {
+ --markerDepth;
+ continue;
+ }
+ if (!startIsPaintType || (startIsPaintType && hasSeenLayerType)) {
+ dom::ProfileTimelineMarker* marker = aStore.AppendElement();
+ marker->mName = NS_ConvertUTF8toUTF16(startPayload->GetName());
+ marker->mStart = startPayload->GetTime();
+ marker->mEnd = endPayload->GetTime();
+ marker->mStack = startPayload->GetStack();
+ if (hasSeenLayerType) {
+ marker->mRectangles.Construct(layerRectangles);
+ }
+ startPayload->AddDetails(aCx, *marker);
+ endPayload->AddDetails(aCx, *marker);
+ }
+ hasSeenEnd = true;
+ break;
+ }
+ }
+
+ // If we did not see the corresponding END, keep the START.
+ if (!hasSeenEnd) {
+ keptStartMarkers.AppendElement(Move(mTimelineMarkers.ElementAt(i)));
+ mTimelineMarkers.RemoveElementAt(i);
+ --i;
+ }
+ }
+ }
+
+ mTimelineMarkers.SwapElements(keptStartMarkers);
+}
+
+} // namespace mozilla
diff --git a/docshell/base/timeline/ObservedDocShell.h b/docshell/base/timeline/ObservedDocShell.h
new file mode 100644
index 000000000..05eafbd0c
--- /dev/null
+++ b/docshell/base/timeline/ObservedDocShell.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_ObservedDocShell_h_
+#define mozilla_ObservedDocShell_h_
+
+#include "MarkersStorage.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
+#include "nsTArray.h"
+
+class nsIDocShell;
+
+namespace mozilla {
+class AbstractTimelineMarker;
+
+namespace dom {
+struct ProfileTimelineMarker;
+}
+
+// # ObservedDocShell
+//
+// A wrapper around a docshell for which docshell-specific markers are
+// allowed to exist. See TimelineConsumers for register/unregister logic.
+class ObservedDocShell : public MarkersStorage
+{
+private:
+ RefPtr<nsIDocShell> mDocShell;
+
+ // Main thread only.
+ nsTArray<UniquePtr<AbstractTimelineMarker>> mTimelineMarkers;
+ bool mPopping;
+
+ // Off the main thread only.
+ nsTArray<UniquePtr<AbstractTimelineMarker>> mOffTheMainThreadTimelineMarkers;
+
+public:
+ explicit ObservedDocShell(nsIDocShell* aDocShell);
+ nsIDocShell* operator*() const { return mDocShell.get(); }
+
+ void AddMarker(UniquePtr<AbstractTimelineMarker>&& aMarker) override;
+ void AddOTMTMarker(UniquePtr<AbstractTimelineMarker>&& aMarker) override;
+ void ClearMarkers() override;
+ void PopMarkers(JSContext* aCx, nsTArray<dom::ProfileTimelineMarker>& aStore) override;
+};
+
+} // namespace mozilla
+
+#endif /* mozilla_ObservedDocShell_h_ */
diff --git a/docshell/base/timeline/RestyleTimelineMarker.h b/docshell/base/timeline/RestyleTimelineMarker.h
new file mode 100644
index 000000000..71055b08e
--- /dev/null
+++ b/docshell/base/timeline/RestyleTimelineMarker.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_RestyleTimelineMarker_h_
+#define mozilla_RestyleTimelineMarker_h_
+
+#include "TimelineMarker.h"
+#include "mozilla/dom/ProfileTimelineMarkerBinding.h"
+
+namespace mozilla {
+
+class RestyleTimelineMarker : public TimelineMarker
+{
+public:
+ RestyleTimelineMarker(nsRestyleHint aRestyleHint,
+ MarkerTracingType aTracingType)
+ : TimelineMarker("Styles", aTracingType)
+ {
+ if (aRestyleHint) {
+ mRestyleHint.AssignWithConversion(RestyleManager::RestyleHintToString(aRestyleHint));
+ }
+ }
+
+ virtual void AddDetails(JSContext* aCx, dom::ProfileTimelineMarker& aMarker) override
+ {
+ TimelineMarker::AddDetails(aCx, aMarker);
+
+ if (GetTracingType() == MarkerTracingType::START) {
+ aMarker.mRestyleHint.Construct(mRestyleHint);
+ }
+ }
+
+private:
+ nsString mRestyleHint;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_RestyleTimelineMarker_h_
diff --git a/docshell/base/timeline/TimelineConsumers.cpp b/docshell/base/timeline/TimelineConsumers.cpp
new file mode 100644
index 000000000..2668cdc9b
--- /dev/null
+++ b/docshell/base/timeline/TimelineConsumers.cpp
@@ -0,0 +1,312 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TimelineConsumers.h"
+
+#include "mozilla/ClearOnShutdown.h"
+#include "nsAppRunner.h" // for XRE_IsContentProcess, XRE_IsParentProcess
+#include "nsDocShell.h"
+
+namespace mozilla {
+
+NS_IMPL_ISUPPORTS(TimelineConsumers, nsIObserver);
+
+StaticMutex TimelineConsumers::sMutex;
+
+// Manually manage this singleton's lifetime and destroy it before shutdown.
+// This avoids the leakchecker detecting false-positive memory leaks when
+// using automatic memory management (i.e. statically instantiating this
+// singleton inside the `Get` method), which would automatically destroy it on
+// application shutdown, but too late for the leakchecker. Sigh...
+StaticRefPtr<TimelineConsumers> TimelineConsumers::sInstance;
+
+// This flag makes sure the singleton never gets instantiated while a shutdown
+// is in progress. This can actually happen, and `ClearOnShutdown` doesn't work
+// in these cases.
+bool TimelineConsumers::sInShutdown = false;
+
+already_AddRefed<TimelineConsumers>
+TimelineConsumers::Get()
+{
+ // Using this class is not supported yet for other processes other than
+ // parent or content. To avoid accidental checks to methods like `IsEmpty`,
+ // which would probably always be true in those cases, assert here.
+ // Remember, there will be different singletons available to each process.
+ MOZ_ASSERT(XRE_IsContentProcess() || XRE_IsParentProcess());
+
+ // If we are shutting down, don't bother doing anything. Note: we can only
+ // know whether or not we're in shutdown if we're instantiated.
+ if (sInShutdown) {
+ return nullptr;
+ }
+
+ // Note: We don't simply check `sInstance` for null-ness here, since otherwise
+ // this can resurrect the TimelineConsumers pretty late during shutdown.
+ // We won't know if we're in shutdown or not though, because the singleton
+ // could have been destroyed or just never instantiated, so in the previous
+ // conditional `sInShutdown` would be false.
+ static bool firstTime = true;
+ if (firstTime) {
+ firstTime = false;
+
+ StaticMutexAutoLock lock(sMutex);
+ sInstance = new TimelineConsumers();
+
+ // Make sure the initialization actually suceeds, otherwise don't allow
+ // access by destroying the instance immediately.
+ if (sInstance->Init()) {
+ ClearOnShutdown(&sInstance);
+ } else {
+ sInstance->RemoveObservers();
+ sInstance = nullptr;
+ }
+ }
+
+ RefPtr<TimelineConsumers> copy = sInstance.get();
+ return copy.forget();
+}
+
+bool
+TimelineConsumers::Init()
+{
+ nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+ if (!obs) {
+ return false;
+ }
+ if (NS_WARN_IF(NS_FAILED(
+ obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false)))) {
+ return false;
+ }
+ return true;
+}
+
+bool
+TimelineConsumers::RemoveObservers()
+{
+ nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+ if (!obs) {
+ return false;
+ }
+ if (NS_WARN_IF(NS_FAILED(
+ obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID)))) {
+ return false;
+ }
+ return true;
+}
+
+nsresult
+TimelineConsumers::Observe(nsISupports* aSubject,
+ const char* aTopic,
+ const char16_t* aData)
+{
+ if (!nsCRT::strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
+ sInShutdown = true;
+ RemoveObservers();
+ return NS_OK;
+ }
+
+ MOZ_ASSERT(false, "TimelineConsumers got unexpected topic!");
+ return NS_ERROR_UNEXPECTED;
+}
+
+TimelineConsumers::TimelineConsumers()
+ : mActiveConsumers(0)
+{
+}
+
+void
+TimelineConsumers::AddConsumer(nsDocShell* aDocShell)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ StaticMutexAutoLock lock(sMutex); // for `mActiveConsumers` and `mMarkersStores`.
+
+ UniquePtr<ObservedDocShell>& observed = aDocShell->mObserved;
+ MOZ_ASSERT(!observed);
+
+ mActiveConsumers++;
+
+ ObservedDocShell* obsDocShell = new ObservedDocShell(aDocShell);
+ MarkersStorage* storage = static_cast<MarkersStorage*>(obsDocShell);
+
+ observed.reset(obsDocShell);
+ mMarkersStores.insertFront(storage);
+}
+
+void
+TimelineConsumers::RemoveConsumer(nsDocShell* aDocShell)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ StaticMutexAutoLock lock(sMutex); // for `mActiveConsumers` and `mMarkersStores`.
+
+ UniquePtr<ObservedDocShell>& observed = aDocShell->mObserved;
+ MOZ_ASSERT(observed);
+
+ mActiveConsumers--;
+
+ // Clear all markers from the `mTimelineMarkers` store.
+ observed.get()->ClearMarkers();
+ // Remove self from the `mMarkersStores` store.
+ observed.get()->remove();
+ // Prepare for becoming a consumer later.
+ observed.reset(nullptr);
+}
+
+bool
+TimelineConsumers::HasConsumer(nsIDocShell* aDocShell)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ return aDocShell
+ ? aDocShell->GetRecordProfileTimelineMarkers()
+ : false;
+}
+
+bool
+TimelineConsumers::IsEmpty()
+{
+ StaticMutexAutoLock lock(sMutex); // for `mActiveConsumers`.
+ return mActiveConsumers == 0;
+}
+
+void
+TimelineConsumers::AddMarkerForDocShell(nsDocShell* aDocShell,
+ const char* aName,
+ MarkerTracingType aTracingType,
+ MarkerStackRequest aStackRequest)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ if (HasConsumer(aDocShell)) {
+ aDocShell->mObserved->AddMarker(Move(MakeUnique<TimelineMarker>(aName, aTracingType, aStackRequest)));
+ }
+}
+
+void
+TimelineConsumers::AddMarkerForDocShell(nsDocShell* aDocShell,
+ const char* aName,
+ const TimeStamp& aTime,
+ MarkerTracingType aTracingType,
+ MarkerStackRequest aStackRequest)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ if (HasConsumer(aDocShell)) {
+ aDocShell->mObserved->AddMarker(Move(MakeUnique<TimelineMarker>(aName, aTime, aTracingType, aStackRequest)));
+ }
+}
+
+void
+TimelineConsumers::AddMarkerForDocShell(nsDocShell* aDocShell,
+ UniquePtr<AbstractTimelineMarker>&& aMarker)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ if (HasConsumer(aDocShell)) {
+ aDocShell->mObserved->AddMarker(Move(aMarker));
+ }
+}
+
+void
+TimelineConsumers::AddMarkerForDocShell(nsIDocShell* aDocShell,
+ const char* aName,
+ MarkerTracingType aTracingType,
+ MarkerStackRequest aStackRequest)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ AddMarkerForDocShell(static_cast<nsDocShell*>(aDocShell), aName, aTracingType, aStackRequest);
+}
+
+void
+TimelineConsumers::AddMarkerForDocShell(nsIDocShell* aDocShell,
+ const char* aName,
+ const TimeStamp& aTime,
+ MarkerTracingType aTracingType,
+ MarkerStackRequest aStackRequest)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ AddMarkerForDocShell(static_cast<nsDocShell*>(aDocShell), aName, aTime, aTracingType, aStackRequest);
+}
+
+void
+TimelineConsumers::AddMarkerForDocShell(nsIDocShell* aDocShell,
+ UniquePtr<AbstractTimelineMarker>&& aMarker)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ AddMarkerForDocShell(static_cast<nsDocShell*>(aDocShell), Move(aMarker));
+}
+
+void
+TimelineConsumers::AddMarkerForAllObservedDocShells(const char* aName,
+ MarkerTracingType aTracingType,
+ MarkerStackRequest aStackRequest /* = STACK */)
+{
+ bool isMainThread = NS_IsMainThread();
+ StaticMutexAutoLock lock(sMutex); // for `mMarkersStores`.
+
+ for (MarkersStorage* storage = mMarkersStores.getFirst();
+ storage != nullptr;
+ storage = storage->getNext()) {
+ UniquePtr<AbstractTimelineMarker> marker =
+ MakeUnique<TimelineMarker>(aName, aTracingType, aStackRequest);
+ if (isMainThread) {
+ storage->AddMarker(Move(marker));
+ } else {
+ storage->AddOTMTMarker(Move(marker));
+ }
+ }
+}
+
+void
+TimelineConsumers::AddMarkerForAllObservedDocShells(const char* aName,
+ const TimeStamp& aTime,
+ MarkerTracingType aTracingType,
+ MarkerStackRequest aStackRequest /* = STACK */)
+{
+ bool isMainThread = NS_IsMainThread();
+ StaticMutexAutoLock lock(sMutex); // for `mMarkersStores`.
+
+ for (MarkersStorage* storage = mMarkersStores.getFirst();
+ storage != nullptr;
+ storage = storage->getNext()) {
+ UniquePtr<AbstractTimelineMarker> marker =
+ MakeUnique<TimelineMarker>(aName, aTime, aTracingType, aStackRequest);
+ if (isMainThread) {
+ storage->AddMarker(Move(marker));
+ } else {
+ storage->AddOTMTMarker(Move(marker));
+ }
+ }
+}
+
+void
+TimelineConsumers::AddMarkerForAllObservedDocShells(UniquePtr<AbstractTimelineMarker>& aMarker)
+{
+ bool isMainThread = NS_IsMainThread();
+ StaticMutexAutoLock lock(sMutex); // for `mMarkersStores`.
+
+ for (MarkersStorage* storage = mMarkersStores.getFirst();
+ storage != nullptr;
+ storage = storage->getNext()) {
+ UniquePtr<AbstractTimelineMarker> clone = aMarker->Clone();
+ if (isMainThread) {
+ storage->AddMarker(Move(clone));
+ } else {
+ storage->AddOTMTMarker(Move(clone));
+ }
+ }
+}
+
+void
+TimelineConsumers::PopMarkers(nsDocShell* aDocShell,
+ JSContext* aCx,
+ nsTArray<dom::ProfileTimelineMarker>& aStore)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!aDocShell || !aDocShell->mObserved) {
+ return;
+ }
+
+ aDocShell->mObserved->PopMarkers(aCx, aStore);
+}
+
+} // namespace mozilla
diff --git a/docshell/base/timeline/TimelineConsumers.h b/docshell/base/timeline/TimelineConsumers.h
new file mode 100644
index 000000000..04099e1b9
--- /dev/null
+++ b/docshell/base/timeline/TimelineConsumers.h
@@ -0,0 +1,135 @@
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_TimelineConsumers_h_
+#define mozilla_TimelineConsumers_h_
+
+#include "nsIObserver.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/LinkedList.h"
+#include "mozilla/StaticMutex.h"
+#include "TimelineMarkerEnums.h" // for MarkerTracingType
+
+class nsDocShell;
+class nsIDocShell;
+struct JSContext;
+
+namespace mozilla {
+class TimeStamp;
+class MarkersStorage;
+class AbstractTimelineMarker;
+
+namespace dom {
+struct ProfileTimelineMarker;
+}
+
+class TimelineConsumers : public nsIObserver
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+private:
+ TimelineConsumers();
+ TimelineConsumers(const TimelineConsumers& aOther) = delete;
+ void operator=(const TimelineConsumers& aOther) = delete;
+ virtual ~TimelineConsumers() = default;
+
+ bool Init();
+ bool RemoveObservers();
+
+public:
+ static already_AddRefed<TimelineConsumers> Get();
+
+ // Methods for registering interested consumers (i.e. "devtools toolboxes").
+ // Each consumer should be directly focused on a particular docshell, but
+ // timeline markers don't necessarily have to be tied to that docshell.
+ // See the public `AddMarker*` methods below.
+ // Main thread only.
+ void AddConsumer(nsDocShell* aDocShell);
+ void RemoveConsumer(nsDocShell* aDocShell);
+
+ bool HasConsumer(nsIDocShell* aDocShell);
+
+ // Checks if there's any existing interested consumer.
+ // May be called from any thread.
+ bool IsEmpty();
+
+ // Methods for adding markers relevant for particular docshells, or generic
+ // (meaning that they either can't be tied to a particular docshell, or one
+ // wasn't accessible in the part of the codebase where they're instantiated).
+ // These will only add markers if at least one docshell is currently being
+ // observed by a timeline. Markers tied to a particular docshell won't be
+ // created unless that docshell is specifically being currently observed.
+ // See nsIDocShell::recordProfileTimelineMarkers
+
+ // These methods create a basic TimelineMarker from a name and some metadata,
+ // relevant for a specific docshell.
+ // Main thread only.
+ void AddMarkerForDocShell(nsDocShell* aDocShell,
+ const char* aName,
+ MarkerTracingType aTracingType,
+ MarkerStackRequest aStackRequest = MarkerStackRequest::STACK);
+ void AddMarkerForDocShell(nsIDocShell* aDocShell,
+ const char* aName,
+ MarkerTracingType aTracingType,
+ MarkerStackRequest aStackRequest = MarkerStackRequest::STACK);
+
+ void AddMarkerForDocShell(nsDocShell* aDocShell,
+ const char* aName,
+ const TimeStamp& aTime,
+ MarkerTracingType aTracingType,
+ MarkerStackRequest aStackRequest = MarkerStackRequest::STACK);
+ void AddMarkerForDocShell(nsIDocShell* aDocShell,
+ const char* aName,
+ const TimeStamp& aTime,
+ MarkerTracingType aTracingType,
+ MarkerStackRequest aStackRequest = MarkerStackRequest::STACK);
+
+ // These methods register and receive ownership of an already created marker,
+ // relevant for a specific docshell.
+ // Main thread only.
+ void AddMarkerForDocShell(nsDocShell* aDocShell,
+ UniquePtr<AbstractTimelineMarker>&& aMarker);
+ void AddMarkerForDocShell(nsIDocShell* aDocShell,
+ UniquePtr<AbstractTimelineMarker>&& aMarker);
+
+ // These methods create a basic marker from a name and some metadata,
+ // which doesn't have to be relevant to a specific docshell.
+ // May be called from any thread.
+ void AddMarkerForAllObservedDocShells(const char* aName,
+ MarkerTracingType aTracingType,
+ MarkerStackRequest aStackRequest = MarkerStackRequest::STACK);
+ void AddMarkerForAllObservedDocShells(const char* aName,
+ const TimeStamp& aTime,
+ MarkerTracingType aTracingType,
+ MarkerStackRequest aStackRequest = MarkerStackRequest::STACK);
+
+ // This method clones and registers an already instantiated marker,
+ // which doesn't have to be relevant to a specific docshell.
+ // May be called from any thread.
+ void AddMarkerForAllObservedDocShells(UniquePtr<AbstractTimelineMarker>& aMarker);
+
+ void PopMarkers(nsDocShell* aDocShell,
+ JSContext* aCx,
+ nsTArray<dom::ProfileTimelineMarker>& aStore);
+
+private:
+ static StaticRefPtr<TimelineConsumers> sInstance;
+ static bool sInShutdown;
+
+ // Counter for how many timelines are currently interested in markers,
+ // and a list of the MarkersStorage interfaces representing them.
+ unsigned long mActiveConsumers;
+ LinkedList<MarkersStorage> mMarkersStores;
+
+ // Protects this class's data structures.
+ static StaticMutex sMutex;
+};
+
+} // namespace mozilla
+
+#endif /* mozilla_TimelineConsumers_h_ */
diff --git a/docshell/base/timeline/TimelineMarker.cpp b/docshell/base/timeline/TimelineMarker.cpp
new file mode 100644
index 000000000..b83e9ceb4
--- /dev/null
+++ b/docshell/base/timeline/TimelineMarker.cpp
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TimelineMarker.h"
+
+namespace mozilla {
+
+TimelineMarker::TimelineMarker(const char* aName,
+ MarkerTracingType aTracingType,
+ MarkerStackRequest aStackRequest)
+ : AbstractTimelineMarker(aName, aTracingType)
+{
+ CaptureStackIfNecessary(aTracingType, aStackRequest);
+}
+
+TimelineMarker::TimelineMarker(const char* aName,
+ const TimeStamp& aTime,
+ MarkerTracingType aTracingType,
+ MarkerStackRequest aStackRequest)
+ : AbstractTimelineMarker(aName, aTime, aTracingType)
+{
+ CaptureStackIfNecessary(aTracingType, aStackRequest);
+}
+
+void
+TimelineMarker::AddDetails(JSContext* aCx, dom::ProfileTimelineMarker& aMarker)
+{
+ if (GetTracingType() == MarkerTracingType::START) {
+ aMarker.mProcessType.Construct(GetProcessType());
+ aMarker.mIsOffMainThread.Construct(IsOffMainThread());
+ }
+}
+
+JSObject*
+TimelineMarker::GetStack()
+{
+ if (mStackTrace.initialized()) {
+ return mStackTrace;
+ }
+ return nullptr;
+}
+
+void
+TimelineMarker::CaptureStack()
+{
+ JSContext* ctx = nsContentUtils::GetCurrentJSContext();
+ if (ctx) {
+ JS::RootedObject stack(ctx);
+ if (JS::CaptureCurrentStack(ctx, &stack)) {
+ mStackTrace.init(ctx, stack.get());
+ } else {
+ JS_ClearPendingException(ctx);
+ }
+ }
+}
+
+void
+TimelineMarker::CaptureStackIfNecessary(MarkerTracingType aTracingType,
+ MarkerStackRequest aStackRequest)
+{
+ if ((aTracingType == MarkerTracingType::START ||
+ aTracingType == MarkerTracingType::TIMESTAMP) &&
+ aStackRequest != MarkerStackRequest::NO_STACK) {
+ CaptureStack();
+ }
+}
+
+} // namespace mozilla
diff --git a/docshell/base/timeline/TimelineMarker.h b/docshell/base/timeline/TimelineMarker.h
new file mode 100644
index 000000000..2e0b4dd02
--- /dev/null
+++ b/docshell/base/timeline/TimelineMarker.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_TimelineMarker_h_
+#define mozilla_TimelineMarker_h_
+
+#include "AbstractTimelineMarker.h"
+#include "js/RootingAPI.h"
+
+namespace mozilla {
+
+// Objects of this type can be added to the timeline if there is an interested
+// consumer. The class can also be subclassed to let a given marker creator
+// provide custom details.
+class TimelineMarker : public AbstractTimelineMarker
+{
+public:
+ TimelineMarker(const char* aName,
+ MarkerTracingType aTracingType,
+ MarkerStackRequest aStackRequest = MarkerStackRequest::STACK);
+
+ TimelineMarker(const char* aName,
+ const TimeStamp& aTime,
+ MarkerTracingType aTracingType,
+ MarkerStackRequest aStackRequest = MarkerStackRequest::STACK);
+
+ virtual void AddDetails(JSContext* aCx, dom::ProfileTimelineMarker& aMarker) override;
+ virtual JSObject* GetStack() override;
+
+protected:
+ void CaptureStack();
+
+private:
+ // While normally it is not a good idea to make a persistent root,
+ // in this case changing nsDocShell to participate in cycle
+ // collection was deemed too invasive, and the markers are only held
+ // here temporarily to boot.
+ JS::PersistentRooted<JSObject*> mStackTrace;
+
+ void CaptureStackIfNecessary(MarkerTracingType aTracingType,
+ MarkerStackRequest aStackRequest);
+};
+
+} // namespace mozilla
+
+#endif /* mozilla_TimelineMarker_h_ */
diff --git a/docshell/base/timeline/TimelineMarkerEnums.h b/docshell/base/timeline/TimelineMarkerEnums.h
new file mode 100644
index 000000000..779f8ba72
--- /dev/null
+++ b/docshell/base/timeline/TimelineMarkerEnums.h
@@ -0,0 +1,26 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_TimelineMarkerEnums_h_
+#define mozilla_TimelineMarkerEnums_h_
+
+namespace mozilla {
+
+enum class MarkerTracingType {
+ START,
+ END,
+ TIMESTAMP,
+ HELPER_EVENT
+};
+
+enum class MarkerStackRequest {
+ STACK,
+ NO_STACK
+};
+
+} // namespace mozilla
+
+#endif // mozilla_TimelineMarkerEnums_h_
diff --git a/docshell/base/timeline/TimestampTimelineMarker.h b/docshell/base/timeline/TimestampTimelineMarker.h
new file mode 100644
index 000000000..4e588b576
--- /dev/null
+++ b/docshell/base/timeline/TimestampTimelineMarker.h
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_TimestampTimelineMarker_h_
+#define mozilla_TimestampTimelineMarker_h_
+
+#include "TimelineMarker.h"
+#include "mozilla/dom/ProfileTimelineMarkerBinding.h"
+
+namespace mozilla {
+
+class TimestampTimelineMarker : public TimelineMarker
+{
+public:
+ explicit TimestampTimelineMarker(const nsAString& aCause)
+ : TimelineMarker("TimeStamp", MarkerTracingType::TIMESTAMP)
+ , mCause(aCause)
+ {}
+
+ virtual void AddDetails(JSContext* aCx, dom::ProfileTimelineMarker& aMarker) override
+ {
+ TimelineMarker::AddDetails(aCx, aMarker);
+
+ if (!mCause.IsEmpty()) {
+ aMarker.mCauseName.Construct(mCause);
+ }
+ }
+
+private:
+ nsString mCause;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_TimestampTimelineMarker_h_
diff --git a/docshell/base/timeline/WorkerTimelineMarker.h b/docshell/base/timeline/WorkerTimelineMarker.h
new file mode 100644
index 000000000..b6b369270
--- /dev/null
+++ b/docshell/base/timeline/WorkerTimelineMarker.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_WorkerTimelineMarker_h_
+#define mozilla_WorkerTimelineMarker_h_
+
+#include "TimelineMarker.h"
+#include "mozilla/dom/ProfileTimelineMarkerBinding.h"
+
+namespace mozilla {
+
+class WorkerTimelineMarker : public TimelineMarker
+{
+public:
+ WorkerTimelineMarker(dom::ProfileTimelineWorkerOperationType aOperationType,
+ MarkerTracingType aTracingType)
+ : TimelineMarker("Worker", aTracingType, MarkerStackRequest::NO_STACK)
+ , mOperationType(aOperationType)
+ {}
+
+ virtual UniquePtr<AbstractTimelineMarker> Clone() override
+ {
+ WorkerTimelineMarker* clone = new WorkerTimelineMarker(mOperationType, GetTracingType());
+ clone->SetCustomTime(GetTime());
+ return UniquePtr<AbstractTimelineMarker>(clone);
+ }
+
+ virtual void AddDetails(JSContext* aCx, dom::ProfileTimelineMarker& aMarker) override
+ {
+ TimelineMarker::AddDetails(aCx, aMarker);
+
+ if (GetTracingType() == MarkerTracingType::START) {
+ aMarker.mWorkerOperation.Construct(mOperationType);
+ }
+ }
+
+private:
+ dom::ProfileTimelineWorkerOperationType mOperationType;
+};
+
+} // namespace mozilla
+
+#endif /* mozilla_WorkerTimelineMarker_h_ */
diff --git a/docshell/base/timeline/moz.build b/docshell/base/timeline/moz.build
new file mode 100644
index 000000000..824e37466
--- /dev/null
+++ b/docshell/base/timeline/moz.build
@@ -0,0 +1,42 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+EXPORTS.mozilla += [
+ 'AbstractTimelineMarker.h',
+ 'AutoGlobalTimelineMarker.h',
+ 'AutoTimelineMarker.h',
+ 'CompositeTimelineMarker.h',
+ 'ConsoleTimelineMarker.h',
+ 'DocLoadingTimelineMarker.h',
+ 'EventTimelineMarker.h',
+ 'JavascriptTimelineMarker.h',
+ 'LayerTimelineMarker.h',
+ 'MarkersStorage.h',
+ 'MessagePortTimelineMarker.h',
+ 'ObservedDocShell.h',
+ 'RestyleTimelineMarker.h',
+ 'TimelineConsumers.h',
+ 'TimelineMarker.h',
+ 'TimelineMarkerEnums.h',
+ 'TimestampTimelineMarker.h',
+ 'WorkerTimelineMarker.h',
+]
+
+UNIFIED_SOURCES += [
+ 'AbstractTimelineMarker.cpp',
+ 'AutoGlobalTimelineMarker.cpp',
+ 'AutoTimelineMarker.cpp',
+ 'MarkersStorage.cpp',
+ 'ObservedDocShell.cpp',
+ 'TimelineConsumers.cpp',
+ 'TimelineMarker.cpp',
+]
+
+FINAL_LIBRARY = 'xul'
+
+LOCAL_INCLUDES += [
+ '/docshell/base'
+]
diff --git a/docshell/base/timeline/readme.md b/docshell/base/timeline/readme.md
new file mode 100644
index 000000000..2af9ed3a2
--- /dev/null
+++ b/docshell/base/timeline/readme.md
@@ -0,0 +1,97 @@
+
+#Timeline
+
+The files in this directory are concerned with providing the backend platform features required for the developer tools interested in tracking down operations done in Gecko. The mechanism we use to define these operations are `markers`.
+
+Examples of traced operations include:
+
+* Style Recalculation
+* Layout
+* Painting
+* JavaScript run-to-completion
+* HTML parsing
+* etc.
+
+The traced operations are displayed in the DevTools Performance tool's timeline.
+
+This is an overview of how everything works and can be extended.
+
+##MarkersStorage
+A `MarkersStorage` is an abstract class defining a place where timeline markers may be held. It defines an interface with pure virtual functions to highlight how this storage can be interacted with:
+
+- `AddMarker`: adding a marker, from the main thread only
+- `AddOTMTMarker`: adding a marker off the main thread only
+- `ClearMarkers`: clearing all accumulated markers (both from the main thread and off it)
+- `PopMarkers`: popping all accumulated markers (both from the main thread and off it).
+
+Note on why we handle on/off the main thread markers separately: since most of our markers will come from the main thread, we can be a little more efficient and avoid dealing with multithreading scenarios until all the markers are actually cleared or popped in `ClearMarkers` or `PopMarkers`. Main thread markers may only be added via `AddMarker`, while off the main thread markers may only be added via `AddOTMTMarker`. Clearing and popping markers will yield until all operations involving off the main thread markers finish. When popping, the markers accumulated off the main thread will be moved over. We expect popping to be fairly infrequent (every few hundred milliseconds, currently we schedule this to happen every 200ms).
+
+##ObservedDocShell
+The only implementation of a MarkersStorage we have right now is an `ObservedDocShell`.
+
+Instances of `ObservedDocShell` accumulate markers that are *mostly* about a particular docshell. At a high level, for example, an `ObservedDocshell` would be created when a timeline tool is opened on a page. It is reasonable to assume that most operations which are interesting for that particular page happen on the main thread. However certain operations may happen outside of it, yet are interesting for its developers, for which markers can be created as well (e.g. web audio stuff, service workers etc.). It is also reasonable to assume that a docshell may sometimes not be easily accessible from certain parts of the platform code, but for which markers still need to be created.
+
+Therefore, the following scenarios arise:
+
+- a). creating a marker on the main thread about a particular dochsell
+
+- b). creating a marker on the main thread without pinpointing to an affected docshell (unlikely, but allowed; in this case such a marker would have to be stored in all currently existing `ObservedDocShell` instances)
+
+- c). creating a marker off the main thread about a particular dochsell (impossible; docshells can't be referenced outside the main thread, in which case some other type of identification mechanism needs to be put in place).
+
+- d). creating a marker off the main thread without pinpointing to a particular docshell (same path as c. here, such a marker would have to be stored in all currently existing `ObservedDocShell` instances).
+
+An observed docshell (in other words, "a docshell for which a timeline tool was opened") can thus receive both main thread and off the main thread markers.
+
+Cross-process markers are unnecessary at the moment, but tracked in bug 1200120.
+
+##TimelineConsumers
+A `TimelineConsumer` is a singleton that facilitates access to `ObservedDocShell` instances. This is where a docshell can register/unregister itself as being observed via the `AddConsumer` and `RemoveConsumer` methods.
+
+All markers may only be stored via this singleton. Certain helper methods are available:
+
+* Main thread only
+`AddMarkerForDocShell(nsDocShell*, const char*, MarkerTracingType)`
+`AddMarkerForDocShell(nsDocShell*, const char*, const TimeStamp&, MarkerTracingType)`
+`AddMarkerForDocShell(nsDocShell*, UniquePtr<AbstractTimelineMarker>&&)`
+
+* Any thread
+`AddMarkerForAllObservedDocShells(const char*, MarkerTracingType)`
+`AddMarkerForAllObservedDocShells(const char*, const TimeStamp&, MarkerTracingType)`
+`AddMarkerForAllObservedDocShells(UniquePtr<AbstractTimelineMarker>&)`
+
+The "main thread only" methods deal with point a). described above. The "any thread" methods deal with points b). and d).
+
+##AbstractTimelineMarker
+
+All markers inherit from this abstract class, providing a simple thread-safe extendable blueprint.
+
+Markers are readonly after instantiation, and will always be identified by a name, a timestamp and their tracing type (`START`, `END`, `TIMESTAMP`). It *should not* make sense to modify their data after their creation.
+
+There are only two accessible constructors:
+`AbstractTimelineMarker(const char*, MarkerTracingType)`
+`AbstractTimelineMarker(const char*, const TimeStamp&, MarkerTracingType)`
+which create a marker with a name and a tracing type. If unspecified, the corresponding timestamp will be the current instantiation time. Instantiating a marker *much later* after a particular operation is possible, but be careful providing the correct timestamp.
+
+The `AddDetails` virtual method should be implemented by subclasses when creating WebIDL versions of these markers, which will be sent over to a JavaScript frontend.
+
+##TimelineMarker
+A `TimelineMarker` is the main `AbstractTimelineMarker` implementation. They allow attaching a JavaScript stack on `START` and `TIMESTAMP` markers.
+
+These markers will be created when using the `TimelineConsumers` helper methods which take in a string, a tracing type and (optionally) a timestamp. For more complex markers, subclasses are encouraged. See `EventTimelineMarker` or `ConsoleTimelineMarker` for some examples.
+
+##RAII
+
+### mozilla::AutoTimelineMarker
+
+The easiest way to trace Gecko events/tasks with start and end timeline markers is to use the `mozilla::AutoTimelineMarker` RAII class. It automatically adds the start marker on construction, and adds the end marker on destruction. Don't worry too much about potential performance impact! It only actually adds the markers when the given docshell is being observed by a timeline consumer, so essentially nothing will happen if a tool to inspect those markers isn't specifically open.
+
+This class may only be used on the main thread, and pointer to a docshell is necessary. If the docshell is a nullptr, nothing happens and this operation fails silently.
+
+Example: `AutoTimelineMarker marker(aTargetNode->OwnerDoc()->GetDocShell(), "Parse HTML");`
+
+### mozilla::AutoGlobalTimelineMarker`
+
+Similar to the previous RAII class, but doesn't expect a specific docshell, and the marker will be visible in all timeline consumers. This is useful for generic operations that don't involve a particular dochsell, or where a docshell isn't accessible. May also only be used on the main thread.
+
+Example: `AutoGlobalTimelineMarker marker("Some global operation");`