docshell/base/nsDocShell.cpp
author Boris Zbarsky <bzbarsky@mit.edu>
Mon, 04 Aug 2014 01:25:30 -0400
changeset 219266 d1b960226e0bd93e1380d45977ccdb25d13ce5a3
parent 218992 38afc229dd0a84bcd497b7e5adbd2486286927ad
child 219449 65493d46700e08849313d75caeed56a892a9a592
permissions -rw-r--r--
Bug 1045096. Make sure initial about:blank in iframes have a nsDOMNavigationTiming object, so we don't end up with window.performance throwing when accessed on their window. r=smaug

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 sw=2 tw=80 et: */
/* 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/Casting.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/TabChild.h"
#include "mozilla/EventStateManager.h"
#include "mozilla/Preferences.h"
#include "mozilla/Services.h"
#include "mozilla/StartupTimeline.h"
#include "mozilla/Telemetry.h"
#include "mozilla/unused.h"
#include "mozilla/VisualEventTracer.h"
#include "URIUtils.h"

#ifdef MOZ_LOGGING
// so we can get logging even in release builds (but only for some things)
#define FORCE_PR_LOG 1
#endif

#include "nsIContent.h"
#include "nsIContentInlines.h"
#include "nsIDocument.h"
#include "nsIDOMDocument.h"
#include "nsIDOMElement.h"
#include "nsIDOMStorage.h"
#include "nsIContentViewer.h"
#include "nsIDocumentLoaderFactory.h"
#include "nsCURILoader.h"
#include "nsDocShellCID.h"
#include "nsDOMCID.h"
#include "nsNetUtil.h"
#include "nsRect.h"
#include "prenv.h"
#include "nsIDOMWindow.h"
#include "nsIGlobalObject.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 "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"

// 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"

// Helper Classes
#include "nsError.h"
#include "nsEscape.h"

// Interfaces Needed
#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 "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 "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 "prlog.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 "nsCxPusher.h"
#include "nsIChannelPolicy.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 "nsISHContainer.h"
#include "nsISHistory.h"
#include "nsISecureBrowserUI.h"
#include "nsIStringBundle.h"
#include "nsISupportsArray.h"
#include "nsIURIFixup.h"
#include "nsIURILoader.h"
#include "nsIWebBrowserFind.h"
#include "nsIWidget.h"
#include "mozilla/dom/EncodingUtils.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;

// 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 PR_LOGGING
#ifdef DEBUG
static PRLogModuleInfo* gDocShellLog;
#endif
static PRLogModuleInfo* gDocShellLeakLog;
#endif

const char kBrandBundleURL[]      = "chrome://branding/locale/brand.properties";
const char kAppstringsBundleURL[] = "chrome://global/locale/appstrings.properties";

static void
FavorPerformanceHint(bool perfOverStarvation)
{
    nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
    if (appShell) {
        appShell->FavorPerformanceHint(perfOverStarvation,
                                       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 *maxPerLink, bool *requireSameHost)
{
  bool allow = Preferences::GetBool(PREF_PINGS_ENABLED, false);

  *maxPerLink = 1;
  *requireSameHost = true;

  if (allow) {
    Preferences::GetInt(PREF_PINGS_MAX_PER_LINK, maxPerLink);
    Preferences::GetBool(PREF_PINGS_REQUIRE_SAME_HOST, requireSameHost);
  }

  return allow;
}

static bool
CheckPingURI(nsIURI* uri, nsIContent* content)
{
  if (!uri)
    return false;

  // Check with nsIScriptSecurityManager
  nsCOMPtr<nsIScriptSecurityManager> ssmgr =
    do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
  NS_ENSURE_TRUE(ssmgr, false);

  nsresult rv =
    ssmgr->CheckLoadURIWithPrincipal(content->NodePrincipal(), uri,
                                     nsIScriptSecurityManager::STANDARD);
  if (NS_FAILED(rv)) {
    return false;
  }

  // Ignore non-HTTP(S)
  bool match;
  if ((NS_FAILED(uri->SchemeIs("http", &match)) || !match) &&
      (NS_FAILED(uri->SchemeIs("https", &match)) || !match)) {
    return false;
  }

  // Check with contentpolicy
  int16_t shouldLoad = nsIContentPolicy::ACCEPT;
  rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_PING,
                                 uri,
                                 content->NodePrincipal(),
                                 content,
                                 EmptyCString(), // mime hint
                                 nullptr, //extra
                                 &shouldLoad);
  return NS_SUCCEEDED(rv) && NS_CP_ACCEPTED(shouldLoad);
}

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.
  if (!aContent->IsHTML()) {
    return false;
  }
  nsIAtom* nameAtom = aContent->Tag();
  return nameAtom == nsGkAtoms::a || nameAtom == nsGkAtoms::area;
}

static void
ForEachPing(nsIContent *content, ForEachPingCallback callback, void *closure)
{
  // 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(content))
    return;

  nsCOMPtr<nsIAtom> pingAtom = do_GetAtom("ping");
  if (!pingAtom)
    return;

  nsAutoString value;
  content->GetAttr(kNameSpaceID_None, pingAtom, value);
  if (value.IsEmpty())
    return;

  nsCOMPtr<nsIIOService> ios = do_GetIOService();
  if (!ios)
    return;

  nsIDocument *doc = content->OwnerDoc();

  nsWhitespaceTokenizer tokenizer(value);

  while (tokenizer.hasMoreTokens()) {
    nsCOMPtr<nsIURI> uri, baseURI = content->GetBaseURI();
    ios->NewURI(NS_ConvertUTF16toUTF8(tokenizer.nextToken()),
                doc->GetDocumentCharacterSet().get(),
                baseURI, getter_AddRefs(uri));
    if (CheckPingURI(uri, content)) {
      callback(closure, content, uri, ios);
    }
  }
}

//----------------------------------------------------------------------

// We wait this many milliseconds before killing the ping channel...
#define PING_TIMEOUT 10000

static void
OnPingTimeout(nsITimer *timer, void *closure)
{
  nsILoadGroup *loadGroup = static_cast<nsILoadGroup *>(closure);
  if (loadGroup)
    loadGroup->Cancel(NS_ERROR_ABORT);
}

// Check to see if two URIs have the same host or not
static bool
IsSameHost(nsIURI *uri1, nsIURI *uri2)
{
  nsAutoCString host1, host2;
  uri1->GetAsciiHost(host1);
  uri2->GetAsciiHost(host2);
  return host1.Equals(host2);
}

class nsPingListener MOZ_FINAL : public nsIStreamListener
                               , public nsIInterfaceRequestor
                               , public nsIChannelEventSink
{
public:
  NS_DECL_ISUPPORTS
  NS_DECL_NSIREQUESTOBSERVER
  NS_DECL_NSISTREAMLISTENER
  NS_DECL_NSIINTERFACEREQUESTOR
  NS_DECL_NSICHANNELEVENTSINK

  nsPingListener(bool requireSameHost, nsIContent* content, nsILoadGroup* loadGroup)
    : mRequireSameHost(requireSameHost),
      mContent(content),
      mLoadGroup(loadGroup)
  {}

  nsresult StartTimeout();

private:
  ~nsPingListener();

  bool mRequireSameHost;
  nsCOMPtr<nsIContent> mContent;
  nsCOMPtr<nsILoadGroup> mLoadGroup;
  nsCOMPtr<nsITimer> mTimer;
};

NS_IMPL_ISUPPORTS(nsPingListener, nsIStreamListener, nsIRequestObserver,
                  nsIInterfaceRequestor, nsIChannelEventSink)

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 *request, nsISupports *context)
{
  return NS_OK;
}

NS_IMETHODIMP
nsPingListener::OnDataAvailable(nsIRequest *request, nsISupports *context,
                                nsIInputStream *stream, uint64_t offset,
                                uint32_t count)
{
  uint32_t result;
  return stream->ReadSegments(NS_DiscardSegment, nullptr, count, &result);
}

NS_IMETHODIMP
nsPingListener::OnStopRequest(nsIRequest *request, nsISupports *context,
                              nsresult status)
{
  mLoadGroup = nullptr;

  if (mTimer) {
    mTimer->Cancel();
    mTimer = nullptr;
  }

  return NS_OK;
}

NS_IMETHODIMP
nsPingListener::GetInterface(const nsIID &iid, void **result)
{
  if (iid.Equals(NS_GET_IID(nsIChannelEventSink))) {
    NS_ADDREF_THIS();
    *result = (nsIChannelEventSink *) this;
    return NS_OK;
  }

  return NS_ERROR_NO_INTERFACE;
}

NS_IMETHODIMP
nsPingListener::AsyncOnChannelRedirect(nsIChannel *oldChan, nsIChannel *newChan,
                                       uint32_t flags,
                                       nsIAsyncVerifyRedirectCallback *callback)
{
  nsCOMPtr<nsIURI> newURI;
  newChan->GetURI(getter_AddRefs(newURI));

  if (!CheckPingURI(newURI, mContent))
    return NS_ERROR_ABORT;

  if (!mRequireSameHost) {
    callback->OnRedirectVerifyCallback(NS_OK);
    return NS_OK;
  }

  // XXXbz should this be using something more like the nsContentUtils
  // same-origin checker?
  nsCOMPtr<nsIURI> oldURI;
  oldChan->GetURI(getter_AddRefs(oldURI));
  NS_ENSURE_STATE(oldURI && newURI);

  if (!IsSameHost(oldURI, newURI))
    return NS_ERROR_ABORT;

  callback->OnRedirectVerifyCallback(NS_OK);
  return NS_OK;
}

struct SendPingInfo {
  int32_t numPings;
  int32_t maxPings;
  bool    requireSameHost;
  nsIURI *target;
  nsIURI *referrer;
};

static void
SendPing(void *closure, nsIContent *content, nsIURI *uri, nsIIOService *ios)
{
  SendPingInfo *info = static_cast<SendPingInfo *>(closure);
  if (info->maxPings > -1 && info->numPings >= info->maxPings)
    return;

  if (info->requireSameHost) {
    // Make sure the referrer and the given uri share the same origin.  We
    // only require the same hostname.  The scheme and port may differ.
    if (!IsSameHost(uri, info->referrer))
      return;
  }

  nsIDocument *doc = content->OwnerDoc();

  nsCOMPtr<nsIChannel> chan;
  ios->NewChannelFromURI(uri, getter_AddRefs(chan));
  if (!chan)
    return;

  // Don't bother caching the result of this URI load.
  chan->SetLoadFlags(nsIRequest::INHIBIT_CACHING);

  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, uri, 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->SetReferrer(info->referrer);
  }

  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;
  chan->SetLoadGroup(loadGroup);

  // Construct a listener that merely discards any response.  If successful at
  // opening the channel, then it is not necessary to hold a reference to the
  // channel.  The networking subsystem will take care of that for us.
  nsPingListener *pingListener =
      new nsPingListener(info->requireSameHost, content, loadGroup);
  if (!pingListener)
    return;

  nsCOMPtr<nsIStreamListener> listener(pingListener);

  // Observe redirects as well:
  nsCOMPtr<nsIInterfaceRequestor> callbacks = do_QueryInterface(listener);
  NS_ASSERTION(callbacks, "oops");
  loadGroup->SetNotificationCallbacks(callbacks);

  chan->AsyncOpen(listener, nullptr);

  // 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);
  }
}

// Spec: http://whatwg.org/specs/web-apps/current-work/#ping
static void
DispatchPings(nsIContent *content, nsIURI *target, nsIURI *referrer)
{
  SendPingInfo info;

  if (!PingsEnabled(&info.maxPings, &info.requireSameHost))
    return;
  if (info.maxPings == 0)
    return;

  info.numPings = 0;
  info.target = target;
  info.referrer = referrer;

  ForEachPing(content, SendPing, &info);
}

static nsDOMPerformanceNavigationType
ConvertLoadTypeToNavigationType(uint32_t aLoadType)
{
  // Not initialized, assume it's normal load.
  if (aLoadType == 0) {
    aLoadType = LOAD_NORMAL;
  }

  nsDOMPerformanceNavigationType result = dom::PerformanceNavigation::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 = dom::PerformanceNavigation::TYPE_NAVIGATE;
        break;
    case LOAD_HISTORY:
        result = dom::PerformanceNavigation::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 = dom::PerformanceNavigation::TYPE_RELOAD;
        break;
    case LOAD_STOP_CONTENT_AND_REPLACE:
    case LOAD_REFRESH:
    case LOAD_BYPASS_HISTORY:
    case LOAD_ERROR_PAGE:
    case LOAD_PUSHSTATE:
        result = dom::PerformanceNavigation::TYPE_RESERVED;
        break;
    default:
        // NS_NOTREACHED("Unexpected load type value");
        result = dom::PerformanceNavigation::TYPE_RESERVED;
        break;
  }

  return result;
}

static nsISHEntry* GetRootSHEntry(nsISHEntry *entry);

static void
IncreasePrivateDocShellCount()
{
    gNumberOfPrivateDocShells++;
    if (gNumberOfPrivateDocShells > 1 ||
        XRE_GetProcessType() != GeckoProcessType_Content) {
        return;
    }

    mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton();
    cc->SendPrivateDocShellsExist(true);
}

static void
DecreasePrivateDocShellCount()
{
    MOZ_ASSERT(gNumberOfPrivateDocShells > 0);
    gNumberOfPrivateDocShells--;
    if (!gNumberOfPrivateDocShells)
    {
        if (XRE_GetProcessType() == GeckoProcessType_Content) {
            mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton();
            cc->SendPrivateDocShellsExist(false);
            return;
        }

        nsCOMPtr<nsIObserverService> obsvc = mozilla::services::GetObserverService();
        if (obsvc) {
            obsvc->NotifyObservers(nullptr, "last-pb-context-exited", nullptr);
        }
    }
}

//*****************************************************************************
//***    nsDocShell: Object Management
//*****************************************************************************

static uint64_t gDocshellIDCounter = 0;

// Note: operator new zeros our memory
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),
    mFullscreenAllowed(CHECK_ATTRIBUTES),
    mCreated(false),
    mAllowSubframes(true),
    mAllowPlugins(true),
    mAllowJavascript(true),
    mAllowMetaRedirects(true),
    mAllowImages(true),
    mAllowMedia(true),
    mAllowDNSPrefetch(true),
    mAllowWindowControl(true),
    mAllowContentRetargeting(true),
    mCreatingDocument(false),
    mUseErrorPages(false),
    mObserveErrorPages(true),
    mAllowAuth(true),
    mAllowKeywordFixup(false),
    mIsOffScreenBrowser(false),
    mIsActive(true),
    mIsAppTab(false),
    mUseGlobalHistory(false),
    mInPrivateBrowsing(false),
    mUseRemoteTabs(false),
    mDeviceSizeIsPageSize(false),
    mCanExecuteScripts(false),
    mFiredUnloadEvent(false),
    mEODForCurrentDocument(false),
    mURIResultedInDocument(false),
    mIsBeingDestroyed(false),
    mIsExecutingOnLoadHandler(false),
    mIsPrintingOrPP(false),
    mSavingOldViewer(false),
#ifdef DEBUG
    mInEnsureScriptEnv(false),
#endif
    mAffectPrivateSessionLifetime(true),
    mInvisible(false),
    mDefaultLoadFlags(nsIRequest::LOAD_NORMAL),
    mFrameType(eFrameTypeRegular),
    mOwnOrContainingAppId(nsIScriptSecurityManager::UNKNOWN_APP_ID),
    mParentCharsetSource(0)
{
    mHistoryID = ++gDocshellIDCounter;
    if (gDocShellCount++ == 0) {
        NS_ASSERTION(sURIFixup == nullptr,
                     "Huh, sURIFixup not null in first nsDocShell ctor!");

        CallGetService(NS_URIFIXUP_CONTRACTID, &sURIFixup);
    }

#ifdef PR_LOGGING
#ifdef DEBUG
    if (! gDocShellLog)
        gDocShellLog = PR_NewLogModule("nsDocShell");
#endif
    if (nullptr == gDocShellLeakLog)
        gDocShellLeakLog = PR_NewLogModule("nsDocShellLeak");
    if (gDocShellLeakLog)
        PR_LOG(gDocShellLeakLog, PR_LOG_DEBUG, ("DOCSHELL %p created\n", this));
#endif

#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(),
                    SafeCast<unsigned long long>(mHistoryID));
  }
#endif
}

nsDocShell::~nsDocShell()
{
    Destroy();

    nsCOMPtr<nsISHistoryInternal>
        shPrivate(do_QueryInterface(mSessionHistory));
    if (shPrivate) {
        shPrivate->SetRootDocShell(nullptr);
    }

    if (--gDocShellCount == 0) {
        NS_IF_RELEASE(sURIFixup);
    }

#ifdef PR_LOGGING
    if (gDocShellLeakLog)
        PR_LOG(gDocShellLeakLog, PR_LOG_DEBUG, ("DOCSHELL %p destroyed\n", this));
#endif

#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(),
                      SafeCast<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);
    NS_ENSURE_TRUE(mContentListener, NS_ERROR_OUT_OF_MEMORY);

    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));
    NS_ENSURE_TRUE(proxy, NS_ERROR_OUT_OF_MEMORY);
    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();
}

//*****************************************************************************
// nsDocShell::nsISupports
//*****************************************************************************   

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_END_INHERITING(nsDocLoader)

///*****************************************************************************
// nsDocShell::nsIInterfaceRequestor
//*****************************************************************************   
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(nsPIDOMWindow)) ||
              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(PR_LOGGING) && defined(DEBUG)
        PR_LOG(gDocShellLog, PR_LOG_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, &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) {
            *aSink = shistory;
            NS_ADDREF((nsISupports *) * 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)) && NS_SUCCEEDED(EnsureEditorData())) {
      nsCOMPtr<nsIEditingSession> editingSession;
      mEditorData->GetEditingSession(getter_AddRefs(editingSession));
      if (editingSession)
      {
        *aSink = editingSession;
        NS_ADDREF((nsISupports *)*aSink);
        return NS_OK;
      }  

      return 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))) {
      nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
      nsresult rv = GetTreeOwner(getter_AddRefs(treeOwner));
      if (NS_SUCCEEDED(rv) && treeOwner) {
        nsCOMPtr<nsIInterfaceRequestor> ir = do_QueryInterface(treeOwner);
        if (ir)
          return ir->GetInterface(aIID, aSink);
      }
    }
    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 {
        nsCOMPtr<nsPIDOMWindow> win = GetWindow();
        if (win) {
          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;
}                                                                               

//*****************************************************************************
// nsDocShell::nsIDocShell
//*****************************************************************************   
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.
    if (IsPrintingOrPP()) {
      return NS_OK; // JS may not handle returning of an error code
    }
    nsCOMPtr<nsIURI> referrer;
    nsCOMPtr<nsIInputStream> postStream;
    nsCOMPtr<nsIInputStream> headersStream;
    nsCOMPtr<nsISupports> owner;
    bool inheritOwner = false;
    bool ownerIsExplicit = false;
    bool sendReferrer = true;
    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));

        nsDocShellInfoLoadType lt = nsIDocShellLoadInfo::loadNormal;
        aLoadInfo->GetLoadType(&lt);
        // Get the appropriate loadType from nsIDocShellLoadInfo type
        loadType = ConvertDocShellLoadInfoToLoadType(lt);

        aLoadInfo->GetOwner(getter_AddRefs(owner));
        aLoadInfo->GetInheritOwner(&inheritOwner);
        aLoadInfo->GetOwnerIsExplicit(&ownerIsExplicit);
        aLoadInfo->GetSHEntry(getter_AddRefs(shEntry));
        aLoadInfo->GetTarget(getter_Copies(target));
        aLoadInfo->GetPostDataStream(getter_AddRefs(postStream));
        aLoadInfo->GetHeadersStream(getter_AddRefs(headersStream));
        aLoadInfo->GetSendReferrer(&sendReferrer);
        aLoadInfo->GetIsSrcdocLoad(&isSrcdoc);
        aLoadInfo->GetSrcdocData(srcdoc);
        aLoadInfo->GetSourceDocShell(getter_AddRefs(sourceDocShell));
        aLoadInfo->GetBaseURI(getter_AddRefs(baseURI));
    }

#if defined(PR_LOGGING) && defined(DEBUG)
    if (PR_LOG_TEST(gDocShellLog, PR_LOG_DEBUG)) {
        nsAutoCString uristr;
        aURI->GetAsciiSpec(uristr);
        PR_LOG(gDocShellLog, PR_LOG_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 == nullptr) {
                // 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
        PR_LOG(gDocShellLog, PR_LOG_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 an owner (a referring principal).
    //
    // If ownerIsExplicit 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 inheritOwner should be true and InternalLoad will get
    //     an owner from the current document. If none of these things are
    //     true, then
    // (4) we pass a null owner into the channel, and an owner will be
    //     created later from the channel's internal data.
    //
    // If ownerIsExplicit *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 inheritOwner should be true and InternalLoad will get an owner
    //     from the current document. If none of these things are true, then
    // (4) we pass a null owner into the channel, and an owner will be
    //     created later from the channel's internal data.
    //
    // NOTE: This all only works because the only thing the owner is used  
    //       for in InternalLoad is data:, javascript:, and about:blank
    //       URIs.  For other URIs this would all be dead wrong!

    if (owner && mItemType != typeChrome) {
        nsCOMPtr<nsIPrincipal> ownerPrincipal = do_QueryInterface(owner);
        if (nsContentUtils::IsSystemOrExpandedPrincipal(ownerPrincipal)) {
            if (ownerIsExplicit) {
                return NS_ERROR_DOM_SECURITY_ERR;
            }
            owner = nullptr;
            inheritOwner = true;
        }
    }
    if (!owner && !inheritOwner && !ownerIsExplicit) {
        // See if there's system or chrome JS code running
        inheritOwner = nsContentUtils::IsCallerChrome();
    }

    if (aLoadFlags & LOAD_FLAGS_DISALLOW_INHERIT_OWNER) {
        inheritOwner = false;
        owner = do_CreateInstance("@mozilla.org/nullprincipal;1");
    }

    uint32_t flags = 0;

    if (inheritOwner)
        flags |= INTERNAL_LOAD_FLAGS_INHERIT_OWNER;

    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,
                        referrer,
                        owner,
                        flags,
                        target.get(),
                        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;
    if (aLoadInfo) {
        nsDocShellInfoLoadType lt = nsIDocShellLoadInfo::loadNormal;
        (void) aLoadInfo->GetLoadType(&lt);
        // Get the appropriate LoadType from nsIDocShellLoadInfo type
        loadType = ConvertDocShellLoadInfoToLoadType(lt);
    }

    NS_ENSURE_SUCCESS(Stop(nsIWebNavigation::STOP_NETWORK), NS_ERROR_FAILURE);

    mLoadType = loadType;

    // build up a channel for this stream.
    nsCOMPtr<nsIChannel> channel;
    NS_ENSURE_SUCCESS(NS_NewInputStreamChannel
                      (getter_AddRefs(channel), uri, aStream,
                       aContentType, aContentCharset),
                      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();
    NS_ENSURE_TRUE(loadInfo, NS_ERROR_OUT_OF_MEMORY);
    nsCOMPtr<nsIDocShellLoadInfo> localRef(loadInfo);

    *aLoadInfo = localRef;
    NS_ADDREF(*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> kungFuDeathGrip(mContentViewer);
        mFiredUnloadEvent = true;

        if (mTiming) {
            mTiming->NotifyUnloadEventStart();
        }

        mContentViewer->PageHide(aIsUnload);

        if (mTiming) {
            mTiming->NotifyUnloadEventEnd();
        }

        nsAutoTArray<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) {
        return;
    }

    mTiming = new nsDOMNavigationTiming();
    mTiming->NotifyNavigationStart();
}


//
// 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;
}

NS_IMETHODIMP
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()
{
    nsRefPtr<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;

    nsRefPtr<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;
}

/* void setCurrentURI (in nsIURI uri); */
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)
{
#ifdef PR_LOGGING
    if (gDocShellLeakLog && PR_LOG_TEST(gDocShellLeakLog, PR_LOG_DEBUG)) {
        nsAutoCString spec;
        if (aURI)
            aURI->GetSpec(spec);
        PR_LogPrint("DOCSHELL %p SetCurrentURI %s\n", this, spec.get());
    }
#endif

    // 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);
    
    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::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);
    
    *aUsePrivateBrowsing = mInPrivateBrowsing;
    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);

    return SetPrivateBrowsing(aUsePrivateBrowsing);
}

NS_IMETHODIMP
nsDocShell::SetPrivateBrowsing(bool aUsePrivateBrowsing)
{
    bool changed = aUsePrivateBrowsing != mInPrivateBrowsing;
    if (changed) {
        mInPrivateBrowsing = 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);
            }
        }
    }
    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 && mInPrivateBrowsing) {
        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) {
        nsPIDOMWindow* innerWin = mScriptGlobal->GetCurrentInnerWindow();
        if (innerWin) {
            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)
{
    mAllowContentRetargeting = aAllowContentRetargeting;
    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;

    // For non-browsers/apps, check that the enclosing iframe element
    // has the allowfullscreen attribute set to true. If any ancestor
    // iframe does not have mozallowfullscreen=true, then fullscreen is
    // prohibited.
    nsCOMPtr<nsPIDOMWindow> win = GetWindow();
    if (!win) {
        return NS_OK;
    }
    nsCOMPtr<Element> frameElement = win->GetFrameElementInternal();
    if (frameElement &&
        frameElement->IsHTML(nsGkAtoms::iframe) &&
        !frameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::allowfullscreen) &&
        !frameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::mozallowfullscreen)) {
        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.
    nsRefPtr<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::GetIsBrowserOrApp()) {
        // 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;
}

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 **outEnum)
{
    NS_ENSURE_ARG_POINTER(outEnum);
    *outEnum = nullptr;
    
    nsRefPtr<nsDocShellEnumerator> docShellEnum;
    if (aDirection == ENUMERATE_FORWARDS)
        docShellEnum = new nsDocShellForwardsEnumerator;
    else
        docShellEnum = new nsDocShellBackwardsEnumerator;
    
    if (!docShellEnum) return NS_ERROR_OUT_OF_MEMORY;
    
    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 **)outEnum);

    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 *zoom)
{
    NS_ENSURE_ARG_POINTER(zoom);
    *zoom = 1.0f;
    return NS_OK;
}

NS_IMETHODIMP
nsDocShell::SetZoom(float zoom)
{
    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* aTookFocus)
{
    NS_ENSURE_ARG_POINTER(aTookFocus);
    
    nsCOMPtr<nsIWebBrowserChromeFocus> chromeFocus = do_GetInterface(mTreeOwner);
    if (chromeFocus) {
        if (aForward)
            *aTookFocus = NS_SUCCEEDED(chromeFocus->FocusNextElement());
        else
            *aTookFocus = NS_SUCCEEDED(chromeFocus->FocusPrevElement());
    } 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;
}

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<nsIDOMWindow> domWin = do_GetInterface(GetAsSupports(this));

    if (aCreate) {
        return manager->CreateStorage(domWin, aPrincipal, aDocumentURI,
                                      mInPrivateBrowsing, aStorage);
    }

    return manager->GetStorage(domWin, aPrincipal, mInPrivateBrowsing,
                               aStorage);
}

nsresult
nsDocShell::AddSessionStorage(nsIPrincipal* aPrincipal,
                              nsIDOMStorage* aStorage)
{
    nsRefPtr<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;
}

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 char16_t *aName, bool *_retval)
{
    NS_ENSURE_ARG_POINTER(aName);
    NS_ENSURE_ARG_POINTER(_retval);
    *_retval = mName.Equals(aName);
    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; 

    nsRefPtr<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;
    nsRefPtr<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();

    nsDocLoader::SetDocLoaderParent(aParent);

    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;
    nsCOMPtr<nsIDocShell> parentAsDocShell(do_QueryInterface(parent));
    if (parentAsDocShell)
    {
        if (NS_SUCCEEDED(parentAsDocShell->GetAllowPlugins(&value)))
        {
            SetAllowPlugins(value);
        }
        if (NS_SUCCEEDED(parentAsDocShell->GetAllowJavascript(&value)))
        {
            SetAllowJavascript(value);
        }
        if (NS_SUCCEEDED(parentAsDocShell->GetAllowMetaRedirects(&value)))
        {
            SetAllowMetaRedirects(value);
        }
        if (NS_SUCCEEDED(parentAsDocShell->GetAllowSubframes(&value)))
        {
            SetAllowSubframes(value);
        }
        if (NS_SUCCEEDED(parentAsDocShell->GetAllowImages(&value)))
        {
            SetAllowImages(value);
        }
        SetAllowMedia(parentAsDocShell->GetAllowMedia());
        if (NS_SUCCEEDED(parentAsDocShell->GetAllowWindowControl(&value)))
        {
            SetAllowWindowControl(value);
        }
        SetAllowContentRetargeting(
            parentAsDocShell->GetAllowContentRetargeting());
        if (NS_SUCCEEDED(parentAsDocShell->GetIsActive(&value)))
        {
            SetIsActive(value);
        }
        if (NS_FAILED(parentAsDocShell->GetAllowDNSPrefetch(&value))) {
            value = false;
        }
        SetAllowDNSPrefetch(value);
        value = parentAsDocShell->GetAffectPrivateSessionLifetime();
        SetAffectPrivateSessionLifetime(value);
        uint32_t flags;
        if (NS_SUCCEEDED(parentAsDocShell->GetDefaultLoadFlags(&flags)))
        {
            SetDefaultLoadFlags(flags);
        }

    }

    nsCOMPtr<nsILoadContext> parentAsLoadContext(do_QueryInterface(parent));
    if (parentAsLoadContext &&
        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();

    return NS_OK;
}

NS_IMETHODIMP
nsDocShell::GetSameTypeParent(nsIDocShellTreeItem ** aParent)
{
    NS_ENSURE_ARG_POINTER(aParent);
    *aParent = nullptr;

    if (nsIDocShell::GetIsBrowserOrApp()) {
        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);

    nsRefPtr<nsDocShell> root = this;
    nsRefPtr<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;
}

/* 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 or neither to nsIDocShell.
        return false;
    }

    if (targetDS && accessingDS &&
        (targetDS->GetIsInBrowserElement() !=
           accessingDS->GetIsInBrowserElement() ||
         targetDS->GetAppId() != accessingDS->GetAppId())) {
        return false;
    }

    nsCOMPtr<nsIDocShellTreeItem> accessingRoot;
    aAccessingItem->GetSameTypeRootTreeItem(getter_AddRefs(accessingRoot));

    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);

    nsCOMPtr<nsIDocShellTreeItem> targetRoot;
    aTargetItem->GetSameTypeRootTreeItem(getter_AddRefs(targetRoot));

    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<nsIDOMWindow> targetWindow = aTargetItem->GetWindow();
    if (!targetWindow) {
        NS_ERROR("This should not happen, really");
        return false;
    }

    nsCOMPtr<nsIDOMWindow> targetOpener;
    targetWindow->GetOpener(getter_AddRefs(targetOpener));
    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)
{
    nsCOMPtr<nsIDOMWindow> window = aItem->GetWindow();

    if (window) {
        bool isClosed;

        if (NS_SUCCEEDED(window->GetClosed(&isClosed)) && !isClosed) {
            return true;
        }
    }

    return false;
}

NS_IMETHODIMP
nsDocShell::FindItemWithName(const char16_t * aName,
                             nsISupports * aRequestor,
                             nsIDocShellTreeItem * aOriginalRequestor,
                             nsIDocShellTreeItem ** _retval)
{
    NS_ENSURE_ARG(aName);
    NS_ENSURE_ARG_POINTER(_retval);

    // If we don't find one, we return NS_OK and a null result
    *_retval = nullptr;

    if (!*aName)
        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,
                                  _retval);
    } 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;
        nsDependentString name(aName);
        if (name.LowerCaseEqualsLiteral("_self")) {
            foundItem = this;
        }
        else if (name.LowerCaseEqualsLiteral("_blank"))
        {
            // Just return null.  Caller must handle creating a new window with
            // a blank name himself.
            return NS_OK;
        }
        else if (name.LowerCaseEqualsLiteral("_parent"))
        {
            GetSameTypeParent(getter_AddRefs(foundItem));
            if(!foundItem)
                foundItem = this;
        }
        else if (name.LowerCaseEqualsLiteral("_top"))
        {
            GetSameTypeRootTreeItem(getter_AddRefs(foundItem));
            NS_ASSERTION(foundItem, "Must have this; worst case it's us!");
        }
        // _main is an IE target which should be case-insensitive but isn't
        // see bug 217886 for details
        else if (name.LowerCaseEqualsLiteral("_content") ||
                 name.EqualsLiteral("_main"))
        {
            // Must pass our same type root as requestor to the
            // treeowner to make sure things work right.
            nsCOMPtr<nsIDocShellTreeItem> root;
            GetSameTypeRootTreeItem(getter_AddRefs(root));
            if (mTreeOwner) {
                NS_ASSERTION(root, "Must have this; worst case it's us!");
                mTreeOwner->FindItemWithName(aName, root, aOriginalRequestor,
                                             getter_AddRefs(foundItem));
            }
#ifdef DEBUG
            else {
                NS_ERROR("Someone isn't setting up the tree owner.  "
                         "You might like to try that.  "
                         "Things will.....you know, work.");
                // Note: _content should always exist.  If we don't have one
                // hanging off the treeowner, just create a named window....
                // so don't return here, in case we did that and can now find
                // it.                
                // XXXbz should we be using |root| instead of creating
                // a new window?
            }
#endif
        } 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(*_retval);
        }
        return NS_OK;
    }
}

nsresult
nsDocShell::DoFindItemWithName(const char16_t* aName,
                               nsISupports* aRequestor,
                               nsIDocShellTreeItem* aOriginalRequestor,
                               nsIDocShellTreeItem** _retval)
{
    // First we check our name.
    if (mName.Equals(aName) && ItemIsActive(this) &&
        CanAccessItem(this, aOriginalRequestor)) {
        NS_ADDREF(*_retval = 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, _retval);
    NS_ASSERTION(NS_SUCCEEDED(rv),
                 "FindChildWithName should not be failing here.");
    if (*_retval)
        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 (parentAsTreeItem->ItemType() == mItemType) {
            return parentAsTreeItem->
                FindItemWithName(aName,
                                 static_cast<nsIDocShellTreeItem*>
                                            (this),
                                 aOriginalRequestor,
                                 _retval);
        }
    }

    // If the parent is null or not of the same type fall through and ask tree
    // owner.

    // This may fail, but comparing against null serves the same purpose
    nsCOMPtr<nsIDocShellTreeOwner>
        reqAsTreeOwner(do_QueryInterface(aRequestor));

    if (mTreeOwner && mTreeOwner != reqAsTreeOwner) {
        return mTreeOwner->
            FindItemWithName(aName, this, aOriginalRequestor, _retval);
    }

    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();
  nsRefPtr<nsPresContext> presContext;
  parentAsDocShell->GetPresContext(getter_AddRefs(presContext));
  nsIDocument *doc = presShell->GetDocument();

  nsCOMPtr<nsIDOMWindow> 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);

    nsRefPtr<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) {
        childsParent->RemoveChildLoader(childAsDocLoader);
    }

    // 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);

    nsRefPtr<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 char16_t * aName,
                              bool aRecurse, bool aSameType,
                              nsIDocShellTreeItem * aRequestor,
                              nsIDocShellTreeItem * aOriginalRequestor,
                              nsIDocShellTreeItem ** _retval)
{
    NS_ENSURE_ARG(aName);
    NS_ENSURE_ARG_POINTER(_retval);

    *_retval = nullptr;          // if we don't find one, we return NS_OK and a null result 

    if (!*aName)
        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(*_retval);
            break;
        }

        if (childType != mItemType)     //Only ask it to check children if it is same type
            continue;

        if (aRecurse && (aRequestor != child))  // Only ask the child if it isn't the requestor
        {
            // 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,
                                     _retval);
            NS_ASSERTION(NS_SUCCEEDED(rv),
                         "FindChildWithName should not fail here");
            if (*_retval)           // 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 loadType,
                            bool aCloneChildren)
{
    nsresult rv;

    if (mLSHE && loadType != 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) {
            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 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 = parent->AddChildSHEntry(aCloneRef, aNewEntry, aChildOffset,
                                         loadType, aCloneChildren);
        }          
    }
    return rv;
}

nsresult
nsDocShell::DoAddChildSHEntry(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);
    nsAutoTArray<uint64_t, 16> ids;
    ids.AppendElement(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();
}

nsPIDOMWindow*
nsDocShell::GetWindow()
{
  NS_ENSURE_SUCCESS(EnsureScriptEnvironment(), nullptr);
  return mScriptGlobal;
}

NS_IMETHODIMP
nsDocShell::SetDeviceSizeIsPageSize(bool aValue)
{
    if (mDeviceSizeIsPageSize != aValue) {
      mDeviceSizeIsPageSize = aValue;
      nsRefPtr<nsPresContext> presContext;
      GetPresContext(getter_AddRefs(presContext));
      if (presContext) {
          presContext->MediaFeatureValuesChanged(presContext->eAlwaysRebuildStyle);
      }
    }
    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);
  nsAutoTArray<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 isAllowed = !IsPrintingOrPP(aDisplayPrintErrorDialog) && !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 LoadURIWithBase(aURI, aLoadFlags, aReferringURI, aPostStream,
                           aHeaderStream, nullptr);
}

NS_IMETHODIMP
nsDocShell::LoadURIWithBase(const char16_t * aURI,
                            uint32_t aLoadFlags,
                            nsIURI * aReferringURI,
                            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;
    }
    
    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;
        nsCOMPtr<nsIURIFixupInfo> fixupInfo;
        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) {
            // CreateFixupURI 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) {
        DisplayLoadError(rv, uri, aURI, nullptr);
    }

    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->SetHeadersStream(aHeaderStream);
    loadInfo->SetBaseURI(aBaseURI);

    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)
{
    // 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_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) {
        // CSP error
        cssClass.AssignLiteral("neterror");
        error.AssignLiteral("cspFrameAncestorBlocked");
    }
    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) {
            // 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 a Strict-Transport-Security host and the cert
                // is bad, don't allow overrides (STS Spec section 7.3).
                uint32_t type = nsISiteSecurityService::HEADER_HSTS;
                uint32_t flags = mInPrivateBrowsing
                                 ? nsISocketProvider::NO_PERMANENT_STORAGE
                                 : 0;
                bool isStsHost = false;
                if (XRE_GetProcessType() == GeckoProcessType_Default) {
                  nsCOMPtr<nsISiteSecurityService> sss =
                            do_GetService(NS_SSSERVICE_CONTRACTID, &rv);
                  NS_ENSURE_SUCCESS(rv, rv);
                  rv = sss->IsSecureURI(type, aURI, flags, &isStsHost);
                  NS_ENSURE_SUCCESS(rv, rv);
                } else {
                  mozilla::dom::ContentChild* cc =
                    mozilla::dom::ContentChild::GetSingleton();
                  mozilla::ipc::URIParams uri;
                  SerializeURI(aURI, uri);
                  cc->SendIsSecureURI(type, uri, flags, &isStsHost);
                }

                uint32_t bucketId;
                if (isStsHost) {
                  cssClass.AssignLiteral("badStsCert");
                  //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;
                }


                if (Preferences::GetBool(
                        "browser.xul.error_pages.expert_bad_cert", false)) {
                    cssClass.AssignLiteral("expertBadCert");
                }

                // 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")) 
                    mozilla::Telemetry::Accumulate(mozilla::Telemetry::SECURITY_UI, bucketId);

            } else {
                error.AssignLiteral("nssFailure2");
            }
        }
    } else if (NS_ERROR_PHISHING_URI == aError || NS_ERROR_MALWARE_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;
        if (NS_ERROR_PHISHING_URI == aError) {
            error.AssignLiteral("phishingBlocked");
            bucketId = IsFrame() ? nsISecurityUITelemetry::WARNING_PHISHING_PAGE_FRAME :
                                   nsISecurityUITelemetry::WARNING_PHISHING_PAGE_TOP ;
        } else {
            error.AssignLiteral("malwareBlocked");
            bucketId = IsFrame() ? nsISecurityUITelemetry::WARNING_MALWARE_PAGE_FRAME :
                                   nsISecurityUITelemetry::WARNING_MALWARE_PAGE_TOP ;
        }

        if (errorPage.EqualsIgnoreCase("blocked"))
            mozilla::Telemetry::Accumulate(mozilla::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(MOZ_UTF16(" "));
      }
    }
    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("corruptedContentError");
            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 (UseErrorPages()) {
        // Display an error page
        LoadErrorPage(aURI, aURL, errorPage.get(), error.get(),
                      messageStr.get(), cssClass.get(), aFailedChannel);
    } 
    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;
}


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(PR_LOGGING) && defined(DEBUG)
    if (PR_LOG_TEST(gDocShellLog, PR_LOG_DEBUG)) {
        nsAutoCString spec;
        aURI->GetSpec(spec);

        nsAutoCString chanName;
        if (aFailedChannel)
            aFailedChannel->GetName(chanName);
        else
            chanName.AssignLiteral("<no channel>");

        PR_LOG(gDocShellLog, PR_LOG_DEBUG,
               ("nsDocShell[%p]::LoadErrorPage(\"%s\", \"%s\", {...}, [%s])\n", this,
                spec.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(cstring, escArg1, escArg2)  \
    {                                           \
        char* s = nsEscape(escArg1, escArg2);   \
        if (!s)                                 \
            return NS_ERROR_OUT_OF_MEMORY;      \
        cstring.Adopt(s);                       \
    }
    nsCString escapedUrl, escapedCharset, escapedError, escapedDescription,
              escapedCSSClass;
    SAFE_ESCAPE(escapedUrl, url.get(), url_Path);
    SAFE_ESCAPE(escapedCharset, charset.get(), url_Path);
    SAFE_ESCAPE(escapedError,
                NS_ConvertUTF16toUTF8(aErrorType).get(), url_Path);
    SAFE_ESCAPE(escapedDescription,
                NS_ConvertUTF16toUTF8(aDescription).get(), url_Path);
    if (aCSSClass) {
        SAFE_ESCAPE(escapedCSSClass, aCSSClass, url_Path);
    }
    nsCString errorPageUrl("about:");
    errorPageUrl.AppendASCII(aErrorPage);
    errorPageUrl.AppendLiteral("?e=");

    errorPageUrl.AppendASCII(escapedError.get());
    errorPageUrl.AppendLiteral("&u=");
    errorPageUrl.AppendASCII(escapedUrl.get());
    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).get(),
                  url_Path);
      errorPageUrl.AppendLiteral("&m=");
      errorPageUrl.AppendASCII(manifestParam.get());
    }

    // 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, nullptr,
                        INTERNAL_LOAD_FLAGS_INHERIT_OWNER, nullptr, 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());

        // Do not inherit owner from document
        uint32_t flags = INTERNAL_LOAD_FLAGS_NONE;
        nsAutoString srcdoc;
        nsIPrincipal* principal = nullptr;
        nsAutoString contentTypeHint;
        nsCOMPtr<nsIURI> baseURI;
        if (doc) {
            principal = doc->NodePrincipal();
            doc->GetContentType(contentTypeHint);

            if (doc->IsSrcdocDocument()) {
                doc->GetSrcdocData(srcdoc);
                flags |= INTERNAL_LOAD_FLAGS_IS_SRCDOC;
                baseURI = doc->GetBaseURI();
            }
        }
        rv = InternalLoad(mCurrentURI,
                          mReferrerURI,
                          principal,
                          flags,
                          nullptr,         // 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);
    }

    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 parentNativeWindow,
                       nsIWidget * parentWidget, int32_t x, int32_t y,
                       int32_t cx, int32_t cy)
{
    SetParentWidget(parentWidget);
    SetPositionAndSize(x, y, cx, cy, false);

    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;
    }

    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;

    // 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 (mInPrivateBrowsing) {
        mInPrivateBrowsing = 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::SetPosition(int32_t x, int32_t y)
{
    mBounds.x = x;
    mBounds.y = y;

    if (mContentViewer)
        NS_ENSURE_SUCCESS(mContentViewer->Move(x, y), NS_ERROR_FAILURE);

    return NS_OK;
}

NS_IMETHODIMP
nsDocShell::GetPosition(int32_t * aX, int32_t * aY)
{
    int32_t dummyHolder;
    return GetPositionAndSize(aX, aY, &dummyHolder, &dummyHolder);
}

NS_IMETHODIMP
nsDocShell::SetSize(int32_t aCX, int32_t aCY, bool aRepaint)
{
    int32_t x = 0, y = 0;
    GetPosition(&x, &y);
    return SetPositionAndSize(x, y, aCX, aCY, aRepaint);
}

NS_IMETHODIMP
nsDocShell::GetSize(int32_t * aCX, int32_t * aCY)
{
    int32_t dummyHolder;
    return GetPositionAndSize(&dummyHolder, &dummyHolder, aCX, aCY);
}

NS_IMETHODIMP
nsDocShell::SetPositionAndSize(int32_t x, int32_t y, int32_t cx,
                               int32_t cy, bool fRepaint)
{
    mBounds.x = x;
    mBounds.y = y;
    mBounds.width = cx;
    mBounds.height = cy;

    // Hold strong ref, since SetBounds can make us null out mContentViewer
    nsCOMPtr<nsIContentViewer> viewer = mContentViewer;
    if (viewer) {
        //XXX Border figured in here or is that handled elsewhere?
        NS_ENSURE_SUCCESS(viewer->SetBounds(mBounds), NS_ERROR_FAILURE);
    }

    return NS_OK;
}

NS_IMETHODIMP
nsDocShell::GetPositionAndSize(int32_t * x, int32_t * y, int32_t * cx,
                               int32_t * cy)
{
    if (mParentWidget) {
        // ensure size is up-to-date if window has changed resolution
        nsIntRect r;
        mParentWidget->GetClientBounds(r);
        SetPositionAndSize(mBounds.x, mBounds.y, r.width, r.height, false);
    }

    // We should really consider just getting this information from
    // our window instead of duplicating the storage and code...
    if (cx || cy) {
        // 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(x, y, cx, cy);
    return NS_OK;
}

void
nsDocShell::DoGetPositionAndSize(int32_t * x, int32_t * y, int32_t * cx,
                                 int32_t * cy)
{    
    if (x)
        *x = mBounds.x;
    if (y)
        *y = mBounds.y;
    if (cx)
        *cx = mBounds.width;
    if (cy)
        *cy = 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 ** parentWidget)
{
    NS_ENSURE_ARG_POINTER(parentWidget);

    *parentWidget = mParentWidget;
    NS_IF_ADDREF(*parentWidget);

    return NS_OK;
}

NS_IMETHODIMP
nsDocShell::SetParentWidget(nsIWidget * aParentWidget)
{
    mParentWidget = aParentWidget;

    return NS_OK;
}

NS_IMETHODIMP
nsDocShell::GetParentNativeWindow(nativeWindow * parentNativeWindow)
{
    NS_ENSURE_ARG_POINTER(parentNativeWindow);

    if (mParentWidget)
        *parentNativeWindow = mParentWidget->GetNativeData(NS_NATIVE_WIDGET);
    else
        *parentNativeWindow = nullptr;

    return NS_OK;
}

NS_IMETHODIMP
nsDocShell::SetParentNativeWindow(nativeWindow parentNativeWindow)
{
    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.

    nsRefPtr<nsDocShell> docShell = this;
    nsRefPtr<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;

    // 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()) {
            doc->PostVisibilityUpdateEvent();
        }
    }

    // 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->GetIsBrowserOrApp()) {
            docshell->SetIsActive(aIsActive);
        }
    }

    return NS_OK;
}

NS_IMETHODIMP
nsDocShell::GetIsActive(bool *aIsActive)
{
    *aIsActive = mIsActive;
    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);
    NS_IF_ADDREF(*aSandboxedNavigator = permittedNavigator);
    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_WARN_IF_FALSE(
         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);
    }

    if (mCurrentURI && mLoadType != LOAD_ERROR_PAGE && mUseGlobalHistory &&
        !mInPrivateBrowsing) {
        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 scrollOrientation, int32_t * curPos)
{
    NS_ENSURE_ARG_POINTER(curPos);

    nsIScrollableFrame* sf = GetRootScrollFrame();
    NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);

    nsPoint pt = sf->GetScrollPosition();

    switch (scrollOrientation) {
    case ScrollOrientation_X:
        *curPos = pt.x;
        return NS_OK;

    case ScrollOrientation_Y:
        *curPos = pt.y;
        return NS_OK;

    default:
        NS_ENSURE_TRUE(false, NS_ERROR_INVALID_ARG);
    }
}

nsresult
nsDocShell::SetCurScrollPosEx(int32_t curHorizontalPos, int32_t curVerticalPos)
{
    nsIScrollableFrame* sf = GetRootScrollFrame();
    NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);

    sf->ScrollTo(nsPoint(curHorizontalPos, curVerticalPos),
                 nsIScrollableFrame::INSTANT);
    return NS_OK;
}

//*****************************************************************************
// nsDocShell::nsIScrollable
//*****************************************************************************   

NS_IMETHODIMP
nsDocShell::GetDefaultScrollbarPreferences(int32_t scrollOrientation,
                                           int32_t * scrollbarPref)
{
    NS_ENSURE_ARG_POINTER(scrollbarPref);
    switch (scrollOrientation) {
    case ScrollOrientation_X:
        *scrollbarPref = mDefaultScrollbarPref.x;
        return NS_OK;

    case ScrollOrientation_Y:
        *scrollbarPref = mDefaultScrollbarPref.y;
        return NS_OK;

    default:
        NS_ENSURE_TRUE(false, NS_ERROR_INVALID_ARG);
    }
    return NS_ERROR_FAILURE;
}

NS_IMETHODIMP
nsDocShell::SetDefaultScrollbarPreferences(int32_t scrollOrientation,
                                           int32_t scrollbarPref)
{
    switch (scrollOrientation) {
    case ScrollOrientation_X:
        mDefaultScrollbarPref.x = scrollbarPref;
        return NS_OK;

    case ScrollOrientation_Y:
        mDefaultScrollbarPref.y = scrollbarPref;
        return NS_OK;

    default:
        NS_ENSURE_TRUE(false, NS_ERROR_INVALID_ARG);
    }
    return NS_ERROR_FAILURE;
}

NS_IMETHODIMP
nsDocShell::GetScrollbarVisibility(bool * verticalVisible,
                                   bool * horizontalVisible)
{
    nsIScrollableFrame* sf = GetRootScrollFrame();
    NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);

    uint32_t scrollbarVisibility = sf->GetScrollbarVisibility();
    if (verticalVisible)
        *verticalVisible = (scrollbarVisibility & nsIScrollableFrame::VERTICAL) != 0;
    if (horizontalVisible)
        *horizontalVisible = (scrollbarVisibility & nsIScrollableFrame::HORIZONTAL) != 0;

    return NS_OK;
}

//*****************************************************************************
// nsDocShell::nsITextScroll
//*****************************************************************************   

NS_IMETHODIMP
nsDocShell::ScrollByLines(int32_t numLines)
{
    nsIScrollableFrame* sf = GetRootScrollFrame();
    NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);

    sf->ScrollBy(nsIntPoint(0, numLines), nsIScrollableFrame::LINES,
                 nsIScrollableFrame::SMOOTH);
    return NS_OK;
}

NS_IMETHODIMP
nsDocShell::ScrollByPages(int32_t numPages)
{
    nsIScrollableFrame* sf = GetRootScrollFrame();
    NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);

    sf->ScrollBy(nsIntPoint(0, numPages), nsIScrollableFrame::PAGES,
                 nsIScrollableFrame::SMOOTH);
    return NS_OK;
}

//*****************************************************************************
// nsDocShell::nsIRefreshURI
//*****************************************************************************   

NS_IMETHODIMP
nsDocShell::RefreshURI(nsIURI * aURI, int32_t aDelay, bool aRepeat,
                       bool aMetaRefresh)
{
    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();
    NS_ENSURE_TRUE(refreshTimer, NS_ERROR_OUT_OF_MEMORY);
    uint32_t busyFlags = 0;
    GetBusyFlags(&busyFlags);

    nsCOMPtr<nsISupports> dataRef = refreshTimer;    // Get the ref count to 1

    refreshTimer->mDocShell = this;
    refreshTimer->mURI = aURI;
    refreshTimer->mDelay = aDelay;
    refreshTimer->mRepeat = aRepeat;
    refreshTimer->mMetaRefresh = aMetaRefresh;

    if (!mRefreshURIList) {
        NS_ENSURE_SUCCESS(NS_NewISupportsArray(getter_AddRefs(mRefreshURIList)),
                          NS_ERROR_FAILURE);
    }

    if (busyFlags & BUSY_FLAGS_BUSY) {
        // We are busy loading another page. Don't create the
        // timer right now. Instead queue up the request and trigger the
        // timer in EndPageLoad(). 
        mRefreshURIList->AppendElement(refreshTimer);
    }
    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);      // 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)
{
    NS_PRECONDITION(aTimer, "Must have a timer here");

    // Remove aTimer from mRefreshURIList if needed
    if (mRefreshURIList) {
        uint32_t n = 0;
        mRefreshURIList->Count(&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);
}

NS_IMETHODIMP
nsDocShell::ForceRefreshURI(nsIURI * aURI,
                            int32_t aDelay, 
                            bool aMetaRefresh)
{
    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 owner to use to avoid picking
     * the current owner.
     */
    loadInfo->SetOwnerIsExplicit(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);
    }

    /*
     * LoadURI(...) will cancel all refresh timers... This causes the
     * Timer and its refreshData instance to be released...
     */
    LoadURI(aURI, loadInfo, nsIWebNavigation::LOAD_FLAGS_NONE, 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);
            }
        }
    }
    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->GetChannelPrincipal(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(nsISupportsArray* aTimerList)
{
    if (!aTimerList)
        return;

    uint32_t n=0;
    aTimerList->Count(&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* _retval)
{
    if (!mRefreshURIList) {
        *_retval = false;
        return NS_OK;
    }

    uint32_t count;
    nsresult rv = mRefreshURIList->Count(&count);
    if (NS_SUCCEEDED(rv))
        *_retval = (count != 0);
    return rv;
}

NS_IMETHODIMP
nsDocShell::SuspendRefreshURIs()
{
    if (mRefreshURIList) {
        uint32_t n = 0;
        mRefreshURIList->Count(&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);
        }
    }

    // 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->Count(&n);

    while (n) {
        nsCOMPtr<nsISupports> element;
        mRefreshURIList->GetElementAt(--n, getter_AddRefs(element));
        nsCOMPtr<nsITimerCallback> refreshInfo(do_QueryInterface(element));

        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);
                timer->InitWithCallback(refreshInfo, delay, nsITimer::TYPE_ONE_SHOT);
            }