tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsDocShell.cpp (538250B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "nsDocShell.h"
      8 
      9 #include <algorithm>
     10 #include "mozilla/dom/HTMLFormElement.h"
     11 
     12 #ifdef XP_WIN
     13 #  include <process.h>
     14 #  define getpid _getpid
     15 #else
     16 #  include <unistd.h>  // for getpid()
     17 #endif
     18 
     19 #include "nsDeviceContext.h"
     20 #include "mozilla/Attributes.h"
     21 #include "mozilla/AutoRestore.h"
     22 #include "mozilla/BasePrincipal.h"
     23 #include "mozilla/Casting.h"
     24 #include "mozilla/CheckedInt.h"
     25 #include "mozilla/Components.h"
     26 #include "mozilla/Encoding.h"
     27 #include "mozilla/EventStateManager.h"
     28 #include "mozilla/HTMLEditor.h"
     29 #include "mozilla/InputTaskManager.h"
     30 #include "mozilla/LoadInfo.h"
     31 #include "mozilla/Logging.h"
     32 #include "mozilla/MediaFeatureChange.h"
     33 #include "mozilla/Preferences.h"
     34 #include "mozilla/PresShell.h"
     35 #include "mozilla/SchedulerGroup.h"
     36 #include "mozilla/ScopeExit.h"
     37 #include "mozilla/ScrollContainerFrame.h"
     38 #include "mozilla/ScrollTypes.h"
     39 #include "mozilla/SimpleEnumerator.h"
     40 #include "mozilla/StaticPrefs_browser.h"
     41 #include "mozilla/StaticPrefs_docshell.h"
     42 #include "mozilla/StaticPrefs_dom.h"
     43 #include "mozilla/StaticPrefs_extensions.h"
     44 #include "mozilla/StaticPrefs_privacy.h"
     45 #include "mozilla/StaticPrefs_security.h"
     46 #include "mozilla/StaticPrefs_ui.h"
     47 #include "mozilla/StaticPrefs_fission.h"
     48 #include "mozilla/StartupTimeline.h"
     49 #include "mozilla/StorageAccess.h"
     50 #include "mozilla/StoragePrincipalHelper.h"
     51 #include "mozilla/Telemetry.h"
     52 
     53 #include "mozilla/WidgetUtils.h"
     54 
     55 #include "mozilla/dom/AutoEntryScript.h"
     56 #include "mozilla/dom/ChildProcessChannelListener.h"
     57 #include "mozilla/dom/ClientChannelHelper.h"
     58 #include "mozilla/dom/ClientHandle.h"
     59 #include "mozilla/dom/ClientInfo.h"
     60 #include "mozilla/dom/ClientManager.h"
     61 #include "mozilla/dom/ClientSource.h"
     62 #include "mozilla/dom/ContentChild.h"
     63 #include "mozilla/dom/ContentFrameMessageManager.h"
     64 #include "mozilla/dom/DocGroup.h"
     65 #include "mozilla/dom/Element.h"
     66 #include "mozilla/dom/FragmentDirective.h"
     67 #include "mozilla/dom/HTMLAnchorElement.h"
     68 #include "mozilla/dom/HTMLIFrameElement.h"
     69 #include "mozilla/dom/Navigation.h"
     70 #include "mozilla/dom/NavigationBinding.h"
     71 #include "mozilla/dom/NavigationHistoryEntry.h"
     72 #include "mozilla/dom/NavigationUtils.h"
     73 #include "mozilla/dom/PerformanceNavigation.h"
     74 #include "mozilla/dom/PermissionMessageUtils.h"
     75 #include "mozilla/dom/PolicyContainer.h"
     76 #include "mozilla/dom/PopupBlocker.h"
     77 #include "mozilla/dom/ScreenOrientation.h"
     78 #include "mozilla/dom/ScriptSettings.h"
     79 #include "mozilla/dom/ServiceWorkerInterceptController.h"
     80 #include "mozilla/dom/ServiceWorkerUtils.h"
     81 #include "mozilla/dom/SessionHistoryEntry.h"
     82 #include "mozilla/dom/SessionStorageManager.h"
     83 #include "mozilla/dom/SessionStoreChangeListener.h"
     84 #include "mozilla/dom/SessionStoreChild.h"
     85 #include "mozilla/dom/SessionStoreUtils.h"
     86 #include "mozilla/dom/TrustedTypeUtils.h"
     87 #include "mozilla/dom/TrustedTypesConstants.h"
     88 #include "mozilla/dom/BrowserChild.h"
     89 #include "mozilla/dom/ToJSValue.h"
     90 #include "mozilla/dom/UserActivation.h"
     91 #include "mozilla/dom/ChildSHistory.h"
     92 #include "mozilla/dom/nsCSPContext.h"
     93 #include "mozilla/dom/nsHTTPSOnlyUtils.h"
     94 #include "mozilla/dom/LoadURIOptionsBinding.h"
     95 #include "mozilla/dom/JSWindowActorChild.h"
     96 #include "mozilla/dom/DocumentBinding.h"
     97 #include "mozilla/glean/DocshellMetrics.h"
     98 #include "mozilla/ipc/ProtocolUtils.h"
     99 #include "mozilla/net/DocumentChannel.h"
    100 #include "mozilla/net/DocumentChannelChild.h"
    101 #include "mozilla/net/ParentChannelWrapper.h"
    102 #include "mozilla/net/UrlClassifierFeatureFactory.h"
    103 #include "ReferrerInfo.h"
    104 
    105 #include "nsIAuthPrompt.h"
    106 #include "nsIAuthPrompt2.h"
    107 #include "nsICachingChannel.h"
    108 #include "nsICaptivePortalService.h"
    109 #include "nsIChannel.h"
    110 #include "nsIChannelEventSink.h"
    111 #include "nsIClassifiedChannel.h"
    112 #include "nsIClassOfService.h"
    113 #include "nsIConsoleReportCollector.h"
    114 #include "nsIContent.h"
    115 #include "nsIContentInlines.h"
    116 #include "nsIContentSecurityPolicy.h"
    117 #include "nsIController.h"
    118 #include "nsIDocShellTreeItem.h"
    119 #include "nsIDocShellTreeOwner.h"
    120 #include "nsIDocumentViewer.h"
    121 #include "mozilla/dom/Document.h"
    122 #include "nsHTMLDocument.h"
    123 #include "nsIDocumentLoaderFactory.h"
    124 #include "nsIDOMWindow.h"
    125 #include "nsIEditingSession.h"
    126 #include "nsIEffectiveTLDService.h"
    127 #include "nsIExternalProtocolService.h"
    128 #include "nsIFormPOSTActionChannel.h"
    129 #include "nsIFrame.h"
    130 #include "nsIGlobalObject.h"
    131 #include "nsIHttpChannel.h"
    132 #include "nsIHttpChannelInternal.h"
    133 #include "nsIIDNService.h"
    134 #include "nsIInputStreamChannel.h"
    135 #include "nsIInterfaceRequestorUtils.h"
    136 #include "nsILayoutHistoryState.h"
    137 #include "nsILoadInfo.h"
    138 #include "nsILoadURIDelegate.h"
    139 #include "nsIMultiPartChannel.h"
    140 #include "nsINestedURI.h"
    141 #include "nsINode.h"
    142 #include "nsINSSErrorsService.h"
    143 #include "nsIObserverService.h"
    144 #include "nsIOService.h"
    145 #include "nsIPrincipal.h"
    146 #include "nsIPrivacyTransitionObserver.h"
    147 #include "nsIPrompt.h"
    148 #include "nsIPromptCollection.h"
    149 #include "nsIPromptFactory.h"
    150 #include "nsIPublicKeyPinningService.h"
    151 #include "nsIReflowObserver.h"
    152 #include "nsIScriptChannel.h"
    153 #include "nsIScriptObjectPrincipal.h"
    154 #include "nsIScriptSecurityManager.h"
    155 #include "nsScriptSecurityManager.h"
    156 #include "nsIScrollObserver.h"
    157 #include "nsISupportsPrimitives.h"
    158 #include "nsISecureBrowserUI.h"
    159 #include "nsISeekableStream.h"
    160 #include "nsISelectionDisplay.h"
    161 #include "nsISHEntry.h"
    162 #include "nsISiteSecurityService.h"
    163 #include "nsISocketProvider.h"
    164 #include "nsIStringBundle.h"
    165 #include "nsIStructuredCloneContainer.h"
    166 #include "nsIBrowserChild.h"
    167 #include "nsITextToSubURI.h"
    168 #include "nsITimedChannel.h"
    169 #include "nsITimer.h"
    170 #include "nsITransportSecurityInfo.h"
    171 #include "nsIUploadChannel.h"
    172 #include "nsIURIFixup.h"
    173 #include "nsIURIMutator.h"
    174 #include "nsIURILoader.h"
    175 #include "nsIViewSourceChannel.h"
    176 #include "nsIWebBrowserChrome.h"
    177 #include "nsIWebBrowserFind.h"
    178 #include "nsIWebProgress.h"
    179 #include "nsIWidget.h"
    180 #include "nsIWindowWatcher.h"
    181 #include "nsIWritablePropertyBag2.h"
    182 #include "nsIX509Cert.h"
    183 #include "nsIXULRuntime.h"
    184 
    185 #include "nsCommandManager.h"
    186 #include "nsPIDOMWindow.h"
    187 #include "nsPIWindowRoot.h"
    188 
    189 #include "IHistory.h"
    190 #include "IUrlClassifierUITelemetry.h"
    191 
    192 #include "nsAboutProtocolUtils.h"
    193 #include "nsArray.h"
    194 #include "nsArrayUtils.h"
    195 #include "nsBrowserStatusFilter.h"
    196 #include "nsCExternalHandlerService.h"
    197 #include "nsContentDLF.h"
    198 #include "nsContentPolicyUtils.h"  // NS_CheckContentLoadPolicy(...)
    199 #include "nsContentSecurityManager.h"
    200 #include "nsContentSecurityUtils.h"
    201 #include "nsContentUtils.h"
    202 #include "nsCURILoader.h"
    203 #include "nsDocElementCreatedNotificationRunner.h"
    204 #include "nsDocShellCID.h"
    205 #include "nsDocShellEditorData.h"
    206 #include "nsDocShellEnumerator.h"
    207 #include "nsDocShellLoadState.h"
    208 #include "nsDocShellLoadTypes.h"
    209 #include "nsDOMCID.h"
    210 #include "nsDOMNavigationTiming.h"
    211 #include "nsDSURIContentListener.h"
    212 #include "nsEditingSession.h"
    213 #include "nsError.h"
    214 #include "nsEscape.h"
    215 #include "nsFocusManager.h"
    216 #include "nsGlobalWindowInner.h"
    217 #include "nsGlobalWindowOuter.h"
    218 #include "nsJSEnvironment.h"
    219 #include "nsNetCID.h"
    220 #include "nsNetUtil.h"
    221 #include "nsObjectLoadingContent.h"
    222 #include "nsPingListener.h"
    223 #include "nsPoint.h"
    224 #include "nsQueryObject.h"
    225 #include "nsQueryActor.h"
    226 #include "nsRect.h"
    227 #include "nsRefreshTimer.h"
    228 #include "nsSandboxFlags.h"
    229 #include "nsSHEntry.h"
    230 #include "nsSHistory.h"
    231 #include "nsSHEntry.h"
    232 #include "nsStructuredCloneContainer.h"
    233 #include "nsSubDocumentFrame.h"
    234 #include "nsURILoader.h"
    235 #include "nsURLHelper.h"
    236 #include "nsViewSourceHandler.h"
    237 #include "nsWebBrowserFind.h"
    238 #include "nsWhitespaceTokenizer.h"
    239 #include "nsWidgetsCID.h"
    240 #include "nsXULAppAPI.h"
    241 
    242 #include "CertVerifier.h"
    243 #include "ThirdPartyUtil.h"
    244 #include "GeckoProfiler.h"
    245 #include "mozilla/NullPrincipal.h"
    246 #include "Navigator.h"
    247 #include "prenv.h"
    248 #include "mozilla/ipc/URIUtils.h"
    249 #include "sslerr.h"
    250 #include "mozpkix/pkix.h"
    251 #include "NSSErrorsService.h"
    252 
    253 #include "nsDocShellTelemetryUtils.h"
    254 
    255 #include "nsIOpenWindowInfo.h"
    256 
    257 #ifdef MOZ_PLACES
    258 #  include "mozilla/places/nsFaviconService.h"
    259 #  include "mozIPlacesPendingOperation.h"
    260 #endif
    261 
    262 #if NS_PRINT_PREVIEW
    263 #  include "nsIDocumentViewerPrint.h"
    264 #  include "nsIWebBrowserPrint.h"
    265 #endif
    266 
    267 using namespace mozilla;
    268 using namespace mozilla::dom;
    269 using namespace mozilla::net;
    270 
    271 using mozilla::ipc::Endpoint;
    272 
    273 // Threshold value in ms for META refresh based redirects
    274 #define REFRESH_REDIRECT_TIMER 15000
    275 
    276 static mozilla::LazyLogModule gCharsetMenuLog("CharsetMenu");
    277 static mozilla::LazyLogModule gDocShellLog("nsDocShell");
    278 
    279 #define LOGCHARSETMENU(args) \
    280  MOZ_LOG(gCharsetMenuLog, mozilla::LogLevel::Debug, args)
    281 
    282 #ifdef DEBUG
    283 unsigned long nsDocShell::gNumberOfDocShells = 0;
    284 static uint64_t gDocshellIDCounter = 0;
    285 
    286 static mozilla::LazyLogModule gDocShellAndDOMWindowLeakLogging(
    287    "DocShellAndDOMWindowLeak");
    288 #endif
    289 static mozilla::LazyLogModule gDocShellLeakLog("nsDocShellLeak");
    290 extern mozilla::LazyLogModule gPageCacheLog;
    291 extern mozilla::LazyLogModule gNavigationAPILog;
    292 mozilla::LazyLogModule gSHLog("SessionHistory");
    293 extern mozilla::LazyLogModule gSHIPBFCacheLog;
    294 
    295 const char kAppstringsBundleURL[] =
    296    "chrome://global/locale/appstrings.properties";
    297 
    298 static bool IsTopLevelDoc(BrowsingContext* aBrowsingContext,
    299                          nsILoadInfo* aLoadInfo) {
    300  MOZ_ASSERT(aBrowsingContext);
    301  MOZ_ASSERT(aLoadInfo);
    302 
    303  if (aLoadInfo->GetExternalContentPolicyType() !=
    304      ExtContentPolicy::TYPE_DOCUMENT) {
    305    return false;
    306  }
    307 
    308  return aBrowsingContext->IsTopContent();
    309 }
    310 
    311 // True if loading for top level document loading in active tab.
    312 static bool IsUrgentStart(BrowsingContext* aBrowsingContext,
    313                          nsILoadInfo* aLoadInfo, uint32_t aLoadType) {
    314  MOZ_ASSERT(aBrowsingContext);
    315  MOZ_ASSERT(aLoadInfo);
    316 
    317  if (!IsTopLevelDoc(aBrowsingContext, aLoadInfo)) {
    318    return false;
    319  }
    320 
    321  if (aLoadType &
    322      (nsIDocShell::LOAD_CMD_NORMAL | nsIDocShell::LOAD_CMD_HISTORY)) {
    323    return true;
    324  }
    325 
    326  return aBrowsingContext->IsActive();
    327 }
    328 
    329 nsDocShell::nsDocShell(BrowsingContext* aBrowsingContext,
    330                       uint64_t aContentWindowID)
    331    : nsDocLoader(true),
    332      mContentWindowID(aContentWindowID),
    333      mBrowsingContext(aBrowsingContext),
    334      mParentCharset(nullptr),
    335      mTreeOwner(nullptr),
    336      mScrollbarPref(ScrollbarPreference::Auto),
    337      mCharsetReloadState(eCharsetReloadInit),
    338      mParentCharsetSource(0),
    339      mFrameMargins(-1, -1),
    340      mItemType(aBrowsingContext->IsContent() ? typeContent : typeChrome),
    341      mPreviousEntryIndex(-1),
    342      mLoadedEntryIndex(-1),
    343      mBusyFlags(BUSY_FLAGS_NONE),
    344      mAppType(nsIDocShell::APP_TYPE_UNKNOWN),
    345      mLoadType(0),
    346      mFailedLoadType(0),
    347      mChannelToDisconnectOnPageHide(0),
    348      mCreatingDocument(false),
    349 #ifdef DEBUG
    350      mInEnsureScriptEnv(false),
    351 #endif
    352      mInitialized(false),
    353      mAllowSubframes(true),
    354      mAllowMetaRedirects(true),
    355      mAllowImages(true),
    356      mAllowMedia(true),
    357      mAllowDNSPrefetch(true),
    358      mAllowWindowControl(true),
    359      mCSSErrorReportingEnabled(false),
    360      mAllowAuth(mItemType == typeContent),
    361      mAllowKeywordFixup(false),
    362      mDisableMetaRefreshWhenInactive(false),
    363      mWindowDraggingAllowed(false),
    364      mInFrameSwap(false),
    365      mFiredUnloadEvent(false),
    366      mEODForCurrentDocument(false),
    367      mURIResultedInDocument(false),
    368      mIsBeingDestroyed(false),
    369      mIsExecutingOnLoadHandler(false),
    370      mSavingOldViewer(false),
    371      mInvisible(false),
    372      mHasLoadedNonBlankURI(false),
    373      mHasStartedLoadingOtherThanInitialBlankURI(false),
    374      mBlankTiming(false),
    375      mTitleValidForCurrentURI(false),
    376      mWillChangeProcess(false),
    377      mIsNavigating(false),
    378      mForcedAutodetection(false),
    379      mCheckingSessionHistory(false),
    380      mNeedToReportActiveAfterLoadingBecomesActive(false) {
    381  // If no outer window ID was provided, generate a new one.
    382  if (aContentWindowID == 0) {
    383    mContentWindowID = nsContentUtils::GenerateWindowId();
    384  }
    385 
    386  MOZ_LOG(gDocShellLeakLog, LogLevel::Debug, ("DOCSHELL %p created\n", this));
    387 
    388 #ifdef DEBUG
    389  mDocShellID = gDocshellIDCounter++;
    390  // We're counting the number of |nsDocShells| to help find leaks
    391  ++gNumberOfDocShells;
    392  MOZ_LOG(gDocShellAndDOMWindowLeakLogging, LogLevel::Info,
    393          ("++DOCSHELL %p == %ld [pid = %d] [id = %" PRIu64 "]\n", (void*)this,
    394           gNumberOfDocShells, getpid(), mDocShellID));
    395 #endif
    396 }
    397 
    398 void nsDocShell::DestroyDocumentViewer() {
    399  if (!mDocumentViewer) {
    400    return;
    401  }
    402  mDocumentViewer->Close(nullptr);
    403  mDocumentViewer->Destroy();
    404  mDocumentViewer = nullptr;
    405 }
    406 
    407 nsDocShell::~nsDocShell() {
    408  // Avoid notifying observers while we're in the dtor.
    409  mIsBeingDestroyed = true;
    410 
    411  Destroy();
    412 
    413  DestroyDocumentViewer();
    414 
    415  MOZ_LOG(gDocShellLeakLog, LogLevel::Debug, ("DOCSHELL %p destroyed\n", this));
    416 
    417 #ifdef DEBUG
    418  if (MOZ_LOG_TEST(gDocShellAndDOMWindowLeakLogging, LogLevel::Info)) {
    419    nsAutoCString url;
    420    if (mLastOpenedURI) {
    421      url = mLastOpenedURI->GetSpecOrDefault();
    422 
    423      // Data URLs can be very long, so truncate to avoid flooding the log.
    424      const uint32_t maxURLLength = 1000;
    425      if (url.Length() > maxURLLength) {
    426        url.Truncate(maxURLLength);
    427      }
    428    }
    429 
    430    // We're counting the number of |nsDocShells| to help find leaks
    431    --gNumberOfDocShells;
    432    MOZ_LOG(
    433        gDocShellAndDOMWindowLeakLogging, LogLevel::Info,
    434        ("--DOCSHELL %p == %ld [pid = %d] [id = %" PRIu64 "] [url = %s]\n",
    435         (void*)this, gNumberOfDocShells, getpid(), mDocShellID, url.get()));
    436  }
    437 #endif
    438 }
    439 
    440 nsresult nsDocShell::InitWindow(nsIWidget* aParentWidget, int32_t aX,
    441                                int32_t aY, int32_t aWidth, int32_t aHeight,
    442                                nsIOpenWindowInfo* aOpenWindowInfo,
    443                                mozilla::dom::WindowGlobalChild* aWindowActor) {
    444  SetParentWidget(aParentWidget);
    445  SetPositionAndSize(aX, aY, aWidth, aHeight, 0);
    446  if (!Initialize(aOpenWindowInfo, aWindowActor)) {
    447    // Since bug 543435, Initialize can fail and propagating this would
    448    // cause callers to crash when they didn't previously (bug 2003244).
    449    NS_WARNING("Failed to initialize docshell");
    450  }
    451 
    452  return NS_OK;
    453 }
    454 
    455 bool nsDocShell::Initialize(nsIOpenWindowInfo* aOpenWindowInfo,
    456                            mozilla::dom::WindowGlobalChild* aWindowActor) {
    457  if (mInitialized) {
    458    // We've already been initialized.
    459    MOZ_ASSERT(!aOpenWindowInfo,
    460               "Tried to reinitialize with override principal");
    461    MOZ_ASSERT(!aWindowActor, "Tried to reinitialize with a window actor");
    462    return true;
    463  }
    464 
    465  MOZ_ASSERT(aOpenWindowInfo,
    466             "Must have openwindowinfo if not already initialized.");
    467 
    468  NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome,
    469               "Unexpected item type in docshell");
    470 
    471  NS_ENSURE_TRUE(Preferences::GetRootBranch(), false);
    472  mInitialized = true;
    473 
    474  mDisableMetaRefreshWhenInactive =
    475      Preferences::GetBool("browser.meta_refresh_when_inactive.disabled",
    476                           mDisableMetaRefreshWhenInactive);
    477 
    478  bool succeeded =
    479      NS_SUCCEEDED(CreateInitialDocumentViewer(aOpenWindowInfo, aWindowActor));
    480 
    481  if (nsCOMPtr<nsIObserverService> serv = services::GetObserverService()) {
    482    const char* msg = mItemType == typeContent ? NS_WEBNAVIGATION_CREATE
    483                                               : NS_CHROME_WEBNAVIGATION_CREATE;
    484    serv->NotifyWhenScriptSafe(GetAsSupports(this), msg, nullptr);
    485  }
    486 
    487  return succeeded;
    488 }
    489 
    490 /* static */
    491 already_AddRefed<nsDocShell> nsDocShell::Create(
    492    BrowsingContext* aBrowsingContext, uint64_t aContentWindowID) {
    493  MOZ_ASSERT(aBrowsingContext, "DocShell without a BrowsingContext!");
    494 
    495  nsresult rv;
    496  RefPtr<nsDocShell> ds = new nsDocShell(aBrowsingContext, aContentWindowID);
    497 
    498  // Initialize the underlying nsDocLoader.
    499  rv = ds->nsDocLoader::InitWithBrowsingContext(aBrowsingContext);
    500  if (NS_WARN_IF(NS_FAILED(rv))) {
    501    return nullptr;
    502  }
    503 
    504  // Create our ContentListener
    505  ds->mContentListener = new nsDSURIContentListener(ds);
    506 
    507  // We enable if we're in the parent process in order to support non-e10s
    508  // configurations.
    509  // Note: This check is duplicated in SharedWorkerInterfaceRequestor's
    510  // constructor.
    511  if (XRE_IsParentProcess()) {
    512    ds->mInterceptController = new ServiceWorkerInterceptController();
    513  }
    514 
    515  // We want to hold a strong ref to the loadgroup, so it better hold a weak
    516  // ref to us...  use an InterfaceRequestorProxy to do this.
    517  nsCOMPtr<nsIInterfaceRequestor> proxy = new InterfaceRequestorProxy(ds);
    518  ds->mLoadGroup->SetNotificationCallbacks(proxy);
    519 
    520  // XXX(nika): We have our BrowsingContext, so we might be able to skip this.
    521  // It could be nice to directly set up our DocLoader tree?
    522  rv = nsDocLoader::AddDocLoaderAsChildOfRoot(ds);
    523  if (NS_WARN_IF(NS_FAILED(rv))) {
    524    return nullptr;
    525  }
    526 
    527  uint32_t notifyMask =
    528      nsIWebProgress::NOTIFY_STATE_ALL | nsIWebProgress::NOTIFY_LOCATION |
    529      nsIWebProgress::NOTIFY_SECURITY | nsIWebProgress::NOTIFY_STATUS;
    530 
    531  // NOTE: Only listen for NOTIFY_PROGRESS on toplevel BrowsingContexts, as
    532  // listeners in the browser UI only cares about total progress on the toplevel
    533  // context. Aggregation of the total progress is currently handled within
    534  // `nsDocLoader`, and does not take out-of-process iframes into account.
    535  if (aBrowsingContext->IsTop()) {
    536    notifyMask |= nsIWebProgress::NOTIFY_PROGRESS;
    537  }
    538 
    539  // Add |ds| as a progress listener to itself.  A little weird, but simpler
    540  // than reproducing all the listener-notification logic in overrides of the
    541  // various methods via which nsDocLoader can be notified.   Note that this
    542  // holds an nsWeakPtr to |ds|, so it's ok.
    543  rv = ds->AddProgressListener(ds, notifyMask);
    544  if (NS_WARN_IF(NS_FAILED(rv))) {
    545    return nullptr;
    546  }
    547 
    548  // If our BrowsingContext has private browsing enabled, update the number of
    549  // private browsing docshells.
    550  if (aBrowsingContext->UsePrivateBrowsing()) {
    551    ds->NotifyPrivateBrowsingChanged();
    552  }
    553 
    554  // If our parent window is present in this process, set up our parent now.
    555  RefPtr<WindowContext> parentWC = aBrowsingContext->GetParentWindowContext();
    556  if (parentWC && parentWC->IsInProcess()) {
    557    // If we don't have a parent element anymore, we can't finish this load!
    558    // How'd we get here?
    559    RefPtr<Element> parentElement = aBrowsingContext->GetEmbedderElement();
    560    if (!parentElement) {
    561      MOZ_ASSERT_UNREACHABLE("nsDocShell::Create() - !parentElement");
    562      return nullptr;
    563    }
    564 
    565    // We have an in-process parent window, but don't have a parent nsDocShell?
    566    // How'd we get here!
    567    nsCOMPtr<nsIDocShell> parentShell =
    568        parentElement->OwnerDoc()->GetDocShell();
    569    if (!parentShell) {
    570      MOZ_ASSERT_UNREACHABLE("nsDocShell::Create() - !parentShell");
    571      return nullptr;
    572    }
    573    parentShell->AddChild(ds);
    574  }
    575 
    576  // Make |ds| the primary DocShell for the given context.
    577  aBrowsingContext->SetDocShell(ds);
    578 
    579  // Set |ds| default load flags on load group.
    580  ds->SetLoadGroupDefaultLoadFlags(aBrowsingContext->GetDefaultLoadFlags());
    581 
    582  return ds.forget();
    583 }
    584 
    585 void nsDocShell::DestroyChildren() {
    586  for (auto* child : mChildList.ForwardRange()) {
    587    nsCOMPtr<nsIDocShellTreeItem> shell = do_QueryObject(child);
    588    NS_ASSERTION(shell, "docshell has null child");
    589 
    590    if (shell) {
    591      shell->SetTreeOwner(nullptr);
    592    }
    593  }
    594 
    595  nsDocLoader::DestroyChildren();
    596 }
    597 
    598 NS_IMPL_CYCLE_COLLECTION_WEAK_PTR_INHERITED(nsDocShell, nsDocLoader,
    599                                            mScriptGlobal, mInitialClientSource,
    600                                            mBrowsingContext,
    601                                            mChromeEventHandler,
    602                                            mBCWebProgressStatusFilter)
    603 
    604 NS_IMPL_ADDREF_INHERITED(nsDocShell, nsDocLoader)
    605 NS_IMPL_RELEASE_INHERITED(nsDocShell, nsDocLoader)
    606 
    607 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDocShell)
    608  NS_INTERFACE_MAP_ENTRY(nsIDocShell)
    609  NS_INTERFACE_MAP_ENTRY(nsIDocShellTreeItem)
    610  NS_INTERFACE_MAP_ENTRY(nsIWebNavigation)
    611  NS_INTERFACE_MAP_ENTRY(nsIBaseWindow)
    612  NS_INTERFACE_MAP_ENTRY(nsIRefreshURI)
    613  NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
    614  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
    615  NS_INTERFACE_MAP_ENTRY(nsIWebPageDescriptor)
    616  NS_INTERFACE_MAP_ENTRY(nsIAuthPromptProvider)
    617  NS_INTERFACE_MAP_ENTRY(nsILoadContext)
    618  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsINetworkInterceptController,
    619                                     mInterceptController)
    620 NS_INTERFACE_MAP_END_INHERITING(nsDocLoader)
    621 
    622 NS_IMETHODIMP
    623 nsDocShell::GetInterface(const nsIID& aIID, void** aSink) {
    624  MOZ_ASSERT(aSink, "null out param");
    625 
    626  *aSink = nullptr;
    627 
    628  if (aIID.Equals(NS_GET_IID(nsICommandManager))) {
    629    NS_ENSURE_SUCCESS(EnsureCommandHandler(), NS_ERROR_FAILURE);
    630    *aSink = static_cast<nsICommandManager*>(mCommandManager.get());
    631  } else if (aIID.Equals(NS_GET_IID(nsIURIContentListener))) {
    632    *aSink = mContentListener;
    633  } else if ((aIID.Equals(NS_GET_IID(nsIScriptGlobalObject)) ||
    634              aIID.Equals(NS_GET_IID(nsIGlobalObject)) ||
    635              aIID.Equals(NS_GET_IID(nsPIDOMWindowOuter)) ||
    636              aIID.Equals(NS_GET_IID(mozIDOMWindowProxy)) ||
    637              aIID.Equals(NS_GET_IID(nsIDOMWindow))) &&
    638             NS_SUCCEEDED(EnsureScriptEnvironment())) {
    639    return mScriptGlobal->QueryInterface(aIID, aSink);
    640  } else if (aIID.Equals(NS_GET_IID(Document)) && VerifyDocumentViewer()) {
    641    RefPtr<Document> doc = mDocumentViewer->GetDocument();
    642    doc.forget(aSink);
    643    return *aSink ? NS_OK : NS_NOINTERFACE;
    644  } else if (aIID.Equals(NS_GET_IID(nsIPrompt)) &&
    645             NS_SUCCEEDED(EnsureScriptEnvironment())) {
    646    nsresult rv;
    647    nsCOMPtr<nsIWindowWatcher> wwatch =
    648        do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
    649    NS_ENSURE_SUCCESS(rv, rv);
    650 
    651    // Get the an auth prompter for our window so that the parenting
    652    // of the dialogs works as it should when using tabs.
    653    nsIPrompt* prompt;
    654    rv = wwatch->GetNewPrompter(mScriptGlobal, &prompt);
    655    NS_ENSURE_SUCCESS(rv, rv);
    656 
    657    *aSink = prompt;
    658    return NS_OK;
    659  } else if (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) ||
    660             aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) {
    661    return NS_SUCCEEDED(GetAuthPrompt(PROMPT_NORMAL, aIID, aSink))
    662               ? NS_OK
    663               : NS_NOINTERFACE;
    664  } else if (aIID.Equals(NS_GET_IID(nsISHistory))) {
    665    // This is deprecated, you should instead directly get
    666    // ChildSHistory from the browsing context.
    667    MOZ_DIAGNOSTIC_ASSERT(
    668        false, "Do not try to get a nsISHistory interface from nsIDocShell");
    669    return NS_NOINTERFACE;
    670  } else if (aIID.Equals(NS_GET_IID(nsIWebBrowserFind))) {
    671    nsresult rv = EnsureFind();
    672    if (NS_FAILED(rv)) {
    673      return rv;
    674    }
    675 
    676    *aSink = mFind;
    677    NS_ADDREF((nsISupports*)*aSink);
    678    return NS_OK;
    679  } else if (aIID.Equals(NS_GET_IID(nsISelectionDisplay))) {
    680    if (PresShell* presShell = GetPresShell()) {
    681      return presShell->QueryInterface(aIID, aSink);
    682    }
    683  } else if (aIID.Equals(NS_GET_IID(nsIDocShellTreeOwner))) {
    684    nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
    685    nsresult rv = GetTreeOwner(getter_AddRefs(treeOwner));
    686    if (NS_SUCCEEDED(rv) && treeOwner) {
    687      return treeOwner->QueryInterface(aIID, aSink);
    688    }
    689  } else if (aIID.Equals(NS_GET_IID(nsIBrowserChild))) {
    690    *aSink = GetBrowserChild().take();
    691    return *aSink ? NS_OK : NS_ERROR_FAILURE;
    692  } else {
    693    return nsDocLoader::GetInterface(aIID, aSink);
    694  }
    695 
    696  NS_IF_ADDREF(((nsISupports*)*aSink));
    697  return *aSink ? NS_OK : NS_NOINTERFACE;
    698 }
    699 
    700 NS_IMETHODIMP
    701 nsDocShell::SetCancelContentJSEpoch(int32_t aEpoch) {
    702  // Note: this gets called fairly early (before a pageload actually starts).
    703  // We could probably defer this even longer.
    704  nsCOMPtr<nsIBrowserChild> browserChild = GetBrowserChild();
    705  static_cast<BrowserChild*>(browserChild.get())
    706      ->SetCancelContentJSEpoch(aEpoch);
    707  return NS_OK;
    708 }
    709 
    710 nsresult nsDocShell::CheckDisallowedJavascriptLoad(
    711    nsDocShellLoadState* aLoadState) {
    712  if (!aLoadState->URI()->SchemeIs("javascript")) {
    713    return NS_OK;
    714  }
    715 
    716  if (nsCOMPtr<nsIPrincipal> targetPrincipal =
    717          GetInheritedPrincipal(/* aConsiderCurrentDocument */ true)) {
    718    if (!aLoadState->TriggeringPrincipal()->Subsumes(targetPrincipal)) {
    719      return NS_ERROR_DOM_BAD_CROSS_ORIGIN_URI;
    720    }
    721    return NS_OK;
    722  }
    723  return NS_ERROR_DOM_BAD_CROSS_ORIGIN_URI;
    724 }
    725 
    726 NS_IMETHODIMP
    727 nsDocShell::LoadURI(nsDocShellLoadState* aLoadState, bool aSetNavigating) {
    728  return LoadURI(aLoadState, aSetNavigating, false);
    729 }
    730 
    731 nsresult nsDocShell::LoadURI(nsDocShellLoadState* aLoadState,
    732                             bool aSetNavigating,
    733                             bool aContinueHandlingSubframeHistory) {
    734  MOZ_ASSERT(aLoadState, "Must have a valid load state!");
    735  // NOTE: This comparison between what appears to be internal/external load
    736  // flags is intentional, as it's ensuring that the caller isn't using any of
    737  // the flags reserved for implementations by the `nsIWebNavigation` interface.
    738  // In the future, this check may be dropped.
    739  MOZ_ASSERT(
    740      (aLoadState->LoadFlags() & INTERNAL_LOAD_FLAGS_LOADURI_SETUP_FLAGS) == 0,
    741      "Should not have these flags set");
    742  MOZ_ASSERT(aLoadState->TargetBrowsingContext().IsNull(),
    743             "Targeting doesn't occur until InternalLoad");
    744 
    745  if (!aLoadState->TriggeringPrincipal()) {
    746    MOZ_ASSERT(false, "LoadURI must have a triggering principal");
    747    return NS_ERROR_FAILURE;
    748  }
    749 
    750  MOZ_TRY(CheckDisallowedJavascriptLoad(aLoadState));
    751 
    752  bool oldIsNavigating = mIsNavigating;
    753  auto cleanupIsNavigating =
    754      MakeScopeExit([&]() { mIsNavigating = oldIsNavigating; });
    755  if (aSetNavigating) {
    756    mIsNavigating = true;
    757  }
    758 
    759  PopupBlocker::PopupControlState popupState = PopupBlocker::openOverridden;
    760  if (aLoadState->HasLoadFlags(LOAD_FLAGS_ALLOW_POPUPS)) {
    761    popupState = PopupBlocker::openAllowed;
    762    // If we allow popups as part of the navigation, ensure we fake a user
    763    // interaction, so that popups can, in fact, be allowed to open.
    764    if (WindowContext* wc = mBrowsingContext->GetCurrentWindowContext()) {
    765      wc->NotifyUserGestureActivation();
    766    }
    767  }
    768 
    769  AutoPopupStatePusher statePusher(popupState);
    770 
    771  if (aLoadState->GetCancelContentJSEpoch().isSome()) {
    772    SetCancelContentJSEpoch(*aLoadState->GetCancelContentJSEpoch());
    773  }
    774 
    775  // Note: we allow loads to get through here even if mFiredUnloadEvent is
    776  // true; that case will get handled in LoadInternal or LoadHistoryEntry,
    777  // so we pass false as the second parameter to IsNavigationAllowed.
    778  // However, we don't allow the page to change location *in the middle of*
    779  // firing beforeunload, so we do need to check if *beforeunload* is currently
    780  // firing, so we call IsNavigationAllowed rather than just IsPrintingOrPP.
    781  if (!IsNavigationAllowed(true, false)) {
    782    return NS_OK;  // JS may not handle returning of an error code
    783  }
    784 
    785  nsLoadFlags defaultLoadFlags = mBrowsingContext->GetDefaultLoadFlags();
    786  if (aLoadState->HasLoadFlags(LOAD_FLAGS_FORCE_TRR)) {
    787    defaultLoadFlags |= nsIRequest::LOAD_TRR_ONLY_MODE;
    788  } else if (aLoadState->HasLoadFlags(LOAD_FLAGS_DISABLE_TRR)) {
    789    defaultLoadFlags |= nsIRequest::LOAD_TRR_DISABLED_MODE;
    790  }
    791 
    792  MOZ_ALWAYS_SUCCEEDS(mBrowsingContext->SetDefaultLoadFlags(defaultLoadFlags));
    793 
    794  if (!StartupTimeline::HasRecord(StartupTimeline::FIRST_LOAD_URI) &&
    795      mItemType == typeContent && !NS_IsAboutBlank(aLoadState->URI())) {
    796    StartupTimeline::RecordOnce(StartupTimeline::FIRST_LOAD_URI);
    797  }
    798 
    799  // LoadType used to be set to a default value here, if no LoadInfo/LoadState
    800  // object was passed in. That functionality has been removed as of bug
    801  // 1492648. LoadType should now be set up by the caller at the time they
    802  // create their nsDocShellLoadState object to pass into LoadURI.
    803 
    804  MOZ_LOG(
    805      gDocShellLeakLog, LogLevel::Debug,
    806      ("nsDocShell[%p]: loading %s with flags 0x%08x", this,
    807       aLoadState->URI()->GetSpecOrDefault().get(), aLoadState->LoadFlags()));
    808 
    809  if ((!aLoadState->LoadIsFromSessionHistory() &&
    810       !LOAD_TYPE_HAS_FLAGS(aLoadState->LoadType(),
    811                            LOAD_FLAGS_REPLACE_HISTORY)) ||
    812      aContinueHandlingSubframeHistory) {
    813    // This is possibly a subframe, so handle it accordingly.
    814    //
    815    // If history exists, it will be loaded into the aLoadState object, and the
    816    // LoadType will be changed.
    817    if (MaybeHandleSubframeHistory(aLoadState,
    818                                   aContinueHandlingSubframeHistory)) {
    819      // MaybeHandleSubframeHistory returns true if we need to continue loading
    820      // asynchronously.
    821      return NS_OK;
    822    }
    823  }
    824 
    825  if (aLoadState->LoadIsFromSessionHistory()) {
    826    MOZ_LOG(gSHLog, LogLevel::Debug,
    827            ("nsDocShell[%p]: loading from session history", this));
    828 
    829    if (!mozilla::SessionHistoryInParent()) {
    830      nsCOMPtr<nsISHEntry> entry = aLoadState->SHEntry();
    831      return LoadHistoryEntry(entry, aLoadState->LoadType(),
    832                              aLoadState->HasValidUserGestureActivation());
    833    }
    834 
    835    // FIXME Null check aLoadState->GetLoadingSessionHistoryInfo()?
    836    return LoadHistoryEntry(*aLoadState->GetLoadingSessionHistoryInfo(),
    837                            aLoadState->LoadType(),
    838                            aLoadState->HasValidUserGestureActivation(),
    839                            aLoadState->NotifiedBeforeUnloadListeners());
    840  }
    841 
    842  // On history navigation via Back/Forward buttons, don't execute
    843  // automatic JavaScript redirection such as |location.href = ...| or
    844  // |window.open()|
    845  //
    846  // LOAD_NORMAL:        window.open(...) etc.
    847  // LOAD_STOP_CONTENT:  location.href = ..., location.assign(...)
    848  if ((aLoadState->LoadType() == LOAD_NORMAL ||
    849       aLoadState->LoadType() == LOAD_STOP_CONTENT) &&
    850      ShouldBlockLoadingForBackButton()) {
    851    return NS_OK;
    852  }
    853 
    854  BrowsingContext::Type bcType = mBrowsingContext->GetType();
    855 
    856  // Set up the inheriting principal in LoadState.
    857  nsresult rv = aLoadState->SetupInheritingPrincipal(
    858      bcType, mBrowsingContext->OriginAttributesRef());
    859  NS_ENSURE_SUCCESS(rv, rv);
    860 
    861  rv = aLoadState->SetupTriggeringPrincipal(
    862      mBrowsingContext->OriginAttributesRef());
    863  NS_ENSURE_SUCCESS(rv, rv);
    864 
    865  aLoadState->CalculateLoadURIFlags();
    866 
    867  MOZ_ASSERT(aLoadState->TypeHint().IsVoid(),
    868             "Typehint should be null when calling InternalLoad from LoadURI");
    869  MOZ_ASSERT(aLoadState->FileName().IsVoid(),
    870             "FileName should be null when calling InternalLoad from LoadURI");
    871  MOZ_ASSERT(!aLoadState->LoadIsFromSessionHistory(),
    872             "Shouldn't be loading from an entry when calling InternalLoad "
    873             "from LoadURI");
    874 
    875  // If we have a system triggering principal, we can assume that this load was
    876  // triggered by some UI in the browser chrome, such as the URL bar or
    877  // bookmark bar. This should count as a user interaction for the current sh
    878  // entry, so that the user may navigate back to the current entry, from the
    879  // entry that is going to be added as part of this load.
    880  nsCOMPtr<nsIPrincipal> triggeringPrincipal =
    881      aLoadState->TriggeringPrincipal();
    882  if (triggeringPrincipal && triggeringPrincipal->IsSystemPrincipal()) {
    883    if (mozilla::SessionHistoryInParent()) {
    884      WindowContext* topWc = mBrowsingContext->GetTopWindowContext();
    885      if (topWc && !topWc->IsDiscarded()) {
    886        MOZ_ALWAYS_SUCCEEDS(topWc->SetSHEntryHasUserInteraction(true));
    887      }
    888    } else {
    889      bool oshe = false;
    890      nsCOMPtr<nsISHEntry> currentSHEntry;
    891      GetCurrentSHEntry(getter_AddRefs(currentSHEntry), &oshe);
    892      if (currentSHEntry) {
    893        currentSHEntry->SetHasUserInteraction(true);
    894      }
    895    }
    896  }
    897 
    898  rv = InternalLoad(aLoadState);
    899  NS_ENSURE_SUCCESS(rv, rv);
    900 
    901  if (aLoadState->GetOriginalURIString().isSome()) {
    902    // Save URI string in case it's needed later when
    903    // sending to search engine service in EndPageLoad()
    904    mOriginalUriString = *aLoadState->GetOriginalURIString();
    905  }
    906 
    907  return NS_OK;
    908 }
    909 
    910 bool nsDocShell::IsLoadingFromSessionHistory() {
    911  return mActiveEntryIsLoadingFromSessionHistory;
    912 }
    913 
    914 // StopDetector is modeled similarly to OnloadBlocker; it is a rather
    915 // dummy nsIRequest implementation which can be added to an nsILoadGroup to
    916 // detect Cancel calls.
    917 class StopDetector final : public nsIRequest {
    918 public:
    919  StopDetector() = default;
    920 
    921  NS_DECL_ISUPPORTS
    922  NS_DECL_NSIREQUEST
    923 
    924  bool Canceled() { return mCanceled; }
    925 
    926 private:
    927  ~StopDetector() = default;
    928 
    929  bool mCanceled = false;
    930 };
    931 
    932 NS_IMPL_ISUPPORTS(StopDetector, nsIRequest)
    933 
    934 NS_IMETHODIMP
    935 StopDetector::GetName(nsACString& aResult) {
    936  aResult.AssignLiteral("about:stop-detector");
    937  return NS_OK;
    938 }
    939 
    940 NS_IMETHODIMP
    941 StopDetector::IsPending(bool* aRetVal) {
    942  *aRetVal = true;
    943  return NS_OK;
    944 }
    945 
    946 NS_IMETHODIMP
    947 StopDetector::GetStatus(nsresult* aStatus) {
    948  *aStatus = NS_OK;
    949  return NS_OK;
    950 }
    951 
    952 NS_IMETHODIMP StopDetector::SetCanceledReason(const nsACString& aReason) {
    953  return SetCanceledReasonImpl(aReason);
    954 }
    955 
    956 NS_IMETHODIMP StopDetector::GetCanceledReason(nsACString& aReason) {
    957  return GetCanceledReasonImpl(aReason);
    958 }
    959 
    960 NS_IMETHODIMP StopDetector::CancelWithReason(nsresult aStatus,
    961                                             const nsACString& aReason) {
    962  return CancelWithReasonImpl(aStatus, aReason);
    963 }
    964 
    965 NS_IMETHODIMP
    966 StopDetector::Cancel(nsresult aStatus) {
    967  mCanceled = true;
    968  return NS_OK;
    969 }
    970 
    971 NS_IMETHODIMP
    972 StopDetector::Suspend(void) { return NS_OK; }
    973 NS_IMETHODIMP
    974 StopDetector::Resume(void) { return NS_OK; }
    975 
    976 NS_IMETHODIMP
    977 StopDetector::GetLoadGroup(nsILoadGroup** aLoadGroup) {
    978  *aLoadGroup = nullptr;
    979  return NS_OK;
    980 }
    981 
    982 NS_IMETHODIMP
    983 StopDetector::SetLoadGroup(nsILoadGroup* aLoadGroup) { return NS_OK; }
    984 
    985 NS_IMETHODIMP
    986 StopDetector::GetLoadFlags(nsLoadFlags* aLoadFlags) {
    987  *aLoadFlags = nsIRequest::LOAD_NORMAL;
    988  return NS_OK;
    989 }
    990 
    991 NS_IMETHODIMP
    992 StopDetector::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
    993  return GetTRRModeImpl(aTRRMode);
    994 }
    995 
    996 NS_IMETHODIMP
    997 StopDetector::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
    998  return SetTRRModeImpl(aTRRMode);
    999 }
   1000 
   1001 NS_IMETHODIMP
   1002 StopDetector::SetLoadFlags(nsLoadFlags aLoadFlags) { return NS_OK; }
   1003 
   1004 bool nsDocShell::MaybeHandleSubframeHistory(
   1005    nsDocShellLoadState* aLoadState, bool aContinueHandlingSubframeHistory) {
   1006  // First, verify if this is a subframe.
   1007  // Note, it is ok to rely on docshell here and not browsing context since when
   1008  // an iframe is created, it has first in-process docshell.
   1009  nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
   1010  GetInProcessSameTypeParent(getter_AddRefs(parentAsItem));
   1011  nsCOMPtr<nsIDocShell> parentDS(do_QueryInterface(parentAsItem));
   1012 
   1013  if (!parentDS || parentDS == static_cast<nsIDocShell*>(this)) {
   1014    if (mBrowsingContext && mBrowsingContext->IsTop() &&
   1015        !aLoadState->HistoryBehavior()) {
   1016      // This is the root docshell. If we got here while
   1017      // executing an onLoad Handler,this load will not go
   1018      // into session history.
   1019      // XXX Why is this code in a method which deals with iframes!
   1020      if (aLoadState->IsFormSubmission()) {
   1021 #ifdef DEBUG
   1022        if (!mEODForCurrentDocument) {
   1023          const MaybeDiscarded<BrowsingContext>& targetBC =
   1024              aLoadState->TargetBrowsingContext();
   1025          MOZ_ASSERT_IF(GetBrowsingContext() == targetBC.get(),
   1026                        aLoadState->LoadType() == LOAD_NORMAL_REPLACE);
   1027        }
   1028 #endif
   1029      } else {
   1030        bool inOnLoadHandler = false;
   1031        GetIsExecutingOnLoadHandler(&inOnLoadHandler);
   1032        if (inOnLoadHandler) {
   1033          aLoadState->SetLoadType(LOAD_NORMAL_REPLACE);
   1034        }
   1035      }
   1036    }
   1037    return false;
   1038  }
   1039 
   1040  /* OK. It is a subframe. Checkout the parent's loadtype. If the parent was
   1041   * loaded through a history mechanism, then get the SH entry for the child
   1042   * from the parent. This is done to restore frameset navigation while going
   1043   * back/forward. If the parent was loaded through any other loadType, set the
   1044   * child's loadType too accordingly, so that session history does not get
   1045   * confused.
   1046   */
   1047 
   1048  // Get the parent's load type
   1049  uint32_t parentLoadType;
   1050  parentDS->GetLoadType(&parentLoadType);
   1051 
   1052  if (!aContinueHandlingSubframeHistory) {
   1053    if (mozilla::SessionHistoryInParent()) {
   1054      if (nsDocShell::Cast(parentDS.get())->IsLoadingFromSessionHistory() &&
   1055          !GetCreatedDynamically()) {
   1056        if (XRE_IsContentProcess()) {
   1057          dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
   1058          nsCOMPtr<nsILoadGroup> loadGroup;
   1059          GetLoadGroup(getter_AddRefs(loadGroup));
   1060          if (contentChild && loadGroup && !mCheckingSessionHistory) {
   1061            RefPtr<Document> parentDoc = parentDS->GetDocument();
   1062            parentDoc->BlockOnload();
   1063            RefPtr<BrowsingContext> browsingContext = mBrowsingContext;
   1064            Maybe<uint64_t> currentLoadIdentifier =
   1065                mBrowsingContext->GetCurrentLoadIdentifier();
   1066            RefPtr<nsDocShellLoadState> loadState = aLoadState;
   1067            bool isNavigating = mIsNavigating;
   1068            RefPtr<StopDetector> stopDetector = new StopDetector();
   1069            loadGroup->AddRequest(stopDetector, nullptr);
   1070            // Need to set mCheckingSessionHistory so that
   1071            // GetIsAttemptingToNavigate() returns true.
   1072            mCheckingSessionHistory = true;
   1073 
   1074            auto resolve =
   1075                [currentLoadIdentifier, browsingContext, parentDoc, loadState,
   1076                 isNavigating, loadGroup, stopDetector](
   1077                    mozilla::Maybe<LoadingSessionHistoryInfo>&& aResult) {
   1078                  RefPtr<nsDocShell> docShell =
   1079                      static_cast<nsDocShell*>(browsingContext->GetDocShell());
   1080                  auto unblockParent = MakeScopeExit(
   1081                      [loadGroup, stopDetector, parentDoc, docShell]() {
   1082                        if (docShell) {
   1083                          docShell->mCheckingSessionHistory = false;
   1084                        }
   1085                        loadGroup->RemoveRequest(stopDetector, nullptr, NS_OK);
   1086                        parentDoc->UnblockOnload(false);
   1087                      });
   1088 
   1089                  if (!docShell || !docShell->mCheckingSessionHistory) {
   1090                    return;
   1091                  }
   1092 
   1093                  if (stopDetector->Canceled()) {
   1094                    return;
   1095                  }
   1096                  if (currentLoadIdentifier ==
   1097                          browsingContext->GetCurrentLoadIdentifier() &&
   1098                      aResult.isSome()) {
   1099                    loadState->SetLoadingSessionHistoryInfo(aResult.value());
   1100                    // This is an initial subframe load from the session
   1101                    // history, index doesn't need to be updated.
   1102                    loadState->SetLoadIsFromSessionHistory(0, false);
   1103                  }
   1104 
   1105                  // We got the results back from the parent process, call
   1106                  // LoadURI again with the possibly updated data.
   1107                  docShell->LoadURI(loadState, isNavigating, true);
   1108                };
   1109            auto reject = [loadGroup, stopDetector, browsingContext,
   1110                           parentDoc](mozilla::ipc::ResponseRejectReason) {
   1111              RefPtr<nsDocShell> docShell =
   1112                  static_cast<nsDocShell*>(browsingContext->GetDocShell());
   1113              if (docShell) {
   1114                docShell->mCheckingSessionHistory = false;
   1115              }
   1116              // In practise reject shouldn't be called ever.
   1117              loadGroup->RemoveRequest(stopDetector, nullptr, NS_OK);
   1118              parentDoc->UnblockOnload(false);
   1119            };
   1120            contentChild->SendGetLoadingSessionHistoryInfoFromParent(
   1121                mBrowsingContext, std::move(resolve), std::move(reject));
   1122            return true;
   1123          }
   1124        } else {
   1125          Maybe<LoadingSessionHistoryInfo> info;
   1126          mBrowsingContext->Canonical()->GetLoadingSessionHistoryInfoFromParent(
   1127              info);
   1128          if (info.isSome()) {
   1129            aLoadState->SetLoadingSessionHistoryInfo(info.value());
   1130            // This is an initial subframe load from the session
   1131            // history, index doesn't need to be updated.
   1132            aLoadState->SetLoadIsFromSessionHistory(0, false);
   1133          }
   1134        }
   1135      }
   1136    } else {
   1137      // Get the ShEntry for the child from the parent
   1138      nsCOMPtr<nsISHEntry> currentSH;
   1139      bool oshe = false;
   1140      parentDS->GetCurrentSHEntry(getter_AddRefs(currentSH), &oshe);
   1141      bool dynamicallyAddedChild = GetCreatedDynamically();
   1142 
   1143      if (!dynamicallyAddedChild && !oshe && currentSH) {
   1144        // Only use the old SHEntry, if we're sure enough that
   1145        // it wasn't originally for some other frame.
   1146        nsCOMPtr<nsISHEntry> shEntry;
   1147        currentSH->GetChildSHEntryIfHasNoDynamicallyAddedChild(
   1148            mBrowsingContext->ChildOffset(), getter_AddRefs(shEntry));
   1149        if (shEntry) {
   1150          aLoadState->SetSHEntry(shEntry);
   1151        }
   1152      }
   1153    }
   1154  }
   1155 
   1156  // Make some decisions on the child frame's loadType based on the
   1157  // parent's loadType, if the subframe hasn't loaded anything into it.
   1158  //
   1159  // In some cases privileged scripts may try to get the DOMWindow
   1160  // reference of this docshell before the loading starts, causing the
   1161  // initial about:blank content viewer being created and mCurrentURI being
   1162  // set. To handle this case we check if mCurrentURI is about:blank and
   1163  // currentSHEntry is null.
   1164  bool oshe = false;
   1165  nsCOMPtr<nsISHEntry> currentChildEntry;
   1166  GetCurrentSHEntry(getter_AddRefs(currentChildEntry), &oshe);
   1167 
   1168  if (mCurrentURI &&
   1169      (!NS_IsAboutBlank(mCurrentURI) || currentChildEntry || mLoadingEntry ||
   1170       mActiveEntry) &&
   1171      !aLoadState->HistoryBehavior()) {
   1172    // This is a pre-existing subframe. If
   1173    // 1. The load of this frame was not originally initiated by session
   1174    //    history directly (i.e. (!shEntry) condition succeeded, but it can
   1175    //    still be a history load on parent which causes this frame being
   1176    //    loaded), which we checked with the above assert, and
   1177    // 2. mCurrentURI is not null, nor the initial about:blank,
   1178    // it is possible that a parent's onLoadHandler or even self's
   1179    // onLoadHandler is loading a new page in this child. Check parent's and
   1180    // self's busy flag and if it is set, we don't want this onLoadHandler
   1181    // load to get in to session history.
   1182    BusyFlags parentBusy = parentDS->GetBusyFlags();
   1183    BusyFlags selfBusy = GetBusyFlags();
   1184 
   1185    if (parentBusy & BUSY_FLAGS_BUSY || selfBusy & BUSY_FLAGS_BUSY) {
   1186      aLoadState->SetLoadType(LOAD_NORMAL_REPLACE);
   1187      aLoadState->ClearLoadIsFromSessionHistory();
   1188    }
   1189    return false;
   1190  }
   1191 
   1192  // This is a newly created frame. Check for exception cases first.
   1193  // By default the subframe will inherit the parent's loadType.
   1194  if (aLoadState->LoadIsFromSessionHistory() &&
   1195      (parentLoadType == LOAD_NORMAL || parentLoadType == LOAD_LINK)) {
   1196    // The parent was loaded normally. In this case, this *brand new*
   1197    // child really shouldn't have a SHEntry. If it does, it could be
   1198    // because the parent is replacing an existing frame with a new frame,
   1199    // in the onLoadHandler. We don't want this url to get into session
   1200    // history. Clear off shEntry, and set load type to
   1201    // LOAD_BYPASS_HISTORY.
   1202    bool inOnLoadHandler = false;
   1203    parentDS->GetIsExecutingOnLoadHandler(&inOnLoadHandler);
   1204    if (inOnLoadHandler) {
   1205      aLoadState->SetLoadType(LOAD_NORMAL_REPLACE);
   1206      aLoadState->ClearLoadIsFromSessionHistory();
   1207    }
   1208  } else if (parentLoadType == LOAD_REFRESH) {
   1209    // Clear shEntry. For refresh loads, we have to load
   1210    // what comes through the pipe, not what's in history.
   1211    aLoadState->ClearLoadIsFromSessionHistory();
   1212  } else if ((parentLoadType == LOAD_BYPASS_HISTORY) ||
   1213             (aLoadState->LoadIsFromSessionHistory() &&
   1214              ((parentLoadType & LOAD_CMD_HISTORY) ||
   1215               (parentLoadType == LOAD_RELOAD_NORMAL) ||
   1216               (parentLoadType == LOAD_RELOAD_CHARSET_CHANGE) ||
   1217               (parentLoadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE) ||
   1218               (parentLoadType ==
   1219                LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE)))) {
   1220    // If the parent url, bypassed history or was loaded from
   1221    // history, pass on the parent's loadType to the new child
   1222    // frame too, so that the child frame will also
   1223    // avoid getting into history.
   1224    aLoadState->SetLoadType(parentLoadType);
   1225  } else if (parentLoadType == LOAD_ERROR_PAGE) {
   1226    // If the parent document is an error page, we don't
   1227    // want to update global/session history. However,
   1228    // this child frame is not an error page.
   1229    aLoadState->SetLoadType(LOAD_BYPASS_HISTORY);
   1230  } else if ((parentLoadType == LOAD_RELOAD_BYPASS_CACHE) ||
   1231             (parentLoadType == LOAD_RELOAD_BYPASS_PROXY) ||
   1232             (parentLoadType == LOAD_RELOAD_BYPASS_PROXY_AND_CACHE)) {
   1233    // the new frame should inherit the parent's load type so that it also
   1234    // bypasses the cache and/or proxy
   1235    aLoadState->SetLoadType(parentLoadType);
   1236  }
   1237 
   1238  return false;
   1239 }
   1240 
   1241 /*
   1242 * Reset state to a new content model within the current document and the
   1243 * document viewer. Called by the document before initiating an out of band
   1244 * document.write().
   1245 */
   1246 NS_IMETHODIMP
   1247 nsDocShell::PrepareForNewContentModel() {
   1248  // Clear out our form control state, because the state of controls
   1249  // in the pre-open() document should not affect the state of
   1250  // controls that are now going to be written.
   1251  SetLayoutHistoryState(nullptr);
   1252  mEODForCurrentDocument = false;
   1253  return NS_OK;
   1254 }
   1255 
   1256 NS_IMETHODIMP
   1257 nsDocShell::FirePageHideNotification(bool aIsUnload) {
   1258  FirePageHideNotificationInternal(aIsUnload, false);
   1259  return NS_OK;
   1260 }
   1261 
   1262 void nsDocShell::FirePageHideNotificationInternal(
   1263    bool aIsUnload, bool aSkipCheckingDynEntries) {
   1264  if (mDocumentViewer && !mFiredUnloadEvent) {
   1265    // Keep an explicit reference since calling PageHide could release
   1266    // mDocumentViewer
   1267    nsCOMPtr<nsIDocumentViewer> viewer(mDocumentViewer);
   1268    mFiredUnloadEvent = true;
   1269 
   1270    if (mTiming) {
   1271      mTiming->NotifyUnloadEventStart();
   1272    }
   1273 
   1274    viewer->PageHide(aIsUnload);
   1275 
   1276    if (mTiming) {
   1277      mTiming->NotifyUnloadEventEnd();
   1278    }
   1279 
   1280    AutoTArray<nsCOMPtr<nsIDocShell>, 8> kids;
   1281    uint32_t n = mChildList.Length();
   1282    kids.SetCapacity(n);
   1283    for (uint32_t i = 0; i < n; i++) {
   1284      kids.AppendElement(do_QueryInterface(ChildAt(i)));
   1285    }
   1286 
   1287    n = kids.Length();
   1288    for (uint32_t i = 0; i < n; ++i) {
   1289      RefPtr<nsDocShell> child = static_cast<nsDocShell*>(kids[i].get());
   1290      if (child) {
   1291        // Skip checking dynamic subframe entries in our children.
   1292        child->FirePageHideNotificationInternal(aIsUnload, true);
   1293      }
   1294    }
   1295 
   1296    // If the document is unloading, remove all dynamic subframe entries.
   1297    if (aIsUnload && !aSkipCheckingDynEntries) {
   1298      RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
   1299      if (rootSH) {
   1300        MOZ_LOG(
   1301            gSHLog, LogLevel::Debug,
   1302            ("nsDocShell %p unloading, remove dynamic subframe entries", this));
   1303        if (mozilla::SessionHistoryInParent()) {
   1304          if (mActiveEntry) {
   1305            mBrowsingContext->RemoveDynEntriesFromActiveSessionHistoryEntry();
   1306          }
   1307          MOZ_LOG(gSHLog, LogLevel::Debug,
   1308                  ("nsDocShell %p unloading, no active entries", this));
   1309        } else if (mOSHE) {
   1310          int32_t index = rootSH->Index();
   1311          rootSH->LegacySHistory()->RemoveDynEntries(index, mOSHE);
   1312        }
   1313      }
   1314    }
   1315 
   1316    // Now make sure our editor, if any, is detached before we go
   1317    // any farther.
   1318    DetachEditorFromWindow();
   1319  }
   1320 }
   1321 
   1322 void nsDocShell::ThawFreezeNonRecursive(bool aThaw) {
   1323  MOZ_ASSERT(mozilla::BFCacheInParent());
   1324 
   1325  if (!mScriptGlobal) {
   1326    return;
   1327  }
   1328 
   1329  if (RefPtr<nsGlobalWindowInner> inner =
   1330          nsGlobalWindowInner::Cast(mScriptGlobal->GetCurrentInnerWindow())) {
   1331    if (aThaw) {
   1332      inner->Thaw(false);
   1333    } else {
   1334      inner->Freeze(false);
   1335    }
   1336  }
   1337 }
   1338 
   1339 void nsDocShell::FirePageHideShowNonRecursive(bool aShow) {
   1340  MOZ_ASSERT(mozilla::BFCacheInParent());
   1341 
   1342  if (!mDocumentViewer) {
   1343    return;
   1344  }
   1345 
   1346  // Emulate what non-SHIP BFCache does too. In pageshow case
   1347  // add and remove a request and before that call SetCurrentURI to get
   1348  // the location change notification.
   1349  // For pagehide, set mFiredUnloadEvent to true, so that unload doesn't fire.
   1350  nsCOMPtr<nsIDocumentViewer> viewer(mDocumentViewer);
   1351  if (aShow) {
   1352    viewer->SetIsHidden(false);
   1353    mRefreshURIList = std::move(mBFCachedRefreshURIList);
   1354    RefreshURIFromQueue();
   1355    mFiredUnloadEvent = false;
   1356    RefPtr<Document> doc = viewer->GetDocument();
   1357    if (doc) {
   1358      doc->NotifyActivityChanged();
   1359      nsCOMPtr<nsPIDOMWindowInner> inner =
   1360          mScriptGlobal ? mScriptGlobal->GetCurrentInnerWindow() : nullptr;
   1361      if (mBrowsingContext->IsTop()) {
   1362        doc->NotifyPossibleTitleChange(false);
   1363        doc->SetLoadingOrRestoredFromBFCacheTimeStampToNow();
   1364        if (inner) {
   1365          // Now that we have found the inner window of the page restored
   1366          // from the history, we have to make sure that
   1367          // performance.navigation.type is 2.
   1368          // Traditionally this type change has been done to the top level page
   1369          // only.
   1370          Performance* performance = inner->GetPerformance();
   1371          if (performance) {
   1372            performance->GetDOMTiming()->NotifyRestoreStart();
   1373          }
   1374        }
   1375      }
   1376 
   1377      nsCOMPtr<nsIChannel> channel = doc->GetChannel();
   1378      if (channel) {
   1379        SetLoadType(LOAD_HISTORY);
   1380        mEODForCurrentDocument = false;
   1381        mIsRestoringDocument = true;
   1382        mLoadGroup->AddRequest(channel, nullptr);
   1383        nsCOMPtr<nsIURI> uri;
   1384        if (doc->FragmentDirective()) {
   1385          // If we have fragment directives, then we've mutated the document
   1386          // uri. Set the current URI from session history instead.
   1387          if (mozilla::SessionHistoryInParent()) {
   1388            uri = mActiveEntry ? mActiveEntry->GetURI() : nullptr;
   1389          } else if (mOSHE) {
   1390            uri = mOSHE->GetURI();
   1391          }
   1392        }
   1393        if (!uri) {
   1394          uri = doc->GetDocumentURI();
   1395        }
   1396        SetCurrentURI(uri, channel,
   1397                      /* aFireOnLocationChange */ true,
   1398                      /* aIsInitialAboutBlank */ false,
   1399                      /* aLocationFlags */ 0);
   1400        mLoadGroup->RemoveRequest(channel, nullptr, NS_OK);
   1401        mIsRestoringDocument = false;
   1402      }
   1403      RefPtr<PresShell> presShell = GetPresShell();
   1404      if (presShell) {
   1405        presShell->Thaw(false);
   1406      }
   1407 
   1408      if (inner) {
   1409        inner->FireDelayedDOMEvents(false);
   1410      }
   1411    }
   1412  } else if (!mFiredUnloadEvent) {
   1413    // XXXBFCache check again that the page can enter bfcache.
   1414    // XXXBFCache should mTiming->NotifyUnloadEventStart()/End() be called here?
   1415 
   1416    if (mRefreshURIList) {
   1417      RefreshURIToQueue();
   1418      mBFCachedRefreshURIList = std::move(mRefreshURIList);
   1419    } else {
   1420      // If Stop was called, the list was moved to mSavedRefreshURIList after
   1421      // calling SuspendRefreshURIs, which calls RefreshURIToQueue.
   1422      mBFCachedRefreshURIList = std::move(mSavedRefreshURIList);
   1423    }
   1424 
   1425    mFiredUnloadEvent = true;
   1426    viewer->PageHide(false);
   1427 
   1428    RefPtr<PresShell> presShell = GetPresShell();
   1429    if (presShell) {
   1430      presShell->Freeze(false);
   1431    }
   1432  }
   1433 }
   1434 
   1435 nsresult nsDocShell::Dispatch(already_AddRefed<nsIRunnable>&& aRunnable) {
   1436  nsCOMPtr<nsIRunnable> runnable(aRunnable);
   1437  if (NS_WARN_IF(!GetWindow())) {
   1438    // Window should only be unavailable after destroyed.
   1439    MOZ_ASSERT(mIsBeingDestroyed);
   1440    return NS_ERROR_FAILURE;
   1441  }
   1442  return SchedulerGroup::Dispatch(runnable.forget());
   1443 }
   1444 
   1445 NS_IMETHODIMP
   1446 nsDocShell::DispatchLocationChangeEvent() {
   1447  return Dispatch(NewRunnableMethod("nsDocShell::FireDummyOnLocationChange",
   1448                                    this,
   1449                                    &nsDocShell::FireDummyOnLocationChange));
   1450 }
   1451 
   1452 NS_IMETHODIMP
   1453 nsDocShell::StartDelayedAutoplayMediaComponents() {
   1454  RefPtr<nsPIDOMWindowOuter> outerWindow = GetWindow();
   1455  if (outerWindow) {
   1456    outerWindow->ActivateMediaComponents();
   1457  }
   1458  return NS_OK;
   1459 }
   1460 
   1461 bool nsDocShell::MaybeInitTiming() {
   1462  if (mTiming && !mBlankTiming) {
   1463    return false;
   1464  }
   1465 
   1466  bool canBeReset = false;
   1467 
   1468  if (mScriptGlobal && mBlankTiming) {
   1469    nsPIDOMWindowInner* innerWin = mScriptGlobal->GetCurrentInnerWindow();
   1470    if (innerWin && innerWin->GetPerformance()) {
   1471      mTiming = innerWin->GetPerformance()->GetDOMTiming();
   1472      mBlankTiming = false;
   1473    }
   1474  }
   1475 
   1476  if (!mTiming) {
   1477    mTiming = new nsDOMNavigationTiming(this);
   1478    canBeReset = true;
   1479  }
   1480 
   1481  mTiming->NotifyNavigationStart(
   1482      mBrowsingContext->IsActive()
   1483          ? nsDOMNavigationTiming::DocShellState::eActive
   1484          : nsDOMNavigationTiming::DocShellState::eInactive);
   1485 
   1486  return canBeReset;
   1487 }
   1488 
   1489 void nsDocShell::MaybeResetInitTiming(bool aReset) {
   1490  if (aReset) {
   1491    mTiming = nullptr;
   1492  }
   1493 }
   1494 
   1495 nsDOMNavigationTiming* nsDocShell::GetNavigationTiming() const {
   1496  return mTiming;
   1497 }
   1498 
   1499 nsPresContext* nsDocShell::GetEldestPresContext() {
   1500  nsIDocumentViewer* viewer = mDocumentViewer;
   1501  while (viewer) {
   1502    nsIDocumentViewer* prevViewer = viewer->GetPreviousViewer();
   1503    if (!prevViewer) {
   1504      return viewer->GetPresContext();
   1505    }
   1506    viewer = prevViewer;
   1507  }
   1508 
   1509  return nullptr;
   1510 }
   1511 
   1512 nsPresContext* nsDocShell::GetPresContext() {
   1513  if (!mDocumentViewer) {
   1514    return nullptr;
   1515  }
   1516 
   1517  return mDocumentViewer->GetPresContext();
   1518 }
   1519 
   1520 PresShell* nsDocShell::GetPresShell() {
   1521  nsPresContext* presContext = GetPresContext();
   1522  return presContext ? presContext->GetPresShell() : nullptr;
   1523 }
   1524 
   1525 PresShell* nsDocShell::GetEldestPresShell() {
   1526  nsPresContext* presContext = GetEldestPresContext();
   1527 
   1528  if (presContext) {
   1529    return presContext->GetPresShell();
   1530  }
   1531 
   1532  return nullptr;
   1533 }
   1534 
   1535 NS_IMETHODIMP
   1536 nsDocShell::GetDocViewer(nsIDocumentViewer** aDocumentViewer) {
   1537  NS_ENSURE_ARG_POINTER(aDocumentViewer);
   1538 
   1539  *aDocumentViewer = mDocumentViewer;
   1540  NS_IF_ADDREF(*aDocumentViewer);
   1541  return NS_OK;
   1542 }
   1543 
   1544 NS_IMETHODIMP
   1545 nsDocShell::GetOuterWindowID(uint64_t* aWindowID) {
   1546  *aWindowID = mContentWindowID;
   1547  return NS_OK;
   1548 }
   1549 
   1550 NS_IMETHODIMP
   1551 nsDocShell::SetChromeEventHandler(EventTarget* aChromeEventHandler) {
   1552  mChromeEventHandler = aChromeEventHandler;
   1553 
   1554  if (mScriptGlobal) {
   1555    mScriptGlobal->SetChromeEventHandler(mChromeEventHandler);
   1556  }
   1557 
   1558  return NS_OK;
   1559 }
   1560 
   1561 NS_IMETHODIMP
   1562 nsDocShell::GetChromeEventHandler(EventTarget** aChromeEventHandler) {
   1563  NS_ENSURE_ARG_POINTER(aChromeEventHandler);
   1564  RefPtr<EventTarget> handler = mChromeEventHandler;
   1565  handler.forget(aChromeEventHandler);
   1566  return NS_OK;
   1567 }
   1568 
   1569 NS_IMETHODIMP
   1570 nsDocShell::SetCurrentURIForSessionStore(nsIURI* aURI) {
   1571  // Note that securityUI will set STATE_IS_INSECURE, even if
   1572  // the scheme of |aURI| is "https".
   1573  SetCurrentURI(aURI, nullptr,
   1574                /* aFireOnLocationChange */
   1575                true,
   1576                /* aIsInitialAboutBlank */
   1577                false,
   1578                /* aLocationFlags */
   1579                nsIWebProgressListener::LOCATION_CHANGE_SESSION_STORE);
   1580  return NS_OK;
   1581 }
   1582 
   1583 bool nsDocShell::SetCurrentURI(nsIURI* aURI, nsIRequest* aRequest,
   1584                               bool aFireOnLocationChange,
   1585                               bool aIsInitialAboutBlank,
   1586                               uint32_t aLocationFlags) {
   1587  MOZ_ASSERT(!mIsBeingDestroyed);
   1588 
   1589  MOZ_LOG(gDocShellLeakLog, LogLevel::Debug,
   1590          ("DOCSHELL %p SetCurrentURI %s\n", this,
   1591           aURI ? aURI->GetSpecOrDefault().get() : ""));
   1592 
   1593  // We don't want to send a location change when we're displaying an error
   1594  // page, and we don't want to change our idea of "current URI" either
   1595  if (mLoadType == LOAD_ERROR_PAGE) {
   1596    return false;
   1597  }
   1598 
   1599  bool uriIsEqual = false;
   1600  if (!mCurrentURI || !aURI ||
   1601      NS_FAILED(mCurrentURI->Equals(aURI, &uriIsEqual)) || !uriIsEqual) {
   1602    mTitleValidForCurrentURI = false;
   1603  }
   1604 
   1605  SetCurrentURIInternal(aURI);
   1606 
   1607 #ifdef DEBUG
   1608  mLastOpenedURI = aURI;
   1609 #endif
   1610 
   1611  if (!NS_IsAboutBlankAllowQueryAndFragment(mCurrentURI)) {
   1612    mHasLoadedNonBlankURI = true;
   1613  }
   1614 
   1615  // Don't fire onLocationChange when creating a the initial about:blank
   1616  // document, as this can happen when it's not safe for us to run script.
   1617  // Note that if this initial about:blank isn't immediately navigated
   1618  // away from, the onLocationChange will be fired as part of committing
   1619  // to keeping the initial about:blank as the actual first initial
   1620  // navigation destination.
   1621  if (aIsInitialAboutBlank) {
   1622    MOZ_ASSERT(!mHasLoadedNonBlankURI && !aRequest && aLocationFlags == 0);
   1623    return false;
   1624  }
   1625 
   1626  MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
   1627 
   1628  if (aFireOnLocationChange) {
   1629    FireOnLocationChange(this, aRequest, aURI, aLocationFlags);
   1630  }
   1631  return !aFireOnLocationChange;
   1632 }
   1633 
   1634 void nsDocShell::SetCurrentURIInternal(nsIURI* aURI) {
   1635  mCurrentURI = aURI;
   1636  if (mBrowsingContext) {
   1637    mBrowsingContext->ClearCachedValuesOfLocations();
   1638  }
   1639 }
   1640 
   1641 NS_IMETHODIMP
   1642 nsDocShell::GetCharset(nsACString& aCharset) {
   1643  aCharset.Truncate();
   1644 
   1645  PresShell* presShell = GetPresShell();
   1646  NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
   1647  Document* doc = presShell->GetDocument();
   1648  NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
   1649  doc->GetDocumentCharacterSet()->Name(aCharset);
   1650  return NS_OK;
   1651 }
   1652 
   1653 NS_IMETHODIMP
   1654 nsDocShell::ForceEncodingDetection() {
   1655  nsCOMPtr<nsIDocumentViewer> viewer;
   1656  GetDocViewer(getter_AddRefs(viewer));
   1657  if (!viewer) {
   1658    return NS_OK;
   1659  }
   1660 
   1661  Document* doc = viewer->GetDocument();
   1662  if (!doc || doc->WillIgnoreCharsetOverride()) {
   1663    return NS_OK;
   1664  }
   1665 
   1666  mForcedAutodetection = true;
   1667 
   1668  nsIURI* uri = doc->GetOriginalURI();
   1669  bool isFileURL = uri && uri->SchemeIs("file");
   1670 
   1671  int32_t charsetSource = doc->GetDocumentCharacterSetSource();
   1672  auto encoding = doc->GetDocumentCharacterSet();
   1673  // AsHTMLDocument is valid, because we called
   1674  // WillIgnoreCharsetOverride() above.
   1675  if (doc->AsHTMLDocument()->IsPlainText()) {
   1676    switch (charsetSource) {
   1677      case kCharsetFromInitialAutoDetectionASCII:
   1678        // Deliberately no final version
   1679        LOGCHARSETMENU(("TEXT:UnlabeledAscii"));
   1680        break;
   1681      case kCharsetFromInitialAutoDetectionWouldNotHaveBeenUTF8Generic:
   1682      case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8Generic:
   1683      case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8GenericInitialWasASCII:
   1684      case kCharsetFromInitialAutoDetectionWouldNotHaveBeenUTF8Content:
   1685      case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8Content:
   1686      case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8ContentInitialWasASCII:
   1687        LOGCHARSETMENU(("TEXT:UnlabeledNonUtf8"));
   1688        break;
   1689      case kCharsetFromInitialAutoDetectionWouldNotHaveBeenUTF8DependedOnTLD:
   1690      case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8DependedOnTLD:
   1691      case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8DependedOnTLDInitialWasASCII:
   1692        LOGCHARSETMENU(("TEXT:UnlabeledNonUtf8TLD"));
   1693        break;
   1694      case kCharsetFromInitialAutoDetectionWouldHaveBeenUTF8:
   1695      case kCharsetFromFinalAutoDetectionWouldHaveBeenUTF8InitialWasASCII:
   1696        LOGCHARSETMENU(("TEXT:UnlabeledUtf8"));
   1697        break;
   1698      case kCharsetFromChannel:
   1699        if (encoding == UTF_8_ENCODING) {
   1700          LOGCHARSETMENU(("TEXT:ChannelUtf8"));
   1701        } else {
   1702          LOGCHARSETMENU(("TEXT:ChannelNonUtf8"));
   1703        }
   1704        break;
   1705      default:
   1706        LOGCHARSETMENU(("TEXT:Bug"));
   1707        break;
   1708    }
   1709  } else {
   1710    switch (charsetSource) {
   1711      case kCharsetFromInitialAutoDetectionASCII:
   1712        // Deliberately no final version
   1713        LOGCHARSETMENU(("HTML:UnlabeledAscii"));
   1714        break;
   1715      case kCharsetFromInitialAutoDetectionWouldNotHaveBeenUTF8Generic:
   1716      case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8Generic:
   1717      case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8GenericInitialWasASCII:
   1718      case kCharsetFromInitialAutoDetectionWouldNotHaveBeenUTF8Content:
   1719      case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8Content:
   1720      case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8ContentInitialWasASCII:
   1721        LOGCHARSETMENU(("HTML:UnlabeledNonUtf8"));
   1722        break;
   1723      case kCharsetFromInitialAutoDetectionWouldNotHaveBeenUTF8DependedOnTLD:
   1724      case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8DependedOnTLD:
   1725      case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8DependedOnTLDInitialWasASCII:
   1726        LOGCHARSETMENU(("HTML:UnlabeledNonUtf8TLD"));
   1727        break;
   1728      case kCharsetFromInitialAutoDetectionWouldHaveBeenUTF8:
   1729      case kCharsetFromFinalAutoDetectionWouldHaveBeenUTF8InitialWasASCII:
   1730        LOGCHARSETMENU(("HTML:UnlabeledUtf8"));
   1731        break;
   1732      case kCharsetFromChannel:
   1733        if (encoding == UTF_8_ENCODING) {
   1734          LOGCHARSETMENU(("HTML:ChannelUtf8"));
   1735        } else {
   1736          LOGCHARSETMENU(("HTML:ChannelNonUtf8"));
   1737        }
   1738        break;
   1739      case kCharsetFromXmlDeclaration:
   1740      case kCharsetFromMetaTag:
   1741        if (isFileURL) {
   1742          LOGCHARSETMENU(("HTML:LocalLabeled"));
   1743        } else if (encoding == UTF_8_ENCODING) {
   1744          LOGCHARSETMENU(("HTML:MetaUtf8"));
   1745        } else {
   1746          LOGCHARSETMENU(("HTML:MetaNonUtf8"));
   1747        }
   1748        break;
   1749      default:
   1750        LOGCHARSETMENU(("HTML:Bug"));
   1751        break;
   1752    }
   1753  }
   1754  return NS_OK;
   1755 }
   1756 
   1757 void nsDocShell::SetParentCharset(const Encoding*& aCharset,
   1758                                  int32_t aCharsetSource,
   1759                                  nsIPrincipal* aPrincipal) {
   1760  mParentCharset = aCharset;
   1761  mParentCharsetSource = aCharsetSource;
   1762  mParentCharsetPrincipal = aPrincipal;
   1763 }
   1764 
   1765 void nsDocShell::GetParentCharset(const Encoding*& aCharset,
   1766                                  int32_t* aCharsetSource,
   1767                                  nsIPrincipal** aPrincipal) {
   1768  aCharset = mParentCharset;
   1769  *aCharsetSource = mParentCharsetSource;
   1770  NS_IF_ADDREF(*aPrincipal = mParentCharsetPrincipal);
   1771 }
   1772 
   1773 NS_IMETHODIMP
   1774 nsDocShell::GetHasTrackingContentBlocked(Promise** aPromise) {
   1775  MOZ_ASSERT(aPromise);
   1776 
   1777  ErrorResult rv;
   1778  RefPtr<Document> doc(GetDocument());
   1779  RefPtr<Promise> retPromise = Promise::Create(doc->GetOwnerGlobal(), rv);
   1780  if (NS_WARN_IF(rv.Failed())) {
   1781    return rv.StealNSResult();
   1782  }
   1783 
   1784  // Retrieve the document's content blocking events from the parent process.
   1785  RefPtr<Document::GetContentBlockingEventsPromise> promise =
   1786      doc->GetContentBlockingEvents();
   1787  if (promise) {
   1788    promise->Then(
   1789        GetCurrentSerialEventTarget(), __func__,
   1790        [retPromise](const Document::GetContentBlockingEventsPromise::
   1791                         ResolveOrRejectValue& aValue) {
   1792          if (aValue.IsResolve()) {
   1793            bool has = aValue.ResolveValue() &
   1794                       nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT;
   1795            retPromise->MaybeResolve(has);
   1796          } else {
   1797            retPromise->MaybeResolve(false);
   1798          }
   1799        });
   1800  } else {
   1801    retPromise->MaybeResolve(false);
   1802  }
   1803 
   1804  retPromise.forget(aPromise);
   1805  return NS_OK;
   1806 }
   1807 
   1808 NS_IMETHODIMP
   1809 nsDocShell::GetCssErrorReportingEnabled(bool* aEnabled) {
   1810  MOZ_ASSERT(aEnabled);
   1811  *aEnabled = mCSSErrorReportingEnabled;
   1812  return NS_OK;
   1813 }
   1814 
   1815 NS_IMETHODIMP
   1816 nsDocShell::SetCssErrorReportingEnabled(bool aEnabled) {
   1817  mCSSErrorReportingEnabled = aEnabled;
   1818  return NS_OK;
   1819 }
   1820 
   1821 NS_IMETHODIMP
   1822 nsDocShell::GetUsePrivateBrowsing(bool* aUsePrivateBrowsing) {
   1823  NS_ENSURE_ARG_POINTER(aUsePrivateBrowsing);
   1824  return mBrowsingContext->GetUsePrivateBrowsing(aUsePrivateBrowsing);
   1825 }
   1826 
   1827 void nsDocShell::NotifyPrivateBrowsingChanged() {
   1828  MOZ_ASSERT(!mIsBeingDestroyed);
   1829 
   1830  nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mPrivacyObservers);
   1831  while (iter.HasMore()) {
   1832    nsWeakPtr ref = iter.GetNext();
   1833    nsCOMPtr<nsIPrivacyTransitionObserver> obs = do_QueryReferent(ref);
   1834    if (!obs) {
   1835      iter.Remove();
   1836    } else {
   1837      obs->PrivateModeChanged(UsePrivateBrowsing());
   1838    }
   1839  }
   1840 }
   1841 
   1842 NS_IMETHODIMP
   1843 nsDocShell::SetUsePrivateBrowsing(bool aUsePrivateBrowsing) {
   1844  return mBrowsingContext->SetUsePrivateBrowsing(aUsePrivateBrowsing);
   1845 }
   1846 
   1847 NS_IMETHODIMP
   1848 nsDocShell::SetPrivateBrowsing(bool aUsePrivateBrowsing) {
   1849  return mBrowsingContext->SetPrivateBrowsing(aUsePrivateBrowsing);
   1850 }
   1851 
   1852 NS_IMETHODIMP
   1853 nsDocShell::GetHasLoadedNonBlankURI(bool* aResult) {
   1854  NS_ENSURE_ARG_POINTER(aResult);
   1855 
   1856  *aResult = mHasLoadedNonBlankURI;
   1857  return NS_OK;
   1858 }
   1859 
   1860 bool nsDocShell::HasStartedLoadingOtherThanInitialBlankURI() {
   1861  return mHasStartedLoadingOtherThanInitialBlankURI;
   1862 }
   1863 
   1864 NS_IMETHODIMP
   1865 nsDocShell::GetUseRemoteTabs(bool* aUseRemoteTabs) {
   1866  NS_ENSURE_ARG_POINTER(aUseRemoteTabs);
   1867  return mBrowsingContext->GetUseRemoteTabs(aUseRemoteTabs);
   1868 }
   1869 
   1870 NS_IMETHODIMP
   1871 nsDocShell::SetRemoteTabs(bool aUseRemoteTabs) {
   1872  return mBrowsingContext->SetRemoteTabs(aUseRemoteTabs);
   1873 }
   1874 
   1875 NS_IMETHODIMP
   1876 nsDocShell::GetUseRemoteSubframes(bool* aUseRemoteSubframes) {
   1877  NS_ENSURE_ARG_POINTER(aUseRemoteSubframes);
   1878  return mBrowsingContext->GetUseRemoteSubframes(aUseRemoteSubframes);
   1879 }
   1880 
   1881 NS_IMETHODIMP
   1882 nsDocShell::SetRemoteSubframes(bool aUseRemoteSubframes) {
   1883  return mBrowsingContext->SetRemoteSubframes(aUseRemoteSubframes);
   1884 }
   1885 
   1886 NS_IMETHODIMP
   1887 nsDocShell::AddWeakPrivacyTransitionObserver(
   1888    nsIPrivacyTransitionObserver* aObserver) {
   1889  nsWeakPtr weakObs = do_GetWeakReference(aObserver);
   1890  if (!weakObs) {
   1891    return NS_ERROR_NOT_AVAILABLE;
   1892  }
   1893  mPrivacyObservers.AppendElement(weakObs);
   1894  return NS_OK;
   1895 }
   1896 
   1897 NS_IMETHODIMP
   1898 nsDocShell::AddWeakReflowObserver(nsIReflowObserver* aObserver) {
   1899  nsWeakPtr weakObs = do_GetWeakReference(aObserver);
   1900  if (!weakObs) {
   1901    return NS_ERROR_FAILURE;
   1902  }
   1903  mReflowObservers.AppendElement(weakObs);
   1904  return NS_OK;
   1905 }
   1906 
   1907 NS_IMETHODIMP
   1908 nsDocShell::RemoveWeakReflowObserver(nsIReflowObserver* aObserver) {
   1909  nsWeakPtr obs = do_GetWeakReference(aObserver);
   1910  return mReflowObservers.RemoveElement(obs) ? NS_OK : NS_ERROR_FAILURE;
   1911 }
   1912 
   1913 NS_IMETHODIMP
   1914 nsDocShell::NotifyReflowObservers(bool aInterruptible,
   1915                                  DOMHighResTimeStamp aStart,
   1916                                  DOMHighResTimeStamp aEnd) {
   1917  nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mReflowObservers);
   1918  while (iter.HasMore()) {
   1919    nsWeakPtr ref = iter.GetNext();
   1920    nsCOMPtr<nsIReflowObserver> obs = do_QueryReferent(ref);
   1921    if (!obs) {
   1922      iter.Remove();
   1923    } else if (aInterruptible) {
   1924      obs->ReflowInterruptible(aStart, aEnd);
   1925    } else {
   1926      obs->Reflow(aStart, aEnd);
   1927    }
   1928  }
   1929  return NS_OK;
   1930 }
   1931 
   1932 NS_IMETHODIMP
   1933 nsDocShell::GetAllowMetaRedirects(bool* aReturn) {
   1934  NS_ENSURE_ARG_POINTER(aReturn);
   1935 
   1936  *aReturn = mAllowMetaRedirects;
   1937  return NS_OK;
   1938 }
   1939 
   1940 NS_IMETHODIMP
   1941 nsDocShell::SetAllowMetaRedirects(bool aValue) {
   1942  mAllowMetaRedirects = aValue;
   1943  return NS_OK;
   1944 }
   1945 
   1946 NS_IMETHODIMP
   1947 nsDocShell::GetAllowSubframes(bool* aAllowSubframes) {
   1948  NS_ENSURE_ARG_POINTER(aAllowSubframes);
   1949 
   1950  *aAllowSubframes = mAllowSubframes;
   1951  return NS_OK;
   1952 }
   1953 
   1954 NS_IMETHODIMP
   1955 nsDocShell::SetAllowSubframes(bool aAllowSubframes) {
   1956  mAllowSubframes = aAllowSubframes;
   1957  return NS_OK;
   1958 }
   1959 
   1960 NS_IMETHODIMP
   1961 nsDocShell::GetAllowImages(bool* aAllowImages) {
   1962  NS_ENSURE_ARG_POINTER(aAllowImages);
   1963 
   1964  *aAllowImages = mAllowImages;
   1965  return NS_OK;
   1966 }
   1967 
   1968 NS_IMETHODIMP
   1969 nsDocShell::SetAllowImages(bool aAllowImages) {
   1970  mAllowImages = aAllowImages;
   1971  return NS_OK;
   1972 }
   1973 
   1974 NS_IMETHODIMP
   1975 nsDocShell::GetAllowMedia(bool* aAllowMedia) {
   1976  *aAllowMedia = mAllowMedia;
   1977  return NS_OK;
   1978 }
   1979 
   1980 NS_IMETHODIMP
   1981 nsDocShell::SetAllowMedia(bool aAllowMedia) {
   1982  mAllowMedia = aAllowMedia;
   1983 
   1984  // Mute or unmute audio contexts attached to the inner window.
   1985  if (mScriptGlobal) {
   1986    if (nsPIDOMWindowInner* innerWin = mScriptGlobal->GetCurrentInnerWindow()) {
   1987      if (aAllowMedia) {
   1988        innerWin->UnmuteAudioContexts();
   1989      } else {
   1990        innerWin->MuteAudioContexts();
   1991      }
   1992    }
   1993  }
   1994 
   1995  return NS_OK;
   1996 }
   1997 
   1998 NS_IMETHODIMP
   1999 nsDocShell::GetAllowDNSPrefetch(bool* aAllowDNSPrefetch) {
   2000  *aAllowDNSPrefetch = mAllowDNSPrefetch;
   2001  return NS_OK;
   2002 }
   2003 
   2004 NS_IMETHODIMP
   2005 nsDocShell::SetAllowDNSPrefetch(bool aAllowDNSPrefetch) {
   2006  mAllowDNSPrefetch = aAllowDNSPrefetch;
   2007  return NS_OK;
   2008 }
   2009 
   2010 NS_IMETHODIMP
   2011 nsDocShell::GetAllowWindowControl(bool* aAllowWindowControl) {
   2012  *aAllowWindowControl = mAllowWindowControl;
   2013  return NS_OK;
   2014 }
   2015 
   2016 NS_IMETHODIMP
   2017 nsDocShell::SetAllowWindowControl(bool aAllowWindowControl) {
   2018  mAllowWindowControl = aAllowWindowControl;
   2019  return NS_OK;
   2020 }
   2021 
   2022 NS_IMETHODIMP
   2023 nsDocShell::GetAllowContentRetargeting(bool* aAllowContentRetargeting) {
   2024  *aAllowContentRetargeting = mBrowsingContext->GetAllowContentRetargeting();
   2025  return NS_OK;
   2026 }
   2027 
   2028 NS_IMETHODIMP
   2029 nsDocShell::SetAllowContentRetargeting(bool aAllowContentRetargeting) {
   2030  BrowsingContext::Transaction txn;
   2031  txn.SetAllowContentRetargeting(aAllowContentRetargeting);
   2032  txn.SetAllowContentRetargetingOnChildren(aAllowContentRetargeting);
   2033  return txn.Commit(mBrowsingContext);
   2034 }
   2035 
   2036 NS_IMETHODIMP
   2037 nsDocShell::GetAllowContentRetargetingOnChildren(
   2038    bool* aAllowContentRetargetingOnChildren) {
   2039  *aAllowContentRetargetingOnChildren =
   2040      mBrowsingContext->GetAllowContentRetargetingOnChildren();
   2041  return NS_OK;
   2042 }
   2043 
   2044 NS_IMETHODIMP
   2045 nsDocShell::SetAllowContentRetargetingOnChildren(
   2046    bool aAllowContentRetargetingOnChildren) {
   2047  return mBrowsingContext->SetAllowContentRetargetingOnChildren(
   2048      aAllowContentRetargetingOnChildren);
   2049 }
   2050 
   2051 NS_IMETHODIMP
   2052 nsDocShell::GetMayEnableCharacterEncodingMenu(
   2053    bool* aMayEnableCharacterEncodingMenu) {
   2054  *aMayEnableCharacterEncodingMenu = false;
   2055  if (!mDocumentViewer) {
   2056    return NS_OK;
   2057  }
   2058  Document* doc = mDocumentViewer->GetDocument();
   2059  if (!doc) {
   2060    return NS_OK;
   2061  }
   2062  if (doc->WillIgnoreCharsetOverride()) {
   2063    return NS_OK;
   2064  }
   2065 
   2066  *aMayEnableCharacterEncodingMenu = true;
   2067  return NS_OK;
   2068 }
   2069 
   2070 NS_IMETHODIMP
   2071 nsDocShell::GetAllDocShellsInSubtree(int32_t aItemType,
   2072                                     DocShellEnumeratorDirection aDirection,
   2073                                     nsTArray<RefPtr<nsIDocShell>>& aResult) {
   2074  aResult.Clear();
   2075 
   2076  nsDocShellEnumerator docShellEnum(
   2077      (aDirection == ENUMERATE_FORWARDS)
   2078          ? nsDocShellEnumerator::EnumerationDirection::Forwards
   2079          : nsDocShellEnumerator::EnumerationDirection::Backwards,
   2080      aItemType, *this);
   2081 
   2082  nsresult rv = docShellEnum.BuildDocShellArray(aResult);
   2083  if (NS_FAILED(rv)) {
   2084    return rv;
   2085  }
   2086 
   2087  return NS_OK;
   2088 }
   2089 
   2090 NS_IMETHODIMP
   2091 nsDocShell::GetAppType(AppType* aAppType) {
   2092  *aAppType = mAppType;
   2093  return NS_OK;
   2094 }
   2095 
   2096 NS_IMETHODIMP
   2097 nsDocShell::SetAppType(AppType aAppType) {
   2098  mAppType = aAppType;
   2099  return NS_OK;
   2100 }
   2101 
   2102 NS_IMETHODIMP
   2103 nsDocShell::GetAllowAuth(bool* aAllowAuth) {
   2104  *aAllowAuth = mAllowAuth;
   2105  return NS_OK;
   2106 }
   2107 
   2108 NS_IMETHODIMP
   2109 nsDocShell::SetAllowAuth(bool aAllowAuth) {
   2110  mAllowAuth = aAllowAuth;
   2111  return NS_OK;
   2112 }
   2113 
   2114 NS_IMETHODIMP
   2115 nsDocShell::GetZoom(float* aZoom) {
   2116  NS_ENSURE_ARG_POINTER(aZoom);
   2117  *aZoom = 1.0f;
   2118  return NS_OK;
   2119 }
   2120 
   2121 NS_IMETHODIMP
   2122 nsDocShell::SetZoom(float aZoom) { return NS_ERROR_NOT_IMPLEMENTED; }
   2123 
   2124 NS_IMETHODIMP
   2125 nsDocShell::GetBusyFlags(BusyFlags* aBusyFlags) {
   2126  NS_ENSURE_ARG_POINTER(aBusyFlags);
   2127 
   2128  *aBusyFlags = mBusyFlags;
   2129  return NS_OK;
   2130 }
   2131 
   2132 NS_IMETHODIMP
   2133 nsDocShell::GetLoadURIDelegate(nsILoadURIDelegate** aLoadURIDelegate) {
   2134  nsCOMPtr<nsILoadURIDelegate> delegate = GetLoadURIDelegate();
   2135  delegate.forget(aLoadURIDelegate);
   2136  return NS_OK;
   2137 }
   2138 
   2139 already_AddRefed<nsILoadURIDelegate> nsDocShell::GetLoadURIDelegate() {
   2140  if (nsCOMPtr<nsILoadURIDelegate> result =
   2141          do_QueryActor("LoadURIDelegate", GetDocument())) {
   2142    return result.forget();
   2143  }
   2144 
   2145  return nullptr;
   2146 }
   2147 
   2148 NS_IMETHODIMP
   2149 nsDocShell::GetUseErrorPages(bool* aUseErrorPages) {
   2150  *aUseErrorPages = mBrowsingContext->GetUseErrorPages();
   2151  return NS_OK;
   2152 }
   2153 
   2154 NS_IMETHODIMP
   2155 nsDocShell::SetUseErrorPages(bool aUseErrorPages) {
   2156  return mBrowsingContext->SetUseErrorPages(aUseErrorPages);
   2157 }
   2158 
   2159 NS_IMETHODIMP
   2160 nsDocShell::GetPreviousEntryIndex(int32_t* aPreviousEntryIndex) {
   2161  *aPreviousEntryIndex = mPreviousEntryIndex;
   2162  return NS_OK;
   2163 }
   2164 
   2165 NS_IMETHODIMP
   2166 nsDocShell::GetLoadedEntryIndex(int32_t* aLoadedEntryIndex) {
   2167  *aLoadedEntryIndex = mLoadedEntryIndex;
   2168  return NS_OK;
   2169 }
   2170 
   2171 NS_IMETHODIMP
   2172 nsDocShell::HistoryPurged(int32_t aNumEntries) {
   2173  // These indices are used for fastback cache eviction, to determine
   2174  // which session history entries are candidates for content viewer
   2175  // eviction.  We need to adjust by the number of entries that we
   2176  // just purged from history, so that we look at the right session history
   2177  // entries during eviction.
   2178  mPreviousEntryIndex = std::max(-1, mPreviousEntryIndex - aNumEntries);
   2179  mLoadedEntryIndex = std::max(0, mLoadedEntryIndex - aNumEntries);
   2180 
   2181  for (auto* child : mChildList.ForwardRange()) {
   2182    nsCOMPtr<nsIDocShell> shell = do_QueryObject(child);
   2183    if (shell) {
   2184      shell->HistoryPurged(aNumEntries);
   2185    }
   2186  }
   2187 
   2188  return NS_OK;
   2189 }
   2190 
   2191 nsresult nsDocShell::HistoryEntryRemoved(int32_t aIndex) {
   2192  // These indices are used for fastback cache eviction, to determine
   2193  // which session history entries are candidates for content viewer
   2194  // eviction.  We need to adjust by the number of entries that we
   2195  // just purged from history, so that we look at the right session history
   2196  // entries during eviction.
   2197  if (aIndex == mPreviousEntryIndex) {
   2198    mPreviousEntryIndex = -1;
   2199  } else if (aIndex < mPreviousEntryIndex) {
   2200    --mPreviousEntryIndex;
   2201  }
   2202  if (mLoadedEntryIndex == aIndex) {
   2203    mLoadedEntryIndex = 0;
   2204  } else if (aIndex < mLoadedEntryIndex) {
   2205    --mLoadedEntryIndex;
   2206  }
   2207 
   2208  for (auto* child : mChildList.ForwardRange()) {
   2209    nsCOMPtr<nsIDocShell> shell = do_QueryObject(child);
   2210    if (shell) {
   2211      static_cast<nsDocShell*>(shell.get())->HistoryEntryRemoved(aIndex);
   2212    }
   2213  }
   2214 
   2215  return NS_OK;
   2216 }
   2217 
   2218 nsresult nsDocShell::Now(DOMHighResTimeStamp* aWhen) {
   2219  *aWhen = (TimeStamp::Now() - TimeStamp::ProcessCreation()).ToMilliseconds();
   2220  return NS_OK;
   2221 }
   2222 
   2223 NS_IMETHODIMP
   2224 nsDocShell::SetWindowDraggingAllowed(bool aValue) {
   2225  RefPtr<nsDocShell> parent;
   2226  if (!aValue && mItemType == typeChrome &&
   2227      !(parent = GetInProcessParentDocshell())) {
   2228    // Window dragging is always allowed for top level
   2229    // chrome docshells.
   2230    return NS_ERROR_FAILURE;
   2231  }
   2232  mWindowDraggingAllowed = aValue;
   2233  return NS_OK;
   2234 }
   2235 
   2236 NS_IMETHODIMP
   2237 nsDocShell::GetWindowDraggingAllowed(bool* aValue) {
   2238  // window dragging regions in CSS (-moz-window-drag:drag)
   2239  // can be slow. Default behavior is to only allow it for
   2240  // chrome top level windows.
   2241  RefPtr<nsDocShell> parent;
   2242  if (mItemType == typeChrome && !(parent = GetInProcessParentDocshell())) {
   2243    // Top level chrome window
   2244    *aValue = true;
   2245  } else {
   2246    *aValue = mWindowDraggingAllowed;
   2247  }
   2248  return NS_OK;
   2249 }
   2250 
   2251 NS_IMETHODIMP
   2252 nsDocShell::GetCurrentDocumentChannel(nsIChannel** aResult) {
   2253  NS_IF_ADDREF(*aResult = GetCurrentDocChannel());
   2254  return NS_OK;
   2255 }
   2256 
   2257 nsIChannel* nsDocShell::GetCurrentDocChannel() {
   2258  if (mDocumentViewer) {
   2259    Document* doc = mDocumentViewer->GetDocument();
   2260    if (doc) {
   2261      return doc->GetChannel();
   2262    }
   2263  }
   2264  return nullptr;
   2265 }
   2266 
   2267 NS_IMETHODIMP
   2268 nsDocShell::AddWeakScrollObserver(nsIScrollObserver* aObserver) {
   2269  nsWeakPtr weakObs = do_GetWeakReference(aObserver);
   2270  if (!weakObs) {
   2271    return NS_ERROR_FAILURE;
   2272  }
   2273  mScrollObservers.AppendElement(weakObs);
   2274  return NS_OK;
   2275 }
   2276 
   2277 NS_IMETHODIMP
   2278 nsDocShell::RemoveWeakScrollObserver(nsIScrollObserver* aObserver) {
   2279  nsWeakPtr obs = do_GetWeakReference(aObserver);
   2280  return mScrollObservers.RemoveElement(obs) ? NS_OK : NS_ERROR_FAILURE;
   2281 }
   2282 
   2283 void nsDocShell::NotifyAsyncPanZoomStarted() {
   2284  nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers);
   2285  while (iter.HasMore()) {
   2286    nsWeakPtr ref = iter.GetNext();
   2287    nsCOMPtr<nsIScrollObserver> obs = do_QueryReferent(ref);
   2288    if (obs) {
   2289      obs->AsyncPanZoomStarted();
   2290    } else {
   2291      iter.Remove();
   2292    }
   2293  }
   2294 }
   2295 
   2296 void nsDocShell::NotifyAsyncPanZoomStopped() {
   2297  nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers);
   2298  while (iter.HasMore()) {
   2299    nsWeakPtr ref = iter.GetNext();
   2300    nsCOMPtr<nsIScrollObserver> obs = do_QueryReferent(ref);
   2301    if (obs) {
   2302      obs->AsyncPanZoomStopped();
   2303    } else {
   2304      iter.Remove();
   2305    }
   2306  }
   2307 }
   2308 
   2309 NS_IMETHODIMP
   2310 nsDocShell::NotifyScrollObservers() {
   2311  nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers);
   2312  while (iter.HasMore()) {
   2313    nsWeakPtr ref = iter.GetNext();
   2314    nsCOMPtr<nsIScrollObserver> obs = do_QueryReferent(ref);
   2315    if (obs) {
   2316      obs->ScrollPositionChanged();
   2317    } else {
   2318      iter.Remove();
   2319    }
   2320  }
   2321  return NS_OK;
   2322 }
   2323 
   2324 //*****************************************************************************
   2325 // nsDocShell::nsIDocShellTreeItem
   2326 //*****************************************************************************
   2327 
   2328 NS_IMETHODIMP
   2329 nsDocShell::GetName(nsAString& aName) {
   2330  aName = mBrowsingContext->Name();
   2331  return NS_OK;
   2332 }
   2333 
   2334 NS_IMETHODIMP
   2335 nsDocShell::SetName(const nsAString& aName) {
   2336  return mBrowsingContext->SetName(aName);
   2337 }
   2338 
   2339 NS_IMETHODIMP
   2340 nsDocShell::NameEquals(const nsAString& aName, bool* aResult) {
   2341  NS_ENSURE_ARG_POINTER(aResult);
   2342  *aResult = mBrowsingContext->NameEquals(aName);
   2343  return NS_OK;
   2344 }
   2345 
   2346 NS_IMETHODIMP
   2347 nsDocShell::GetCustomUserAgent(nsAString& aCustomUserAgent) {
   2348  mBrowsingContext->GetCustomUserAgent(aCustomUserAgent);
   2349  return NS_OK;
   2350 }
   2351 
   2352 NS_IMETHODIMP
   2353 nsDocShell::SetCustomUserAgent(const nsAString& aCustomUserAgent) {
   2354  if (mWillChangeProcess) {
   2355    NS_WARNING("SetCustomUserAgent: Process is changing. Ignoring set");
   2356    return NS_ERROR_FAILURE;
   2357  }
   2358 
   2359  return mBrowsingContext->SetCustomUserAgent(aCustomUserAgent);
   2360 }
   2361 
   2362 NS_IMETHODIMP
   2363 nsDocShell::ClearCachedPlatform() {
   2364  nsCOMPtr<nsPIDOMWindowInner> win =
   2365      mScriptGlobal ? mScriptGlobal->GetCurrentInnerWindow() : nullptr;
   2366  if (win) {
   2367    Navigator* navigator = win->Navigator();
   2368    if (navigator) {
   2369      navigator->ClearPlatformCache();
   2370    }
   2371  }
   2372 
   2373  return NS_OK;
   2374 }
   2375 
   2376 NS_IMETHODIMP
   2377 nsDocShell::ClearCachedUserAgent() {
   2378  nsCOMPtr<nsPIDOMWindowInner> win =
   2379      mScriptGlobal ? mScriptGlobal->GetCurrentInnerWindow() : nullptr;
   2380  if (win) {
   2381    Navigator* navigator = win->Navigator();
   2382    if (navigator) {
   2383      navigator->ClearUserAgentCache();
   2384    }
   2385  }
   2386 
   2387  return NS_OK;
   2388 }
   2389 
   2390 /* virtual */
   2391 int32_t nsDocShell::ItemType() { return mItemType; }
   2392 
   2393 NS_IMETHODIMP
   2394 nsDocShell::GetItemType(int32_t* aItemType) {
   2395  NS_ENSURE_ARG_POINTER(aItemType);
   2396 
   2397  MOZ_DIAGNOSTIC_ASSERT(
   2398      (mBrowsingContext->IsContent() ? typeContent : typeChrome) == mItemType);
   2399  *aItemType = mItemType;
   2400  return NS_OK;
   2401 }
   2402 
   2403 NS_IMETHODIMP
   2404 nsDocShell::GetInProcessParent(nsIDocShellTreeItem** aParent) {
   2405  if (!mParent) {
   2406    *aParent = nullptr;
   2407  } else {
   2408    CallQueryInterface(mParent, aParent);
   2409  }
   2410  // Note that in the case when the parent is not an nsIDocShellTreeItem we
   2411  // don't want to throw; we just want to return null.
   2412  return NS_OK;
   2413 }
   2414 
   2415 // With Fission, related nsDocShell objects may exist in a different process. In
   2416 // that case, this method will return `nullptr`, despite a parent nsDocShell
   2417 // object existing.
   2418 //
   2419 // Prefer using `BrowsingContext::Parent()`, which will succeed even if the
   2420 // parent entry is not in the current process, and handle the case where the
   2421 // parent nsDocShell is inaccessible.
   2422 already_AddRefed<nsDocShell> nsDocShell::GetInProcessParentDocshell() {
   2423  nsCOMPtr<nsIDocShell> docshell = do_QueryInterface(GetAsSupports(mParent));
   2424  return docshell.forget().downcast<nsDocShell>();
   2425 }
   2426 
   2427 void nsDocShell::MaybeCreateInitialClientSource(nsIPrincipal* aPrincipal) {
   2428  MOZ_ASSERT(!mIsBeingDestroyed);
   2429 
   2430  // If there is an existing document then there is no need to create
   2431  // a client for a future initial about:blank document.
   2432  if (mScriptGlobal && mScriptGlobal->GetCurrentInnerWindow() &&
   2433      mScriptGlobal->GetCurrentInnerWindow()->GetExtantDoc()) {
   2434    MOZ_DIAGNOSTIC_ASSERT(
   2435        mScriptGlobal->GetCurrentInnerWindow()->GetClientInfo().isSome());
   2436    MOZ_DIAGNOSTIC_ASSERT(!mInitialClientSource);
   2437    return;
   2438  }
   2439 
   2440  // Don't recreate the initial client source.  We call this multiple times
   2441  // when DoChannelLoad() is called before CreateAboutBlankDocumentViewer.
   2442  if (mInitialClientSource) {
   2443    return;
   2444  }
   2445 
   2446  // Don't pre-allocate the client when we are sandboxed.  The inherited
   2447  // principal does not take sandboxing into account.
   2448  // TODO: Refactor sandboxing principal code out so we can use it here.
   2449  if (!aPrincipal && mBrowsingContext->GetSandboxFlags()) {
   2450    return;
   2451  }
   2452 
   2453  // We cannot get inherited foreign partitioned principal here. Instead, we
   2454  // directly check which principal we want to inherit for the service worker.
   2455  nsIPrincipal* principal =
   2456      aPrincipal
   2457          ? aPrincipal
   2458          : GetInheritedPrincipal(
   2459                false, StoragePrincipalHelper::
   2460                           ShouldUsePartitionPrincipalForServiceWorker(this));
   2461 
   2462  // Sometimes there is no principal available when we are called from
   2463  // CreateAboutBlankDocumentViewer.  For example, sometimes the principal
   2464  // is only extracted from the load context after the document is created
   2465  // in Document::ResetToURI().  Ideally we would do something similar
   2466  // here, but for now lets just avoid the issue by not preallocating the
   2467  // client.
   2468  if (!principal) {
   2469    return;
   2470  }
   2471 
   2472  nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
   2473  if (!win) {
   2474    return;
   2475  }
   2476 
   2477  mInitialClientSource = ClientManager::CreateSource(
   2478      ClientType::Window, GetMainThreadSerialEventTarget(), principal);
   2479  MOZ_DIAGNOSTIC_ASSERT(mInitialClientSource);
   2480 
   2481  // Mark the initial client as execution ready, but owned by the docshell.
   2482  // If the client is actually used this will cause ClientSource to force
   2483  // the creation of the initial about:blank by calling
   2484  // nsDocShell::GetDocument().
   2485  mInitialClientSource->DocShellExecutionReady(this);
   2486 
   2487  // Next, check to see if the parent is controlled.
   2488  MaybeInheritController(mInitialClientSource.get(), principal);
   2489 }
   2490 
   2491 void VerifyCientPrincipalInfosMatch(const mozilla::ipc::PrincipalInfo& aLeft,
   2492                                    const mozilla::ipc::PrincipalInfo& aRight) {
   2493  // Inheriting a controller when the principals don't match would cause a
   2494  // crash. Let's do the checks earlier to crash here already instead of
   2495  // ClientSource::SetController. And assert each condition separately. See bug
   2496  // 1880012.
   2497  MOZ_RELEASE_ASSERT(aLeft.type() == aRight.type());
   2498 
   2499  switch (aLeft.type()) {
   2500    case mozilla::ipc::PrincipalInfo::TContentPrincipalInfo: {
   2501      const mozilla::ipc::ContentPrincipalInfo& leftContent =
   2502          aLeft.get_ContentPrincipalInfo();
   2503      const mozilla::ipc::ContentPrincipalInfo& rightContent =
   2504          aRight.get_ContentPrincipalInfo();
   2505      MOZ_RELEASE_ASSERT(leftContent.attrs() == rightContent.attrs() &&
   2506                         leftContent.originNoSuffix() ==
   2507                             rightContent.originNoSuffix());
   2508      return;
   2509    }
   2510    case mozilla::ipc::PrincipalInfo::TNullPrincipalInfo: {
   2511      // null principal never matches
   2512      MOZ_RELEASE_ASSERT(false, "Clients have null principals");
   2513      return;
   2514    }
   2515    default: {
   2516      break;
   2517    }
   2518  }
   2519 }
   2520 
   2521 void nsDocShell::MaybeInheritController(
   2522    mozilla::dom::ClientSource* aClientSource, nsIPrincipal* aPrincipal) {
   2523  nsCOMPtr<nsIDocShell> parent = GetInProcessParentDocshell();
   2524  if (!parent) {
   2525    if (RefPtr<BrowsingContext> opener = mBrowsingContext->GetOpener()) {
   2526      parent = opener->GetDocShell();
   2527    }
   2528  }
   2529  nsPIDOMWindowOuter* parentOuter = parent ? parent->GetWindow() : nullptr;
   2530  nsPIDOMWindowInner* parentInner =
   2531      parentOuter ? parentOuter->GetCurrentInnerWindow() : nullptr;
   2532  if (!parentInner) {
   2533    return;
   2534  }
   2535 
   2536  nsCOMPtr<nsIURI> uri;
   2537  MOZ_ALWAYS_SUCCEEDS(NS_NewURI(getter_AddRefs(uri), "about:blank"_ns));
   2538 
   2539  // We're done if there is no parent controller or if this docshell
   2540  // is not permitted to control for some reason.
   2541  Maybe<ServiceWorkerDescriptor> controller(parentInner->GetController());
   2542  if (controller.isNothing() ||
   2543      !ServiceWorkerAllowedToControlWindow(aPrincipal, uri)) {
   2544    return;
   2545  }
   2546 
   2547  VerifyCientPrincipalInfosMatch(aClientSource->Info().PrincipalInfo(),
   2548                                 controller->PrincipalInfo());
   2549  aClientSource->InheritController(controller.ref());
   2550 }
   2551 
   2552 Maybe<ClientInfo> nsDocShell::GetInitialClientInfo() const {
   2553  if (mInitialClientSource) {
   2554    Maybe<ClientInfo> result;
   2555    result.emplace(mInitialClientSource->Info());
   2556    return result;
   2557  }
   2558 
   2559  nsPIDOMWindowInner* innerWindow =
   2560      mScriptGlobal ? mScriptGlobal->GetCurrentInnerWindow() : nullptr;
   2561  Document* doc = innerWindow ? innerWindow->GetExtantDoc() : nullptr;
   2562 
   2563  if (!doc || !doc->IsUncommittedInitialDocument()) {
   2564    // We won't reuse the inner window so let the channel determine one
   2565    return Maybe<ClientInfo>();
   2566  }
   2567 
   2568  return innerWindow->GetClientInfo();
   2569 }
   2570 
   2571 nsresult nsDocShell::SetDocLoaderParent(nsDocLoader* aParent) {
   2572  bool wasFrame = IsSubframe();
   2573 
   2574  nsresult rv = nsDocLoader::SetDocLoaderParent(aParent);
   2575  NS_ENSURE_SUCCESS(rv, rv);
   2576 
   2577  nsCOMPtr<nsISupportsPriority> priorityGroup = do_QueryInterface(mLoadGroup);
   2578  if (wasFrame != IsSubframe() && priorityGroup) {
   2579    priorityGroup->AdjustPriority(wasFrame ? -1 : 1);
   2580  }
   2581 
   2582  // Curse ambiguous nsISupports inheritance!
   2583  nsISupports* parent = GetAsSupports(aParent);
   2584 
   2585  // If parent is another docshell, we inherit all their flags for
   2586  // allowing plugins, scripting etc.
   2587  bool value;
   2588  nsCOMPtr<nsIDocShell> parentAsDocShell(do_QueryInterface(parent));
   2589 
   2590  if (parentAsDocShell) {
   2591    if (mAllowMetaRedirects &&
   2592        NS_SUCCEEDED(parentAsDocShell->GetAllowMetaRedirects(&value))) {
   2593      SetAllowMetaRedirects(value);
   2594    }
   2595    if (mAllowSubframes &&
   2596        NS_SUCCEEDED(parentAsDocShell->GetAllowSubframes(&value))) {
   2597      SetAllowSubframes(value);
   2598    }
   2599    if (mAllowImages &&
   2600        NS_SUCCEEDED(parentAsDocShell->GetAllowImages(&value))) {
   2601      SetAllowImages(value);
   2602    }
   2603    SetAllowMedia(parentAsDocShell->GetAllowMedia() && mAllowMedia);
   2604    if (mAllowWindowControl &&
   2605        NS_SUCCEEDED(parentAsDocShell->GetAllowWindowControl(&value))) {
   2606      SetAllowWindowControl(value);
   2607    }
   2608    if (NS_FAILED(parentAsDocShell->GetAllowDNSPrefetch(&value))) {
   2609      value = false;
   2610    }
   2611    SetAllowDNSPrefetch(mAllowDNSPrefetch && value);
   2612  }
   2613 
   2614  nsCOMPtr<nsIURIContentListener> parentURIListener(do_GetInterface(parent));
   2615  if (parentURIListener) {
   2616    mContentListener->SetParentContentListener(parentURIListener);
   2617  }
   2618 
   2619  return NS_OK;
   2620 }
   2621 
   2622 void nsDocShell::MaybeRestoreWindowName() {
   2623  if (!StaticPrefs::privacy_window_name_update_enabled()) {
   2624    return;
   2625  }
   2626 
   2627  // We only restore window.name for the top-level content.
   2628  if (!mBrowsingContext->IsTopContent()) {
   2629    return;
   2630  }
   2631 
   2632  nsAutoString name;
   2633 
   2634  // Following implements https://html.spec.whatwg.org/#history-traversal:
   2635  // Step 4.4. Check if the loading entry has a name.
   2636 
   2637  if (mLSHE) {
   2638    mLSHE->GetName(name);
   2639  }
   2640 
   2641  if (mLoadingEntry) {
   2642    name = mLoadingEntry->mInfo.GetName();
   2643  }
   2644 
   2645  if (name.IsEmpty()) {
   2646    return;
   2647  }
   2648 
   2649  // Step 4.4.1. Set the name to the browsing context.
   2650  (void)mBrowsingContext->SetName(name);
   2651 
   2652  // Step 4.4.2. Clear the name of all entries that are contiguous and
   2653  // same-origin with the loading entry.
   2654  if (mLSHE) {
   2655    nsSHistory::WalkContiguousEntries(
   2656        mLSHE, [](nsISHEntry* aEntry) { aEntry->SetName(EmptyString()); });
   2657  }
   2658 
   2659  if (mLoadingEntry) {
   2660    // Clear the name of the session entry in the child side. For parent side,
   2661    // the clearing will be done when we commit the history to the parent.
   2662    mLoadingEntry->mInfo.SetName(EmptyString());
   2663  }
   2664 }
   2665 
   2666 void nsDocShell::StoreWindowNameToSHEntries() {
   2667  MOZ_ASSERT(mBrowsingContext->IsTopContent());
   2668 
   2669  nsAutoString name;
   2670  mBrowsingContext->GetName(name);
   2671 
   2672  if (mOSHE) {
   2673    nsSHistory::WalkContiguousEntries(
   2674        mOSHE, [&](nsISHEntry* aEntry) { aEntry->SetName(name); });
   2675  }
   2676 
   2677  if (mozilla::SessionHistoryInParent()) {
   2678    if (XRE_IsParentProcess()) {
   2679      SessionHistoryEntry* entry =
   2680          mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
   2681      if (entry) {
   2682        nsSHistory::WalkContiguousEntries(
   2683            entry, [&](nsISHEntry* aEntry) { aEntry->SetName(name); });
   2684      }
   2685    } else {
   2686      // Ask parent process to store the name in entries.
   2687      (void)ContentChild::GetSingleton()
   2688          ->SendSessionHistoryEntryStoreWindowNameInContiguousEntries(
   2689              mBrowsingContext, name);
   2690    }
   2691  }
   2692 }
   2693 
   2694 NS_IMETHODIMP
   2695 nsDocShell::GetInProcessSameTypeParent(nsIDocShellTreeItem** aParent) {
   2696  if (BrowsingContext* parentBC = mBrowsingContext->GetParent()) {
   2697    *aParent = do_AddRef(parentBC->GetDocShell()).take();
   2698  }
   2699  return NS_OK;
   2700 }
   2701 
   2702 NS_IMETHODIMP
   2703 nsDocShell::GetInProcessRootTreeItem(nsIDocShellTreeItem** aRootTreeItem) {
   2704  NS_ENSURE_ARG_POINTER(aRootTreeItem);
   2705 
   2706  RefPtr<nsDocShell> root = this;
   2707  RefPtr<nsDocShell> parent = root->GetInProcessParentDocshell();
   2708  while (parent) {
   2709    root = parent;
   2710    parent = root->GetInProcessParentDocshell();
   2711  }
   2712 
   2713  root.forget(aRootTreeItem);
   2714  return NS_OK;
   2715 }
   2716 
   2717 NS_IMETHODIMP
   2718 nsDocShell::GetInProcessSameTypeRootTreeItem(
   2719    nsIDocShellTreeItem** aRootTreeItem) {
   2720  NS_ENSURE_ARG_POINTER(aRootTreeItem);
   2721  *aRootTreeItem = static_cast<nsIDocShellTreeItem*>(this);
   2722 
   2723  nsCOMPtr<nsIDocShellTreeItem> parent;
   2724  NS_ENSURE_SUCCESS(GetInProcessSameTypeParent(getter_AddRefs(parent)),
   2725                    NS_ERROR_FAILURE);
   2726  while (parent) {
   2727    *aRootTreeItem = parent;
   2728    NS_ENSURE_SUCCESS(
   2729        (*aRootTreeItem)->GetInProcessSameTypeParent(getter_AddRefs(parent)),
   2730        NS_ERROR_FAILURE);
   2731  }
   2732  NS_ADDREF(*aRootTreeItem);
   2733  return NS_OK;
   2734 }
   2735 
   2736 NS_IMETHODIMP
   2737 nsDocShell::GetTreeOwner(nsIDocShellTreeOwner** aTreeOwner) {
   2738  NS_ENSURE_ARG_POINTER(aTreeOwner);
   2739 
   2740  *aTreeOwner = mTreeOwner;
   2741  NS_IF_ADDREF(*aTreeOwner);
   2742  return NS_OK;
   2743 }
   2744 
   2745 NS_IMETHODIMP
   2746 nsDocShell::SetTreeOwner(nsIDocShellTreeOwner* aTreeOwner) {
   2747  if (mIsBeingDestroyed && aTreeOwner) {
   2748    return NS_ERROR_FAILURE;
   2749  }
   2750 
   2751  // Don't automatically set the progress based on the tree owner for frames
   2752  if (!IsSubframe()) {
   2753    nsCOMPtr<nsIWebProgress> webProgress =
   2754        do_QueryInterface(GetAsSupports(this));
   2755 
   2756    if (webProgress) {
   2757      nsCOMPtr<nsIWebProgressListener> oldListener =
   2758          do_QueryInterface(mTreeOwner);
   2759      nsCOMPtr<nsIWebProgressListener> newListener =
   2760          do_QueryInterface(aTreeOwner);
   2761 
   2762      if (oldListener) {
   2763        webProgress->RemoveProgressListener(oldListener);
   2764      }
   2765 
   2766      if (newListener) {
   2767        webProgress->AddProgressListener(newListener,
   2768                                         nsIWebProgress::NOTIFY_ALL);
   2769      }
   2770    }
   2771  }
   2772 
   2773  mTreeOwner = aTreeOwner;  // Weak reference per API
   2774 
   2775  for (auto* childDocLoader : mChildList.ForwardRange()) {
   2776    nsCOMPtr<nsIDocShellTreeItem> child = do_QueryObject(childDocLoader);
   2777    NS_ENSURE_TRUE(child, NS_ERROR_FAILURE);
   2778 
   2779    if (child->ItemType() == mItemType) {
   2780      child->SetTreeOwner(aTreeOwner);
   2781    }
   2782  }
   2783 
   2784  // If we're in the content process and have had a TreeOwner set on us, extract
   2785  // our BrowserChild actor. If we've already had our BrowserChild set, assert
   2786  // that it hasn't changed.
   2787  if (mTreeOwner && XRE_IsContentProcess()) {
   2788    nsCOMPtr<nsIBrowserChild> newBrowserChild = do_GetInterface(mTreeOwner);
   2789    MOZ_ASSERT(newBrowserChild,
   2790               "No BrowserChild actor for tree owner in Content!");
   2791 
   2792    if (mBrowserChild) {
   2793      nsCOMPtr<nsIBrowserChild> oldBrowserChild =
   2794          do_QueryReferent(mBrowserChild);
   2795      MOZ_RELEASE_ASSERT(
   2796          oldBrowserChild == newBrowserChild,
   2797          "Cannot change BrowserChild during nsDocShell lifetime!");
   2798    } else {
   2799      mBrowserChild = do_GetWeakReference(newBrowserChild);
   2800    }
   2801  }
   2802 
   2803  return NS_OK;
   2804 }
   2805 
   2806 NS_IMETHODIMP
   2807 nsDocShell::GetHistoryID(nsID& aID) {
   2808  aID = mBrowsingContext->GetHistoryID();
   2809  return NS_OK;
   2810 }
   2811 
   2812 const nsID& nsDocShell::HistoryID() { return mBrowsingContext->GetHistoryID(); }
   2813 
   2814 NS_IMETHODIMP
   2815 nsDocShell::GetIsInUnload(bool* aIsInUnload) {
   2816  *aIsInUnload = mFiredUnloadEvent;
   2817  return NS_OK;
   2818 }
   2819 
   2820 NS_IMETHODIMP
   2821 nsDocShell::GetInProcessChildCount(int32_t* aChildCount) {
   2822  NS_ENSURE_ARG_POINTER(aChildCount);
   2823  *aChildCount = mChildList.Length();
   2824  return NS_OK;
   2825 }
   2826 
   2827 NS_IMETHODIMP
   2828 nsDocShell::AddChild(nsIDocShellTreeItem* aChild) {
   2829  NS_ENSURE_ARG_POINTER(aChild);
   2830 
   2831  RefPtr<nsDocLoader> childAsDocLoader = GetAsDocLoader(aChild);
   2832  NS_ENSURE_TRUE(childAsDocLoader, NS_ERROR_UNEXPECTED);
   2833 
   2834  // Make sure we're not creating a loop in the docshell tree
   2835  nsDocLoader* ancestor = this;
   2836  do {
   2837    if (childAsDocLoader == ancestor) {
   2838      return NS_ERROR_ILLEGAL_VALUE;
   2839    }
   2840    ancestor = ancestor->GetParent();
   2841  } while (ancestor);
   2842 
   2843  // Make sure to remove the child from its current parent.
   2844  nsDocLoader* childsParent = childAsDocLoader->GetParent();
   2845  if (childsParent) {
   2846    nsresult rv = childsParent->RemoveChildLoader(childAsDocLoader);
   2847    NS_ENSURE_SUCCESS(rv, rv);
   2848  }
   2849 
   2850  // Make sure to clear the treeowner in case this child is a different type
   2851  // from us.
   2852  aChild->SetTreeOwner(nullptr);
   2853 
   2854  nsresult res = AddChildLoader(childAsDocLoader);
   2855  NS_ENSURE_SUCCESS(res, res);
   2856  NS_ASSERTION(!mChildList.IsEmpty(),
   2857               "child list must not be empty after a successful add");
   2858 
   2859  /* Set the child's global history if the parent has one */
   2860  if (mBrowsingContext->GetUseGlobalHistory()) {
   2861    // childDocShell->SetUseGlobalHistory(true);
   2862    // this should be set through BC inherit
   2863    MOZ_ASSERT(aChild->GetBrowsingContext()->GetUseGlobalHistory());
   2864  }
   2865 
   2866  if (aChild->ItemType() != mItemType) {
   2867    return NS_OK;
   2868  }
   2869 
   2870  aChild->SetTreeOwner(mTreeOwner);
   2871 
   2872  nsCOMPtr<nsIDocShell> childAsDocShell(do_QueryInterface(aChild));
   2873  if (!childAsDocShell) {
   2874    return NS_OK;
   2875  }
   2876 
   2877  // charset, style-disabling, and zoom will be inherited in SetupNewViewer()
   2878 
   2879  // Now take this document's charset and set the child's parentCharset field
   2880  // to it. We'll later use that field, in the loading process, for the
   2881  // charset choosing algorithm.
   2882  // If we fail, at any point, we just return NS_OK.
   2883  // This code has some performance impact. But this will be reduced when
   2884  // the current charset will finally be stored as an Atom, avoiding the
   2885  // alias resolution extra look-up.
   2886 
   2887  // we are NOT going to propagate the charset is this Chrome's docshell
   2888  if (mItemType == nsIDocShellTreeItem::typeChrome) {
   2889    return NS_OK;
   2890  }
   2891 
   2892  // get the parent's current charset
   2893  if (!mDocumentViewer) {
   2894    return NS_OK;
   2895  }
   2896  Document* doc = mDocumentViewer->GetDocument();
   2897  if (!doc) {
   2898    return NS_OK;
   2899  }
   2900 
   2901  const Encoding* parentCS = doc->GetDocumentCharacterSet();
   2902  int32_t charsetSource = doc->GetDocumentCharacterSetSource();
   2903  // set the child's parentCharset
   2904  childAsDocShell->SetParentCharset(parentCS, charsetSource,
   2905                                    doc->NodePrincipal());
   2906 
   2907  // printf("### 1 >>> Adding child. Parent CS = %s. ItemType = %d.\n",
   2908  //        NS_LossyConvertUTF16toASCII(parentCS).get(), mItemType);
   2909 
   2910  return NS_OK;
   2911 }
   2912 
   2913 NS_IMETHODIMP
   2914 nsDocShell::RemoveChild(nsIDocShellTreeItem* aChild) {
   2915  NS_ENSURE_ARG_POINTER(aChild);
   2916 
   2917  RefPtr<nsDocLoader> childAsDocLoader = GetAsDocLoader(aChild);
   2918  NS_ENSURE_TRUE(childAsDocLoader, NS_ERROR_UNEXPECTED);
   2919 
   2920  nsresult rv = RemoveChildLoader(childAsDocLoader);
   2921  NS_ENSURE_SUCCESS(rv, rv);
   2922 
   2923  aChild->SetTreeOwner(nullptr);
   2924 
   2925  return nsDocLoader::AddDocLoaderAsChildOfRoot(childAsDocLoader);
   2926 }
   2927 
   2928 NS_IMETHODIMP
   2929 nsDocShell::GetInProcessChildAt(int32_t aIndex, nsIDocShellTreeItem** aChild) {
   2930  NS_ENSURE_ARG_POINTER(aChild);
   2931 
   2932  RefPtr<nsDocShell> child = GetInProcessChildAt(aIndex);
   2933  NS_ENSURE_TRUE(child, NS_ERROR_UNEXPECTED);
   2934 
   2935  child.forget(aChild);
   2936 
   2937  return NS_OK;
   2938 }
   2939 
   2940 nsDocShell* nsDocShell::GetInProcessChildAt(int32_t aIndex) {
   2941 #ifdef DEBUG
   2942  if (aIndex < 0) {
   2943    NS_WARNING("Negative index passed to GetChildAt");
   2944  } else if (static_cast<uint32_t>(aIndex) >= mChildList.Length()) {
   2945    NS_WARNING("Too large an index passed to GetChildAt");
   2946  }
   2947 #endif
   2948 
   2949  nsIDocumentLoader* child = ChildAt(aIndex);
   2950 
   2951  // child may be nullptr here.
   2952  return static_cast<nsDocShell*>(child);
   2953 }
   2954 
   2955 nsresult nsDocShell::AddChildSHEntry(nsISHEntry* aCloneRef,
   2956                                     nsISHEntry* aNewEntry,
   2957                                     int32_t aChildOffset, uint32_t aLoadType,
   2958                                     bool aCloneChildren) {
   2959  MOZ_ASSERT(!mozilla::SessionHistoryInParent());
   2960  nsresult rv = NS_OK;
   2961 
   2962  if (mLSHE && aLoadType != LOAD_PUSHSTATE) {
   2963    /* You get here if you are currently building a
   2964     * hierarchy ie.,you just visited a frameset page
   2965     */
   2966    if (NS_FAILED(mLSHE->ReplaceChild(aNewEntry))) {
   2967      rv = mLSHE->AddChild(aNewEntry, aChildOffset);
   2968    }
   2969  } else if (!aCloneRef) {
   2970    /* This is an initial load in some subframe.  Just append it if we can */
   2971    if (mOSHE) {
   2972      rv = mOSHE->AddChild(aNewEntry, aChildOffset, UseRemoteSubframes());
   2973    }
   2974  } else {
   2975    RefPtr<ChildSHistory> shistory = GetRootSessionHistory();
   2976    if (shistory) {
   2977      rv = shistory->LegacySHistory()->AddNestedSHEntry(
   2978          aCloneRef, aNewEntry, mBrowsingContext->Top(), aCloneChildren);
   2979    }
   2980  }
   2981  return rv;
   2982 }
   2983 
   2984 nsresult nsDocShell::AddChildSHEntryToParent(nsISHEntry* aNewEntry,
   2985                                             int32_t aChildOffset,
   2986                                             bool aCloneChildren) {
   2987  MOZ_ASSERT(!mozilla::SessionHistoryInParent());
   2988  /* You will get here when you are in a subframe and
   2989   * a new url has been loaded on you.
   2990   * The mOSHE in this subframe will be the previous url's
   2991   * mOSHE. This mOSHE will be used as the identification
   2992   * for this subframe in the  CloneAndReplace function.
   2993   */
   2994 
   2995  // In this case, we will end up calling AddEntry, which increases the
   2996  // current index by 1
   2997  RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
   2998  if (rootSH) {
   2999    mPreviousEntryIndex = rootSH->Index();
   3000  }
   3001 
   3002  nsresult rv;
   3003  // XXX(farre): this is not Fission safe, expect errors. This never
   3004  // get's executed once session history in the parent is enabled.
   3005  nsCOMPtr<nsIDocShell> parent = do_QueryInterface(GetAsSupports(mParent), &rv);
   3006  NS_WARNING_ASSERTION(
   3007      parent || !UseRemoteSubframes(),
   3008      "Failed to add child session history entry! This will be resolved once "
   3009      "session history in the parent is enabled.");
   3010  if (parent) {
   3011    rv = nsDocShell::Cast(parent)->AddChildSHEntry(
   3012        mOSHE, aNewEntry, aChildOffset, mLoadType, aCloneChildren);
   3013  }
   3014 
   3015  if (rootSH) {
   3016    mLoadedEntryIndex = rootSH->Index();
   3017 
   3018    if (MOZ_UNLIKELY(MOZ_LOG_TEST(gPageCacheLog, LogLevel::Verbose))) {
   3019      MOZ_LOG(gPageCacheLog, LogLevel::Verbose,
   3020              ("Previous index: %d, Loaded index: %d", mPreviousEntryIndex,
   3021               mLoadedEntryIndex));
   3022    }
   3023  }
   3024 
   3025  return rv;
   3026 }
   3027 
   3028 NS_IMETHODIMP
   3029 nsDocShell::GetCurrentSHEntry(nsISHEntry** aEntry, bool* aOSHE) {
   3030  *aOSHE = false;
   3031  *aEntry = nullptr;
   3032  if (mLSHE) {
   3033    NS_ADDREF(*aEntry = mLSHE);
   3034  } else if (mOSHE) {
   3035    NS_ADDREF(*aEntry = mOSHE);
   3036    *aOSHE = true;
   3037  }
   3038  return NS_OK;
   3039 }
   3040 
   3041 NS_IMETHODIMP nsDocShell::SynchronizeLayoutHistoryState() {
   3042  if (mActiveEntry && mActiveEntry->GetLayoutHistoryState() &&
   3043      mBrowsingContext) {
   3044    if (XRE_IsContentProcess()) {
   3045      dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
   3046      if (contentChild) {
   3047        contentChild->SendSynchronizeLayoutHistoryState(
   3048            mBrowsingContext, mActiveEntry->GetLayoutHistoryState());
   3049      }
   3050    } else {
   3051      SessionHistoryEntry* entry =
   3052          mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
   3053      if (entry) {
   3054        entry->SetLayoutHistoryState(mActiveEntry->GetLayoutHistoryState());
   3055      }
   3056    }
   3057    if (mLoadingEntry &&
   3058        mLoadingEntry->mInfo.SharedId() == mActiveEntry->SharedId()) {
   3059      mLoadingEntry->mInfo.SetLayoutHistoryState(
   3060          mActiveEntry->GetLayoutHistoryState());
   3061    }
   3062  }
   3063 
   3064  return NS_OK;
   3065 }
   3066 
   3067 void nsDocShell::SetLoadGroupDefaultLoadFlags(nsLoadFlags aLoadFlags) {
   3068  if (mLoadGroup) {
   3069    mLoadGroup->SetDefaultLoadFlags(aLoadFlags);
   3070  } else {
   3071    NS_WARNING(
   3072        "nsDocShell::SetLoadGroupDefaultLoadFlags has no loadGroup to "
   3073        "propagate the mode to");
   3074  }
   3075 }
   3076 
   3077 nsIScriptGlobalObject* nsDocShell::GetScriptGlobalObject() {
   3078  NS_ENSURE_SUCCESS(EnsureScriptEnvironment(), nullptr);
   3079  return mScriptGlobal;
   3080 }
   3081 
   3082 Document* nsDocShell::GetDocument() {
   3083  NS_ENSURE_TRUE(VerifyDocumentViewer(), nullptr);
   3084  return mDocumentViewer->GetDocument();
   3085 }
   3086 
   3087 Document* nsDocShell::GetExtantDocument() {
   3088  return mDocumentViewer ? mDocumentViewer->GetDocument() : nullptr;
   3089 }
   3090 
   3091 nsPIDOMWindowOuter* nsDocShell::GetWindow() {
   3092  if (NS_FAILED(EnsureScriptEnvironment())) {
   3093    return nullptr;
   3094  }
   3095  return mScriptGlobal;
   3096 }
   3097 
   3098 NS_IMETHODIMP
   3099 nsDocShell::GetDomWindow(mozIDOMWindowProxy** aWindow) {
   3100  NS_ENSURE_ARG_POINTER(aWindow);
   3101 
   3102  nsresult rv = EnsureScriptEnvironment();
   3103  NS_ENSURE_SUCCESS(rv, rv);
   3104 
   3105  RefPtr<nsGlobalWindowOuter> window = mScriptGlobal;
   3106  window.forget(aWindow);
   3107  return NS_OK;
   3108 }
   3109 
   3110 NS_IMETHODIMP
   3111 nsDocShell::GetMessageManager(ContentFrameMessageManager** aMessageManager) {
   3112  RefPtr<ContentFrameMessageManager> mm;
   3113  if (RefPtr<BrowserChild> browserChild = BrowserChild::GetFrom(this)) {
   3114    mm = browserChild->GetMessageManager();
   3115  } else if (nsPIDOMWindowOuter* win = GetWindow()) {
   3116    mm = win->GetMessageManager();
   3117  }
   3118  mm.forget(aMessageManager);
   3119  return NS_OK;
   3120 }
   3121 
   3122 NS_IMETHODIMP
   3123 nsDocShell::GetIsNavigating(bool* aOut) {
   3124  *aOut = mIsNavigating;
   3125  return NS_OK;
   3126 }
   3127 
   3128 void nsDocShell::ClearFrameHistory(nsISHEntry* aEntry) {
   3129  MOZ_ASSERT(!mozilla::SessionHistoryInParent());
   3130  RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
   3131  if (!rootSH || !aEntry) {
   3132    return;
   3133  }
   3134 
   3135  rootSH->LegacySHistory()->RemoveFrameEntries(aEntry);
   3136 }
   3137 
   3138 //-------------------------------------
   3139 //-- Helper Method for Print discovery
   3140 //-------------------------------------
   3141 bool nsDocShell::NavigationBlockedByPrinting(bool aDisplayErrorDialog) {
   3142  if (!mBrowsingContext->Top()->GetIsPrinting()) {
   3143    return false;
   3144  }
   3145  if (aDisplayErrorDialog) {
   3146    DisplayLoadError(NS_ERROR_DOCUMENT_IS_PRINTMODE, nullptr, nullptr, nullptr);
   3147  }
   3148  return true;
   3149 }
   3150 
   3151 bool nsDocShell::IsNavigationAllowed(bool aDisplayPrintErrorDialog,
   3152                                     bool aCheckIfUnloadFired) {
   3153  bool isAllowed = !NavigationBlockedByPrinting(aDisplayPrintErrorDialog) &&
   3154                   (!aCheckIfUnloadFired || !mFiredUnloadEvent);
   3155  if (!isAllowed) {
   3156    return false;
   3157  }
   3158  if (!mDocumentViewer) {
   3159    return true;
   3160  }
   3161  bool firingBeforeUnload;
   3162  mDocumentViewer->GetBeforeUnloadFiring(&firingBeforeUnload);
   3163  return !firingBeforeUnload;
   3164 }
   3165 
   3166 //*****************************************************************************
   3167 // nsDocShell::nsIWebNavigation
   3168 //*****************************************************************************
   3169 
   3170 NS_IMETHODIMP
   3171 nsDocShell::GetCanGoBack(bool* aCanGoBack) {
   3172  *aCanGoBack = false;
   3173  if (!IsNavigationAllowed(false)) {
   3174    return NS_OK;  // JS may not handle returning of an error code
   3175  }
   3176  RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
   3177  if (rootSH) {
   3178    *aCanGoBack = rootSH->CanGo(
   3179        -1, StaticPrefs::browser_navigation_requireUserInteraction());
   3180    MOZ_LOG(gSHLog, LogLevel::Verbose,
   3181            ("nsDocShell %p CanGoBack()->%d", this, *aCanGoBack));
   3182 
   3183    return NS_OK;
   3184  }
   3185  return NS_ERROR_FAILURE;
   3186 }
   3187 
   3188 NS_IMETHODIMP
   3189 nsDocShell::GetCanGoBackIgnoringUserInteraction(bool* aCanGoBack) {
   3190  *aCanGoBack = false;
   3191  if (!IsNavigationAllowed(false)) {
   3192    return NS_OK;  // JS may not handle returning of an error code
   3193  }
   3194  RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
   3195  if (rootSH) {
   3196    *aCanGoBack = rootSH->CanGo(-1, false);
   3197    MOZ_LOG(gSHLog, LogLevel::Verbose,
   3198            ("nsDocShell %p CanGoBackIgnoringUserInteraction()->%d", this,
   3199             *aCanGoBack));
   3200 
   3201    return NS_OK;
   3202  }
   3203  return NS_ERROR_FAILURE;
   3204 }
   3205 
   3206 NS_IMETHODIMP
   3207 nsDocShell::GetCanGoForward(bool* aCanGoForward) {
   3208  *aCanGoForward = false;
   3209  if (!IsNavigationAllowed(false)) {
   3210    return NS_OK;  // JS may not handle returning of an error code
   3211  }
   3212  RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
   3213  if (rootSH) {
   3214    *aCanGoForward = rootSH->CanGo(
   3215        1, StaticPrefs::browser_navigation_requireUserInteraction());
   3216    MOZ_LOG(gSHLog, LogLevel::Verbose,
   3217            ("nsDocShell %p CanGoForward()->%d", this, *aCanGoForward));
   3218    return NS_OK;
   3219  }
   3220  return NS_ERROR_FAILURE;
   3221 }
   3222 
   3223 NS_IMETHODIMP
   3224 nsDocShell::GoBack(bool aRequireUserInteraction, bool aUserActivation) {
   3225  if (!IsNavigationAllowed()) {
   3226    return NS_OK;  // JS may not handle returning of an error code
   3227  }
   3228 
   3229  auto cleanupIsNavigating = MakeScopeExit([&]() { mIsNavigating = false; });
   3230  mIsNavigating = true;
   3231 
   3232  RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
   3233  NS_ENSURE_TRUE(rootSH, NS_ERROR_FAILURE);
   3234  ErrorResult rv;
   3235  rootSH->Go(-1, aRequireUserInteraction, aUserActivation, rv);
   3236  return rv.StealNSResult();
   3237 }
   3238 
   3239 NS_IMETHODIMP
   3240 nsDocShell::GoForward(bool aRequireUserInteraction, bool aUserActivation) {
   3241  if (!IsNavigationAllowed()) {
   3242    return NS_OK;  // JS may not handle returning of an error code
   3243  }
   3244 
   3245  auto cleanupIsNavigating = MakeScopeExit([&]() { mIsNavigating = false; });
   3246  mIsNavigating = true;
   3247 
   3248  RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
   3249  NS_ENSURE_TRUE(rootSH, NS_ERROR_FAILURE);
   3250  ErrorResult rv;
   3251  rootSH->Go(1, aRequireUserInteraction, aUserActivation, rv);
   3252  return rv.StealNSResult();
   3253 }
   3254 
   3255 // XXX(nika): We may want to stop exposing this API in the child process? Going
   3256 // to a specific index from multiple different processes could definitely race.
   3257 NS_IMETHODIMP
   3258 nsDocShell::GotoIndex(int32_t aIndex, bool aUserActivation) {
   3259  if (!IsNavigationAllowed()) {
   3260    return NS_OK;  // JS may not handle returning of an error code
   3261  }
   3262 
   3263  auto cleanupIsNavigating = MakeScopeExit([&]() { mIsNavigating = false; });
   3264  mIsNavigating = true;
   3265 
   3266  RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
   3267  NS_ENSURE_TRUE(rootSH, NS_ERROR_FAILURE);
   3268 
   3269  ErrorResult rv;
   3270  rootSH->GotoIndex(aIndex, aIndex - rootSH->Index(), false, aUserActivation,
   3271                    rv);
   3272  return rv.StealNSResult();
   3273 }
   3274 
   3275 nsresult nsDocShell::LoadURI(nsIURI* aURI,
   3276                             const LoadURIOptions& aLoadURIOptions) {
   3277  if (!IsNavigationAllowed()) {
   3278    return NS_OK;  // JS may not handle returning of an error code
   3279  }
   3280  RefPtr<nsDocShellLoadState> loadState;
   3281  nsresult rv = nsDocShellLoadState::CreateFromLoadURIOptions(
   3282      mBrowsingContext, aURI, aLoadURIOptions, getter_AddRefs(loadState));
   3283  MOZ_ASSERT(rv != NS_ERROR_MALFORMED_URI);
   3284  if (NS_FAILED(rv) || !loadState) {
   3285    return NS_ERROR_FAILURE;
   3286  }
   3287 
   3288  // Set the captive portal tab flag on the browsing context if requested
   3289  if (loadState->GetIsCaptivePortalTab()) {
   3290    (void)mBrowsingContext->SetIsCaptivePortalTab(true);
   3291  }
   3292 
   3293  return LoadURI(loadState, true);
   3294 }
   3295 
   3296 NS_IMETHODIMP
   3297 nsDocShell::LoadURIFromScript(nsIURI* aURI,
   3298                              JS::Handle<JS::Value> aLoadURIOptions,
   3299                              JSContext* aCx) {
   3300  // generate dictionary for aLoadURIOptions and forward call
   3301  LoadURIOptions loadURIOptions;
   3302  if (!loadURIOptions.Init(aCx, aLoadURIOptions)) {
   3303    return NS_ERROR_INVALID_ARG;
   3304  }
   3305  return LoadURI(aURI, loadURIOptions);
   3306 }
   3307 
   3308 nsresult nsDocShell::FixupAndLoadURIString(
   3309    const nsAString& aURIString, const LoadURIOptions& aLoadURIOptions) {
   3310  if (!IsNavigationAllowed()) {
   3311    return NS_OK;  // JS may not handle returning of an error code
   3312  }
   3313 
   3314  RefPtr<nsDocShellLoadState> loadState;
   3315  nsresult rv = nsDocShellLoadState::CreateFromLoadURIOptions(
   3316      mBrowsingContext, aURIString, aLoadURIOptions, getter_AddRefs(loadState));
   3317 
   3318  uint32_t loadFlags = aLoadURIOptions.mLoadFlags;
   3319  if (NS_ERROR_MALFORMED_URI == rv) {
   3320    MOZ_LOG(gSHLog, LogLevel::Debug,
   3321            ("Creating an active entry on nsDocShell %p to %s (because "
   3322             "we're showing an error page)",
   3323             this, NS_ConvertUTF16toUTF8(aURIString).get()));
   3324 
   3325    // We need to store a session history entry. We don't have a valid URI, so
   3326    // we use about:blank instead.
   3327    nsCOMPtr<nsIURI> uri;
   3328    MOZ_ALWAYS_SUCCEEDS(NS_NewURI(getter_AddRefs(uri), "about:blank"_ns));
   3329    nsCOMPtr<nsIPrincipal> triggeringPrincipal;
   3330    if (aLoadURIOptions.mTriggeringPrincipal) {
   3331      triggeringPrincipal = aLoadURIOptions.mTriggeringPrincipal;
   3332    } else {
   3333      triggeringPrincipal = nsContentUtils::GetSystemPrincipal();
   3334    }
   3335    if (mozilla::SessionHistoryInParent()) {
   3336      UniquePtr<SessionHistoryInfo> previousActiveEntry(mActiveEntry.release());
   3337      mActiveEntry = MakeUnique<SessionHistoryInfo>(
   3338          uri, triggeringPrincipal, nullptr, nullptr, nullptr,
   3339          nsLiteralCString("text/html"));
   3340      mBrowsingContext->SetActiveSessionHistoryEntry(
   3341          Nothing(), mActiveEntry.get(), previousActiveEntry.get(),
   3342          MAKE_LOAD_TYPE(LOAD_NORMAL, loadFlags),
   3343          /* aUpdatedCacheKey = */ 0);
   3344    }
   3345    if (DisplayLoadError(rv, nullptr, PromiseFlatString(aURIString).get(),
   3346                         nullptr) &&
   3347        (loadFlags & LOAD_FLAGS_ERROR_LOAD_CHANGES_RV) != 0) {
   3348      return NS_ERROR_LOAD_SHOWED_ERRORPAGE;
   3349    }
   3350  }
   3351 
   3352  if (NS_FAILED(rv) || !loadState) {
   3353    return NS_ERROR_FAILURE;
   3354  }
   3355 
   3356  // Set the captive portal tab flag on the browsing context if requested
   3357  if (loadState->GetIsCaptivePortalTab()) {
   3358    (void)mBrowsingContext->SetIsCaptivePortalTab(true);
   3359  }
   3360 
   3361  return LoadURI(loadState, true);
   3362 }
   3363 
   3364 NS_IMETHODIMP
   3365 nsDocShell::FixupAndLoadURIStringFromScript(
   3366    const nsAString& aURIString, JS::Handle<JS::Value> aLoadURIOptions,
   3367    JSContext* aCx) {
   3368  // generate dictionary for aLoadURIOptions and forward call
   3369  LoadURIOptions loadURIOptions;
   3370  if (!loadURIOptions.Init(aCx, aLoadURIOptions)) {
   3371    return NS_ERROR_INVALID_ARG;
   3372  }
   3373  return FixupAndLoadURIString(aURIString, loadURIOptions);
   3374 }
   3375 
   3376 void nsDocShell::UnblockEmbedderLoadEventForFailure(bool aFireFrameErrorEvent) {
   3377  // If we're not in a content frame, or are at a BrowsingContext tree boundary,
   3378  // such as the content-chrome boundary, don't fire the error event.
   3379  if (mBrowsingContext->IsTopContent() || mBrowsingContext->IsChrome()) {
   3380    return;
   3381  }
   3382 
   3383  // If embedder is same-process, then unblocking the load event is already
   3384  // handled by nsDocLoader. Fire the error event on our embedder element if
   3385  // requested.
   3386  //
   3387  // XXX: Bug 1440212 is looking into potentially changing this behaviour to act
   3388  // more like the remote case when in-process.
   3389  RefPtr<Element> element = mBrowsingContext->GetEmbedderElement();
   3390  if (element) {
   3391    if (aFireFrameErrorEvent) {
   3392      if (RefPtr<nsFrameLoaderOwner> flo = do_QueryObject(element)) {
   3393        if (RefPtr<nsFrameLoader> fl = flo->GetFrameLoader()) {
   3394          fl->FireErrorEvent();
   3395        }
   3396      }
   3397    }
   3398    return;
   3399  }
   3400 
   3401  // If we have a cross-process parent document, we must notify it that we no
   3402  // longer block its load event.  This is necessary for OOP sub-documents
   3403  // because error documents do not result in a call to
   3404  // SendMaybeFireEmbedderLoadEvents via any of the normal call paths.
   3405  // (Obviously, we must do this before any of the returns below.)
   3406  RefPtr<BrowserChild> browserChild = BrowserChild::GetFrom(this);
   3407  if (browserChild &&
   3408      !mBrowsingContext->GetParentWindowContext()->IsInProcess()) {
   3409    (void)browserChild->SendMaybeFireEmbedderLoadEvents(
   3410        aFireFrameErrorEvent ? EmbedderElementEventType::ErrorEvent
   3411                             : EmbedderElementEventType::NoEvent);
   3412  }
   3413 }
   3414 
   3415 NS_IMETHODIMP
   3416 nsDocShell::DisplayLoadError(nsresult aError, nsIURI* aURI,
   3417                             const char16_t* aURL, nsIChannel* aFailedChannel,
   3418                             bool* aDisplayedErrorPage) {
   3419  MOZ_LOG(gDocShellLeakLog, LogLevel::Debug,
   3420          ("DOCSHELL %p DisplayLoadError %s\n", this,
   3421           aURI ? aURI->GetSpecOrDefault().get() : ""));
   3422 
   3423  *aDisplayedErrorPage = false;
   3424  // Get prompt and string bundle services
   3425  nsCOMPtr<nsIPrompt> prompter;
   3426  nsCOMPtr<nsIStringBundle> stringBundle;
   3427  GetPromptAndStringBundle(getter_AddRefs(prompter),
   3428                           getter_AddRefs(stringBundle));
   3429 
   3430  NS_ENSURE_TRUE(stringBundle, NS_ERROR_FAILURE);
   3431  NS_ENSURE_TRUE(prompter, NS_ERROR_FAILURE);
   3432 
   3433  const char* error = nullptr;
   3434  // The key used to select the appropriate error message from the properties
   3435  // file.
   3436  const char* errorDescriptionID = nullptr;
   3437  AutoTArray<nsString, 3> formatStrs;
   3438  bool addHostPort = false;
   3439  bool isBadStsCertError = false;
   3440  nsresult rv = NS_OK;
   3441  nsAutoString messageStr;
   3442  nsAutoCString cssClass;
   3443  nsAutoCString errorPage;
   3444 
   3445  errorPage.AssignLiteral("neterror");
   3446 
   3447  // Turn the error code into a human readable error message.
   3448  if (NS_ERROR_UNKNOWN_PROTOCOL == aError) {
   3449    NS_ENSURE_ARG_POINTER(aURI);
   3450 
   3451    // Extract the schemes into a comma delimited list.
   3452    nsAutoCString scheme;
   3453    aURI->GetScheme(scheme);
   3454    CopyASCIItoUTF16(scheme, *formatStrs.AppendElement());
   3455    nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(aURI);
   3456    while (nestedURI) {
   3457      nsCOMPtr<nsIURI> tempURI;
   3458      nsresult rv2;
   3459      rv2 = nestedURI->GetInnerURI(getter_AddRefs(tempURI));
   3460      if (NS_SUCCEEDED(rv2) && tempURI) {
   3461        tempURI->GetScheme(scheme);
   3462        formatStrs[0].AppendLiteral(", ");
   3463        AppendASCIItoUTF16(scheme, formatStrs[0]);
   3464      }
   3465      nestedURI = do_QueryInterface(tempURI);
   3466    }
   3467    error = "unknownProtocolFound";
   3468  } else if (NS_ERROR_NET_EMPTY_RESPONSE == aError) {
   3469    NS_ENSURE_ARG_POINTER(aURI);
   3470    error = "httpErrorPage";
   3471  } else if (NS_ERROR_NET_ERROR_RESPONSE == aError) {
   3472    NS_ENSURE_ARG_POINTER(aURI);
   3473    error = "serverError";
   3474  } else if (NS_ERROR_FILE_NOT_FOUND == aError) {
   3475    NS_ENSURE_ARG_POINTER(aURI);
   3476    error = "fileNotFound";
   3477  } else if (NS_ERROR_FILE_ACCESS_DENIED == aError) {
   3478    NS_ENSURE_ARG_POINTER(aURI);
   3479    error = "fileAccessDenied";
   3480  } else if (NS_ERROR_UNKNOWN_HOST == aError) {
   3481    NS_ENSURE_ARG_POINTER(aURI);
   3482    // Get the host
   3483    nsAutoCString host;
   3484    nsCOMPtr<nsIURI> innermostURI = NS_GetInnermostURI(aURI);
   3485    innermostURI->GetHost(host);
   3486    CopyUTF8toUTF16(host, *formatStrs.AppendElement());
   3487    errorDescriptionID = "dnsNotFound2";
   3488    error = "dnsNotFound";
   3489  } else if (NS_ERROR_CONNECTION_REFUSED == aError ||
   3490             NS_ERROR_PROXY_BAD_GATEWAY == aError) {
   3491    NS_ENSURE_ARG_POINTER(aURI);
   3492    addHostPort = true;
   3493    error = "connectionFailure";
   3494  } else if (NS_ERROR_NET_INTERRUPT == aError) {
   3495    NS_ENSURE_ARG_POINTER(aURI);
   3496    addHostPort = true;
   3497    error = "netInterrupt";
   3498  } else if (NS_ERROR_NET_TIMEOUT == aError ||
   3499             NS_ERROR_PROXY_GATEWAY_TIMEOUT == aError ||
   3500             NS_ERROR_NET_TIMEOUT_EXTERNAL == aError) {
   3501    NS_ENSURE_ARG_POINTER(aURI);
   3502    // Get the host
   3503    nsAutoCString host;
   3504    aURI->GetHost(host);
   3505    CopyUTF8toUTF16(host, *formatStrs.AppendElement());
   3506    error = "netTimeout";
   3507  } else if (NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION == aError ||
   3508             NS_ERROR_CSP_FORM_ACTION_VIOLATION == aError) {
   3509    // CSP error
   3510    cssClass.AssignLiteral("neterror");
   3511    error = "cspBlocked";
   3512  } else if (NS_ERROR_XFO_VIOLATION == aError) {
   3513    // XFO error
   3514    cssClass.AssignLiteral("neterror");
   3515    error = "xfoBlocked";
   3516  } else if (NS_ERROR_GET_MODULE(aError) == NS_ERROR_MODULE_SECURITY) {
   3517    nsCOMPtr<nsINSSErrorsService> nsserr =
   3518        do_GetService(NS_NSS_ERRORS_SERVICE_CONTRACTID);
   3519 
   3520    uint32_t errorClass;
   3521    if (!nsserr || NS_FAILED(nsserr->GetErrorClass(aError, &errorClass))) {
   3522      errorClass = nsINSSErrorsService::ERROR_CLASS_SSL_PROTOCOL;
   3523    }
   3524 
   3525    nsCOMPtr<nsITransportSecurityInfo> tsi;
   3526    if (aFailedChannel) {
   3527      aFailedChannel->GetSecurityInfo(getter_AddRefs(tsi));
   3528    }
   3529    if (tsi) {
   3530      uint32_t securityState;
   3531      tsi->GetSecurityState(&securityState);
   3532      if (securityState & nsIWebProgressListener::STATE_USES_SSL_3) {
   3533        error = "sslv3Used";
   3534        addHostPort = true;
   3535      } else if (securityState &
   3536                 nsIWebProgressListener::STATE_USES_WEAK_CRYPTO) {
   3537        error = "weakCryptoUsed";
   3538        addHostPort = true;
   3539      }
   3540    } else {
   3541      // No channel, let's obtain the generic error message
   3542      if (nsserr) {
   3543        nsserr->GetErrorMessage(aError, messageStr);
   3544      }
   3545    }
   3546    // We don't have a message string here anymore but DisplayLoadError
   3547    // requires a non-empty messageStr.
   3548    messageStr.Truncate();
   3549    messageStr.AssignLiteral(u" ");
   3550    if (errorClass == nsINSSErrorsService::ERROR_CLASS_BAD_CERT) {
   3551      error = "nssBadCert";
   3552 
   3553      // If this is an HTTP Strict Transport Security host or a pinned host
   3554      // and the certificate is bad, don't allow overrides (RFC 6797 section
   3555      // 12.1).
   3556      bool isStsHost = false;
   3557      bool isPinnedHost = false;
   3558      OriginAttributes attrsForHSTS;
   3559      if (aFailedChannel) {
   3560        StoragePrincipalHelper::GetOriginAttributesForHSTS(aFailedChannel,
   3561                                                           attrsForHSTS);
   3562      } else {
   3563        attrsForHSTS = GetOriginAttributes();
   3564      }
   3565 
   3566      if (XRE_IsParentProcess()) {
   3567        nsCOMPtr<nsISiteSecurityService> sss =
   3568            do_GetService(NS_SSSERVICE_CONTRACTID, &rv);
   3569        NS_ENSURE_SUCCESS(rv, rv);
   3570        rv = sss->IsSecureURI(aURI, attrsForHSTS, &isStsHost);
   3571        NS_ENSURE_SUCCESS(rv, rv);
   3572      } else {
   3573        mozilla::dom::ContentChild* cc =
   3574            mozilla::dom::ContentChild::GetSingleton();
   3575        cc->SendIsSecureURI(aURI, attrsForHSTS, &isStsHost);
   3576      }
   3577      nsCOMPtr<nsIPublicKeyPinningService> pkps =
   3578          do_GetService(NS_PKPSERVICE_CONTRACTID, &rv);
   3579      NS_ENSURE_SUCCESS(rv, rv);
   3580      rv = pkps->HostHasPins(aURI, &isPinnedHost);
   3581 
   3582      if (Preferences::GetBool("browser.xul.error_pages.expert_bad_cert",
   3583                               false)) {
   3584        cssClass.AssignLiteral("expertBadCert");
   3585      }
   3586 
   3587      // HSTS/pinning takes precedence over the expert bad cert pref. We
   3588      // never want to show the "Add Exception" button for these sites.
   3589      // In the future we should differentiate between an HSTS host and a
   3590      // pinned host and display a more informative message to the user.
   3591      if (isStsHost || isPinnedHost) {
   3592        isBadStsCertError = true;
   3593        cssClass.AssignLiteral("badStsCert");
   3594      }
   3595 
   3596      errorPage.Assign("certerror");
   3597    } else {
   3598      error = "nssFailure2";
   3599    }
   3600  } else if (NS_ERROR_PHISHING_URI == aError ||
   3601             NS_ERROR_MALWARE_URI == aError ||
   3602             NS_ERROR_UNWANTED_URI == aError ||
   3603             NS_ERROR_HARMFULADDON_URI == aError ||
   3604             NS_ERROR_HARMFUL_URI == aError) {
   3605    nsAutoCString host;
   3606    aURI->GetHost(host);
   3607    CopyUTF8toUTF16(host, *formatStrs.AppendElement());
   3608 
   3609    // Malware and phishing detectors may want to use an alternate error
   3610    // page, but if the pref's not set, we'll fall back on the standard page
   3611    nsAutoCString alternateErrorPage;
   3612    nsresult rv = Preferences::GetCString("urlclassifier.alternate_error_page",
   3613                                          alternateErrorPage);
   3614    if (NS_SUCCEEDED(rv)) {
   3615      errorPage.Assign(alternateErrorPage);
   3616    }
   3617 
   3618    if (NS_ERROR_PHISHING_URI == aError) {
   3619      error = "deceptiveBlocked";
   3620    } else if (NS_ERROR_MALWARE_URI == aError) {
   3621      error = "malwareBlocked";
   3622    } else if (NS_ERROR_UNWANTED_URI == aError) {
   3623      error = "unwantedBlocked";
   3624    } else if (NS_ERROR_HARMFUL_URI == aError) {
   3625      error = "harmfulBlocked";
   3626    } else if (NS_ERROR_HARMFULADDON_URI == aError) {
   3627      error = "addonBlocked";
   3628    }
   3629 
   3630    cssClass.AssignLiteral("blacklist");
   3631  } else if (NS_ERROR_CONTENT_CRASHED == aError) {
   3632    errorPage.AssignLiteral("tabcrashed");
   3633    error = "tabcrashed";
   3634 
   3635    RefPtr<EventTarget> handler = mChromeEventHandler;
   3636    if (handler) {
   3637      nsCOMPtr<Element> element = do_QueryInterface(handler);
   3638      element->GetAttribute(u"crashedPageTitle"_ns, messageStr);
   3639    }
   3640 
   3641    // DisplayLoadError requires a non-empty messageStr to proceed and call
   3642    // LoadErrorPage. If the page doesn't have a title, we will use a blank
   3643    // space which will be trimmed and thus treated as empty by the front-end.
   3644    if (messageStr.IsEmpty()) {
   3645      messageStr.AssignLiteral(u" ");
   3646    }
   3647  } else if (NS_ERROR_FRAME_CRASHED == aError) {
   3648    errorPage.AssignLiteral("framecrashed");
   3649    error = "framecrashed";
   3650    messageStr.AssignLiteral(u" ");
   3651  } else if (NS_ERROR_BUILDID_MISMATCH == aError) {
   3652    errorPage.AssignLiteral("restartrequired");
   3653    error = "restartrequired";
   3654 
   3655    // DisplayLoadError requires a non-empty messageStr to proceed and call
   3656    // LoadErrorPage. If the page doesn't have a title, we will use a blank
   3657    // space which will be trimmed and thus treated as empty by the front-end.
   3658    if (messageStr.IsEmpty()) {
   3659      messageStr.AssignLiteral(u" ");
   3660    }
   3661  } else if (aError == NS_ERROR_RESTRICTED_CONTENT) {
   3662    errorPage.AssignLiteral("restricted");
   3663    error = "restrictedcontent";
   3664    if (messageStr.IsEmpty()) {
   3665      messageStr.AssignLiteral(u" ");
   3666    }
   3667  } else {
   3668    // Errors requiring simple formatting
   3669    bool isOnionAuthError = false;
   3670    bool isOnionError = false;
   3671    switch (aError) {
   3672      case NS_ERROR_MALFORMED_URI:
   3673        // URI is malformed
   3674        error = "malformedURI";
   3675        errorDescriptionID = "malformedURI2";
   3676        break;
   3677      case NS_ERROR_REDIRECT_LOOP:
   3678        // Doc failed to load because the server generated too many redirects
   3679        error = "redirectLoop";
   3680        break;
   3681      case NS_ERROR_UNKNOWN_SOCKET_TYPE:
   3682        // Doc failed to load because PSM is not installed
   3683        error = "unknownSocketType";
   3684        break;
   3685      case NS_ERROR_NET_RESET:
   3686        // Doc failed to load because the server kept reseting the connection
   3687        // before we could read any data from it
   3688        error = "netReset";
   3689        break;
   3690      case NS_ERROR_DOCUMENT_NOT_CACHED:
   3691        // Doc failed to load because the cache does not contain a copy of
   3692        // the document.
   3693        error = "notCached";
   3694        break;
   3695      case NS_ERROR_OFFLINE:
   3696        // Doc failed to load because we are offline.
   3697        error = "netOffline";
   3698        break;
   3699      case NS_ERROR_DOCUMENT_IS_PRINTMODE:
   3700        // Doc navigation attempted while Printing or Print Preview
   3701        error = "isprinting";
   3702        break;
   3703      case NS_ERROR_PORT_ACCESS_NOT_ALLOWED:
   3704        // Port blocked for security reasons
   3705        addHostPort = true;
   3706        error = "deniedPortAccess";
   3707        break;
   3708      case NS_ERROR_UNKNOWN_PROXY_HOST:
   3709        // Proxy hostname could not be resolved.
   3710        error = "proxyResolveFailure";
   3711        break;
   3712      case NS_ERROR_PROXY_CONNECTION_REFUSED:
   3713      case NS_ERROR_PROXY_FORBIDDEN:
   3714      case NS_ERROR_PROXY_NOT_IMPLEMENTED:
   3715      case NS_ERROR_PROXY_AUTHENTICATION_FAILED:
   3716      case NS_ERROR_PROXY_TOO_MANY_REQUESTS:
   3717        // Proxy connection was refused.
   3718        error = "proxyConnectFailure";
   3719        break;
   3720      case NS_ERROR_INVALID_CONTENT_ENCODING:
   3721        // Bad Content Encoding.
   3722        error = "contentEncodingError";
   3723        break;
   3724      case NS_ERROR_UNSAFE_CONTENT_TYPE:
   3725        // Channel refused to load from an unrecognized content type.
   3726        error = "unsafeContentType";
   3727        break;
   3728      case NS_ERROR_CORRUPTED_CONTENT:
   3729        // Broken Content Detected. e.g. Content-MD5 check failure.
   3730        error = "corruptedContentErrorv2";
   3731        break;
   3732      case NS_ERROR_INTERCEPTION_FAILED:
   3733        // ServiceWorker intercepted request, but something went wrong.
   3734        error = "corruptedContentErrorv2";
   3735        break;
   3736      case NS_ERROR_NET_INADEQUATE_SECURITY:
   3737        // Server negotiated bad TLS for HTTP/2.
   3738        error = "inadequateSecurityError";
   3739        addHostPort = true;
   3740        break;
   3741      case NS_ERROR_BLOCKED_BY_POLICY:
   3742        // Page blocked by policy
   3743        error = "blockedByPolicy";
   3744        break;
   3745      case NS_ERROR_DOM_COOP_FAILED:
   3746        error = "blockedByCOOP";
   3747        errorDescriptionID = "blockedByCORP";
   3748        break;
   3749      case NS_ERROR_DOM_COEP_FAILED:
   3750        error = "blockedByCOEP";
   3751        errorDescriptionID = "blockedByCORP";
   3752        break;
   3753      case NS_ERROR_DOM_INVALID_HEADER_VALUE:
   3754        error = "invalidHeaderValue";
   3755        break;
   3756      case NS_ERROR_NET_HTTP2_SENT_GOAWAY:
   3757      case NS_ERROR_NET_HTTP3_PROTOCOL_ERROR:
   3758        // HTTP/2 or HTTP/3 stack detected a protocol error
   3759        error = "networkProtocolError";
   3760        break;
   3761      case NS_ERROR_BASIC_HTTP_AUTH_DISABLED:
   3762        error = "basicHttpAuthDisabled";
   3763        break;
   3764      case NS_ERROR_TOR_ONION_SVC_NOT_FOUND:
   3765        error = "onionServices.descNotFound";
   3766        isOnionError = true;
   3767        break;
   3768      case NS_ERROR_TOR_ONION_SVC_IS_INVALID:
   3769        error = "onionServices.descInvalid";
   3770        isOnionError = true;
   3771        break;
   3772      case NS_ERROR_TOR_ONION_SVC_INTRO_FAILED:
   3773        error = "onionServices.introFailed";
   3774        isOnionError = true;
   3775        break;
   3776      case NS_ERROR_TOR_ONION_SVC_REND_FAILED:
   3777        error = "onionServices.rendezvousFailed";
   3778        isOnionError = true;
   3779        break;
   3780      case NS_ERROR_TOR_ONION_SVC_MISSING_CLIENT_AUTH:
   3781        error = "onionServices.clientAuthMissing";
   3782        isOnionError = true;
   3783        isOnionAuthError = true;
   3784        break;
   3785      case NS_ERROR_TOR_ONION_SVC_BAD_CLIENT_AUTH:
   3786        error = "onionServices.clientAuthIncorrect";
   3787        isOnionError = true;
   3788        isOnionAuthError = true;
   3789        break;
   3790      case NS_ERROR_TOR_ONION_SVC_BAD_ADDRESS:
   3791        error = "onionServices.badAddress";
   3792        isOnionError = true;
   3793        break;
   3794      case NS_ERROR_TOR_ONION_SVC_INTRO_TIMEDOUT:
   3795        error = "onionServices.introTimedOut";
   3796        isOnionError = true;
   3797        break;
   3798      default:
   3799        break;
   3800    }
   3801 
   3802    // The presence of aFailedChannel indicates that we arrived here due to a
   3803    // failed connection attempt. Note that we will arrive here a second time
   3804    // if the user cancels the Tor client auth prompt, but in that case we
   3805    // will not have a failed channel and therefore we will not prompt again.
   3806    if (isOnionAuthError && aFailedChannel) {
   3807      // Display about:neterror with a style emulating about:blank while the
   3808      // Tor client auth prompt is open. Do not use about:blank directly: it
   3809      // will mess with the failed channel information persistence!
   3810      cssClass.AssignLiteral("onionAuthPrompt");
   3811    }
   3812    if (isOnionError) {
   3813      // DisplayLoadError requires a non-empty messageStr to proceed and call
   3814      // LoadErrorPage. We use a blank space.
   3815      if (messageStr.IsEmpty()) {
   3816        messageStr.AssignLiteral(u" ");
   3817      }
   3818    }
   3819  }
   3820 
   3821  nsresult delegateErrorCode = aError;
   3822  // If the HTTPS-Only Mode upgraded this request and the upgrade might have
   3823  // caused this error, we replace the error-page with about:httpsonlyerror
   3824  if (nsHTTPSOnlyUtils::CouldBeHttpsOnlyError(aFailedChannel, aError)) {
   3825    errorPage.AssignLiteral("httpsonlyerror");
   3826    delegateErrorCode = NS_ERROR_HTTPS_ONLY;
   3827  } else if (isBadStsCertError) {
   3828    delegateErrorCode = NS_ERROR_BAD_HSTS_CERT;
   3829  }
   3830 
   3831  if (nsCOMPtr<nsILoadURIDelegate> loadURIDelegate = GetLoadURIDelegate()) {
   3832    nsCOMPtr<nsIURI> errorPageURI;
   3833    rv = loadURIDelegate->HandleLoadError(
   3834        aURI, delegateErrorCode, NS_ERROR_GET_MODULE(delegateErrorCode),
   3835        getter_AddRefs(errorPageURI));
   3836    // If the docshell is going away there's no point in showing an error page.
   3837    if (NS_FAILED(rv) || mIsBeingDestroyed) {
   3838      *aDisplayedErrorPage = false;
   3839      return NS_OK;
   3840    }
   3841 
   3842    if (errorPageURI) {
   3843      *aDisplayedErrorPage =
   3844          NS_SUCCEEDED(LoadErrorPage(errorPageURI, aURI, aFailedChannel));
   3845      return NS_OK;
   3846    }
   3847  }
   3848 
   3849  // Test if the error should be displayed
   3850  if (!error) {
   3851    return NS_OK;
   3852  }
   3853 
   3854  if (!errorDescriptionID) {
   3855    errorDescriptionID = error;
   3856  }
   3857 
   3858  glean::page::load_error
   3859      .Get(IsSubframe() ? "frame"_ns : "top"_ns,
   3860           mozilla::dom::LoadErrorToTelemetryLabel(aError))
   3861      .Add();
   3862 
   3863  // Test if the error needs to be formatted
   3864  if (!messageStr.IsEmpty()) {
   3865    // already obtained message
   3866  } else {
   3867    if (addHostPort) {
   3868      // Build up the host:port string.
   3869      nsAutoCString hostport;
   3870      if (aURI) {
   3871        aURI->GetHostPort(hostport);
   3872      } else {
   3873        hostport.Assign('?');
   3874      }
   3875      CopyUTF8toUTF16(hostport, *formatStrs.AppendElement());
   3876    }
   3877 
   3878    nsAutoCString spec;
   3879    rv = NS_ERROR_NOT_AVAILABLE;
   3880    auto& nextFormatStr = *formatStrs.AppendElement();
   3881    if (aURI) {
   3882      // displaying "file://" is aesthetically unpleasing and could even be
   3883      // confusing to the user
   3884      if (aURI->SchemeIs("file")) {
   3885        aURI->GetPathQueryRef(spec);
   3886      } else {
   3887        aURI->GetSpec(spec);
   3888      }
   3889 
   3890      nsCOMPtr<nsITextToSubURI> textToSubURI(
   3891          do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv));
   3892      if (NS_SUCCEEDED(rv)) {
   3893        rv = textToSubURI->UnEscapeURIForUI(spec, nextFormatStr);
   3894      }
   3895    } else {
   3896      spec.Assign('?');
   3897    }
   3898    if (NS_FAILED(rv)) {
   3899      CopyUTF8toUTF16(spec, nextFormatStr);
   3900    }
   3901    rv = NS_OK;
   3902 
   3903    nsAutoString str;
   3904    rv =
   3905        stringBundle->FormatStringFromName(errorDescriptionID, formatStrs, str);
   3906    NS_ENSURE_SUCCESS(rv, rv);
   3907    messageStr.Assign(str);
   3908  }
   3909 
   3910  // Display the error as a page or an alert prompt
   3911  NS_ENSURE_FALSE(messageStr.IsEmpty(), NS_ERROR_FAILURE);
   3912 
   3913  if ((NS_ERROR_NET_INTERRUPT == aError || NS_ERROR_NET_RESET == aError) &&
   3914      aURI->SchemeIs("https")) {
   3915    // Maybe TLS intolerant. Treat this as an SSL error.
   3916    error = "nssFailure2";
   3917  }
   3918 
   3919  if (mBrowsingContext->GetUseErrorPages()) {
   3920    // Display an error page
   3921    nsresult loadedPage =
   3922        LoadErrorPage(aURI, aURL, errorPage.get(), error, messageStr.get(),
   3923                      cssClass.get(), aFailedChannel);
   3924    *aDisplayedErrorPage = NS_SUCCEEDED(loadedPage);
   3925  } else {
   3926    // The prompter reqires that our private window has a document (or it
   3927    // asserts). Satisfy that assertion now since GetDoc will force
   3928    // creation of one if it hasn't already been created.
   3929    if (mScriptGlobal) {
   3930      (void)mScriptGlobal->GetDoc();
   3931    }
   3932 
   3933    // Display a message box
   3934    prompter->Alert(nullptr, messageStr.get());
   3935  }
   3936 
   3937  return NS_OK;
   3938 }
   3939 
   3940 #define PREF_SAFEBROWSING_ALLOWOVERRIDE "browser.safebrowsing.allowOverride"
   3941 
   3942 nsresult nsDocShell::LoadErrorPage(nsIURI* aURI, const char16_t* aURL,
   3943                                   const char* aErrorPage,
   3944                                   const char* aErrorType,
   3945                                   const char16_t* aDescription,
   3946                                   const char* aCSSClass,
   3947                                   nsIChannel* aFailedChannel) {
   3948  if (mIsBeingDestroyed) {
   3949    return NS_ERROR_NOT_AVAILABLE;
   3950  }
   3951 
   3952 #if defined(DEBUG)
   3953  if (MOZ_LOG_TEST(gDocShellLog, LogLevel::Debug)) {
   3954    nsAutoCString chanName;
   3955    if (aFailedChannel) {
   3956      aFailedChannel->GetName(chanName);
   3957    } else {
   3958      chanName.AssignLiteral("<no channel>");
   3959    }
   3960 
   3961    MOZ_LOG(gDocShellLog, LogLevel::Debug,
   3962            ("nsDocShell[%p]::LoadErrorPage(\"%s\", \"%s\", {...}, [%s])\n",
   3963             this, aURI ? aURI->GetSpecOrDefault().get() : "",
   3964             NS_ConvertUTF16toUTF8(aURL).get(), chanName.get()));
   3965  }
   3966 #endif
   3967 
   3968  nsAutoCString url;
   3969  if (aURI) {
   3970    nsresult rv = aURI->GetSpec(url);
   3971    NS_ENSURE_SUCCESS(rv, rv);
   3972  } else if (aURL) {
   3973    CopyUTF16toUTF8(MakeStringSpan(aURL), url);
   3974  } else {
   3975    return NS_ERROR_INVALID_POINTER;
   3976  }
   3977 
   3978  // Create a URL to pass all the error information through to the page.
   3979 
   3980 #undef SAFE_ESCAPE
   3981 #define SAFE_ESCAPE(output, input, params)             \
   3982  if (NS_WARN_IF(!NS_Escape(input, output, params))) { \
   3983    return NS_ERROR_OUT_OF_MEMORY;                     \
   3984  }
   3985 
   3986  nsCString escapedUrl, escapedError, escapedDescription, escapedCSSClass;
   3987  SAFE_ESCAPE(escapedUrl, url, url_Path);
   3988  SAFE_ESCAPE(escapedError, nsDependentCString(aErrorType), url_Path);
   3989  SAFE_ESCAPE(escapedDescription, NS_ConvertUTF16toUTF8(aDescription),
   3990              url_Path);
   3991  if (aCSSClass) {
   3992    nsCString cssClass(aCSSClass);
   3993    SAFE_ESCAPE(escapedCSSClass, cssClass, url_Path);
   3994  }
   3995  nsCString errorPageUrl("about:");
   3996  errorPageUrl.AppendASCII(aErrorPage);
   3997  errorPageUrl.AppendLiteral("?e=");
   3998 
   3999  errorPageUrl.AppendASCII(escapedError.get());
   4000  errorPageUrl.AppendLiteral("&u=");
   4001  errorPageUrl.AppendASCII(escapedUrl.get());
   4002  if ((strcmp(aErrorPage, "blocked") == 0) &&
   4003      Preferences::GetBool(PREF_SAFEBROWSING_ALLOWOVERRIDE, true)) {
   4004    errorPageUrl.AppendLiteral("&o=1");
   4005  }
   4006  if (!escapedCSSClass.IsEmpty()) {
   4007    errorPageUrl.AppendLiteral("&s=");
   4008    errorPageUrl.AppendASCII(escapedCSSClass.get());
   4009  }
   4010  errorPageUrl.AppendLiteral("&c=UTF-8");
   4011 
   4012  nsCOMPtr<nsICaptivePortalService> cps = do_GetService(NS_CAPTIVEPORTAL_CID);
   4013  int32_t cpsState;
   4014  if (cps && NS_SUCCEEDED(cps->GetState(&cpsState)) &&
   4015      cpsState == nsICaptivePortalService::LOCKED_PORTAL) {
   4016    errorPageUrl.AppendLiteral("&captive=true");
   4017  }
   4018 
   4019  errorPageUrl.AppendLiteral("&d=");
   4020  errorPageUrl.AppendASCII(escapedDescription.get());
   4021 
   4022  nsCOMPtr<nsIWritablePropertyBag2> props(do_QueryInterface(aFailedChannel));
   4023  if (props) {
   4024    nsAutoCString addonName;
   4025    props->GetPropertyAsACString(u"blockedExtension"_ns, addonName);
   4026 
   4027    nsCString escapedAddonName;
   4028    SAFE_ESCAPE(escapedAddonName, addonName, url_Path);
   4029 
   4030    errorPageUrl.AppendLiteral("&a=");
   4031    errorPageUrl.AppendASCII(escapedAddonName.get());
   4032  }
   4033 
   4034  nsCOMPtr<nsIURI> errorPageURI;
   4035  nsresult rv = NS_NewURI(getter_AddRefs(errorPageURI), errorPageUrl);
   4036  NS_ENSURE_SUCCESS(rv, rv);
   4037 
   4038  return LoadErrorPage(errorPageURI, aURI, aFailedChannel);
   4039 }
   4040 
   4041 nsresult nsDocShell::LoadErrorPage(nsIURI* aErrorURI, nsIURI* aFailedURI,
   4042                                   nsIChannel* aFailedChannel) {
   4043  mFailedChannel = aFailedChannel;
   4044  mFailedURI = aFailedURI;
   4045  mFailedLoadType = mLoadType;
   4046 
   4047  if (mLSHE) {
   4048    // Abandon mLSHE's BFCache entry and create a new one.  This way, if
   4049    // we go back or forward to another SHEntry with the same doc
   4050    // identifier, the error page won't persist.
   4051    mLSHE->AbandonBFCacheEntry();
   4052  }
   4053 
   4054  RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(aErrorURI);
   4055  loadState->SetTriggeringPrincipal(nsContentUtils::GetSystemPrincipal());
   4056  if (mBrowsingContext) {
   4057    loadState->SetTriggeringSandboxFlags(mBrowsingContext->GetSandboxFlags());
   4058    loadState->SetTriggeringWindowId(
   4059        mBrowsingContext->GetCurrentInnerWindowId());
   4060    nsPIDOMWindowInner* innerWin = mScriptGlobal->GetCurrentInnerWindow();
   4061    if (innerWin) {
   4062      loadState->SetTriggeringStorageAccess(innerWin->UsingStorageAccess());
   4063    }
   4064  }
   4065  loadState->SetLoadType(LOAD_ERROR_PAGE);
   4066  loadState->SetFirstParty(true);
   4067  loadState->SetSourceBrowsingContext(mBrowsingContext);
   4068  if (mozilla::SessionHistoryInParent() && mLoadingEntry) {
   4069    // We keep the loading entry for the load that failed here. If the user
   4070    // reloads we want to try to reload the original load, not the error page.
   4071    loadState->SetLoadingSessionHistoryInfo(
   4072        MakeUnique<LoadingSessionHistoryInfo>(*mLoadingEntry));
   4073  }
   4074 
   4075  // Prevent initial about:blank handling, as it's likely irrelevant and
   4076  // keeps us from needing to change GeckoView / NavigationDelegateTest.
   4077  // It also makes the load more consistent with non-about-blank cases.
   4078  loadState->ProhibitInitialAboutBlankHandling();
   4079 
   4080  return InternalLoad(loadState);
   4081 }
   4082 
   4083 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODIMP
   4084 nsDocShell::Reload(uint32_t aReloadFlags) {
   4085  return ReloadNavigable(Nothing(), aReloadFlags, nullptr,
   4086                         UserNavigationInvolvement::BrowserUI);
   4087 }
   4088 
   4089 // https://html.spec.whatwg.org/#reload
   4090 // To reload a navigable navigable given an optional serialized state-or-null
   4091 // navigationAPIState (default null) and an optional user navigation
   4092 // involvement userInvolvement (default "none"):
   4093 nsresult nsDocShell::ReloadNavigable(
   4094    mozilla::Maybe<NotNull<JSContext*>> aCx, uint32_t aReloadFlags,
   4095    nsIStructuredCloneContainer* aNavigationAPIState,
   4096    UserNavigationInvolvement aUserInvolvement,
   4097    NavigationAPIMethodTracker* aNavigationAPIMethodTracker) {
   4098  if (!IsNavigationAllowed()) {
   4099    return NS_OK;  // JS may not handle returning of an error code
   4100  }
   4101 
   4102  NS_ASSERTION(((aReloadFlags & INTERNAL_LOAD_FLAGS_LOADURI_SETUP_FLAGS) == 0),
   4103               "Reload command not updated to use load flags!");
   4104  NS_ASSERTION((aReloadFlags & EXTRA_LOAD_FLAGS) == 0,
   4105               "Don't pass these flags to Reload");
   4106 
   4107  uint32_t loadType = MAKE_LOAD_TYPE(LOAD_RELOAD_NORMAL, aReloadFlags);
   4108  NS_ENSURE_TRUE(IsValidLoadType(loadType), NS_ERROR_INVALID_ARG);
   4109  NS_ENSURE_TRUE(
   4110      aUserInvolvement == UserNavigationInvolvement::BrowserUI || aCx,
   4111      NS_ERROR_INVALID_ARG);
   4112 
   4113  RefPtr<nsDocShell> docShell(this);
   4114 
   4115  // 1. If userInvolvement is not "browser UI", then:
   4116  if (aUserInvolvement != UserNavigationInvolvement::BrowserUI) {
   4117    // 1.1 Let navigation be navigable's active window's navigation API.
   4118    nsPIDOMWindowOuter* windowOuter = GetWindow();
   4119    MOZ_DIAGNOSTIC_ASSERT(windowOuter);
   4120    nsPIDOMWindowInner* windowInner = windowOuter->GetCurrentInnerWindow();
   4121    MOZ_DIAGNOSTIC_ASSERT(windowInner);
   4122    RefPtr navigation = windowInner->Navigation();
   4123 
   4124    // 1.2 Let destinationNavigationAPIState be navigable's active session
   4125    //     history entry's navigation API state.
   4126    // 1.3 If navigationAPIState is not null, then set
   4127    //     destinationNavigationAPIState to navigationAPIState.
   4128    RefPtr<nsIStructuredCloneContainer> destinationNavigationAPIState =
   4129        aNavigationAPIState;
   4130    if (!destinationNavigationAPIState) {
   4131      destinationNavigationAPIState =
   4132          mActiveEntry ? mActiveEntry->GetNavigationAPIState() : nullptr;
   4133    }
   4134 
   4135    // 1.4 Let continue be the result of firing a push/replace/reload navigate
   4136    //     event at navigation with navigationType set to "reload",
   4137    //     isSameDocument set to false, userInvolvement set to userInvolvement,
   4138    //     destinationURL set to navigable's active session history entry's URL,
   4139    //     and navigationAPIState set to destinationNavigationAPIState.
   4140    // 1.5 If continue is false, then return.
   4141    RefPtr destinationURL = mActiveEntry ? mActiveEntry->GetURI() : nullptr;
   4142    if (navigation &&
   4143        !navigation->FirePushReplaceReloadNavigateEvent(
   4144            *aCx, NavigationType::Reload, destinationURL,
   4145            /* aIsSameDocument */ false, Some(aUserInvolvement),
   4146            /* aSourceElement*/ nullptr, /* aFormDataEntryList */ nullptr,
   4147            destinationNavigationAPIState,
   4148            /* aClassiCHistoryAPIState */ nullptr,
   4149            aNavigationAPIMethodTracker)) {
   4150      return NS_OK;
   4151    }
   4152  }
   4153 
   4154  // The following steps are implemented by the remainder of ReloadNavigable.
   4155 
   4156  // Send notifications to the HistoryListener if any, about the impending
   4157  // reload
   4158  RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
   4159  if (mozilla::SessionHistoryInParent()) {
   4160    MOZ_LOG(gSHLog, LogLevel::Debug, ("nsDocShell %p Reload", this));
   4161    bool forceReload = IsForceReloadType(loadType);
   4162    if (!XRE_IsParentProcess()) {
   4163      ++mPendingReloadCount;
   4164      nsCOMPtr<nsIDocumentViewer> viewer(mDocumentViewer);
   4165      NS_ENSURE_STATE(viewer);
   4166 
   4167      bool okToUnload = true;
   4168      MOZ_TRY(viewer->PermitUnload(&okToUnload));
   4169      if (mIsBeingDestroyed) {
   4170        // unload handler destroyed this docshell.
   4171        return NS_ERROR_NOT_AVAILABLE;
   4172      }
   4173      if (!okToUnload) {
   4174        return NS_OK;
   4175      }
   4176 
   4177      RefPtr<Document> doc(GetDocument());
   4178      RefPtr<BrowsingContext> browsingContext(mBrowsingContext);
   4179      nsCOMPtr<nsIURI> currentURI(mCurrentURI);
   4180      nsCOMPtr<nsIReferrerInfo> referrerInfo(mReferrerInfo);
   4181      RefPtr<StopDetector> stopDetector = new StopDetector();
   4182      nsCOMPtr<nsILoadGroup> loadGroup;
   4183      GetLoadGroup(getter_AddRefs(loadGroup));
   4184      if (loadGroup) {
   4185        // loadGroup may be null in theory. In that case stopDetector just
   4186        // doesn't do anything.
   4187        loadGroup->AddRequest(stopDetector, nullptr);
   4188      }
   4189 
   4190      ContentChild::GetSingleton()->SendNotifyOnHistoryReload(
   4191          mBrowsingContext, forceReload,
   4192          [docShell, doc, loadType, browsingContext, currentURI, referrerInfo,
   4193           loadGroup, stopDetector](
   4194              std::tuple<bool, Maybe<NotNull<RefPtr<nsDocShellLoadState>>>,
   4195                         Maybe<bool>>&& aResult) {
   4196            auto scopeExit = MakeScopeExit([loadGroup, stopDetector]() {
   4197              if (loadGroup) {
   4198                loadGroup->RemoveRequest(stopDetector, nullptr, NS_OK);
   4199              }
   4200            });
   4201 
   4202            // Decrease mPendingReloadCount before any other early returns!
   4203            if (--(docShell->mPendingReloadCount) > 0) {
   4204              return;
   4205            }
   4206 
   4207            if (stopDetector->Canceled()) {
   4208              return;
   4209            }
   4210            bool canReload;
   4211            Maybe<NotNull<RefPtr<nsDocShellLoadState>>> loadState;
   4212            Maybe<bool> reloadingActiveEntry;
   4213 
   4214            std::tie(canReload, loadState, reloadingActiveEntry) = aResult;
   4215 
   4216            if (!canReload) {
   4217              return;
   4218            }
   4219 
   4220            if (loadState.isSome()) {
   4221              MOZ_LOG(
   4222                  gSHLog, LogLevel::Debug,
   4223                  ("nsDocShell %p Reload - LoadHistoryEntry", docShell.get()));
   4224              loadState.ref()->SetNotifiedBeforeUnloadListeners(true);
   4225              docShell->LoadHistoryEntry(loadState.ref(), loadType,
   4226                                         reloadingActiveEntry.ref());
   4227            } else {
   4228              MOZ_LOG(gSHLog, LogLevel::Debug,
   4229                      ("nsDocShell %p ReloadDocument", docShell.get()));
   4230              ReloadDocument(docShell, doc, loadType, browsingContext,
   4231                             currentURI, referrerInfo,
   4232                             /* aNotifiedBeforeUnloadListeners */ true);
   4233            }
   4234          },
   4235          [](mozilla::ipc::ResponseRejectReason) {});
   4236    } else {
   4237      // Parent process
   4238      bool canReload = false;
   4239      Maybe<NotNull<RefPtr<nsDocShellLoadState>>> loadState;
   4240      Maybe<bool> reloadingActiveEntry;
   4241      if (!mBrowsingContext->IsDiscarded()) {
   4242        mBrowsingContext->Canonical()->NotifyOnHistoryReload(
   4243            forceReload, canReload, loadState, reloadingActiveEntry);
   4244      }
   4245      if (canReload) {
   4246        if (loadState.isSome()) {
   4247          MOZ_LOG(gSHLog, LogLevel::Debug,
   4248                  ("nsDocShell %p Reload - LoadHistoryEntry", this));
   4249          LoadHistoryEntry(loadState.ref(), loadType,
   4250                           reloadingActiveEntry.ref());
   4251        } else {
   4252          MOZ_LOG(gSHLog, LogLevel::Debug,
   4253                  ("nsDocShell %p ReloadDocument", this));
   4254          RefPtr<Document> doc = GetDocument();
   4255          RefPtr<BrowsingContext> bc = mBrowsingContext;
   4256          nsCOMPtr<nsIURI> currentURI = mCurrentURI;
   4257          nsCOMPtr<nsIReferrerInfo> referrerInfo = mReferrerInfo;
   4258          ReloadDocument(this, doc, loadType, bc, currentURI, referrerInfo);
   4259        }
   4260      }
   4261    }
   4262    return NS_OK;
   4263  }
   4264 
   4265  bool canReload = true;
   4266  if (rootSH) {
   4267    rootSH->LegacySHistory()->NotifyOnHistoryReload(&canReload);
   4268  }
   4269 
   4270  if (!canReload) {
   4271    return NS_OK;
   4272  }
   4273 
   4274  /* If you change this part of code, make sure bug 45297 does not re-occur */
   4275  if (mOSHE) {
   4276    nsCOMPtr<nsISHEntry> oshe = mOSHE;
   4277    return LoadHistoryEntry(
   4278        oshe, loadType,
   4279        aReloadFlags & nsIWebNavigation::LOAD_FLAGS_USER_ACTIVATION);
   4280  }
   4281 
   4282  if (mLSHE) {  // In case a reload happened before the current load is done
   4283    nsCOMPtr<nsISHEntry> lshe = mLSHE;
   4284    return LoadHistoryEntry(
   4285        lshe, loadType,
   4286        aReloadFlags & nsIWebNavigation::LOAD_FLAGS_USER_ACTIVATION);
   4287  }
   4288 
   4289  RefPtr<Document> doc = GetDocument();
   4290  RefPtr<BrowsingContext> bc = mBrowsingContext;
   4291  nsCOMPtr<nsIURI> currentURI = mCurrentURI;
   4292  nsCOMPtr<nsIReferrerInfo> referrerInfo = mReferrerInfo;
   4293  return ReloadDocument(this, doc, loadType, bc, currentURI, referrerInfo);
   4294 }
   4295 
   4296 void nsDocShell::DisplayRestrictedContentError() {
   4297  bool didDisplayLoadError = false;
   4298  RefPtr<mozilla::dom::Document> doc = GetDocument();
   4299  if (!doc) {
   4300    return;
   4301  }
   4302  doc->TerminateParserAndDisableScripts();
   4303  DisplayLoadError(NS_ERROR_RESTRICTED_CONTENT, doc->GetDocumentURI(), nullptr,
   4304                   nullptr, &didDisplayLoadError);
   4305 }
   4306 
   4307 /* static */
   4308 nsresult nsDocShell::ReloadDocument(nsDocShell* aDocShell, Document* aDocument,
   4309                                    uint32_t aLoadType,
   4310                                    BrowsingContext* aBrowsingContext,
   4311                                    nsIURI* aCurrentURI,
   4312                                    nsIReferrerInfo* aReferrerInfo,
   4313                                    bool aNotifiedBeforeUnloadListeners) {
   4314  if (!aDocument) {
   4315    return NS_OK;
   4316  }
   4317 
   4318  // Do not inherit owner from document
   4319  uint32_t flags = INTERNAL_LOAD_FLAGS_NONE;
   4320  nsAutoString srcdoc;
   4321  nsIURI* baseURI = nullptr;
   4322  nsCOMPtr<nsIURI> originalURI;
   4323  nsCOMPtr<nsIURI> resultPrincipalURI;
   4324  bool loadReplace = false;
   4325 
   4326  nsIPrincipal* triggeringPrincipal = aDocument->NodePrincipal();
   4327  nsCOMPtr<nsIPolicyContainer> policyContainer =
   4328      aDocument->GetPolicyContainer();
   4329  uint32_t triggeringSandboxFlags = aDocument->GetSandboxFlags();
   4330  uint64_t triggeringWindowId = aDocument->InnerWindowID();
   4331  bool triggeringStorageAccess = aDocument->UsingStorageAccess();
   4332  net::ClassificationFlags triggeringClassificationFlags =
   4333      aDocument->GetScriptTrackingFlags();
   4334 
   4335  nsAutoString contentTypeHint;
   4336  aDocument->GetContentType(contentTypeHint);
   4337 
   4338  if (aDocument->IsSrcdocDocument()) {
   4339    aDocument->GetSrcdocData(srcdoc);
   4340    flags |= INTERNAL_LOAD_FLAGS_IS_SRCDOC;
   4341    baseURI = aDocument->GetBaseURI();
   4342  } else {
   4343    srcdoc = VoidString();
   4344  }
   4345  nsCOMPtr<nsIChannel> chan = aDocument->GetChannel();
   4346  if (chan) {
   4347    uint32_t loadFlags;
   4348    chan->GetLoadFlags(&loadFlags);
   4349    loadReplace = loadFlags & nsIChannel::LOAD_REPLACE;
   4350    nsCOMPtr<nsIHttpChannel> httpChan(do_QueryInterface(chan));
   4351    if (httpChan) {
   4352      httpChan->GetOriginalURI(getter_AddRefs(originalURI));
   4353    }
   4354 
   4355    nsCOMPtr<nsILoadInfo> loadInfo = chan->LoadInfo();
   4356    loadInfo->GetResultPrincipalURI(getter_AddRefs(resultPrincipalURI));
   4357  }
   4358 
   4359  if (!triggeringPrincipal) {
   4360    MOZ_ASSERT(false, "Reload needs a valid triggeringPrincipal");
   4361    return NS_ERROR_FAILURE;
   4362  }
   4363 
   4364  // Stack variables to ensure changes to the member variables don't affect to
   4365  // the call.
   4366  nsCOMPtr<nsIURI> currentURI = aCurrentURI;
   4367 
   4368  // Reload always rewrites result principal URI.
   4369  Maybe<nsCOMPtr<nsIURI>> emplacedResultPrincipalURI;
   4370  emplacedResultPrincipalURI.emplace(std::move(resultPrincipalURI));
   4371 
   4372  RefPtr<WindowContext> context = aBrowsingContext->GetCurrentWindowContext();
   4373  RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(currentURI);
   4374  loadState->SetReferrerInfo(aReferrerInfo);
   4375  loadState->SetOriginalURI(originalURI);
   4376  loadState->SetMaybeResultPrincipalURI(emplacedResultPrincipalURI);
   4377  loadState->SetLoadReplace(loadReplace);
   4378  loadState->SetTriggeringPrincipal(triggeringPrincipal);
   4379  loadState->SetTriggeringSandboxFlags(triggeringSandboxFlags);
   4380  loadState->SetTriggeringWindowId(triggeringWindowId);
   4381  loadState->SetTriggeringStorageAccess(triggeringStorageAccess);
   4382  loadState->SetTriggeringClassificationFlags(triggeringClassificationFlags);
   4383  loadState->SetPrincipalToInherit(triggeringPrincipal);
   4384  loadState->SetPolicyContainer(policyContainer);
   4385  loadState->SetInternalLoadFlags(flags);
   4386  loadState->SetTypeHint(NS_ConvertUTF16toUTF8(contentTypeHint));
   4387  loadState->SetLoadType(aLoadType);
   4388  loadState->SetFirstParty(true);
   4389  loadState->SetSrcdocData(srcdoc);
   4390  loadState->SetSourceBrowsingContext(aBrowsingContext);
   4391  loadState->SetBaseURI(baseURI);
   4392  loadState->SetHasValidUserGestureActivation(
   4393      context && context->HasValidTransientUserGestureActivation());
   4394 
   4395  loadState->SetTextDirectiveUserActivation(
   4396      aDocument->ConsumeTextDirectiveUserActivation() ||
   4397      loadState->HasValidUserGestureActivation());
   4398 
   4399  loadState->SetNotifiedBeforeUnloadListeners(aNotifiedBeforeUnloadListeners);
   4400  return aDocShell->InternalLoad(loadState);
   4401 }
   4402 
   4403 // TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230)
   4404 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODIMP
   4405 nsDocShell::Stop(uint32_t aStopFlags) {
   4406  return StopInternal(aStopFlags, UnsetOngoingNavigation::Yes);
   4407 }
   4408 
   4409 nsresult nsDocShell::StopInternal(
   4410    uint32_t aStopFlags, UnsetOngoingNavigation aUnsetOngoingNavigation) {
   4411  RefPtr kungFuDeathGrip = this;
   4412  if (RefPtr<Document> doc = GetExtantDocument();
   4413      aUnsetOngoingNavigation == UnsetOngoingNavigation::Yes && doc &&
   4414      !doc->ShouldIgnoreOpens() &&
   4415      mOngoingNavigation == Some(OngoingNavigation::NavigationID)) {
   4416    SetOngoingNavigation(Nothing());
   4417  }
   4418 
   4419  // Revoke any pending event related to content viewer restoration
   4420  mRestorePresentationEvent.Revoke();
   4421 
   4422  if (mLoadType == LOAD_ERROR_PAGE) {
   4423    if (mLSHE) {
   4424      // Since error page loads never unset mLSHE, do so now
   4425      SetHistoryEntryAndUpdateBC(Some(nullptr), Some<nsISHEntry*>(mLSHE));
   4426    }
   4427    mActiveEntryIsLoadingFromSessionHistory = false;
   4428 
   4429    mFailedChannel = nullptr;
   4430    mFailedURI = nullptr;
   4431  }
   4432 
   4433  if (nsIWebNavigation::STOP_CONTENT & aStopFlags) {
   4434    // Stop the document loading and animations
   4435    if (mDocumentViewer) {
   4436      nsCOMPtr<nsIDocumentViewer> viewer = mDocumentViewer;
   4437      viewer->Stop();
   4438    }
   4439  } else if (nsIWebNavigation::STOP_NETWORK & aStopFlags) {
   4440    // Stop the document loading only
   4441    if (mDocumentViewer) {
   4442      RefPtr<Document> doc = mDocumentViewer->GetDocument();
   4443      if (doc) {
   4444        doc->StopDocumentLoad();
   4445      }
   4446    }
   4447  }
   4448 
   4449  if (nsIWebNavigation::STOP_NETWORK & aStopFlags) {
   4450    // Suspend any timers that were set for this loader.  We'll clear
   4451    // them out for good in CreateDocumentViewer.
   4452    if (mRefreshURIList) {
   4453      SuspendRefreshURIs();
   4454      mSavedRefreshURIList.swap(mRefreshURIList);
   4455      mRefreshURIList = nullptr;
   4456    }
   4457 
   4458    // XXXbz We could also pass |this| to nsIURILoader::Stop.  That will
   4459    // just call Stop() on us as an nsIDocumentLoader... We need fewer
   4460    // redundant apis!
   4461    Stop();
   4462 
   4463    // Clear out mChannelToDisconnectOnPageHide. This page won't go in the
   4464    // BFCache now, and the Stop above will have removed the DocumentChannel
   4465    // from the loadgroup.
   4466    mChannelToDisconnectOnPageHide = 0;
   4467  }
   4468 
   4469  for (auto* child : mChildList.ForwardRange()) {
   4470    nsCOMPtr<nsIWebNavigation> shellAsNav(do_QueryObject(child));
   4471    if (shellAsNav) {
   4472      shellAsNav->Stop(aStopFlags);
   4473    }
   4474  }
   4475 
   4476  return NS_OK;
   4477 }
   4478 
   4479 NS_IMETHODIMP
   4480 nsDocShell::GetDocument(Document** aDocument) {
   4481  NS_ENSURE_ARG_POINTER(aDocument);
   4482  NS_ENSURE_TRUE(VerifyDocumentViewer(), NS_ERROR_FAILURE);
   4483 
   4484  RefPtr<Document> doc = mDocumentViewer->GetDocument();
   4485  if (!doc) {
   4486    return NS_ERROR_NOT_AVAILABLE;
   4487  }
   4488 
   4489  doc.forget(aDocument);
   4490  return NS_OK;
   4491 }
   4492 
   4493 NS_IMETHODIMP
   4494 nsDocShell::GetCurrentURI(nsIURI** aURI) {
   4495  NS_ENSURE_ARG_POINTER(aURI);
   4496 
   4497  nsCOMPtr<nsIURI> uri = mCurrentURI;
   4498  uri.forget(aURI);
   4499  return NS_OK;
   4500 }
   4501 
   4502 NS_IMETHODIMP
   4503 nsDocShell::GetSessionHistoryXPCOM(nsISupports** aSessionHistory) {
   4504  NS_ENSURE_ARG_POINTER(aSessionHistory);
   4505  RefPtr<ChildSHistory> shistory = GetSessionHistory();
   4506  shistory.forget(aSessionHistory);
   4507  return NS_OK;
   4508 }
   4509 
   4510 //*****************************************************************************
   4511 // nsDocShell::nsIWebPageDescriptor
   4512 //*****************************************************************************
   4513 
   4514 NS_IMETHODIMP
   4515 nsDocShell::LoadPageAsViewSource(nsIDocShell* aOtherDocShell,
   4516                                 const nsAString& aURI) {
   4517  if (!aOtherDocShell) {
   4518    return NS_ERROR_INVALID_POINTER;
   4519  }
   4520  nsCOMPtr<nsIURI> newURI;
   4521  nsresult rv = NS_NewURI(getter_AddRefs(newURI), aURI);
   4522  if (NS_FAILED(rv)) {
   4523    return rv;
   4524  }
   4525 
   4526  RefPtr<nsDocShellLoadState> loadState;
   4527  uint32_t cacheKey;
   4528  auto* otherDocShell = nsDocShell::Cast(aOtherDocShell);
   4529  if (mozilla::SessionHistoryInParent()) {
   4530    loadState = new nsDocShellLoadState(newURI);
   4531    if (!otherDocShell->FillLoadStateFromCurrentEntry(*loadState)) {
   4532      return NS_ERROR_INVALID_POINTER;
   4533    }
   4534    cacheKey = otherDocShell->GetCacheKeyFromCurrentEntry().valueOr(0);
   4535  } else {
   4536    nsCOMPtr<nsISHEntry> entry;
   4537    bool isOriginalSHE;
   4538    otherDocShell->GetCurrentSHEntry(getter_AddRefs(entry), &isOriginalSHE);
   4539    if (!entry) {
   4540      return NS_ERROR_INVALID_POINTER;
   4541    }
   4542    rv = entry->CreateLoadInfo(getter_AddRefs(loadState));
   4543    NS_ENSURE_SUCCESS(rv, rv);
   4544    entry->GetCacheKey(&cacheKey);
   4545    loadState->SetURI(newURI);
   4546    loadState->SetSHEntry(nullptr);
   4547  }
   4548 
   4549  // We're doing a load of the page, via an API that
   4550  // is only exposed to system code.  The triggering principal for this load
   4551  // should be the system principal.
   4552  loadState->SetTriggeringPrincipal(nsContentUtils::GetSystemPrincipal());
   4553  loadState->SetOriginalURI(nullptr);
   4554  loadState->SetResultPrincipalURI(nullptr);
   4555 
   4556  // Initial about:blank handling is probably irrelevant, but newURI shouldn't
   4557  // anyway be about:blank. Otherwise we should prohibit initial about blank
   4558  // handling.
   4559  MOZ_ASSERT(!NS_IsAboutBlankAllowQueryAndFragment(newURI),
   4560             "We only expect view-source:// URIs");
   4561 
   4562  return InternalLoad(loadState, Some(cacheKey));
   4563 }
   4564 
   4565 NS_IMETHODIMP
   4566 nsDocShell::GetCurrentDescriptor(nsISupports** aPageDescriptor) {
   4567  MOZ_ASSERT(aPageDescriptor, "Null out param?");
   4568 
   4569  *aPageDescriptor = nullptr;
   4570 
   4571  nsISHEntry* src = mOSHE ? mOSHE : mLSHE;
   4572  if (src) {
   4573    nsCOMPtr<nsISHEntry> dest;
   4574 
   4575    nsresult rv = src->Clone(getter_AddRefs(dest));
   4576    if (NS_FAILED(rv)) {
   4577      return rv;
   4578    }
   4579 
   4580    // null out inappropriate cloned attributes...
   4581    dest->SetParent(nullptr);
   4582    dest->SetIsSubFrame(false);
   4583 
   4584    return CallQueryInterface(dest, aPageDescriptor);
   4585  }
   4586 
   4587  return NS_ERROR_NOT_AVAILABLE;
   4588 }
   4589 
   4590 already_AddRefed<nsIInputStream> nsDocShell::GetPostDataFromCurrentEntry()
   4591    const {
   4592  nsCOMPtr<nsIInputStream> postData;
   4593  if (mozilla::SessionHistoryInParent()) {
   4594    if (mActiveEntry) {
   4595      postData = mActiveEntry->GetPostData();
   4596    } else if (mLoadingEntry) {
   4597      postData = mLoadingEntry->mInfo.GetPostData();
   4598    }
   4599  } else {
   4600    if (mOSHE) {
   4601      postData = mOSHE->GetPostData();
   4602    } else if (mLSHE) {
   4603      postData = mLSHE->GetPostData();
   4604    }
   4605  }
   4606 
   4607  return postData.forget();
   4608 }
   4609 
   4610 Maybe<uint32_t> nsDocShell::GetCacheKeyFromCurrentEntry() const {
   4611  if (mozilla::SessionHistoryInParent()) {
   4612    if (mActiveEntry) {
   4613      return Some(mActiveEntry->GetCacheKey());
   4614    }
   4615 
   4616    if (mLoadingEntry) {
   4617      return Some(mLoadingEntry->mInfo.GetCacheKey());
   4618    }
   4619  } else {
   4620    if (mOSHE) {
   4621      return Some(mOSHE->GetCacheKey());
   4622    }
   4623 
   4624    if (mLSHE) {
   4625      return Some(mLSHE->GetCacheKey());
   4626    }
   4627  }
   4628 
   4629  return Nothing();
   4630 }
   4631 
   4632 bool nsDocShell::FillLoadStateFromCurrentEntry(
   4633    nsDocShellLoadState& aLoadState) {
   4634  if (mLoadingEntry) {
   4635    mLoadingEntry->mInfo.FillLoadInfo(aLoadState);
   4636    return true;
   4637  }
   4638  if (mActiveEntry) {
   4639    mActiveEntry->FillLoadInfo(aLoadState);
   4640    return true;
   4641  }
   4642  return false;
   4643 }
   4644 
   4645 //*****************************************************************************
   4646 // nsDocShell::nsIBaseWindow
   4647 //*****************************************************************************
   4648 
   4649 NS_IMETHODIMP
   4650 nsDocShell::Destroy() {
   4651  // XXX: We allow this function to be called just once.  If you are going to
   4652  // reset new variables in this function, please make sure the variables will
   4653  // never be re-initialized.  Adding assertions to check |mIsBeingDestroyed|
   4654  // in the setter functions for the variables would be enough.
   4655  if (mIsBeingDestroyed) {
   4656    return NS_ERROR_DOCSHELL_DYING;
   4657  }
   4658 
   4659  NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome,
   4660               "Unexpected item type in docshell");
   4661 
   4662  nsCOMPtr<nsIObserverService> serv = services::GetObserverService();
   4663  if (serv) {
   4664    const char* msg = mItemType == typeContent
   4665                          ? NS_WEBNAVIGATION_DESTROY
   4666                          : NS_CHROME_WEBNAVIGATION_DESTROY;
   4667    serv->NotifyObservers(GetAsSupports(this), msg, nullptr);
   4668  }
   4669 
   4670  mIsBeingDestroyed = true;
   4671 
   4672  // Brak the cycle with the initial client, if present.
   4673  mInitialClientSource.reset();
   4674 
   4675  // Make sure to blow away our mLoadingURI just in case.  No loads
   4676  // from inside this pagehide.
   4677  mLoadingURI = nullptr;
   4678 
   4679  // Fire unload event before we blow anything away.
   4680  (void)FirePageHideNotification(true);
   4681 
   4682  // Clear pointers to any detached nsEditorData that's lying
   4683  // around in shistory entries. Breaks cycle. See bug 430921.
   4684  if (mOSHE) {
   4685    mOSHE->SetEditorData(nullptr);
   4686  }
   4687  if (mLSHE) {
   4688    mLSHE->SetEditorData(nullptr);
   4689  }
   4690 
   4691  // Note: mContentListener can be null if Init() failed and we're being
   4692  // called from the destructor.
   4693  if (mContentListener) {
   4694    mContentListener->DropDocShellReference();
   4695    mContentListener->SetParentContentListener(nullptr);
   4696    // Note that we do NOT set mContentListener to null here; that
   4697    // way if someone tries to do a load in us after this point
   4698    // the nsDSURIContentListener will block it.  All of which
   4699    // means that we should do this before calling Stop(), of
   4700    // course.
   4701  }
   4702 
   4703  if (BrowsingContext* browsingContext = GetBrowsingContext();
   4704      browsingContext && !browsingContext->IsTop()) {
   4705    InformNavigationAPIAboutChildNavigableDestruction();
   4706  }
   4707 
   4708  // Stop any URLs that are currently being loaded...
   4709  Stop(nsIWebNavigation::STOP_ALL);
   4710 
   4711  mEditorData = nullptr;
   4712 
   4713  // Save the state of the current document, before destroying the window.
   4714  // This is needed to capture the state of a frameset when the new document
   4715  // causes the frameset to be destroyed...
   4716  PersistLayoutHistoryState();
   4717 
   4718  // Remove this docshell from its parent's child list
   4719  nsCOMPtr<nsIDocShellTreeItem> docShellParentAsItem =
   4720      do_QueryInterface(GetAsSupports(mParent));
   4721  if (docShellParentAsItem) {
   4722    docShellParentAsItem->RemoveChild(this);
   4723  }
   4724 
   4725  DestroyDocumentViewer();
   4726 
   4727  nsDocLoader::Destroy();
   4728 
   4729  mParentWidget = nullptr;
   4730  SetCurrentURIInternal(nullptr);
   4731 
   4732  if (mScriptGlobal) {
   4733    mScriptGlobal->DetachFromDocShell(!mWillChangeProcess);
   4734    mScriptGlobal = nullptr;
   4735  }
   4736 
   4737  if (GetSessionHistory()) {
   4738    // We want to destroy these content viewers now rather than
   4739    // letting their destruction wait for the session history
   4740    // entries to get garbage collected.  (Bug 488394)
   4741    GetSessionHistory()->EvictLocalDocumentViewers();
   4742  }
   4743 
   4744  if (mWillChangeProcess && !mBrowsingContext->IsDiscarded()) {
   4745    mBrowsingContext->PrepareForProcessChange();
   4746  }
   4747 
   4748  SetTreeOwner(nullptr);
   4749 
   4750  mBrowserChild = nullptr;
   4751 
   4752  mChromeEventHandler = nullptr;
   4753 
   4754  mBCWebProgressStatusFilter = nullptr;
   4755 
   4756  // Cancel any timers that were set for this docshell; this is needed
   4757  // to break the cycle between us and the timers.
   4758  CancelRefreshURITimers();
   4759 
   4760  return NS_OK;
   4761 }
   4762 
   4763 double nsDocShell::GetWidgetCSSToDeviceScale() {
   4764  if (mParentWidget) {
   4765    return mParentWidget->GetDefaultScale().scale;
   4766  }
   4767  if (nsCOMPtr<nsIBaseWindow> ownerWindow = do_QueryInterface(mTreeOwner)) {
   4768    return ownerWindow->GetWidgetCSSToDeviceScale();
   4769  }
   4770  return 1.0;
   4771 }
   4772 
   4773 NS_IMETHODIMP
   4774 nsDocShell::GetDevicePixelsPerDesktopPixel(double* aScale) {
   4775  if (mParentWidget) {
   4776    *aScale = mParentWidget->GetDesktopToDeviceScale().scale;
   4777    return NS_OK;
   4778  }
   4779 
   4780  nsCOMPtr<nsIBaseWindow> ownerWindow(do_QueryInterface(mTreeOwner));
   4781  if (ownerWindow) {
   4782    return ownerWindow->GetDevicePixelsPerDesktopPixel(aScale);
   4783  }
   4784 
   4785  *aScale = 1.0;
   4786  return NS_OK;
   4787 }
   4788 
   4789 NS_IMETHODIMP
   4790 nsDocShell::SetPosition(int32_t aX, int32_t aY) {
   4791  mBounds.MoveTo(aX, aY);
   4792 
   4793  if (mDocumentViewer) {
   4794    NS_ENSURE_SUCCESS(mDocumentViewer->Move(aX, aY), NS_ERROR_FAILURE);
   4795  }
   4796 
   4797  return NS_OK;
   4798 }
   4799 
   4800 NS_IMETHODIMP
   4801 nsDocShell::SetPositionDesktopPix(int32_t aX, int32_t aY) {
   4802  nsCOMPtr<nsIBaseWindow> ownerWindow(do_QueryInterface(mTreeOwner));
   4803  if (ownerWindow) {
   4804    return ownerWindow->SetPositionDesktopPix(aX, aY);
   4805  }
   4806 
   4807  double scale = 1.0;
   4808  GetDevicePixelsPerDesktopPixel(&scale);
   4809  return SetPosition(NSToIntRound(aX * scale), NSToIntRound(aY * scale));
   4810 }
   4811 
   4812 NS_IMETHODIMP
   4813 nsDocShell::GetPosition(int32_t* aX, int32_t* aY) {
   4814  return GetPositionAndSize(aX, aY, nullptr, nullptr);
   4815 }
   4816 
   4817 NS_IMETHODIMP
   4818 nsDocShell::SetSize(int32_t aWidth, int32_t aHeight, bool aRepaint) {
   4819  int32_t x = 0, y = 0;
   4820  GetPosition(&x, &y);
   4821  return SetPositionAndSize(x, y, aWidth, aHeight,
   4822                            aRepaint ? nsIBaseWindow::eRepaint : 0);
   4823 }
   4824 
   4825 NS_IMETHODIMP
   4826 nsDocShell::GetSize(int32_t* aWidth, int32_t* aHeight) {
   4827  return GetPositionAndSize(nullptr, nullptr, aWidth, aHeight);
   4828 }
   4829 
   4830 NS_IMETHODIMP
   4831 nsDocShell::SetPositionAndSize(int32_t aX, int32_t aY, int32_t aWidth,
   4832                               int32_t aHeight, uint32_t aFlags) {
   4833  mBounds.SetRect(aX, aY, aWidth, aHeight);
   4834 
   4835  // Hold strong ref, since SetBounds can make us null out mDocumentViewer
   4836  nsCOMPtr<nsIDocumentViewer> viewer = mDocumentViewer;
   4837  if (viewer) {
   4838    uint32_t cvflags = (aFlags & nsIBaseWindow::eDelayResize)
   4839                           ? nsIDocumentViewer::eDelayResize
   4840                           : 0;
   4841    // XXX Border figured in here or is that handled elsewhere?
   4842    nsresult rv = viewer->SetBoundsWithFlags(mBounds, cvflags);
   4843    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
   4844  }
   4845 
   4846  return NS_OK;
   4847 }
   4848 
   4849 NS_IMETHODIMP
   4850 nsDocShell::GetPositionAndSize(int32_t* aX, int32_t* aY, int32_t* aWidth,
   4851                               int32_t* aHeight) {
   4852  if (mParentWidget) {
   4853    // ensure size is up-to-date if window has changed resolution
   4854    LayoutDeviceIntRect r = mParentWidget->GetClientBounds();
   4855    SetPositionAndSize(mBounds.X(), mBounds.Y(), r.Width(), r.Height(), 0);
   4856  }
   4857 
   4858  // We should really consider just getting this information from
   4859  // our window instead of duplicating the storage and code...
   4860  if (aWidth || aHeight) {
   4861    // Caller wants to know our size; make sure to give them up to
   4862    // date information.
   4863    RefPtr<Document> doc(do_GetInterface(GetAsSupports(mParent)));
   4864    if (doc) {
   4865      doc->FlushPendingNotifications(FlushType::Layout);
   4866    }
   4867  }
   4868 
   4869  DoGetPositionAndSize(aX, aY, aWidth, aHeight);
   4870  return NS_OK;
   4871 }
   4872 
   4873 void nsDocShell::DoGetPositionAndSize(int32_t* aX, int32_t* aY, int32_t* aWidth,
   4874                                      int32_t* aHeight) {
   4875  if (aX) {
   4876    *aX = mBounds.X();
   4877  }
   4878  if (aY) {
   4879    *aY = mBounds.Y();
   4880  }
   4881  if (aWidth) {
   4882    *aWidth = mBounds.Width();
   4883  }
   4884  if (aHeight) {
   4885    *aHeight = mBounds.Height();
   4886  }
   4887 }
   4888 
   4889 NS_IMETHODIMP
   4890 nsDocShell::SetDimensions(DimensionRequest&& aRequest) {
   4891  return NS_ERROR_NOT_IMPLEMENTED;
   4892 }
   4893 
   4894 NS_IMETHODIMP
   4895 nsDocShell::GetDimensions(DimensionKind aDimensionKind, int32_t* aX,
   4896                          int32_t* aY, int32_t* aCX, int32_t* aCY) {
   4897  return NS_ERROR_NOT_IMPLEMENTED;
   4898 }
   4899 
   4900 NS_IMETHODIMP
   4901 nsDocShell::GetParentWidget(nsIWidget** aParentWidget) {
   4902  NS_ENSURE_ARG_POINTER(aParentWidget);
   4903 
   4904  *aParentWidget = mParentWidget;
   4905  NS_IF_ADDREF(*aParentWidget);
   4906 
   4907  return NS_OK;
   4908 }
   4909 
   4910 NS_IMETHODIMP
   4911 nsDocShell::SetParentWidget(nsIWidget* aParentWidget) {
   4912  MOZ_ASSERT(!mIsBeingDestroyed);
   4913  mParentWidget = aParentWidget;
   4914 
   4915  return NS_OK;
   4916 }
   4917 
   4918 NS_IMETHODIMP
   4919 nsDocShell::GetNativeHandle(nsAString& aNativeHandle) {
   4920  // the nativeHandle should be accessed from nsIAppWindow
   4921  return NS_ERROR_NOT_IMPLEMENTED;
   4922 }
   4923 
   4924 NS_IMETHODIMP
   4925 nsDocShell::GetVisibility(bool* aVisibility) {
   4926  NS_ENSURE_ARG_POINTER(aVisibility);
   4927 
   4928  *aVisibility = false;
   4929 
   4930  if (!mDocumentViewer) {
   4931    return NS_OK;
   4932  }
   4933 
   4934  PresShell* presShell = GetPresShell();
   4935  if (!presShell || presShell->IsUnderHiddenEmbedderElement()) {
   4936    // NOTE(emilio): IsUnderHiddenEmbedderElement() accounts for ancestors as
   4937    // well.
   4938    return NS_OK;
   4939  }
   4940 
   4941  nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_QueryInterface(mTreeOwner));
   4942  if (!treeOwnerAsWin) {
   4943    *aVisibility = true;
   4944    return NS_OK;
   4945  }
   4946 
   4947  // Check with the tree owner as well to give embedders a chance to
   4948  // expose visibility as well.
   4949  nsresult rv = treeOwnerAsWin->GetVisibility(aVisibility);
   4950  if (rv == NS_ERROR_NOT_IMPLEMENTED) {
   4951    // The tree owner had no opinion on our visibility.
   4952    *aVisibility = true;
   4953    return NS_OK;
   4954  }
   4955  return rv;
   4956 }
   4957 
   4958 void nsDocShell::ActivenessMaybeChanged() {
   4959  const bool isActive = mBrowsingContext->IsActive();
   4960  if (RefPtr<PresShell> presShell = GetPresShell()) {
   4961    presShell->ActivenessMaybeChanged();
   4962  }
   4963 
   4964  // Tell the window about it
   4965  if (mScriptGlobal) {
   4966    mScriptGlobal->SetIsBackground(!isActive);
   4967    if (RefPtr<Document> doc = mScriptGlobal->GetExtantDoc()) {
   4968      // Update orientation when the top-level browsing context becomes active.
   4969      if (isActive && mBrowsingContext->IsTop() &&
   4970          !mBrowsingContext->Windowless()) {
   4971        // We only care about the top-level browsing context.
   4972        auto orientation = mBrowsingContext->GetOrientationLock();
   4973        ScreenOrientation::UpdateActiveOrientationLock(orientation);
   4974      }
   4975 
   4976      doc->PostVisibilityUpdateEvent();
   4977    }
   4978  }
   4979 
   4980  // Tell the nsDOMNavigationTiming about it
   4981  RefPtr<nsDOMNavigationTiming> timing = mTiming;
   4982  if (!timing && mDocumentViewer) {
   4983    if (Document* doc = mDocumentViewer->GetDocument()) {
   4984      timing = doc->GetNavigationTiming();
   4985    }
   4986  }
   4987  if (timing) {
   4988    timing->NotifyDocShellStateChanged(
   4989        isActive ? nsDOMNavigationTiming::DocShellState::eActive
   4990                 : nsDOMNavigationTiming::DocShellState::eInactive);
   4991  }
   4992 
   4993  // Restart or stop meta refresh timers if necessary
   4994  if (mDisableMetaRefreshWhenInactive) {
   4995    if (isActive) {
   4996      ResumeRefreshURIs();
   4997    } else {
   4998      SuspendRefreshURIs();
   4999    }
   5000  }
   5001 
   5002  if (InputTaskManager::CanSuspendInputEvent()) {
   5003    mBrowsingContext->Group()->UpdateInputTaskManagerIfNeeded(isActive);
   5004  }
   5005 }
   5006 
   5007 NS_IMETHODIMP
   5008 nsDocShell::SetDefaultLoadFlags(uint32_t aDefaultLoadFlags) {
   5009  if (!mWillChangeProcess) {
   5010    // Intentionally ignoring handling discarded browsing contexts.
   5011    (void)mBrowsingContext->SetDefaultLoadFlags(aDefaultLoadFlags);
   5012  } else {
   5013    // Bug 1623565: DevTools tries to clean up defaultLoadFlags on
   5014    // shutdown. Sorry DevTools, your DocShell is in another process.
   5015    NS_WARNING("nsDocShell::SetDefaultLoadFlags called on Zombie DocShell");
   5016  }
   5017  return NS_OK;
   5018 }
   5019 
   5020 NS_IMETHODIMP
   5021 nsDocShell::GetDefaultLoadFlags(uint32_t* aDefaultLoadFlags) {
   5022  *aDefaultLoadFlags = mBrowsingContext->GetDefaultLoadFlags();
   5023  return NS_OK;
   5024 }
   5025 
   5026 NS_IMETHODIMP
   5027 nsDocShell::GetFailedChannel(nsIChannel** aFailedChannel) {
   5028  NS_ENSURE_ARG_POINTER(aFailedChannel);
   5029  Document* doc = GetDocument();
   5030  if (!doc) {
   5031    *aFailedChannel = nullptr;
   5032    return NS_OK;
   5033  }
   5034  NS_IF_ADDREF(*aFailedChannel = doc->GetFailedChannel());
   5035  return NS_OK;
   5036 }
   5037 
   5038 NS_IMETHODIMP
   5039 nsDocShell::SetVisibility(bool aVisibility) {
   5040  // Show()/Hide() may change mDocumentViewer.
   5041  nsCOMPtr<nsIDocumentViewer> viewer = mDocumentViewer;
   5042  if (!viewer) {
   5043    return NS_OK;
   5044  }
   5045  if (aVisibility) {
   5046    viewer->Show();
   5047  } else {
   5048    viewer->Hide();
   5049  }
   5050 
   5051  return NS_OK;
   5052 }
   5053 
   5054 NS_IMETHODIMP
   5055 nsDocShell::GetEnabled(bool* aEnabled) {
   5056  NS_ENSURE_ARG_POINTER(aEnabled);
   5057  *aEnabled = true;
   5058  return NS_ERROR_NOT_IMPLEMENTED;
   5059 }
   5060 
   5061 NS_IMETHODIMP
   5062 nsDocShell::SetEnabled(bool aEnabled) { return NS_ERROR_NOT_IMPLEMENTED; }
   5063 
   5064 NS_IMETHODIMP
   5065 nsDocShell::GetMainWidget(nsIWidget** aMainWidget) {
   5066  // We don't create our own widget, so simply return the parent one.
   5067  return GetParentWidget(aMainWidget);
   5068 }
   5069 
   5070 NS_IMETHODIMP
   5071 nsDocShell::GetTitle(nsAString& aTitle) {
   5072  aTitle = mTitle;
   5073  return NS_OK;
   5074 }
   5075 
   5076 NS_IMETHODIMP
   5077 nsDocShell::SetTitle(const nsAString& aTitle) {
   5078  // Avoid unnecessary updates of the title if the URI and the title haven't
   5079  // changed.
   5080  if (mTitleValidForCurrentURI && mTitle == aTitle) {
   5081    return NS_OK;
   5082  }
   5083 
   5084  // Store local title
   5085  mTitle = aTitle;
   5086  mTitleValidForCurrentURI = true;
   5087 
   5088  // When title is set on the top object it should then be passed to the
   5089  // tree owner.
   5090  if (mBrowsingContext->IsTop()) {
   5091    nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_QueryInterface(mTreeOwner));
   5092    if (treeOwnerAsWin) {
   5093      treeOwnerAsWin->SetTitle(aTitle);
   5094    }
   5095  }
   5096 
   5097  if (mCurrentURI && mLoadType != LOAD_ERROR_PAGE) {
   5098    UpdateGlobalHistoryTitle(mCurrentURI);
   5099  }
   5100 
   5101  // Update SessionHistory with the document's title.
   5102  if (mLoadType != LOAD_BYPASS_HISTORY && mLoadType != LOAD_ERROR_PAGE) {
   5103    SetTitleOnHistoryEntry(true);
   5104  }
   5105 
   5106  return NS_OK;
   5107 }
   5108 
   5109 void nsDocShell::SetTitleOnHistoryEntry(bool aUpdateEntryInSessionHistory) {
   5110  if (mOSHE) {
   5111    mOSHE->SetTitle(mTitle);
   5112  }
   5113 
   5114  if (mActiveEntry && mBrowsingContext) {
   5115    mActiveEntry->SetTitle(mTitle);
   5116    if (aUpdateEntryInSessionHistory) {
   5117      if (XRE_IsParentProcess()) {
   5118        SessionHistoryEntry* entry =
   5119            mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
   5120        if (entry) {
   5121          entry->SetTitle(mTitle);
   5122        }
   5123      } else {
   5124        (void)ContentChild::GetSingleton()->SendSessionHistoryEntryTitle(
   5125            mBrowsingContext, mTitle);
   5126      }
   5127    }
   5128  }
   5129 }
   5130 
   5131 nsPoint nsDocShell::GetCurScrollPos() {
   5132  nsPoint scrollPos;
   5133  if (ScrollContainerFrame* sf = GetRootScrollContainerFrame()) {
   5134    scrollPos = sf->GetVisualViewportOffset();
   5135  }
   5136  return scrollPos;
   5137 }
   5138 
   5139 nsresult nsDocShell::SetCurScrollPosEx(int32_t aCurHorizontalPos,
   5140                                       int32_t aCurVerticalPos) {
   5141  ScrollContainerFrame* sf = GetRootScrollContainerFrame();
   5142  NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
   5143 
   5144  ScrollMode scrollMode = sf->ScrollModeForScrollBehavior();
   5145 
   5146  nsPoint targetPos(aCurHorizontalPos, aCurVerticalPos);
   5147  sf->ScrollTo(targetPos, scrollMode);
   5148 
   5149  // Set the visual viewport offset as well.
   5150 
   5151  RefPtr<PresShell> presShell = GetPresShell();
   5152  NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
   5153 
   5154  nsPresContext* presContext = presShell->GetPresContext();
   5155  NS_ENSURE_TRUE(presContext, NS_ERROR_FAILURE);
   5156 
   5157  // Only the root content document can have a distinct visual viewport offset.
   5158  if (!presContext->IsRootContentDocumentCrossProcess()) {
   5159    return NS_OK;
   5160  }
   5161 
   5162  // Not on a platform with a distinct visual viewport - don't bother setting
   5163  // the visual viewport offset.
   5164  if (!presShell->IsVisualViewportSizeSet()) {
   5165    return NS_OK;
   5166  }
   5167 
   5168  presShell->ScrollToVisual(targetPos, layers::FrameMetrics::eMainThread,
   5169                            scrollMode);
   5170 
   5171  return NS_OK;
   5172 }
   5173 
   5174 void nsDocShell::RestoreScrollPositionFromTargetSessionHistoryInfo(
   5175    SessionHistoryInfo* aTarget) {
   5176  MOZ_DIAGNOSTIC_ASSERT(mozilla::SessionHistoryInParent());
   5177  nscoord bx = 0;
   5178  nscoord by = 0;
   5179  if (aTarget) {
   5180    aTarget->GetScrollPosition(&bx, &by);
   5181  }
   5182  SetCurScrollPosEx(bx, by);
   5183 }
   5184 
   5185 void nsDocShell::SetScrollbarPreference(mozilla::ScrollbarPreference aPref) {
   5186  if (mScrollbarPref == aPref) {
   5187    return;
   5188  }
   5189  mScrollbarPref = aPref;
   5190  auto* ps = GetPresShell();
   5191  if (!ps) {
   5192    return;
   5193  }
   5194  nsIFrame* rootScrollContainerFrame = ps->GetRootScrollContainerFrame();
   5195  if (!rootScrollContainerFrame) {
   5196    return;
   5197  }
   5198  ps->FrameNeedsReflow(rootScrollContainerFrame,
   5199                       IntrinsicDirty::FrameAncestorsAndDescendants,
   5200                       NS_FRAME_IS_DIRTY);
   5201 }
   5202 
   5203 //*****************************************************************************
   5204 // nsDocShell::nsIRefreshURI
   5205 //*****************************************************************************
   5206 
   5207 NS_IMETHODIMP
   5208 nsDocShell::RefreshURI(nsIURI* aURI, nsIPrincipal* aPrincipal,
   5209                       uint32_t aDelay) {
   5210  MOZ_ASSERT(!mIsBeingDestroyed);
   5211 
   5212  NS_ENSURE_ARG(aURI);
   5213 
   5214  /* Check if Meta refresh/redirects are permitted. Some
   5215   * embedded applications may not want to do this.
   5216   * Must do this before sending out NOTIFY_REFRESH events
   5217   * because listeners may have side effects (e.g. displaying a
   5218   * button to manually trigger the refresh later).
   5219   */
   5220  bool allowRedirects = true;
   5221  GetAllowMetaRedirects(&allowRedirects);
   5222  if (!allowRedirects) {
   5223    return NS_OK;
   5224  }
   5225 
   5226  // If any web progress listeners are listening for NOTIFY_REFRESH events,
   5227  // give them a chance to block this refresh.
   5228  bool sameURI;
   5229  nsresult rv = aURI->Equals(mCurrentURI, &sameURI);
   5230  if (NS_FAILED(rv)) {
   5231    sameURI = false;
   5232  }
   5233  if (!RefreshAttempted(this, aURI, aDelay, sameURI)) {
   5234    return NS_OK;
   5235  }
   5236 
   5237  nsCOMPtr<nsITimerCallback> refreshTimer =
   5238      new nsRefreshTimer(this, aURI, aPrincipal, aDelay);
   5239 
   5240  BusyFlags busyFlags = GetBusyFlags();
   5241 
   5242  if (!mRefreshURIList) {
   5243    mRefreshURIList = nsArray::Create();
   5244  }
   5245 
   5246  if (busyFlags & BUSY_FLAGS_BUSY ||
   5247      (!mBrowsingContext->IsActive() && mDisableMetaRefreshWhenInactive)) {
   5248    // We don't  want to create the timer right now. Instead queue up the
   5249    // request and trigger the timer in EndPageLoad() or whenever we become
   5250    // active.
   5251    mRefreshURIList->AppendElement(refreshTimer);
   5252  } else {
   5253    // There is no page loading going on right now.  Create the
   5254    // timer and fire it right away.
   5255    nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
   5256    NS_ENSURE_TRUE(win, NS_ERROR_FAILURE);
   5257 
   5258    nsCOMPtr<nsITimer> timer = MOZ_TRY(
   5259        NS_NewTimerWithCallback(refreshTimer, aDelay, nsITimer::TYPE_ONE_SHOT));
   5260 
   5261    mRefreshURIList->AppendElement(timer);  // owning timer ref
   5262  }
   5263  return NS_OK;
   5264 }
   5265 
   5266 nsresult nsDocShell::ForceRefreshURIFromTimer(nsIURI* aURI,
   5267                                              nsIPrincipal* aPrincipal,
   5268                                              uint32_t aDelay,
   5269                                              nsITimer* aTimer) {
   5270  MOZ_ASSERT(aTimer, "Must have a timer here");
   5271 
   5272  // Remove aTimer from mRefreshURIList if needed
   5273  if (mRefreshURIList) {
   5274    uint32_t n = 0;
   5275    mRefreshURIList->GetLength(&n);
   5276 
   5277    for (uint32_t i = 0; i < n; ++i) {
   5278      nsCOMPtr<nsITimer> timer = do_QueryElementAt(mRefreshURIList, i);
   5279      if (timer == aTimer) {
   5280        mRefreshURIList->RemoveElementAt(i);
   5281        break;
   5282      }
   5283    }
   5284  }
   5285 
   5286  return ForceRefreshURI(aURI, aPrincipal, aDelay);
   5287 }
   5288 
   5289 NS_IMETHODIMP
   5290 nsDocShell::ForceRefreshURI(nsIURI* aURI, nsIPrincipal* aPrincipal,
   5291                            uint32_t aDelay) {
   5292  NS_ENSURE_ARG(aURI);
   5293 
   5294  RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(aURI);
   5295  loadState->SetOriginalURI(mCurrentURI);
   5296  loadState->SetResultPrincipalURI(aURI);
   5297  loadState->SetResultPrincipalURIIsSome(true);
   5298  loadState->SetKeepResultPrincipalURIIfSet(true);
   5299  loadState->SetIsMetaRefresh(true);
   5300 
   5301  RefPtr<Document> doc = GetDocument();
   5302  NS_ENSURE_STATE(doc);
   5303 
   5304  // Set the triggering pricipal to aPrincipal if available, or current
   5305  // document's principal otherwise.
   5306  nsCOMPtr<nsIPrincipal> principal = aPrincipal;
   5307  if (!principal) {
   5308    principal = doc->NodePrincipal();
   5309  }
   5310  loadState->SetTriggeringPrincipal(principal);
   5311  loadState->SetPolicyContainer(doc->GetPolicyContainer());
   5312  loadState->SetHasValidUserGestureActivation(
   5313      doc->HasValidTransientUserGestureActivation());
   5314 
   5315  loadState->SetTextDirectiveUserActivation(
   5316      doc->ConsumeTextDirectiveUserActivation() ||
   5317      loadState->HasValidUserGestureActivation());
   5318  loadState->SetTriggeringSandboxFlags(doc->GetSandboxFlags());
   5319  loadState->SetTriggeringWindowId(doc->InnerWindowID());
   5320  loadState->SetTriggeringStorageAccess(doc->UsingStorageAccess());
   5321  loadState->SetTriggeringClassificationFlags(doc->GetScriptTrackingFlags());
   5322 
   5323  loadState->SetPrincipalIsExplicit(true);
   5324 
   5325  /* Check if this META refresh causes a redirection
   5326   * to another site.
   5327   */
   5328  bool equalUri = false;
   5329  nsresult rv = aURI->Equals(mCurrentURI, &equalUri);
   5330 
   5331  if (NS_SUCCEEDED(rv) && !equalUri && aDelay <= REFRESH_REDIRECT_TIMER) {
   5332    /* It is a META refresh based redirection within the threshold time
   5333     * we have in mind (15000 ms as defined by REFRESH_REDIRECT_TIMER).
   5334     * Pass a REPLACE flag to LoadURI().
   5335     */
   5336    loadState->SetLoadType(LOAD_REFRESH_REPLACE);
   5337  } else {
   5338    loadState->SetLoadType(LOAD_REFRESH);
   5339  }
   5340 
   5341  const bool sendReferrer = StaticPrefs::network_http_referer_sendFromRefresh();
   5342  /* The document's referrer policy is needed instead of mReferrerInfo's
   5343   * referrer policy.
   5344   */
   5345  const nsCOMPtr<nsIReferrerInfo> referrerInfo =
   5346      new ReferrerInfo(*doc, sendReferrer);
   5347  /* We mimic HTTP, which passes the original referrer. See step 3 of
   5348   * <https://html.spec.whatwg.org/multipage/browsing-the-web.html#create-navigation-params-by-fetching>.
   5349   */
   5350  loadState->SetReferrerInfo(referrerInfo);
   5351 
   5352  loadState->SetLoadFlags(
   5353      nsIWebNavigation::LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL);
   5354  loadState->SetFirstParty(true);
   5355 
   5356  /*
   5357   * LoadURI(...) will cancel all refresh timers... This causes the
   5358   * Timer and its refreshData instance to be released...
   5359   */
   5360  LoadURI(loadState, false);
   5361 
   5362  return NS_OK;
   5363 }
   5364 
   5365 static const char16_t* SkipASCIIWhitespace(const char16_t* aStart,
   5366                                           const char16_t* aEnd) {
   5367  const char16_t* iter = aStart;
   5368  while (iter != aEnd && mozilla::IsAsciiWhitespace(*iter)) {
   5369    ++iter;
   5370  }
   5371  return iter;
   5372 }
   5373 
   5374 static std::tuple<const char16_t*, const char16_t*> ExtractURLString(
   5375    const char16_t* aPosition, const char16_t* aEnd) {
   5376  MOZ_ASSERT(aPosition != aEnd);
   5377 
   5378  // 1. Let urlString be the substring of input from the code point at
   5379  //    position to the end of the string.
   5380  const char16_t* urlStart = aPosition;
   5381  const char16_t* urlEnd = aEnd;
   5382 
   5383  // 2. If the code point in input pointed to by position is U+0055 (U) or
   5384  //    U+0075 (u), then advance position to the next code point.
   5385  //    Otherwise, jump to the step labeled skip quotes.
   5386  if (*aPosition == 'U' || *aPosition == 'u') {
   5387    ++aPosition;
   5388 
   5389    // 3. If the code point in input pointed to by position is U+0052 (R) or
   5390    //    U+0072 (r), then advance position to the next code point.
   5391    //    Otherwise, jump to the step labeled parse.
   5392    if (aPosition == aEnd || (*aPosition != 'R' && *aPosition != 'r')) {
   5393      return std::make_tuple(urlStart, urlEnd);
   5394    }
   5395 
   5396    ++aPosition;
   5397 
   5398    // 4. If the code point in input pointed to by position is U+004C (L) or
   5399    //    U+006C (l), then advance position to the next code point.
   5400    //    Otherwise, jump to the step labeled parse.
   5401    if (aPosition == aEnd || (*aPosition != 'L' && *aPosition != 'l')) {
   5402      return std::make_tuple(urlStart, urlEnd);
   5403    }
   5404 
   5405    ++aPosition;
   5406 
   5407    // 5. Skip ASCII whitespace within input given position.
   5408    aPosition = SkipASCIIWhitespace(aPosition, aEnd);
   5409 
   5410    // 6. If the code point in input pointed to by position is U+003D (=),
   5411    //    then advance position to the next code point. Otherwise, jump to
   5412    //    the step labeled parse.
   5413    if (aPosition == aEnd || *aPosition != '=') {
   5414      return std::make_tuple(urlStart, urlEnd);
   5415    }
   5416 
   5417    ++aPosition;
   5418 
   5419    // 7. Skip ASCII whitespace within input given position.
   5420    aPosition = SkipASCIIWhitespace(aPosition, aEnd);
   5421  }
   5422 
   5423  // 8. Skip quotes: If the code point in input pointed to by position is
   5424  //    U+0027 (') or U+0022 ("), then let quote be that code point, and
   5425  //    advance position to the next code point. Otherwise, let quote be
   5426  //    the empty string.
   5427  Maybe<char> quote;
   5428  if (aPosition != aEnd && (*aPosition == '\'' || *aPosition == '"')) {
   5429    quote.emplace(*aPosition);
   5430    ++aPosition;
   5431  }
   5432 
   5433  // 9. Set urlString to the substring of input from the code point at
   5434  //    position to the end of the string.
   5435  urlStart = aPosition;
   5436  urlEnd = aEnd;
   5437 
   5438  // 10. If quote is not the empty string, and there is a code point in
   5439  //     urlString equal to quote, then truncate urlString at that code
   5440  //     point, so that it and all subsequent code points are removed.
   5441  const char16_t* quotePos;
   5442  if (quote.isSome() &&
   5443      (quotePos = nsCharTraits<char16_t>::find(
   5444           urlStart, std::distance(urlStart, aEnd), quote.value()))) {
   5445    urlEnd = quotePos;
   5446  }
   5447 
   5448  return std::make_tuple(urlStart, urlEnd);
   5449 }
   5450 
   5451 void nsDocShell::SetupRefreshURIFromHeader(Document* aDocument,
   5452                                           const nsAString& aHeader) {
   5453  if (mIsBeingDestroyed) {
   5454    return;
   5455  }
   5456 
   5457  const char16_t* position = aHeader.BeginReading();
   5458  const char16_t* end = aHeader.EndReading();
   5459 
   5460  // See https://html.spec.whatwg.org/#shared-declarative-refresh-steps.
   5461 
   5462  // 3. Skip ASCII whitespace
   5463  position = SkipASCIIWhitespace(position, end);
   5464 
   5465  // 4. Let time be 0.
   5466  CheckedInt<uint32_t> milliSeconds;
   5467 
   5468  // 5. Collect a sequence of code points that are ASCII digits
   5469  const char16_t* digitsStart = position;
   5470  while (position != end && mozilla::IsAsciiDigit(*position)) {
   5471    ++position;
   5472  }
   5473 
   5474  if (position == digitsStart) {
   5475    // 6. If timeString is the empty string, then:
   5476    //    1. If the code point in input pointed to by position is not U+002E
   5477    //       (.), then return.
   5478    if (position == end || *position != '.') {
   5479      return;
   5480    }
   5481  } else {
   5482    // 7. Otherwise, set time to the result of parsing timeString using the
   5483    //    rules for parsing non-negative integers.
   5484    nsContentUtils::ParseHTMLIntegerResultFlags result;
   5485    uint32_t seconds =
   5486        nsContentUtils::ParseHTMLInteger(digitsStart, position, &result);
   5487    MOZ_ASSERT(!(result & nsContentUtils::eParseHTMLInteger_Negative));
   5488    if (result & nsContentUtils::eParseHTMLInteger_Error) {
   5489      // The spec assumes no errors here (since we only pass ASCII digits in),
   5490      // but we can still overflow, so this block should deal with that (and
   5491      // only that).
   5492      MOZ_ASSERT(
   5493          !(result & ~(nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput |
   5494                       nsContentUtils::eParseHTMLInteger_Error |
   5495                       nsContentUtils::eParseHTMLInteger_ErrorOverflow)));
   5496      return;
   5497    }
   5498    MOZ_ASSERT(
   5499        !(result & nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput));
   5500 
   5501    milliSeconds = seconds;
   5502    milliSeconds *= 1000;
   5503    if (!milliSeconds.isValid()) {
   5504      return;
   5505    }
   5506  }
   5507 
   5508  // 8. Collect a sequence of code points that are ASCII digits and U+002E FULL
   5509  //    STOP characters (.) from input given position. Ignore any collected
   5510  //    characters.
   5511  while (position != end &&
   5512         (mozilla::IsAsciiDigit(*position) || *position == '.')) {
   5513    ++position;
   5514  }
   5515 
   5516  // 9. Let urlRecord be document's URL.
   5517  nsCOMPtr<nsIURI> urlRecord(aDocument->GetDocumentURI());
   5518 
   5519  // 10. If position is not past the end of input
   5520  if (position != end) {
   5521    // 1. If the code point in input pointed to by position is not U+003B (;),
   5522    //    U+002C (,), or ASCII whitespace, then return.
   5523    if (*position != ';' && *position != ',' &&
   5524        !mozilla::IsAsciiWhitespace(*position)) {
   5525      return;
   5526    }
   5527 
   5528    // 2. Skip ASCII whitespace within input given position.
   5529    position = SkipASCIIWhitespace(position, end);
   5530 
   5531    // 3. If the code point in input pointed to by position is U+003B (;) or
   5532    //    U+002C (,), then advance position to the next code point.
   5533    if (position != end && (*position == ';' || *position == ',')) {
   5534      ++position;
   5535 
   5536      // 4. Skip ASCII whitespace within input given position.
   5537      position = SkipASCIIWhitespace(position, end);
   5538    }
   5539 
   5540    // 11. If position is not past the end of input, then:
   5541    if (position != end) {
   5542      const char16_t* urlStart;
   5543      const char16_t* urlEnd;
   5544 
   5545      // 1-10. See ExtractURLString.
   5546      std::tie(urlStart, urlEnd) = ExtractURLString(position, end);
   5547 
   5548      // 11. Parse: Parse urlString relative to document. If that fails, return.
   5549      //     Otherwise, set urlRecord to the resulting URL record.
   5550      nsresult rv =
   5551          NS_NewURI(getter_AddRefs(urlRecord),
   5552                    Substring(urlStart, std::distance(urlStart, urlEnd)),
   5553                    /* charset = */ nullptr, aDocument->GetDocBaseURI());
   5554      NS_ENSURE_SUCCESS_VOID(rv);
   5555    }
   5556  }
   5557 
   5558  nsIPrincipal* principal = aDocument->NodePrincipal();
   5559  nsCOMPtr<nsIScriptSecurityManager> securityManager =
   5560      nsContentUtils::GetSecurityManager();
   5561  nsresult rv = securityManager->CheckLoadURIWithPrincipal(
   5562      principal, urlRecord,
   5563      nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT,
   5564      aDocument->InnerWindowID());
   5565  NS_ENSURE_SUCCESS_VOID(rv);
   5566 
   5567  bool isjs = true;
   5568  rv = NS_URIChainHasFlags(
   5569      urlRecord, nsIProtocolHandler::URI_OPENING_EXECUTES_SCRIPT, &isjs);
   5570  NS_ENSURE_SUCCESS_VOID(rv);
   5571 
   5572  if (isjs) {
   5573    return;
   5574  }
   5575 
   5576  RefreshURI(urlRecord, principal, milliSeconds.value());
   5577 }
   5578 
   5579 static void DoCancelRefreshURITimers(nsIMutableArray* aTimerList) {
   5580  if (!aTimerList) {
   5581    return;
   5582  }
   5583 
   5584  uint32_t n = 0;
   5585  aTimerList->GetLength(&n);
   5586 
   5587  while (n) {
   5588    nsCOMPtr<nsITimer> timer(do_QueryElementAt(aTimerList, --n));
   5589 
   5590    aTimerList->RemoveElementAt(n);  // bye bye owning timer ref
   5591 
   5592    if (timer) {
   5593      timer->Cancel();
   5594    }
   5595  }
   5596 }
   5597 
   5598 NS_IMETHODIMP
   5599 nsDocShell::CancelRefreshURITimers() {
   5600  DoCancelRefreshURITimers(mRefreshURIList);
   5601  DoCancelRefreshURITimers(mSavedRefreshURIList);
   5602  DoCancelRefreshURITimers(mBFCachedRefreshURIList);
   5603  mRefreshURIList = nullptr;
   5604  mSavedRefreshURIList = nullptr;
   5605  mBFCachedRefreshURIList = nullptr;
   5606 
   5607  return NS_OK;
   5608 }
   5609 
   5610 NS_IMETHODIMP
   5611 nsDocShell::GetRefreshPending(bool* aResult) {
   5612  if (!mRefreshURIList) {
   5613    *aResult = false;
   5614    return NS_OK;
   5615  }
   5616 
   5617  uint32_t count;
   5618  nsresult rv = mRefreshURIList->GetLength(&count);
   5619  if (NS_SUCCEEDED(rv)) {
   5620    *aResult = (count != 0);
   5621  }
   5622  return rv;
   5623 }
   5624 
   5625 void nsDocShell::RefreshURIToQueue() {
   5626  if (mRefreshURIList) {
   5627    uint32_t n = 0;
   5628    mRefreshURIList->GetLength(&n);
   5629 
   5630    for (uint32_t i = 0; i < n; ++i) {
   5631      nsCOMPtr<nsITimer> timer = do_QueryElementAt(mRefreshURIList, i);
   5632      if (!timer) {
   5633        continue;  // this must be a nsRefreshURI already
   5634      }
   5635 
   5636      // Replace this timer object with a nsRefreshTimer object.
   5637      nsCOMPtr<nsITimerCallback> callback;
   5638      timer->GetCallback(getter_AddRefs(callback));
   5639 
   5640      timer->Cancel();
   5641 
   5642      mRefreshURIList->ReplaceElementAt(callback, i);
   5643    }
   5644  }
   5645 }
   5646 
   5647 NS_IMETHODIMP
   5648 nsDocShell::SuspendRefreshURIs() {
   5649  RefreshURIToQueue();
   5650 
   5651  // Suspend refresh URIs for our child shells as well.
   5652  for (auto* child : mChildList.ForwardRange()) {
   5653    nsCOMPtr<nsIDocShell> shell = do_QueryObject(child);
   5654    if (shell) {
   5655      shell->SuspendRefreshURIs();
   5656    }
   5657  }
   5658 
   5659  return NS_OK;
   5660 }
   5661 
   5662 NS_IMETHODIMP
   5663 nsDocShell::ResumeRefreshURIs() {
   5664  RefreshURIFromQueue();
   5665 
   5666  // Resume refresh URIs for our child shells as well.
   5667  for (auto* child : mChildList.ForwardRange()) {
   5668    nsCOMPtr<nsIDocShell> shell = do_QueryObject(child);
   5669    if (shell) {
   5670      shell->ResumeRefreshURIs();
   5671    }
   5672  }
   5673 
   5674  return NS_OK;
   5675 }
   5676 
   5677 nsresult nsDocShell::RefreshURIFromQueue() {
   5678  if (!mRefreshURIList) {
   5679    return NS_OK;
   5680  }
   5681  uint32_t n = 0;
   5682  mRefreshURIList->GetLength(&n);
   5683 
   5684  while (n) {
   5685    nsCOMPtr<nsITimerCallback> refreshInfo =
   5686        do_QueryElementAt(mRefreshURIList, --n);
   5687 
   5688    if (refreshInfo) {
   5689      // This is the nsRefreshTimer object, waiting to be
   5690      // setup in a timer object and fired.
   5691      // Create the timer and  trigger it.
   5692      uint32_t delay = static_cast<nsRefreshTimer*>(
   5693                           static_cast<nsITimerCallback*>(refreshInfo))
   5694                           ->GetDelay();
   5695      nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
   5696      if (win) {
   5697        nsCOMPtr<nsITimer> timer;
   5698        NS_NewTimerWithCallback(getter_AddRefs(timer), refreshInfo, delay,
   5699                                nsITimer::TYPE_ONE_SHOT);
   5700 
   5701        if (timer) {
   5702          // Replace the nsRefreshTimer element in the queue with
   5703          // its corresponding timer object, so that in case another
   5704          // load comes through before the timer can go off, the timer will
   5705          // get cancelled in CancelRefreshURITimer()
   5706          mRefreshURIList->ReplaceElementAt(timer, n);
   5707        }
   5708      }
   5709    }
   5710  }
   5711 
   5712  return NS_OK;
   5713 }
   5714 
   5715 static bool IsFollowupPartOfMultipart(nsIRequest* aRequest) {
   5716  nsCOMPtr<nsIMultiPartChannel> multiPartChannel = do_QueryInterface(aRequest);
   5717  bool firstPart = false;
   5718  return multiPartChannel &&
   5719         NS_SUCCEEDED(multiPartChannel->GetIsFirstPart(&firstPart)) &&
   5720         !firstPart;
   5721 }
   5722 
   5723 nsresult nsDocShell::Embed(nsIDocumentViewer* aDocumentViewer,
   5724                           WindowGlobalChild* aWindowActor,
   5725                           bool aIsTransientAboutBlank, nsIRequest* aRequest,
   5726                           nsIURI* aPreviousURI) {
   5727  // Save the LayoutHistoryState of the previous document, before
   5728  // setting up new document
   5729  PersistLayoutHistoryState();
   5730 
   5731  nsresult rv = SetupNewViewer(aDocumentViewer, aWindowActor);
   5732  NS_ENSURE_SUCCESS(rv, rv);
   5733 
   5734  // XXX What if SetupNewViewer fails?
   5735  if (mozilla::SessionHistoryInParent() ? !!mLoadingEntry : !!mLSHE) {
   5736    // Set history.state
   5737    SetDocCurrentStateObj(mLSHE,
   5738                          mLoadingEntry ? &mLoadingEntry->mInfo : nullptr);
   5739  }
   5740 
   5741  if (mLSHE) {
   5742    // Restore the editing state, if it's stored in session history.
   5743    if (mLSHE->HasDetachedEditor()) {
   5744      ReattachEditorToWindow(mLSHE);
   5745    }
   5746 
   5747    SetHistoryEntryAndUpdateBC(Nothing(), Some<nsISHEntry*>(mLSHE));
   5748  }
   5749 
   5750  if (!aIsTransientAboutBlank && mozilla::SessionHistoryInParent() &&
   5751      !IsFollowupPartOfMultipart(aRequest)) {
   5752    bool expired = false;
   5753    uint32_t cacheKey = 0;
   5754    nsCOMPtr<nsICacheInfoChannel> cacheChannel = do_QueryInterface(aRequest);
   5755    if (cacheChannel) {
   5756      // Check if the page has expired from cache
   5757      uint32_t expTime = 0;
   5758      cacheChannel->GetCacheTokenExpirationTime(&expTime);
   5759      uint32_t now = PRTimeToSeconds(PR_Now());
   5760      if (expTime <= now) {
   5761        expired = true;
   5762      }
   5763 
   5764      // The checks for updating cache key are similar to the old session
   5765      // history in OnNewURI. Try to update the cache key if
   5766      //  - we should update session history and aren't doing a session
   5767      //    history load.
   5768      //  - we're doing a forced reload.
   5769      if (((!mLoadingEntry || !mLoadingEntry->mLoadIsFromSessionHistory) &&
   5770           mBrowsingContext->ShouldUpdateSessionHistory(mLoadType)) ||
   5771          IsForceReloadType(mLoadType)) {
   5772        cacheChannel->GetCacheKey(&cacheKey);
   5773      }
   5774    }
   5775 
   5776    MOZ_LOG(gSHLog, LogLevel::Debug, ("document %p Embed", this));
   5777    MoveLoadingToActiveEntry(expired, cacheKey, aPreviousURI);
   5778  }
   5779 
   5780  bool updateHistory = true;
   5781 
   5782  // Determine if this type of load should update history
   5783  switch (mLoadType) {
   5784    case LOAD_NORMAL_REPLACE:
   5785    case LOAD_REFRESH_REPLACE:
   5786    case LOAD_STOP_CONTENT_AND_REPLACE:
   5787    case LOAD_RELOAD_BYPASS_CACHE:
   5788    case LOAD_RELOAD_BYPASS_PROXY:
   5789    case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
   5790    case LOAD_REPLACE_BYPASS_CACHE:
   5791      updateHistory = false;
   5792      break;
   5793    default:
   5794      break;
   5795  }
   5796 
   5797  if (!updateHistory) {
   5798    SetLayoutHistoryState(nullptr);
   5799  }
   5800 
   5801  return NS_OK;
   5802 }
   5803 
   5804 //*****************************************************************************
   5805 // nsDocShell::nsIWebProgressListener
   5806 //*****************************************************************************
   5807 
   5808 NS_IMETHODIMP
   5809 nsDocShell::OnProgressChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
   5810                             int32_t aCurSelfProgress, int32_t aMaxSelfProgress,
   5811                             int32_t aCurTotalProgress,
   5812                             int32_t aMaxTotalProgress) {
   5813  // Listeners in the parent process only care about aCurTotalProgress and
   5814  // aMaxTotalProgress, which is internally managed by nsDocLoader. Because of
   5815  // this, we don't send progress notifications except when they are recorded by
   5816  // the toplevel context, and only report them on the toplevel context in the
   5817  // parent process.
   5818  //
   5819  // FIXME: We should track progress for out-of-process iframes and manage total
   5820  // progress in the parent process for more accurate notifications.
   5821  MOZ_ASSERT(
   5822      mBrowsingContext->IsTop(),
   5823      "notification excluded in AddProgressListener(...) for non-toplevel BCs");
   5824 
   5825  if (nsCOMPtr<nsIWebProgressListener> listener = BCWebProgressListener()) {
   5826    listener->OnProgressChange(aProgress, aRequest, aCurSelfProgress,
   5827                               aMaxSelfProgress, aCurTotalProgress,
   5828                               aMaxTotalProgress);
   5829  }
   5830 
   5831  return NS_OK;
   5832 }
   5833 
   5834 NS_IMETHODIMP
   5835 nsDocShell::OnStateChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
   5836                          uint32_t aStateFlags, nsresult aStatus) {
   5837  // If we're receiving a notification on ourselves which has at least one of
   5838  // the state change flags in kStateChangeFlagFilter, also notify WebProgress
   5839  // on BrowsingContextWebProgress, potentially over IPC.
   5840  //
   5841  // NOTE: We don't notify for bubbled notifications (aProgress != this), as
   5842  // BrowsingContextWebProgress independently handles event bubbling in the
   5843  // parent process.
   5844  //
   5845  // NOTE: We don't filter notifications when registering our listener, as
   5846  // `STATE_IS_REDIRECTED_DOCUMENT` cannot be filtered for at registration time.
   5847  static constexpr uint32_t kStateChangeFlagFilter =
   5848      STATE_IS_NETWORK | STATE_IS_DOCUMENT | STATE_IS_WINDOW |
   5849      STATE_IS_REDIRECTED_DOCUMENT;
   5850  if (aProgress == this && (aStateFlags & kStateChangeFlagFilter) != 0) {
   5851    if (nsCOMPtr<nsIWebProgressListener> listener = BCWebProgressListener()) {
   5852      listener->OnStateChange(aProgress, aRequest, aStateFlags, aStatus);
   5853    }
   5854  }
   5855 
   5856  if ((~aStateFlags & (STATE_START | STATE_IS_NETWORK)) == 0) {
   5857    // Save timing statistics.
   5858    nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
   5859    nsCOMPtr<nsIURI> uri;
   5860    channel->GetURI(getter_AddRefs(uri));
   5861    nsAutoCString aURI;
   5862    uri->GetAsciiSpec(aURI);
   5863 
   5864    if (this == aProgress) {
   5865      (void)MaybeInitTiming();
   5866      mTiming->NotifyFetchStart(uri,
   5867                                ConvertLoadTypeToNavigationType(mLoadType));
   5868      // If we are starting a DocumentChannel, we need to pass the timing
   5869      // statistics so that should a process switch occur, the starting type can
   5870      // be passed to the new DocShell running in the other content process.
   5871      if (RefPtr<DocumentChannel> docChannel = do_QueryObject(aRequest)) {
   5872        docChannel->SetNavigationTiming(mTiming);
   5873      }
   5874    }
   5875 
   5876    // Page has begun to load
   5877    mBusyFlags = (BusyFlags)(BUSY_FLAGS_BUSY | BUSY_FLAGS_BEFORE_PAGE_LOAD);
   5878 
   5879    if ((aStateFlags & STATE_RESTORING) == 0) {
   5880      if (SessionStorePlatformCollection()) {
   5881        if (IsForceReloadType(mLoadType)) {
   5882          if (WindowContext* windowContext =
   5883                  mBrowsingContext->GetCurrentWindowContext()) {
   5884            SessionStoreChild::From(windowContext->GetWindowGlobalChild())
   5885                ->ResetSessionStore(mBrowsingContext,
   5886                                    mBrowsingContext->GetSessionStoreEpoch());
   5887          }
   5888        }
   5889      }
   5890    }
   5891  } else if ((~aStateFlags & (STATE_TRANSFERRING | STATE_IS_DOCUMENT)) == 0) {
   5892    // Page is loading
   5893    mBusyFlags = (BusyFlags)(BUSY_FLAGS_BUSY | BUSY_FLAGS_PAGE_LOADING);
   5894  } else if ((aStateFlags & STATE_STOP) && (aStateFlags & STATE_IS_NETWORK)) {
   5895    // Page has finished loading
   5896    mBusyFlags = BUSY_FLAGS_NONE;
   5897  }
   5898 
   5899  if ((~aStateFlags & (STATE_IS_DOCUMENT | STATE_STOP)) == 0) {
   5900    nsCOMPtr<nsIWebProgress> webProgress =
   5901        do_QueryInterface(GetAsSupports(this));
   5902    // Is the document stop notification for this document?
   5903    if (aProgress == webProgress.get()) {
   5904      nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
   5905      EndPageLoad(aProgress, channel, aStatus);
   5906    }
   5907  }
   5908  // note that redirect state changes will go through here as well, but it
   5909  // is better to handle those in OnRedirectStateChange where more
   5910  // information is available.
   5911  return NS_OK;
   5912 }
   5913 
   5914 NS_IMETHODIMP
   5915 nsDocShell::OnLocationChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
   5916                             nsIURI* aURI, uint32_t aFlags) {
   5917  // If we're receiving a notification on ourselves, also notify WebProgress on
   5918  // BrowsingContextWebProgress, potentially over IPC.
   5919  //
   5920  // NOTE: We don't notify for bubbled notifications (aProgress != this), as
   5921  // BrowsingContextWebProgress independently handles event bubbling in the
   5922  // parent process.
   5923  //
   5924  // NOTE: Tests depend on this happening before UpdateSecurityState.
   5925  if (aProgress == this) {
   5926    if (nsCOMPtr<nsIWebProgressListener> listener = BCWebProgressListener()) {
   5927      listener->OnLocationChange(aProgress, aRequest, aURI, aFlags);
   5928    }
   5929  }
   5930 
   5931  // Since we've now changed Documents, notify the BrowsingContext that we've
   5932  // changed. Ideally we'd just let the BrowsingContext do this when it
   5933  // changes the current window global, but that happens before this and we
   5934  // have a lot of tests that depend on the specific ordering of messages.
   5935  bool isTopLevel = false;
   5936  if (XRE_IsParentProcess() &&
   5937      !(aFlags & nsIWebProgressListener::LOCATION_CHANGE_SAME_DOCUMENT) &&
   5938      NS_SUCCEEDED(aProgress->GetIsTopLevel(&isTopLevel)) && isTopLevel) {
   5939    GetBrowsingContext()->Canonical()->UpdateSecurityState();
   5940  }
   5941  return NS_OK;
   5942 }
   5943 
   5944 void nsDocShell::OnRedirectStateChange(nsIChannel* aOldChannel,
   5945                                       nsIChannel* aNewChannel,
   5946                                       uint32_t aRedirectFlags,
   5947                                       uint32_t aStateFlags) {
   5948  NS_ASSERTION(aStateFlags & STATE_REDIRECTING,
   5949               "Calling OnRedirectStateChange when there is no redirect");
   5950 
   5951  if (!(aStateFlags & STATE_IS_DOCUMENT)) {
   5952    return;  // not a toplevel document
   5953  }
   5954 
   5955  nsCOMPtr<nsIURI> oldURI, newURI;
   5956  aOldChannel->GetURI(getter_AddRefs(oldURI));
   5957  aNewChannel->GetURI(getter_AddRefs(newURI));
   5958  if (!oldURI || !newURI) {
   5959    return;
   5960  }
   5961 
   5962  // DocumentChannel adds redirect chain to global history in the parent
   5963  // process. The redirect chain can't be queried from the content process, so
   5964  // there's no need to update global history here.
   5965  RefPtr<DocumentChannel> docChannel = do_QueryObject(aOldChannel);
   5966  if (!docChannel) {
   5967    // Below a URI visit is saved (see AddURIVisit method doc).
   5968    // The visit chain looks something like:
   5969    //   ...
   5970    //   Site N - 1
   5971    //                =>  Site N
   5972    //   (redirect to =>) Site N + 1 (we are here!)
   5973 
   5974    // Get N - 1 and transition type
   5975    nsCOMPtr<nsIURI> previousURI;
   5976    uint32_t previousFlags = 0;
   5977    ExtractLastVisit(aOldChannel, getter_AddRefs(previousURI), &previousFlags);
   5978 
   5979    if (aRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL ||
   5980        net::ChannelIsPost(aOldChannel)) {
   5981      // 1. Internal redirects are ignored because they are specific to the
   5982      //    channel implementation.
   5983      // 2. POSTs are not saved by global history.
   5984      //
   5985      // Regardless, we need to propagate the previous visit to the new
   5986      // channel.
   5987      SaveLastVisit(aNewChannel, previousURI, previousFlags);
   5988    } else {
   5989      // Get the HTTP response code, if available.
   5990      uint32_t responseStatus = 0;
   5991      nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aOldChannel);
   5992      if (httpChannel) {
   5993        (void)httpChannel->GetResponseStatus(&responseStatus);
   5994      }
   5995 
   5996      // Add visit N -1 => N
   5997      AddURIVisit(oldURI, previousURI, previousFlags, responseStatus);
   5998 
   5999      // Since N + 1 could be the final destination, we will not save N => N + 1
   6000      // here.  OnNewURI will do that, so we will cache it.
   6001      SaveLastVisit(aNewChannel, oldURI, aRedirectFlags);
   6002    }
   6003  }
   6004 
   6005  if (!(aRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL) &&
   6006      mLoadType & (LOAD_CMD_RELOAD | LOAD_CMD_HISTORY)) {
   6007    mLoadType = LOAD_NORMAL_REPLACE;
   6008    SetHistoryEntryAndUpdateBC(Some(nullptr), Nothing());
   6009  }
   6010 }
   6011 
   6012 NS_IMETHODIMP
   6013 nsDocShell::OnStatusChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
   6014                           nsresult aStatus, const char16_t* aMessage) {
   6015  // If we're receiving a notification on ourselves, also notify WebProgress on
   6016  // BrowsingContextWebProgress, potentially over IPC.
   6017  //
   6018  // NOTE: We don't notify for bubbled notifications (aWebProgress != this), as
   6019  // BrowsingContextWebProgress independently handles event bubbling in the
   6020  // parent process.
   6021  if (aWebProgress == this) {
   6022    if (nsCOMPtr<nsIWebProgressListener> listener = BCWebProgressListener()) {
   6023      listener->OnStatusChange(aWebProgress, aRequest, aStatus, aMessage);
   6024    }
   6025  }
   6026 
   6027  return NS_OK;
   6028 }
   6029 
   6030 NS_IMETHODIMP
   6031 nsDocShell::OnSecurityChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
   6032                             uint32_t aState) {
   6033  // If we're receiving a notification on ourselves, also notify WebProgress on
   6034  // BrowsingContextWebProgress, potentially over IPC.
   6035  //
   6036  // NOTE: We don't notify for bubbled notifications (aWebProgress != this), as
   6037  // BrowsingContextWebProgress independently handles event bubbling in the
   6038  // parent process.
   6039  if (aWebProgress == this) {
   6040    if (nsCOMPtr<nsIWebProgressListener> listener = BCWebProgressListener()) {
   6041      listener->OnSecurityChange(aWebProgress, aRequest, aState);
   6042    }
   6043  }
   6044 
   6045  return NS_OK;
   6046 }
   6047 
   6048 NS_IMETHODIMP
   6049 nsDocShell::OnContentBlockingEvent(nsIWebProgress* aWebProgress,
   6050                                   nsIRequest* aRequest, uint32_t aEvent) {
   6051  MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
   6052  return NS_OK;
   6053 }
   6054 
   6055 already_AddRefed<nsIWebProgressListener> nsDocShell::BCWebProgressListener() {
   6056  // If this BrowsingContext has been replaced, we should discard any
   6057  // notifications which would otherwise be delivered in-process.
   6058  if (XRE_IsParentProcess() && mBrowsingContext->Canonical()->IsReplaced()) {
   6059    return nullptr;
   6060  }
   6061 
   6062  // Create a nsBrowserStatusFilter to perform some throttling of
   6063  // OnProgressChange and OnStatusChange notifications which are delivered to
   6064  // our BCWebProgress listener. This reduces the amount of IPC traffic.
   6065  if (!mBCWebProgressStatusFilter && !mIsBeingDestroyed) {
   6066    nsCOMPtr<nsIWebProgressListener> innerListener;
   6067    if (XRE_IsParentProcess()) {
   6068      innerListener = mBrowsingContext->Canonical()->GetWebProgress();
   6069    } else {
   6070      innerListener = do_QueryReferent(mBrowserChild);
   6071    }
   6072    if (innerListener) {
   6073      // NOTE: We need to disable filtering of StateChange events here, as
   6074      // listeners on BrowsingContextWebProgress may depend on state change
   6075      // notifications are otherwise filtered.
   6076      // NOTE: Unlike other nsIWebProgress types, nsBrowserStatusFilter holds a
   6077      // strong cycle-collected reference to the inner listener.
   6078      mBCWebProgressStatusFilter =
   6079          new nsBrowserStatusFilter(/* aDisableStateChangeFilters */ true);
   6080      mBCWebProgressStatusFilter->AddProgressListener(
   6081          innerListener, nsIWebProgress::NOTIFY_ALL);
   6082    }
   6083  }
   6084 
   6085  return do_AddRef(mBCWebProgressStatusFilter);
   6086 }
   6087 
   6088 already_AddRefed<nsIURIFixupInfo> nsDocShell::KeywordToURI(
   6089    const nsACString& aKeyword, bool aIsPrivateContext) {
   6090  nsCOMPtr<nsIURIFixupInfo> info;
   6091  if (!XRE_IsContentProcess()) {
   6092    nsCOMPtr<nsIURIFixup> uriFixup = components::URIFixup::Service();
   6093    if (uriFixup) {
   6094      uriFixup->KeywordToURI(aKeyword, aIsPrivateContext, getter_AddRefs(info));
   6095    }
   6096  }
   6097  return info.forget();
   6098 }
   6099 
   6100 /* static */
   6101 already_AddRefed<nsIURI> nsDocShell::MaybeFixBadCertDomainErrorURI(
   6102    nsIChannel* aChannel, nsIURI* aUrl) {
   6103  if (!aChannel) {
   6104    return nullptr;
   6105  }
   6106 
   6107  nsresult rv = NS_OK;
   6108  nsAutoCString host;
   6109  rv = aUrl->GetAsciiHost(host);
   6110  if (NS_WARN_IF(NS_FAILED(rv))) {
   6111    return nullptr;
   6112  }
   6113 
   6114  // Return if fixup enable pref is turned off.
   6115  if (!mozilla::StaticPrefs::security_bad_cert_domain_error_url_fix_enabled()) {
   6116    return nullptr;
   6117  }
   6118 
   6119  // Return if scheme is not HTTPS.
   6120  if (!aUrl->SchemeIs("https")) {
   6121    return nullptr;
   6122  }
   6123 
   6124  nsCOMPtr<nsILoadInfo> info = aChannel->LoadInfo();
   6125  if (!info) {
   6126    return nullptr;
   6127  }
   6128 
   6129  // Skip doing the fixup if our channel was redirected, because we
   6130  // shouldn't be guessing things about the post-redirect URI.
   6131  if (!info->RedirectChain().IsEmpty()) {
   6132    return nullptr;
   6133  }
   6134 
   6135  int32_t port = 0;
   6136  rv = aUrl->GetPort(&port);
   6137  if (NS_WARN_IF(NS_FAILED(rv))) {
   6138    return nullptr;
   6139  }
   6140 
   6141  // Don't fix up hosts with ports.
   6142  if (port != -1) {
   6143    return nullptr;
   6144  }
   6145 
   6146  // Don't fix up localhost url.
   6147  if (host == "localhost") {
   6148    return nullptr;
   6149  }
   6150 
   6151  // Don't fix up hostnames with IP address.
   6152  if (net_IsValidIPv4Addr(host) || net_IsValidIPv6Addr(host)) {
   6153    return nullptr;
   6154  }
   6155 
   6156  nsAutoCString userPass;
   6157  rv = aUrl->GetUserPass(userPass);
   6158  if (NS_WARN_IF(NS_FAILED(rv))) {
   6159    return nullptr;
   6160  }
   6161 
   6162  // Security - URLs with user / password info should NOT be modified.
   6163  if (!userPass.IsEmpty()) {
   6164    return nullptr;
   6165  }
   6166 
   6167  nsCOMPtr<nsITransportSecurityInfo> tsi;
   6168  rv = aChannel->GetSecurityInfo(getter_AddRefs(tsi));
   6169  if (NS_WARN_IF(NS_FAILED(rv))) {
   6170    return nullptr;
   6171  }
   6172 
   6173  if (NS_WARN_IF(!tsi)) {
   6174    return nullptr;
   6175  }
   6176 
   6177  nsCOMPtr<nsIX509Cert> cert;
   6178  rv = tsi->GetServerCert(getter_AddRefs(cert));
   6179  if (NS_WARN_IF(NS_FAILED(rv) || !cert)) {
   6180    return nullptr;
   6181  }
   6182 
   6183  nsTArray<uint8_t> certBytes;
   6184  rv = cert->GetRawDER(certBytes);
   6185  if (NS_FAILED(rv)) {
   6186    return nullptr;
   6187  }
   6188 
   6189  mozilla::pkix::Input serverCertInput;
   6190  mozilla::pkix::Result result =
   6191      serverCertInput.Init(certBytes.Elements(), certBytes.Length());
   6192  if (result != mozilla::pkix::Success) {
   6193    return nullptr;
   6194  }
   6195 
   6196  constexpr auto wwwPrefix = "www."_ns;
   6197  nsAutoCString newHost;
   6198  if (StringBeginsWith(host, wwwPrefix)) {
   6199    // Try www.example.com -> example.com
   6200    newHost.Assign(Substring(host, wwwPrefix.Length()));
   6201  } else {
   6202    // Try example.com -> www.example.com
   6203    newHost.Assign(wwwPrefix);
   6204    newHost.Append(host);
   6205  }
   6206 
   6207  mozilla::pkix::Input newHostInput;
   6208  result = newHostInput.Init(
   6209      BitwiseCast<const uint8_t*, const char*>(newHost.BeginReading()),
   6210      newHost.Length());
   6211  if (result != mozilla::pkix::Success) {
   6212    return nullptr;
   6213  }
   6214 
   6215  // Because certificate verification returned Result::ERROR_BAD_CERT_DOMAIN /
   6216  // SSL_ERROR_BAD_CERT_DOMAIN, a chain was built and we know whether or not
   6217  // the root was a built-in.
   6218  bool rootIsBuiltIn;
   6219  if (NS_FAILED(tsi->GetIsBuiltCertChainRootBuiltInRoot(&rootIsBuiltIn))) {
   6220    return nullptr;
   6221  }
   6222  mozilla::psm::SkipInvalidSANsForNonBuiltInRootsPolicy nameMatchingPolicy(
   6223      rootIsBuiltIn);
   6224 
   6225  // Check if the certificate is valid for the new hostname.
   6226  result = mozilla::pkix::CheckCertHostname(serverCertInput, newHostInput,
   6227                                            nameMatchingPolicy);
   6228  if (result != mozilla::pkix::Success) {
   6229    return nullptr;
   6230  }
   6231 
   6232  nsCOMPtr<nsIURI> newURI;
   6233  (void)NS_MutateURI(aUrl).SetHost(newHost).Finalize(getter_AddRefs(newURI));
   6234 
   6235  return newURI.forget();
   6236 }
   6237 
   6238 /* static */
   6239 already_AddRefed<nsIURI> nsDocShell::AttemptURIFixup(
   6240    nsIChannel* aChannel, nsresult aStatus,
   6241    const mozilla::Maybe<nsCString>& aOriginalURIString, uint32_t aLoadType,
   6242    bool aIsTopFrame, bool aAllowKeywordFixup, bool aUsePrivateBrowsing,
   6243    bool aNotifyKeywordSearchLoading, nsIInputStream** aNewPostData,
   6244    nsILoadInfo::SchemelessInputType* outSchemelessInput) {
   6245  if (aStatus != NS_ERROR_UNKNOWN_HOST && aStatus != NS_ERROR_NET_RESET &&
   6246      aStatus != NS_ERROR_CONNECTION_REFUSED &&
   6247      aStatus !=
   6248          mozilla::psm::GetXPCOMFromNSSError(SSL_ERROR_BAD_CERT_DOMAIN)) {
   6249    return nullptr;
   6250  }
   6251 
   6252  if (!(aLoadType == LOAD_NORMAL && aIsTopFrame) && !aAllowKeywordFixup) {
   6253    return nullptr;
   6254  }
   6255 
   6256  nsCOMPtr<nsIURI> url;
   6257  nsresult rv = aChannel->GetURI(getter_AddRefs(url));
   6258  if (NS_FAILED(rv)) {
   6259    return nullptr;
   6260  }
   6261 
   6262  //
   6263  // Try and make an alternative URI from the old one
   6264  //
   6265  nsCOMPtr<nsIURI> newURI;
   6266  nsCOMPtr<nsIInputStream> newPostData;
   6267 
   6268  nsAutoCString oldSpec;
   6269  url->GetSpec(oldSpec);
   6270 
   6271  //
   6272  // First try keyword fixup
   6273  //
   6274  nsAutoString keywordProviderName, keywordAsSent;
   6275  if (aStatus == NS_ERROR_UNKNOWN_HOST && aAllowKeywordFixup) {
   6276    // we should only perform a keyword search under the following
   6277    // conditions:
   6278    // (0) Pref keyword.enabled is true
   6279    // (1) the url scheme is http (or https)
   6280    // (2) the url does not have a protocol scheme
   6281    // If we don't enforce such a policy, then we end up doing
   6282    // keyword searchs on urls we don't intend like imap, file,
   6283    // mailbox, etc. This could lead to a security problem where we
   6284    // send data to the keyword server that we shouldn't be.
   6285    // Someone needs to clean up keywords in general so we can
   6286    // determine on a per url basis if we want keywords
   6287    // enabled...this is just a bandaid...
   6288    if (Preferences::GetBool("keyword.enabled", false) &&
   6289        net::SchemeIsHttpOrHttps(url)) {
   6290      bool attemptFixup = false;
   6291      nsAutoCString host;
   6292      (void)url->GetHost(host);
   6293      if (host.FindChar('.') == kNotFound) {
   6294        attemptFixup = true;
   6295      } else {
   6296        // For domains with dots, we check the public suffix validity.
   6297        nsCOMPtr<nsIEffectiveTLDService> tldService =
   6298            do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
   6299        if (tldService) {
   6300          nsAutoCString suffix;
   6301          attemptFixup =
   6302              NS_SUCCEEDED(tldService->GetKnownPublicSuffix(url, suffix)) &&
   6303              suffix.IsEmpty();
   6304        }
   6305      }
   6306      if (attemptFixup) {
   6307        nsCOMPtr<nsIURIFixupInfo> info;
   6308        // only send non-qualified hosts to the keyword server
   6309        if (aOriginalURIString && !aOriginalURIString->IsEmpty()) {
   6310          info = KeywordToURI(*aOriginalURIString, aUsePrivateBrowsing);
   6311        } else {
   6312          //
   6313          // If this string was passed through nsStandardURL by
   6314          // chance, then it may have been converted from UTF-8 to
   6315          // Punycode, which would result in a completely bogus keyword
   6316          // query.  Here we try to recover the original Unicode
   6317          // value, but this is not 100% correct since the value may
   6318          // have been normalized per the IDN normalization rules.
   6319          //
   6320          // Since we don't have access to the exact original string
   6321          // that was entered by the user, this will just have to do.
   6322          nsAutoCString utf8Host;
   6323          mozilla_net_recover_keyword_from_punycode(&host, &utf8Host);
   6324          info = KeywordToURI(utf8Host, aUsePrivateBrowsing);
   6325        }
   6326        if (info) {
   6327          info->GetPreferredURI(getter_AddRefs(newURI));
   6328          info->GetSchemelessInput(outSchemelessInput);
   6329          if (newURI) {
   6330            info->GetKeywordAsSent(keywordAsSent);
   6331            info->GetKeywordProviderName(keywordProviderName);
   6332            info->GetPostData(getter_AddRefs(newPostData));
   6333          }
   6334        }
   6335      }
   6336    }
   6337  }
   6338 
   6339  //
   6340  // Now try change the address, e.g. turn http://foo into
   6341  // http://www.foo.com, and if that doesn't work try https with
   6342  // https://foo and https://www.foo.com.
   6343  //
   6344  if (aStatus == NS_ERROR_UNKNOWN_HOST || aStatus == NS_ERROR_NET_RESET) {
   6345    // Skip fixup for anything except a normal document load
   6346    // operation on the topframe.
   6347    bool doCreateAlternate = aLoadType == LOAD_NORMAL && aIsTopFrame;
   6348 
   6349    if (doCreateAlternate) {
   6350      nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
   6351      nsIPrincipal* principal = loadInfo->TriggeringPrincipal();
   6352      // Only do this if our channel was loaded directly by the user from the
   6353      // URL bar or similar (system principal) and not redirected, because we
   6354      // shouldn't be guessing things about links from other sites, or a
   6355      // post-redirect URI.
   6356      doCreateAlternate = principal && principal->IsSystemPrincipal() &&
   6357                          loadInfo->RedirectChain().IsEmpty();
   6358    }
   6359    // Test if keyword lookup produced a new URI or not
   6360    if (doCreateAlternate && newURI) {
   6361      bool sameURI = false;
   6362      url->Equals(newURI, &sameURI);
   6363      if (!sameURI) {
   6364        // Keyword lookup made a new URI so no need to try
   6365        // an alternate one.
   6366        doCreateAlternate = false;
   6367      }
   6368    }
   6369    if (doCreateAlternate) {
   6370      newURI = nullptr;
   6371      newPostData = nullptr;
   6372      keywordProviderName.Truncate();
   6373      keywordAsSent.Truncate();
   6374      nsCOMPtr<nsIURIFixup> uriFixup = components::URIFixup::Service();
   6375      if (uriFixup) {
   6376        nsCOMPtr<nsIURIFixupInfo> fixupInfo;
   6377        uriFixup->GetFixupURIInfo(oldSpec, nsIURIFixup::FIXUP_FLAG_NONE,
   6378                                  getter_AddRefs(fixupInfo));
   6379        if (fixupInfo) {
   6380          fixupInfo->GetPreferredURI(getter_AddRefs(newURI));
   6381        }
   6382      }
   6383    }
   6384  } else if (aStatus == NS_ERROR_CONNECTION_REFUSED &&
   6385             Preferences::GetBool("browser.fixup.fallback-to-https", false)) {
   6386    // Try HTTPS, since http didn't work
   6387    if (url->SchemeIs("http")) {
   6388      int32_t port = 0;
   6389      url->GetPort(&port);
   6390 
   6391      // Fall back to HTTPS only if port is default
   6392      if (port == -1) {
   6393        newURI = nullptr;
   6394        newPostData = nullptr;
   6395        (void)NS_MutateURI(url)
   6396            .SetScheme("https"_ns)
   6397            .Finalize(getter_AddRefs(newURI));
   6398      }
   6399    }
   6400  }
   6401 
   6402  // If we have a SSL_ERROR_BAD_CERT_DOMAIN error, try adding or removing
   6403  // "www." to/from the beginning of the domain name to see if we can avoid
   6404  // showing the cert error page. For example, https://example.com ->
   6405  // https://www.example.com or https://www.example.com -> https://example.com.
   6406  if (aStatus ==
   6407      mozilla::psm::GetXPCOMFromNSSError(SSL_ERROR_BAD_CERT_DOMAIN)) {
   6408    newPostData = nullptr;
   6409    newURI = MaybeFixBadCertDomainErrorURI(aChannel, url);
   6410  }
   6411 
   6412  // Did we make a new URI that is different to the old one? If so
   6413  // load it.
   6414  //
   6415  if (newURI) {
   6416    // Make sure the new URI is different from the old one,
   6417    // otherwise there's little point trying to load it again.
   6418    bool sameURI = false;
   6419    url->Equals(newURI, &sameURI);
   6420    if (!sameURI) {
   6421      if (aNewPostData) {
   6422        newPostData.forget(aNewPostData);
   6423      }
   6424      if (aNotifyKeywordSearchLoading) {
   6425        // This notification is meant for Firefox Health Report so it
   6426        // can increment counts from the search engine
   6427        MaybeNotifyKeywordSearchLoading(keywordProviderName, keywordAsSent);
   6428      }
   6429      return newURI.forget();
   6430    }
   6431  }
   6432 
   6433  return nullptr;
   6434 }
   6435 
   6436 nsresult nsDocShell::FilterStatusForErrorPage(
   6437    nsresult aStatus, nsIChannel* aChannel, uint32_t aLoadType,
   6438    bool aIsTopFrame, bool aUseErrorPages,
   6439    bool* aSkippedUnknownProtocolNavigation) {
   6440  // Errors to be shown only on top-level frames
   6441  if ((aStatus == NS_ERROR_UNKNOWN_HOST ||
   6442       aStatus == NS_ERROR_CONNECTION_REFUSED ||
   6443       aStatus == NS_ERROR_UNKNOWN_PROXY_HOST ||
   6444       aStatus == NS_ERROR_PROXY_CONNECTION_REFUSED ||
   6445       aStatus == NS_ERROR_PROXY_FORBIDDEN ||
   6446       aStatus == NS_ERROR_PROXY_NOT_IMPLEMENTED ||
   6447       aStatus == NS_ERROR_PROXY_AUTHENTICATION_FAILED ||
   6448       aStatus == NS_ERROR_PROXY_TOO_MANY_REQUESTS ||
   6449       aStatus == NS_ERROR_MALFORMED_URI ||
   6450       aStatus == NS_ERROR_HARMFULADDON_URI ||
   6451       aStatus == NS_ERROR_BLOCKED_BY_POLICY ||
   6452       aStatus == NS_ERROR_DOM_COOP_FAILED ||
   6453       aStatus == NS_ERROR_DOM_COEP_FAILED ||
   6454       aStatus == NS_ERROR_DOM_INVALID_HEADER_VALUE) &&
   6455      (aIsTopFrame || aUseErrorPages)) {
   6456    return aStatus;
   6457  }
   6458 
   6459  if (aStatus == NS_ERROR_NET_TIMEOUT ||
   6460      aStatus == NS_ERROR_NET_TIMEOUT_EXTERNAL ||
   6461      aStatus == NS_ERROR_NET_EMPTY_RESPONSE ||
   6462      aStatus == NS_ERROR_NET_ERROR_RESPONSE ||
   6463      aStatus == NS_ERROR_PROXY_GATEWAY_TIMEOUT ||
   6464      aStatus == NS_ERROR_REDIRECT_LOOP ||
   6465      aStatus == NS_ERROR_UNKNOWN_SOCKET_TYPE ||
   6466      aStatus == NS_ERROR_NET_INTERRUPT || aStatus == NS_ERROR_NET_RESET ||
   6467      aStatus == NS_ERROR_PROXY_BAD_GATEWAY || aStatus == NS_ERROR_OFFLINE ||
   6468      aStatus == NS_ERROR_MALWARE_URI || aStatus == NS_ERROR_PHISHING_URI ||
   6469      aStatus == NS_ERROR_UNWANTED_URI || aStatus == NS_ERROR_HARMFUL_URI ||
   6470      aStatus == NS_ERROR_UNSAFE_CONTENT_TYPE ||
   6471      aStatus == NS_ERROR_INTERCEPTION_FAILED ||
   6472      aStatus == NS_ERROR_NET_INADEQUATE_SECURITY ||
   6473      aStatus == NS_ERROR_NET_HTTP2_SENT_GOAWAY ||
   6474      aStatus == NS_ERROR_NET_HTTP3_PROTOCOL_ERROR ||
   6475      aStatus == NS_ERROR_BASIC_HTTP_AUTH_DISABLED ||
   6476      aStatus == NS_ERROR_DOM_BAD_URI || aStatus == NS_ERROR_FILE_NOT_FOUND ||
   6477      aStatus == NS_ERROR_FILE_ACCESS_DENIED ||
   6478      aStatus == NS_ERROR_CORRUPTED_CONTENT ||
   6479      aStatus == NS_ERROR_INVALID_CONTENT_ENCODING ||
   6480      NS_ERROR_GET_MODULE(aStatus) == NS_ERROR_MODULE_TOR ||
   6481      NS_ERROR_GET_MODULE(aStatus) == NS_ERROR_MODULE_SECURITY) {
   6482    // Errors to be shown for any frame
   6483    return aStatus;
   6484  }
   6485 
   6486  if (aStatus == NS_ERROR_UNKNOWN_PROTOCOL) {
   6487    // For unknown protocols we only display an error if the load is triggered
   6488    // by the browser itself. Showing the error for page-triggered navigations
   6489    // causes annoying behavior for users when a page tries to open an external
   6490    // app which has not been installed, see bug 1528305. A missing WebExtension
   6491    // protocol handlers will however always load the error page, as it is not
   6492    // expected to be opened externally, see bug 1921426.
   6493    nsCOMPtr<nsILoadInfo> info = aChannel->LoadInfo();
   6494    if (!info->TriggeringPrincipal()->IsSystemPrincipal() &&
   6495        !BasePrincipal::Cast(info->TriggeringPrincipal())->AddonPolicy()) {
   6496      if (aSkippedUnknownProtocolNavigation) {
   6497        *aSkippedUnknownProtocolNavigation = true;
   6498      }
   6499      return NS_OK;
   6500    }
   6501    return aStatus;
   6502  }
   6503 
   6504  if (aStatus == NS_ERROR_DOCUMENT_NOT_CACHED) {
   6505    // Non-caching channels will simply return NS_ERROR_OFFLINE.
   6506    // Caching channels would have to look at their flags to work
   6507    // out which error to return. Or we can fix up the error here.
   6508    if (!(aLoadType & LOAD_CMD_HISTORY)) {
   6509      return NS_ERROR_OFFLINE;
   6510    }
   6511    return aStatus;
   6512  }
   6513 
   6514  return NS_OK;
   6515 }
   6516 
   6517 nsresult nsDocShell::EndPageLoad(nsIWebProgress* aProgress,
   6518                                 nsIChannel* aChannel, nsresult aStatus) {
   6519  MOZ_LOG(gDocShellLeakLog, LogLevel::Debug,
   6520          ("DOCSHELL %p EndPageLoad status: %" PRIx32 "\n", this,
   6521           static_cast<uint32_t>(aStatus)));
   6522  if (!aChannel) {
   6523    return NS_ERROR_NULL_POINTER;
   6524  }
   6525 
   6526  // Make sure to discard the initial client if we never created the initial
   6527  // about:blank document.  Do this before possibly returning from the method
   6528  // due to an error.
   6529  mInitialClientSource.reset();
   6530 
   6531  nsCOMPtr<nsIConsoleReportCollector> reporter = do_QueryInterface(aChannel);
   6532  if (reporter) {
   6533    nsCOMPtr<nsILoadGroup> loadGroup;
   6534    aChannel->GetLoadGroup(getter_AddRefs(loadGroup));
   6535    if (loadGroup) {
   6536      reporter->FlushConsoleReports(loadGroup);
   6537    } else {
   6538      reporter->FlushConsoleReports(GetDocument());
   6539    }
   6540  }
   6541 
   6542  nsCOMPtr<nsIURI> url;
   6543  nsresult rv = aChannel->GetURI(getter_AddRefs(url));
   6544  if (NS_FAILED(rv)) {
   6545    return rv;
   6546  }
   6547 
   6548  nsCOMPtr<nsITimedChannel> timingChannel = do_QueryInterface(aChannel);
   6549  if (timingChannel) {
   6550    TimeStamp channelCreationTime;
   6551    rv = timingChannel->GetChannelCreation(&channelCreationTime);
   6552    if (NS_SUCCEEDED(rv) && !channelCreationTime.IsNull()) {
   6553      glean::performance_page::total_content_page_load.AccumulateRawDuration(
   6554          TimeStamp::Now() - channelCreationTime);
   6555    }
   6556  }
   6557 
   6558  // Timing is picked up by the window, we don't need it anymore
   6559  mTiming = nullptr;
   6560 
   6561  // clean up reload state for meta charset
   6562  if (eCharsetReloadRequested == mCharsetReloadState) {
   6563    mCharsetReloadState = eCharsetReloadStopOrigional;
   6564  } else {
   6565    mCharsetReloadState = eCharsetReloadInit;
   6566  }
   6567 
   6568  // Save a pointer to the currently-loading history entry.
   6569  // nsDocShell::EndPageLoad will clear mLSHE, but we may need this history
   6570  // entry further down in this method.
   6571  nsCOMPtr<nsISHEntry> loadingSHE = mLSHE;
   6572  (void)loadingSHE;
   6573  //
   6574  // one of many safeguards that prevent death and destruction if
   6575  // someone is so very very rude as to bring this window down
   6576  // during this load handler.
   6577  //
   6578  nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
   6579 
   6580  // Notify the DocumentViewer that the Document has finished loading.  This
   6581  // will cause any OnLoad(...) and PopState(...) handlers to fire.
   6582  if (!mEODForCurrentDocument && mDocumentViewer) {
   6583    mIsExecutingOnLoadHandler = true;
   6584    nsCOMPtr<nsIDocumentViewer> viewer = mDocumentViewer;
   6585    viewer->LoadComplete(aStatus);
   6586    mIsExecutingOnLoadHandler = false;
   6587 
   6588    mEODForCurrentDocument = true;
   6589  }
   6590  /* Check if the httpChannel has any cache-control related response headers,
   6591   * like no-store, no-cache. If so, update SHEntry so that
   6592   * when a user goes back/forward to this page, we appropriately do
   6593   * form value restoration or load from server.
   6594   */
   6595  nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
   6596  if (!httpChannel) {
   6597    // HttpChannel could be hiding underneath a Multipart channel.
   6598    GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
   6599  }
   6600 
   6601  if (httpChannel) {
   6602    // figure out if SH should be saving layout state.
   6603    bool discardLayoutState = ShouldDiscardLayoutState(httpChannel);
   6604    if (mLSHE && discardLayoutState && (mLoadType & LOAD_CMD_NORMAL) &&
   6605        (mLoadType != LOAD_BYPASS_HISTORY) && (mLoadType != LOAD_ERROR_PAGE)) {
   6606      mLSHE->SetSaveLayoutStateFlag(false);
   6607    }
   6608  }
   6609 
   6610  // Clear mLSHE after calling the onLoadHandlers. This way, if the
   6611  // onLoadHandler tries to load something different in
   6612  // itself or one of its children, we can deal with it appropriately.
   6613  if (mLSHE) {
   6614    mLSHE->SetLoadType(LOAD_HISTORY);
   6615 
   6616    // Clear the mLSHE reference to indicate document loading is done one
   6617    // way or another.
   6618    SetHistoryEntryAndUpdateBC(Some(nullptr), Nothing());
   6619  }
   6620  mActiveEntryIsLoadingFromSessionHistory = false;
   6621 
   6622  // if there's a refresh header in the channel, this method
   6623  // will set it up for us.
   6624  if (mBrowsingContext->IsActive() || !mDisableMetaRefreshWhenInactive)
   6625    RefreshURIFromQueue();
   6626 
   6627  // Test whether this is the top frame or a subframe
   6628  bool isTopFrame = mBrowsingContext->IsTop();
   6629 
   6630  bool hadErrorStatus = false;
   6631  // If status code indicates an error it means that DocumentChannel already
   6632  // tried to fixup the uri and failed. Throw an error dialog box here.
   6633  if (NS_FAILED(aStatus)) {
   6634    // If we got CONTENT_BLOCKED from EndPageLoad, then we need to fire
   6635    // the error event to our embedder, since tests are relying on this.
   6636    // The error event is usually fired by the caller of InternalLoad, but
   6637    // this particular error can happen asynchronously.
   6638    // Bug 1629201 is filed for having much clearer decision making around
   6639    // which cases need error events.
   6640    bool fireFrameErrorEvent = (aStatus == NS_ERROR_CONTENT_BLOCKED_SHOW_ALT ||
   6641                                aStatus == NS_ERROR_CONTENT_BLOCKED);
   6642    UnblockEmbedderLoadEventForFailure(fireFrameErrorEvent);
   6643 
   6644    bool skippedUnknownProtocolNavigation = false;
   6645    aStatus = FilterStatusForErrorPage(aStatus, aChannel, mLoadType, isTopFrame,
   6646                                       mBrowsingContext->GetUseErrorPages(),
   6647                                       &skippedUnknownProtocolNavigation);
   6648    hadErrorStatus = true;
   6649    if (NS_FAILED(aStatus)) {
   6650      if (!mIsBeingDestroyed) {
   6651        DisplayLoadError(aStatus, url, nullptr, aChannel);
   6652      }
   6653    } else if (skippedUnknownProtocolNavigation) {
   6654      nsAutoCString sanitized;
   6655      nsTArray<nsString> params;
   6656      if (NS_SUCCEEDED(NS_GetSanitizedURIStringFromURI(url, sanitized))) {
   6657        params.AppendElement(NS_ConvertUTF8toUTF16(sanitized));
   6658      } else {
   6659        params.AppendElement(u"(unknown uri)"_ns);
   6660      }
   6661      nsContentUtils::ReportToConsole(
   6662          nsIScriptError::warningFlag, "DOM"_ns, GetExtantDocument(),
   6663          nsContentUtils::eDOM_PROPERTIES, "UnknownProtocolNavigationPrevented",
   6664          params);
   6665    }
   6666  }
   6667 
   6668  if (hadErrorStatus) {
   6669    // Don't send session store updates if the reason EndPageLoad was called is
   6670    // because we are process switching. Sometimes the update takes too long and
   6671    // incorrectly overrides session store data from the following load.
   6672    return NS_OK;
   6673  }
   6674  if (SessionStorePlatformCollection()) {
   6675    if (WindowContext* windowContext =
   6676            mBrowsingContext->GetCurrentWindowContext()) {
   6677      using Change = SessionStoreChangeListener::Change;
   6678 
   6679      // We've finished loading the page and now we want to collect all the
   6680      // session store state that the page is initialized with.
   6681      SessionStoreChangeListener::CollectSessionStoreData(
   6682          windowContext,
   6683          EnumSet<Change>(Change::Input, Change::Scroll, Change::SessionHistory,
   6684                          Change::WireFrame));
   6685    }
   6686  }
   6687 
   6688  return NS_OK;
   6689 }
   6690 
   6691 //*****************************************************************************
   6692 // nsDocShell: Content Viewer Management
   6693 //*****************************************************************************
   6694 
   6695 bool nsDocShell::VerifyDocumentViewer() {
   6696  if (mDocumentViewer) {
   6697    return true;
   6698  }
   6699  if (mIsBeingDestroyed) {
   6700    return false;
   6701  }
   6702  if (!mInitialized) {
   6703    // The viewer should be created during docshell initialization. If something
   6704    // wants a viewer or document, it has to initialize the docshell first.
   6705    MOZ_ASSERT_UNREACHABLE(
   6706        "The docshell should be initialized to get a viewer.");
   6707  } else {
   6708    NS_WARNING("No document viewer, docshell failed to initialize.");
   6709  }
   6710  return false;
   6711 }
   6712 
   6713 nsresult nsDocShell::CreateInitialDocumentViewer(
   6714    nsIOpenWindowInfo* aOpenWindowInfo,
   6715    mozilla::dom::WindowGlobalChild* aWindowActor) {
   6716  if (mIsBeingDestroyed) {
   6717    return NS_ERROR_FAILURE;
   6718  }
   6719  MOZ_ASSERT(!mDocumentViewer);
   6720  MOZ_ASSERT(aOpenWindowInfo, "Why don't we have openwindowinfo?");
   6721 
   6722  // Previously, CreateDocumentViewerForActor would've used the actor's
   6723  // principal.
   6724  MOZ_ASSERT_IF(aWindowActor,
   6725                aWindowActor->DocumentPrincipal() ==
   6726                    aOpenWindowInfo->PrincipalToInheritForAboutBlank());
   6727  MOZ_ASSERT_IF(
   6728      aWindowActor,
   6729      aWindowActor->DocumentPrincipal() ==
   6730          aOpenWindowInfo->PartitionedPrincipalToInheritForAboutBlank());
   6731 
   6732  nsresult rv = CreateAboutBlankDocumentViewer(
   6733      aOpenWindowInfo->PrincipalToInheritForAboutBlank(),
   6734      aOpenWindowInfo->PartitionedPrincipalToInheritForAboutBlank(),
   6735      aOpenWindowInfo->PolicyContainerToInheritForAboutBlank(),
   6736      aOpenWindowInfo->BaseUriToInheritForAboutBlank(),
   6737      /* aIsInitialDocument */ true,
   6738      aOpenWindowInfo->CoepToInheritForAboutBlank(),
   6739      /* aTryToSaveOldPresentation */ true,
   6740      /* aCheckPermitUnload */ true, aWindowActor);
   6741 
   6742  NS_ENSURE_STATE(mDocumentViewer);
   6743 
   6744  if (NS_SUCCEEDED(rv)) {
   6745    RefPtr<Document> doc(GetDocument());
   6746    MOZ_ASSERT(doc,
   6747               "Should have doc if CreateAboutBlankDocumentViewer "
   6748               "succeeded!");
   6749    MOZ_ASSERT(doc->IsInitialDocument(), "Document should be initial document");
   6750 
   6751    // Documents created using CreateInitialDocumentViewer may be transient
   6752    // placeholders created by framescripts before content has a
   6753    // chance to load. In some cases, window.open(..., "noopener")
   6754    // will create such a document and then synchronously tear it
   6755    // down, firing a "pagehide" event. Doing so violates our
   6756    // assertions about DocGroups. It's easier to silence the
   6757    // assertion here than to avoid creating the extra document.
   6758    doc->IgnoreDocGroupMismatches();
   6759  }
   6760 
   6761  return rv;
   6762 }
   6763 
   6764 // Location.ancestorOrigins for about:blank, need special case handling.
   6765 // Particularly in the case for initial about:blank, which does not go through
   6766 // normal code that happen with navigations.
   6767 static void CreateAboutBlankAncestorOriginsForNonTopLevel(Document* aDoc) {
   6768  BrowsingContext* bc = aDoc->GetBrowsingContext();
   6769  MOZ_ASSERT(bc && !bc->IsDiscarded() && bc->GetEmbedderElement());
   6770  // We're not even going to attempt to deal with location.ancestorOrigins stuff
   6771  // in the parent process.
   6772  if (!XRE_IsContentProcess()) {
   6773    return;
   6774  }
   6775 
   6776  const auto* frame = bc->GetEmbedderElement();
   6777  const auto referrerPolicy = frame->GetReferrerPolicyAsEnum();
   6778  // Inform the parent process that it needs to create an internal ancestor
   6779  // origins list for this browsing context `bc`
   6780  (void)ContentChild::GetSingleton()->SendUpdateAncestorOriginsList(bc);
   6781 
   6782  const bool masked = referrerPolicy == ReferrerPolicy::No_referrer;
   6783  BrowsingContext* parent = bc->GetParent();
   6784  MOZ_DIAGNOSTIC_ASSERT(parent && parent->IsInProcess() &&
   6785                        parent->GetExtantDocument());
   6786 
   6787  nsTArray<nsCOMPtr<nsIPrincipal>> ancestorPrincipals;
   6788  constexpr auto getPrincipal =
   6789      [](const BrowsingContext* ctx) -> nsIPrincipal* {
   6790    if (!ctx) {
   6791      return nullptr;
   6792    }
   6793    auto* doc = ctx->GetExtantDocument();
   6794    return doc ? doc->GetPrincipal() : nullptr;
   6795  };
   6796  BrowsingContext* ancestorContextToCopyAncestorListFrom = parent;
   6797 
   6798  // about:blank is different from normal docs.
   6799  // We only care about in-process, same-origin ancestors.
   6800  // Therefore run the algorithm all the way up to the last same-origin doc
   6801  // add that origin to the list, and then append that origin's ancestor origins
   6802  // list
   6803  if (masked) {
   6804    ancestorPrincipals.AppendElement(nullptr);
   6805    // 16.1.1. If ancestorOrigin is same origin with parentDoc's origin, then
   6806    // append a new opaque origin to output.
   6807    auto* parentDocPrincipal = getPrincipal(parent);
   6808    for (auto* ancestor = parent->GetParent(); ancestor;
   6809         ancestor = ancestor->GetParent()) {
   6810      auto* principal = getPrincipal(ancestor);
   6811      if (principal && principal->Equals(parentDocPrincipal)) {
   6812        // same principal, same process, adding nullptr for
   6813        // parentContextToCopyAncestorListFrom
   6814        ancestorContextToCopyAncestorListFrom = ancestor;
   6815        ancestorPrincipals.AppendElement(nullptr);
   6816      } else {
   6817        // 16.1.2 Otherwise, append ancestorOrigin to output and set masked to
   6818        // false.
   6819        // Note: But in the case of about:blank, ancestorOrigin can
   6820        // potentially live in another process. So we stop right before it, and
   6821        // just copy `ancestorContextToCopyAncestorListFrom` list, since the
   6822        // masking steps should finish here.
   6823        break;
   6824      }
   6825    }
   6826  } else {
   6827    ancestorPrincipals.AppendElement(getPrincipal(parent));
   6828  }
   6829 
   6830  nsTArray<nsString> list = ProduceAncestorOriginsList(ancestorPrincipals);
   6831  Document* ancestorDoc =
   6832      ancestorContextToCopyAncestorListFrom->GetExtantDocument();
   6833  MOZ_DIAGNOSTIC_ASSERT(ancestorDoc);
   6834  list.AppendElements(ancestorDoc->GetAncestorOriginsList());
   6835  aDoc->SetAncestorOriginsList(std::move(list));
   6836 }
   6837 
   6838 nsresult nsDocShell::CreateAboutBlankDocumentViewer(
   6839    nsIPrincipal* aPrincipal, nsIPrincipal* aPartitionedPrincipal,
   6840    nsIPolicyContainer* aPolicyContainer, nsIURI* aBaseURI,
   6841    bool aIsInitialDocument,
   6842    const Maybe<nsILoadInfo::CrossOriginEmbedderPolicy>& aCOEP,
   6843    bool aTryToSaveOldPresentation, bool aCheckPermitUnload,
   6844    WindowGlobalChild* aActor) {
   6845  RefPtr<Document> blankDoc;
   6846  nsCOMPtr<nsIDocumentViewer> viewer;
   6847  nsresult rv = NS_ERROR_FAILURE;
   6848 
   6849  PROFILER_MARKER_UNTYPED("CreateAboutBlankDocumentViewer", DOM,
   6850                          MarkerStack::Capture());
   6851 
   6852  MOZ_ASSERT_IF(aActor, aActor->DocumentPrincipal() == aPrincipal);
   6853 
   6854  /* mCreatingDocument should never be true at this point. However, it's
   6855     a theoretical possibility. We want to know about it and make it stop,
   6856     and this sounds like a job for an assertion. */
   6857  NS_ASSERTION(!mCreatingDocument,
   6858               "infinite(?) loop creating document averted");
   6859  if (mCreatingDocument) {
   6860    return NS_ERROR_FAILURE;
   6861  }
   6862 
   6863  if (!mBrowsingContext->AncestorsAreCurrent() ||
   6864      (mozilla::SessionHistoryInParent() && mBrowsingContext->IsInBFCache())) {
   6865    mBrowsingContext->RemoveRootFromBFCacheSync();
   6866    return NS_ERROR_NOT_AVAILABLE;
   6867  }
   6868 
   6869  // mDocumentViewer->PermitUnload may release |this| docshell.
   6870  nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
   6871 
   6872  // Ensure that UsesOriginAgentCluster has been initialized for this
   6873  // BrowsingContextGroup/principal pair before creating the document.
   6874  if (aPrincipal) {
   6875    mBrowsingContext->Group()->EnsureUsesOriginAgentClusterInitialized(
   6876        aPrincipal);
   6877  }
   6878 
   6879  AutoRestore<bool> creatingDocument(mCreatingDocument);
   6880  mCreatingDocument = true;
   6881 
   6882  if (aPrincipal && !aPrincipal->IsSystemPrincipal() &&
   6883      mItemType != typeChrome) {
   6884    if (GetIsTopLevelContentDocShell()) {
   6885      // Bug 1948216 tracks having a FPD for top-level initial about:blank
   6886      MOZ_ASSERT(aPrincipal->OriginAttributesRef().EqualsIgnoringFPD(
   6887          mBrowsingContext->OriginAttributesRef()));
   6888    } else {
   6889      MOZ_ASSERT(aPrincipal->OriginAttributesRef() ==
   6890                 mBrowsingContext->OriginAttributesRef());
   6891    }
   6892  }
   6893 
   6894  // Make sure timing is created.  But first record whether we had it
   6895  // already, so we don't clobber the timing for an in-progress load.
   6896  bool hadTiming = mTiming;
   6897  bool toBeReset = MaybeInitTiming();
   6898  if (mDocumentViewer) {
   6899    if (aCheckPermitUnload) {
   6900      // We've got a content viewer already. Make sure the user
   6901      // permits us to discard the current document and replace it
   6902      // with about:blank. And also ensure we fire the unload events
   6903      // in the current document.
   6904 
   6905      // Unload gets fired first for
   6906      // document loaded from the session history.
   6907      mTiming->NotifyBeforeUnload();
   6908 
   6909      bool okToUnload;
   6910      rv = mDocumentViewer->PermitUnload(&okToUnload);
   6911      if (mIsBeingDestroyed) {
   6912        // unload handler destroyed this docshell.
   6913        return NS_ERROR_NOT_AVAILABLE;
   6914      }
   6915      if (NS_SUCCEEDED(rv) && !okToUnload) {
   6916        // The user chose not to unload the page, interrupt the load.
   6917        MaybeResetInitTiming(toBeReset);
   6918        return NS_ERROR_FAILURE;
   6919      }
   6920      if (mTiming) {
   6921        mTiming->NotifyUnloadAccepted(mCurrentURI);
   6922      }
   6923    }
   6924 
   6925    mSavingOldViewer =
   6926        aTryToSaveOldPresentation &&
   6927        CanSavePresentation(LOAD_NORMAL, nullptr, nullptr,
   6928                            /* aReportBFCacheComboTelemetry */ true);
   6929 
   6930    // Make sure to blow away our mLoadingURI just in case.  No loads
   6931    // from inside this pagehide.
   6932    mLoadingURI = nullptr;
   6933 
   6934    // Stop any in-progress loading, so that we don't accidentally trigger any
   6935    // PageShow notifications from Embed() interrupting our loading below.
   6936    Stop();
   6937 
   6938    // Notify the current document that it is about to be unloaded!!
   6939    //
   6940    // It is important to fire the unload() notification *before* any state
   6941    // is changed within the DocShell - otherwise, javascript will get the
   6942    // wrong information :-(
   6943    //
   6944    (void)FirePageHideNotification(!mSavingOldViewer);
   6945    // pagehide notification might destroy this docshell.
   6946    if (mIsBeingDestroyed) {
   6947      return NS_ERROR_DOCSHELL_DYING;
   6948    }
   6949  }
   6950 
   6951  // Now make sure we don't think we're in the middle of firing unload after
   6952  // this point.  This will make us fire unload when the about:blank document
   6953  // unloads... but that's ok, more or less.  Would be nice if it fired load
   6954  // too, of course.
   6955  mFiredUnloadEvent = false;
   6956 
   6957  nsCOMPtr<nsIDocumentLoaderFactory> docFactory =
   6958      nsContentUtils::FindInternalDocumentViewer("text/html"_ns);
   6959 
   6960  if (docFactory) {
   6961    nsCOMPtr<nsIPrincipal> principal, partitionedPrincipal;
   6962    const uint32_t sandboxFlags =
   6963        mBrowsingContext->GetHasLoadedNonInitialDocument()
   6964            ? mBrowsingContext->GetSandboxFlags()
   6965            : mBrowsingContext->GetInitialSandboxFlags();
   6966    // If we're sandboxed, then create a new null principal. We skip
   6967    // this if we're being created from WindowGlobalChild, since in
   6968    // that case we already have a null principal if required.
   6969    // We can't compare againt the BrowsingContext sandbox flag, since
   6970    // the value was taken when the load initiated and may have since
   6971    // changed.
   6972    if ((sandboxFlags & SANDBOXED_ORIGIN) && !aActor) {
   6973      if (aPrincipal) {
   6974        principal = NullPrincipal::CreateWithInheritedAttributes(aPrincipal);
   6975      } else {
   6976        principal = NullPrincipal::Create(GetOriginAttributes());
   6977      }
   6978      partitionedPrincipal = principal;
   6979    } else {
   6980      principal = aPrincipal;
   6981      partitionedPrincipal = aPartitionedPrincipal;
   6982    }
   6983 
   6984    // We cannot get the foreign partitioned principal for the initial
   6985    // about:blank page. So, we change to check if we need to use the
   6986    // partitioned principal for the service worker here.
   6987    MaybeCreateInitialClientSource(
   6988        StoragePrincipalHelper::ShouldUsePartitionPrincipalForServiceWorker(
   6989            this)
   6990            ? partitionedPrincipal
   6991            : principal);
   6992 
   6993    // generate (about:blank) document to load
   6994    blankDoc = nsContentDLF::CreateBlankDocument(mLoadGroup, principal,
   6995                                                 partitionedPrincipal, this);
   6996    if (blankDoc) {
   6997      // Hack: manually set the policyContainer for the new document
   6998      // Please create an actual copy of the policyContainer (do not share the
   6999      // same reference) otherwise modifying the new container (such as
   7000      // appending a new policy to CSP) within the new document will be
   7001      // incorrectly propagated to the opening doc.
   7002      if (aPolicyContainer) {
   7003        RefPtr<PolicyContainer> policyContainerToInherit =
   7004            new PolicyContainer();
   7005        policyContainerToInherit->InitFromOther(
   7006            PolicyContainer::Cast(aPolicyContainer));
   7007        blankDoc->SetPolicyContainer(policyContainerToInherit);
   7008        nsIContentSecurityPolicy* csp =
   7009            PolicyContainer::GetCSP(policyContainerToInherit);
   7010        if (!csp) {
   7011          csp = new nsCSPContext();
   7012          policyContainerToInherit->SetCSP(csp);
   7013        };
   7014        nsresult rv = csp->SetRequestContextWithDocument(blankDoc);
   7015        if (NS_WARN_IF(NS_FAILED(rv))) {
   7016          return rv;
   7017        }
   7018      }
   7019 
   7020      blankDoc->SetInitialStatus(
   7021          aIsInitialDocument ? Document::InitialStatus::IsInitialUncommitted
   7022                             : Document::InitialStatus::NeverInitial);
   7023 
   7024      blankDoc->SetEmbedderPolicy(aCOEP);
   7025 
   7026      // Hack: set the base URI manually, since this document never
   7027      // got Reset() with a channel.
   7028      blankDoc->SetBaseURI(aBaseURI);
   7029 
   7030      // Copy our sandbox flags to the document. These are immutable
   7031      // after being set here.
   7032      blankDoc->SetSandboxFlags(sandboxFlags);
   7033 
   7034      // We inherit the classification flags from the parent document if the
   7035      // principal matches.
   7036      nsCOMPtr<nsIDocShellTreeItem> parentItem;
   7037      GetInProcessSameTypeParent(getter_AddRefs(parentItem));
   7038      if (parentItem) {
   7039        RefPtr<Document> parentDocument = parentItem->GetDocument();
   7040        if (parentDocument && principal &&
   7041            principal->Equals(parentDocument->NodePrincipal())) {
   7042          blankDoc->SetClassificationFlags(
   7043              parentDocument->GetClassificationFlags());
   7044        }
   7045      }
   7046 
   7047      // create a content viewer for us and the new document
   7048      docFactory->CreateInstanceForDocument(
   7049          NS_ISUPPORTS_CAST(nsIDocShell*, this), blankDoc, "view",
   7050          getter_AddRefs(viewer));
   7051 
   7052      // hook 'em up
   7053      if (viewer) {
   7054        viewer->SetContainer(this);
   7055        if (mLoadingEntry && mBrowsingContext->IsTop()) {
   7056          mLoadingEntry->mInfo.SetTransient();
   7057        }
   7058        rv = Embed(viewer, aActor, true, nullptr, mCurrentURI);
   7059        NS_ENSURE_SUCCESS(rv, rv);
   7060 
   7061        SetCurrentURI(blankDoc->GetDocumentURI(), nullptr,
   7062                      /* aFireLocationChange */ true,
   7063                      /* aIsInitialAboutBlank */ aIsInitialDocument,
   7064                      /* aLocationFlags */ 0);
   7065        rv = mIsBeingDestroyed ? NS_ERROR_NOT_AVAILABLE : NS_OK;
   7066      }
   7067 
   7068      if (Element* embedderElement = blankDoc->GetEmbedderElement()) {
   7069        blankDoc->InitFeaturePolicy(AsVariant(embedderElement));
   7070      } else {
   7071        blankDoc->InitFeaturePolicy(AsVariant(Nothing{}));
   7072      }
   7073 
   7074      // Perform redacted location.ancestorOrigins algorithm for about:blank
   7075      if (BrowsingContext* bc = GetBrowsingContext();
   7076          bc && bc->GetEmbedderElement()) {
   7077        CreateAboutBlankAncestorOriginsForNonTopLevel(blankDoc);
   7078      }
   7079    }
   7080  }
   7081 
   7082  // The transient about:blank viewer doesn't have a session history entry.
   7083  SetHistoryEntryAndUpdateBC(Nothing(), Some(nullptr));
   7084 
   7085  // Clear out our mTiming like we would in EndPageLoad, if we didn't
   7086  // have one before entering this function.
   7087  if (!hadTiming) {
   7088    mTiming = nullptr;
   7089    mBlankTiming = true;
   7090  }
   7091 
   7092  return rv;
   7093 }
   7094 
   7095 NS_IMETHODIMP
   7096 nsDocShell::CreateAboutBlankDocumentViewer(
   7097    nsIPrincipal* aPrincipal, nsIPrincipal* aPartitionedPrincipal,
   7098    nsIPolicyContainer* aPolicyContainer) {
   7099  return CreateAboutBlankDocumentViewer(aPrincipal, aPartitionedPrincipal,
   7100                                        aPolicyContainer, nullptr,
   7101                                        /* aIsInitialDocument */ false);
   7102 }
   7103 
   7104 bool nsDocShell::CanSavePresentation(uint32_t aLoadType,
   7105                                     nsIRequest* aNewRequest,
   7106                                     Document* aNewDocument,
   7107                                     bool aReportBFCacheComboTelemetry) {
   7108  if (!mOSHE) {
   7109    return false;  // no entry to save into
   7110  }
   7111 
   7112  MOZ_ASSERT(!mozilla::SessionHistoryInParent(),
   7113             "mOSHE cannot be non-null with SHIP");
   7114  nsCOMPtr<nsIDocumentViewer> viewer = mOSHE->GetDocumentViewer();
   7115  if (viewer) {
   7116    NS_WARNING("mOSHE already has a content viewer!");
   7117    return false;
   7118  }
   7119 
   7120  // Only save presentation for "normal" loads and link loads.  Anything else
   7121  // probably wants to refetch the page, so caching the old presentation
   7122  // would be incorrect.
   7123  if (aLoadType != LOAD_NORMAL && aLoadType != LOAD_HISTORY &&
   7124      aLoadType != LOAD_LINK && aLoadType != LOAD_STOP_CONTENT &&
   7125      aLoadType != LOAD_STOP_CONTENT_AND_REPLACE &&
   7126      aLoadType != LOAD_ERROR_PAGE) {
   7127    return false;
   7128  }
   7129 
   7130  // If the session history entry has the saveLayoutState flag set to false,
   7131  // then we should not cache the presentation.
   7132  if (!mOSHE->GetSaveLayoutStateFlag()) {
   7133    return false;
   7134  }
   7135 
   7136  // If the document is not done loading, don't cache it.
   7137  if (!mScriptGlobal || mScriptGlobal->IsLoading()) {
   7138    MOZ_LOG(gPageCacheLog, mozilla::LogLevel::Verbose,
   7139            ("Blocked due to document still loading"));
   7140    return false;
   7141  }
   7142 
   7143  if (mScriptGlobal->WouldReuseInnerWindow(aNewDocument)) {
   7144    return false;
   7145  }
   7146 
   7147  // Avoid doing the work of saving the presentation state in the case where
   7148  // the content viewer cache is disabled.
   7149  if (nsSHistory::GetMaxTotalViewers() == 0) {
   7150    return false;
   7151  }
   7152 
   7153  // Don't cache the content viewer if we're in a subframe.
   7154  if (mBrowsingContext->GetParent()) {
   7155    return false;  // this is a subframe load
   7156  }
   7157 
   7158  // If the document does not want its presentation cached, then don't.
   7159  RefPtr<Document> doc = mScriptGlobal->GetExtantDoc();
   7160 
   7161  uint32_t bfCacheCombo = 0;
   7162  bool canSavePresentation =
   7163      doc->CanSavePresentation(aNewRequest, bfCacheCombo, true);
   7164  MOZ_ASSERT_IF(canSavePresentation, bfCacheCombo == 0);
   7165  if (canSavePresentation && doc->IsTopLevelContentDocument()) {
   7166    auto* browsingContextGroup = mBrowsingContext->Group();
   7167    nsTArray<RefPtr<BrowsingContext>>& topLevelContext =
   7168        browsingContextGroup->Toplevels();
   7169 
   7170    for (const auto& browsingContext : topLevelContext) {
   7171      if (browsingContext != mBrowsingContext) {
   7172        if (StaticPrefs::docshell_shistory_bfcache_require_no_opener()) {
   7173          canSavePresentation = false;
   7174        }
   7175        bfCacheCombo |= BFCacheStatus::NOT_ONLY_TOPLEVEL_IN_BCG;
   7176        break;
   7177      }
   7178    }
   7179  }
   7180 
   7181  if (aReportBFCacheComboTelemetry) {
   7182    ReportBFCacheComboTelemetry(bfCacheCombo);
   7183  }
   7184  return doc && canSavePresentation;
   7185 }
   7186 
   7187 /* static */
   7188 void nsDocShell::ReportBFCacheComboTelemetry(uint32_t aCombo) {
   7189  // There are 11 possible reasons to make a request fails to use BFCache
   7190  // (see BFCacheStatus in dom/base/Document.h), and we'd like to record
   7191  // the common combinations for reasons which make requests fail to use
   7192  // BFCache. These combinations are generated based on some local browsings,
   7193  // we need to adjust them when necessary.
   7194  enum BFCacheStatusCombo : uint32_t {
   7195    BFCACHE_SUCCESS,
   7196    NOT_ONLY_TOPLEVEL = mozilla::dom::BFCacheStatus::NOT_ONLY_TOPLEVEL_IN_BCG,
   7197    // If both unload and beforeunload listeners are presented, it'll be
   7198    // recorded as unload
   7199    UNLOAD = mozilla::dom::BFCacheStatus::UNLOAD_LISTENER,
   7200    UNLOAD_REQUEST = mozilla::dom::BFCacheStatus::UNLOAD_LISTENER |
   7201                     mozilla::dom::BFCacheStatus::REQUEST,
   7202    REQUEST = mozilla::dom::BFCacheStatus::REQUEST,
   7203    UNLOAD_REQUEST_PEER = mozilla::dom::BFCacheStatus::UNLOAD_LISTENER |
   7204                          mozilla::dom::BFCacheStatus::REQUEST |
   7205                          mozilla::dom::BFCacheStatus::ACTIVE_PEER_CONNECTION,
   7206    UNLOAD_REQUEST_PEER_MSE =
   7207        mozilla::dom::BFCacheStatus::UNLOAD_LISTENER |
   7208        mozilla::dom::BFCacheStatus::REQUEST |
   7209        mozilla::dom::BFCacheStatus::ACTIVE_PEER_CONNECTION |
   7210        mozilla::dom::BFCacheStatus::CONTAINS_MSE_CONTENT,
   7211    UNLOAD_REQUEST_MSE = mozilla::dom::BFCacheStatus::UNLOAD_LISTENER |
   7212                         mozilla::dom::BFCacheStatus::REQUEST |
   7213                         mozilla::dom::BFCacheStatus::CONTAINS_MSE_CONTENT,
   7214    SUSPENDED_UNLOAD_REQUEST_PEER =
   7215        mozilla::dom::BFCacheStatus::SUSPENDED |
   7216        mozilla::dom::BFCacheStatus::UNLOAD_LISTENER |
   7217        mozilla::dom::BFCacheStatus::REQUEST |
   7218        mozilla::dom::BFCacheStatus::ACTIVE_PEER_CONNECTION,
   7219    REMOTE_SUBFRAMES = mozilla::dom::BFCacheStatus::CONTAINS_REMOTE_SUBFRAMES,
   7220    BEFOREUNLOAD = mozilla::dom::BFCacheStatus::BEFOREUNLOAD_LISTENER,
   7221  };
   7222 
   7223  // Beforeunload is recorded as a blocker only if it is the only one to block
   7224  // bfcache.
   7225  if (aCombo != mozilla::dom::BFCacheStatus::BEFOREUNLOAD_LISTENER) {
   7226    aCombo &= ~mozilla::dom::BFCacheStatus::BEFOREUNLOAD_LISTENER;
   7227  }
   7228  switch (aCombo) {
   7229    case BFCACHE_SUCCESS:
   7230      glean::bfcache::combo.EnumGet(glean::bfcache::ComboLabel::eBfcacheSuccess)
   7231          .Add();
   7232      break;
   7233    case NOT_ONLY_TOPLEVEL:
   7234      if (StaticPrefs::docshell_shistory_bfcache_require_no_opener()) {
   7235        glean::bfcache::combo.EnumGet(glean::bfcache::ComboLabel::eOther).Add();
   7236        break;
   7237      }
   7238      glean::bfcache::combo.EnumGet(glean::bfcache::ComboLabel::eBfcacheSuccess)
   7239          .Add();
   7240      glean::bfcache::combo
   7241          .EnumGet(glean::bfcache::ComboLabel::eSuccessNotToplevel)
   7242          .Add();
   7243      break;
   7244    case UNLOAD:
   7245      glean::bfcache::combo.EnumGet(glean::bfcache::ComboLabel::eUnload).Add();
   7246      break;
   7247    case BEFOREUNLOAD:
   7248      glean::bfcache::combo.EnumGet(glean::bfcache::ComboLabel::eBeforeunload)
   7249          .Add();
   7250      break;
   7251    case UNLOAD_REQUEST:
   7252      glean::bfcache::combo.EnumGet(glean::bfcache::ComboLabel::eUnloadReq)
   7253          .Add();
   7254      break;
   7255    case REQUEST:
   7256      glean::bfcache::combo.EnumGet(glean::bfcache::ComboLabel::eReq).Add();
   7257      break;
   7258    case UNLOAD_REQUEST_PEER:
   7259      glean::bfcache::combo.EnumGet(glean::bfcache::ComboLabel::eUnloadReqPeer)
   7260          .Add();
   7261      break;
   7262    case UNLOAD_REQUEST_PEER_MSE:
   7263      glean::bfcache::combo
   7264          .EnumGet(glean::bfcache::ComboLabel::eUnloadReqPeerMse)
   7265          .Add();
   7266      break;
   7267    case UNLOAD_REQUEST_MSE:
   7268      glean::bfcache::combo.EnumGet(glean::bfcache::ComboLabel::eUnloadReqMse)
   7269          .Add();
   7270      break;
   7271    case SUSPENDED_UNLOAD_REQUEST_PEER:
   7272      glean::bfcache::combo
   7273          .EnumGet(glean::bfcache::ComboLabel::eSpdUnloadReqPeer)
   7274          .Add();
   7275      break;
   7276    case REMOTE_SUBFRAMES:
   7277      glean::bfcache::combo
   7278          .EnumGet(glean::bfcache::ComboLabel::eRemoteSubframes)
   7279          .Add();
   7280      break;
   7281    default:
   7282      glean::bfcache::combo.EnumGet(glean::bfcache::ComboLabel::eOther).Add();
   7283      break;
   7284  }
   7285 };
   7286 
   7287 void nsDocShell::ReattachEditorToWindow(nsISHEntry* aSHEntry) {
   7288  MOZ_ASSERT(!mozilla::SessionHistoryInParent());
   7289  MOZ_ASSERT(!mIsBeingDestroyed);
   7290 
   7291  NS_ASSERTION(!mEditorData,
   7292               "Why reattach an editor when we already have one?");
   7293  NS_ASSERTION(aSHEntry && aSHEntry->HasDetachedEditor(),
   7294               "Reattaching when there's not a detached editor.");
   7295 
   7296  if (mEditorData || !aSHEntry) {
   7297    return;
   7298  }
   7299 
   7300  mEditorData = WrapUnique(aSHEntry->ForgetEditorData());
   7301  if (mEditorData) {
   7302 #ifdef DEBUG
   7303    nsresult rv =
   7304 #endif
   7305        mEditorData->ReattachToWindow(this);
   7306    NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to reattach editing session");
   7307  }
   7308 }
   7309 
   7310 void nsDocShell::DetachEditorFromWindow() {
   7311  if (!mEditorData || mEditorData->WaitingForLoad()) {
   7312    // If there's nothing to detach, or if the editor data is actually set
   7313    // up for the _new_ page that's coming in, don't detach.
   7314    return;
   7315  }
   7316 
   7317  NS_ASSERTION(!mOSHE || !mOSHE->HasDetachedEditor(),
   7318               "Detaching editor when it's already detached.");
   7319 
   7320  nsresult res = mEditorData->DetachFromWindow();
   7321  NS_ASSERTION(NS_SUCCEEDED(res), "Failed to detach editor");
   7322 
   7323  if (NS_SUCCEEDED(res)) {
   7324    // Make mOSHE hold the owning ref to the editor data.
   7325    if (mOSHE) {
   7326      MOZ_ASSERT(!mIsBeingDestroyed || !mOSHE->HasDetachedEditor(),
   7327                 "We should not set the editor data again once after we "
   7328                 "detached the editor data during destroying this docshell");
   7329      mOSHE->SetEditorData(mEditorData.release());
   7330    } else {
   7331      mEditorData = nullptr;
   7332    }
   7333  }
   7334 
   7335 #ifdef DEBUG
   7336  {
   7337    bool isEditable;
   7338    GetEditable(&isEditable);
   7339    NS_ASSERTION(!isEditable,
   7340                 "Window is still editable after detaching editor.");
   7341  }
   7342 #endif  // DEBUG
   7343 }
   7344 
   7345 nsresult nsDocShell::CaptureState() {
   7346  MOZ_ASSERT(!mozilla::SessionHistoryInParent());
   7347 
   7348  if (!mOSHE || mOSHE == mLSHE) {
   7349    // No entry to save into, or we're replacing the existing entry.
   7350    return NS_ERROR_FAILURE;
   7351  }
   7352 
   7353  if (!mScriptGlobal) {
   7354    return NS_ERROR_FAILURE;
   7355  }
   7356 
   7357  nsCOMPtr<nsISupports> windowState = mScriptGlobal->SaveWindowState();
   7358  NS_ENSURE_TRUE(windowState, NS_ERROR_FAILURE);
   7359 
   7360  if (MOZ_UNLIKELY(MOZ_LOG_TEST(gPageCacheLog, LogLevel::Debug))) {
   7361    nsAutoCString spec;
   7362    nsCOMPtr<nsIURI> uri = mOSHE->GetURI();
   7363    if (uri) {
   7364      uri->GetSpec(spec);
   7365    }
   7366    MOZ_LOG(gPageCacheLog, LogLevel::Debug,
   7367            ("Saving presentation into session history, URI: %s", spec.get()));
   7368  }
   7369 
   7370  mOSHE->SetWindowState(windowState);
   7371 
   7372  // Suspend refresh URIs and save off the timer queue
   7373  mOSHE->SetRefreshURIList(mSavedRefreshURIList);
   7374 
   7375  // Capture the current content viewer bounds.
   7376  if (mDocumentViewer) {
   7377    LayoutDeviceIntRect bounds;
   7378    mDocumentViewer->GetBounds(bounds);
   7379    mOSHE->SetViewerBounds(bounds.ToUnknownRect());
   7380  }
   7381 
   7382  // Capture the docshell hierarchy.
   7383  mOSHE->ClearChildShells();
   7384 
   7385  uint32_t childCount = mChildList.Length();
   7386  for (uint32_t i = 0; i < childCount; ++i) {
   7387    nsCOMPtr<nsIDocShellTreeItem> childShell = do_QueryInterface(ChildAt(i));
   7388    NS_ASSERTION(childShell, "null child shell");
   7389 
   7390    mOSHE->AddChildShell(childShell);
   7391  }
   7392 
   7393  return NS_OK;
   7394 }
   7395 
   7396 NS_IMETHODIMP
   7397 nsDocShell::RestorePresentationEvent::Run() {
   7398  MOZ_ASSERT(!mozilla::SessionHistoryInParent());
   7399 
   7400  if (mDocShell && NS_FAILED(mDocShell->RestoreFromHistory())) {
   7401    NS_WARNING("RestoreFromHistory failed");
   7402  }
   7403  return NS_OK;
   7404 }
   7405 
   7406 NS_IMETHODIMP
   7407 nsDocShell::BeginRestore(nsIDocumentViewer* aDocumentViewer, bool aTop) {
   7408  MOZ_ASSERT(!mozilla::SessionHistoryInParent());
   7409 
   7410  nsresult rv;
   7411  if (!aDocumentViewer) {
   7412    NS_ENSURE_TRUE(VerifyDocumentViewer(), NS_ERROR_FAILURE);
   7413 
   7414    aDocumentViewer = mDocumentViewer;
   7415  }
   7416 
   7417  // Dispatch events for restoring the presentation.  We try to simulate
   7418  // the progress notifications loading the document would cause, so we add
   7419  // the document's channel to the loadgroup to initiate stateChange
   7420  // notifications.
   7421 
   7422  RefPtr<Document> doc = aDocumentViewer->GetDocument();
   7423  if (doc) {
   7424    nsIChannel* channel = doc->GetChannel();
   7425    if (channel) {
   7426      mEODForCurrentDocument = false;
   7427      mIsRestoringDocument = true;
   7428      mLoadGroup->AddRequest(channel, nullptr);
   7429      mIsRestoringDocument = false;
   7430    }
   7431  }
   7432 
   7433  if (!aTop) {
   7434    // This point corresponds to us having gotten OnStartRequest or
   7435    // STATE_START, so do the same thing that CreateDocumentViewer does at
   7436    // this point to ensure that unload/pagehide events for this document
   7437    // will fire when it's unloaded again.
   7438    mFiredUnloadEvent = false;
   7439 
   7440    // For non-top frames, there is no notion of making sure that the
   7441    // previous document is in the domwindow when STATE_START notifications
   7442    // happen.  We can just call BeginRestore for all of the child shells
   7443    // now.
   7444    rv = BeginRestoreChildren();
   7445    NS_ENSURE_SUCCESS(rv, rv);
   7446  }
   7447 
   7448  return NS_OK;
   7449 }
   7450 
   7451 nsresult nsDocShell::BeginRestoreChildren() {
   7452  MOZ_ASSERT(!mozilla::SessionHistoryInParent());
   7453 
   7454  for (auto* childDocLoader : mChildList.ForwardRange()) {
   7455    nsCOMPtr<nsIDocShell> child = do_QueryObject(childDocLoader);
   7456    if (child) {
   7457      nsresult rv = child->BeginRestore(nullptr, false);
   7458      NS_ENSURE_SUCCESS(rv, rv);
   7459    }
   7460  }
   7461  return NS_OK;
   7462 }
   7463 
   7464 NS_IMETHODIMP
   7465 nsDocShell::FinishRestore() {
   7466  MOZ_ASSERT(!mozilla::SessionHistoryInParent());
   7467 
   7468  // First we call finishRestore() on our children.  In the simulated load,
   7469  // all of the child frames finish loading before the main document.
   7470 
   7471  for (auto* childDocLoader : mChildList.ForwardRange()) {
   7472    nsCOMPtr<nsIDocShell> child = do_QueryObject(childDocLoader);
   7473    if (child) {
   7474      child->FinishRestore();
   7475    }
   7476  }
   7477 
   7478  if (mOSHE && mOSHE->HasDetachedEditor()) {
   7479    ReattachEditorToWindow(mOSHE);
   7480  }
   7481 
   7482  RefPtr<Document> doc = GetDocument();
   7483  if (doc) {
   7484    // Finally, we remove the request from the loadgroup.  This will
   7485    // cause onStateChange(STATE_STOP) to fire, which will fire the
   7486    // pageshow event to the chrome.
   7487 
   7488    nsIChannel* channel = doc->GetChannel();
   7489    if (channel) {
   7490      mIsRestoringDocument = true;
   7491      mLoadGroup->RemoveRequest(channel, nullptr, NS_OK);
   7492      mIsRestoringDocument = false;
   7493    }
   7494  }
   7495 
   7496  return NS_OK;
   7497 }
   7498 
   7499 NS_IMETHODIMP
   7500 nsDocShell::GetRestoringDocument(bool* aRestoring) {
   7501  *aRestoring = mIsRestoringDocument;
   7502  return NS_OK;
   7503 }
   7504 
   7505 nsresult nsDocShell::RestorePresentation(nsISHEntry* aSHEntry,
   7506                                         bool* aRestoring) {
   7507  MOZ_ASSERT(!mozilla::SessionHistoryInParent());
   7508  MOZ_ASSERT(!mIsBeingDestroyed);
   7509 
   7510  NS_ASSERTION(mLoadType & LOAD_CMD_HISTORY,
   7511               "RestorePresentation should only be called for history loads");
   7512 
   7513  nsCOMPtr<nsIDocumentViewer> viewer = aSHEntry->GetDocumentViewer();
   7514 
   7515  nsAutoCString spec;
   7516  if (MOZ_UNLIKELY(MOZ_LOG_TEST(gPageCacheLog, LogLevel::Debug))) {
   7517    nsCOMPtr<nsIURI> uri = aSHEntry->GetURI();
   7518    if (uri) {
   7519      uri->GetSpec(spec);
   7520    }
   7521  }
   7522 
   7523  *aRestoring = false;
   7524 
   7525  if (!viewer) {
   7526    MOZ_LOG(gPageCacheLog, LogLevel::Debug,
   7527            ("no saved presentation for uri: %s", spec.get()));
   7528    return NS_OK;
   7529  }
   7530 
   7531  // We need to make sure the content viewer's container is this docshell.
   7532  // In subframe navigation, it's possible for the docshell that the
   7533  // content viewer was originally loaded into to be replaced with a
   7534  // different one.  We don't currently support restoring the presentation
   7535  // in that case.
   7536 
   7537  nsCOMPtr<nsIDocShell> container;
   7538  viewer->GetContainer(getter_AddRefs(container));
   7539  if (!::SameCOMIdentity(container, GetAsSupports(this))) {
   7540    MOZ_LOG(gPageCacheLog, LogLevel::Debug,
   7541            ("No valid container, clearing presentation"));
   7542    aSHEntry->SetDocumentViewer(nullptr);
   7543    return NS_ERROR_FAILURE;
   7544  }
   7545 
   7546  NS_ASSERTION(mDocumentViewer != viewer, "Restoring existing presentation");
   7547 
   7548  MOZ_LOG(gPageCacheLog, LogLevel::Debug,
   7549          ("restoring presentation from session history: %s", spec.get()));
   7550 
   7551  SetHistoryEntryAndUpdateBC(Some(aSHEntry), Nothing());
   7552 
   7553  // Post an event that will remove the request after we've returned
   7554  // to the event loop.  This mimics the way it is called by nsIChannel
   7555  // implementations.
   7556 
   7557  // Revoke any pending restore (just in case).
   7558  NS_ASSERTION(!mRestorePresentationEvent.IsPending(),
   7559               "should only have one RestorePresentationEvent");
   7560  mRestorePresentationEvent.Revoke();
   7561 
   7562  RefPtr<RestorePresentationEvent> evt = new RestorePresentationEvent(this);
   7563  nsresult rv = Dispatch(do_AddRef(evt));
   7564  if (NS_SUCCEEDED(rv)) {
   7565    mRestorePresentationEvent = evt.get();
   7566    // The rest of the restore processing will happen on our event
   7567    // callback.
   7568    *aRestoring = true;
   7569  }
   7570 
   7571  return rv;
   7572 }
   7573 
   7574 namespace {
   7575 class MOZ_STACK_CLASS PresentationEventForgetter {
   7576 public:
   7577  explicit PresentationEventForgetter(
   7578      nsRevocableEventPtr<nsDocShell::RestorePresentationEvent>&
   7579          aRestorePresentationEvent)
   7580      : mRestorePresentationEvent(aRestorePresentationEvent),
   7581        mEvent(aRestorePresentationEvent.get()) {}
   7582 
   7583  ~PresentationEventForgetter() { Forget(); }
   7584 
   7585  void Forget() {
   7586    if (mRestorePresentationEvent.get() == mEvent) {
   7587      mRestorePresentationEvent.Forget();
   7588      mEvent = nullptr;
   7589    }
   7590  }
   7591 
   7592 private:
   7593  nsRevocableEventPtr<nsDocShell::RestorePresentationEvent>&
   7594      mRestorePresentationEvent;
   7595  RefPtr<nsDocShell::RestorePresentationEvent> mEvent;
   7596 };
   7597 
   7598 }  // namespace
   7599 
   7600 bool nsDocShell::SandboxFlagsImplyCookies(const uint32_t& aSandboxFlags) {
   7601  return (aSandboxFlags & (SANDBOXED_ORIGIN | SANDBOXED_SCRIPTS)) == 0;
   7602 }
   7603 
   7604 nsresult nsDocShell::RestoreFromHistory() {
   7605  MOZ_ASSERT(!mozilla::SessionHistoryInParent());
   7606  MOZ_ASSERT(mRestorePresentationEvent.IsPending());
   7607  PresentationEventForgetter forgetter(mRestorePresentationEvent);
   7608 
   7609  // This section of code follows the same ordering as CreateDocumentViewer.
   7610  if (!mLSHE) {
   7611    return NS_ERROR_FAILURE;
   7612  }
   7613 
   7614  nsCOMPtr<nsIDocumentViewer> viewer = mLSHE->GetDocumentViewer();
   7615  if (!viewer) {
   7616    return NS_ERROR_FAILURE;
   7617  }
   7618 
   7619  if (mSavingOldViewer) {
   7620    // We determined that it was safe to cache the document presentation
   7621    // at the time we initiated the new load.  We need to check whether
   7622    // it's still safe to do so, since there may have been DOM mutations
   7623    // or new requests initiated.
   7624    RefPtr<Document> doc = viewer->GetDocument();
   7625    nsIRequest* request = nullptr;
   7626    if (doc) {
   7627      request = doc->GetChannel();
   7628    }
   7629    mSavingOldViewer = CanSavePresentation(
   7630        mLoadType, request, doc, /* aReportBFCacheComboTelemetry */ false);
   7631  }
   7632 
   7633  // Protect against mLSHE going away via a load triggered from
   7634  // pagehide or unload.
   7635  nsCOMPtr<nsISHEntry> origLSHE = mLSHE;
   7636 
   7637  // Make sure to blow away our mLoadingURI just in case.  No loads
   7638  // from inside this pagehide.
   7639  mLoadingURI = nullptr;
   7640 
   7641  // Notify the old content viewer that it's being hidden.
   7642  FirePageHideNotification(!mSavingOldViewer);
   7643  // pagehide notification might destroy this docshell.
   7644  if (mIsBeingDestroyed) {
   7645    return NS_ERROR_DOCSHELL_DYING;
   7646  }
   7647 
   7648  // If mLSHE was changed as a result of the pagehide event, then
   7649  // something else was loaded.  Don't finish restoring.
   7650  if (mLSHE != origLSHE) {
   7651    return NS_OK;
   7652  }
   7653 
   7654  // Add the request to our load group.  We do this before swapping out
   7655  // the content viewers so that consumers of STATE_START can access
   7656  // the old document.  We only deal with the toplevel load at this time --
   7657  // to be consistent with normal document loading, subframes cannot start
   7658  // loading until after data arrives, which is after STATE_START completes.
   7659 
   7660  RefPtr<RestorePresentationEvent> currentPresentationRestoration =
   7661      mRestorePresentationEvent.get();
   7662  Stop();
   7663  // Make sure we're still restoring the same presentation.
   7664  // If we aren't, docshell is in process doing another load already.
   7665  NS_ENSURE_STATE(currentPresentationRestoration ==
   7666                  mRestorePresentationEvent.get());
   7667  BeginRestore(viewer, true);
   7668  NS_ENSURE_STATE(currentPresentationRestoration ==
   7669                  mRestorePresentationEvent.get());
   7670  forgetter.Forget();
   7671 
   7672  // Set mFiredUnloadEvent = false so that the unload handler for the
   7673  // *new* document will fire.
   7674  mFiredUnloadEvent = false;
   7675 
   7676  mURIResultedInDocument = true;
   7677  RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
   7678  if (rootSH) {
   7679    mPreviousEntryIndex = rootSH->Index();
   7680    rootSH->LegacySHistory()->UpdateIndex();
   7681    mLoadedEntryIndex = rootSH->Index();
   7682    MOZ_LOG(gPageCacheLog, LogLevel::Verbose,
   7683            ("Previous index: %d, Loaded index: %d", mPreviousEntryIndex,
   7684             mLoadedEntryIndex));
   7685  }
   7686 
   7687  // Rather than call Embed(), we will retrieve the viewer from the session
   7688  // history entry and swap it in.
   7689  // XXX can we refactor this so that we can just call Embed()?
   7690  PersistLayoutHistoryState();
   7691  nsresult rv;
   7692  if (mDocumentViewer) {
   7693    if (mSavingOldViewer && NS_FAILED(CaptureState())) {
   7694      if (mOSHE) {
   7695        mOSHE->SyncPresentationState();
   7696      }
   7697      mSavingOldViewer = false;
   7698    }
   7699  }
   7700 
   7701  mSavedRefreshURIList = nullptr;
   7702 
   7703  // In cases where we use a transient about:blank viewer between loads,
   7704  // we never show the transient viewer, so _its_ previous viewer is never
   7705  // destroyed.  Destroy any such previous viewer now.
   7706  if (mDocumentViewer) {
   7707    // Make sure to hold a strong ref to previousViewer here while we
   7708    // drop the reference to it from mDocumentViewer.
   7709    nsCOMPtr<nsIDocumentViewer> previousViewer =
   7710        mDocumentViewer->GetPreviousViewer();
   7711    if (previousViewer) {
   7712      mDocumentViewer->SetPreviousViewer(nullptr);
   7713      previousViewer->Destroy();
   7714    }
   7715  }
   7716 
   7717  // Save the bounds of the root view's widget.
   7718  LayoutDeviceIntRect newBounds(0, 0, 0, 0);
   7719 
   7720  PresShell* oldPresShell = GetPresShell();
   7721  if (oldPresShell) {
   7722    mDocumentViewer->GetBounds(newBounds);
   7723  }
   7724 
   7725  // Transfer ownership to mDocumentViewer.  By ensuring that either the
   7726  // docshell or the session history, but not both, have references to the
   7727  // content viewer, we prevent the viewer from being torn down after
   7728  // Destroy() is called.
   7729 
   7730  if (mDocumentViewer) {
   7731    mDocumentViewer->Close(mSavingOldViewer ? mOSHE.get() : nullptr);
   7732    viewer->SetPreviousViewer(mDocumentViewer);
   7733  }
   7734  if (mOSHE && (!mDocumentViewer || !mSavingOldViewer)) {
   7735    // We don't plan to save a viewer in mOSHE; tell it to drop
   7736    // any other state it's holding.
   7737    mOSHE->SyncPresentationState();
   7738  }
   7739 
   7740  // Order the mDocumentViewer setup just like Embed does.
   7741  mDocumentViewer = nullptr;
   7742 
   7743  // Now that we're about to switch documents, forget all of our children.
   7744  // Note that we cached them as needed up in CaptureState above.
   7745  DestroyChildren();
   7746 
   7747  mDocumentViewer.swap(viewer);
   7748 
   7749  // Grab all of the related presentation from the SHEntry now.
   7750  // Clearing the viewer from the SHEntry will clear all of this state.
   7751  nsCOMPtr<nsISupports> windowState = mLSHE->GetWindowState();
   7752  mLSHE->SetWindowState(nullptr);
   7753 
   7754  bool sticky = mLSHE->GetSticky();
   7755 
   7756  RefPtr<Document> document = mDocumentViewer->GetDocument();
   7757 
   7758  nsCOMArray<nsIDocShellTreeItem> childShells;
   7759  int32_t i = 0;
   7760  nsCOMPtr<nsIDocShellTreeItem> child;
   7761  while (NS_SUCCEEDED(mLSHE->ChildShellAt(i++, getter_AddRefs(child))) &&
   7762         child) {
   7763    childShells.AppendObject(child);
   7764  }
   7765 
   7766  // get the previous content viewer size
   7767  nsIntRect oldBounds(0, 0, 0, 0);
   7768  mLSHE->GetViewerBounds(oldBounds);
   7769 
   7770  // Restore the refresh URI list.  The refresh timers will be restarted
   7771  // when EndPageLoad() is called.
   7772  nsCOMPtr<nsIMutableArray> refreshURIList = mLSHE->GetRefreshURIList();
   7773 
   7774  // Reattach to the window object.
   7775  mIsRestoringDocument = true;  // for MediaDocument::BecomeInteractive
   7776  rv = mDocumentViewer->Open(windowState, mLSHE);
   7777  mIsRestoringDocument = false;
   7778 
   7779  // Hack to keep nsDocShellEditorData alive across the
   7780  // SetDocumentViewer(nullptr) call below.
   7781  UniquePtr<nsDocShellEditorData> data(mLSHE->ForgetEditorData());
   7782 
   7783  // Now remove it from the cached presentation.
   7784  mLSHE->SetDocumentViewer(nullptr);
   7785  mEODForCurrentDocument = false;
   7786 
   7787  mLSHE->SetEditorData(data.release());
   7788 
   7789 #ifdef DEBUG
   7790  {
   7791    nsCOMPtr<nsIMutableArray> refreshURIs = mLSHE->GetRefreshURIList();
   7792    nsCOMPtr<nsIDocShellTreeItem> childShell;
   7793    mLSHE->ChildShellAt(0, getter_AddRefs(childShell));
   7794    NS_ASSERTION(!refreshURIs && !childShell,
   7795                 "SHEntry should have cleared presentation state");
   7796  }
   7797 #endif
   7798 
   7799  // Restore the sticky state of the viewer.  The viewer has set this state
   7800  // on the history entry in Destroy() just before marking itself non-sticky,
   7801  // to avoid teardown of the presentation.
   7802  mDocumentViewer->SetSticky(sticky);
   7803 
   7804  NS_ENSURE_SUCCESS(rv, rv);
   7805 
   7806  // mLSHE is now our currently-loaded document.
   7807  SetHistoryEntryAndUpdateBC(Nothing(), Some<nsISHEntry*>(mLSHE));
   7808 
   7809  // We aren't going to restore any items from the LayoutHistoryState,
   7810  // but we don't want them to stay around in case the page is reloaded.
   7811  SetLayoutHistoryState(nullptr);
   7812 
   7813  // This is the end of our Embed() replacement
   7814 
   7815  mSavingOldViewer = false;
   7816  mEODForCurrentDocument = false;
   7817 
   7818  if (document) {
   7819    RefPtr<nsDocShell> parent = GetInProcessParentDocshell();
   7820    if (parent) {
   7821      RefPtr<Document> d = parent->GetDocument();
   7822      if (d) {
   7823        if (d->EventHandlingSuppressed()) {
   7824          document->SuppressEventHandling(d->EventHandlingSuppressed());
   7825        }
   7826      }
   7827    }
   7828 
   7829    // Use the uri from the mLSHE we had when we entered this function
   7830    // (which need not match the document's URI if anchors are involved),
   7831    // since that's the history entry we're loading.  Note that if we use
   7832    // origLSHE we don't have to worry about whether the entry in question
   7833    // is still mLSHE or whether it's now mOSHE.
   7834    nsCOMPtr<nsIURI> uri = origLSHE->GetURI();
   7835    SetCurrentURI(uri, document->GetChannel(), /* aFireLocationChange */ true,
   7836                  /* aIsInitialAboutBlank */ false,
   7837                  /* aLocationFlags */ 0);
   7838  }
   7839 
   7840  // This is the end of our CreateDocumentViewer() replacement.
   7841  // Now we simulate a load.  First, we restore the state of the javascript
   7842  // window object.
   7843  nsCOMPtr<nsPIDOMWindowOuter> privWin = GetWindow();
   7844  NS_ASSERTION(privWin, "could not get nsPIDOMWindow interface");
   7845 
   7846  // Now, dispatch a title change event which would happen as the
   7847  // <head> is parsed.
   7848  document->NotifyPossibleTitleChange(false);
   7849 
   7850  // Now we simulate appending child docshells for subframes.
   7851  for (i = 0; i < childShells.Count(); ++i) {
   7852    nsIDocShellTreeItem* childItem = childShells.ObjectAt(i);
   7853    nsCOMPtr<nsIDocShell> childShell = do_QueryInterface(childItem);
   7854 
   7855    // Make sure to not clobber the state of the child.  Since AddChild
   7856    // always clobbers it, save it off first.
   7857    bool allowRedirects;
   7858    childShell->GetAllowMetaRedirects(&allowRedirects);
   7859 
   7860    bool allowSubframes;
   7861    childShell->GetAllowSubframes(&allowSubframes);
   7862 
   7863    bool allowImages;
   7864    childShell->GetAllowImages(&allowImages);
   7865 
   7866    bool allowMedia = childShell->GetAllowMedia();
   7867 
   7868    bool allowDNSPrefetch;
   7869    childShell->GetAllowDNSPrefetch(&allowDNSPrefetch);
   7870 
   7871    bool allowContentRetargeting = childShell->GetAllowContentRetargeting();
   7872    bool allowContentRetargetingOnChildren =
   7873        childShell->GetAllowContentRetargetingOnChildren();
   7874 
   7875    // this.AddChild(child) calls child.SetDocLoaderParent(this), meaning that
   7876    // the child inherits our state. Among other things, this means that the
   7877    // child inherits our mPrivateBrowsingId, which is what we want.
   7878    AddChild(childItem);
   7879 
   7880    childShell->SetAllowMetaRedirects(allowRedirects);
   7881    childShell->SetAllowSubframes(allowSubframes);
   7882    childShell->SetAllowImages(allowImages);
   7883    childShell->SetAllowMedia(allowMedia);
   7884    childShell->SetAllowDNSPrefetch(allowDNSPrefetch);
   7885    childShell->SetAllowContentRetargeting(allowContentRetargeting);
   7886    childShell->SetAllowContentRetargetingOnChildren(
   7887        allowContentRetargetingOnChildren);
   7888 
   7889    rv = childShell->BeginRestore(nullptr, false);
   7890    NS_ENSURE_SUCCESS(rv, rv);
   7891  }
   7892 
   7893  // Make sure to restore the window state after adding the child shells back
   7894  // to the tree.  This is necessary for Thaw() and Resume() to propagate
   7895  // properly.
   7896  rv = privWin->RestoreWindowState(windowState);
   7897  NS_ENSURE_SUCCESS(rv, rv);
   7898 
   7899  RefPtr<PresShell> presShell = GetPresShell();
   7900 
   7901  // We may be displayed on a different monitor (or in a different
   7902  // HiDPI mode) than when we got into the history list.  So we need
   7903  // to check if this has happened. See bug 838239.
   7904 
   7905  // Because the prescontext normally handles resolution changes via
   7906  // a runnable (see nsPresContext::UIResolutionChanged), its device
   7907  // context won't be -immediately- updated as a result of calling
   7908  // presShell->BackingScaleFactorChanged().
   7909 
   7910  // But we depend on that device context when adjusting the view size
   7911  // via mDocumentViewer->SetBounds(newBounds) below. So we need to
   7912  // explicitly tell it to check for changed resolution here.
   7913  if (presShell) {
   7914    RefPtr<nsPresContext> pc = presShell->GetPresContext();
   7915    if (pc->DeviceContext()->CheckDPIChange()) {
   7916      presShell->BackingScaleFactorChanged();
   7917    }
   7918    // Recompute zoom and text-zoom and such.
   7919    pc->RecomputeBrowsingContextDependentData();
   7920  }
   7921 
   7922  nsCOMPtr<nsPIDOMWindowInner> privWinInner = privWin->GetCurrentInnerWindow();
   7923 
   7924  // If parent is suspended, increase suspension count.
   7925  // This can't be done as early as event suppression since this
   7926  // depends on docshell tree.
   7927  privWinInner->SyncStateFromParentWindow();
   7928 
   7929  // Now that all of the child docshells have been put into place, we can
   7930  // restart the timers for the window and all of the child frames.
   7931  privWinInner->Resume();
   7932 
   7933  // Now that we have found the inner window of the page restored
   7934  // from the history, we have to  make sure that
   7935  // performance.navigation.type is 2.
   7936  Performance* performance = privWinInner->GetPerformance();
   7937  if (performance) {
   7938    performance->GetDOMTiming()->NotifyRestoreStart();
   7939  }
   7940 
   7941  // Restore the refresh URI list.  The refresh timers will be restarted
   7942  // when EndPageLoad() is called.
   7943  mRefreshURIList = refreshURIList;
   7944 
   7945  // Meta-refresh timers have been restarted for this shell, but not
   7946  // for our children.  Walk the child shells and restart their timers.
   7947  for (auto* childDocLoader : mChildList.ForwardRange()) {
   7948    nsCOMPtr<nsIDocShell> child = do_QueryObject(childDocLoader);
   7949    if (child) {
   7950      child->ResumeRefreshURIs();
   7951    }
   7952  }
   7953 
   7954  // Make sure this presentation is the same size as the previous
   7955  // presentation.  If this is not the same size we showed it at last time,
   7956  // then we need to resize the widget.
   7957 
   7958  if (presShell) {
   7959    if (!newBounds.IsEmpty() &&
   7960        !newBounds.ToUnknownRect().IsEqualEdges(oldBounds)) {
   7961      MOZ_LOG(gPageCacheLog, LogLevel::Debug,
   7962              ("resize widget(%d, %d, %d, %d)", newBounds.x, newBounds.y,
   7963               newBounds.width, newBounds.height));
   7964      mDocumentViewer->SetBounds(newBounds);
   7965    } else if (ScrollContainerFrame* sf =
   7966                   presShell->GetRootScrollContainerFrame()) {
   7967      sf->PostScrolledAreaEventForCurrentArea();
   7968    }
   7969  }
   7970 
   7971  // If the IsUnderHiddenEmbedderElement() state has been changed, we need to
   7972  // update it.
   7973  if (oldPresShell && presShell &&
   7974      presShell->IsUnderHiddenEmbedderElement() !=
   7975          oldPresShell->IsUnderHiddenEmbedderElement()) {
   7976    presShell->SetIsUnderHiddenEmbedderElement(
   7977        oldPresShell->IsUnderHiddenEmbedderElement());
   7978  }
   7979 
   7980  // Simulate the completion of the load.
   7981  nsDocShell::FinishRestore();
   7982 
   7983  // Restart plugins, and paint the content.
   7984  if (presShell) {
   7985    presShell->Thaw();
   7986  }
   7987 
   7988  return privWin->FireDelayedDOMEvents(true);
   7989 }
   7990 
   7991 nsresult nsDocShell::CreateDocumentViewer(const nsACString& aContentType,
   7992                                          nsIRequest* aRequest,
   7993                                          nsIStreamListener** aContentHandler) {
   7994  *aContentHandler = nullptr;
   7995 
   7996  if (!mTreeOwner || mIsBeingDestroyed) {
   7997    // If we don't have a tree owner, then we're in the process of being
   7998    // destroyed. Rather than continue trying to load something, just give up.
   7999    return NS_ERROR_DOCSHELL_DYING;
   8000  }
   8001 
   8002  if (!mBrowsingContext->AncestorsAreCurrent() ||
   8003      mBrowsingContext->IsInBFCache()) {
   8004    mBrowsingContext->RemoveRootFromBFCacheSync();
   8005    return NS_ERROR_NOT_AVAILABLE;
   8006  }
   8007 
   8008  // Can we check the content type of the current content viewer
   8009  // and reuse it without destroying it and re-creating it?
   8010 
   8011  NS_ASSERTION(mLoadGroup, "Someone ignored return from Init()?");
   8012 
   8013  // Instantiate the content viewer object
   8014  nsCOMPtr<nsIDocumentViewer> viewer;
   8015  nsresult rv = NewDocumentViewerObj(aContentType, aRequest, mLoadGroup,
   8016                                     aContentHandler, getter_AddRefs(viewer));
   8017 
   8018  if (NS_FAILED(rv)) {
   8019    return rv;
   8020  }
   8021 
   8022  // Notify the current document that it is about to be unloaded!!
   8023  //
   8024  // It is important to fire the unload() notification *before* any state
   8025  // is changed within the DocShell - otherwise, javascript will get the
   8026  // wrong information :-(
   8027  //
   8028 
   8029  if (mSavingOldViewer) {
   8030    // We determined that it was safe to cache the document presentation
   8031    // at the time we initiated the new load.  We need to check whether
   8032    // it's still safe to do so, since there may have been DOM mutations
   8033    // or new requests initiated.
   8034    RefPtr<Document> doc = viewer->GetDocument();
   8035    mSavingOldViewer = CanSavePresentation(
   8036        mLoadType, aRequest, doc, /* aReportBFCacheComboTelemetry */ false);
   8037  }
   8038 
   8039  NS_ASSERTION(!mLoadingURI, "Re-entering unload?");
   8040 
   8041  nsCOMPtr<nsIChannel> aOpenedChannel = do_QueryInterface(aRequest);
   8042  if (aOpenedChannel) {
   8043    aOpenedChannel->GetURI(getter_AddRefs(mLoadingURI));
   8044  }
   8045 
   8046  // Grab the current URI, we need to pass it to Embed, and OnNewURI will reset
   8047  // it before we do call Embed.
   8048  nsCOMPtr<nsIURI> previousURI = mCurrentURI;
   8049 
   8050  FirePageHideNotification(!mSavingOldViewer);
   8051  if (mIsBeingDestroyed) {
   8052    // Force to stop the newly created orphaned viewer.
   8053    viewer->Stop();
   8054    return NS_ERROR_DOCSHELL_DYING;
   8055  }
   8056  mLoadingURI = nullptr;
   8057 
   8058  // Set mFiredUnloadEvent = false so that the unload handler for the
   8059  // *new* document will fire.
   8060  mFiredUnloadEvent = false;
   8061 
   8062  // we've created a new document so go ahead and call
   8063  // OnNewURI(), but don't fire OnLocationChange()
   8064  // notifications before we've called Embed(). See bug 284993.
   8065  mURIResultedInDocument = true;
   8066  bool errorOnLocationChangeNeeded = false;
   8067  nsCOMPtr<nsIChannel> failedChannel = mFailedChannel;
   8068  nsCOMPtr<nsIURI> failedURI;
   8069 
   8070  // https://html.spec.whatwg.org/#finalize-a-cross-document-navigation
   8071  // 9. If entryToReplace is null, then: ...
   8072  //    Otherwise: ...
   8073  //      4. If historyEntry's document state's origin is same origin with
   8074  //         entryToReplace's document state's origin, then set
   8075  //         historyEntry's navigation API key to entryToReplace's
   8076  //         navigation API key.
   8077  bool isReplace =
   8078      mActiveEntry && mLoadingEntry &&
   8079      mLoadingEntry->mTriggeringNavigationType
   8080          .map([](auto type) { return type == NavigationType::Replace; })
   8081          .valueOr(false);
   8082  if (isReplace) {
   8083    nsCOMPtr<nsIURI> uri = mActiveEntry->GetURIOrInheritedForAboutBlank();
   8084    nsCOMPtr<nsIURI> targetURI =
   8085        mLoadingEntry->mInfo.GetURIOrInheritedForAboutBlank();
   8086    bool sameOrigin =
   8087        NS_SUCCEEDED(nsContentUtils::GetSecurityManager()->CheckSameOriginURI(
   8088            targetURI, uri, false, false));
   8089    if (sameOrigin) {
   8090      mLoadingEntry->mInfo.NavigationKey() = mActiveEntry->NavigationKey();
   8091    }
   8092  }
   8093 
   8094  if (mLoadType == LOAD_ERROR_PAGE) {
   8095    // We need to set the SH entry and our current URI here and not
   8096    // at the moment we load the page. We want the same behavior
   8097    // of Stop() as for a normal page load. See bug 514232 for details.
   8098 
   8099    // Revert mLoadType to load type to state the page load failed,
   8100    // following function calls need it.
   8101    mLoadType = mFailedLoadType;
   8102 
   8103    Document* doc = viewer->GetDocument();
   8104    if (doc) {
   8105      doc->SetFailedChannel(failedChannel);
   8106    }
   8107 
   8108    nsCOMPtr<nsIPrincipal> triggeringPrincipal;
   8109    if (failedChannel) {
   8110      // Make sure we have a URI to set currentURI.
   8111      NS_GetFinalChannelURI(failedChannel, getter_AddRefs(failedURI));
   8112    } else {
   8113      // if there is no failed channel we have to explicitly provide
   8114      // a triggeringPrincipal for the history entry.
   8115      triggeringPrincipal = nsContentUtils::GetSystemPrincipal();
   8116    }
   8117 
   8118    if (!failedURI) {
   8119      failedURI = mFailedURI;
   8120    }
   8121    if (!failedURI) {
   8122      // We need a URI object to store a session history entry, so make up a URI
   8123      NS_NewURI(getter_AddRefs(failedURI), "about:blank");
   8124    }
   8125 
   8126    // When we don't have failedURI, something wrong will happen. See
   8127    // bug 291876.
   8128    MOZ_ASSERT(failedURI, "We don't have a URI for history APIs.");
   8129 
   8130    mFailedChannel = nullptr;
   8131    mFailedURI = nullptr;
   8132 
   8133    // Create an shistory entry for the old load.
   8134    if (failedURI) {
   8135      errorOnLocationChangeNeeded =
   8136          OnNewURI(failedURI, failedChannel, triggeringPrincipal, nullptr,
   8137                   nullptr, nullptr, false, false);
   8138    }
   8139 
   8140    // Be sure to have a correct mLSHE, it may have been cleared by
   8141    // EndPageLoad. See bug 302115.
   8142    ChildSHistory* shistory = GetSessionHistory();
   8143    if (!mozilla::SessionHistoryInParent() && shistory && !mLSHE) {
   8144      int32_t idx = shistory->LegacySHistory()->GetRequestedIndex();
   8145      if (idx == -1) {
   8146        idx = shistory->Index();
   8147      }
   8148      shistory->LegacySHistory()->GetEntryAtIndex(idx, getter_AddRefs(mLSHE));
   8149    }
   8150 
   8151    mLoadType = LOAD_ERROR_PAGE;
   8152  }
   8153 
   8154  nsCOMPtr<nsIURI> finalURI;
   8155  // If this a redirect, use the final url (uri)
   8156  // else use the original url
   8157  //
   8158  // Note that this should match what documents do (see Document::Reset).
   8159  NS_GetFinalChannelURI(aOpenedChannel, getter_AddRefs(finalURI));
   8160 
   8161  bool onLocationChangeNeeded = false;
   8162  if (finalURI) {
   8163    // Pass false for aCloneSHChildren, since we're loading a new page here.
   8164    onLocationChangeNeeded = OnNewURI(finalURI, aOpenedChannel, nullptr,
   8165                                      nullptr, nullptr, nullptr, true, false);
   8166  }
   8167 
   8168  // We inherit the classification flags from the parent document if the
   8169  // document is about:blank and the principal matches.
   8170  nsCOMPtr<nsIDocShellTreeItem> parentItem;
   8171  GetInProcessSameTypeParent(getter_AddRefs(parentItem));
   8172  if (parentItem && finalURI && NS_IsAboutBlank(finalURI)) {
   8173    RefPtr<Document> doc = viewer->GetDocument();
   8174    RefPtr<Document> parentDocument = parentItem->GetDocument();
   8175    if (parentDocument && doc &&
   8176        doc->NodePrincipal()->Equals(parentDocument->NodePrincipal())) {
   8177      doc->SetClassificationFlags(parentDocument->GetClassificationFlags());
   8178    }
   8179  }
   8180 
   8181  // let's try resetting the load group if we need to...
   8182  nsCOMPtr<nsILoadGroup> currentLoadGroup;
   8183  NS_ENSURE_SUCCESS(
   8184      aOpenedChannel->GetLoadGroup(getter_AddRefs(currentLoadGroup)),
   8185      NS_ERROR_FAILURE);
   8186 
   8187  if (currentLoadGroup != mLoadGroup) {
   8188    nsLoadFlags loadFlags = 0;
   8189 
   8190    // Cancel any URIs that are currently loading...
   8191    // XXX: Need to do this eventually      Stop();
   8192    //
   8193    // Retarget the document to this loadgroup...
   8194    //
   8195    /* First attach the channel to the right loadgroup
   8196     * and then remove from the old loadgroup. This
   8197     * puts the notifications in the right order and
   8198     * we don't null-out mLSHE in OnStateChange() for
   8199     * all redirected urls
   8200     */
   8201    aOpenedChannel->SetLoadGroup(mLoadGroup);
   8202 
   8203    // Mark the channel as being a document URI...
   8204    aOpenedChannel->GetLoadFlags(&loadFlags);
   8205    loadFlags |= nsIChannel::LOAD_DOCUMENT_URI;
   8206    nsCOMPtr<nsILoadInfo> loadInfo = aOpenedChannel->LoadInfo();
   8207    if (SandboxFlagsImplyCookies(loadInfo->GetSandboxFlags())) {
   8208      loadFlags |= nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE;
   8209    }
   8210 
   8211    aOpenedChannel->SetLoadFlags(loadFlags);
   8212 
   8213    mLoadGroup->AddRequest(aRequest, nullptr);
   8214    if (currentLoadGroup) {
   8215      currentLoadGroup->RemoveRequest(aRequest, nullptr, NS_BINDING_RETARGETED);
   8216    }
   8217 
   8218    // Update the notification callbacks, so that progress and
   8219    // status information are sent to the right docshell...
   8220    aOpenedChannel->SetNotificationCallbacks(this);
   8221  }
   8222 
   8223  if (mLoadingEntry && mBrowsingContext->IsTop() &&
   8224      !ShouldAddToSessionHistory(finalURI, aOpenedChannel)) {
   8225    mLoadingEntry->mInfo.SetTransient();
   8226  }
   8227  NS_ENSURE_SUCCESS(Embed(viewer, nullptr, false, aOpenedChannel, previousURI),
   8228                    NS_ERROR_FAILURE);
   8229 
   8230  if (!mBrowsingContext->GetHasLoadedNonInitialDocument()) {
   8231    MOZ_ALWAYS_SUCCEEDS(mBrowsingContext->SetHasLoadedNonInitialDocument(true));
   8232  }
   8233 
   8234  mSavedRefreshURIList = nullptr;
   8235  mSavingOldViewer = false;
   8236  mEODForCurrentDocument = false;
   8237 
   8238  // if this document is part of a multipart document,
   8239  // the ID can be used to distinguish it from the other parts.
   8240  nsCOMPtr<nsIMultiPartChannel> multiPartChannel(do_QueryInterface(aRequest));
   8241  if (multiPartChannel) {
   8242    if (PresShell* presShell = GetPresShell()) {
   8243      if (Document* doc = presShell->GetDocument()) {
   8244        uint32_t partID;
   8245        multiPartChannel->GetPartID(&partID);
   8246        doc->SetPartID(partID);
   8247      }
   8248    }
   8249  }
   8250 
   8251  if (errorOnLocationChangeNeeded) {
   8252    FireOnLocationChange(this, failedChannel, failedURI,
   8253                         LOCATION_CHANGE_ERROR_PAGE);
   8254  } else if (onLocationChangeNeeded) {
   8255    uint32_t locationFlags =
   8256        (mLoadType & LOAD_CMD_RELOAD) ? uint32_t(LOCATION_CHANGE_RELOAD) : 0;
   8257    FireOnLocationChange(this, aRequest, mCurrentURI, locationFlags);
   8258  }
   8259 
   8260  // Arrange to show a Tor onion service client authentication prompt if
   8261  // appropriate.
   8262  if ((mLoadType == LOAD_ERROR_PAGE) && failedChannel) {
   8263    nsresult status = NS_OK;
   8264    if (NS_SUCCEEDED(failedChannel->GetStatus(&status)) &&
   8265        ((status == NS_ERROR_TOR_ONION_SVC_MISSING_CLIENT_AUTH) ||
   8266         (status == NS_ERROR_TOR_ONION_SVC_BAD_CLIENT_AUTH))) {
   8267      nsAutoCString onionHost;
   8268      failedURI->GetHost(onionHost);
   8269      const char* topic = (status == NS_ERROR_TOR_ONION_SVC_MISSING_CLIENT_AUTH)
   8270                              ? "tor-onion-services-clientauth-missing"
   8271                              : "tor-onion-services-clientauth-incorrect";
   8272      if (XRE_IsContentProcess()) {
   8273        nsCOMPtr<nsIBrowserChild> browserChild = GetBrowserChild();
   8274        if (browserChild) {
   8275          static_cast<BrowserChild*>(browserChild.get())
   8276              ->SendShowOnionServicesAuthPrompt(onionHost, nsCString(topic));
   8277        }
   8278      } else {
   8279        nsCOMPtr<nsPIDOMWindowOuter> browserWin = GetWindow();
   8280        nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
   8281        if (browserWin && obsSvc) {
   8282          obsSvc->NotifyObservers(browserWin, topic,
   8283                                  NS_ConvertUTF8toUTF16(onionHost).get());
   8284        }
   8285      }
   8286    }
   8287  }
   8288 
   8289  return NS_OK;
   8290 }
   8291 
   8292 nsresult nsDocShell::NewDocumentViewerObj(const nsACString& aContentType,
   8293                                          nsIRequest* aRequest,
   8294                                          nsILoadGroup* aLoadGroup,
   8295                                          nsIStreamListener** aContentHandler,
   8296                                          nsIDocumentViewer** aViewer) {
   8297  nsCOMPtr<nsIChannel> aOpenedChannel = do_QueryInterface(aRequest);
   8298 
   8299  nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
   8300      nsContentUtils::FindInternalDocumentViewer(aContentType);
   8301  if (!docLoaderFactory) {
   8302    return NS_ERROR_FAILURE;
   8303  }
   8304 
   8305  // Now create an instance of the content viewer nsLayoutDLF makes the
   8306  // determination if it should be a "view-source" instead of "view"
   8307  nsresult rv = docLoaderFactory->CreateInstance(
   8308      "view", aOpenedChannel, aLoadGroup, aContentType, this, nullptr,
   8309      aContentHandler, aViewer);
   8310  NS_ENSURE_SUCCESS(rv, rv);
   8311 
   8312  (*aViewer)->SetContainer(this);
   8313  return NS_OK;
   8314 }
   8315 
   8316 nsresult nsDocShell::SetupNewViewer(nsIDocumentViewer* aNewViewer,
   8317                                    WindowGlobalChild* aWindowActor) {
   8318  MOZ_ASSERT(!mIsBeingDestroyed);
   8319 
   8320  //
   8321  // Copy content viewer state from previous or parent content viewer.
   8322  //
   8323  // The following logic is mirrored in nsHTMLDocument::StartDocumentLoad!
   8324  //
   8325  // Do NOT to maintain a reference to the old content viewer outside
   8326  // of this "copying" block, or it will not be destroyed until the end of
   8327  // this routine and all <SCRIPT>s and event handlers fail! (bug 20315)
   8328  //
   8329  // In this block of code, if we get an error result, we return it
   8330  // but if we get a null pointer, that's perfectly legal for parent
   8331  // and parentDocumentViewer.
   8332  //
   8333 
   8334  int32_t x = 0;
   8335  int32_t y = 0;
   8336  int32_t cx = 0;
   8337  int32_t cy = 0;
   8338 
   8339  // This will get the size from the current content viewer or from the
   8340  // Init settings
   8341  DoGetPositionAndSize(&x, &y, &cx, &cy);
   8342 
   8343  nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
   8344  NS_ENSURE_SUCCESS(GetInProcessSameTypeParent(getter_AddRefs(parentAsItem)),
   8345                    NS_ERROR_FAILURE);
   8346  nsCOMPtr<nsIDocShell> parent(do_QueryInterface(parentAsItem));
   8347 
   8348  const Encoding* reloadEncoding = nullptr;
   8349  int32_t reloadEncodingSource = kCharsetUninitialized;
   8350  // |newMUDV| also serves as a flag to set the data from the above vars
   8351  nsCOMPtr<nsIDocumentViewer> newViewer;
   8352 
   8353  if (mDocumentViewer || parent) {
   8354    nsCOMPtr<nsIDocumentViewer> oldViewer;
   8355    if (mDocumentViewer) {
   8356      // Get any interesting state from old content viewer
   8357      // XXX: it would be far better to just reuse the document viewer ,
   8358      //      since we know we're just displaying the same document as before
   8359      oldViewer = mDocumentViewer;
   8360 
   8361      // Tell the old content viewer to hibernate in session history when
   8362      // it is destroyed.
   8363 
   8364      if (mSavingOldViewer && NS_FAILED(CaptureState())) {
   8365        if (mOSHE) {
   8366          mOSHE->SyncPresentationState();
   8367        }
   8368        mSavingOldViewer = false;
   8369      }
   8370    } else {
   8371      // No old content viewer, so get state from parent's content viewer
   8372      parent->GetDocViewer(getter_AddRefs(oldViewer));
   8373    }
   8374 
   8375    if (oldViewer) {
   8376      newViewer = aNewViewer;
   8377      if (newViewer) {
   8378        reloadEncoding =
   8379            oldViewer->GetReloadEncodingAndSource(&reloadEncodingSource);
   8380      }
   8381    }
   8382  }
   8383 
   8384  SingleCanvasBackground canvasBg = {};
   8385  bool isUnderHiddenEmbedderElement = false;
   8386  // Ensure that the content viewer is destroyed *after* the GC - bug 71515
   8387  nsCOMPtr<nsIDocumentViewer> viewer = mDocumentViewer;
   8388  if (viewer) {
   8389    // Stop any activity that may be happening in the old document before
   8390    // releasing it...
   8391    viewer->Stop();
   8392 
   8393    // Try to extract the canvas background color from the old
   8394    // presentation shell, so we can use it for the next document.
   8395    if (PresShell* presShell = viewer->GetPresShell()) {
   8396      canvasBg = presShell->GetViewportCanvasBackground();
   8397      isUnderHiddenEmbedderElement = presShell->IsUnderHiddenEmbedderElement();
   8398    }
   8399 
   8400    viewer->Close(mSavingOldViewer ? mOSHE.get() : nullptr);
   8401    aNewViewer->SetPreviousViewer(viewer);
   8402  }
   8403  if (mOSHE && (!mDocumentViewer || !mSavingOldViewer)) {
   8404    // We don't plan to save a viewer in mOSHE; tell it to drop
   8405    // any other state it's holding.
   8406    mOSHE->SyncPresentationState();
   8407  }
   8408 
   8409  mDocumentViewer = nullptr;
   8410 
   8411  // Now that we're about to switch documents, forget all of our children.
   8412  // Note that we cached them as needed up in CaptureState above.
   8413  DestroyChildren();
   8414 
   8415  mDocumentViewer = aNewViewer;
   8416 
   8417  nsCOMPtr<nsIWidget> widget = GetMainWidget();
   8418  LayoutDeviceIntRect bounds(x, y, cx, cy);
   8419 
   8420  mDocumentViewer->SetNavigationTiming(mTiming);
   8421 
   8422  if (NS_FAILED(mDocumentViewer->Init(widget, bounds, aWindowActor))) {
   8423    nsCOMPtr<nsIDocumentViewer> viewer = mDocumentViewer;
   8424    viewer->Close(nullptr);
   8425    viewer->Destroy();
   8426    mDocumentViewer = nullptr;
   8427    SetCurrentURIInternal(nullptr);
   8428    NS_WARNING("DocumentViewer Initialization failed");
   8429    return NS_ERROR_FAILURE;
   8430  }
   8431 
   8432  // If we have old state to copy, set the old state onto the new content
   8433  // viewer
   8434  if (newViewer) {
   8435    newViewer->SetReloadEncodingAndSource(reloadEncoding, reloadEncodingSource);
   8436  }
   8437 
   8438  NS_ENSURE_TRUE(mDocumentViewer, NS_ERROR_FAILURE);
   8439 
   8440  // Stuff the bgcolor from the old pres shell into the new
   8441  // pres shell. This improves page load continuity.
   8442  if (RefPtr<PresShell> presShell = mDocumentViewer->GetPresShell()) {
   8443    presShell->SetViewportCanvasBackground(canvasBg);
   8444    presShell->ActivenessMaybeChanged();
   8445    if (isUnderHiddenEmbedderElement) {
   8446      presShell->SetIsUnderHiddenEmbedderElement(isUnderHiddenEmbedderElement);
   8447    }
   8448  }
   8449 
   8450  // XXX: It looks like the LayoutState gets restored again in Embed()
   8451  //      right after the call to SetupNewViewer(...)
   8452 
   8453  // We don't show the mDocumentViewer yet, since we want to draw the old page
   8454  // until we have enough of the new page to show.  Just return with the new
   8455  // viewer still set to hidden.
   8456 
   8457  return NS_OK;
   8458 }
   8459 
   8460 void nsDocShell::SetDocCurrentStateObj(nsISHEntry* aShEntry,
   8461                                       SessionHistoryInfo* aInfo) {
   8462  NS_ENSURE_TRUE_VOID(mDocumentViewer);
   8463 
   8464  RefPtr<Document> document = GetDocument();
   8465  NS_ENSURE_TRUE_VOID(document);
   8466 
   8467  nsCOMPtr<nsIStructuredCloneContainer> scContainer;
   8468  if (mozilla::SessionHistoryInParent()) {
   8469    // If aInfo is null, just set the document's state object to null.
   8470    if (aInfo) {
   8471      scContainer = aInfo->GetStateData();
   8472    }
   8473    MOZ_LOG(gSHLog, LogLevel::Debug,
   8474            ("nsDocShell %p SetCurrentDocState %p", this, scContainer.get()));
   8475  } else {
   8476    if (aShEntry) {
   8477      scContainer = aShEntry->GetStateData();
   8478 
   8479      // If aShEntry is null, just set the document's state object to null.
   8480    }
   8481  }
   8482 
   8483  // It's OK for scContainer too be null here; that just means there's no
   8484  // state data associated with this history entry.
   8485  document->SetStateObject(scContainer);
   8486 }
   8487 
   8488 nsresult nsDocShell::CheckLoadingPermissions() {
   8489  // This method checks whether the caller may load content into
   8490  // this docshell. Even though we've done our best to hide windows
   8491  // from code that doesn't have the right to access them, it's
   8492  // still possible for an evil site to open a window and access
   8493  // frames in the new window through window.frames[] (which is
   8494  // allAccess for historic reasons), so we still need to do this
   8495  // check on load.
   8496  nsresult rv = NS_OK;
   8497 
   8498  if (!IsSubframe()) {
   8499    // We're not a frame. Permit all loads.
   8500    return rv;
   8501  }
   8502 
   8503  // Note - The check for a current JSContext here isn't necessarily sensical.
   8504  // It's just designed to preserve the old semantics during a mass-conversion
   8505  // patch.
   8506  if (!nsContentUtils::GetCurrentJSContext()) {
   8507    return NS_OK;
   8508  }
   8509 
   8510  // Check if the caller is from the same origin as this docshell,
   8511  // or any of its ancestors.
   8512  nsIPrincipal* subjectPrincipal = nsContentUtils::SubjectPrincipal();
   8513  for (RefPtr<BrowsingContext> bc = mBrowsingContext; bc;
   8514       bc = bc->GetParent()) {
   8515    // If the BrowsingContext is not in process, then it
   8516    // is true by construction that its principal will not
   8517    // subsume the current docshell principal.
   8518    if (!bc->IsInProcess()) {
   8519      continue;
   8520    }
   8521 
   8522    nsCOMPtr<nsIScriptGlobalObject> sgo =
   8523        bc->GetDocShell()->GetScriptGlobalObject();
   8524    nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(sgo));
   8525 
   8526    nsIPrincipal* p;
   8527    if (!sop || !(p = sop->GetPrincipal())) {
   8528      return NS_ERROR_UNEXPECTED;
   8529    }
   8530 
   8531    // file: URIs are considered the same domain for the purpose of frame
   8532    // navigation by clicking a targeted link, regardless of script
   8533    // accessibility (bug 1934807).
   8534    if (subjectPrincipal->Subsumes(p) ||
   8535        (subjectPrincipal->SchemeIs("file") && p->SchemeIs("file"))) {
   8536      // Same origin, permit load
   8537      return NS_OK;
   8538    }
   8539  }
   8540 
   8541  return NS_ERROR_DOM_PROP_ACCESS_DENIED;
   8542 }
   8543 
   8544 //*****************************************************************************
   8545 // nsDocShell: Site Loading
   8546 //*****************************************************************************
   8547 
   8548 void nsDocShell::CopyFavicon(nsIURI* aOldURI, nsIURI* aNewURI,
   8549                             bool aInPrivateBrowsing) {
   8550  if (XRE_IsContentProcess()) {
   8551    dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
   8552    if (contentChild) {
   8553      contentChild->SendCopyFavicon(aOldURI, aNewURI, aInPrivateBrowsing);
   8554    }
   8555    return;
   8556  }
   8557 
   8558 #ifdef MOZ_PLACES
   8559  auto* faviconService = nsFaviconService::GetFaviconService();
   8560  if (faviconService) {
   8561    faviconService->AsyncTryCopyFavicons(
   8562        aOldURI, aNewURI,
   8563        aInPrivateBrowsing ? nsIFaviconService::FAVICON_LOAD_PRIVATE
   8564                           : nsIFaviconService::FAVICON_LOAD_NON_PRIVATE);
   8565  }
   8566 #endif
   8567 }
   8568 
   8569 class InternalLoadEvent : public Runnable {
   8570 public:
   8571  InternalLoadEvent(nsDocShell* aDocShell, nsDocShellLoadState* aLoadState)
   8572      : mozilla::Runnable("InternalLoadEvent"),
   8573        mDocShell(aDocShell),
   8574        mLoadState(aLoadState) {
   8575    // For events, both target and filename should be the version of "null" they
   8576    // expect. By the time the event is fired, both window targeting and file
   8577    // downloading have been handled, so we should never have an internal load
   8578    // event that retargets or had a download.
   8579    mLoadState->SetTarget(u""_ns);
   8580    mLoadState->SetFileName(VoidString());
   8581  }
   8582 
   8583  NS_IMETHOD
   8584  Run() override {
   8585 #ifndef ANDROID
   8586    MOZ_ASSERT(mLoadState->TriggeringPrincipal(),
   8587               "InternalLoadEvent: Should always have a principal here");
   8588 #endif
   8589    return mDocShell->InternalLoad(mLoadState);
   8590  }
   8591 
   8592 private:
   8593  RefPtr<nsDocShell> mDocShell;
   8594  RefPtr<nsDocShellLoadState> mLoadState;
   8595 };
   8596 
   8597 /**
   8598 * Returns true if we started an asynchronous load (i.e., from the network), but
   8599 * the document we're loading there hasn't yet become this docshell's active
   8600 * document.
   8601 *
   8602 * When JustStartedNetworkLoad is true, you should be careful about modifying
   8603 * mLoadType and mLSHE.  These are both set when the asynchronous load first
   8604 * starts, and the load expects that, when it eventually runs InternalLoad,
   8605 * mLoadType and mLSHE will have their original values.
   8606 */
   8607 bool nsDocShell::JustStartedNetworkLoad() {
   8608  return mDocumentRequest && mDocumentRequest != GetCurrentDocChannel();
   8609 }
   8610 
   8611 // The contentType will be INTERNAL_(I)FRAME if this docshell is for a
   8612 // non-toplevel browsing context in spec terms. (frame, iframe, <object>,
   8613 // <embed>, etc)
   8614 //
   8615 // This return value will be used when we call NS_CheckContentLoadPolicy, and
   8616 // later when we call DoURILoad.
   8617 nsContentPolicyType nsDocShell::DetermineContentType() {
   8618  if (!IsSubframe()) {
   8619    return nsIContentPolicy::TYPE_DOCUMENT;
   8620  }
   8621 
   8622  const auto& maybeEmbedderElementType =
   8623      GetBrowsingContext()->GetEmbedderElementType();
   8624  if (!maybeEmbedderElementType) {
   8625    // If the EmbedderElementType hasn't been set yet, just assume we're
   8626    // an iframe since that's more common.
   8627    return nsIContentPolicy::TYPE_INTERNAL_IFRAME;
   8628  }
   8629 
   8630  return maybeEmbedderElementType->EqualsLiteral("iframe")
   8631             ? nsIContentPolicy::TYPE_INTERNAL_IFRAME
   8632             : nsIContentPolicy::TYPE_INTERNAL_FRAME;
   8633 }
   8634 
   8635 bool nsDocShell::NoopenerForceEnabled() {
   8636  // If current's top-level browsing context's active document's
   8637  // cross-origin-opener-policy is "same-origin" or "same-origin + COEP" then
   8638  // if currentDoc's origin is not same origin with currentDoc's top-level
   8639  // origin, noopener is force enabled, and name is cleared to "_blank".
   8640  auto topPolicy = mBrowsingContext->Top()->GetOpenerPolicy();
   8641  return (topPolicy == nsILoadInfo::OPENER_POLICY_SAME_ORIGIN ||
   8642          topPolicy ==
   8643              nsILoadInfo::
   8644                  OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP) &&
   8645         !mBrowsingContext->SameOriginWithTop();
   8646 }
   8647 
   8648 nsresult nsDocShell::PerformRetargeting(nsDocShellLoadState* aLoadState) {
   8649  MOZ_ASSERT(aLoadState, "need a load state!");
   8650  MOZ_ASSERT(!aLoadState->Target().IsEmpty(), "should have a target here!");
   8651  MOZ_ASSERT(aLoadState->TargetBrowsingContext().IsNull(),
   8652             "should not have picked target yet");
   8653 
   8654  nsresult rv = NS_OK;
   8655  RefPtr<BrowsingContext> targetContext;
   8656 
   8657  // Only _self, _parent, and _top are supported in noopener case.  But we
   8658  // have to be careful to not apply that to the noreferrer case.  See bug
   8659  // 1358469.
   8660  bool allowNamedTarget =
   8661      !aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_NO_OPENER) ||
   8662      aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER);
   8663  if (allowNamedTarget ||
   8664      aLoadState->Target().LowerCaseEqualsLiteral("_self") ||
   8665      aLoadState->Target().LowerCaseEqualsLiteral("_parent") ||
   8666      aLoadState->Target().LowerCaseEqualsLiteral("_top")) {
   8667    Document* document = GetDocument();
   8668    NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
   8669    WindowGlobalChild* wgc = document->GetWindowGlobalChild();
   8670    NS_ENSURE_TRUE(wgc, NS_ERROR_FAILURE);
   8671    targetContext = wgc->FindBrowsingContextWithName(
   8672        aLoadState->Target(), /* aUseEntryGlobalForAccessCheck */ false);
   8673  }
   8674 
   8675  if (!targetContext) {
   8676    // If the targetContext doesn't exist, then this is a new docShell and we
   8677    // should consider this a TYPE_DOCUMENT load
   8678    //
   8679    // For example, when target="_blank"
   8680 
   8681    // If there's no targetContext, that means we are about to create a new
   8682    // window. Perform a content policy check before creating the window. Please
   8683    // note for all other docshell loads content policy checks are performed
   8684    // within the contentSecurityManager when the channel is about to be
   8685    // openend.
   8686    nsISupports* requestingContext = nullptr;
   8687    if (XRE_IsContentProcess()) {
   8688      // In e10s the child process doesn't have access to the element that
   8689      // contains the browsing context (because that element is in the chrome
   8690      // process). So we just pass mScriptGlobal.
   8691      requestingContext = ToSupports(mScriptGlobal);
   8692    } else {
   8693      // This is for loading non-e10s tabs and toplevel windows of various
   8694      // sorts.
   8695      // For the toplevel window cases, requestingElement will be null.
   8696      nsCOMPtr<Element> requestingElement =
   8697          mScriptGlobal->GetFrameElementInternal();
   8698      requestingContext = requestingElement;
   8699    }
   8700 
   8701    // Ideally we should use the same loadinfo as within DoURILoad which
   8702    // should match this one when both are applicable.
   8703    nsCOMPtr<nsILoadInfo> secCheckLoadInfo =
   8704        new LoadInfo(mScriptGlobal, aLoadState->URI(),
   8705                     aLoadState->TriggeringPrincipal(), requestingContext,
   8706                     nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK, 0);
   8707 
   8708    // Since Content Policy checks are performed within docShell as well as
   8709    // the ContentSecurityManager we need a reliable way to let certain
   8710    // nsIContentPolicy consumers ignore duplicate calls.
   8711    secCheckLoadInfo->SetSkipContentPolicyCheckForWebRequest(true);
   8712 
   8713    int16_t shouldLoad = nsIContentPolicy::ACCEPT;
   8714    rv = NS_CheckContentLoadPolicy(aLoadState->URI(), secCheckLoadInfo,
   8715                                   &shouldLoad);
   8716 
   8717    if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
   8718      if (NS_SUCCEEDED(rv)) {
   8719        if (shouldLoad == nsIContentPolicy::REJECT_TYPE) {
   8720          return NS_ERROR_CONTENT_BLOCKED_SHOW_ALT;
   8721        }
   8722        if (shouldLoad == nsIContentPolicy::REJECT_POLICY) {
   8723          return NS_ERROR_BLOCKED_BY_POLICY;
   8724        }
   8725      }
   8726 
   8727      return NS_ERROR_CONTENT_BLOCKED;
   8728    }
   8729  }
   8730 
   8731  //
   8732  // Resolve the window target before going any further...
   8733  // If the load has been targeted to another DocShell, then transfer the
   8734  // load to it...
   8735  //
   8736 
   8737  // We've already done our owner-inheriting.  Mask out that bit, so we
   8738  // don't try inheriting an owner from the target window if we came up
   8739  // with a null owner above.
   8740  aLoadState->UnsetInternalLoadFlag(INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL);
   8741 
   8742  if (!targetContext) {
   8743    // If the docshell's document is sandboxed, only open a new window
   8744    // if the document's SANDBOXED_AUXILLARY_NAVIGATION flag is not set.
   8745    // (i.e. if allow-popups is specified)
   8746    NS_ENSURE_TRUE(mDocumentViewer, NS_ERROR_FAILURE);
   8747    Document* doc = mDocumentViewer->GetDocument();
   8748 
   8749    const bool isDocumentAuxSandboxed =
   8750        doc && (doc->GetSandboxFlags() & SANDBOXED_AUXILIARY_NAVIGATION);
   8751 
   8752    if (isDocumentAuxSandboxed) {
   8753      return NS_ERROR_DOM_INVALID_ACCESS_ERR;
   8754    }
   8755 
   8756    nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
   8757    NS_ENSURE_TRUE(win, NS_ERROR_NOT_AVAILABLE);
   8758 
   8759    RefPtr<BrowsingContext> newBC;
   8760    nsAutoCString spec;
   8761    aLoadState->URI()->GetSpec(spec);
   8762 
   8763    // If we are a noopener load, we just hand the whole thing over to our
   8764    // window.
   8765    if (aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_NO_OPENER) ||
   8766        NoopenerForceEnabled()) {
   8767      // Various asserts that we know to hold because NO_OPENER loads can only
   8768      // happen for links.
   8769      MOZ_ASSERT(!aLoadState->LoadReplace());
   8770      MOZ_ASSERT(aLoadState->PrincipalToInherit() ==
   8771                 aLoadState->TriggeringPrincipal());
   8772      MOZ_ASSERT(!(aLoadState->InternalLoadFlags() &
   8773                   ~(INTERNAL_LOAD_FLAGS_NO_OPENER |
   8774                     INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER)),
   8775                 "Only INTERNAL_LOAD_FLAGS_NO_OPENER and "
   8776                 "INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER can be set");
   8777      MOZ_ASSERT_IF(aLoadState->PostDataStream(),
   8778                    aLoadState->IsFormSubmission());
   8779      MOZ_ASSERT(!aLoadState->HeadersStream());
   8780      // If OnLinkClickSync was invoked inside the onload handler, the load
   8781      // type would be set to LOAD_NORMAL_REPLACE; otherwise it should be
   8782      // LOAD_LINK.
   8783      MOZ_ASSERT(aLoadState->LoadType() == LOAD_LINK ||
   8784                 aLoadState->LoadType() == LOAD_NORMAL_REPLACE);
   8785      MOZ_ASSERT(!aLoadState->LoadIsFromSessionHistory());
   8786      MOZ_ASSERT(aLoadState->FirstParty());  // Windowwatcher will assume this.
   8787 
   8788      RefPtr<nsDocShellLoadState> loadState =
   8789          new nsDocShellLoadState(aLoadState->URI());
   8790 
   8791      // Set up our loadinfo so it will do the load as much like we would have
   8792      // as possible.
   8793      loadState->SetReferrerInfo(aLoadState->GetReferrerInfo());
   8794      loadState->SetOriginalURI(aLoadState->OriginalURI());
   8795 
   8796      Maybe<nsCOMPtr<nsIURI>> resultPrincipalURI;
   8797      aLoadState->GetMaybeResultPrincipalURI(resultPrincipalURI);
   8798 
   8799      loadState->SetMaybeResultPrincipalURI(resultPrincipalURI);
   8800      loadState->SetKeepResultPrincipalURIIfSet(
   8801          aLoadState->KeepResultPrincipalURIIfSet());
   8802      // LoadReplace will always be false due to asserts above, skip setting
   8803      // it.
   8804      loadState->SetTriggeringPrincipal(aLoadState->TriggeringPrincipal());
   8805      loadState->SetTriggeringSandboxFlags(
   8806          aLoadState->TriggeringSandboxFlags());
   8807      loadState->SetTriggeringWindowId(aLoadState->TriggeringWindowId());
   8808      loadState->SetTriggeringStorageAccess(
   8809          aLoadState->TriggeringStorageAccess());
   8810      loadState->SetTriggeringClassificationFlags(
   8811          aLoadState->TriggeringClassificationFlags());
   8812      loadState->SetPolicyContainer(aLoadState->PolicyContainer());
   8813      loadState->SetInheritPrincipal(aLoadState->HasInternalLoadFlags(
   8814          INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL));
   8815      // Explicit principal because we do not want any guesses as to what the
   8816      // principal to inherit is: it should be aTriggeringPrincipal.
   8817      loadState->SetPrincipalIsExplicit(true);
   8818      loadState->SetLoadType(aLoadState->LoadType());
   8819      loadState->SetForceAllowDataURI(aLoadState->HasInternalLoadFlags(
   8820          INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI));
   8821 
   8822      loadState->SetHasValidUserGestureActivation(
   8823          aLoadState->HasValidUserGestureActivation());
   8824 
   8825      loadState->SetTextDirectiveUserActivation(
   8826          aLoadState->GetTextDirectiveUserActivation());
   8827 
   8828      // Propagate POST data to the new load.
   8829      loadState->SetPostDataStream(aLoadState->PostDataStream());
   8830      loadState->SetIsFormSubmission(aLoadState->IsFormSubmission());
   8831 
   8832      loadState->SetNavigationAPIState(aLoadState->GetNavigationAPIState());
   8833 
   8834      rv = win->Open(spec,
   8835                     aLoadState->Target(),  // window name
   8836                     u""_ns,                // Features
   8837                     loadState,
   8838                     true,  // aForceNoOpener
   8839                     getter_AddRefs(newBC));
   8840      MOZ_ASSERT(!newBC);
   8841      return rv;
   8842    }
   8843 
   8844    rv = win->OpenNoNavigate(spec,
   8845                             aLoadState->Target(),  // window name
   8846                             u""_ns,                // Features
   8847                             getter_AddRefs(newBC));
   8848 
   8849    // In some cases the Open call doesn't actually result in a new
   8850    // window being opened.  We can detect these cases by examining the
   8851    // document in |newBC|, if any.
   8852    nsCOMPtr<nsPIDOMWindowOuter> piNewWin =
   8853        newBC ? newBC->GetDOMWindow() : nullptr;
   8854    if (piNewWin) {
   8855      RefPtr<Document> newDoc = piNewWin->GetExtantDoc();
   8856      if (!newDoc || newDoc->IsInitialDocument()) {
   8857        aLoadState->SetInternalLoadFlag(INTERNAL_LOAD_FLAGS_FIRST_LOAD);
   8858      }
   8859    }
   8860 
   8861    if (newBC) {
   8862      targetContext = newBC;
   8863    }
   8864  }
   8865  NS_ENSURE_SUCCESS(rv, rv);
   8866  NS_ENSURE_TRUE(targetContext, rv);
   8867 
   8868  // If our target BrowsingContext is still pending initialization, ignore the
   8869  // navigation request targeting it.
   8870  if (NS_WARN_IF(targetContext->GetPendingInitialization())) {
   8871    return NS_OK;
   8872  }
   8873 
   8874  aLoadState->SetTargetBrowsingContext(targetContext);
   8875  if (aLoadState->IsFormSubmission()) {
   8876    aLoadState->SetLoadType(
   8877        GetLoadTypeForFormSubmission(targetContext, aLoadState));
   8878  }
   8879 
   8880  //
   8881  // Transfer the load to the target BrowsingContext... Clear the window target
   8882  // name to the empty string to prevent recursive retargeting!
   8883  //
   8884  // No window target
   8885  aLoadState->SetTarget(u""_ns);
   8886  // No forced download
   8887  aLoadState->SetFileName(VoidString());
   8888  return targetContext->InternalLoad(aLoadState);
   8889 }
   8890 
   8891 static nsAutoCString RefMaybeNull(nsIURI* aURI) {
   8892  nsAutoCString result;
   8893  if (NS_FAILED(aURI->GetRef(result))) {
   8894    result.SetIsVoid(true);
   8895  }
   8896  return result;
   8897 }
   8898 
   8899 uint32_t nsDocShell::GetSameDocumentNavigationFlags(nsIURI* aNewURI) {
   8900  uint32_t flags = LOCATION_CHANGE_SAME_DOCUMENT;
   8901 
   8902  bool equal = false;
   8903  if (mCurrentURI &&
   8904      NS_SUCCEEDED(mCurrentURI->EqualsExceptRef(aNewURI, &equal)) && equal &&
   8905      RefMaybeNull(mCurrentURI) != RefMaybeNull(aNewURI)) {
   8906    flags |= LOCATION_CHANGE_HASHCHANGE;
   8907  }
   8908 
   8909  return flags;
   8910 }
   8911 
   8912 struct SameDocumentNavigationState {
   8913  nsAutoCString mCurrentHash;
   8914  nsAutoCString mNewHash;
   8915  nsTArray<TextDirective> mTextDirectives;
   8916  bool mCurrentURIHasRef = false;
   8917  bool mNewURIHasRef = false;
   8918  bool mSameExceptHashes = false;
   8919  bool mSecureUpgradeURI = false;
   8920  bool mHistoryNavBetweenSameDoc = false;
   8921  bool mIdentical = false;
   8922 };
   8923 
   8924 bool nsDocShell::IsSameDocumentNavigation(nsDocShellLoadState* aLoadState,
   8925                                          SameDocumentNavigationState& aState) {
   8926  MOZ_ASSERT(aLoadState);
   8927  if (!(aLoadState->LoadType() == LOAD_NORMAL ||
   8928        aLoadState->LoadType() == LOAD_STOP_CONTENT ||
   8929        LOAD_TYPE_HAS_FLAGS(aLoadState->LoadType(),
   8930                            LOAD_FLAGS_REPLACE_HISTORY) ||
   8931        aLoadState->LoadType() == LOAD_HISTORY ||
   8932        aLoadState->LoadType() == LOAD_LINK)) {
   8933    return false;
   8934  }
   8935 
   8936  if (GetExtantDocument() &&
   8937      GetExtantDocument()->IsUncommittedInitialDocument()) {
   8938    MOZ_LOG(gSHLog, LogLevel::Debug,
   8939            ("nsDocShell::IsSameDocumentNavigation %p false, document is "
   8940             "uncommitted initial",
   8941             this));
   8942    return false;
   8943  }
   8944 
   8945  nsCOMPtr<nsIURI> currentURI = mCurrentURI;
   8946 
   8947  nsresult rvURINew = aLoadState->URI()->GetRef(aState.mNewHash);
   8948  if (NS_SUCCEEDED(rvURINew)) {
   8949    rvURINew = aLoadState->URI()->GetHasRef(&aState.mNewURIHasRef);
   8950  }
   8951 
   8952  // A Fragment Directive must be removed from the new hash in order to allow
   8953  // fallback element id scroll.
   8954  FragmentDirective::ParseAndRemoveFragmentDirectiveFromFragmentString(
   8955      aState.mNewHash, &aState.mTextDirectives, aLoadState->URI());
   8956 
   8957  if (currentURI && NS_SUCCEEDED(rvURINew)) {
   8958    nsresult rvURIOld = currentURI->GetRef(aState.mCurrentHash);
   8959    if (NS_SUCCEEDED(rvURIOld)) {
   8960      rvURIOld = currentURI->GetHasRef(&aState.mCurrentURIHasRef);
   8961    }
   8962    if (NS_SUCCEEDED(rvURIOld)) {
   8963      if (NS_FAILED(currentURI->EqualsExceptRef(aLoadState->URI(),
   8964                                                &aState.mSameExceptHashes))) {
   8965        aState.mSameExceptHashes = false;
   8966      }
   8967    }
   8968  }
   8969 
   8970  if (!aState.mSameExceptHashes && currentURI && NS_SUCCEEDED(rvURINew)) {
   8971    // Maybe aLoadState->URI() came from the exposable form of currentURI?
   8972    nsCOMPtr<nsIURI> currentExposableURI =
   8973        nsIOService::CreateExposableURI(currentURI);
   8974    nsresult rvURIOld = currentExposableURI->GetRef(aState.mCurrentHash);
   8975    if (NS_SUCCEEDED(rvURIOld)) {
   8976      rvURIOld = currentExposableURI->GetHasRef(&aState.mCurrentURIHasRef);
   8977    }
   8978    if (NS_SUCCEEDED(rvURIOld)) {
   8979      if (NS_FAILED(currentExposableURI->EqualsExceptRef(
   8980              aLoadState->URI(), &aState.mSameExceptHashes))) {
   8981        aState.mSameExceptHashes = false;
   8982      }
   8983      // HTTPS-Only Mode upgrades schemes from http to https in Necko, hence we
   8984      // have to perform a special check here to avoid an actual navigation. If
   8985      // HTTPS-Only Mode is enabled and the two URIs are same-origin (modulo the
   8986      // fact that the new URI is currently http), then set mSameExceptHashes to
   8987      // true and only perform a fragment navigation.
   8988      if (!aState.mSameExceptHashes) {
   8989        if (nsCOMPtr<nsIChannel> docChannel = GetCurrentDocChannel()) {
   8990          nsCOMPtr<nsILoadInfo> docLoadInfo = docChannel->LoadInfo();
   8991          nsHTTPSOnlyUtils::UpgradeMode upgradeMode =
   8992              nsHTTPSOnlyUtils::GetUpgradeMode(docLoadInfo);
   8993          if (!docLoadInfo->GetLoadErrorPage() &&
   8994              (upgradeMode == nsHTTPSOnlyUtils::HTTPS_ONLY_MODE ||
   8995               upgradeMode == nsHTTPSOnlyUtils::HTTPS_FIRST_MODE) &&
   8996              nsHTTPSOnlyUtils::IsHttpDowngrade(currentExposableURI,
   8997                                                aLoadState->URI())) {
   8998            uint32_t status = docLoadInfo->GetHttpsOnlyStatus();
   8999            if ((status &
   9000                 (nsILoadInfo::HTTPS_ONLY_UPGRADED_LISTENER_REGISTERED |
   9001                  nsILoadInfo::HTTPS_ONLY_UPGRADED_HTTPS_FIRST)) &&
   9002                !(status & nsILoadInfo::HTTPS_ONLY_EXEMPT)) {
   9003              // At this point the requested URI is for sure a fragment
   9004              // navigation via HTTP and HTTPS-Only mode or HTTPS-First is
   9005              // enabled. Also it is not interfering the upgrade order of
   9006              // https://searchfox.org/mozilla-central/source/netwerk/base/nsNetUtil.cpp#2948-2953.
   9007              // Since we are on an HTTPS site the fragment
   9008              // navigation should also be an HTTPS.
   9009              // For that reason we should upgrade the URI to HTTPS.
   9010              aState.mSecureUpgradeURI = true;
   9011              aState.mSameExceptHashes = true;
   9012            }
   9013          }
   9014        }
   9015      }
   9016    }
   9017  }
   9018 
   9019  if (mozilla::SessionHistoryInParent()) {
   9020    if (mActiveEntry && aLoadState->LoadIsFromSessionHistory()) {
   9021      aState.mHistoryNavBetweenSameDoc = mActiveEntry->SharesDocumentWith(
   9022          aLoadState->GetLoadingSessionHistoryInfo()->mInfo);
   9023    }
   9024    MOZ_LOG(gSHLog, LogLevel::Debug,
   9025            ("nsDocShell::IsSameDocumentNavigation %p NavBetweenSameDoc=%d",
   9026             this, aState.mHistoryNavBetweenSameDoc));
   9027  } else {
   9028    if (mOSHE && aLoadState->LoadIsFromSessionHistory()) {
   9029      // We're doing a history load.
   9030 
   9031      mOSHE->SharesDocumentWith(aLoadState->SHEntry(),
   9032                                &aState.mHistoryNavBetweenSameDoc);
   9033    }
   9034  }
   9035 
   9036  // Two URIs are identical if they're same except hashes, they both have
   9037  // hashes, and their hashes are the same.
   9038  aState.mIdentical = aState.mSameExceptHashes &&
   9039                      (aState.mNewURIHasRef == aState.mCurrentURIHasRef) &&
   9040                      aState.mCurrentHash.Equals(aState.mNewHash);
   9041 
   9042  // A same document navigation happens when we navigate between two SHEntries
   9043  // for the same document. We do a same document navigation under two
   9044  // circumstances. Either
   9045  //
   9046  //  a) we're navigating between two different SHEntries which share a
   9047  //     document, or
   9048  //
   9049  //  b) we're navigating to a new shentry whose URI differs from the
   9050  //     current URI only in its hash, the new hash is non-empty, and
   9051  //     we're not doing a POST.
   9052  //
   9053  // The restriction that the SHEntries in (a) must be different ensures
   9054  // that history.go(0) and the like trigger full refreshes, rather than
   9055  // same document navigations.
   9056  if (!mozilla::SessionHistoryInParent()) {
   9057    bool doSameDocumentNavigation =
   9058        (aState.mHistoryNavBetweenSameDoc && mOSHE != aLoadState->SHEntry()) ||
   9059        (!aLoadState->SHEntry() && !aLoadState->PostDataStream() &&
   9060         aState.mSameExceptHashes && aState.mNewURIHasRef);
   9061    MOZ_LOG(gSHLog, LogLevel::Debug,
   9062            ("nsDocShell %p NavBetweenSameDoc=%d is same doc = %d", this,
   9063             aState.mHistoryNavBetweenSameDoc, doSameDocumentNavigation));
   9064    return doSameDocumentNavigation;
   9065  }
   9066 
   9067  if (aState.mHistoryNavBetweenSameDoc &&
   9068      !aLoadState->GetLoadingSessionHistoryInfo()->mLoadingCurrentEntry) {
   9069    return true;
   9070  }
   9071 
   9072  MOZ_LOG(
   9073      gSHLog, LogLevel::Debug,
   9074      ("nsDocShell::IsSameDocumentNavigation %p !LoadIsFromSessionHistory=%s "
   9075       "!PostDataStream: %s mSameExceptHashes: %s mNewURIHasRef: %s",
   9076       this, !aLoadState->LoadIsFromSessionHistory() ? "true" : "false",
   9077       !aLoadState->PostDataStream() ? "true" : "false",
   9078       aState.mSameExceptHashes ? "true" : "false",
   9079       aState.mNewURIHasRef ? "true" : "false"));
   9080  return !aLoadState->LoadIsFromSessionHistory() &&
   9081         !aLoadState->PostDataStream() && aState.mSameExceptHashes &&
   9082         aState.mNewURIHasRef;
   9083 }
   9084 
   9085 static bool IsSamePrincipalForDocumentURI(nsIPrincipal* aCurrentPrincipal,
   9086                                          nsIURI* aCurrentURI,
   9087                                          nsIURI* aNewURI) {
   9088  nsCOMPtr<nsIURI> principalURI = aCurrentPrincipal->GetURI();
   9089  if (aCurrentPrincipal->GetIsNullPrincipal()) {
   9090    if (nsCOMPtr<nsIPrincipal> precursor =
   9091            aCurrentPrincipal->GetPrecursorPrincipal()) {
   9092      principalURI = precursor->GetURI();
   9093    }
   9094  }
   9095 
   9096  return !nsScriptSecurityManager::IsHttpOrHttpsAndCrossOrigin(principalURI,
   9097                                                               aNewURI) &&
   9098         !nsScriptSecurityManager::IsHttpOrHttpsAndCrossOrigin(principalURI,
   9099                                                               aCurrentURI) &&
   9100         !nsScriptSecurityManager::IsHttpOrHttpsAndCrossOrigin(aCurrentURI,
   9101                                                               aNewURI);
   9102 }
   9103 
   9104 nsresult nsDocShell::HandleSameDocumentNavigation(
   9105    nsDocShellLoadState* aLoadState, SameDocumentNavigationState& aState,
   9106    bool& aSameDocument) {
   9107  aSameDocument = true;
   9108 #ifdef DEBUG
   9109  SameDocumentNavigationState state;
   9110  MOZ_ASSERT(IsSameDocumentNavigation(aLoadState, state));
   9111 #endif
   9112 
   9113  MOZ_LOG(gSHLog, LogLevel::Debug,
   9114          ("nsDocShell::HandleSameDocumentNavigation %p %s -> %s", this,
   9115           mCurrentURI->GetSpecOrDefault().get(),
   9116           aLoadState->URI()->GetSpecOrDefault().get()));
   9117 
   9118  RefPtr<Document> doc = GetDocument();
   9119  NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
   9120 
   9121  nsCOMPtr<nsIURI> currentURI = mCurrentURI;
   9122 
   9123  // We need to upgrade the new URI from http: to https:
   9124  nsCOMPtr<nsIURI> newURI = aLoadState->URI();
   9125  if (aState.mSecureUpgradeURI) {
   9126    MOZ_TRY(NS_GetSecureUpgradedURI(aLoadState->URI(), getter_AddRefs(newURI)));
   9127    MOZ_LOG(gSHLog, LogLevel::Debug,
   9128            ("Upgraded URI to %s", newURI->GetSpecOrDefault().get()));
   9129  }
   9130 
   9131  // check if documentPrincipal, mCurrentURI, and aLoadState->URI() are same
   9132  // origin skip handling otherwise
   9133  if (!IsSamePrincipalForDocumentURI(doc->NodePrincipal(), mCurrentURI,
   9134                                     newURI)) {
   9135    aSameDocument = false;
   9136    MOZ_LOG(gSHLog, LogLevel::Debug,
   9137            ("nsDocShell[%p]: possible violation of the same origin policy "
   9138             "during same document navigation",
   9139             this));
   9140    return NS_OK;
   9141  }
   9142 
   9143  // https://html.spec.whatwg.org/#navigate-fragid
   9144  // Step 2
   9145  RefPtr<nsIStructuredCloneContainer> destinationNavigationAPIState =
   9146      mActiveEntry ? mActiveEntry->GetNavigationAPIState() : nullptr;
   9147  // Step 3
   9148  if (auto* navigationAPIState = aLoadState->GetNavigationAPIState()) {
   9149    destinationNavigationAPIState = navigationAPIState;
   9150  }
   9151 
   9152  if (nsCOMPtr<nsPIDOMWindowInner> window = doc->GetInnerWindow();
   9153      window && !aState.mHistoryNavBetweenSameDoc) {
   9154    // https://html.spec.whatwg.org/#navigate-fragid
   9155    // Step 1
   9156    if (RefPtr<Navigation> navigation = window->Navigation()) {
   9157      AutoJSAPI jsapi;
   9158      if (jsapi.Init(window)) {
   9159        RefPtr<Element> sourceElement = aLoadState->GetSourceElement();
   9160        // Step 4
   9161        RefPtr apiMethodTracker = aLoadState->GetNavigationAPIMethodTracker();
   9162        bool shouldContinue = navigation->FirePushReplaceReloadNavigateEvent(
   9163            jsapi.cx(), aLoadState->GetNavigationType(), newURI,
   9164            /* aIsSameDocument */ true,
   9165            Some(aLoadState->UserNavigationInvolvement()), sourceElement,
   9166            /* aFormDataEntryList */ nullptr,
   9167            /* aNavigationAPIState */ destinationNavigationAPIState,
   9168            /* aClassicHistoryAPIState */ nullptr, apiMethodTracker);
   9169 
   9170        // Step 5
   9171        if (!shouldContinue) {
   9172          return NS_OK;
   9173        }
   9174      }
   9175    }
   9176  }
   9177 
   9178  doc->DoNotifyPossibleTitleChange();
   9179 
   9180  // Store the pending uninvoked directives if it is a same document
   9181  // navigation. We need to set it here, in case the navigation happens before
   9182  // the document has actually finished loading.
   9183  doc->FragmentDirective()->SetTextDirectives(
   9184      std::move(aState.mTextDirectives));
   9185 
   9186 #ifdef DEBUG
   9187  if (aState.mSameExceptHashes) {
   9188    bool sameExceptHashes = false;
   9189    currentURI->EqualsExceptRef(newURI, &sameExceptHashes);
   9190    MOZ_ASSERT(sameExceptHashes);
   9191  }
   9192 #endif
   9193  const nsCOMPtr<nsILoadInfo> loadInfo =
   9194      doc->GetChannel() ? doc->GetChannel()->LoadInfo() : nullptr;
   9195  if (loadInfo) {
   9196    loadInfo->SetIsSameDocumentNavigation(true);
   9197  }
   9198  // Save the position of the scrollers.
   9199  nsPoint scrollPos = GetCurScrollPos();
   9200 
   9201  // Reset mLoadType to its original value once we exit this block, because this
   9202  // same document navigation might have started after a normal, network load,
   9203  // and we don't want to clobber its load type. See bug 737307.
   9204  Maybe<AutoRestore<uint32_t>> loadTypeResetter;
   9205  if (StaticPrefs::
   9206          docshell_shistory_sameDocumentNavigationOverridesLoadType() &&
   9207      !doc->NodePrincipal()->IsURIInPrefList(
   9208          "docshell.shistory.sameDocumentNavigationOverridesLoadType."
   9209          "forceDisable")) {
   9210    loadTypeResetter.emplace(mLoadType);
   9211  }
   9212  if (JustStartedNetworkLoad() && !loadTypeResetter.isSome()) {
   9213    loadTypeResetter.emplace(mLoadType);
   9214  }
   9215 
   9216  // If a non-same-document-navigation (i.e., a network load) is pending, make
   9217  // this a replacement load, so that we don't add a SHEntry here and the
   9218  // network load goes into the SHEntry it expects to.
   9219  if (JustStartedNetworkLoad() && (aLoadState->LoadType() & LOAD_CMD_NORMAL)) {
   9220    mLoadType = LOAD_NORMAL_REPLACE;
   9221  } else {
   9222    mLoadType = aLoadState->LoadType();
   9223  }
   9224 
   9225  mURIResultedInDocument = true;
   9226 
   9227  nsCOMPtr<nsISHEntry> oldLSHE = mLSHE;
   9228 
   9229  // we need to assign aLoadState->SHEntry() to mLSHE right here, so that on
   9230  // History loads, SetCurrentURI() called from OnNewURI() will send proper
   9231  // onLocationChange() notifications to the browser to update back/forward
   9232  // buttons.
   9233  SetHistoryEntryAndUpdateBC(Some<nsISHEntry*>(aLoadState->SHEntry()),
   9234                             Nothing());
   9235  UniquePtr<mozilla::dom::LoadingSessionHistoryInfo> oldLoadingEntry;
   9236  mLoadingEntry.swap(oldLoadingEntry);
   9237  if (aLoadState->GetLoadingSessionHistoryInfo()) {
   9238    mLoadingEntry = MakeUnique<LoadingSessionHistoryInfo>(
   9239        *aLoadState->GetLoadingSessionHistoryInfo());
   9240    mNeedToReportActiveAfterLoadingBecomesActive = false;
   9241  }
   9242 
   9243  // Set the doc's URI according to the new history entry's URI.
   9244  doc->SetDocumentURI(newURI);
   9245 
   9246  /* This is a anchor traversal within the same page.
   9247   * call OnNewURI() so that, this traversal will be
   9248   * recorded in session and global history.
   9249   */
   9250  nsCOMPtr<nsIPrincipal> newURITriggeringPrincipal, newURIPrincipalToInherit,
   9251      newURIPartitionedPrincipalToInherit;
   9252  nsCOMPtr<nsIPolicyContainer> newPolicyContainer;
   9253  if (mozilla::SessionHistoryInParent() ? !!mActiveEntry : !!mOSHE) {
   9254    if (mozilla::SessionHistoryInParent()) {
   9255      newURITriggeringPrincipal = mActiveEntry->GetTriggeringPrincipal();
   9256      newURIPrincipalToInherit = mActiveEntry->GetPrincipalToInherit();
   9257      newURIPartitionedPrincipalToInherit =
   9258          mActiveEntry->GetPartitionedPrincipalToInherit();
   9259      newPolicyContainer = mActiveEntry->GetPolicyContainer();
   9260    } else {
   9261      newURITriggeringPrincipal = mOSHE->GetTriggeringPrincipal();
   9262      newURIPrincipalToInherit = mOSHE->GetPrincipalToInherit();
   9263      newURIPartitionedPrincipalToInherit =
   9264          mOSHE->GetPartitionedPrincipalToInherit();
   9265      newPolicyContainer = mOSHE->GetPolicyContainer();
   9266    }
   9267  } else {
   9268    newURITriggeringPrincipal = aLoadState->TriggeringPrincipal();
   9269    newURIPrincipalToInherit = doc->NodePrincipal();
   9270    newURIPartitionedPrincipalToInherit = doc->PartitionedPrincipal();
   9271    newPolicyContainer = doc->GetPolicyContainer();
   9272  }
   9273 
   9274  uint32_t locationChangeFlags = GetSameDocumentNavigationFlags(newURI);
   9275 
   9276  // Pass true for aCloneSHChildren, since we're not
   9277  // changing documents here, so all of our subframes are
   9278  // still relevant to the new session history entry.
   9279  //
   9280  // It also makes OnNewURI(...) set LOCATION_CHANGE_SAME_DOCUMENT
   9281  // flag on firing onLocationChange(...).
   9282  // Anyway, aCloneSHChildren param is simply reflecting
   9283  // doSameDocumentNavigation in this scope.
   9284  //
   9285  // Note: we'll actually fire onLocationChange later, in order to preserve
   9286  // ordering of HistoryCommit() in the parent vs onLocationChange (bug
   9287  // 1668126)
   9288  bool locationChangeNeeded = OnNewURI(
   9289      newURI, nullptr, newURITriggeringPrincipal, newURIPrincipalToInherit,
   9290      newURIPartitionedPrincipalToInherit, newPolicyContainer, true, true);
   9291 
   9292  nsCOMPtr<nsIInputStream> postData;
   9293  nsCOMPtr<nsIReferrerInfo> referrerInfo;
   9294  uint32_t cacheKey = 0;
   9295 
   9296  bool scrollRestorationIsManual = false;
   9297  if (!mozilla::SessionHistoryInParent()) {
   9298    if (mOSHE) {
   9299      /* save current position of scroller(s) (bug 59774) */
   9300      mOSHE->SetScrollPosition(scrollPos.x, scrollPos.y);
   9301      scrollRestorationIsManual = mOSHE->GetScrollRestorationIsManual();
   9302      // Get the postdata, page ident and referrer info from the current page,
   9303      // if the new load is being done via normal means.  Note that "normal
   9304      // means" can be checked for just by checking for LOAD_CMD_NORMAL, given
   9305      // the loadType and allowScroll check above -- it filters out some
   9306      // LOAD_CMD_NORMAL cases that we wouldn't want here.
   9307      if (aLoadState->LoadType() & LOAD_CMD_NORMAL) {
   9308        postData = mOSHE->GetPostData();
   9309        cacheKey = mOSHE->GetCacheKey();
   9310        referrerInfo = mOSHE->GetReferrerInfo();
   9311      }
   9312 
   9313      // Link our new SHEntry to the old SHEntry's back/forward
   9314      // cache data, since the two SHEntries correspond to the
   9315      // same document.
   9316      if (mLSHE) {
   9317        if (!aLoadState->LoadIsFromSessionHistory()) {
   9318          // If we're not doing a history load, scroll restoration
   9319          // should be inherited from the previous session history entry.
   9320          SetScrollRestorationIsManualOnHistoryEntry(mLSHE,
   9321                                                     scrollRestorationIsManual);
   9322        }
   9323        mLSHE->AdoptBFCacheEntry(mOSHE);
   9324      }
   9325    }
   9326  } else {
   9327    if (mActiveEntry) {
   9328      mActiveEntry->SetScrollPosition(scrollPos.x, scrollPos.y);
   9329      if (mBrowsingContext) {
   9330        CollectWireframe();
   9331        if (XRE_IsParentProcess()) {
   9332          SessionHistoryEntry* entry =
   9333              mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
   9334          if (entry) {
   9335            entry->SetScrollPosition(scrollPos.x, scrollPos.y);
   9336          }
   9337        } else {
   9338          (void)ContentChild::GetSingleton()
   9339              ->SendSessionHistoryEntryScrollPosition(mBrowsingContext,
   9340                                                      scrollPos.x, scrollPos.y);
   9341        }
   9342      }
   9343    }
   9344    if (mLoadingEntry) {
   9345      if (!mLoadingEntry->mLoadIsFromSessionHistory) {
   9346        // If we're not doing a history load, scroll restoration
   9347        // should be inherited from the previous session history entry.
   9348        // XXX This needs most probably tweaks once fragment navigation is
   9349        // fixed to work with session-history-in-parent.
   9350        SetScrollRestorationIsManualOnHistoryEntry(nullptr,
   9351                                                   scrollRestorationIsManual);
   9352      }
   9353    }
   9354  }
   9355 
   9356  // If we're doing a history load, use its scroll restoration state.
   9357  if (aLoadState->LoadIsFromSessionHistory()) {
   9358    if (mozilla::SessionHistoryInParent()) {
   9359      scrollRestorationIsManual = aLoadState->GetLoadingSessionHistoryInfo()
   9360                                      ->mInfo.GetScrollRestorationIsManual();
   9361    } else {
   9362      scrollRestorationIsManual =
   9363          aLoadState->SHEntry()->GetScrollRestorationIsManual();
   9364    }
   9365  }
   9366 
   9367  /* Assign mLSHE to mOSHE. This will either be a new entry created
   9368   * by OnNewURI() for normal loads or aLoadState->SHEntry() for history
   9369   * loads.
   9370   */
   9371  if (!mozilla::SessionHistoryInParent()) {
   9372    if (mLSHE) {
   9373      SetHistoryEntryAndUpdateBC(Nothing(), Some<nsISHEntry*>(mLSHE));
   9374      // Save the postData obtained from the previous page
   9375      // in to the session history entry created for the
   9376      // anchor page, so that any history load of the anchor
   9377      // page will restore the appropriate postData.
   9378      if (postData) {
   9379        mOSHE->SetPostData(postData);
   9380      }
   9381 
   9382      // Make sure we won't just repost without hitting the
   9383      // cache first
   9384      if (cacheKey != 0) {
   9385        mOSHE->SetCacheKey(cacheKey);
   9386      }
   9387 
   9388      // As the document has not changed, the referrer info hasn't changed too,
   9389      // so we can just copy it over.
   9390      if (referrerInfo) {
   9391        mOSHE->SetReferrerInfo(referrerInfo);
   9392      }
   9393    }
   9394 
   9395    /* Set the title for the SH entry for this target url so that
   9396     * SH menus in go/back/forward buttons won't be empty for this.
   9397     * Note, this happens on mOSHE (and mActiveEntry in the future) because of
   9398     * the code above.
   9399     * Note, when session history lives in the parent process, this does not
   9400     * update the title there.
   9401     */
   9402    SetTitleOnHistoryEntry(false);
   9403  } else {
   9404    if (aLoadState->LoadIsFromSessionHistory()) {
   9405      MOZ_LOG(
   9406          gSHLog, LogLevel::Debug,
   9407          ("Moving the loading entry to the active entry on nsDocShell %p to "
   9408           "%s",
   9409           this, mLoadingEntry->mInfo.GetURI()->GetSpecOrDefault().get()));
   9410 
   9411      nsCOMPtr<nsILayoutHistoryState> currentLayoutHistoryState;
   9412      if (mActiveEntry) {
   9413        currentLayoutHistoryState = mActiveEntry->GetLayoutHistoryState();
   9414      }
   9415 
   9416      UniquePtr<SessionHistoryInfo> previousActiveEntry(mActiveEntry.release());
   9417      mActiveEntry = MakeUnique<SessionHistoryInfo>(mLoadingEntry->mInfo);
   9418      if (currentLayoutHistoryState) {
   9419        // Restore the existing nsILayoutHistoryState object, since it is
   9420        // possibly being used by the layout. When doing a new load, the
   9421        // shared state is copied from the existing active entry, so this
   9422        // special case is needed only with the history loads.
   9423        mActiveEntry->SetLayoutHistoryState(currentLayoutHistoryState);
   9424      }
   9425 
   9426      if (cacheKey != 0) {
   9427        mActiveEntry->SetCacheKey(cacheKey);
   9428      }
   9429 
   9430      // We're passing in mCurrentURI, which could be null. SessionHistoryCommit
   9431      // does require a non-null uri if this is for a refresh load of the same
   9432      // URI, but in that case mCurrentURI won't be null here.
   9433      mBrowsingContext->SessionHistoryCommit(
   9434          *mLoadingEntry, mLoadType, mCurrentURI, previousActiveEntry.get(),
   9435          true,
   9436          /* No expiration update on the same document loads*/
   9437          false, cacheKey);
   9438      // FIXME Need to set postdata.
   9439 
   9440      // Set the title for the SH entry for this target url so that
   9441      // SH menus in go/back/forward buttons won't be empty for this.
   9442      // Note, when session history lives in the parent process, this does not
   9443      // update the title there.
   9444      SetTitleOnHistoryEntry(false);
   9445    } else {
   9446      Maybe<bool> scrollRestorationIsManual;
   9447      if (mActiveEntry) {
   9448        scrollRestorationIsManual.emplace(
   9449            mActiveEntry->GetScrollRestorationIsManual());
   9450 
   9451        // Get the postdata, page ident and referrer info from the current page,
   9452        // if the new load is being done via normal means.  Note that "normal
   9453        // means" can be checked for just by checking for LOAD_CMD_NORMAL, given
   9454        // the loadType and allowScroll check above -- it filters out some
   9455        // LOAD_CMD_NORMAL cases that we wouldn't want here.
   9456        if (aLoadState->LoadType() & LOAD_CMD_NORMAL) {
   9457          postData = mActiveEntry->GetPostData();
   9458          cacheKey = mActiveEntry->GetCacheKey();
   9459          referrerInfo = mActiveEntry->GetReferrerInfo();
   9460        }
   9461      }
   9462 
   9463      MOZ_LOG(gSHLog, LogLevel::Debug,
   9464              ("Creating an active entry on nsDocShell %p to %s", this,
   9465               newURI->GetSpecOrDefault().get()));
   9466      UniquePtr<SessionHistoryInfo> previousActiveEntry(mActiveEntry.release());
   9467      if (previousActiveEntry) {
   9468        mActiveEntry =
   9469            MakeUnique<SessionHistoryInfo>(*previousActiveEntry, newURI);
   9470      } else {
   9471        mActiveEntry = MakeUnique<SessionHistoryInfo>(
   9472            newURI, newURITriggeringPrincipal, newURIPrincipalToInherit,
   9473            newURIPartitionedPrincipalToInherit, newPolicyContainer,
   9474            mContentTypeHint);
   9475      }
   9476 
   9477      // Save the postData obtained from the previous page in to the session
   9478      // history entry created for the anchor page, so that any history load of
   9479      // the anchor page will restore the appropriate postData.
   9480      if (postData) {
   9481        mActiveEntry->SetPostData(postData);
   9482      }
   9483 
   9484      // Make sure we won't just repost without hitting the
   9485      // cache first
   9486      if (cacheKey != 0) {
   9487        mActiveEntry->SetCacheKey(cacheKey);
   9488      }
   9489 
   9490      // As the document has not changed, the referrer info hasn't changed too,
   9491      // so we can just copy it over.
   9492      if (referrerInfo) {
   9493        mActiveEntry->SetReferrerInfo(referrerInfo);
   9494      }
   9495 
   9496      // Set the title for the SH entry for this target url so that
   9497      // SH menus in go/back/forward buttons won't be empty for this.
   9498      mActiveEntry->SetTitle(mTitle);
   9499 
   9500      if (scrollRestorationIsManual.isSome()) {
   9501        mActiveEntry->SetScrollRestorationIsManual(
   9502            scrollRestorationIsManual.value());
   9503      }
   9504 
   9505      if (destinationNavigationAPIState) {
   9506        mActiveEntry->SetNavigationAPIState(destinationNavigationAPIState);
   9507      }
   9508 
   9509      if (LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY)) {
   9510        if (previousActiveEntry) {
   9511          mActiveEntry->NavigationKey() = previousActiveEntry->NavigationKey();
   9512        }
   9513        mBrowsingContext->ReplaceActiveSessionHistoryEntry(mActiveEntry.get());
   9514      } else {
   9515        mBrowsingContext->IncrementHistoryEntryCountForBrowsingContext();
   9516        // FIXME We should probably just compute mChildOffset in the parent
   9517        //       instead of passing it over IPC here.
   9518        mBrowsingContext->SetActiveSessionHistoryEntry(
   9519            Some(scrollPos), mActiveEntry.get(), previousActiveEntry.get(),
   9520            mLoadType, cacheKey);
   9521        // FIXME Do we need to update mPreviousEntryIndex and mLoadedEntryIndex?
   9522      }
   9523    }
   9524  }
   9525 
   9526  if (locationChangeNeeded) {
   9527    FireOnLocationChange(this, nullptr, newURI, locationChangeFlags);
   9528  }
   9529 
   9530  /* Restore the original LSHE if we were loading something
   9531   * while same document navigation was initiated.
   9532   */
   9533  SetHistoryEntryAndUpdateBC(Some<nsISHEntry*>(oldLSHE), Nothing());
   9534  mLoadingEntry.swap(oldLoadingEntry);
   9535 
   9536  /* Set the title for the Global History entry for this anchor url.
   9537   */
   9538  UpdateGlobalHistoryTitle(newURI);
   9539 
   9540  SetDocCurrentStateObj(mOSHE, mActiveEntry.get());
   9541 
   9542  // Inform the favicon service that the favicon for oldURI also
   9543  // applies to newURI.
   9544  CopyFavicon(currentURI, newURI, UsePrivateBrowsing());
   9545 
   9546  RefPtr<nsGlobalWindowOuter> scriptGlobal = mScriptGlobal;
   9547  nsCOMPtr<nsPIDOMWindowInner> win =
   9548      scriptGlobal ? scriptGlobal->GetCurrentInnerWindow() : nullptr;
   9549 
   9550  // https://html.spec.whatwg.org/#scroll-to-fragid
   9551  // 14. Update document for history step application given navigable's
   9552  //     active document, historyEntry, true, scriptHistoryIndex,
   9553  //     scriptHistoryLength, and historyHandling.
   9554  if (RefPtr navigation = win ? win->Navigation() : nullptr) {
   9555    MOZ_LOG(gNavigationAPILog, LogLevel::Debug,
   9556            ("nsDocShell %p triggering a navigation event from "
   9557             "HandleSameDocumentNavigation",
   9558             this));
   9559    // https://html.spec.whatwg.org/#update-document-for-history-step-application
   9560    // 6.4.2. Update the navigation API entries for a same-document
   9561    //        navigation given navigation, entry, and navigationType.
   9562    navigation->UpdateEntriesForSameDocumentNavigation(
   9563        mActiveEntry.get(),
   9564        NavigationUtils::NavigationTypeFromLoadType(mLoadType).valueOr(
   9565            NavigationType::Push));
   9566  }
   9567 
   9568  // The check for uninvoked directives must come before ScrollToAnchor() is
   9569  // called.
   9570  const bool hasTextDirectives =
   9571      doc->FragmentDirective()->HasUninvokedDirectives();
   9572 
   9573  // https://html.spec.whatwg.org/#scroll-to-fragid
   9574  // 15. Scroll to the fragment given navigable's active document.
   9575 
   9576  // ScrollToAnchor doesn't necessarily cause us to scroll the window;
   9577  // the function decides whether a scroll is appropriate based on the
   9578  // arguments it receives.  But even if we don't end up scrolling,
   9579  // ScrollToAnchor performs other important tasks, such as informing
   9580  // the presShell that we have a new hash.  See bug 680257.
   9581  nsresult rv = ScrollToAnchor(aState.mCurrentURIHasRef, aState.mNewURIHasRef,
   9582                               aState.mNewHash, aLoadState->LoadType());
   9583  NS_ENSURE_SUCCESS(rv, rv);
   9584 
   9585  /* restore previous position of scroller(s), if we're moving
   9586   * back in history (bug 59774)
   9587   */
   9588  nscoord bx = 0;
   9589  nscoord by = 0;
   9590  bool needsScrollPosUpdate = false;
   9591  if ((mozilla::SessionHistoryInParent() ? !!mActiveEntry : !!mOSHE) &&
   9592      (aLoadState->LoadType() == LOAD_HISTORY ||
   9593       aLoadState->LoadType() == LOAD_RELOAD_NORMAL) &&
   9594      !scrollRestorationIsManual) {
   9595    needsScrollPosUpdate = true;
   9596    if (mozilla::SessionHistoryInParent()) {
   9597      mActiveEntry->GetScrollPosition(&bx, &by);
   9598    } else {
   9599      mOSHE->GetScrollPosition(&bx, &by);
   9600    }
   9601  }
   9602 
   9603  // Dispatch the popstate and hashchange events, as appropriate.
   9604  //
   9605  // The event dispatch below can cause us to re-enter script and
   9606  // destroy the docshell, nulling out mScriptGlobal. Hold a stack
   9607  // reference to avoid null derefs. See bug 914521.
   9608  if (win) {
   9609    // Fire a hashchange event URIs differ, and only in their hashes.
   9610    // If the fragment contains a directive, compare hasRef.
   9611    bool doHashchange = aState.mSameExceptHashes &&
   9612                        (!aState.mCurrentHash.Equals(aState.mNewHash) ||
   9613                         (hasTextDirectives &&
   9614                          aState.mCurrentURIHasRef != aState.mNewURIHasRef));
   9615 
   9616    if (aState.mHistoryNavBetweenSameDoc || doHashchange) {
   9617      win->DispatchSyncPopState();
   9618    }
   9619 
   9620    if (needsScrollPosUpdate && win->HasActiveDocument()) {
   9621      SetCurScrollPosEx(bx, by);
   9622    }
   9623 
   9624    if (doHashchange) {
   9625      // Note that currentURI hasn't changed because it's on the
   9626      // stack, so we can just use it directly as the old URI.
   9627      win->DispatchAsyncHashchange(currentURI, newURI);
   9628    }
   9629  }
   9630 
   9631  return NS_OK;
   9632 }
   9633 
   9634 static bool NavigationShouldTakeFocus(nsDocShell* aDocShell,
   9635                                      nsDocShellLoadState* aLoadState) {
   9636  if (!aLoadState->AllowFocusMove()) {
   9637    return false;
   9638  }
   9639  if (!aLoadState->HasValidUserGestureActivation()) {
   9640    return false;
   9641  }
   9642  const auto& sourceBC = aLoadState->SourceBrowsingContext();
   9643  if (!sourceBC || !sourceBC->IsActive()) {
   9644    // If the navigation didn't come from a foreground tab, then we don't steal
   9645    // focus.
   9646    return false;
   9647  }
   9648  auto* bc = aDocShell->GetBrowsingContext();
   9649  if (sourceBC.get() == bc) {
   9650    // If it comes from the same tab / frame, don't steal focus either.
   9651    return false;
   9652  }
   9653  auto* fm = nsFocusManager::GetFocusManager();
   9654  if (fm && bc->IsActive() && fm->IsInActiveWindow(bc)) {
   9655    // If we're already on the foreground tab of the foreground window, then we
   9656    // don't need to do this. This helps to e.g. not steal focus from the
   9657    // browser chrome unnecessarily.
   9658    return false;
   9659  }
   9660  if (auto* doc = aDocShell->GetExtantDocument()) {
   9661    if (doc->IsInitialDocument()) {
   9662      // If we're the initial load for the browsing context, the browser
   9663      // chrome determines what to focus. This is important because the
   9664      // browser chrome may want to e.g focus the url-bar
   9665      return false;
   9666    }
   9667  }
   9668  // Take loadDivertedInBackground into account so the behavior would be the
   9669  // same as how the tab first opened.
   9670  return !Preferences::GetBool("browser.tabs.loadDivertedInBackground", false);
   9671 }
   9672 
   9673 uint32_t nsDocShell::GetLoadTypeForFormSubmission(
   9674    BrowsingContext* aTargetBC, nsDocShellLoadState* aLoadState) {
   9675  MOZ_ASSERT(aLoadState->IsFormSubmission());
   9676 
   9677  // https://html.spec.whatwg.org/#form-submission-algorithm
   9678  //  22. Let historyHandling be "push".
   9679  //  23. If form document equals targetNavigable's active document, and
   9680  //      form document has not yet completely loaded, then set
   9681  //      historyHandling to "replace".
   9682  return GetBrowsingContext() == aTargetBC && !mEODForCurrentDocument
   9683             ? LOAD_NORMAL_REPLACE
   9684             : LOAD_LINK;
   9685 }
   9686 
   9687 static void MaybeConvertToReplaceLoad(nsDocShellLoadState* aLoadState,
   9688                                      Document* aExtantDocument,
   9689                                      bool aIdenticalURI,
   9690                                      bool aHasActiveEntry) {
   9691  // MaybeConvertToReplaceLoad implements steps 12 and 13 of #navigate, but
   9692  // since we're not yet using historyBehavior for all types of loads and
   9693  // configurations, we need to sometimes bail and revert to the old way of
   9694  // handling push to replace load conversion. The different cases we can't
   9695  // handle are:
   9696  //
   9697  // * When we don't have an active document
   9698  // * When a document doesn't yet have a session history entry
   9699  // * When we don't use SHIP
   9700  // * When we don't use historyBehavior
   9701  if (!aExtantDocument || !aHasActiveEntry ||
   9702      !mozilla::SessionHistoryInParent() || !aLoadState->HistoryBehavior()) {
   9703    aLoadState->ResetHistoryBehavior();
   9704    return;
   9705  }
   9706 
   9707  bool convertToReplaceLoad = aLoadState->NeedsCompletelyLoadedDocument() &&
   9708                              !aExtantDocument->IsCompletelyLoaded();
   9709  if (const auto& historyBehavior = aLoadState->HistoryBehavior();
   9710      !convertToReplaceLoad && historyBehavior &&
   9711      *historyBehavior == NavigationHistoryBehavior::Auto) {
   9712    convertToReplaceLoad = aIdenticalURI;
   9713    if (convertToReplaceLoad && aExtantDocument->GetPrincipal()) {
   9714      aExtantDocument->GetPrincipal()->Equals(aLoadState->TriggeringPrincipal(),
   9715                                              &convertToReplaceLoad);
   9716    }
   9717  }
   9718 
   9719  convertToReplaceLoad =
   9720      convertToReplaceLoad || nsContentUtils::NavigationMustBeAReplace(
   9721                                  *aLoadState->URI(), *aExtantDocument);
   9722 
   9723  if (convertToReplaceLoad) {
   9724    MOZ_LOG_FMT(gNavigationAPILog, LogLevel::Debug,
   9725                "Convert to replace when navigating from {} to {}, {}",
   9726                *aExtantDocument->GetDocumentURI(), *aLoadState->URI(),
   9727                (aLoadState->NeedsCompletelyLoadedDocument() &&
   9728                 !nsContentUtils::NavigationMustBeAReplace(*aLoadState->URI(),
   9729                                                           *aExtantDocument))
   9730                    ? "needs completely loaded document"
   9731                    : "navigation must be a replace");
   9732    // There is no replace variant for LOAD_LINK, so we convert it to
   9733    // LOAD_NORMAL_REPLACE just like in nsDocShell::OnNewURI.
   9734    if (aLoadState->LoadType() == LOAD_LINK) {
   9735      aLoadState->SetLoadType(LOAD_NORMAL_REPLACE);
   9736    } else {
   9737      aLoadState->SetLoadType(
   9738          MaybeAddLoadFlags(aLoadState->LoadType(),
   9739                            nsIWebNavigation::LOAD_FLAGS_REPLACE_HISTORY));
   9740    }
   9741    aLoadState->SetHistoryBehavior(NavigationHistoryBehavior::Replace);
   9742  } else {
   9743    aLoadState->SetHistoryBehavior(NavigationHistoryBehavior::Push);
   9744  }
   9745 }
   9746 
   9747 // InternalLoad performs several of the steps from
   9748 // https://html.spec.whatwg.org/#navigate.
   9749 nsresult nsDocShell::InternalLoad(nsDocShellLoadState* aLoadState,
   9750                                  Maybe<uint32_t> aCacheKey) {
   9751  MOZ_ASSERT(aLoadState, "need a load state!");
   9752  MOZ_ASSERT(aLoadState->TriggeringPrincipal(),
   9753             "need a valid TriggeringPrincipal");
   9754 
   9755  if (!aLoadState->TriggeringPrincipal()) {
   9756    MOZ_ASSERT(false, "InternalLoad needs a valid triggeringPrincipal");
   9757    return NS_ERROR_FAILURE;
   9758  }
   9759  if (NS_WARN_IF(mBrowsingContext->GetPendingInitialization())) {
   9760    return NS_ERROR_NOT_AVAILABLE;
   9761  }
   9762 
   9763  const bool shouldTakeFocus = NavigationShouldTakeFocus(this, aLoadState);
   9764 
   9765  mOriginalUriString.Truncate();
   9766 
   9767  MOZ_LOG(gDocShellLeakLog, LogLevel::Debug,
   9768          ("DOCSHELL %p InternalLoad %s\n", this,
   9769           aLoadState->URI()->GetSpecOrDefault().get()));
   9770 
   9771  NS_ENSURE_TRUE(IsValidLoadType(aLoadState->LoadType()), NS_ERROR_INVALID_ARG);
   9772 
   9773  // Cancel loads coming from Docshells that are being destroyed.
   9774  if (mIsBeingDestroyed) {
   9775    return NS_ERROR_NOT_AVAILABLE;
   9776  }
   9777 
   9778  nsresult rv = EnsureScriptEnvironment();
   9779  if (NS_FAILED(rv)) {
   9780    return rv;
   9781  }
   9782 
   9783  // If we have a target to move to, do that now.
   9784  if (!aLoadState->Target().IsEmpty()) {
   9785    return PerformRetargeting(aLoadState);
   9786  }
   9787 
   9788  // This is the non-retargeting load path, we've already set the right loadtype
   9789  // for form submissions in nsDocShell::OnLinkClickSync.
   9790  if (aLoadState->TargetBrowsingContext().IsNull()) {
   9791    aLoadState->SetTargetBrowsingContext(GetBrowsingContext());
   9792  }
   9793 
   9794  MOZ_DIAGNOSTIC_ASSERT(
   9795      aLoadState->TargetBrowsingContext() == GetBrowsingContext(),
   9796      "Load must be targeting this BrowsingContext");
   9797 
   9798  MOZ_TRY(CheckDisallowedJavascriptLoad(aLoadState));
   9799 
   9800  // If we don't have a target, we're loading into ourselves, and our load
   9801  // delegate may want to intercept that load.
   9802  SameDocumentNavigationState sameDocumentNavigationState;
   9803  bool sameDocument =
   9804      IsSameDocumentNavigation(aLoadState, sameDocumentNavigationState) &&
   9805      !aLoadState->GetPendingRedirectedChannel();
   9806 
   9807  if (mLoadType != LOAD_ERROR_PAGE &&
   9808      !aLoadState->HasLoadFlags(LOAD_FLAGS_FROM_EXTERNAL)) {
   9809    MaybeConvertToReplaceLoad(aLoadState, GetExtantDocument(),
   9810                              sameDocumentNavigationState.mIdentical,
   9811                              !!mActiveEntry);
   9812  }
   9813 
   9814  // Note: We do this check both here and in BrowsingContext::
   9815  // LoadURI/InternalLoad, since document-specific sandbox flags are only
   9816  // available in the process triggering the load, and we don't want the target
   9817  // process to have to trust the triggering process to do the appropriate
   9818  // checks for the BrowsingContext's sandbox flags.
   9819  MOZ_TRY(mBrowsingContext->CheckSandboxFlags(aLoadState));
   9820  MOZ_TRY(mBrowsingContext->CheckFramebusting(aLoadState));
   9821 
   9822  NS_ENSURE_STATE(!HasUnloadedParent());
   9823 
   9824  rv = CheckLoadingPermissions();
   9825  if (NS_FAILED(rv)) {
   9826    return rv;
   9827  }
   9828 
   9829  if (mFiredUnloadEvent) {
   9830    if (IsOKToLoadURI(aLoadState->URI())) {
   9831      MOZ_ASSERT(aLoadState->Target().IsEmpty(),
   9832                 "Shouldn't have a window target here!");
   9833 
   9834      // If this is a replace load, make whatever load triggered
   9835      // the unload event also a replace load, so we don't
   9836      // create extra history entries.
   9837      if (LOAD_TYPE_HAS_FLAGS(aLoadState->LoadType(),
   9838                              LOAD_FLAGS_REPLACE_HISTORY)) {
   9839        mLoadType = LOAD_NORMAL_REPLACE;
   9840      }
   9841 
   9842      // Do this asynchronously
   9843      nsCOMPtr<nsIRunnable> ev = new InternalLoadEvent(this, aLoadState);
   9844      return Dispatch(ev.forget());
   9845    }
   9846 
   9847    // Just ignore this load attempt
   9848    return NS_OK;
   9849  }
   9850 
   9851  // If we are loading a URI that should inherit a security context (basically
   9852  // javascript: at this point), and the caller has said that principal
   9853  // inheritance is allowed, there are a few possible cases:
   9854  //
   9855  // 1) We are provided with the principal to inherit. In that case, we just use
   9856  //    it.
   9857  //
   9858  // 2) The load is coming from some other application. In this case we don't
   9859  //    want to inherit from whatever document we have loaded now, since the
   9860  //    load is unrelated to it.
   9861  //
   9862  // 3) It's a load from our application, but does not provide an explicit
   9863  //    principal to inherit. In that case, we want to inherit the principal of
   9864  //    our current document, or of our parent document (if any) if we don't
   9865  //    have a current document.
   9866  {
   9867    bool inherits;
   9868 
   9869    if (!aLoadState->HasLoadFlags(LOAD_FLAGS_FROM_EXTERNAL) &&
   9870        !aLoadState->PrincipalToInherit() &&
   9871        (aLoadState->HasInternalLoadFlags(
   9872            INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL)) &&
   9873        NS_SUCCEEDED(nsContentUtils::URIInheritsSecurityContext(
   9874            aLoadState->URI(), &inherits)) &&
   9875        inherits) {
   9876      aLoadState->SetPrincipalToInherit(GetInheritedPrincipal(true));
   9877    }
   9878    // If principalToInherit is still null (e.g. if some of the conditions of
   9879    // were not satisfied), then no inheritance of any sort will happen: the
   9880    // load will just get a principal based on the URI being loaded.
   9881  }
   9882 
   9883  // If this docshell is owned by a frameloader, make sure to cancel
   9884  // possible frameloader initialization before loading a new page.
   9885  nsCOMPtr<nsIDocShellTreeItem> parent = GetInProcessParentDocshell();
   9886  if (parent) {
   9887    RefPtr<Document> doc = parent->GetDocument();
   9888    if (doc) {
   9889      doc->TryCancelFrameLoaderInitialization(this);
   9890    }
   9891  }
   9892 
   9893  // Before going any further vet loads initiated by external programs.
   9894  if (aLoadState->HasLoadFlags(LOAD_FLAGS_FROM_EXTERNAL)) {
   9895    MOZ_DIAGNOSTIC_ASSERT(aLoadState->LoadType() == LOAD_NORMAL);
   9896 
   9897    // Disallow external chrome: loads targetted at content windows
   9898    if (aLoadState->URI()->SchemeIs("chrome")) {
   9899      NS_WARNING("blocked external chrome: url -- use '--chrome' option");
   9900      return NS_ERROR_FAILURE;
   9901    }
   9902 
   9903    // clear the decks to prevent context bleed-through (bug 298255)
   9904    rv = CreateAboutBlankDocumentViewer(nullptr, nullptr, nullptr, nullptr,
   9905                                        /* aIsInitialDocument */ false);
   9906    if (NS_FAILED(rv)) {
   9907      return NS_ERROR_FAILURE;
   9908    }
   9909 
   9910    if (Document* doc = GetDocument()) {
   9911      doc->DisallowBFCaching();
   9912    }
   9913  }
   9914 
   9915  mAllowKeywordFixup = aLoadState->HasInternalLoadFlags(
   9916      INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP);
   9917  mURIResultedInDocument = false;  // reset the clock...
   9918 
   9919  // If container is an iframe element and will lazy load element steps given
   9920  // container returns true, then stop intersection-observing a lazy loading
   9921  // element container and set container's lazy load resumption steps to null.
   9922  if (IsSubframe()) {
   9923    if (auto* iframe = HTMLIFrameElement::FromNodeOrNull(
   9924            mBrowsingContext->GetEmbedderElement())) {
   9925      // Per spec, reload doesn't cancel lazy loading iframes.
   9926      if (!(aLoadState->LoadType() & LOAD_RELOAD_NORMAL)) {
   9927        iframe->CancelLazyLoading(true /* aClearLazyLoadState */);
   9928      }
   9929    }
   9930  }
   9931 
   9932  // See if this is actually a load between two history entries for the same
   9933  // document. If the process fails, or if we successfully navigate within the
   9934  // same document, return.
   9935  if (sameDocument) {
   9936    nsresult rv = HandleSameDocumentNavigation(
   9937        aLoadState, sameDocumentNavigationState, sameDocument);
   9938    NS_ENSURE_SUCCESS(rv, rv);
   9939    if (shouldTakeFocus) {
   9940      mBrowsingContext->Focus(CallerType::System, IgnoreErrors());
   9941    }
   9942    if (sameDocument) {
   9943      if (aLoadState->LoadIsFromSessionHistory() &&
   9944          (mLoadType & LOAD_CMD_HISTORY)) {
   9945        SetOngoingNavigation(Nothing());
   9946      }
   9947      return rv;
   9948    }
   9949  }
   9950 
   9951  // mDocumentViewer->PermitUnload can destroy |this| docShell, which
   9952  // causes the next call of CanSavePresentation to crash.
   9953  // Hold onto |this| until we return, to prevent a crash from happening.
   9954  // (bug#331040)
   9955  nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
   9956 
   9957  // Don't init timing for javascript:, since it generally doesn't
   9958  // actually start a load or anything.  If it does, we'll init
   9959  // timing then, from OnStateChange.
   9960 
   9961  // XXXbz mTiming should know what channel it's for, so we don't
   9962  // need this hackery.
   9963  const bool isJavaScript = aLoadState->URI()->SchemeIs("javascript");
   9964  const bool isExternalProtocol =
   9965      nsContentUtils::IsExternalProtocol(aLoadState->URI());
   9966  const bool isDownload = !aLoadState->FileName().IsVoid();
   9967  const bool toBeReset = !isJavaScript && MaybeInitTiming();
   9968 
   9969  // FIXME(emilio): Should this be done by javascript: uris? What about external
   9970  // protocols?
   9971  if (mTiming && !isDownload) {
   9972    mTiming->NotifyBeforeUnload();
   9973  }
   9974 
   9975  // The following steps are from https://html.spec.whatwg.org/#navigate
   9976  // Step 19, and here we actually also perform step 2 from
   9977  // #navigate-to-a-javascript:-url (step 20) where the ongoing navigation is
   9978  // set to null.
   9979  SetOngoingNavigation(isJavaScript ? Nothing()
   9980                                    : Some(OngoingNavigation::NavigationID));
   9981 
   9982  // Step 21
   9983  if (RefPtr<Document> document = GetDocument();
   9984      !aLoadState->LoadIsFromSessionHistory() && document &&
   9985      aLoadState->UserNavigationInvolvement() !=
   9986          UserNavigationInvolvement::BrowserUI &&
   9987      !document->IsInitialDocument() &&
   9988      !NS_IsAboutBlankAllowQueryAndFragment(document->GetDocumentURI()) &&
   9989      NS_IsFetchScheme(aLoadState->URI()) &&
   9990      document->NodePrincipal()->EqualsConsideringDomain(
   9991          aLoadState->TriggeringPrincipal())) {
   9992    if (nsCOMPtr<nsPIDOMWindowInner> window = document->GetInnerWindow()) {
   9993      // Step 21.1
   9994      if (RefPtr<Navigation> navigation = window->Navigation()) {
   9995        AutoJSAPI jsapi;
   9996        if (jsapi.Init(window)) {
   9997          RefPtr<Element> sourceElement = aLoadState->GetSourceElement();
   9998 
   9999          // Step 21.2
  10000          RefPtr<FormData> formData = aLoadState->GetFormDataEntryList();
  10001 
  10002          // Step 21.3
  10003          RefPtr<nsIStructuredCloneContainer> navigationAPIStateForFiring =
  10004              aLoadState->GetNavigationAPIState();
  10005 
  10006          nsCOMPtr<nsIURI> destinationURL = aLoadState->URI();
  10007          // Step 21.4
  10008          RefPtr apiMethodTracker = aLoadState->GetNavigationAPIMethodTracker();
  10009          bool shouldContinue = navigation->FirePushReplaceReloadNavigateEvent(
  10010              jsapi.cx(), aLoadState->GetNavigationType(), destinationURL,
  10011              /* aIsSameDocument */ false,
  10012              Some(aLoadState->UserNavigationInvolvement()), sourceElement,
  10013              formData, navigationAPIStateForFiring,
  10014              /* aClassicHistoryAPIState */ nullptr, apiMethodTracker);
  10015 
  10016          // Step 21.5
  10017          if (!shouldContinue) {
  10018            return NS_OK;
  10019          }
  10020        }
  10021      }
  10022    }
  10023  }
  10024 
  10025  // Check if the page doesn't want to be unloaded. The javascript:
  10026  // protocol handler deals with this for javascript: URLs.
  10027  // NOTE(emilio): As of this writing, other browsers fire beforeunload for
  10028  // external protocols, so keep doing that even though they don't return data
  10029  // and thus we won't really unload this...
  10030  if (!isJavaScript && !isDownload &&
  10031      !aLoadState->NotifiedBeforeUnloadListeners() && mDocumentViewer) {
  10032    // Check if request is exempted from HTTPSOnlyMode and if https-first is
  10033    // enabled, if so it means:
  10034    //    * https-first failed to upgrade request to https
  10035    //    * we already asked for permission to unload and the user accepted
  10036    //      otherwise we wouldn't be here.
  10037    const bool isPrivateWin = GetOriginAttributes().IsPrivateBrowsing();
  10038    const uint32_t loadType = aLoadState->LoadType();
  10039 
  10040    // Check if request is a reload.
  10041    const bool isHistoryOrReload =
  10042        loadType == LOAD_RELOAD_NORMAL ||
  10043        loadType == LOAD_RELOAD_BYPASS_CACHE ||
  10044        loadType == LOAD_RELOAD_BYPASS_PROXY ||
  10045        loadType == LOAD_RELOAD_BYPASS_PROXY_AND_CACHE ||
  10046        loadType == LOAD_HISTORY;
  10047 
  10048    // If it isn't a reload, the request already failed to be upgraded and
  10049    // https-first is enabled then don't ask the user again for permission to
  10050    // unload and just unload.
  10051    bool okToUnload;
  10052    if (!isHistoryOrReload && aLoadState->IsExemptFromHTTPSFirstMode() &&
  10053        nsHTTPSOnlyUtils::GetUpgradeMode(isPrivateWin) ==
  10054            nsHTTPSOnlyUtils::HTTPS_FIRST_MODE) {
  10055      rv = mDocumentViewer->PermitUnload(
  10056          nsIDocumentViewer::PermitUnloadAction::eDontPromptAndUnload,
  10057          &okToUnload);
  10058    } else {
  10059      rv = mDocumentViewer->PermitUnload(&okToUnload);
  10060      if (mIsBeingDestroyed) {
  10061        // unload handler destroyed this docshell.
  10062        return NS_ERROR_NOT_AVAILABLE;
  10063      }
  10064    }
  10065 
  10066    if (NS_SUCCEEDED(rv) && !okToUnload) {
  10067      // The user chose not to unload the page, interrupt the
  10068      // load.
  10069      MaybeResetInitTiming(toBeReset);
  10070      return NS_OK;
  10071    }
  10072  }
  10073 
  10074  if (mTiming && !isDownload) {
  10075    mTiming->NotifyUnloadAccepted(mCurrentURI);
  10076  }
  10077 
  10078  // In e10s, in the parent process, we refuse to load anything other than
  10079  // "safe" resources that we ship or trust enough to give "special" URLs.
  10080  // Similar check will be performed by the ParentProcessDocumentChannel if in
  10081  // use.
  10082  if (XRE_IsE10sParentProcess() &&
  10083      !DocumentChannel::CanUseDocumentChannel(aLoadState->URI()) &&
  10084      !CanLoadInParentProcess(aLoadState->URI())) {
  10085    return NS_ERROR_FAILURE;
  10086  }
  10087 
  10088  // Whenever a top-level browsing context is navigated, the user agent MUST
  10089  // lock the orientation of the document to the document's default
  10090  // orientation. We don't explicitly check for a top-level browsing context
  10091  // here because orientation is only set on top-level browsing contexts.
  10092  if (mBrowsingContext->GetOrientationLock() != hal::ScreenOrientation::None) {
  10093    MOZ_ASSERT(mBrowsingContext->IsTop());
  10094    MOZ_ALWAYS_SUCCEEDS(
  10095        mBrowsingContext->SetOrientationLock(hal::ScreenOrientation::None));
  10096    if (mBrowsingContext->IsActive()) {
  10097      ScreenOrientation::UpdateActiveOrientationLock(
  10098          hal::ScreenOrientation::None);
  10099    }
  10100  }
  10101 
  10102  // Check for saving the presentation here, before calling Stop().
  10103  // This is necessary so that we can catch any pending requests.
  10104  // Since the new request has not been created yet, we pass null for the
  10105  // new request parameter.
  10106  // Also pass nullptr for the document, since it doesn't affect the return
  10107  // value for our purposes here.
  10108  const bool savePresentation =
  10109      CanSavePresentation(aLoadState->LoadType(), nullptr, nullptr,
  10110                          /* aReportBFCacheComboTelemetry */ true);
  10111 
  10112  // nsDocShell::CanSavePresentation is for non-SHIP version only. Do a
  10113  // separate check for SHIP so that we know if there are ongoing requests
  10114  // before calling Stop() below.
  10115  if (mozilla::SessionHistoryInParent()) {
  10116    Document* document = GetDocument();
  10117    uint32_t flags = 0;
  10118    if (document && !document->CanSavePresentation(nullptr, flags, true)) {
  10119      // This forces some flags into the WindowGlobalParent's mBFCacheStatus,
  10120      // which we'll then use in CanonicalBrowsingContext::AllowedInBFCache,
  10121      // and in particular we'll store BFCacheStatus::REQUEST if needed.
  10122      // Also, we want to report all the flags to the parent process here (and
  10123      // not just BFCacheStatus::NOT_ALLOWED), so that it can update the
  10124      // telemetry data correctly.
  10125      document->DisallowBFCaching(flags);
  10126    }
  10127 
  10128    if (aLoadState->LoadIsFromSessionHistory() &&
  10129        (mLoadType & LOAD_CMD_HISTORY)) {
  10130      SetOngoingNavigation(Nothing());
  10131    }
  10132  }
  10133 
  10134  // Don't stop current network activity for javascript: URL's since they might
  10135  // not result in any data, and thus nothing should be stopped in those cases.
  10136  // In the case where they do result in data, the javascript: URL channel takes
  10137  // care of stopping current network activity. Similarly, downloads don't
  10138  // unload this document...
  10139  if (!isJavaScript && !isDownload && !isExternalProtocol) {
  10140    // Stop any current network activity.
  10141    // Also stop content if this is a zombie doc. otherwise
  10142    // the onload will be delayed by other loads initiated in the
  10143    // background by the first document that
  10144    // didn't fully load before the next load was initiated.
  10145    // If not a zombie, don't stop content until data
  10146    // starts arriving from the new URI...
  10147    if ((mDocumentViewer && mDocumentViewer->GetPreviousViewer()) ||
  10148        LOAD_TYPE_HAS_FLAGS(aLoadState->LoadType(), LOAD_FLAGS_STOP_CONTENT)) {
  10149      rv = StopInternal(nsIWebNavigation::STOP_ALL, UnsetOngoingNavigation::No);
  10150    } else {
  10151      rv = StopInternal(nsIWebNavigation::STOP_NETWORK,
  10152                        UnsetOngoingNavigation::No);
  10153    }
  10154 
  10155    if (NS_FAILED(rv)) {
  10156      return rv;
  10157    }
  10158  }
  10159 
  10160  mLoadType = aLoadState->LoadType();
  10161 
  10162  // aLoadState->SHEntry() should be assigned to mLSHE, only after Stop() has
  10163  // been called. But when loading an error page, do not clear the
  10164  // mLSHE for the real page.
  10165  if (mLoadType != LOAD_ERROR_PAGE) {
  10166    SetHistoryEntryAndUpdateBC(Some<nsISHEntry*>(aLoadState->SHEntry()),
  10167                               Nothing());
  10168    if (aLoadState->LoadIsFromSessionHistory() &&
  10169        !mozilla::SessionHistoryInParent()) {
  10170      // We're making history navigation or a reload. Make sure our history ID
  10171      // points to the same ID as SHEntry's docshell ID.
  10172      nsID historyID = {};
  10173      aLoadState->SHEntry()->GetDocshellID(historyID);
  10174 
  10175      (void)mBrowsingContext->SetHistoryID(historyID);
  10176    }
  10177  }
  10178 
  10179  mSavingOldViewer = savePresentation;
  10180 
  10181  // If we have a saved content viewer in history, restore and show it now.
  10182  if (aLoadState->LoadIsFromSessionHistory() &&
  10183      (mLoadType & LOAD_CMD_HISTORY)) {
  10184    // https://html.spec.whatwg.org/#history-traversal:
  10185    // To traverse the history
  10186    // "If entry has a different Document object than the current entry, then
  10187    // run the following substeps: Remove any tasks queued by the history
  10188    // traversal task source..."
  10189    // Same document object case was handled already above with
  10190    // HandleSameDocumentNavigation call.
  10191    RefPtr<ChildSHistory> shistory = GetRootSessionHistory();
  10192    if (shistory) {
  10193      shistory->RemovePendingHistoryNavigations();
  10194    }
  10195    if (!mozilla::SessionHistoryInParent()) {
  10196      // It's possible that the previous viewer of mDocumentViewer is the
  10197      // viewer that will end up in aLoadState->SHEntry() when it gets closed.
  10198      // If that's the case, we need to go ahead and force it into its shentry
  10199      // so we can restore it.
  10200      if (mDocumentViewer) {
  10201        nsCOMPtr<nsIDocumentViewer> prevViewer =
  10202            mDocumentViewer->GetPreviousViewer();
  10203        if (prevViewer) {
  10204 #ifdef DEBUG
  10205          nsCOMPtr<nsIDocumentViewer> prevPrevViewer =
  10206              prevViewer->GetPreviousViewer();
  10207          NS_ASSERTION(!prevPrevViewer, "Should never have viewer chain here");
  10208 #endif
  10209          nsCOMPtr<nsISHEntry> viewerEntry;
  10210          prevViewer->GetHistoryEntry(getter_AddRefs(viewerEntry));
  10211          if (viewerEntry == aLoadState->SHEntry()) {
  10212            // Make sure this viewer ends up in the right place
  10213            mDocumentViewer->SetPreviousViewer(nullptr);
  10214            prevViewer->Destroy();
  10215          }
  10216        }
  10217      }
  10218      nsCOMPtr<nsISHEntry> oldEntry = mOSHE;
  10219      bool restoring;
  10220      rv = RestorePresentation(aLoadState->SHEntry(), &restoring);
  10221      if (restoring) {
  10222        glean::bfcache::page_restored
  10223            .EnumGet(glean::bfcache::PageRestoredLabel::eTrue)
  10224            .Add();
  10225        return rv;
  10226      }
  10227      glean::bfcache::page_restored
  10228          .EnumGet(glean::bfcache::PageRestoredLabel::eFalse)
  10229          .Add();
  10230 
  10231      // We failed to restore the presentation, so clean up.
  10232      // Both the old and new history entries could potentially be in
  10233      // an inconsistent state.
  10234      if (NS_FAILED(rv)) {
  10235        if (oldEntry) {
  10236          oldEntry->SyncPresentationState();
  10237        }
  10238 
  10239        aLoadState->SHEntry()->SyncPresentationState();
  10240      }
  10241    }
  10242  }
  10243 
  10244  bool isTopLevelDoc = mBrowsingContext->IsTopContent();
  10245 
  10246  OriginAttributes attrs = GetOriginAttributes();
  10247  attrs.SetFirstPartyDomain(isTopLevelDoc, aLoadState->URI());
  10248 
  10249  nsCOMPtr<nsIRequest> req;
  10250  rv = DoURILoad(aLoadState, aCacheKey, getter_AddRefs(req));
  10251 
  10252  if (NS_SUCCEEDED(rv)) {
  10253    if (shouldTakeFocus) {
  10254      mBrowsingContext->Focus(CallerType::System, IgnoreErrors());
  10255    }
  10256  }
  10257 
  10258  if (NS_FAILED(rv)) {
  10259    nsCOMPtr<nsIChannel> chan(do_QueryInterface(req));
  10260    UnblockEmbedderLoadEventForFailure();
  10261 
  10262    // The spec says no exception should be raised for pre-navigation check
  10263    // failures.
  10264    if (NS_ERROR_DOM_SECURITY_ERR == rv) {
  10265      return NS_OK;
  10266    }
  10267 
  10268    nsCOMPtr<nsIURI> uri = aLoadState->URI();
  10269    if (DisplayLoadError(rv, uri, nullptr, chan) &&
  10270        // FIXME: At this point code was using internal load flags, but checking
  10271        // non-internal load flags?
  10272        aLoadState->HasLoadFlags(LOAD_FLAGS_ERROR_LOAD_CHANGES_RV)) {
  10273      return NS_ERROR_LOAD_SHOWED_ERRORPAGE;
  10274    }
  10275 
  10276    // We won't report any error if this is an unknown protocol error. The
  10277    // reason behind this is that it will allow enumeration of external
  10278    // protocols if we report an error for each unknown protocol.
  10279    if (NS_ERROR_UNKNOWN_PROTOCOL == rv) {
  10280      return NS_OK;
  10281    }
  10282  }
  10283 
  10284  return rv;
  10285 }
  10286 
  10287 /* static */
  10288 bool nsDocShell::CanLoadInParentProcess(nsIURI* aURI) {
  10289  nsCOMPtr<nsIURI> uri = aURI;
  10290  // In e10s, in the parent process, we refuse to load anything other than
  10291  // "safe" resources that we ship or trust enough to give "special" URLs.
  10292  bool canLoadInParent = false;
  10293  if (NS_SUCCEEDED(NS_URIChainHasFlags(
  10294          uri, nsIProtocolHandler::URI_IS_UI_RESOURCE, &canLoadInParent)) &&
  10295      canLoadInParent) {
  10296    // We allow UI resources.
  10297    return true;
  10298  }
  10299  // For about: and extension-based URIs, which don't get
  10300  // URI_IS_UI_RESOURCE, first remove layers of view-source:, if present.
  10301  while (uri && uri->SchemeIs("view-source")) {
  10302    nsCOMPtr<nsINestedURI> nested = do_QueryInterface(uri);
  10303    if (nested) {
  10304      nested->GetInnerURI(getter_AddRefs(uri));
  10305    } else {
  10306      break;
  10307    }
  10308  }
  10309  // Allow about: URIs, and allow moz-extension ones if we're running
  10310  // extension content in the parent process.
  10311  if (!uri || uri->SchemeIs("about") ||
  10312      (!StaticPrefs::extensions_webextensions_remote() &&
  10313       uri->SchemeIs("moz-extension"))) {
  10314    return true;
  10315  }
  10316 #ifdef MOZ_THUNDERBIRD
  10317  if (uri->SchemeIs("imap") || uri->SchemeIs("mailbox") ||
  10318      uri->SchemeIs("news") || uri->SchemeIs("nntp") ||
  10319      uri->SchemeIs("snews") || uri->SchemeIs("x-moz-ews")) {
  10320    return true;
  10321  }
  10322 #endif
  10323  nsAutoCString scheme;
  10324  uri->GetScheme(scheme);
  10325  // Allow ext+foo URIs (extension-registered custom protocols). See
  10326  // https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/protocol_handlers
  10327  if (StringBeginsWith(scheme, "ext+"_ns) &&
  10328      !StaticPrefs::extensions_webextensions_remote()) {
  10329    return true;
  10330  }
  10331  // Final exception for some legacy automated tests:
  10332  if (xpc::IsInAutomation() &&
  10333      StaticPrefs::security_allow_unsafe_parent_loads()) {
  10334    return true;
  10335  }
  10336  return false;
  10337 }
  10338 
  10339 nsIPrincipal* nsDocShell::GetInheritedPrincipal(
  10340    bool aConsiderCurrentDocument, bool aConsiderPartitionedPrincipal) {
  10341  RefPtr<Document> document;
  10342  bool inheritedFromCurrent = false;
  10343 
  10344  if (aConsiderCurrentDocument && mDocumentViewer) {
  10345    document = mDocumentViewer->GetDocument();
  10346    inheritedFromCurrent = true;
  10347  }
  10348 
  10349  if (!document) {
  10350    nsCOMPtr<nsIDocShellTreeItem> parentItem;
  10351    GetInProcessSameTypeParent(getter_AddRefs(parentItem));
  10352    if (parentItem) {
  10353      document = parentItem->GetDocument();
  10354    }
  10355  }
  10356 
  10357  if (!document) {
  10358    if (!aConsiderCurrentDocument) {
  10359      return nullptr;
  10360    }
  10361 
  10362    // Make sure we end up with _something_ as the principal no matter
  10363    // what.If this fails, we'll just get a null docViewer and bail.
  10364    if (!VerifyDocumentViewer()) {
  10365      return nullptr;
  10366    }
  10367    document = mDocumentViewer->GetDocument();
  10368  }
  10369 
  10370  //-- Get the document's principal
  10371  if (document) {
  10372    nsIPrincipal* docPrincipal = aConsiderPartitionedPrincipal
  10373                                     ? document->PartitionedPrincipal()
  10374                                     : document->NodePrincipal();
  10375 
  10376    // Don't allow loads in typeContent docShells to inherit the system
  10377    // principal from existing documents.
  10378    if (inheritedFromCurrent && mItemType == typeContent &&
  10379        docPrincipal->IsSystemPrincipal()) {
  10380      return nullptr;
  10381    }
  10382 
  10383    return docPrincipal;
  10384  }
  10385 
  10386  return nullptr;
  10387 }
  10388 
  10389 /* static */ nsresult nsDocShell::CreateRealChannelForDocument(
  10390    nsIChannel** aChannel, nsIURI* aURI, nsILoadInfo* aLoadInfo,
  10391    nsIInterfaceRequestor* aCallbacks, nsLoadFlags aLoadFlags,
  10392    const nsAString& aSrcdoc, nsIURI* aBaseURI) {
  10393  nsCOMPtr<nsIChannel> channel;
  10394  if (aSrcdoc.IsVoid()) {
  10395    MOZ_TRY(NS_NewChannelInternal(getter_AddRefs(channel), aURI, aLoadInfo,
  10396                                  nullptr,  // PerformanceStorage
  10397                                  nullptr,  // loadGroup
  10398                                  aCallbacks, aLoadFlags));
  10399 
  10400    if (aBaseURI) {
  10401      nsCOMPtr<nsIViewSourceChannel> vsc = do_QueryInterface(channel);
  10402      if (vsc) {
  10403        MOZ_ALWAYS_SUCCEEDS(vsc->SetBaseURI(aBaseURI));
  10404      }
  10405    }
  10406  } else if (aURI->SchemeIs("view-source")) {
  10407    // Instantiate view source handler protocol, if it doesn't exist already.
  10408    nsCOMPtr<nsIIOService> io(do_GetIOService());
  10409    MOZ_ASSERT(io);
  10410    nsCOMPtr<nsIProtocolHandler> handler;
  10411    nsresult rv =
  10412        io->GetProtocolHandler("view-source", getter_AddRefs(handler));
  10413    if (NS_FAILED(rv)) {
  10414      return rv;
  10415    }
  10416 
  10417    nsViewSourceHandler* vsh = nsViewSourceHandler::GetInstance();
  10418    if (!vsh) {
  10419      return NS_ERROR_FAILURE;
  10420    }
  10421 
  10422    MOZ_TRY(vsh->NewSrcdocChannel(aURI, aBaseURI, aSrcdoc, aLoadInfo,
  10423                                  getter_AddRefs(channel)));
  10424  } else {
  10425    MOZ_TRY(NS_NewInputStreamChannelInternal(getter_AddRefs(channel), aURI,
  10426                                             aSrcdoc, "text/html"_ns, aLoadInfo,
  10427                                             true));
  10428    nsCOMPtr<nsIInputStreamChannel> isc = do_QueryInterface(channel);
  10429    MOZ_ASSERT(isc);
  10430    isc->SetBaseURI(aBaseURI);
  10431  }
  10432 
  10433  if (aLoadFlags != nsIRequest::LOAD_NORMAL) {
  10434    nsresult rv = channel->SetLoadFlags(aLoadFlags);
  10435    NS_ENSURE_SUCCESS(rv, rv);
  10436  }
  10437 
  10438  channel.forget(aChannel);
  10439  return NS_OK;
  10440 }
  10441 
  10442 /* static */ bool nsDocShell::CreateAndConfigureRealChannelForLoadState(
  10443    BrowsingContext* aBrowsingContext, nsDocShellLoadState* aLoadState,
  10444    LoadInfo* aLoadInfo, nsIInterfaceRequestor* aCallbacks,
  10445    nsDocShell* aDocShell, const OriginAttributes& aOriginAttributes,
  10446    nsLoadFlags aLoadFlags, uint32_t aCacheKey, nsresult& aRv,
  10447    nsIChannel** aChannel) {
  10448  MOZ_ASSERT(aLoadInfo);
  10449 
  10450  nsString srcdoc = VoidString();
  10451  bool isSrcdoc =
  10452      aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_IS_SRCDOC);
  10453  if (isSrcdoc) {
  10454    srcdoc = aLoadState->SrcdocData();
  10455  }
  10456 
  10457  aLoadInfo->SetTriggeringRemoteType(
  10458      aLoadState->GetEffectiveTriggeringRemoteType());
  10459 
  10460  if (aLoadState->PrincipalToInherit()) {
  10461    aLoadInfo->SetPrincipalToInherit(aLoadState->PrincipalToInherit());
  10462  }
  10463  aLoadInfo->SetLoadTriggeredFromExternal(
  10464      aLoadState->HasLoadFlags(LOAD_FLAGS_FROM_EXTERNAL));
  10465  aLoadInfo->SetForceAllowDataURI(aLoadState->HasInternalLoadFlags(
  10466      INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI));
  10467  aLoadInfo->SetOriginalFrameSrcLoad(
  10468      aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_ORIGINAL_FRAME_SRC));
  10469  aLoadInfo->SetIsNewWindowTarget(
  10470      aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_FIRST_LOAD));
  10471  aLoadInfo->SetForceMediaDocument(aLoadState->GetForceMediaDocument());
  10472 
  10473  bool inheritAttrs = false;
  10474  if (aLoadState->PrincipalToInherit()) {
  10475    inheritAttrs = nsContentUtils::ChannelShouldInheritPrincipal(
  10476        aLoadState->PrincipalToInherit(), aLoadState->URI(),
  10477        true,  // aInheritForAboutBlank
  10478        isSrcdoc);
  10479  }
  10480 
  10481  // Strip the target query parameters before creating the channel.
  10482  aLoadState->MaybeStripTrackerQueryStrings(aBrowsingContext);
  10483 
  10484  OriginAttributes attrs;
  10485 
  10486  // Inherit origin attributes from PrincipalToInherit if inheritAttrs is
  10487  // true. Otherwise we just use the origin attributes from docshell.
  10488  if (inheritAttrs) {
  10489    MOZ_ASSERT(aLoadState->PrincipalToInherit(),
  10490               "We should have PrincipalToInherit here.");
  10491    attrs = aLoadState->PrincipalToInherit()->OriginAttributesRef();
  10492    // If firstPartyIsolation is not enabled, then PrincipalToInherit should
  10493    // have the same origin attributes with docshell.
  10494    MOZ_ASSERT_IF(!OriginAttributes::IsFirstPartyEnabled(),
  10495                  attrs == aOriginAttributes);
  10496  } else {
  10497    attrs = aOriginAttributes;
  10498    attrs.SetFirstPartyDomain(IsTopLevelDoc(aBrowsingContext, aLoadInfo),
  10499                              aLoadState->URI());
  10500  }
  10501 
  10502  aRv = aLoadInfo->SetOriginAttributes(attrs);
  10503  if (NS_WARN_IF(NS_FAILED(aRv))) {
  10504    return false;
  10505  }
  10506 
  10507  if (aLoadState->GetIsFromProcessingFrameAttributes()) {
  10508    aLoadInfo->SetIsFromProcessingFrameAttributes();
  10509  }
  10510 
  10511  // Propagate the IsFormSubmission flag to the loadInfo.
  10512  if (aLoadState->IsFormSubmission()) {
  10513    aLoadInfo->SetIsFormSubmission(true);
  10514  }
  10515 
  10516  aLoadInfo->SetUnstrippedURI(aLoadState->GetUnstrippedURI());
  10517 
  10518  nsCOMPtr<nsIChannel> channel;
  10519  aRv = CreateRealChannelForDocument(getter_AddRefs(channel), aLoadState->URI(),
  10520                                     aLoadInfo, aCallbacks, aLoadFlags, srcdoc,
  10521                                     aLoadState->BaseURI());
  10522  NS_ENSURE_SUCCESS(aRv, false);
  10523 
  10524  if (!channel) {
  10525    return false;
  10526  }
  10527 
  10528  // If the HTTPS-Only mode is enabled, every insecure request gets upgraded to
  10529  // HTTPS by default. This behavior can be disabled through the loadinfo flag
  10530  // HTTPS_ONLY_EXEMPT.
  10531  nsHTTPSOnlyUtils::TestSitePermissionAndPotentiallyAddExemption(channel);
  10532 
  10533  // hack
  10534  nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
  10535  nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal(
  10536      do_QueryInterface(channel));
  10537  nsCOMPtr<nsIURI> referrer;
  10538  nsIReferrerInfo* referrerInfo = aLoadState->GetReferrerInfo();
  10539  if (referrerInfo) {
  10540    referrerInfo->GetOriginalReferrer(getter_AddRefs(referrer));
  10541  }
  10542  if (httpChannelInternal) {
  10543    if (aLoadState->HasInternalLoadFlags(
  10544            INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES)) {
  10545      aRv = httpChannelInternal->SetThirdPartyFlags(
  10546          nsIHttpChannelInternal::THIRD_PARTY_FORCE_ALLOW);
  10547      MOZ_ASSERT(NS_SUCCEEDED(aRv));
  10548    }
  10549    if (aLoadState->FirstParty()) {
  10550      aRv = httpChannelInternal->SetDocumentURI(aLoadState->URI());
  10551      MOZ_ASSERT(NS_SUCCEEDED(aRv));
  10552    } else {
  10553      aRv = httpChannelInternal->SetDocumentURI(referrer);
  10554      MOZ_ASSERT(NS_SUCCEEDED(aRv));
  10555    }
  10556    aRv = httpChannelInternal->SetRedirectMode(
  10557        nsIHttpChannelInternal::REDIRECT_MODE_MANUAL);
  10558    MOZ_ASSERT(NS_SUCCEEDED(aRv));
  10559  }
  10560 
  10561  if (httpChannel) {
  10562    if (aLoadState->HeadersStream()) {
  10563      aRv = AddHeadersToChannel(aLoadState->HeadersStream(), httpChannel);
  10564    }
  10565    // Set the referrer explicitly
  10566    // Referrer is currenly only set for link clicks here.
  10567    if (referrerInfo) {
  10568      aRv = httpChannel->SetReferrerInfo(referrerInfo);
  10569      MOZ_ASSERT(NS_SUCCEEDED(aRv));
  10570    }
  10571 
  10572    // Mark the http channel as UrgentStart for top level document loading in
  10573    // active tab.
  10574    if (IsUrgentStart(aBrowsingContext, aLoadInfo, aLoadState->LoadType())) {
  10575      nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(channel));
  10576      if (cos) {
  10577        cos->AddClassFlags(nsIClassOfService::UrgentStart);
  10578        if (StaticPrefs::dom_document_priority_incremental()) {
  10579          cos->SetIncremental(true);
  10580        }
  10581      }
  10582    }
  10583  }
  10584 
  10585  channel->SetOriginalURI(aLoadState->OriginalURI() ? aLoadState->OriginalURI()
  10586                                                    : aLoadState->URI());
  10587 
  10588  const nsACString& typeHint = aLoadState->TypeHint();
  10589  if (!typeHint.IsVoid()) {
  10590    channel->SetContentType(typeHint);
  10591  }
  10592 
  10593  const nsAString& fileName = aLoadState->FileName();
  10594  if (!fileName.IsVoid()) {
  10595    aRv = channel->SetContentDisposition(nsIChannel::DISPOSITION_ATTACHMENT);
  10596    NS_ENSURE_SUCCESS(aRv, false);
  10597    if (!fileName.IsEmpty()) {
  10598      aRv = channel->SetContentDispositionFilename(fileName);
  10599      NS_ENSURE_SUCCESS(aRv, false);
  10600    }
  10601  }
  10602 
  10603  if (nsCOMPtr<nsIWritablePropertyBag2> props = do_QueryInterface(channel)) {
  10604    nsCOMPtr<nsIURI> referrer;
  10605    nsIReferrerInfo* referrerInfo = aLoadState->GetReferrerInfo();
  10606    if (referrerInfo) {
  10607      referrerInfo->GetOriginalReferrer(getter_AddRefs(referrer));
  10608    }
  10609    // save true referrer for those who need it (e.g. xpinstall whitelisting)
  10610    // Currently only http and ftp channels support this.
  10611    props->SetPropertyAsInterface(u"docshell.internalReferrer"_ns, referrer);
  10612  }
  10613 
  10614  nsCOMPtr<nsICacheInfoChannel> cacheChannel(do_QueryInterface(channel));
  10615  auto loadType = aLoadState->LoadType();
  10616 
  10617  if (loadType == LOAD_RELOAD_NORMAL &&
  10618      StaticPrefs::
  10619          browser_soft_reload_only_force_validate_top_level_document()) {
  10620    nsCOMPtr<nsICacheInfoChannel> cachingChannel = do_QueryInterface(channel);
  10621    if (cachingChannel) {
  10622      cachingChannel->SetForceValidateCacheContent(true);
  10623    }
  10624  }
  10625 
  10626  // figure out if we need to set the post data stream on the channel...
  10627  if (aLoadState->PostDataStream()) {
  10628    if (nsCOMPtr<nsIFormPOSTActionChannel> postChannel =
  10629            do_QueryInterface(channel)) {
  10630      // XXX it's a bit of a hack to rewind the postdata stream here but
  10631      // it has to be done in case the post data is being reused multiple
  10632      // times.
  10633      nsCOMPtr<nsISeekableStream> postDataSeekable =
  10634          do_QueryInterface(aLoadState->PostDataStream());
  10635      if (postDataSeekable) {
  10636        aRv = postDataSeekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
  10637        NS_ENSURE_SUCCESS(aRv, false);
  10638      }
  10639 
  10640      // we really need to have a content type associated with this stream!!
  10641      postChannel->SetUploadStream(aLoadState->PostDataStream(), ""_ns, -1);
  10642 
  10643      // Ownership of the stream has transferred to the channel, clear our
  10644      // reference.
  10645      aLoadState->SetPostDataStream(nullptr);
  10646    }
  10647 
  10648    /* If there is a valid postdata *and* it is a History Load,
  10649     * set up the cache key on the channel, to retrieve the
  10650     * data *only* from the cache. If it is a normal reload, the
  10651     * cache is free to go to the server for updated postdata.
  10652     */
  10653    if (cacheChannel && aCacheKey != 0) {
  10654      if (loadType == LOAD_HISTORY || loadType == LOAD_RELOAD_CHARSET_CHANGE) {
  10655        cacheChannel->SetCacheKey(aCacheKey);
  10656        uint32_t loadFlags;
  10657        if (NS_SUCCEEDED(channel->GetLoadFlags(&loadFlags))) {
  10658          channel->SetLoadFlags(loadFlags |
  10659                                nsICachingChannel::LOAD_ONLY_FROM_CACHE);
  10660        }
  10661      } else if (loadType == LOAD_RELOAD_NORMAL) {
  10662        cacheChannel->SetCacheKey(aCacheKey);
  10663      }
  10664    }
  10665  } else {
  10666    /* If there is no postdata, set the cache key on the channel, and
  10667     * do not set the LOAD_ONLY_FROM_CACHE flag, so that the channel
  10668     * will be free to get it from net if it is not found in cache.
  10669     * New cache may use it creatively on CGI pages with GET
  10670     * method and even on those that say "no-cache"
  10671     */
  10672    if (loadType == LOAD_HISTORY || loadType == LOAD_RELOAD_NORMAL ||
  10673        loadType == LOAD_RELOAD_CHARSET_CHANGE ||
  10674        loadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE ||
  10675        loadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE) {
  10676      if (cacheChannel && aCacheKey != 0) {
  10677        cacheChannel->SetCacheKey(aCacheKey);
  10678      }
  10679    }
  10680  }
  10681 
  10682  if (nsCOMPtr<nsIScriptChannel> scriptChannel = do_QueryInterface(channel)) {
  10683    // Allow execution against our context if the principals match
  10684    scriptChannel->SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL);
  10685  }
  10686 
  10687  if (nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(channel)) {
  10688    nsString initiatorType;
  10689    switch (aLoadInfo->InternalContentPolicyType()) {
  10690      case nsIContentPolicy::TYPE_INTERNAL_EMBED:
  10691        initiatorType = u"embed"_ns;
  10692        break;
  10693      case nsIContentPolicy::TYPE_INTERNAL_OBJECT:
  10694        initiatorType = u"object"_ns;
  10695        break;
  10696      default: {
  10697        const auto& embedderElementType =
  10698            aBrowsingContext->GetEmbedderElementType();
  10699        if (embedderElementType) {
  10700          initiatorType = *embedderElementType;
  10701        }
  10702        break;
  10703      }
  10704    }
  10705 
  10706    if (!initiatorType.IsEmpty()) {
  10707      timedChannel->SetInitiatorType(initiatorType);
  10708    }
  10709  }
  10710 
  10711  nsCOMPtr<nsIURI> rpURI;
  10712  aLoadInfo->GetResultPrincipalURI(getter_AddRefs(rpURI));
  10713  Maybe<nsCOMPtr<nsIURI>> originalResultPrincipalURI;
  10714  aLoadState->GetMaybeResultPrincipalURI(originalResultPrincipalURI);
  10715  if (originalResultPrincipalURI &&
  10716      (!aLoadState->KeepResultPrincipalURIIfSet() || !rpURI)) {
  10717    // Unconditionally override, we want the replay to be equal to what has
  10718    // been captured.
  10719    aLoadInfo->SetResultPrincipalURI(originalResultPrincipalURI.ref());
  10720  }
  10721 
  10722  if (aLoadState->OriginalURI() && aLoadState->LoadReplace()) {
  10723    // The LOAD_REPLACE flag and its handling here will be removed as part
  10724    // of bug 1319110.  For now preserve its restoration here to not break
  10725    // any code expecting it being set specially on redirected channels.
  10726    // If the flag has originally been set to change result of
  10727    // NS_GetFinalChannelURI it won't have any effect and also won't cause
  10728    // any harm.
  10729    uint32_t loadFlags;
  10730    aRv = channel->GetLoadFlags(&loadFlags);
  10731    NS_ENSURE_SUCCESS(aRv, false);
  10732    channel->SetLoadFlags(loadFlags | nsIChannel::LOAD_REPLACE);
  10733  }
  10734 
  10735  nsCOMPtr<nsIPolicyContainer> policyContainer = aLoadState->PolicyContainer();
  10736  if (nsCOMPtr<nsIContentSecurityPolicy> csp =
  10737          PolicyContainer::GetCSP(policyContainer)) {
  10738    // Navigational requests that are same origin need to be upgraded in case
  10739    // upgrade-insecure-requests is present. Please note that for document
  10740    // navigations that bit is re-computed in case we encounter a server
  10741    // side redirect so the navigation is not same-origin anymore.
  10742    bool upgradeInsecureRequests = false;
  10743    csp->GetUpgradeInsecureRequests(&upgradeInsecureRequests);
  10744    if (upgradeInsecureRequests) {
  10745      // only upgrade if the navigation is same origin
  10746      nsCOMPtr<nsIPrincipal> resultPrincipal;
  10747      aRv = nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
  10748          channel, getter_AddRefs(resultPrincipal));
  10749      NS_ENSURE_SUCCESS(aRv, false);
  10750      if (nsContentSecurityUtils::IsConsideredSameOriginForUIR(
  10751              aLoadState->TriggeringPrincipal(), resultPrincipal)) {
  10752        aLoadInfo->SetUpgradeInsecureRequests(true);
  10753      }
  10754    }
  10755  }
  10756 
  10757  if (policyContainer) {
  10758    // For document loads we store the policyContainer that potentially needs to
  10759    // be inherited by the new document, e.g. in case we are loading
  10760    // an opaque origin like a data: URI. The actual inheritance
  10761    // check happens within Document::InitPolicyContainer().
  10762    // Please create an actual copy of the policyContainer (do not share the
  10763    // same reference) otherwise modifications done (such as the meta CSP of the
  10764    // new doc) in an opaque origin will incorrectly be propagated to the
  10765    // embedding document.
  10766    RefPtr<PolicyContainer> policyContainerToInherit = new PolicyContainer();
  10767    policyContainerToInherit->InitFromOther(
  10768        PolicyContainer::Cast(policyContainer));
  10769    aLoadInfo->SetPolicyContainerToInherit(policyContainerToInherit);
  10770  }
  10771 
  10772  channel.forget(aChannel);
  10773  return true;
  10774 }
  10775 
  10776 bool nsDocShell::ShouldDoInitialAboutBlankSyncLoad(
  10777    nsIURI* aURI, nsDocShellLoadState* aLoadState,
  10778    nsIPrincipal* aPrincipalToInherit) {
  10779  MOZ_ASSERT(mDocumentViewer);
  10780 
  10781  if (!NS_IsAboutBlankAllowQueryAndFragment(aURI)) {
  10782    return false;
  10783  }
  10784 
  10785  if (aLoadState->IsInitialAboutBlankHandlingProhibited()) {
  10786    return false;
  10787  }
  10788 
  10789  if (mHasStartedLoadingOtherThanInitialBlankURI ||
  10790      !mDocumentViewer->GetDocument()->IsUncommittedInitialDocument()) {
  10791    return false;
  10792  }
  10793 
  10794  if (!aPrincipalToInherit) {
  10795    MOZ_ASSERT(
  10796        mDocumentViewer->GetDocument()->NodePrincipal()->GetIsNullPrincipal(),
  10797        "Load looks like first load but does not want principal inheritance.");
  10798  } else {
  10799    if (XRE_IsContentProcess() &&
  10800        !ValidatePrincipalCouldPotentiallyBeLoadedBy(
  10801            aPrincipalToInherit, ContentChild::GetSingleton()->GetRemoteType(),
  10802            {})) {
  10803      // Principal doesn't match our remote type, so the we need the normal
  10804      // load path to do a process switch.
  10805      return false;
  10806    }
  10807 
  10808    // If a page opens about:blank, it will have a content principal.
  10809    // If it is then restored after a restart, we might not have initialized
  10810    // UsesOAC for it. If this is the case, do a normal load (bug 2004165).
  10811    // XXX bug 2005205 tracks removing this workaround.
  10812    if (aLoadState->LoadIsFromSessionHistory() &&
  10813        !mBrowsingContext->Group()
  10814             ->UsesOriginAgentCluster(aPrincipalToInherit)
  10815             .isSome()) {
  10816      return false;
  10817    }
  10818  }
  10819 
  10820  return true;
  10821 }
  10822 
  10823 void nsDocShell::UnsuppressPaintingIfNoNavigationAwayFromAboutBlank(
  10824    mozilla::PresShell* aPresShell) {
  10825  if (mHasStartedLoadingOtherThanInitialBlankURI || !mDocumentViewer) {
  10826    return;
  10827  }
  10828  Document* doc = mDocumentViewer->GetDocument();
  10829  if (!doc || !doc->IsInitialDocument()) {
  10830    return;
  10831  }
  10832  if (mDocumentViewer->GetPresShell() != aPresShell) {
  10833    return;
  10834  }
  10835  // Our surroundings appear to remain in the same state
  10836  // as before posting the runnable.
  10837  aPresShell->UnsuppressPainting();
  10838  // The content viewer's mPresShell could have been removed now, see bug
  10839  // 378682/421432
  10840  if ((aPresShell = mDocumentViewer->GetPresShell())) {
  10841    aPresShell->LoadComplete();
  10842  }
  10843 }
  10844 
  10845 nsresult nsDocShell::PerformTrustedTypesPreNavigationCheck(
  10846    nsDocShellLoadState* aLoadState, nsGlobalWindowInner* aWindow) const {
  10847  MOZ_ASSERT(aWindow);
  10848  RefPtr<nsIContentSecurityPolicy> csp =
  10849      PolicyContainer::GetCSP(aWindow->GetPolicyContainer());
  10850  if (csp->GetRequireTrustedTypesForDirectiveState() ==
  10851      RequireTrustedTypesForDirectiveState::NONE) {
  10852    return NS_OK;
  10853  }
  10854 
  10855  // Exempt web extension content scripts from trusted types policies defined by
  10856  // the page in which they are running.
  10857  if (auto principal = BasePrincipal::Cast(aLoadState->TriggeringPrincipal())) {
  10858    if (principal->ContentScriptAddonPolicyCore()) {
  10859      return NS_OK;
  10860    }
  10861  }
  10862 
  10863  // If disposion is enforce for require-trusted-types-for, then we return
  10864  // errors in order to block navigation. If it's report-only, errors are
  10865  // ignored and the URL is unchanged.
  10866  bool shouldBlockOnError = csp->GetRequireTrustedTypesForDirectiveState() ==
  10867                            RequireTrustedTypesForDirectiveState::ENFORCE;
  10868 
  10869  // 2. Let urlString be the result of running the URL serializer on
  10870  // request’s url.
  10871  nsAutoCString urlString;
  10872  aLoadState->URI()->GetSpec(urlString);
  10873 
  10874  // 3. Let encodedScriptSource be the result of removing the leading
  10875  // "javascript:" from urlString.
  10876  constexpr auto javascriptScheme = "javascript:"_ns;
  10877  const nsDependentCSubstring encodedScriptSource =
  10878      Substring(urlString, javascriptScheme.Length());
  10879 
  10880  // 4. Let convertedScriptSource be the result of executing Process value
  10881  // with a default policy algorithm
  10882  Maybe<nsAutoString> compliantStringHolder;
  10883  NS_ConvertUTF8toUTF16 encodedScriptSourceUTF16(encodedScriptSource);
  10884  constexpr nsLiteralString sink = u"Location href"_ns;
  10885  auto reportPreNavigationCheckViolations = [&csp, &sink,
  10886                                             &encodedScriptSourceUTF16] {
  10887    // Report violation the same way as for "Should sink type mismatch
  10888    // violation be blocked by Content Security Policy", since that's what
  10889    // other browsers do. See
  10890    // https://github.com/w3c/trusted-types/issues/584.
  10891    auto location = JSCallingLocation::Get();
  10892    TrustedTypeUtils::ReportSinkTypeMismatchViolations(
  10893        csp, nullptr /* aCSPEventListener */, location.FileName(),
  10894        location.mLine, location.mColumn, sink, kTrustedTypesOnlySinkGroup,
  10895        encodedScriptSourceUTF16);
  10896  };
  10897  ErrorResult error;
  10898  auto convertedScriptSource =
  10899      TrustedTypeUtils::GetConvertedScriptSourceForPreNavigationCheck(
  10900          *aWindow, encodedScriptSourceUTF16, sink, compliantStringHolder,
  10901          error);
  10902  error.WouldReportJSException();
  10903  if (error.Failed()) {
  10904    reportPreNavigationCheckViolations();
  10905    if (shouldBlockOnError) {
  10906      RETURN_NSRESULT_ON_FAILURE(error);
  10907    }
  10908    error.SuppressException();
  10909    return NS_OK;
  10910  }
  10911 
  10912  // 5. Set urlString to be the result of prepending "javascript:" to
  10913  // stringified convertedScriptSource.
  10914  urlString = javascriptScheme + NS_ConvertUTF16toUTF8(*convertedScriptSource);
  10915 
  10916  // 6. Let newURL be the result of running the URL parser on urlString.
  10917  nsCOMPtr<nsIURI> newURL;
  10918  nsresult rv = NS_NewURI(getter_AddRefs(newURL), urlString);
  10919  if (NS_FAILED(rv)) {
  10920    reportPreNavigationCheckViolations();
  10921    return shouldBlockOnError ? rv : NS_OK;
  10922  }
  10923 
  10924  // 7. Set request’s url to newURL.
  10925  aLoadState->SetURI(newURL);
  10926  return NS_OK;
  10927 }
  10928 
  10929 nsresult nsDocShell::DoURILoad(nsDocShellLoadState* aLoadState,
  10930                               Maybe<uint32_t> aCacheKey,
  10931                               nsIRequest** aRequest) {
  10932  // Double-check that we're still around to load this URI.
  10933  if (mIsBeingDestroyed) {
  10934    // Return NS_OK despite not doing anything to avoid throwing exceptions
  10935    // from nsLocation::SetHref if the unload handler of the existing page
  10936    // tears us down.
  10937    return NS_OK;
  10938  }
  10939 
  10940  nsCOMPtr<nsIURILoader> uriLoader = components::URILoader::Service();
  10941  if (NS_WARN_IF(!uriLoader)) {
  10942    return NS_ERROR_UNEXPECTED;
  10943  }
  10944 
  10945  // Persist and sync layout history state before we load a new uri, as this
  10946  // might be our last chance to do so, in the content process.
  10947  PersistLayoutHistoryState();
  10948  SynchronizeLayoutHistoryState();
  10949 
  10950  nsresult rv;
  10951  nsContentPolicyType contentPolicyType = DetermineContentType();
  10952 
  10953  auto getSourceWindowContext = [this, &aLoadState] {
  10954    const MaybeDiscardedBrowsingContext& sourceBC =
  10955        aLoadState->SourceBrowsingContext();
  10956    if (!sourceBC.IsNullOrDiscarded()) {
  10957      if (WindowContext* wc = sourceBC.get()->GetCurrentWindowContext()) {
  10958        return wc;
  10959      }
  10960    }
  10961    return mBrowsingContext->GetParentWindowContext();
  10962  };
  10963 
  10964  if (StaticPrefs::dom_security_trusted_types_enabled() &&
  10965      aLoadState->URI()->SchemeIs("javascript")) {
  10966    if (WindowContext* sourceWindowContext = getSourceWindowContext()) {
  10967      RefPtr<nsGlobalWindowInner> window =
  10968          sourceWindowContext->GetInnerWindow();
  10969      rv = PerformTrustedTypesPreNavigationCheck(aLoadState, window);
  10970      // Default policy might destroy the whole docshell, so check that again.
  10971      if (mIsBeingDestroyed) {
  10972        return NS_OK;
  10973      }
  10974      if (NS_FAILED(rv)) {
  10975        return NS_ERROR_DOM_SECURITY_ERR;
  10976      }
  10977    }
  10978  }
  10979 
  10980  if (IsSubframe()) {
  10981    MOZ_ASSERT(contentPolicyType == nsIContentPolicy::TYPE_INTERNAL_IFRAME ||
  10982                   contentPolicyType == nsIContentPolicy::TYPE_INTERNAL_FRAME,
  10983               "DoURILoad thinks this is a frame and InternalLoad does not");
  10984    if (StaticPrefs::dom_block_external_protocol_in_iframes()) {
  10985      // Only allow URLs able to return data in iframes.
  10986      if (nsContentUtils::IsExternalProtocol(aLoadState->URI())) {
  10987        // The context to check user-interaction with for the purposes of
  10988        // popup-blocking.
  10989        //
  10990        // We generally want to check the context that initiated the navigation.
  10991        WindowContext* sourceWindowContext = getSourceWindowContext();
  10992        MOZ_ASSERT(sourceWindowContext);
  10993 
  10994        // FIXME: We can't check user-interaction against an OOP window. This is
  10995        // the next best thing we can really do. The load state keeps whether
  10996        // the navigation had a user interaction in process
  10997        // (aLoadState->HasValidUserGestureActivation()), but we can't really
  10998        // consume it, which we want to prevent popup-spamming from the same
  10999        // click event.
  11000        WindowContext* context =
  11001            sourceWindowContext->IsInProcess()
  11002                ? sourceWindowContext
  11003                : mBrowsingContext->GetCurrentWindowContext();
  11004        const bool popupBlocked = [&] {
  11005          const bool active = mBrowsingContext->IsActive();
  11006 
  11007          // For same-origin-with-top windows, we grant a single free popup
  11008          // without user activation, see bug 1680721.
  11009          //
  11010          // We consume the flag now even if there's no user activation.
  11011          const bool hasFreePass = [&] {
  11012            if (!active ||
  11013                !(context->IsInProcess() && context->SameOriginWithTop())) {
  11014              return false;
  11015            }
  11016            nsGlobalWindowInner* win =
  11017                context->TopWindowContext()->GetInnerWindow();
  11018            return win && win->TryOpenExternalProtocolIframe();
  11019          }();
  11020 
  11021          if (context->IsInProcess() &&
  11022              context->ConsumeTransientUserGestureActivation()) {
  11023            // If the user has interacted with the page, consume it.
  11024            return false;
  11025          }
  11026 
  11027          // TODO(emilio): Can we remove this check? It seems like what prompted
  11028          // this code (bug 1514547) should be covered by transient user
  11029          // activation, see bug 1514547.
  11030          if (active &&
  11031              PopupBlocker::ConsumeTimerTokenForExternalProtocolIframe()) {
  11032            return false;
  11033          }
  11034 
  11035          if (sourceWindowContext->CanShowPopup()) {
  11036            return false;
  11037          }
  11038 
  11039          if (hasFreePass) {
  11040            return false;
  11041          }
  11042 
  11043          return true;
  11044        }();
  11045 
  11046        // No error must be returned when iframes are blocked.
  11047        if (popupBlocked) {
  11048          nsAutoString message;
  11049          nsresult rv = nsContentUtils::GetLocalizedString(
  11050              nsContentUtils::eDOM_PROPERTIES,
  11051              "ExternalProtocolFrameBlockedNoUserActivation", message);
  11052          if (NS_SUCCEEDED(rv)) {
  11053            nsContentUtils::ReportToConsoleByWindowID(
  11054                message, nsIScriptError::warningFlag, "DOM"_ns,
  11055                context->InnerWindowId());
  11056          }
  11057          return NS_OK;
  11058        }
  11059      }
  11060    }
  11061 
  11062    // Only allow view-source scheme in top-level docshells. view-source is
  11063    // the only scheme to which this applies at the moment due to potential
  11064    // timing attacks to read data from cross-origin iframes. If this widens
  11065    // we should add a protocol flag for whether the scheme is allowed in
  11066    // frames and use something like nsNetUtil::NS_URIChainHasFlags.
  11067    nsCOMPtr<nsIURI> tempURI = aLoadState->URI();
  11068    nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(tempURI);
  11069    while (nestedURI) {
  11070      // view-source should always be an nsINestedURI, loop and check the
  11071      // scheme on this and all inner URIs that are also nested URIs.
  11072      if (tempURI->SchemeIs("view-source")) {
  11073        return NS_ERROR_UNKNOWN_PROTOCOL;
  11074      }
  11075      nestedURI->GetInnerURI(getter_AddRefs(tempURI));
  11076      nestedURI = do_QueryInterface(tempURI);
  11077    }
  11078  } else {
  11079    MOZ_ASSERT(contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT,
  11080               "DoURILoad thinks this is a document and InternalLoad does not");
  11081  }
  11082 
  11083  // We want to inherit aLoadState->PrincipalToInherit() when:
  11084  // 1. ChannelShouldInheritPrincipal returns true.
  11085  // 2. aLoadState->URI() is not data: URI, or data: URI is not
  11086  //    configured as unique opaque origin.
  11087  bool inheritPrincipal = false;
  11088 
  11089  nsCOMPtr<nsIURI> uri = aLoadState->URI();
  11090  if (aLoadState->PrincipalToInherit()) {
  11091    bool isSrcdoc =
  11092        aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_IS_SRCDOC);
  11093    bool inheritAttrs = nsContentUtils::ChannelShouldInheritPrincipal(
  11094        aLoadState->PrincipalToInherit(), uri,
  11095        true,  // aInheritForAboutBlank
  11096        isSrcdoc);
  11097 
  11098    inheritPrincipal = inheritAttrs && !uri->SchemeIs("data");
  11099  }
  11100 
  11101  MOZ_ASSERT_IF(NS_IsAboutBlankAllowQueryAndFragment(uri) &&
  11102                    aLoadState->PrincipalToInherit(),
  11103                inheritPrincipal);
  11104  // See https://bugzilla.mozilla.org/show_bug.cgi?id=1736570
  11105  const bool doInitialSyncLoad = ShouldDoInitialAboutBlankSyncLoad(
  11106      uri, aLoadState, aLoadState->PrincipalToInherit());
  11107 
  11108  if (!doInitialSyncLoad) {
  11109    // https://wicg.github.io/document-picture-in-picture/#close-on-navigate
  11110    if (Document* doc = GetExtantDocument()) {
  11111      NS_DispatchToMainThread(NS_NewRunnableFunction(
  11112          "Close PIP window on navigate", [doc = RefPtr(doc)]() {
  11113            doc->CloseAnyAssociatedDocumentPiPWindows();
  11114          }));
  11115    }
  11116    if (GetBrowsingContext()->GetIsDocumentPiP()) {
  11117      return NS_OK;
  11118    }
  11119  }
  11120 
  11121  // FIXME We still have a ton of codepaths that don't pass through
  11122  //       DocumentLoadListener, so probably need to create session history info
  11123  //       in more places.
  11124  if (aLoadState->GetLoadingSessionHistoryInfo()) {
  11125    SetLoadingSessionHistoryInfo(*aLoadState->GetLoadingSessionHistoryInfo());
  11126  } else if (doInitialSyncLoad && mozilla::SessionHistoryInParent()) {
  11127    // Materialize LoadingSessionHistoryInfo here, because DocumentChannel
  11128    // loads have it, and later history behavior depends on it existing.
  11129    UniquePtr<SessionHistoryInfo> entry = MakeUnique<SessionHistoryInfo>(
  11130        uri, aLoadState->TriggeringPrincipal(),
  11131        aLoadState->PrincipalToInherit(),
  11132        aLoadState->PartitionedPrincipalToInherit(),
  11133        aLoadState->PolicyContainer(), mContentTypeHint);
  11134    entry->SetTransient();
  11135    mozilla::dom::LoadingSessionHistoryInfo info(*entry);
  11136    info.mContiguousEntries.AppendElement(*entry);
  11137    SetLoadingSessionHistoryInfo(info, true);
  11138  }
  11139 
  11140  // open a channel for the url
  11141 
  11142  // If we have a pending channel, use the channel we've already created here.
  11143  // We don't need to set up load flags for our channel, as it has already been
  11144  // created.
  11145 
  11146  if (nsCOMPtr<nsIChannel> channel =
  11147          aLoadState->GetPendingRedirectedChannel()) {
  11148    // If we have a request outparameter, shove our channel into it.
  11149    if (aRequest) {
  11150      nsCOMPtr<nsIRequest> outRequest = channel;
  11151      outRequest.forget(aRequest);
  11152    }
  11153 
  11154    mHasStartedLoadingOtherThanInitialBlankURI = true;
  11155    return OpenRedirectedChannel(aLoadState);
  11156  }
  11157 
  11158  // There are two cases we care about:
  11159  // * Top-level load: In this case, loadingNode is null, but loadingWindow
  11160  //   is our mScriptGlobal. We pass null for loadingPrincipal in this case.
  11161  // * Subframe load: loadingWindow is null, but loadingNode is the frame
  11162  //   element for the load. loadingPrincipal is the NodePrincipal of the
  11163  //   frame element.
  11164  nsCOMPtr<nsINode> loadingNode;
  11165  nsCOMPtr<nsPIDOMWindowOuter> loadingWindow;
  11166  nsCOMPtr<nsIPrincipal> loadingPrincipal;
  11167  nsCOMPtr<nsISupports> topLevelLoadingContext;
  11168 
  11169  if (contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT) {
  11170    loadingNode = nullptr;
  11171    loadingPrincipal = nullptr;
  11172    loadingWindow = mScriptGlobal;
  11173    if (XRE_IsContentProcess()) {
  11174      // In e10s the child process doesn't have access to the element that
  11175      // contains the browsing context (because that element is in the chrome
  11176      // process).
  11177      nsCOMPtr<nsIBrowserChild> browserChild = GetBrowserChild();
  11178      topLevelLoadingContext = ToSupports(browserChild);
  11179    } else {
  11180      // This is for loading non-e10s tabs and toplevel windows of various
  11181      // sorts.
  11182      // For the toplevel window cases, requestingElement will be null.
  11183      nsCOMPtr<Element> requestingElement =
  11184          loadingWindow->GetFrameElementInternal();
  11185      topLevelLoadingContext = requestingElement;
  11186    }
  11187  } else {
  11188    loadingWindow = nullptr;
  11189    loadingNode = mScriptGlobal->GetFrameElementInternal();
  11190    if (loadingNode) {
  11191      // If we have a loading node, then use that as our loadingPrincipal.
  11192      loadingPrincipal = loadingNode->NodePrincipal();
  11193 #ifdef DEBUG
  11194      // Get the docshell type for requestingElement.
  11195      RefPtr<Document> requestingDoc = loadingNode->OwnerDoc();
  11196      nsCOMPtr<nsIDocShell> elementDocShell = requestingDoc->GetDocShell();
  11197      // requestingElement docshell type = current docshell type.
  11198      MOZ_ASSERT(
  11199          mItemType == elementDocShell->ItemType(),
  11200          "subframes should have the same docshell type as their parent");
  11201 #endif
  11202    } else {
  11203      if (mIsBeingDestroyed) {
  11204        // If this isn't a top-level load and mScriptGlobal's frame element is
  11205        // null, then the element got removed from the DOM while we were trying
  11206        // to load this resource. This docshell is scheduled for destruction
  11207        // already, so bail out here.
  11208        return NS_OK;
  11209      }
  11210      // If we are not being destroyed and we do not have access to the loading
  11211      // node, then we are a remote subframe. Set the loading principal
  11212      // to be a null principal and then set it correctly in the parent.
  11213      loadingPrincipal = NullPrincipal::Create(GetOriginAttributes(), nullptr);
  11214    }
  11215  }
  11216 
  11217  if (!aLoadState->TriggeringPrincipal()) {
  11218    MOZ_ASSERT(false, "DoURILoad needs a valid triggeringPrincipal");
  11219    return NS_ERROR_FAILURE;
  11220  }
  11221 
  11222  uint32_t sandboxFlags = mBrowsingContext->GetSandboxFlags();
  11223  nsSecurityFlags securityFlags =
  11224      nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL;
  11225 
  11226  if (mLoadType == LOAD_ERROR_PAGE) {
  11227    securityFlags |= nsILoadInfo::SEC_LOAD_ERROR_PAGE;
  11228  }
  11229 
  11230  if (inheritPrincipal) {
  11231    securityFlags |= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
  11232  }
  11233 
  11234  // Must never have a parent for TYPE_DOCUMENT loads
  11235  MOZ_ASSERT_IF(contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT,
  11236                !mBrowsingContext->GetParent());
  11237  // Subdocuments must have a parent
  11238  MOZ_ASSERT_IF(contentPolicyType == nsIContentPolicy::TYPE_SUBDOCUMENT,
  11239                mBrowsingContext->GetParent());
  11240  mBrowsingContext->SetTriggeringAndInheritPrincipals(
  11241      aLoadState->TriggeringPrincipal(), aLoadState->PrincipalToInherit(),
  11242      aLoadState->GetLoadIdentifier());
  11243  RefPtr<LoadInfo> loadInfo;
  11244  if (contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT) {
  11245    loadInfo =
  11246        new LoadInfo(loadingWindow, uri, aLoadState->TriggeringPrincipal(),
  11247                     topLevelLoadingContext, securityFlags, sandboxFlags);
  11248  } else {
  11249    loadInfo = MOZ_TRY(LoadInfo::Create(
  11250        loadingPrincipal, aLoadState->TriggeringPrincipal(), loadingNode,
  11251        securityFlags, contentPolicyType, Maybe<mozilla::dom::ClientInfo>(),
  11252        Maybe<mozilla::dom::ServiceWorkerDescriptor>(), sandboxFlags));
  11253  }
  11254  RefPtr<WindowContext> context = mBrowsingContext->GetCurrentWindowContext();
  11255 
  11256  if (doInitialSyncLoad) {
  11257    // Stay on the eagerly created document and adjust it to match what we would
  11258    // be loading.
  11259    return CompleteInitialAboutBlankLoad(aLoadState, loadInfo);
  11260  }
  11261  mHasStartedLoadingOtherThanInitialBlankURI = true;
  11262 
  11263  if (mLoadType != LOAD_ERROR_PAGE && context && context->IsInProcess()) {
  11264    if (context->HasValidTransientUserGestureActivation()) {
  11265      aLoadState->SetHasValidUserGestureActivation(true);
  11266      aLoadState->SetTextDirectiveUserActivation(true);
  11267    }
  11268    if (!aLoadState->TriggeringWindowId()) {
  11269      aLoadState->SetTriggeringWindowId(context->Id());
  11270    }
  11271    if (!aLoadState->TriggeringStorageAccess()) {
  11272      Document* contextDoc = context->GetExtantDoc();
  11273      if (contextDoc) {
  11274        aLoadState->SetTriggeringStorageAccess(
  11275            contextDoc->UsingStorageAccess());
  11276      }
  11277    }
  11278  }
  11279 
  11280  // in case this docshell load was triggered by a valid transient user gesture,
  11281  // or also the load originates from external, then we pass that information on
  11282  // to the loadinfo, which allows e.g. setting Sec-Fetch-User request headers.
  11283  if (aLoadState->HasValidUserGestureActivation() ||
  11284      aLoadState->HasLoadFlags(LOAD_FLAGS_FROM_EXTERNAL)) {
  11285    loadInfo->SetHasValidUserGestureActivation(true);
  11286    aLoadState->SetTextDirectiveUserActivation(true);
  11287  }
  11288 
  11289  loadInfo->SetTextDirectiveUserActivation(
  11290      aLoadState->GetTextDirectiveUserActivation());
  11291 
  11292  loadInfo->SetTriggeringWindowId(aLoadState->TriggeringWindowId());
  11293  loadInfo->SetTriggeringStorageAccess(aLoadState->TriggeringStorageAccess());
  11294  loadInfo->SetTriggeringSandboxFlags(aLoadState->TriggeringSandboxFlags());
  11295  net::ClassificationFlags flags = aLoadState->TriggeringClassificationFlags();
  11296  loadInfo->SetTriggeringFirstPartyClassificationFlags(flags.firstPartyFlags);
  11297  loadInfo->SetTriggeringThirdPartyClassificationFlags(flags.thirdPartyFlags);
  11298  loadInfo->SetIsMetaRefresh(aLoadState->IsMetaRefresh());
  11299 
  11300  uint32_t cacheKey = 0;
  11301  if (aCacheKey) {
  11302    cacheKey = *aCacheKey;
  11303  } else if (mozilla::SessionHistoryInParent()) {
  11304    if (mLoadingEntry) {
  11305      cacheKey = mLoadingEntry->mInfo.GetCacheKey();
  11306    } else if (mActiveEntry) {  // for reload cases
  11307      cacheKey = mActiveEntry->GetCacheKey();
  11308    }
  11309  } else {
  11310    if (mLSHE) {
  11311      cacheKey = mLSHE->GetCacheKey();
  11312    } else if (mOSHE) {  // for reload cases
  11313      cacheKey = mOSHE->GetCacheKey();
  11314    }
  11315  }
  11316 
  11317  bool uriModified;
  11318  if (mLSHE || mLoadingEntry) {
  11319    if (mLoadingEntry) {
  11320      uriModified = mLoadingEntry->mInfo.GetURIWasModified();
  11321    } else {
  11322      uriModified = mLSHE->GetURIWasModified();
  11323    }
  11324  } else {
  11325    uriModified = false;
  11326  }
  11327 
  11328  bool isEmbeddingBlockedError = false;
  11329  if (mFailedChannel) {
  11330    nsresult status;
  11331    mFailedChannel->GetStatus(&status);
  11332    isEmbeddingBlockedError = status == NS_ERROR_XFO_VIOLATION ||
  11333                              status == NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION;
  11334  }
  11335 
  11336  nsLoadFlags loadFlags = aLoadState->CalculateChannelLoadFlags(
  11337      mBrowsingContext, uriModified, Some(isEmbeddingBlockedError));
  11338 
  11339  nsCOMPtr<nsIChannel> channel;
  11340  if (DocumentChannel::CanUseDocumentChannel(uri)) {
  11341    channel = DocumentChannel::CreateForDocument(
  11342        aLoadState, loadInfo, loadFlags, this, cacheKey, uriModified,
  11343        isEmbeddingBlockedError);
  11344    MOZ_ASSERT(channel);
  11345 
  11346    // Disable keyword fixup when using DocumentChannel, since
  11347    // DocumentLoadListener will handle this for us (in the parent process).
  11348    mAllowKeywordFixup = false;
  11349  } else if (!CreateAndConfigureRealChannelForLoadState(
  11350                 mBrowsingContext, aLoadState, loadInfo, this, this,
  11351                 GetOriginAttributes(), loadFlags, cacheKey, rv,
  11352                 getter_AddRefs(channel))) {
  11353    return rv;
  11354  }
  11355 
  11356  // Make sure to give the caller a channel if we managed to create one
  11357  // This is important for correct error page/session history interaction
  11358  if (aRequest) {
  11359    NS_ADDREF(*aRequest = channel);
  11360  }
  11361 
  11362  const nsACString& typeHint = aLoadState->TypeHint();
  11363  if (!typeHint.IsVoid()) {
  11364    mContentTypeHint = typeHint;
  11365  } else {
  11366    mContentTypeHint.Truncate();
  11367  }
  11368 
  11369  // Load attributes depend on load type...
  11370  if (mLoadType == LOAD_RELOAD_CHARSET_CHANGE) {
  11371    // Use SetAllowStaleCacheContent (not LOAD_FROM_CACHE flag) since we
  11372    // only want to force cache load for this channel, not the whole
  11373    // loadGroup.
  11374    nsCOMPtr<nsICacheInfoChannel> cachingChannel = do_QueryInterface(channel);
  11375    if (cachingChannel) {
  11376      cachingChannel->SetAllowStaleCacheContent(true);
  11377    }
  11378  }
  11379 
  11380  uint32_t openFlags =
  11381      nsDocShell::ComputeURILoaderFlags(mBrowsingContext, mLoadType);
  11382  return OpenInitializedChannel(channel, uriLoader, openFlags);
  11383 }
  11384 
  11385 nsresult nsDocShell::CompleteInitialAboutBlankLoad(
  11386    nsDocShellLoadState* aLoadState, nsILoadInfo* aLoadInfo) {
  11387  nsresult rv;
  11388  // Match the DocumentChannel case where the default for third-partiness
  11389  // differs from the default in LoadInfo construction here.
  11390  // toolkit/components/antitracking/test/browser/browser_aboutblank.js
  11391  // fails without this.
  11392  BrowsingContext* top = mBrowsingContext->Top();
  11393  if (top == mBrowsingContext) {
  11394    // If we're at the top, this must be a window.open()ed
  11395    // window, and we can't be third-party relative to ourselves.
  11396    aLoadInfo->SetIsThirdPartyContextToTopWindow(false);
  11397  } else {
  11398    if (Document* topDoc = top->GetDocument()) {
  11399      bool thirdParty = false;
  11400      (void)topDoc->GetPrincipal()->IsThirdPartyPrincipal(
  11401          aLoadState->PrincipalToInherit(), &thirdParty);
  11402      aLoadInfo->SetIsThirdPartyContextToTopWindow(thirdParty);
  11403    } else {
  11404      // If top is in a different process, we have to be third-party relative
  11405      // to it.
  11406      aLoadInfo->SetIsThirdPartyContextToTopWindow(true);
  11407    }
  11408  }
  11409 
  11410  if (!mDocumentViewer) {
  11411    MOZ_ASSERT(false, "How did the viewer go away?");
  11412    return NS_ERROR_FAILURE;
  11413  }
  11414  RefPtr<Document> doc = mDocumentViewer->GetDocument();
  11415  MOZ_LOG(gDocShellLog, LogLevel::Debug,
  11416          ("nsDocShell[%p]::DoURILoad sync about:blank onto initial "
  11417           "about:blank. Document[%p]\n",
  11418           this, doc.get()));
  11419  if (!doc) {
  11420    MOZ_ASSERT(false, "How did the document go away?");
  11421    return NS_ERROR_FAILURE;
  11422  }
  11423 
  11424  const bool principalMissmatch =
  11425      aLoadState->PrincipalToInherit() &&
  11426      !aLoadState->PrincipalToInherit()->Equals(doc->GetPrincipal());
  11427  MOZ_ASSERT_IF(!aLoadState->PrincipalToInherit(),
  11428                doc->GetPrincipal()->GetIsNullPrincipal());
  11429 
  11430  // The channel would sandbox aLoadState->PrincipalToInherit(). Even if
  11431  // the document already has a null principal, we don't know if it's the right
  11432  // sandboxed one. So be safe and clobber.
  11433  const uint32_t sandboxFlags =
  11434      mBrowsingContext->GetHasLoadedNonInitialDocument()
  11435          ? mBrowsingContext->GetSandboxFlags()
  11436          : mBrowsingContext->GetInitialSandboxFlags();
  11437  const bool shouldBeSandboxed = sandboxFlags & SANDBOXED_ORIGIN;
  11438  MOZ_ASSERT_IF(shouldBeSandboxed, aLoadState->PrincipalToInherit());
  11439 
  11440  // Clobber document before completing the synchronous load if it doesn't have
  11441  // the right principal (bug 1979032)
  11442  if (principalMissmatch || shouldBeSandboxed) {
  11443    nsIPrincipal* principal = aLoadState->PrincipalToInherit();
  11444    nsIPrincipal* partitionedPrincipal =
  11445        aLoadState->PartitionedPrincipalToInherit();
  11446    if (!partitionedPrincipal) {
  11447      partitionedPrincipal = principal;
  11448    }
  11449 
  11450    // This will sandbox the principals as needed
  11451    rv = CreateAboutBlankDocumentViewer(
  11452        principal, partitionedPrincipal, aLoadState->PolicyContainer(),
  11453        doc->GetDocBaseURI(), /* aIsInitialDocument */ true);
  11454    NS_ENSURE_SUCCESS(rv, rv);
  11455 
  11456    doc = mDocumentViewer->GetDocument();
  11457    MOZ_ASSERT(doc);
  11458    MOZ_LOG(gDocShellLog, LogLevel::Warning,
  11459            ("nsDocShell[%p] sync about:blank principals don't match, create "
  11460             "new document. Document[%p] \n",
  11461             this, doc.get()));
  11462  }
  11463 
  11464  MOZ_ASSERT(doc->IsInitialDocument(),
  11465             "How come the doc is no longer the initial one?");
  11466 
  11467  MOZ_ASSERT(doc->GetReadyStateEnum() == Document::READYSTATE_COMPLETE);
  11468  MOZ_ASSERT(!mIsLoadingDocument);
  11469 
  11470  doc->ApplyCspFromLoadInfo(aLoadInfo);
  11471  doc->ApplySettingsFromCSP(false);
  11472  doc->RecomputeResistFingerprinting();
  11473 
  11474  rv = doc->GetWindowContext()->SetIsOriginalFrameSource(
  11475      aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_ORIGINAL_FRAME_SRC));
  11476  NS_ENSURE_SUCCESS(rv, rv);
  11477 
  11478  nsPIDOMWindowInner* innerWindow = doc->GetInnerWindow();
  11479  if (innerWindow) {
  11480    mozilla::dom::ClientSource* clientSource =
  11481        nsGlobalWindowInner::Cast(innerWindow)->GetClientSource();
  11482    // See if we don't have a controller but the parent has gained a
  11483    // controller.
  11484    if (clientSource && clientSource->GetController().isNothing()) {
  11485      MaybeInheritController(
  11486          clientSource,
  11487          StoragePrincipalHelper::ShouldUsePartitionPrincipalForServiceWorker(
  11488              this)
  11489              ? doc->PartitionedPrincipal()
  11490              : doc->GetPrincipal());
  11491    }
  11492  }
  11493 
  11494  // Get the load event fired for the initial about:blank without starting
  11495  // a real load from a channel. We still need a channel object even though
  11496  // we don't care about reading from the channel.
  11497  nsCOMPtr<nsIChannel> aboutBlankChannel;
  11498  rv = NS_NewChannelInternal(getter_AddRefs(aboutBlankChannel),
  11499                             aLoadState->URI(), aLoadInfo, nullptr, mLoadGroup,
  11500                             nullptr, nsIChannel::LOAD_DOCUMENT_URI);
  11501  if (NS_FAILED(rv)) {
  11502    return rv;
  11503  }
  11504  if (!aboutBlankChannel) {
  11505    return NS_ERROR_FAILURE;
  11506  }
  11507 
  11508  MOZ_ASSERT(!mIsLoadingDocument);
  11509  MOZ_ASSERT(!mDocumentRequest);
  11510 
  11511  // Call OnStartRequest so that nsDocLoader sets mIsLoadingDocument and fire
  11512  // state start
  11513  OnStartRequest(aboutBlankChannel);
  11514 
  11515  MOZ_ASSERT(mIsLoadingDocument);
  11516  MOZ_ASSERT(mDocumentRequest == aboutBlankChannel);
  11517  MOZ_ASSERT(!doc->InitialAboutBlankLoadCompleting());
  11518 
  11519  doc->BeginInitialAboutBlankLoadCompleting(aboutBlankChannel);
  11520  auto resetLoadCompleting =
  11521      MakeScopeExit([&] { doc->EndInitialAboutBlankLoadCompleting(); });
  11522 
  11523  mCurrentURI = aLoadState->URI();
  11524  doc->SetDocumentURI(aLoadState->URI());
  11525 
  11526  // Normal documents fire the location change at content viewer creation.
  11527  // The initial about:blank does not do that at content viewer creation,
  11528  // so that the UI isn't bothered about the initial about:blank if there's
  11529  // immediate navigation away. However, now that the initial about:blank is
  11530  // going to remain in this docshell, we need to let to the UI know about it
  11531  // (at least in the top-level case).
  11532  FireOnLocationChange(this, aboutBlankChannel, aLoadState->URI(), 0);
  11533 
  11534  if (SessionHistoryInParent()) {
  11535    MoveLoadingToActiveEntry(false, 0, nullptr);
  11536  }
  11537 
  11538  doc->BeginLoad();
  11539 
  11540  nsContentUtils::AddScriptRunner(
  11541      new nsDocElementCreatedNotificationRunner(doc));
  11542  // When scripts are not blocked (are they ever blocked here?), the runnable
  11543  // runs immediately, so let's check if this docshell got destroyed or the
  11544  // document got swapped. Unclear if this ever happens; this is a defensive
  11545  // check.
  11546  if (mIsBeingDestroyed || !mDocumentViewer ||
  11547      doc != mDocumentViewer->GetDocument()) {
  11548    return NS_OK;
  11549  }
  11550 
  11551  // Initialize the presShell here in the window.open() case.
  11552  RefPtr<PresShell> presShell = doc->GetPresShell();
  11553  if (presShell && !presShell->DidInitialize()) {
  11554    rv = presShell->Initialize();
  11555    NS_ENSURE_SUCCESS(rv, rv);
  11556  }
  11557 
  11558  doc->SetScrollToRef(doc->GetDocumentURI());
  11559 
  11560  OnStopRequest(aboutBlankChannel, NS_OK);
  11561 
  11562  doc->EndLoad();
  11563  // Can't assert any postcondition, because the load event
  11564  // handler may have started loading something new in this
  11565  // docshell.
  11566 
  11567  return NS_OK;
  11568 }
  11569 
  11570 static nsresult AppendSegmentToString(nsIInputStream* aIn, void* aClosure,
  11571                                      const char* aFromRawSegment,
  11572                                      uint32_t aToOffset, uint32_t aCount,
  11573                                      uint32_t* aWriteCount) {
  11574  // aFromSegment now contains aCount bytes of data.
  11575 
  11576  nsAutoCString* buf = static_cast<nsAutoCString*>(aClosure);
  11577  buf->Append(aFromRawSegment, aCount);
  11578 
  11579  // Indicate that we have consumed all of aFromSegment
  11580  *aWriteCount = aCount;
  11581  return NS_OK;
  11582 }
  11583 
  11584 /* static */ nsresult nsDocShell::AddHeadersToChannel(
  11585    nsIInputStream* aHeadersData, nsIChannel* aGenericChannel) {
  11586  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aGenericChannel);
  11587  NS_ENSURE_STATE(httpChannel);
  11588 
  11589  uint32_t numRead;
  11590  nsAutoCString headersString;
  11591  nsresult rv = aHeadersData->ReadSegments(
  11592      AppendSegmentToString, &headersString, UINT32_MAX, &numRead);
  11593  NS_ENSURE_SUCCESS(rv, rv);
  11594 
  11595  // used during the manipulation of the String from the InputStream
  11596  nsAutoCString headerName;
  11597  nsAutoCString headerValue;
  11598  int32_t crlf;
  11599  int32_t colon;
  11600 
  11601  //
  11602  // Iterate over the headersString: for each "\r\n" delimited chunk,
  11603  // add the value as a header to the nsIHttpChannel
  11604  //
  11605 
  11606  static const char kWhitespace[] = "\b\t\r\n ";
  11607  while (true) {
  11608    crlf = headersString.Find("\r\n");
  11609    if (crlf == kNotFound) {
  11610      return NS_OK;
  11611    }
  11612 
  11613    const nsACString& oneHeader = StringHead(headersString, crlf);
  11614 
  11615    colon = oneHeader.FindChar(':');
  11616    if (colon == kNotFound) {
  11617      return NS_ERROR_UNEXPECTED;
  11618    }
  11619 
  11620    headerName = StringHead(oneHeader, colon);
  11621    headerValue = Substring(oneHeader, colon + 1);
  11622 
  11623    headerName.Trim(kWhitespace);
  11624    headerValue.Trim(kWhitespace);
  11625 
  11626    headersString.Cut(0, crlf + 2);
  11627 
  11628    //
  11629    // FINALLY: we can set the header!
  11630    //
  11631 
  11632    rv = httpChannel->SetRequestHeader(headerName, headerValue, true);
  11633    NS_ENSURE_SUCCESS(rv, rv);
  11634  }
  11635 
  11636  MOZ_ASSERT_UNREACHABLE("oops");
  11637  return NS_ERROR_UNEXPECTED;
  11638 }
  11639 
  11640 /* static */ uint32_t nsDocShell::ComputeURILoaderFlags(
  11641    BrowsingContext* aBrowsingContext, uint32_t aLoadType,
  11642    bool aIsDocumentLoad) {
  11643  MOZ_ASSERT(aBrowsingContext);
  11644 
  11645  uint32_t openFlags = 0;
  11646  if (aLoadType == LOAD_LINK) {
  11647    openFlags |= nsIURILoader::IS_CONTENT_PREFERRED;
  11648  }
  11649  if (!aBrowsingContext->GetAllowContentRetargeting()) {
  11650    openFlags |= nsIURILoader::DONT_RETARGET;
  11651  }
  11652 
  11653  if (!aIsDocumentLoad) {
  11654    openFlags |= nsIURILoader::IS_OBJECT_EMBED;
  11655 
  11656    // Unless the pref is set, object/embed loads always specify DONT_RETARGET.
  11657    // See bug 1868001 for details.
  11658    if (!StaticPrefs::dom_navigation_object_embed_allow_retargeting()) {
  11659      openFlags |= nsIURILoader::DONT_RETARGET;
  11660    }
  11661  }
  11662 
  11663  return openFlags;
  11664 }
  11665 
  11666 nsresult nsDocShell::OpenInitializedChannel(nsIChannel* aChannel,
  11667                                            nsIURILoader* aURILoader,
  11668                                            uint32_t aOpenFlags) {
  11669  nsresult rv = NS_OK;
  11670 
  11671  // If anything fails here, make sure to clear our initial ClientSource.
  11672  auto cleanupInitialClient =
  11673      MakeScopeExit([&] { mInitialClientSource.reset(); });
  11674 
  11675  nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
  11676  NS_ENSURE_TRUE(win, NS_ERROR_FAILURE);
  11677 
  11678  MaybeCreateInitialClientSource();
  11679 
  11680  // Let the client channel helper know if we are using DocumentChannel,
  11681  // since redirects get handled in the parent process in that case.
  11682  RefPtr<net::DocumentChannel> docChannel = do_QueryObject(aChannel);
  11683  if (docChannel && XRE_IsContentProcess()) {
  11684    // Tell the content process nsDocumentOpenInfo to not try to do
  11685    // any sort of targeting.
  11686    aOpenFlags |= nsIURILoader::DONT_RETARGET;
  11687  }
  11688 
  11689  // Since we are loading a document we need to make sure the proper reserved
  11690  // and initial client data is stored on the nsILoadInfo.  The
  11691  // ClientChannelHelper does this and ensures that it is propagated properly
  11692  // on redirects.  We pass no reserved client here so that the helper will
  11693  // create the reserved ClientSource if necessary.
  11694  Maybe<ClientInfo> noReservedClient;
  11695  if (docChannel) {
  11696    // When using DocumentChannel, all redirect handling is done in the parent,
  11697    // so we just need the child variant to watch for the internal redirect
  11698    // to the final channel.
  11699    rv = AddClientChannelHelperInChild(aChannel,
  11700                                       GetMainThreadSerialEventTarget());
  11701    docChannel->SetInitialClientInfo(GetInitialClientInfo());
  11702  } else {
  11703    rv = AddClientChannelHelper(aChannel, std::move(noReservedClient),
  11704                                GetInitialClientInfo(),
  11705                                GetMainThreadSerialEventTarget());
  11706  }
  11707  NS_ENSURE_SUCCESS(rv, rv);
  11708 
  11709  rv = aURILoader->OpenURI(aChannel, aOpenFlags, this);
  11710  NS_ENSURE_SUCCESS(rv, rv);
  11711 
  11712  // We're about to load a new page and it may take time before necko
  11713  // gives back any data, so main thread might have a chance to process a
  11714  // collector slice
  11715  nsJSContext::MaybeRunNextCollectorSlice(this, JS::GCReason::DOCSHELL);
  11716 
  11717  // Success.  Keep the initial ClientSource if it exists.
  11718  cleanupInitialClient.release();
  11719 
  11720  return NS_OK;
  11721 }
  11722 
  11723 nsresult nsDocShell::OpenRedirectedChannel(nsDocShellLoadState* aLoadState) {
  11724  nsCOMPtr<nsIChannel> channel = aLoadState->GetPendingRedirectedChannel();
  11725  MOZ_ASSERT(channel);
  11726 
  11727  // If anything fails here, make sure to clear our initial ClientSource.
  11728  auto cleanupInitialClient =
  11729      MakeScopeExit([&] { mInitialClientSource.reset(); });
  11730 
  11731  nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
  11732  NS_ENSURE_TRUE(win, NS_ERROR_FAILURE);
  11733 
  11734  MaybeCreateInitialClientSource();
  11735 
  11736  nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
  11737 
  11738  LoadInfo* li = static_cast<LoadInfo*>(loadInfo.get());
  11739  if (loadInfo->GetExternalContentPolicyType() ==
  11740      ExtContentPolicy::TYPE_DOCUMENT) {
  11741    li->UpdateBrowsingContextID(mBrowsingContext->Id());
  11742  } else if (loadInfo->GetExternalContentPolicyType() ==
  11743             ExtContentPolicy::TYPE_SUBDOCUMENT) {
  11744    li->UpdateFrameBrowsingContextID(mBrowsingContext->Id());
  11745  }
  11746 
  11747  // If we did a process switch, then we should have an existing allocated
  11748  // ClientInfo, so we just need to allocate a corresponding ClientSource.
  11749  CreateReservedSourceIfNeeded(channel, GetMainThreadSerialEventTarget());
  11750 
  11751  uint32_t documentOpenInfoFlags = nsIURILoader::DONT_RETARGET;
  11752  if (loadInfo->GetExternalContentPolicyType() ==
  11753      ExtContentPolicy::TYPE_OBJECT) {
  11754    documentOpenInfoFlags |= nsIURILoader::IS_OBJECT_EMBED;
  11755  }
  11756 
  11757  RefPtr<nsDocumentOpenInfo> loader =
  11758      new nsDocumentOpenInfo(this, documentOpenInfoFlags, nullptr);
  11759  channel->SetLoadGroup(mLoadGroup);
  11760 
  11761  MOZ_ALWAYS_SUCCEEDS(loader->Prepare());
  11762 
  11763  nsresult rv = NS_OK;
  11764  if (XRE_IsParentProcess()) {
  11765    // If we're in the parent, the we don't have an nsIChildChannel, just
  11766    // the original channel, which is already open in this process.
  11767 
  11768    // DocumentLoadListener expects to get an nsIParentChannel, so
  11769    // we create a wrapper around the channel and nsIStreamListener
  11770    // that forwards functionality as needed, and then we register
  11771    // it under the provided identifier.
  11772    RefPtr<ParentChannelWrapper> wrapper =
  11773        new ParentChannelWrapper(channel, loader);
  11774    wrapper->Register(aLoadState->GetPendingRedirectChannelRegistrarId());
  11775 
  11776    mLoadGroup->AddRequest(channel, nullptr);
  11777  } else if (nsCOMPtr<nsIChildChannel> childChannel =
  11778                 do_QueryInterface(channel)) {
  11779    // Our channel was redirected from another process, so doesn't need to
  11780    // be opened again. However, it does need its listener hooked up
  11781    // correctly.
  11782    rv = childChannel->CompleteRedirectSetup(loader);
  11783  } else {
  11784    // It's possible for the redirected channel to not implement
  11785    // nsIChildChannel and be entirely local (like srcdoc). In that case we
  11786    // can just open the local instance and it will work.
  11787    rv = channel->AsyncOpen(loader);
  11788  }
  11789  if (rv == NS_ERROR_NO_CONTENT) {
  11790    return NS_OK;
  11791  }
  11792  NS_ENSURE_SUCCESS(rv, rv);
  11793 
  11794  // Success.  Keep the initial ClientSource if it exists.
  11795  cleanupInitialClient.release();
  11796  return NS_OK;
  11797 }
  11798 
  11799 // https://html.spec.whatwg.org/#scrolling-to-a-fragment
  11800 nsresult nsDocShell::ScrollToAnchor(bool aCurHasRef, bool aNewHasRef,
  11801                                    nsACString& aNewHash, uint32_t aLoadType) {
  11802  if (!mCurrentURI) {
  11803    return NS_OK;
  11804  }
  11805 
  11806  RefPtr<PresShell> presShell = GetPresShell();
  11807  if (!presShell) {
  11808    // If we failed to get the shell, or if there is no shell,
  11809    // nothing left to do here.
  11810    return NS_OK;
  11811  }
  11812 
  11813  ScrollContainerFrame* rootScroll = presShell->GetRootScrollContainerFrame();
  11814  if (rootScroll) {
  11815    rootScroll->ClearDidHistoryRestore();
  11816  }
  11817 
  11818  // If it's a load from history, we don't have any anchor jumping to do.
  11819  // Scrollbar position will be restored by the caller based on positions stored
  11820  // in session history.
  11821  bool scroll = aLoadType != LOAD_HISTORY && aLoadType != LOAD_RELOAD_NORMAL;
  11822  // If the load contains text directives, try to apply them. This may fail if
  11823  // the load is a same-document load that was initiated before the document was
  11824  // fully loaded and the target is not yet included in the DOM tree.
  11825  // For this case, the `uninvokedTextDirectives` are not cleared, so that
  11826  // `Document::ScrollToRef()` can re-apply the text directive.
  11827  // `Document::ScrollToRef()` is (presumably) the second "async" call mentioned
  11828  // in sec. 7.4.2.3.3 in the HTML spec, "Fragment navigations":
  11829  // https://html.spec.whatwg.org/#scroll-to-fragid:~:text=This%20algorithm%20will%20be%20called%20twice
  11830 
  11831  const RefPtr fragmentDirective = GetDocument()->FragmentDirective();
  11832  const nsTArray<RefPtr<nsRange>> textDirectiveRanges =
  11833      fragmentDirective->FindTextFragmentsInDocument();
  11834  fragmentDirective->HighlightTextDirectives(textDirectiveRanges);
  11835  const bool scrollToTextDirective =
  11836      !textDirectiveRanges.IsEmpty() &&
  11837      fragmentDirective->IsTextDirectiveAllowedToBeScrolledTo();
  11838  const RefPtr<nsRange> textDirectiveToScroll =
  11839      scrollToTextDirective ? textDirectiveRanges[0] : nullptr;
  11840 
  11841  // If we have no new anchor, we do not want to scroll, unless there is a
  11842  // current anchor and we are doing a history load.  So return if we have no
  11843  // new anchor, and there is no current anchor or the load is not a history
  11844  // load.
  11845  if ((!aCurHasRef || aLoadType != LOAD_HISTORY) && !aNewHasRef &&
  11846      !scrollToTextDirective) {
  11847    return NS_OK;
  11848  }
  11849 
  11850  // Both the new and current URIs refer to the same page. We can now
  11851  // browse to the hash stored in the new URI.
  11852 
  11853  if (aNewHash.IsEmpty() && !scrollToTextDirective) {
  11854    // 2. If fragment is the empty string, then return the special value top of
  11855    // the document.
  11856    //
  11857    // Tell the shell it's at an anchor without scrolling.
  11858    presShell->GoToAnchor(u""_ns, nullptr, false);
  11859 
  11860    if (scroll) {
  11861      // Scroll to the top of the page. Ignore the return value; failure to
  11862      // scroll here (e.g. if there is no root scrollframe) is not grounds for
  11863      // canceling the load!
  11864      SetCurScrollPosEx(0, 0);
  11865    }
  11866 
  11867    return NS_OK;
  11868  }
  11869 
  11870  // 3. Let potentialIndicatedElement be the result of finding a potential
  11871  // indicated element given document and fragment.
  11872  NS_ConvertUTF8toUTF16 uStr(aNewHash);
  11873 
  11874  MOZ_ASSERT(!uStr.IsEmpty() || scrollToTextDirective);
  11875 
  11876  auto rv = presShell->GoToAnchor(uStr, textDirectiveToScroll, scroll,
  11877                                  ScrollFlags::ScrollSmoothAuto);
  11878 
  11879  // 4. If potentialIndicatedElement is not null, then return
  11880  // potentialIndicatedElement.
  11881  if (NS_SUCCEEDED(rv)) {
  11882    return NS_OK;
  11883  }
  11884 
  11885  // 5. Let fragmentBytes be the result of percent-decoding fragment.
  11886  nsAutoCString fragmentBytes;
  11887  const bool unescaped = NS_UnescapeURL(aNewHash.Data(), aNewHash.Length(),
  11888                                        /* aFlags = */ 0, fragmentBytes);
  11889 
  11890  if (!unescaped) {
  11891    // Another attempt is only necessary if characters were unescaped.
  11892    return NS_OK;
  11893  }
  11894 
  11895  if (fragmentBytes.IsEmpty()) {
  11896    // When aNewHash contains "%00", the unescaped string may be empty, and
  11897    // GoToAnchor asserts if we ask it to scroll to an empty ref.
  11898    presShell->GoToAnchor(u""_ns, nullptr, false);
  11899    return NS_OK;
  11900  }
  11901 
  11902  // 6. Let decodedFragment be the result of running UTF-8 decode without BOM on
  11903  // fragmentBytes.
  11904  nsAutoString decodedFragment;
  11905  rv = UTF_8_ENCODING->DecodeWithoutBOMHandling(fragmentBytes, decodedFragment);
  11906  NS_ENSURE_SUCCESS(rv, rv);
  11907 
  11908  // 7. Set potentialIndicatedElement to the result of finding a potential
  11909  // indicated element given document and decodedFragment.
  11910  //
  11911  // Ignore the return value of GoToAnchor, since it will return an error if
  11912  // there is no such anchor in the document, which is actually a success
  11913  // condition for us (we want to update the session history with the new URI no
  11914  // matter whether we actually scrolled somewhere).
  11915  presShell->GoToAnchor(decodedFragment, nullptr, scroll,
  11916                        ScrollFlags::ScrollSmoothAuto);
  11917 
  11918  return NS_OK;
  11919 }
  11920 
  11921 bool nsDocShell::OnNewURI(nsIURI* aURI, nsIChannel* aChannel,
  11922                          nsIPrincipal* aTriggeringPrincipal,
  11923                          nsIPrincipal* aPrincipalToInherit,
  11924                          nsIPrincipal* aPartitionedPrincipalToInherit,
  11925                          nsIPolicyContainer* aPolicyContainer,
  11926                          bool aAddToGlobalHistory, bool aCloneSHChildren) {
  11927  MOZ_ASSERT(aURI, "uri is null");
  11928  MOZ_ASSERT(!aChannel || !aTriggeringPrincipal, "Shouldn't have both set");
  11929 
  11930  MOZ_ASSERT(!aPrincipalToInherit ||
  11931             (aPrincipalToInherit && aTriggeringPrincipal));
  11932 
  11933 #if defined(DEBUG)
  11934  if (MOZ_LOG_TEST(gDocShellLog, LogLevel::Debug)) {
  11935    nsAutoCString chanName;
  11936    if (aChannel) {
  11937      aChannel->GetName(chanName);
  11938    } else {
  11939      chanName.AssignLiteral("<no channel>");
  11940    }
  11941 
  11942    MOZ_LOG(gDocShellLog, LogLevel::Debug,
  11943            ("nsDocShell[%p]::OnNewURI(\"%s\", [%s], 0x%x)\n", this,
  11944             aURI->GetSpecOrDefault().get(), chanName.get(), mLoadType));
  11945  }
  11946 #endif
  11947 
  11948  bool equalUri = false;
  11949 
  11950  // Get the post data and the HTTP response code from the channel.
  11951  uint32_t responseStatus = 0;
  11952  nsCOMPtr<nsIInputStream> inputStream;
  11953  if (aChannel) {
  11954    nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
  11955 
  11956    // Check if the HTTPChannel is hiding under a multiPartChannel
  11957    if (!httpChannel) {
  11958      GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
  11959    }
  11960 
  11961    if (httpChannel) {
  11962      nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
  11963      if (uploadChannel) {
  11964        uploadChannel->GetUploadStream(getter_AddRefs(inputStream));
  11965      }
  11966 
  11967      // If the response status indicates an error, unlink this session
  11968      // history entry from any entries sharing its document.
  11969      nsresult rv = httpChannel->GetResponseStatus(&responseStatus);
  11970      if (mLSHE && NS_SUCCEEDED(rv) && responseStatus >= 400) {
  11971        mLSHE->AbandonBFCacheEntry();
  11972        // FIXME Do the same for mLoadingEntry
  11973      }
  11974    }
  11975  }
  11976 
  11977  // Determine if this type of load should update history.
  11978  bool updateGHistory = ShouldUpdateGlobalHistory(mLoadType);
  11979 
  11980  // We don't update session history on reload unless we're loading
  11981  // an iframe in shift-reload case.
  11982  bool updateSHistory = mBrowsingContext->ShouldUpdateSessionHistory(mLoadType);
  11983 
  11984  // Create SH Entry (mLSHE) only if there is a SessionHistory object in the
  11985  // root browsing context.
  11986  // FIXME If session history in the parent is enabled then we only do this if
  11987  //       the session history object is in process, otherwise we can't really
  11988  //       use the mLSHE anyway. Once session history is only stored in the
  11989  //       parent then this code will probably be removed anyway.
  11990  RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
  11991  if (!rootSH) {
  11992    updateSHistory = false;
  11993    updateGHistory = false;  // XXX Why global history too?
  11994  }
  11995 
  11996  // Check if the url to be loaded is the same as the one already loaded.
  11997  if (mCurrentURI) {
  11998    aURI->Equals(mCurrentURI, &equalUri);
  11999  }
  12000 
  12001 #ifdef DEBUG
  12002  bool shAvailable = (rootSH != nullptr);
  12003 
  12004  // XXX This log message is almost useless because |updateSHistory|
  12005  //     and |updateGHistory| are not correct at this point.
  12006 
  12007  MOZ_LOG(gDocShellLog, LogLevel::Debug,
  12008          ("  shAvailable=%i updateSHistory=%i updateGHistory=%i"
  12009           " equalURI=%i\n",
  12010           shAvailable, updateSHistory, updateGHistory, equalUri));
  12011 #endif
  12012 
  12013  /* If the url to be loaded is the same as the one already there,
  12014   * and the original loadType is LOAD_NORMAL, LOAD_LINK, or
  12015   * LOAD_STOP_CONTENT, set loadType to LOAD_NORMAL_REPLACE so that
  12016   * AddToSessionHistory() won't mess with the current SHEntry and
  12017   * if this page has any frame children, it also will be handled
  12018   * properly. see bug 83684
  12019   *
  12020   * NB: If mOSHE is null but we have a current URI, then it probably
  12021   * means that we must be at the transient about:blank content viewer;
  12022   * we should let the normal load continue, since there's nothing to
  12023   * replace. Sometimes this happens after a session restore (eg process
  12024   * switch) and mCurrentURI is not about:blank; we assume we can let the load
  12025   * continue (Bug 1301399).
  12026   *
  12027   * XXX Hopefully changing the loadType at this time will not hurt
  12028   *  anywhere. The other way to take care of sequentially repeating
  12029   *  frameset pages is to add new methods to nsIDocShellTreeItem.
  12030   * Hopefully I don't have to do that.
  12031   */
  12032  if (equalUri &&
  12033      (mozilla::SessionHistoryInParent() ? !!mActiveEntry : !!mOSHE) &&
  12034      (mLoadType == LOAD_NORMAL || mLoadType == LOAD_LINK ||
  12035       mLoadType == LOAD_STOP_CONTENT) &&
  12036      !inputStream) {
  12037    mLoadType = LOAD_NORMAL_REPLACE;
  12038  }
  12039 
  12040  // If this is a refresh to the currently loaded url, we don't
  12041  // have to update session or global history.
  12042  if (mLoadType == LOAD_REFRESH && !inputStream && equalUri) {
  12043    SetHistoryEntryAndUpdateBC(Some<nsISHEntry*>(mOSHE), Nothing());
  12044  }
  12045 
  12046  /* If the user pressed shift-reload, cache will create a new cache key
  12047   * for the page. Save the new cacheKey in Session History.
  12048   * see bug 90098
  12049   */
  12050  if (aChannel && IsForceReloadType(mLoadType)) {
  12051    MOZ_ASSERT(!updateSHistory || IsSubframe(),
  12052               "We shouldn't be updating session history for forced"
  12053               " reloads unless we're in a newly created iframe!");
  12054 
  12055    nsCOMPtr<nsICacheInfoChannel> cacheChannel(do_QueryInterface(aChannel));
  12056    uint32_t cacheKey = 0;
  12057    // Get the Cache Key and store it in SH.
  12058    if (cacheChannel) {
  12059      cacheChannel->GetCacheKey(&cacheKey);
  12060    }
  12061    // If we already have a loading history entry, store the new cache key
  12062    // in it.  Otherwise, since we're doing a reload and won't be updating
  12063    // our history entry, store the cache key in our current history entry.
  12064    SetCacheKeyOnHistoryEntry(mLSHE ? mLSHE : mOSHE, cacheKey);
  12065 
  12066    if (!mozilla::SessionHistoryInParent()) {
  12067      // Since we're force-reloading, clear all the sub frame history.
  12068      ClearFrameHistory(mLSHE);
  12069      ClearFrameHistory(mOSHE);
  12070    }
  12071  }
  12072 
  12073  if (!mozilla::SessionHistoryInParent()) {
  12074    // Clear subframe history on refresh.
  12075    // XXX: history.go(0) won't go this path as mLoadType is LOAD_HISTORY in
  12076    // this case. One should re-validate after bug 1331865 fixed.
  12077    if (mLoadType == LOAD_REFRESH) {
  12078      ClearFrameHistory(mLSHE);
  12079      ClearFrameHistory(mOSHE);
  12080    }
  12081 
  12082    if (updateSHistory) {
  12083      // Update session history if necessary...
  12084      if (!mLSHE && (mItemType == typeContent) && mURIResultedInDocument) {
  12085        /* This is  a fresh page getting loaded for the first time
  12086         *.Create a Entry for it and add it to SH, if this is the
  12087         * rootDocShell
  12088         */
  12089        (void)AddToSessionHistory(
  12090            aURI, aChannel, aTriggeringPrincipal, aPrincipalToInherit,
  12091            aPartitionedPrincipalToInherit, aPolicyContainer, aCloneSHChildren,
  12092            getter_AddRefs(mLSHE));
  12093      }
  12094    } else if (GetSessionHistory() && mLSHE && mURIResultedInDocument) {
  12095      // Even if we don't add anything to SHistory, ensure the current index
  12096      // points to the same SHEntry as our mLSHE.
  12097 
  12098      GetSessionHistory()->LegacySHistory()->EnsureCorrectEntryAtCurrIndex(
  12099          mLSHE);
  12100    }
  12101  }
  12102 
  12103  // If this is a POST request, we do not want to include this in global
  12104  // history.
  12105  if (ShouldAddURIVisit(aChannel) && updateGHistory && aAddToGlobalHistory &&
  12106      !net::ChannelIsPost(aChannel)) {
  12107    nsCOMPtr<nsIURI> previousURI;
  12108    uint32_t previousFlags = 0;
  12109 
  12110    if (mLoadType & LOAD_CMD_RELOAD) {
  12111      // On a reload request, we don't set redirecting flags.
  12112      previousURI = aURI;
  12113    } else {
  12114      ExtractLastVisit(aChannel, getter_AddRefs(previousURI), &previousFlags);
  12115    }
  12116 
  12117    AddURIVisit(aURI, previousURI, previousFlags, responseStatus);
  12118  }
  12119 
  12120  // If this was a history load or a refresh, or it was a history load but
  12121  // later changed to LOAD_NORMAL_REPLACE due to redirection, update the index
  12122  // in session history.
  12123  if (!mozilla::SessionHistoryInParent() && rootSH &&
  12124      ((mLoadType & (LOAD_CMD_HISTORY | LOAD_CMD_RELOAD)) ||
  12125       mLoadType == LOAD_NORMAL_REPLACE || mLoadType == LOAD_REFRESH_REPLACE)) {
  12126    mPreviousEntryIndex = rootSH->Index();
  12127    if (!mozilla::SessionHistoryInParent()) {
  12128      rootSH->LegacySHistory()->UpdateIndex();
  12129    }
  12130    mLoadedEntryIndex = rootSH->Index();
  12131    MOZ_LOG(gPageCacheLog, LogLevel::Verbose,
  12132            ("Previous index: %d, Loaded index: %d", mPreviousEntryIndex,
  12133             mLoadedEntryIndex));
  12134  }
  12135 
  12136  // aCloneSHChildren exactly means "we are not loading a new document".
  12137  uint32_t locationFlags =
  12138      aCloneSHChildren ? uint32_t(LOCATION_CHANGE_SAME_DOCUMENT) : 0;
  12139 
  12140  bool onLocationChangeNeeded =
  12141      SetCurrentURI(aURI, aChannel, false,
  12142                    /* aIsInitialAboutBlank */ false, locationFlags);
  12143  // Make sure to store the referrer from the channel, if any
  12144  nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
  12145  if (httpChannel) {
  12146    mReferrerInfo = httpChannel->GetReferrerInfo();
  12147  }
  12148  return onLocationChangeNeeded;
  12149 }
  12150 
  12151 Maybe<Wireframe> nsDocShell::GetWireframe() {
  12152  const bool collectWireFrame =
  12153      mozilla::SessionHistoryInParent() &&
  12154      StaticPrefs::browser_history_collectWireframes() &&
  12155      mBrowsingContext->IsTopContent() && mActiveEntry;
  12156 
  12157  if (!collectWireFrame) {
  12158    return Nothing();
  12159  }
  12160 
  12161  RefPtr<Document> doc = mDocumentViewer->GetDocument();
  12162  Nullable<Wireframe> wireframe;
  12163  doc->GetWireframeWithoutFlushing(false, wireframe);
  12164  if (wireframe.IsNull()) {
  12165    return Nothing();
  12166  }
  12167  return Some(wireframe.Value());
  12168 }
  12169 
  12170 bool nsDocShell::CollectWireframe() {
  12171  Maybe<Wireframe> wireframe = GetWireframe();
  12172  if (wireframe.isNothing()) {
  12173    return false;
  12174  }
  12175 
  12176  if (XRE_IsParentProcess()) {
  12177    SessionHistoryEntry* entry =
  12178        mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
  12179    if (entry) {
  12180      entry->SetWireframe(wireframe);
  12181    }
  12182  } else {
  12183    (void)ContentChild::GetSingleton()->SendSessionHistoryEntryWireframe(
  12184        mBrowsingContext, wireframe.ref());
  12185  }
  12186 
  12187  return true;
  12188 }
  12189 
  12190 //*****************************************************************************
  12191 // nsDocShell: Session History
  12192 //*****************************************************************************
  12193 
  12194 NS_IMETHODIMP
  12195 nsDocShell::AddState(JS::Handle<JS::Value> aData, const nsAString& aTitle,
  12196                     const nsAString& aURL, bool aReplace, JSContext* aCx) {
  12197  MOZ_LOG(gSHLog, LogLevel::Debug,
  12198          ("nsDocShell[%p]: AddState(..., %s, %s, %d)", this,
  12199           NS_ConvertUTF16toUTF8(aTitle).get(),
  12200           NS_ConvertUTF16toUTF8(aURL).get(), aReplace));
  12201  // Implements History.pushState and History.replaceState
  12202 
  12203  // Here's what we do, roughly in the order specified by HTML5.  The specific
  12204  // steps we are executing are at
  12205  // <https://html.spec.whatwg.org/multipage/history.html#dom-history-pushstate>,
  12206  // <https://html.spec.whatwg.org/#shared-history-push/replace-state-steps>,
  12207  // and
  12208  // <https://html.spec.whatwg.org/multipage/history.html#url-and-history-update-steps>.
  12209  // This function basically implements #dom-history-pushstate and
  12210  // UpdateURLAndHistory implements #url-and-history-update-steps.
  12211  //
  12212  // A. Serialize aData using structured clone.  This is #dom-history-pushstate
  12213  //    step 5.
  12214  // B. If the third argument is present, #dom-history-pushstate step 7.
  12215  //     7.1. Resolve the url, relative to our document.
  12216  //     7.2. If (a) fails, raise a SECURITY_ERR
  12217  //     7.4. Compare the resulting absolute URL to the document's address.  If
  12218  //          any part of the URLs difer other than the <path>, <query>, and
  12219  //          <fragment> components, raise a SECURITY_ERR and abort.
  12220  // C. If !aReplace, #url-and-history-update-steps steps 2.1-2.3:
  12221  //     Remove from the session history all entries after the current entry,
  12222  //     as we would after a regular navigation, and save the current
  12223  //     entry's scroll position (bug 590573).
  12224  // D. #url-and-history-update-steps step 2.4 or step 3.  As apropriate,
  12225  //    either add a state object entry to the session history after the
  12226  //    current entry with the following properties, or modify the current
  12227  //    session history entry to set
  12228  //      a. cloned data as the state object,
  12229  //      b. if the third argument was present, the absolute URL found in
  12230  //         step 2
  12231  //    Also clear the new history entry's POST data (see bug 580069).
  12232  // E. If aReplace is false (i.e. we're doing a pushState instead of a
  12233  //    replaceState), notify bfcache that we've navigated to a new page.
  12234  // F. If the third argument is present, set the document's current address
  12235  //    to the absolute URL found in step B.  This is
  12236  //    #url-and-history-update-steps step 4.
  12237  //
  12238  // It's important that this function not run arbitrary scripts after step A
  12239  // and before completing step E.  For example, if a script called
  12240  // history.back() before we completed step E, bfcache might destroy an
  12241  // active content viewer.  Since EvictOutOfRangeDocumentViewers at the end of
  12242  // step E might run script, we can't just put a script blocker around the
  12243  // critical section.
  12244  //
  12245  // Note that we completely ignore the aTitle parameter.
  12246 
  12247  nsresult rv;
  12248 
  12249  RefPtr<Document> document = GetDocument();
  12250  NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
  12251 
  12252  // Don't clobber the load type of an existing network load.
  12253  Maybe<AutoRestore<uint32_t>> loadTypeResetter;
  12254  if (StaticPrefs::
  12255          docshell_shistory_sameDocumentNavigationOverridesLoadType() &&
  12256      !document->NodePrincipal()->IsURIInPrefList(
  12257          "docshell.shistory.sameDocumentNavigationOverridesLoadType."
  12258          "forceDisable")) {
  12259    loadTypeResetter.emplace(mLoadType);
  12260  }
  12261 
  12262  // pushState effectively becomes replaceState when we've started a network
  12263  // load but haven't adopted its document yet.  This mirrors what we do with
  12264  // changes to the hash at this stage of the game.
  12265  if (JustStartedNetworkLoad()) {
  12266    if (!loadTypeResetter.isSome()) {
  12267      loadTypeResetter.emplace(mLoadType);
  12268    }
  12269    aReplace = true;
  12270  }
  12271 
  12272  // Step A: Serialize aData using structured clone.
  12273  // https://html.spec.whatwg.org/multipage/history.html#dom-history-pushstate
  12274  // step 5.
  12275  nsCOMPtr<nsIStructuredCloneContainer> scContainer;
  12276 
  12277  // scContainer->Init might cause arbitrary JS to run, and this code might
  12278  // navigate the page we're on, potentially to a different origin! (bug
  12279  // 634834)  To protect against this, we abort if our principal changes due
  12280  // to the InitFromJSVal() call.
  12281  {
  12282    RefPtr<Document> origDocument = GetDocument();
  12283    if (!origDocument) {
  12284      return NS_ERROR_DOM_SECURITY_ERR;
  12285    }
  12286    nsCOMPtr<nsIPrincipal> origPrincipal = origDocument->NodePrincipal();
  12287 
  12288    scContainer = new nsStructuredCloneContainer();
  12289    rv = scContainer->InitFromJSVal(aData, aCx);
  12290    NS_ENSURE_SUCCESS(rv, rv);
  12291 
  12292    RefPtr<Document> newDocument = GetDocument();
  12293    if (!newDocument) {
  12294      return NS_ERROR_DOM_SECURITY_ERR;
  12295    }
  12296    nsCOMPtr<nsIPrincipal> newPrincipal = newDocument->NodePrincipal();
  12297 
  12298    bool principalsEqual = false;
  12299    origPrincipal->Equals(newPrincipal, &principalsEqual);
  12300    NS_ENSURE_TRUE(principalsEqual, NS_ERROR_DOM_SECURITY_ERR);
  12301  }
  12302 
  12303  // Check that the state object isn't too long.
  12304  int32_t maxStateObjSize = StaticPrefs::browser_history_maxStateObjectSize();
  12305  if (maxStateObjSize < 0) {
  12306    maxStateObjSize = 0;
  12307  }
  12308 
  12309  uint64_t scSize;
  12310  rv = scContainer->GetSerializedNBytes(&scSize);
  12311  NS_ENSURE_SUCCESS(rv, rv);
  12312 
  12313  NS_ENSURE_TRUE(scSize <= (uint32_t)maxStateObjSize, NS_ERROR_ILLEGAL_VALUE);
  12314 
  12315  // Step B: Resolve aURL.
  12316  // https://html.spec.whatwg.org/multipage/history.html#dom-history-pushstate
  12317  // step 7.
  12318  bool equalURIs = true;
  12319  nsCOMPtr<nsIURI> currentURI;
  12320  if (mCurrentURI) {
  12321    currentURI = nsIOService::CreateExposableURI(mCurrentURI);
  12322  } else {
  12323    currentURI = mCurrentURI;
  12324  }
  12325  nsCOMPtr<nsIURI> newURI;
  12326  if (aURL.Length() == 0) {
  12327    newURI = currentURI;
  12328  } else {
  12329    // 7.1: Resolve aURL relative to mURI
  12330 
  12331    nsIURI* docBaseURI = document->GetDocBaseURI();
  12332    if (!docBaseURI) {
  12333      return NS_ERROR_FAILURE;
  12334    }
  12335 
  12336    nsAutoCString spec;
  12337    docBaseURI->GetSpec(spec);
  12338 
  12339    rv = NS_NewURI(getter_AddRefs(newURI), aURL,
  12340                   document->GetDocumentCharacterSet(), docBaseURI);
  12341 
  12342    // 7.2: If 2a fails, raise a SECURITY_ERR
  12343    if (NS_FAILED(rv)) {
  12344      return NS_ERROR_DOM_SECURITY_ERR;
  12345    }
  12346 
  12347    if (!document->CanRewriteURL(newURI)) {
  12348      return NS_ERROR_DOM_SECURITY_ERR;
  12349    }
  12350 
  12351    if (currentURI) {
  12352      currentURI->Equals(newURI, &equalURIs);
  12353    } else {
  12354      equalURIs = false;
  12355    }
  12356 
  12357  }  // end of same-origin check
  12358 
  12359  // https://html.spec.whatwg.org/#shared-history-push/replace-state-steps
  12360  // Step 8
  12361  if (nsCOMPtr<nsPIDOMWindowInner> window = document->GetInnerWindow()) {
  12362    if (RefPtr<Navigation> navigation = window->Navigation()) {
  12363      bool shouldContinue = navigation->FirePushReplaceReloadNavigateEvent(
  12364          aCx, aReplace ? NavigationType::Replace : NavigationType::Push,
  12365          newURI,
  12366          /* aIsSameDocument */ true,
  12367          /* aUserInvolvement */ Nothing(),
  12368          /* aSourceElement */ nullptr, /* aFormDataEntryList */ nullptr,
  12369          /* aNavigationAPIState */ nullptr, scContainer);
  12370 
  12371      // Step 9
  12372      if (!shouldContinue) {
  12373        return NS_OK;
  12374      }
  12375    }
  12376  }
  12377 
  12378  // Step 10
  12379  // Run #url-and-history-update-steps
  12380  rv = UpdateURLAndHistory(document, newURI, scContainer,
  12381                           aReplace ? NavigationHistoryBehavior::Replace
  12382                                    : NavigationHistoryBehavior::Push,
  12383                           currentURI, equalURIs);
  12384  NS_ENSURE_SUCCESS(rv, rv);
  12385 
  12386  return NS_OK;
  12387 }
  12388 
  12389 nsresult nsDocShell::UpdateURLAndHistory(
  12390    Document* aDocument, nsIURI* aNewURI, nsIStructuredCloneContainer* aData,
  12391    NavigationHistoryBehavior aHistoryHandling, nsIURI* aCurrentURI,
  12392    bool aEqualURIs) {
  12393  // Implements
  12394  // https://html.spec.whatwg.org/multipage/history.html#url-and-history-update-steps
  12395  MOZ_ASSERT(aHistoryHandling != NavigationHistoryBehavior::Auto);
  12396  bool isReplace = aHistoryHandling == NavigationHistoryBehavior::Replace;
  12397 
  12398  // If we have a pending title change, handle it before creating a new entry.
  12399  aDocument->DoNotifyPossibleTitleChange();
  12400 
  12401  // Step 2, if aReplace is false: Create a new entry in the session
  12402  // history. This will erase all SHEntries after the new entry and make this
  12403  // entry the current one.  This operation may modify mOSHE, which we need
  12404  // later, so we keep a reference here.
  12405  NS_ENSURE_TRUE(mOSHE || mActiveEntry || isReplace, NS_ERROR_FAILURE);
  12406  nsCOMPtr<nsISHEntry> oldOSHE = mOSHE;
  12407 
  12408  // If this push/replaceState changed the document's current URI and the new
  12409  // URI differs from the old URI in more than the hash, or if the old
  12410  // SHEntry's URI was modified in this way by a push/replaceState call
  12411  // set URIWasModified to true for the current SHEntry (bug 669671).
  12412  bool sameExceptHashes = true;
  12413  aNewURI->EqualsExceptRef(aCurrentURI, &sameExceptHashes);
  12414  bool uriWasModified;
  12415  if (sameExceptHashes) {
  12416    if (mozilla::SessionHistoryInParent()) {
  12417      uriWasModified = mActiveEntry && mActiveEntry->GetURIWasModified();
  12418    } else {
  12419      uriWasModified = oldOSHE && oldOSHE->GetURIWasModified();
  12420    }
  12421  } else {
  12422    uriWasModified = true;
  12423  }
  12424 
  12425  mLoadType = LOAD_PUSHSTATE;
  12426 
  12427  nsCOMPtr<nsISHEntry> newSHEntry;
  12428  if (!isReplace) {
  12429    // Step 2.
  12430 
  12431    // Step 2.2, "Remove any tasks queued by the history traversal task
  12432    // source that are associated with any Document objects in the
  12433    // top-level browsing context's document family."  This is very hard in
  12434    // SessionHistoryInParent since we can't synchronously access the
  12435    // pending navigations that are already sent to the parent. We can
  12436    // abort any AsyncGo navigations that are waiting to be sent.  If we
  12437    // send a message to the parent, it would be processed after any
  12438    // navigations previously sent.  So long as we consider the "history
  12439    // traversal task source" to be the list in this process we match the
  12440    // spec.  If we move the entire list to the parent, we can handle the
  12441    // aborting of loads there, but we don't have a way to synchronously
  12442    // remove entries as we do here for non-SHIP.
  12443    RefPtr<ChildSHistory> shistory = GetRootSessionHistory();
  12444    if (shistory) {
  12445      shistory->RemovePendingHistoryNavigations();
  12446    }
  12447 
  12448    nsPoint scrollPos = GetCurScrollPos();
  12449 
  12450    bool scrollRestorationIsManual;
  12451    if (mozilla::SessionHistoryInParent()) {
  12452      // FIXME Need to save the current scroll position on mActiveEntry.
  12453      scrollRestorationIsManual = mActiveEntry->GetScrollRestorationIsManual();
  12454    } else {
  12455      // Save the current scroll position (bug 590573).  Step 2.3.
  12456      mOSHE->SetScrollPosition(scrollPos.x, scrollPos.y);
  12457 
  12458      scrollRestorationIsManual = mOSHE->GetScrollRestorationIsManual();
  12459    }
  12460 
  12461    nsCOMPtr<nsIPolicyContainer> policyContainer =
  12462        aDocument->GetPolicyContainer();
  12463 
  12464    if (mozilla::SessionHistoryInParent()) {
  12465      MOZ_LOG(gSHLog, LogLevel::Debug,
  12466              ("nsDocShell %p UpdateActiveEntry (not replacing)", this));
  12467 
  12468      nsString title(mActiveEntry->GetTitle());
  12469      nsCOMPtr<nsIReferrerInfo> referrerInfo = mActiveEntry->GetReferrerInfo();
  12470 
  12471      UpdateActiveEntry(false,
  12472                        /* aPreviousScrollPos = */ Some(scrollPos), aNewURI,
  12473                        /* aOriginalURI = */ nullptr,
  12474                        /* aReferrerInfo = */ referrerInfo,
  12475                        /* aTriggeringPrincipal = */ aDocument->NodePrincipal(),
  12476                        policyContainer, title, scrollRestorationIsManual,
  12477                        aData, uriWasModified);
  12478    } else {
  12479      // Since we're not changing which page we have loaded, pass
  12480      // true for aCloneChildren.
  12481      nsresult rv = AddToSessionHistory(
  12482          aNewURI, nullptr,
  12483          aDocument->NodePrincipal(),  // triggeringPrincipal
  12484          nullptr, nullptr, policyContainer, true, getter_AddRefs(newSHEntry));
  12485      NS_ENSURE_SUCCESS(rv, rv);
  12486 
  12487      NS_ENSURE_TRUE(newSHEntry, NS_ERROR_FAILURE);
  12488 
  12489      // Session history entries created by pushState inherit scroll restoration
  12490      // mode from the current entry.
  12491      newSHEntry->SetScrollRestorationIsManual(scrollRestorationIsManual);
  12492 
  12493      // Set the new SHEntry's title (bug 655273).
  12494      nsString title;
  12495      mOSHE->GetTitle(title);
  12496      newSHEntry->SetTitle(title);
  12497 
  12498      nsCOMPtr<nsIReferrerInfo> referrerInfo = mOSHE->GetReferrerInfo();
  12499      newSHEntry->SetReferrerInfo(referrerInfo);
  12500 
  12501      // Link the new SHEntry to the old SHEntry's BFCache entry, since the
  12502      // two entries correspond to the same document.
  12503      NS_ENSURE_SUCCESS(newSHEntry->AdoptBFCacheEntry(oldOSHE),
  12504                        NS_ERROR_FAILURE);
  12505 
  12506      // AddToSessionHistory may not modify mOSHE.  In case it doesn't,
  12507      // we'll just set mOSHE here.
  12508      mOSHE = newSHEntry;
  12509    }
  12510  } else if (mozilla::SessionHistoryInParent()) {
  12511    MOZ_LOG(gSHLog, LogLevel::Debug,
  12512            ("nsDocShell %p UpdateActiveEntry (replacing) mActiveEntry %p",
  12513             this, mActiveEntry.get()));
  12514    // Setting the resultPrincipalURI to nullptr is fine here: it will cause
  12515    // NS_GetFinalChannelURI to use the originalURI as the URI, which is aNewURI
  12516    // in our case.  We could also set it to aNewURI, with the same result.
  12517    // We don't use aTitle here, see bug 544535.
  12518    nsString title;
  12519    nsCOMPtr<nsIReferrerInfo> referrerInfo;
  12520    if (mActiveEntry) {
  12521      title = mActiveEntry->GetTitle();
  12522      referrerInfo = mActiveEntry->GetReferrerInfo();
  12523    } else {
  12524      referrerInfo = nullptr;
  12525    }
  12526    UpdateActiveEntry(
  12527        true, /* aPreviousScrollPos = */ Nothing(), aNewURI, aNewURI,
  12528        /* aReferrerInfo = */ referrerInfo, aDocument->NodePrincipal(),
  12529        aDocument->GetPolicyContainer(), title,
  12530        mActiveEntry && mActiveEntry->GetScrollRestorationIsManual(), aData,
  12531        uriWasModified);
  12532  } else {
  12533    // Step 3.
  12534    newSHEntry = mOSHE;
  12535 
  12536    MOZ_LOG(gSHLog, LogLevel::Debug, ("nsDocShell %p step 3", this));
  12537    // Since we're not changing which page we have loaded, pass
  12538    // true for aCloneChildren.
  12539    if (!newSHEntry) {
  12540      nsresult rv = AddToSessionHistory(
  12541          aNewURI, nullptr,
  12542          aDocument->NodePrincipal(),  // triggeringPrincipal
  12543          nullptr, nullptr, aDocument->GetPolicyContainer(), true,
  12544          getter_AddRefs(newSHEntry));
  12545      NS_ENSURE_SUCCESS(rv, rv);
  12546      mOSHE = newSHEntry;
  12547    }
  12548 
  12549    nsCOMPtr<nsIReferrerInfo> referrerInfo = mOSHE->GetReferrerInfo();
  12550 
  12551    newSHEntry->SetURI(aNewURI);
  12552    newSHEntry->SetOriginalURI(aNewURI);
  12553    // We replaced the URI of the entry, clear the unstripped URI as it
  12554    // shouldn't be used for reloads anymore.
  12555    newSHEntry->SetUnstrippedURI(nullptr);
  12556    // Setting the resultPrincipalURI to nullptr is fine here: it will cause
  12557    // NS_GetFinalChannelURI to use the originalURI as the URI, which is aNewURI
  12558    // in our case.  We could also set it to aNewURI, with the same result.
  12559    newSHEntry->SetResultPrincipalURI(nullptr);
  12560    newSHEntry->SetLoadReplace(false);
  12561    newSHEntry->SetReferrerInfo(referrerInfo);
  12562  }
  12563 
  12564  if (!mozilla::SessionHistoryInParent()) {
  12565    // Step 2.4 and 3: Modify new/original session history entry and clear its
  12566    // POST data, if there is any.
  12567    newSHEntry->SetStateData(aData);
  12568    newSHEntry->SetPostData(nullptr);
  12569 
  12570    newSHEntry->SetURIWasModified(uriWasModified);
  12571 
  12572    // Step E as described at the top of AddState: If aReplace is false,
  12573    // indicating that we're doing a pushState rather than a replaceState,
  12574    // notify bfcache that we've added a page to the history so it can evict
  12575    // content viewers if appropriate. Otherwise call ReplaceEntry so that we
  12576    // notify nsIHistoryListeners that an entry was replaced.  We may not have a
  12577    // root session history if this call is coming from a document.open() in a
  12578    // docshell subtree that disables session history.
  12579    RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
  12580    if (rootSH) {
  12581      rootSH->LegacySHistory()->EvictDocumentViewersOrReplaceEntry(newSHEntry,
  12582                                                                   isReplace);
  12583    }
  12584  }
  12585 
  12586  // Step 4: If the document's URI changed, update document's URI and update
  12587  // global history.
  12588  //
  12589  // We need to call FireOnLocationChange so that the browser's address bar
  12590  // gets updated and the back button is enabled, but we only need to
  12591  // explicitly call FireOnLocationChange if we're not calling SetCurrentURI,
  12592  // since SetCurrentURI will call FireOnLocationChange for us.
  12593  //
  12594  // Both SetCurrentURI(...) and FireDummyOnLocationChange() pass
  12595  // nullptr for aRequest param to FireOnLocationChange(...). Such an update
  12596  // notification is allowed only when we know docshell is not loading a new
  12597  // document and it requires LOCATION_CHANGE_SAME_DOCUMENT flag. Otherwise,
  12598  // FireOnLocationChange(...) breaks security UI.
  12599  //
  12600  // If the docshell is shutting down, don't update the document URI, as we
  12601  // can't load into a docshell that is being destroyed.
  12602  if (!aEqualURIs && !mIsBeingDestroyed) {
  12603    aDocument->SetDocumentURI(aNewURI);
  12604    SetCurrentURI(aNewURI, nullptr, /* aFireLocationChange */ true,
  12605                  /* aIsInitialAboutBlank */ false,
  12606                  GetSameDocumentNavigationFlags(aNewURI));
  12607 
  12608    AddURIVisit(aNewURI, aCurrentURI, 0);
  12609 
  12610    // AddURIVisit doesn't set the title for the new URI in global history,
  12611    // so do that here.
  12612    UpdateGlobalHistoryTitle(aNewURI);
  12613 
  12614    // Inform the favicon service that our old favicon applies to this new
  12615    // URI.
  12616    CopyFavicon(aCurrentURI, aNewURI, UsePrivateBrowsing());
  12617  } else {
  12618    FireDummyOnLocationChange();
  12619  }
  12620  aDocument->SetStateObject(aData);
  12621 
  12622  if (RefPtr navigation = aDocument->GetInnerWindow()->Navigation()) {
  12623    MOZ_LOG(gNavigationAPILog, LogLevel::Debug,
  12624            ("nsDocShell %p triggering a navigation event for a same-document "
  12625             "navigation from UpdateURLAndHistory -> isReplace: %s",
  12626             this, isReplace ? "true" : "false"));
  12627    // Step 11: Update the navigation API entries for a same-document
  12628    // navigation given document's relevant global object's navigation API,
  12629    // newEntry, and historyHandling.
  12630    navigation->UpdateEntriesForSameDocumentNavigation(
  12631        mActiveEntry.get(),
  12632        isReplace ? NavigationType::Replace : NavigationType::Push);
  12633  }
  12634 
  12635  return NS_OK;
  12636 }
  12637 
  12638 NS_IMETHODIMP
  12639 nsDocShell::GetCurrentScrollRestorationIsManual(bool* aIsManual) {
  12640  if (mozilla::SessionHistoryInParent()) {
  12641    *aIsManual = mActiveEntry && mActiveEntry->GetScrollRestorationIsManual();
  12642    return NS_OK;
  12643  }
  12644 
  12645  *aIsManual = false;
  12646  if (mOSHE) {
  12647    return mOSHE->GetScrollRestorationIsManual(aIsManual);
  12648  }
  12649 
  12650  return NS_OK;
  12651 }
  12652 
  12653 NS_IMETHODIMP
  12654 nsDocShell::SetCurrentScrollRestorationIsManual(bool aIsManual) {
  12655  SetScrollRestorationIsManualOnHistoryEntry(mOSHE, aIsManual);
  12656 
  12657  return NS_OK;
  12658 }
  12659 
  12660 void nsDocShell::SetScrollRestorationIsManualOnHistoryEntry(
  12661    nsISHEntry* aSHEntry, bool aIsManual) {
  12662  if (aSHEntry) {
  12663    aSHEntry->SetScrollRestorationIsManual(aIsManual);
  12664  }
  12665 
  12666  if (mActiveEntry && mBrowsingContext) {
  12667    mActiveEntry->SetScrollRestorationIsManual(aIsManual);
  12668    if (XRE_IsParentProcess()) {
  12669      SessionHistoryEntry* entry =
  12670          mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
  12671      if (entry) {
  12672        entry->SetScrollRestorationIsManual(aIsManual);
  12673      }
  12674    } else {
  12675      (void)ContentChild::GetSingleton()
  12676          ->SendSessionHistoryEntryScrollRestorationIsManual(mBrowsingContext,
  12677                                                             aIsManual);
  12678    }
  12679  }
  12680 }
  12681 
  12682 void nsDocShell::SetCacheKeyOnHistoryEntry(nsISHEntry* aSHEntry,
  12683                                           uint32_t aCacheKey) {
  12684  if (aSHEntry) {
  12685    aSHEntry->SetCacheKey(aCacheKey);
  12686  }
  12687 
  12688  if (mActiveEntry && mBrowsingContext) {
  12689    mActiveEntry->SetCacheKey(aCacheKey);
  12690    if (XRE_IsParentProcess()) {
  12691      SessionHistoryEntry* entry =
  12692          mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
  12693      if (entry) {
  12694        entry->SetCacheKey(aCacheKey);
  12695      }
  12696    } else {
  12697      (void)ContentChild::GetSingleton()->SendSessionHistoryEntryCacheKey(
  12698          mBrowsingContext, aCacheKey);
  12699    }
  12700  }
  12701 }
  12702 
  12703 /* static */
  12704 bool nsDocShell::ShouldAddToSessionHistory(nsIURI* aURI, nsIChannel* aChannel) {
  12705  // I believe none of the about: urls should go in the history. But then
  12706  // that could just be me... If the intent is only deny about:blank then we
  12707  // should just do a spec compare, rather than two gets of the scheme and
  12708  // then the path.  -Gagan
  12709  nsresult rv;
  12710  nsAutoCString buf;
  12711 
  12712  rv = aURI->GetScheme(buf);
  12713  if (NS_FAILED(rv)) {
  12714    return false;
  12715  }
  12716 
  12717  if (buf.EqualsLiteral("about")) {
  12718    rv = aURI->GetPathQueryRef(buf);
  12719    if (NS_FAILED(rv)) {
  12720      return false;
  12721    }
  12722 
  12723    if (buf.EqualsLiteral("blank")) {
  12724      return false;
  12725    }
  12726    // We only want to add about:newtab if it's not privileged, and
  12727    // if it is not configured to show the blank page.
  12728    if (buf.EqualsLiteral("newtab")) {
  12729      if (!StaticPrefs::browser_newtabpage_enabled()) {
  12730        return false;
  12731      }
  12732 
  12733      NS_ENSURE_TRUE(aChannel, false);
  12734      nsCOMPtr<nsIPrincipal> resultPrincipal;
  12735      rv = nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
  12736          aChannel, getter_AddRefs(resultPrincipal));
  12737      NS_ENSURE_SUCCESS(rv, false);
  12738      return !resultPrincipal->IsSystemPrincipal();
  12739    }
  12740  }
  12741 
  12742  return true;
  12743 }
  12744 
  12745 nsresult nsDocShell::AddToSessionHistory(
  12746    nsIURI* aURI, nsIChannel* aChannel, nsIPrincipal* aTriggeringPrincipal,
  12747    nsIPrincipal* aPrincipalToInherit,
  12748    nsIPrincipal* aPartitionedPrincipalToInherit,
  12749    nsIPolicyContainer* aPolicyContainer, bool aCloneChildren,
  12750    nsISHEntry** aNewEntry) {
  12751  MOZ_ASSERT(aURI, "uri is null");
  12752  MOZ_ASSERT(!aChannel || !aTriggeringPrincipal, "Shouldn't have both set");
  12753  MOZ_DIAGNOSTIC_ASSERT(!mozilla::SessionHistoryInParent());
  12754 
  12755 #if defined(DEBUG)
  12756  if (MOZ_LOG_TEST(gDocShellLog, LogLevel::Debug)) {
  12757    nsAutoCString chanName;
  12758    if (aChannel) {
  12759      aChannel->GetName(chanName);
  12760    } else {
  12761      chanName.AssignLiteral("<no channel>");
  12762    }
  12763 
  12764    MOZ_LOG(gDocShellLog, LogLevel::Debug,
  12765            ("nsDocShell[%p]::AddToSessionHistory(\"%s\", [%s])\n", this,
  12766             aURI->GetSpecOrDefault().get(), chanName.get()));
  12767  }
  12768 #endif
  12769 
  12770  nsresult rv = NS_OK;
  12771  nsCOMPtr<nsISHEntry> entry;
  12772 
  12773  /*
  12774   * If this is a LOAD_FLAGS_REPLACE_HISTORY in a subframe, we use
  12775   * the existing SH entry in the page and replace the url and
  12776   * other vitalities.
  12777   */
  12778  if (LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY) &&
  12779      !mBrowsingContext->IsTop()) {
  12780    // This is a subframe
  12781    entry = mOSHE;
  12782    if (entry) {
  12783      entry->ClearEntry();
  12784    }
  12785  }
  12786 
  12787  // Create a new entry if necessary.
  12788  if (!entry) {
  12789    entry = new nsSHEntry();
  12790  }
  12791 
  12792  // Get the post data & referrer
  12793  nsCOMPtr<nsIInputStream> inputStream;
  12794  nsCOMPtr<nsIURI> originalURI;
  12795  nsCOMPtr<nsIURI> resultPrincipalURI;
  12796  nsCOMPtr<nsIURI> unstrippedURI;
  12797  bool loadReplace = false;
  12798  nsCOMPtr<nsIReferrerInfo> referrerInfo;
  12799  uint32_t cacheKey = 0;
  12800  nsCOMPtr<nsIPrincipal> triggeringPrincipal = aTriggeringPrincipal;
  12801  nsCOMPtr<nsIPrincipal> principalToInherit = aPrincipalToInherit;
  12802  nsCOMPtr<nsIPrincipal> partitionedPrincipalToInherit =
  12803      aPartitionedPrincipalToInherit;
  12804  nsCOMPtr<nsIPolicyContainer> policyContainer = aPolicyContainer;
  12805  bool expired = false;  // by default the page is not expired
  12806  bool discardLayoutState = false;
  12807  nsCOMPtr<nsICacheInfoChannel> cacheChannel;
  12808  bool userActivation = false;
  12809 
  12810  if (aChannel) {
  12811    cacheChannel = do_QueryInterface(aChannel);
  12812 
  12813    /* If there is a caching channel, get the Cache Key and store it
  12814     * in SH.
  12815     */
  12816    if (cacheChannel) {
  12817      cacheChannel->GetCacheKey(&cacheKey);
  12818    }
  12819    nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
  12820 
  12821    // Check if the httpChannel is hiding under a multipartChannel
  12822    if (!httpChannel) {
  12823      GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
  12824    }
  12825    if (httpChannel) {
  12826      nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
  12827      if (uploadChannel) {
  12828        uploadChannel->GetUploadStream(getter_AddRefs(inputStream));
  12829      }
  12830      httpChannel->GetOriginalURI(getter_AddRefs(originalURI));
  12831      uint32_t loadFlags;
  12832      aChannel->GetLoadFlags(&loadFlags);
  12833      loadReplace = loadFlags & nsIChannel::LOAD_REPLACE;
  12834      rv = httpChannel->GetReferrerInfo(getter_AddRefs(referrerInfo));
  12835      MOZ_ASSERT(NS_SUCCEEDED(rv));
  12836 
  12837      discardLayoutState = ShouldDiscardLayoutState(httpChannel);
  12838    }
  12839 
  12840    nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
  12841    if (!triggeringPrincipal) {
  12842      triggeringPrincipal = loadInfo->TriggeringPrincipal();
  12843    }
  12844    if (!policyContainer) {
  12845      policyContainer = loadInfo->GetPolicyContainerToInherit();
  12846    }
  12847 
  12848    loadInfo->GetResultPrincipalURI(getter_AddRefs(resultPrincipalURI));
  12849 
  12850    loadInfo->GetUnstrippedURI(getter_AddRefs(unstrippedURI));
  12851 
  12852    userActivation = loadInfo->GetHasValidUserGestureActivation();
  12853 
  12854    // For now keep storing just the principal in the SHEntry.
  12855    if (!principalToInherit) {
  12856      if (loadInfo->GetLoadingSandboxed()) {
  12857        if (loadInfo->GetLoadingPrincipal()) {
  12858          principalToInherit = NullPrincipal::CreateWithInheritedAttributes(
  12859              loadInfo->GetLoadingPrincipal());
  12860        } else {
  12861          // get the OriginAttributes
  12862          OriginAttributes attrs;
  12863          loadInfo->GetOriginAttributes(&attrs);
  12864          principalToInherit = NullPrincipal::Create(attrs);
  12865        }
  12866      } else {
  12867        principalToInherit = loadInfo->PrincipalToInherit();
  12868      }
  12869    }
  12870 
  12871    if (!partitionedPrincipalToInherit) {
  12872      // XXXehsan is it correct to fall back to the principal to inherit in all
  12873      // cases?  For example, what about the cases where we are using the load
  12874      // info's principal to inherit?  Do we need to add a similar concept to
  12875      // load info for partitioned principal?
  12876      partitionedPrincipalToInherit = principalToInherit;
  12877    }
  12878  }
  12879 
  12880  nsAutoString srcdoc;
  12881  bool srcdocEntry = false;
  12882  nsCOMPtr<nsIURI> baseURI;
  12883 
  12884  nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(aChannel);
  12885  if (inStrmChan) {
  12886    bool isSrcdocChannel;
  12887    inStrmChan->GetIsSrcdocChannel(&isSrcdocChannel);
  12888    if (isSrcdocChannel) {
  12889      inStrmChan->GetSrcdocData(srcdoc);
  12890      srcdocEntry = true;
  12891      inStrmChan->GetBaseURI(getter_AddRefs(baseURI));
  12892    } else {
  12893      srcdoc.SetIsVoid(true);
  12894    }
  12895  }
  12896  /* If cache got a 'no-store', ask SH not to store
  12897   * HistoryLayoutState. By default, SH will set this
  12898   * flag to true and save HistoryLayoutState.
  12899   */
  12900  bool saveLayoutState = !discardLayoutState;
  12901 
  12902  if (cacheChannel) {
  12903    // Check if the page has expired from cache
  12904    uint32_t expTime = 0;
  12905    cacheChannel->GetCacheTokenExpirationTime(&expTime);
  12906    uint32_t now = PRTimeToSeconds(PR_Now());
  12907    if (expTime <= now) {
  12908      expired = true;
  12909    }
  12910  }
  12911 
  12912  // Title is set in nsDocShell::SetTitle()
  12913  entry->Create(aURI,                 // uri
  12914                u""_ns,               // Title
  12915                inputStream,          // Post data stream
  12916                cacheKey,             // CacheKey
  12917                mContentTypeHint,     // Content-type
  12918                triggeringPrincipal,  // Channel or provided principal
  12919                principalToInherit, partitionedPrincipalToInherit,
  12920                policyContainer, HistoryID(), GetCreatedDynamically(),
  12921                originalURI, resultPrincipalURI, unstrippedURI, loadReplace,
  12922                referrerInfo, srcdoc, srcdocEntry, baseURI, saveLayoutState,
  12923                expired, userActivation);
  12924 
  12925  if (mBrowsingContext->IsTop() && GetSessionHistory()) {
  12926    Maybe<int32_t> previousEntryIndex;
  12927    Maybe<int32_t> loadedEntryIndex;
  12928 
  12929    if (mBrowsingContext->IsTop() &&
  12930        !ShouldAddToSessionHistory(aURI, aChannel)) {
  12931      entry->SetTransient();
  12932    }
  12933    rv = GetSessionHistory()->LegacySHistory()->AddToRootSessionHistory(
  12934        aCloneChildren, mOSHE, mBrowsingContext, entry, mLoadType,
  12935        &previousEntryIndex, &loadedEntryIndex);
  12936 
  12937    MOZ_ASSERT(NS_SUCCEEDED(rv), "Could not add entry to root session history");
  12938    if (previousEntryIndex.isSome()) {
  12939      mPreviousEntryIndex = previousEntryIndex.value();
  12940    }
  12941    if (loadedEntryIndex.isSome()) {
  12942      mLoadedEntryIndex = loadedEntryIndex.value();
  12943    }
  12944 
  12945    // aCloneChildren implies that we are retaining the same document, thus we
  12946    // need to signal to the top WC that the new SHEntry may receive a fresh
  12947    // user interaction flag.
  12948    if (aCloneChildren) {
  12949      WindowContext* topWc = mBrowsingContext->GetTopWindowContext();
  12950      if (topWc && !topWc->IsDiscarded()) {
  12951        MOZ_ALWAYS_SUCCEEDS(topWc->SetSHEntryHasUserInteraction(false));
  12952      }
  12953    }
  12954  } else {
  12955    // This is a subframe, make sure that this new SHEntry will be
  12956    // marked with user interaction.
  12957    WindowContext* topWc = mBrowsingContext->GetTopWindowContext();
  12958    if (topWc && !topWc->IsDiscarded()) {
  12959      MOZ_ALWAYS_SUCCEEDS(topWc->SetSHEntryHasUserInteraction(false));
  12960    }
  12961    if (!mOSHE || !LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY)) {
  12962      rv = AddChildSHEntryToParent(entry, mBrowsingContext->ChildOffset(),
  12963                                   aCloneChildren);
  12964    }
  12965  }
  12966 
  12967  // Return the new SH entry...
  12968  if (aNewEntry) {
  12969    *aNewEntry = nullptr;
  12970    if (NS_SUCCEEDED(rv)) {
  12971      entry.forget(aNewEntry);
  12972    }
  12973  }
  12974 
  12975  return rv;
  12976 }
  12977 
  12978 void nsDocShell::UpdateActiveEntry(
  12979    bool aReplace, const Maybe<nsPoint>& aPreviousScrollPos, nsIURI* aURI,
  12980    nsIURI* aOriginalURI, nsIReferrerInfo* aReferrerInfo,
  12981    nsIPrincipal* aTriggeringPrincipal, nsIPolicyContainer* aPolicyContainer,
  12982    const nsAString& aTitle, bool aScrollRestorationIsManual,
  12983    nsIStructuredCloneContainer* aData, bool aURIWasModified) {
  12984  MOZ_ASSERT(mozilla::SessionHistoryInParent());
  12985  MOZ_ASSERT(aURI, "uri is null");
  12986  MOZ_ASSERT(mLoadType == LOAD_PUSHSTATE,
  12987             "This code only deals with pushState");
  12988  MOZ_ASSERT_IF(aPreviousScrollPos.isSome(), !aReplace);
  12989 
  12990  MOZ_LOG(gSHLog, LogLevel::Debug,
  12991          ("Creating an active entry on nsDocShell %p to %s", this,
  12992           aURI->GetSpecOrDefault().get()));
  12993 
  12994  // Even if we're replacing an existing entry we create new a
  12995  // SessionHistoryInfo. In the parent process we'll keep the existing
  12996  // SessionHistoryEntry, but just replace its SessionHistoryInfo, that way the
  12997  // entry keeps identity but its data is replaced.
  12998  bool replace = aReplace && mActiveEntry;
  12999 
  13000  if (!replace) {
  13001    CollectWireframe();
  13002  }
  13003 
  13004  UniquePtr<SessionHistoryInfo> previousActiveEntry(mActiveEntry.release());
  13005  if (previousActiveEntry) {
  13006    // Link this entry to the previous active entry.
  13007    mActiveEntry = MakeUnique<SessionHistoryInfo>(*previousActiveEntry, aURI);
  13008  } else {
  13009    mActiveEntry = MakeUnique<SessionHistoryInfo>(
  13010        aURI, aTriggeringPrincipal, nullptr, nullptr, aPolicyContainer,
  13011        mContentTypeHint);
  13012  }
  13013  mActiveEntry->SetOriginalURI(aOriginalURI);
  13014  mActiveEntry->SetUnstrippedURI(nullptr);
  13015  mActiveEntry->SetReferrerInfo(aReferrerInfo);
  13016  mActiveEntry->SetTitle(aTitle);
  13017  mActiveEntry->SetStateData(static_cast<nsStructuredCloneContainer*>(aData));
  13018  mActiveEntry->SetURIWasModified(aURIWasModified);
  13019  mActiveEntry->SetScrollRestorationIsManual(aScrollRestorationIsManual);
  13020 
  13021  if (replace) {
  13022    mActiveEntry->NavigationKey() = previousActiveEntry->NavigationKey();
  13023    mBrowsingContext->ReplaceActiveSessionHistoryEntry(mActiveEntry.get());
  13024  } else {
  13025    mBrowsingContext->IncrementHistoryEntryCountForBrowsingContext();
  13026    // FIXME We should probably just compute mChildOffset in the parent
  13027    //       instead of passing it over IPC here.
  13028    mBrowsingContext->SetActiveSessionHistoryEntry(
  13029        aPreviousScrollPos, mActiveEntry.get(), previousActiveEntry.get(),
  13030        mLoadType,
  13031        /* aCacheKey = */ 0);
  13032    // FIXME Do we need to update mPreviousEntryIndex and mLoadedEntryIndex?
  13033  }
  13034 }
  13035 
  13036 nsresult nsDocShell::LoadHistoryEntry(nsISHEntry* aEntry, uint32_t aLoadType,
  13037                                      bool aUserActivation) {
  13038  NS_ENSURE_TRUE(aEntry, NS_ERROR_FAILURE);
  13039 
  13040  nsresult rv;
  13041  RefPtr<nsDocShellLoadState> loadState;
  13042  rv = aEntry->CreateLoadInfo(getter_AddRefs(loadState));
  13043  NS_ENSURE_SUCCESS(rv, rv);
  13044 
  13045  // Calling CreateAboutBlankDocumentViewer can set mOSHE to null, and if
  13046  // that's the only thing holding a ref to aEntry that will cause aEntry to
  13047  // die while we're loading it.  So hold a strong ref to aEntry here, just
  13048  // in case.
  13049  nsCOMPtr<nsISHEntry> kungFuDeathGrip(aEntry);
  13050 
  13051  loadState->SetHasValidUserGestureActivation(
  13052      loadState->HasValidUserGestureActivation() || aUserActivation);
  13053 
  13054  loadState->SetTextDirectiveUserActivation(
  13055      loadState->GetTextDirectiveUserActivation() || aUserActivation);
  13056 
  13057  return LoadHistoryEntry(loadState, aLoadType, aEntry == mOSHE);
  13058 }
  13059 
  13060 nsresult nsDocShell::LoadHistoryEntry(const LoadingSessionHistoryInfo& aEntry,
  13061                                      uint32_t aLoadType, bool aUserActivation,
  13062                                      bool aNotifiedBeforeUnloadListeners) {
  13063  RefPtr<nsDocShellLoadState> loadState = aEntry.CreateLoadInfo();
  13064  loadState->SetHasValidUserGestureActivation(
  13065      loadState->HasValidUserGestureActivation() || aUserActivation);
  13066 
  13067  loadState->SetTextDirectiveUserActivation(
  13068      loadState->GetTextDirectiveUserActivation() || aUserActivation);
  13069 
  13070  loadState->SetNotifiedBeforeUnloadListeners(aNotifiedBeforeUnloadListeners);
  13071 
  13072  return LoadHistoryEntry(loadState, aLoadType, aEntry.mLoadingCurrentEntry);
  13073 }
  13074 
  13075 void nsDocShell::MaybeFireTraverseHistory(nsDocShellLoadState* aLoadState) {
  13076  if (!Navigation::IsAPIEnabled()) {
  13077    return;
  13078  }
  13079 
  13080  BrowsingContext* browsingContext = GetBrowsingContext();
  13081  if (!browsingContext || browsingContext->IsTop()) {
  13082    return;
  13083  }
  13084 
  13085  if (!mActiveEntry || !aLoadState->GetLoadingSessionHistoryInfo()) {
  13086    return;
  13087  }
  13088  if (mActiveEntry->NavigationKey() ==
  13089      aLoadState->GetLoadingSessionHistoryInfo()->mInfo.NavigationKey()) {
  13090    return;
  13091  }
  13092 
  13093  nsCOMPtr activeURI = mActiveEntry->GetURIOrInheritedForAboutBlank();
  13094  nsCOMPtr<nsIURI> loadingURI = aLoadState->GetLoadingSessionHistoryInfo()
  13095                                    ->mInfo.GetURIOrInheritedForAboutBlank();
  13096  if (NS_FAILED(nsContentUtils::GetSecurityManager()->CheckSameOriginURI(
  13097          activeURI, loadingURI,
  13098          /*reportError=*/false,
  13099          /*fromPrivateWindow=*/false))) {
  13100    return;
  13101  }
  13102 
  13103  if (RefPtr window = GetActiveWindow()) {
  13104    if (RefPtr navigation = window->Navigation()) {
  13105      if (AutoJSAPI jsapi; jsapi.Init(window)) {
  13106        // This should send the correct user involvment. See bug 1903552.
  13107        navigation->FireTraverseNavigateEvent(
  13108            jsapi.cx(), aLoadState->GetLoadingSessionHistoryInfo()->mInfo,
  13109            Nothing());
  13110      }
  13111    }
  13112  }
  13113 }
  13114 
  13115 nsIDocumentViewer::PermitUnloadResult
  13116 nsDocShell::MaybeFireTraversableTraverseHistory(
  13117    const SessionHistoryInfo& aInfo,
  13118    Maybe<UserNavigationInvolvement> aUserInvolvement) {
  13119  MOZ_DIAGNOSTIC_ASSERT(GetBrowsingContext());
  13120  MOZ_DIAGNOSTIC_ASSERT(GetBrowsingContext()->IsTop());
  13121 
  13122  SetOngoingNavigation(Some(OngoingNavigation::Traversal));
  13123 
  13124  nsIDocumentViewer::PermitUnloadResult finalStatus =
  13125      nsIDocumentViewer::eContinue;
  13126  if (RefPtr<nsPIDOMWindowInner> activeWindow = GetActiveWindow()) {
  13127    if (RefPtr navigation = activeWindow->Navigation()) {
  13128      if (AutoJSAPI jsapi; jsapi.Init(activeWindow)) {
  13129        bool shouldContinue = navigation->FireTraverseNavigateEvent(
  13130            jsapi.cx(), aInfo, aUserInvolvement);
  13131 
  13132        if (!shouldContinue) {
  13133          finalStatus = nsIDocumentViewer::eCanceledByNavigate;
  13134        }
  13135      }
  13136    }
  13137  }
  13138 
  13139  return finalStatus;
  13140 }
  13141 
  13142 nsresult nsDocShell::LoadHistoryEntry(nsDocShellLoadState* aLoadState,
  13143                                      uint32_t aLoadType,
  13144                                      bool aLoadingCurrentEntry) {
  13145  if (!IsNavigationAllowed()) {
  13146    return NS_OK;
  13147  }
  13148 
  13149  // We are setting load type afterwards so we don't have to
  13150  // send it in an IPC message
  13151  aLoadState->SetLoadType(aLoadType);
  13152 
  13153  SetOngoingNavigation(Some(OngoingNavigation::Traversal));
  13154 
  13155  nsresult rv;
  13156  if (aLoadState->URI()->SchemeIs("javascript")) {
  13157    // We're loading a URL that will execute script from inside asyncOpen.
  13158    // Replace the current document with about:blank now to prevent
  13159    // anything from the current document from leaking into any JavaScript
  13160    // code in the URL.
  13161    // Don't cache the presentation if we're going to just reload the
  13162    // current entry. Caching would lead to trying to save the different
  13163    // content viewers in the same nsISHEntry object.
  13164    rv = CreateAboutBlankDocumentViewer(
  13165        aLoadState->PrincipalToInherit(),
  13166        aLoadState->PartitionedPrincipalToInherit(), nullptr, nullptr,
  13167        /* aIsInitialDocument */ false, Nothing(), !aLoadingCurrentEntry);
  13168 
  13169    if (NS_FAILED(rv)) {
  13170      // The creation of the intermittent about:blank content
  13171      // viewer failed for some reason (potentially because the
  13172      // user prevented it). Interrupt the history load.
  13173      return NS_OK;
  13174    }
  13175 
  13176    if (!aLoadState->TriggeringPrincipal()) {
  13177      // Ensure that we have a triggeringPrincipal.  Otherwise javascript:
  13178      // URIs will pick it up from the about:blank page we just loaded,
  13179      // and we don't really want even that in this case.
  13180      nsCOMPtr<nsIPrincipal> principal =
  13181          NullPrincipal::Create(GetOriginAttributes());
  13182      aLoadState->SetTriggeringPrincipal(principal);
  13183    }
  13184  }
  13185 
  13186  /* If there is a valid postdata *and* the user pressed
  13187   * reload or shift-reload, take user's permission before we
  13188   * repost the data to the server.
  13189   */
  13190  if ((aLoadType & LOAD_CMD_RELOAD) && aLoadState->PostDataStream()) {
  13191    bool repost;
  13192    rv = ConfirmRepost(&repost);
  13193    if (NS_FAILED(rv)) {
  13194      return rv;
  13195    }
  13196 
  13197    // If the user pressed cancel in the dialog, return.  We're done here.
  13198    if (!repost) {
  13199      return NS_BINDING_ABORTED;
  13200    }
  13201  }
  13202 
  13203  // If there is no valid triggeringPrincipal, we deny the load
  13204  MOZ_ASSERT(aLoadState->TriggeringPrincipal(),
  13205             "need a valid triggeringPrincipal to load from history");
  13206  if (!aLoadState->TriggeringPrincipal()) {
  13207    return NS_ERROR_FAILURE;
  13208  }
  13209 
  13210  MaybeFireTraverseHistory(aLoadState);
  13211 
  13212  return InternalLoad(aLoadState);  // No nsIRequest
  13213 }
  13214 
  13215 NS_IMETHODIMP
  13216 nsDocShell::PersistLayoutHistoryState() {
  13217  nsresult rv = NS_OK;
  13218 
  13219  if (mozilla::SessionHistoryInParent() ? !!mActiveEntry : !!mOSHE) {
  13220    bool scrollRestorationIsManual;
  13221    if (mozilla::SessionHistoryInParent()) {
  13222      scrollRestorationIsManual = mActiveEntry->GetScrollRestorationIsManual();
  13223    } else {
  13224      scrollRestorationIsManual = mOSHE->GetScrollRestorationIsManual();
  13225    }
  13226    nsCOMPtr<nsILayoutHistoryState> layoutState;
  13227    if (RefPtr<PresShell> presShell = GetPresShell()) {
  13228      rv = presShell->CaptureHistoryState(getter_AddRefs(layoutState));
  13229    } else if (scrollRestorationIsManual) {
  13230      // Even if we don't have layout anymore, we may want to reset the
  13231      // current scroll state in layout history.
  13232      GetLayoutHistoryState(getter_AddRefs(layoutState));
  13233    }
  13234 
  13235    if (scrollRestorationIsManual && layoutState) {
  13236      layoutState->ResetScrollState();
  13237    }
  13238  }
  13239 
  13240  return rv;
  13241 }
  13242 
  13243 void nsDocShell::SwapHistoryEntries(nsISHEntry* aOldEntry,
  13244                                    nsISHEntry* aNewEntry) {
  13245  if (aOldEntry == mOSHE) {
  13246    mOSHE = aNewEntry;
  13247  }
  13248 
  13249  if (aOldEntry == mLSHE) {
  13250    mLSHE = aNewEntry;
  13251  }
  13252 }
  13253 
  13254 void nsDocShell::SetHistoryEntryAndUpdateBC(const Maybe<nsISHEntry*>& aLSHE,
  13255                                            const Maybe<nsISHEntry*>& aOSHE) {
  13256  // We want to hold on to the reference in mLSHE before we update it.
  13257  // Otherwise, SetHistoryEntry could release the last reference to
  13258  // the entry while aOSHE is pointing to it.
  13259  nsCOMPtr<nsISHEntry> deathGripOldLSHE;
  13260  if (aLSHE.isSome()) {
  13261    deathGripOldLSHE = SetHistoryEntry(&mLSHE, aLSHE.value());
  13262    MOZ_ASSERT(mLSHE.get() == aLSHE.value());
  13263  }
  13264  nsCOMPtr<nsISHEntry> deathGripOldOSHE;
  13265  if (aOSHE.isSome()) {
  13266    deathGripOldOSHE = SetHistoryEntry(&mOSHE, aOSHE.value());
  13267    MOZ_ASSERT(mOSHE.get() == aOSHE.value());
  13268  }
  13269 }
  13270 
  13271 already_AddRefed<nsISHEntry> nsDocShell::SetHistoryEntry(
  13272    nsCOMPtr<nsISHEntry>* aPtr, nsISHEntry* aEntry) {
  13273  // We need to sync up the docshell and session history trees for
  13274  // subframe navigation.  If the load was in a subframe, we forward up to
  13275  // the root docshell, which will then recursively sync up all docshells
  13276  // to their corresponding entries in the new session history tree.
  13277  // If we don't do this, then we can cache a content viewer on the wrong
  13278  // cloned entry, and subsequently restore it at the wrong time.
  13279  RefPtr<BrowsingContext> topBC = mBrowsingContext->Top();
  13280  if (topBC->IsDiscarded()) {
  13281    topBC = nullptr;
  13282  }
  13283  RefPtr<BrowsingContext> currBC =
  13284      mBrowsingContext->IsDiscarded() ? nullptr : mBrowsingContext;
  13285  if (topBC && *aPtr) {
  13286    (*aPtr)->SyncTreesForSubframeNavigation(aEntry, topBC, currBC);
  13287  }
  13288  nsCOMPtr<nsISHEntry> entry(aEntry);
  13289  entry.swap(*aPtr);
  13290  return entry.forget();
  13291 }
  13292 
  13293 already_AddRefed<ChildSHistory> nsDocShell::GetRootSessionHistory() {
  13294  RefPtr<ChildSHistory> childSHistory =
  13295      mBrowsingContext->Top()->GetChildSessionHistory();
  13296  return childSHistory.forget();
  13297 }
  13298 
  13299 nsresult nsDocShell::GetHttpChannel(nsIChannel* aChannel,
  13300                                    nsIHttpChannel** aReturn) {
  13301  NS_ENSURE_ARG_POINTER(aReturn);
  13302  if (!aChannel) {
  13303    return NS_ERROR_FAILURE;
  13304  }
  13305 
  13306  nsCOMPtr<nsIMultiPartChannel> multiPartChannel(do_QueryInterface(aChannel));
  13307  if (multiPartChannel) {
  13308    nsCOMPtr<nsIChannel> baseChannel;
  13309    multiPartChannel->GetBaseChannel(getter_AddRefs(baseChannel));
  13310    nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(baseChannel));
  13311    *aReturn = httpChannel;
  13312    NS_IF_ADDREF(*aReturn);
  13313  }
  13314  return NS_OK;
  13315 }
  13316 
  13317 bool nsDocShell::ShouldDiscardLayoutState(nsIHttpChannel* aChannel) {
  13318  // By default layout State will be saved.
  13319  if (!aChannel) {
  13320    return false;
  13321  }
  13322 
  13323  // figure out if SH should be saving layout state
  13324  bool noStore = false;
  13325  (void)aChannel->IsNoStoreResponse(&noStore);
  13326  return noStore;
  13327 }
  13328 
  13329 NS_IMETHODIMP
  13330 nsDocShell::GetEditor(nsIEditor** aEditor) {
  13331  NS_ENSURE_ARG_POINTER(aEditor);
  13332  RefPtr<HTMLEditor> htmlEditor = GetHTMLEditorInternal();
  13333  htmlEditor.forget(aEditor);
  13334  return NS_OK;
  13335 }
  13336 
  13337 NS_IMETHODIMP
  13338 nsDocShell::SetEditor(nsIEditor* aEditor) {
  13339  HTMLEditor* htmlEditor = aEditor ? aEditor->GetAsHTMLEditor() : nullptr;
  13340  // If TextEditor comes, throw an error.
  13341  if (aEditor && !htmlEditor) {
  13342    return NS_ERROR_INVALID_ARG;
  13343  }
  13344  return SetHTMLEditorInternal(htmlEditor);
  13345 }
  13346 
  13347 HTMLEditor* nsDocShell::GetHTMLEditorInternal() {
  13348  return mEditorData ? mEditorData->GetHTMLEditor() : nullptr;
  13349 }
  13350 
  13351 nsresult nsDocShell::SetHTMLEditorInternal(HTMLEditor* aHTMLEditor) {
  13352  if (!aHTMLEditor && !mEditorData) {
  13353    return NS_OK;
  13354  }
  13355 
  13356  nsresult rv = EnsureEditorData();
  13357  if (NS_FAILED(rv)) {
  13358    return rv;
  13359  }
  13360 
  13361  return mEditorData->SetHTMLEditor(aHTMLEditor);
  13362 }
  13363 
  13364 NS_IMETHODIMP
  13365 nsDocShell::GetEditable(bool* aEditable) {
  13366  NS_ENSURE_ARG_POINTER(aEditable);
  13367  *aEditable = mEditorData && mEditorData->GetEditable();
  13368  return NS_OK;
  13369 }
  13370 
  13371 NS_IMETHODIMP
  13372 nsDocShell::GetHasEditingSession(bool* aHasEditingSession) {
  13373  NS_ENSURE_ARG_POINTER(aHasEditingSession);
  13374 
  13375  if (mEditorData) {
  13376    *aHasEditingSession = !!mEditorData->GetEditingSession();
  13377  } else {
  13378    *aHasEditingSession = false;
  13379  }
  13380 
  13381  return NS_OK;
  13382 }
  13383 
  13384 NS_IMETHODIMP
  13385 nsDocShell::MakeEditable(bool aInWaitForUriLoad) {
  13386  nsresult rv = EnsureEditorData();
  13387  if (NS_FAILED(rv)) {
  13388    return rv;
  13389  }
  13390 
  13391  return mEditorData->MakeEditable(aInWaitForUriLoad);
  13392 }
  13393 
  13394 /* static */ bool nsDocShell::ShouldAddURIVisit(nsIChannel* aChannel) {
  13395  bool needToAddURIVisit = true;
  13396  nsCOMPtr<nsIPropertyBag2> props(do_QueryInterface(aChannel));
  13397  if (props) {
  13398    (void)props->GetPropertyAsBool(u"docshell.needToAddURIVisit"_ns,
  13399                                   &needToAddURIVisit);
  13400  }
  13401 
  13402  return needToAddURIVisit;
  13403 }
  13404 
  13405 /* static */ void nsDocShell::ExtractLastVisit(
  13406    nsIChannel* aChannel, nsIURI** aURI, uint32_t* aChannelRedirectFlags) {
  13407  nsCOMPtr<nsIPropertyBag2> props(do_QueryInterface(aChannel));
  13408  if (!props) {
  13409    return;
  13410  }
  13411 
  13412  nsresult rv;
  13413  nsCOMPtr<nsIURI> uri(do_GetProperty(props, u"docshell.previousURI"_ns, &rv));
  13414  if (NS_SUCCEEDED(rv)) {
  13415    uri.forget(aURI);
  13416 
  13417    rv = props->GetPropertyAsUint32(u"docshell.previousFlags"_ns,
  13418                                    aChannelRedirectFlags);
  13419 
  13420    NS_WARNING_ASSERTION(
  13421        NS_SUCCEEDED(rv),
  13422        "Could not fetch previous flags, URI will be treated like referrer");
  13423 
  13424  } else {
  13425    // There is no last visit for this channel, so this must be the first
  13426    // link.  Link the visit to the referrer of this request, if any.
  13427    // Treat referrer as null if there is an error getting it.
  13428    NS_GetReferrerFromChannel(aChannel, aURI);
  13429  }
  13430 }
  13431 
  13432 void nsDocShell::SaveLastVisit(nsIChannel* aChannel, nsIURI* aURI,
  13433                               uint32_t aChannelRedirectFlags) {
  13434  nsCOMPtr<nsIWritablePropertyBag2> props(do_QueryInterface(aChannel));
  13435  if (!props || !aURI) {
  13436    return;
  13437  }
  13438 
  13439  props->SetPropertyAsInterface(u"docshell.previousURI"_ns, aURI);
  13440  props->SetPropertyAsUint32(u"docshell.previousFlags"_ns,
  13441                             aChannelRedirectFlags);
  13442 }
  13443 
  13444 /* static */ void nsDocShell::InternalAddURIVisit(
  13445    nsIURI* aURI, nsIURI* aPreviousURI, uint32_t aChannelRedirectFlags,
  13446    uint32_t aResponseStatus, BrowsingContext* aBrowsingContext,
  13447    nsIWidget* aWidget, uint32_t aLoadType, bool aWasUpgraded) {
  13448  MOZ_ASSERT(aURI, "Visited URI is null!");
  13449  MOZ_ASSERT(aLoadType != LOAD_ERROR_PAGE && aLoadType != LOAD_BYPASS_HISTORY,
  13450             "Do not add error or bypass pages to global history");
  13451 
  13452  bool usePrivateBrowsing = false;
  13453  aBrowsingContext->GetUsePrivateBrowsing(&usePrivateBrowsing);
  13454 
  13455  // Only content-type docshells save URI visits.  Also don't do
  13456  // anything here if we're not supposed to use global history.
  13457  if (!aBrowsingContext->IsContent() ||
  13458      !aBrowsingContext->GetUseGlobalHistory() || usePrivateBrowsing) {
  13459    return;
  13460  }
  13461 
  13462  nsCOMPtr<IHistory> history = components::History::Service();
  13463 
  13464  if (history) {
  13465    uint32_t visitURIFlags = 0;
  13466 
  13467    if (aBrowsingContext->IsTop()) {
  13468      visitURIFlags |= IHistory::TOP_LEVEL;
  13469    }
  13470 
  13471    if (aChannelRedirectFlags & nsIChannelEventSink::REDIRECT_TEMPORARY) {
  13472      visitURIFlags |= IHistory::REDIRECT_TEMPORARY;
  13473    } else if (aChannelRedirectFlags &
  13474               nsIChannelEventSink::REDIRECT_PERMANENT) {
  13475      visitURIFlags |= IHistory::REDIRECT_PERMANENT;
  13476    } else {
  13477      MOZ_ASSERT(!aChannelRedirectFlags,
  13478                 "One of REDIRECT_TEMPORARY or REDIRECT_PERMANENT must be set "
  13479                 "if any flags in aChannelRedirectFlags is set.");
  13480    }
  13481 
  13482    if (aResponseStatus >= 300 && aResponseStatus < 400) {
  13483      visitURIFlags |= IHistory::REDIRECT_SOURCE;
  13484      if (aResponseStatus == 301 || aResponseStatus == 308) {
  13485        visitURIFlags |= IHistory::REDIRECT_SOURCE_PERMANENT;
  13486      }
  13487    }
  13488    // Errors 400-501 and 505 are considered unrecoverable, in the sense a
  13489    // simple retry attempt by the user is unlikely to solve them.
  13490    // 408 is special cased, since may actually indicate a temporary
  13491    // connection problem.
  13492    else if (aResponseStatus != 408 &&
  13493             ((aResponseStatus >= 400 && aResponseStatus <= 501) ||
  13494              aResponseStatus == 505)) {
  13495      visitURIFlags |= IHistory::UNRECOVERABLE_ERROR;
  13496    }
  13497 
  13498    if (aWasUpgraded) {
  13499      visitURIFlags |=
  13500          IHistory::REDIRECT_SOURCE | IHistory::REDIRECT_SOURCE_UPGRADED;
  13501    }
  13502 
  13503    (void)history->VisitURI(aWidget, aURI, aPreviousURI, visitURIFlags,
  13504                            aBrowsingContext->BrowserId());
  13505  }
  13506 }
  13507 
  13508 void nsDocShell::AddURIVisit(nsIURI* aURI, nsIURI* aPreviousURI,
  13509                             uint32_t aChannelRedirectFlags,
  13510                             uint32_t aResponseStatus) {
  13511  nsPIDOMWindowOuter* outer = GetWindow();
  13512  nsCOMPtr<nsIWidget> widget = widget::WidgetUtils::DOMWindowToWidget(outer);
  13513 
  13514  InternalAddURIVisit(aURI, aPreviousURI, aChannelRedirectFlags,
  13515                      aResponseStatus, mBrowsingContext, widget, mLoadType,
  13516                      false);
  13517 }
  13518 
  13519 //*****************************************************************************
  13520 // nsDocShell: Helper Routines
  13521 //*****************************************************************************
  13522 
  13523 NS_IMETHODIMP
  13524 nsDocShell::SetLoadType(uint32_t aLoadType) {
  13525  mLoadType = aLoadType;
  13526  return NS_OK;
  13527 }
  13528 
  13529 NS_IMETHODIMP
  13530 nsDocShell::GetLoadType(uint32_t* aLoadType) {
  13531  *aLoadType = mLoadType;
  13532  return NS_OK;
  13533 }
  13534 
  13535 nsresult nsDocShell::ConfirmRepost(bool* aRepost) {
  13536  if (StaticPrefs::dom_confirm_repost_testing_always_accept()) {
  13537    *aRepost = true;
  13538    return NS_OK;
  13539  }
  13540 
  13541  nsCOMPtr<nsIPromptCollection> prompter =
  13542      do_GetService("@mozilla.org/embedcomp/prompt-collection;1");
  13543  if (!prompter) {
  13544    return NS_ERROR_NOT_AVAILABLE;
  13545  }
  13546 
  13547  return prompter->ConfirmRepost(mBrowsingContext, aRepost);
  13548 }
  13549 
  13550 nsresult nsDocShell::GetPromptAndStringBundle(nsIPrompt** aPrompt,
  13551                                              nsIStringBundle** aStringBundle) {
  13552  NS_ENSURE_SUCCESS(GetInterface(NS_GET_IID(nsIPrompt), (void**)aPrompt),
  13553                    NS_ERROR_FAILURE);
  13554 
  13555  nsCOMPtr<nsIStringBundleService> stringBundleService =
  13556      mozilla::components::StringBundle::Service();
  13557  NS_ENSURE_TRUE(stringBundleService, NS_ERROR_FAILURE);
  13558 
  13559  NS_ENSURE_SUCCESS(
  13560      stringBundleService->CreateBundle(kAppstringsBundleURL, aStringBundle),
  13561      NS_ERROR_FAILURE);
  13562 
  13563  return NS_OK;
  13564 }
  13565 
  13566 ScrollContainerFrame* nsDocShell::GetRootScrollContainerFrame() {
  13567  PresShell* presShell = GetPresShell();
  13568  NS_ENSURE_TRUE(presShell, nullptr);
  13569 
  13570  return presShell->GetRootScrollContainerFrame();
  13571 }
  13572 
  13573 nsresult nsDocShell::EnsureScriptEnvironment() {
  13574  if (mScriptGlobal) {
  13575    return NS_OK;
  13576  }
  13577 
  13578  if (mIsBeingDestroyed) {
  13579    return NS_ERROR_NOT_AVAILABLE;
  13580  }
  13581 
  13582 #ifdef DEBUG
  13583  NS_ASSERTION(!mInEnsureScriptEnv,
  13584               "Infinite loop! Calling EnsureScriptEnvironment() from "
  13585               "within EnsureScriptEnvironment()!");
  13586 
  13587  // Yeah, this isn't re-entrant safe, but that's ok since if we
  13588  // re-enter this method, we'll infinitely loop...
  13589  AutoRestore<bool> boolSetter(mInEnsureScriptEnv);
  13590  mInEnsureScriptEnv = true;
  13591 #endif
  13592 
  13593  nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(mTreeOwner));
  13594  NS_ENSURE_TRUE(browserChrome, NS_ERROR_NOT_AVAILABLE);
  13595 
  13596  uint32_t chromeFlags;
  13597  browserChrome->GetChromeFlags(&chromeFlags);
  13598 
  13599  // If our window is modal and we're not opened as chrome, make
  13600  // this window a modal content window.
  13601  mScriptGlobal = nsGlobalWindowOuter::Create(this, mItemType == typeChrome);
  13602  MOZ_ASSERT(mScriptGlobal);
  13603 
  13604  // Ensure the script object is set up to run script.
  13605  return mScriptGlobal->EnsureScriptEnvironment();
  13606 }
  13607 
  13608 nsresult nsDocShell::EnsureEditorData() {
  13609  MOZ_ASSERT(!mIsBeingDestroyed);
  13610 
  13611  bool openDocHasDetachedEditor = mOSHE && mOSHE->HasDetachedEditor();
  13612  if (!mEditorData && !mIsBeingDestroyed && !openDocHasDetachedEditor) {
  13613    // We shouldn't recreate the editor data if it already exists, or
  13614    // we're shutting down, or we already have a detached editor data
  13615    // stored in the session history. We should only have one editordata
  13616    // per docshell.
  13617    mEditorData = MakeUnique<nsDocShellEditorData>(this);
  13618  }
  13619 
  13620  return mEditorData ? NS_OK : NS_ERROR_NOT_AVAILABLE;
  13621 }
  13622 
  13623 nsresult nsDocShell::EnsureFind() {
  13624  if (!mFind) {
  13625    mFind = new nsWebBrowserFind();
  13626  }
  13627 
  13628  // we promise that the nsIWebBrowserFind that we return has been set
  13629  // up to point to the focused, or content window, so we have to
  13630  // set that up each time.
  13631 
  13632  nsIScriptGlobalObject* scriptGO = GetScriptGlobalObject();
  13633  NS_ENSURE_TRUE(scriptGO, NS_ERROR_UNEXPECTED);
  13634 
  13635  // default to our window
  13636  nsCOMPtr<nsPIDOMWindowOuter> ourWindow = do_QueryInterface(scriptGO);
  13637  nsCOMPtr<nsPIDOMWindowOuter> windowToSearch;
  13638  nsFocusManager::GetFocusedDescendant(ourWindow,
  13639                                       nsFocusManager::eIncludeAllDescendants,
  13640                                       getter_AddRefs(windowToSearch));
  13641 
  13642  nsCOMPtr<nsIWebBrowserFindInFrames> findInFrames = do_QueryInterface(mFind);
  13643  if (!findInFrames) {
  13644    return NS_ERROR_NO_INTERFACE;
  13645  }
  13646 
  13647  nsresult rv = findInFrames->SetRootSearchFrame(ourWindow);
  13648  if (NS_FAILED(rv)) {
  13649    return rv;
  13650  }
  13651  rv = findInFrames->SetCurrentSearchFrame(windowToSearch);
  13652  if (NS_FAILED(rv)) {
  13653    return rv;
  13654  }
  13655 
  13656  return NS_OK;
  13657 }
  13658 
  13659 NS_IMETHODIMP
  13660 nsDocShell::IsBeingDestroyed(bool* aDoomed) {
  13661  NS_ENSURE_ARG(aDoomed);
  13662  *aDoomed = mIsBeingDestroyed;
  13663  return NS_OK;
  13664 }
  13665 
  13666 NS_IMETHODIMP
  13667 nsDocShell::GetIsExecutingOnLoadHandler(bool* aResult) {
  13668  NS_ENSURE_ARG(aResult);
  13669  *aResult = mIsExecutingOnLoadHandler;
  13670  return NS_OK;
  13671 }
  13672 
  13673 NS_IMETHODIMP
  13674 nsDocShell::GetLayoutHistoryState(nsILayoutHistoryState** aLayoutHistoryState) {
  13675  nsCOMPtr<nsILayoutHistoryState> state;
  13676  if (mozilla::SessionHistoryInParent()) {
  13677    if (mActiveEntry) {
  13678      state = mActiveEntry->GetLayoutHistoryState();
  13679    }
  13680  } else {
  13681    if (mOSHE) {
  13682      state = mOSHE->GetLayoutHistoryState();
  13683    }
  13684  }
  13685  state.forget(aLayoutHistoryState);
  13686  return NS_OK;
  13687 }
  13688 
  13689 NS_IMETHODIMP
  13690 nsDocShell::SetLayoutHistoryState(nsILayoutHistoryState* aLayoutHistoryState) {
  13691  if (mOSHE) {
  13692    mOSHE->SetLayoutHistoryState(aLayoutHistoryState);
  13693  }
  13694  if (mActiveEntry) {
  13695    mActiveEntry->SetLayoutHistoryState(aLayoutHistoryState);
  13696  }
  13697  return NS_OK;
  13698 }
  13699 
  13700 nsDocShell::InterfaceRequestorProxy::InterfaceRequestorProxy(
  13701    nsIInterfaceRequestor* aRequestor) {
  13702  if (aRequestor) {
  13703    mWeakPtr = do_GetWeakReference(aRequestor);
  13704  }
  13705 }
  13706 
  13707 nsDocShell::InterfaceRequestorProxy::~InterfaceRequestorProxy() {
  13708  mWeakPtr = nullptr;
  13709 }
  13710 
  13711 NS_IMPL_ISUPPORTS(nsDocShell::InterfaceRequestorProxy, nsIInterfaceRequestor)
  13712 
  13713 NS_IMETHODIMP
  13714 nsDocShell::InterfaceRequestorProxy::GetInterface(const nsIID& aIID,
  13715                                                  void** aSink) {
  13716  NS_ENSURE_ARG_POINTER(aSink);
  13717  nsCOMPtr<nsIInterfaceRequestor> ifReq = do_QueryReferent(mWeakPtr);
  13718  if (ifReq) {
  13719    return ifReq->GetInterface(aIID, aSink);
  13720  }
  13721  *aSink = nullptr;
  13722  return NS_NOINTERFACE;
  13723 }
  13724 
  13725 //*****************************************************************************
  13726 // nsDocShell::nsIAuthPromptProvider
  13727 //*****************************************************************************
  13728 
  13729 NS_IMETHODIMP
  13730 nsDocShell::GetAuthPrompt(uint32_t aPromptReason, const nsIID& aIID,
  13731                          void** aResult) {
  13732  // a priority prompt request will override a false mAllowAuth setting
  13733  bool priorityPrompt = (aPromptReason == PROMPT_PROXY);
  13734 
  13735  if (!mAllowAuth && !priorityPrompt) {
  13736    return NS_ERROR_NOT_AVAILABLE;
  13737  }
  13738 
  13739  // we're either allowing auth, or it's a proxy request
  13740  nsresult rv;
  13741  nsCOMPtr<nsIPromptFactory> wwatch =
  13742      do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
  13743  NS_ENSURE_SUCCESS(rv, rv);
  13744 
  13745  rv = EnsureScriptEnvironment();
  13746  NS_ENSURE_SUCCESS(rv, rv);
  13747 
  13748  // Get the an auth prompter for our window so that the parenting
  13749  // of the dialogs works as it should when using tabs.
  13750 
  13751  return wwatch->GetPrompt(mScriptGlobal, aIID,
  13752                           reinterpret_cast<void**>(aResult));
  13753 }
  13754 
  13755 //*****************************************************************************
  13756 // nsDocShell::nsILoadContext
  13757 //*****************************************************************************
  13758 
  13759 NS_IMETHODIMP
  13760 nsDocShell::GetAssociatedWindow(mozIDOMWindowProxy** aWindow) {
  13761  CallGetInterface(this, aWindow);
  13762  return NS_OK;
  13763 }
  13764 
  13765 NS_IMETHODIMP
  13766 nsDocShell::GetTopWindow(mozIDOMWindowProxy** aWindow) {
  13767  return mBrowsingContext->GetTopWindow(aWindow);
  13768 }
  13769 
  13770 NS_IMETHODIMP
  13771 nsDocShell::GetTopFrameElement(Element** aElement) {
  13772  return mBrowsingContext->GetTopFrameElement(aElement);
  13773 }
  13774 
  13775 NS_IMETHODIMP
  13776 nsDocShell::GetUseTrackingProtection(bool* aUseTrackingProtection) {
  13777  return mBrowsingContext->GetUseTrackingProtection(aUseTrackingProtection);
  13778 }
  13779 
  13780 NS_IMETHODIMP
  13781 nsDocShell::SetUseTrackingProtection(bool aUseTrackingProtection) {
  13782  return mBrowsingContext->SetUseTrackingProtection(aUseTrackingProtection);
  13783 }
  13784 
  13785 NS_IMETHODIMP
  13786 nsDocShell::GetIsContent(bool* aIsContent) {
  13787  *aIsContent = (mItemType == typeContent);
  13788  return NS_OK;
  13789 }
  13790 
  13791 bool nsDocShell::IsOKToLoadURI(nsIURI* aURI) {
  13792  MOZ_ASSERT(aURI, "Must have a URI!");
  13793 
  13794  if (!mFiredUnloadEvent) {
  13795    return true;
  13796  }
  13797 
  13798  if (!mLoadingURI) {
  13799    return false;
  13800  }
  13801 
  13802  bool isPrivateWin = false;
  13803  Document* doc = GetDocument();
  13804  if (doc) {
  13805    isPrivateWin =
  13806        doc->NodePrincipal()->OriginAttributesRef().IsPrivateBrowsing();
  13807  }
  13808 
  13809  nsCOMPtr<nsIScriptSecurityManager> secMan =
  13810      do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
  13811  return secMan && NS_SUCCEEDED(secMan->CheckSameOriginURI(
  13812                       aURI, mLoadingURI, false, isPrivateWin));
  13813 }
  13814 
  13815 //
  13816 // Routines for selection and clipboard
  13817 //
  13818 nsresult nsDocShell::GetControllerForCommand(const char* aCommand,
  13819                                             nsIController** aResult) {
  13820  NS_ENSURE_ARG_POINTER(aResult);
  13821  *aResult = nullptr;
  13822 
  13823  NS_ENSURE_TRUE(mScriptGlobal, NS_ERROR_FAILURE);
  13824 
  13825  nsCOMPtr<nsPIWindowRoot> root = mScriptGlobal->GetTopWindowRoot();
  13826  NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
  13827 
  13828  return root->GetControllerForCommand(aCommand, false /* for any window */,
  13829                                       aResult);
  13830 }
  13831 
  13832 NS_IMETHODIMP
  13833 nsDocShell::IsCommandEnabled(const char* aCommand, bool* aResult) {
  13834  NS_ENSURE_ARG_POINTER(aResult);
  13835  *aResult = false;
  13836 
  13837  nsresult rv = NS_ERROR_FAILURE;
  13838 
  13839  nsCOMPtr<nsIController> controller;
  13840  rv = GetControllerForCommand(aCommand, getter_AddRefs(controller));
  13841  if (controller) {
  13842    rv = controller->IsCommandEnabled(aCommand, aResult);
  13843  }
  13844 
  13845  return rv;
  13846 }
  13847 
  13848 NS_IMETHODIMP
  13849 nsDocShell::DoCommand(const char* aCommand) {
  13850  nsresult rv = NS_ERROR_FAILURE;
  13851 
  13852  nsCOMPtr<nsIController> controller;
  13853  rv = GetControllerForCommand(aCommand, getter_AddRefs(controller));
  13854  if (controller) {
  13855    rv = controller->DoCommand(aCommand);
  13856  }
  13857 
  13858  return rv;
  13859 }
  13860 
  13861 NS_IMETHODIMP
  13862 nsDocShell::DoCommandWithParams(const char* aCommand,
  13863                                nsICommandParams* aParams) {
  13864  nsCOMPtr<nsIController> controller;
  13865  nsresult rv = GetControllerForCommand(aCommand, getter_AddRefs(controller));
  13866  if (NS_WARN_IF(NS_FAILED(rv))) {
  13867    return rv;
  13868  }
  13869 
  13870  nsCOMPtr<nsICommandController> commandController =
  13871      do_QueryInterface(controller, &rv);
  13872  if (NS_WARN_IF(NS_FAILED(rv))) {
  13873    return rv;
  13874  }
  13875 
  13876  return commandController->DoCommandWithParams(aCommand, aParams);
  13877 }
  13878 
  13879 nsresult nsDocShell::EnsureCommandHandler() {
  13880  if (!mCommandManager) {
  13881    if (nsCOMPtr<nsPIDOMWindowOuter> domWindow = GetWindow()) {
  13882      mCommandManager = new nsCommandManager(domWindow);
  13883    }
  13884  }
  13885  return mCommandManager ? NS_OK : NS_ERROR_FAILURE;
  13886 }
  13887 
  13888 // link handling
  13889 
  13890 class OnLinkClickEvent : public Runnable {
  13891 public:
  13892  OnLinkClickEvent(nsDocShell* aHandler, nsIContent* aContent,
  13893                   nsDocShellLoadState* aLoadState, bool aNoOpenerImplied,
  13894                   nsIPrincipal* aTriggeringPrincipal);
  13895 
  13896  NS_IMETHOD Run() override {
  13897    // We need to set up an AutoJSAPI here for the following reason: When we
  13898    // do OnLinkClickSync we'll eventually end up in
  13899    // nsGlobalWindow::OpenInternal which only does popup blocking if
  13900    // !LegacyIsCallerChromeOrNativeCode(). So we need to fake things so that
  13901    // we don't look like native code as far as LegacyIsCallerNativeCode() is
  13902    // concerned. (Bug 1930445)
  13903    AutoJSAPI jsapi;
  13904    if (jsapi.Init(mContent->OwnerDoc()->GetScopeObject())) {
  13905      mLoadState->SetSourceElement(mContent->AsElement());
  13906      mHandler->OnLinkClickSync(mContent, mLoadState, mNoOpenerImplied,
  13907                                mTriggeringPrincipal);
  13908    }
  13909    return NS_OK;
  13910  }
  13911 
  13912 private:
  13913  RefPtr<nsDocShell> mHandler;
  13914  nsCOMPtr<nsIContent> mContent;
  13915  RefPtr<nsDocShellLoadState> mLoadState;
  13916  nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
  13917  bool mNoOpenerImplied;
  13918 };
  13919 
  13920 OnLinkClickEvent::OnLinkClickEvent(nsDocShell* aHandler, nsIContent* aContent,
  13921                                   nsDocShellLoadState* aLoadState,
  13922                                   bool aNoOpenerImplied,
  13923                                   nsIPrincipal* aTriggeringPrincipal)
  13924    : mozilla::Runnable("OnLinkClickEvent"),
  13925      mHandler(aHandler),
  13926      mContent(aContent),
  13927      mLoadState(aLoadState),
  13928      mTriggeringPrincipal(aTriggeringPrincipal),
  13929      mNoOpenerImplied(aNoOpenerImplied) {}
  13930 
  13931 nsresult nsDocShell::OnLinkClick(
  13932    nsIContent* aContent, nsIURI* aURI, const nsAString& aTargetSpec,
  13933    const nsAString& aFileName, nsIInputStream* aPostDataStream,
  13934    nsIInputStream* aHeadersDataStream, bool aIsUserTriggered,
  13935    UserNavigationInvolvement aUserInvolvement,
  13936    nsIPrincipal* aTriggeringPrincipal, nsIPolicyContainer* aPolicyContainer) {
  13937 #ifndef ANDROID
  13938  MOZ_ASSERT(aTriggeringPrincipal, "Need a valid triggeringPrincipal");
  13939 #endif
  13940  NS_ASSERTION(NS_IsMainThread(), "wrong thread");
  13941 
  13942  if (!IsNavigationAllowed() || !IsOKToLoadURI(aURI)) {
  13943    return NS_OK;
  13944  }
  13945 
  13946  // On history navigation through Back/Forward buttons, don't execute
  13947  // automatic JavaScript redirection such as |anchorElement.click()| or
  13948  // |formElement.submit()|.
  13949  //
  13950  // XXX |formElement.submit()| bypasses this checkpoint because it calls
  13951  //     nsDocShell::OnLinkClickSync(...) instead.
  13952  if (ShouldBlockLoadingForBackButton()) {
  13953    return NS_OK;
  13954  }
  13955 
  13956  if (aContent->IsEditable()) {
  13957    return NS_OK;
  13958  }
  13959 
  13960  Document* ownerDoc = aContent->OwnerDoc();
  13961  if (nsContentUtils::IsExternalProtocol(aURI)) {
  13962    ownerDoc->EnsureNotEnteringAndExitFullscreen();
  13963  }
  13964 
  13965  bool noOpenerImplied = false;
  13966  nsAutoString target(aTargetSpec);
  13967  if (aFileName.IsVoid() &&
  13968      ShouldOpenInBlankTarget(aTargetSpec, aURI, aContent, aIsUserTriggered)) {
  13969    target = u"_blank";
  13970    if (!aTargetSpec.Equals(target)) {
  13971      noOpenerImplied = true;
  13972    }
  13973  }
  13974 
  13975  // https://html.spec.whatwg.org/#downloading-hyperlinks
  13976  // Step 6, step 6.1, step 6.2
  13977  // aFileName not being void implies a download attribute, since we've already
  13978  // checked if the attribute is present in `nsContentUtils::TriggerLinkClick`
  13979  // and made it void otherwise.
  13980  if (!aFileName.IsVoid() &&
  13981      aUserInvolvement != UserNavigationInvolvement::BrowserUI) {
  13982    if (nsCOMPtr<nsPIDOMWindowInner> window = ownerDoc->GetInnerWindow()) {
  13983      if (RefPtr<Navigation> navigation = window->Navigation()) {
  13984        AutoJSAPI jsapi;
  13985        if (jsapi.Init(window)) {
  13986          RefPtr element = aContent->AsElement();
  13987          // Step 6.4
  13988          bool shouldContinue = navigation->FireDownloadRequestNavigateEvent(
  13989              jsapi.cx(), aURI, aUserInvolvement, element, aFileName);
  13990 
  13991          // Step 6.5
  13992          if (!shouldContinue) {
  13993            return NS_OK;
  13994          }
  13995        }
  13996      }
  13997    }
  13998  }
  13999 
  14000  RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(aURI);
  14001  loadState->SetTarget(target);
  14002  loadState->SetFileName(aFileName);
  14003  loadState->SetPostDataStream(aPostDataStream);
  14004  loadState->SetHeadersStream(aHeadersDataStream);
  14005  loadState->SetFirstParty(true);
  14006  loadState->SetTriggeringPrincipal(
  14007      aTriggeringPrincipal ? aTriggeringPrincipal : aContent->NodePrincipal());
  14008  loadState->SetPrincipalToInherit(aContent->NodePrincipal());
  14009  loadState->SetPolicyContainer(
  14010      aPolicyContainer ? aPolicyContainer : aContent->GetPolicyContainer());
  14011  loadState->SetAllowFocusMove(UserActivation::IsHandlingUserInput());
  14012 
  14013  const bool hasValidUserGestureActivation =
  14014      ownerDoc->HasValidTransientUserGestureActivation();
  14015  loadState->SetHasValidUserGestureActivation(hasValidUserGestureActivation);
  14016  loadState->SetTextDirectiveUserActivation(
  14017      ownerDoc->ConsumeTextDirectiveUserActivation() ||
  14018      hasValidUserGestureActivation);
  14019  loadState->SetUserNavigationInvolvement(aUserInvolvement);
  14020  loadState->SetTriggeringClassificationFlags(
  14021      ownerDoc->GetScriptTrackingFlags());
  14022  loadState->SetHistoryBehavior(NavigationHistoryBehavior::Auto);
  14023 
  14024  nsCOMPtr<nsIRunnable> ev = new OnLinkClickEvent(
  14025      this, aContent, loadState, noOpenerImplied, aTriggeringPrincipal);
  14026  return Dispatch(ev.forget());
  14027 }
  14028 
  14029 bool nsDocShell::ShouldOpenInBlankTarget(const nsAString& aOriginalTarget,
  14030                                         nsIURI* aLinkURI, nsIContent* aContent,
  14031                                         bool aIsUserTriggered) {
  14032  if (aLinkURI->SchemeIs("javascript")) {
  14033    return false;
  14034  }
  14035 
  14036  // External links from within app tabs should always open in new tabs
  14037  // instead of replacing the app tab's page (Bug 575561)
  14038  // nsIURI.host can throw for non-nsStandardURL nsIURIs. If we fail to
  14039  // get either host, just return false to use the original target.
  14040  nsAutoCString linkHost;
  14041  if (NS_FAILED(aLinkURI->GetHost(linkHost))) {
  14042    return false;
  14043  }
  14044 
  14045  // The targetTopLevelLinkClicksToBlank property on BrowsingContext allows
  14046  // privileged code to change the default targeting behaviour. In particular,
  14047  // if a user-initiated link click for the (or targetting the) top-level frame
  14048  // is detected, we default the target to "_blank" to give it a new
  14049  // top-level BrowsingContext.
  14050  if (mBrowsingContext->TargetTopLevelLinkClicksToBlank() && aIsUserTriggered &&
  14051      ((aOriginalTarget.IsEmpty() && mBrowsingContext->IsTop()) ||
  14052       aOriginalTarget == u"_top"_ns)) {
  14053    return true;
  14054  }
  14055 
  14056  // Don't modify non-default targets.
  14057  if (!aOriginalTarget.IsEmpty()) {
  14058    return false;
  14059  }
  14060 
  14061  // Only check targets that are in extension panels or app tabs.
  14062  // (isAppTab will be false for app tab subframes).
  14063  nsString mmGroup = mBrowsingContext->Top()->GetMessageManagerGroup();
  14064  if (!mmGroup.EqualsLiteral("webext-browsers") &&
  14065      !mBrowsingContext->IsAppTab()) {
  14066    return false;
  14067  }
  14068 
  14069  nsCOMPtr<nsIURI> docURI = aContent->OwnerDoc()->GetDocumentURIObject();
  14070  if (!docURI) {
  14071    return false;
  14072  }
  14073 
  14074  nsAutoCString docHost;
  14075  if (NS_FAILED(docURI->GetHost(docHost))) {
  14076    return false;
  14077  }
  14078 
  14079  if (linkHost.Equals(docHost)) {
  14080    return false;
  14081  }
  14082 
  14083  // Special case: ignore "www" prefix if it is part of host string
  14084  return linkHost.Length() < docHost.Length()
  14085             ? !docHost.Equals("www."_ns + linkHost)
  14086             : !linkHost.Equals("www."_ns + docHost);
  14087 }
  14088 
  14089 static bool ElementCanHaveNoopener(nsIContent* aContent) {
  14090  // Make sure we are dealing with either an <A>, <AREA>, or <FORM> element in
  14091  // the HTML, XHTML, or SVG namespace.
  14092  return aContent->IsAnyOfHTMLElements(nsGkAtoms::a, nsGkAtoms::area,
  14093                                       nsGkAtoms::form) ||
  14094         aContent->IsSVGElement(nsGkAtoms::a);
  14095 }
  14096 
  14097 nsresult nsDocShell::OnLinkClickSync(nsIContent* aContent,
  14098                                     nsDocShellLoadState* aLoadState,
  14099                                     bool aNoOpenerImplied,
  14100                                     nsIPrincipal* aTriggeringPrincipal) {
  14101  if (!IsNavigationAllowed() || !IsOKToLoadURI(aLoadState->URI())) {
  14102    return NS_OK;
  14103  }
  14104 
  14105  // XXX When the linking node was HTMLFormElement, it is synchronous event.
  14106  //     That is, the caller of this method is not |OnLinkClickEvent::Run()|
  14107  //     but |HTMLFormElement::SubmitSubmission(...)|.
  14108  if (aContent->IsHTMLElement(nsGkAtoms::form) &&
  14109      ShouldBlockLoadingForBackButton()) {
  14110    return NS_OK;
  14111  }
  14112 
  14113  if (aContent->IsEditable()) {
  14114    return NS_OK;
  14115  }
  14116 
  14117  // if the triggeringPrincipal is not passed explicitly, then we
  14118  // fall back to using doc->NodePrincipal() as the triggeringPrincipal.
  14119  nsCOMPtr<nsIPrincipal> triggeringPrincipal =
  14120      aTriggeringPrincipal ? aTriggeringPrincipal : aContent->NodePrincipal();
  14121 
  14122  {
  14123    // defer to an external protocol handler if necessary...
  14124    nsCOMPtr<nsIExternalProtocolService> extProtService =
  14125        do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID);
  14126    if (extProtService) {
  14127      nsAutoCString scheme;
  14128      aLoadState->URI()->GetScheme(scheme);
  14129      if (!scheme.IsEmpty()) {
  14130        // if the URL scheme does not correspond to an exposed protocol, then
  14131        // we need to hand this link click over to the external protocol
  14132        // handler.
  14133        bool isExposed;
  14134        nsresult rv =
  14135            extProtService->IsExposedProtocol(scheme.get(), &isExposed);
  14136        if (NS_SUCCEEDED(rv) && !isExposed) {
  14137          return extProtService->LoadURI(
  14138              aLoadState->URI(), triggeringPrincipal, nullptr, mBrowsingContext,
  14139              /* aTriggeredExternally */
  14140              false,
  14141              /* aHasValidUserGestureActivation */
  14142              aContent->OwnerDoc()->HasValidTransientUserGestureActivation(),
  14143              /* aNewWindowTarget */ false);
  14144        }
  14145      }
  14146    }
  14147  }
  14148  uint32_t triggeringSandboxFlags = 0;
  14149  uint64_t triggeringWindowId = 0;
  14150  bool triggeringStorageAccess = false;
  14151  if (mBrowsingContext) {
  14152    triggeringSandboxFlags = aContent->OwnerDoc()->GetSandboxFlags();
  14153    triggeringWindowId = aContent->OwnerDoc()->InnerWindowID();
  14154    triggeringStorageAccess = aContent->OwnerDoc()->UsingStorageAccess();
  14155  }
  14156 
  14157  uint32_t flags = INTERNAL_LOAD_FLAGS_NONE;
  14158  bool elementCanHaveNoopener = ElementCanHaveNoopener(aContent);
  14159  bool triggeringPrincipalIsSystemPrincipal =
  14160      aLoadState->TriggeringPrincipal()->IsSystemPrincipal();
  14161  if (elementCanHaveNoopener) {
  14162    MOZ_ASSERT(aContent->IsHTMLElement() || aContent->IsSVGElement());
  14163    nsAutoString relString;
  14164    aContent->AsElement()->GetAttr(nsGkAtoms::rel, relString);
  14165    nsWhitespaceTokenizerTemplate<nsContentUtils::IsHTMLWhitespace> tok(
  14166        relString);
  14167 
  14168    bool targetBlank = aLoadState->Target().LowerCaseEqualsLiteral("_blank");
  14169    bool explicitOpenerSet = false;
  14170 
  14171    // The opener behaviour follows a hierarchy, such that if a higher
  14172    // priority behaviour is specified, it always takes priority. That
  14173    // priority is currently: norefrerer > noopener > opener > default
  14174 
  14175    while (tok.hasMoreTokens()) {
  14176      const nsAString& token = tok.nextToken();
  14177      if (token.LowerCaseEqualsLiteral("noreferrer")) {
  14178        flags |= INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER |
  14179                 INTERNAL_LOAD_FLAGS_NO_OPENER;
  14180        // noreferrer cannot be overwritten by a 'rel=opener'.
  14181        explicitOpenerSet = true;
  14182        break;
  14183      }
  14184 
  14185      if (token.LowerCaseEqualsLiteral("noopener")) {
  14186        flags |= INTERNAL_LOAD_FLAGS_NO_OPENER;
  14187        explicitOpenerSet = true;
  14188      }
  14189 
  14190      if (targetBlank && StaticPrefs::dom_targetBlankNoOpener_enabled() &&
  14191          token.LowerCaseEqualsLiteral("opener") && !explicitOpenerSet) {
  14192        explicitOpenerSet = true;
  14193      }
  14194    }
  14195 
  14196    if (targetBlank && StaticPrefs::dom_targetBlankNoOpener_enabled() &&
  14197        !explicitOpenerSet && !triggeringPrincipalIsSystemPrincipal) {
  14198      flags |= INTERNAL_LOAD_FLAGS_NO_OPENER;
  14199    }
  14200 
  14201    if (aNoOpenerImplied) {
  14202      flags |= INTERNAL_LOAD_FLAGS_NO_OPENER;
  14203    }
  14204  }
  14205 
  14206  // Get the owner document of the link that was clicked, this will be
  14207  // the document that the link is in, or the last document that the
  14208  // link was in. From that document, we'll get the URI to use as the
  14209  // referrer, since the current URI in this docshell may be a
  14210  // new document that we're in the process of loading.
  14211  RefPtr<Document> referrerDoc = aContent->OwnerDoc();
  14212 
  14213  // Now check that the referrerDoc's inner window is the current inner
  14214  // window for mScriptGlobal.  If it's not, then we don't want to
  14215  // follow this link.
  14216  nsPIDOMWindowInner* referrerInner = referrerDoc->GetInnerWindow();
  14217  if (!mScriptGlobal || !referrerInner ||
  14218      mScriptGlobal->GetCurrentInnerWindow() != referrerInner) {
  14219    // We're no longer the current inner window
  14220    return NS_OK;
  14221  }
  14222 
  14223  // referrer could be null here in some odd cases, but that's ok,
  14224  // we'll just load the link w/o sending a referrer in those cases.
  14225 
  14226  uint32_t loadType = LOAD_LINK;
  14227  if (aLoadState->IsFormSubmission()) {
  14228    if (aLoadState->Target().IsEmpty()) {
  14229      // We set the right load type here for form submissions with an empty
  14230      // target. Form submission with a non-empty target are handled in
  14231      // nsDocShell::PerformRetargeting after we've selected the correct target
  14232      // BC.
  14233      loadType = GetLoadTypeForFormSubmission(GetBrowsingContext(), aLoadState);
  14234    }
  14235  } else {
  14236    // Link click can be triggered inside an onload handler, and we don't want
  14237    // to add history entry in this case.
  14238    bool inOnLoadHandler = false;
  14239    GetIsExecutingOnLoadHandler(&inOnLoadHandler);
  14240    if (inOnLoadHandler) {
  14241      loadType = LOAD_NORMAL_REPLACE;
  14242    }
  14243  }
  14244 
  14245  nsCOMPtr<nsIReferrerInfo> referrerInfo =
  14246      elementCanHaveNoopener ? new ReferrerInfo(*aContent->AsElement())
  14247                             : new ReferrerInfo(*referrerDoc);
  14248 
  14249  aLoadState->SetTriggeringSandboxFlags(triggeringSandboxFlags);
  14250  aLoadState->SetTriggeringWindowId(triggeringWindowId);
  14251  aLoadState->SetTriggeringStorageAccess(triggeringStorageAccess);
  14252  aLoadState->SetReferrerInfo(referrerInfo);
  14253  aLoadState->SetInternalLoadFlags(flags);
  14254  aLoadState->SetLoadType(loadType);
  14255  aLoadState->SetSourceBrowsingContext(mBrowsingContext);
  14256 
  14257  nsresult rv = InternalLoad(aLoadState);
  14258 
  14259  if (NS_SUCCEEDED(rv)) {
  14260    nsPingListener::DispatchPings(this, aContent, aLoadState->URI(),
  14261                                  referrerInfo);
  14262  }
  14263 
  14264  return rv;
  14265 }
  14266 
  14267 nsresult nsDocShell::OnOverLink(nsIContent* aContent, nsIURI* aURI,
  14268                                const nsAString& aTargetSpec) {
  14269  if (aContent->IsEditable()) {
  14270    return NS_OK;
  14271  }
  14272 
  14273  nsresult rv = NS_ERROR_FAILURE;
  14274 
  14275  nsCOMPtr<nsIWebBrowserChrome> browserChrome = do_GetInterface(mTreeOwner);
  14276  if (!browserChrome) {
  14277    return rv;
  14278  }
  14279 
  14280  nsCOMPtr<nsIURI> exposableURI = nsIOService::CreateExposableURI(aURI);
  14281  nsAutoCString spec;
  14282  rv = exposableURI->GetDisplaySpec(spec);
  14283  NS_ENSURE_SUCCESS(rv, rv);
  14284 
  14285  NS_ConvertUTF8toUTF16 uStr(spec);
  14286 
  14287  // The speculative connect used to go through the predictor, but we don't
  14288  // need all that just to initiate a speculative connect.
  14289  if ((StaticPrefs::network_predictor_enable_hover_on_ssl() &&
  14290       mCurrentURI->SchemeIs("https")) ||
  14291      mCurrentURI->SchemeIs("http")) {
  14292    if (nsCOMPtr<nsISpeculativeConnect> specService =
  14293            mozilla::components::IO::Service()) {
  14294      // This would be a navigation, so if this is cross origin the speculative
  14295      // connection needs to have the origin of the URL not the current page.
  14296      nsCOMPtr<nsIPrincipal> principal = BasePrincipal::CreateContentPrincipal(
  14297          aURI, aContent->NodePrincipal()->OriginAttributesRef());
  14298 
  14299      specService->SpeculativeConnect(aURI, principal, nullptr, false);
  14300    }
  14301  }
  14302 
  14303  rv = browserChrome->SetLinkStatus(uStr);
  14304  return rv;
  14305 }
  14306 
  14307 nsresult nsDocShell::OnLeaveLink() {
  14308  nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(mTreeOwner));
  14309  nsresult rv = NS_ERROR_FAILURE;
  14310 
  14311  if (browserChrome) {
  14312    rv = browserChrome->SetLinkStatus(u""_ns);
  14313  }
  14314  return rv;
  14315 }
  14316 
  14317 bool nsDocShell::ShouldBlockLoadingForBackButton() {
  14318  if (!(mLoadType & LOAD_CMD_HISTORY) ||
  14319      UserActivation::IsHandlingUserInput() ||
  14320      !Preferences::GetBool("accessibility.blockjsredirection")) {
  14321    return false;
  14322  }
  14323 
  14324  bool canGoForward = false;
  14325  GetCanGoForward(&canGoForward);
  14326  return canGoForward;
  14327 }
  14328 
  14329 //----------------------------------------------------------------------
  14330 // Web Shell Services API
  14331 
  14332 // This functions is only called when a new charset is detected in loading a
  14333 // document.
  14334 nsresult nsDocShell::CharsetChangeReloadDocument(
  14335    mozilla::NotNull<const mozilla::Encoding*> aEncoding, int32_t aSource) {
  14336  // XXX hack. keep the aCharset and aSource wait to pick it up
  14337  nsCOMPtr<nsIDocumentViewer> viewer;
  14338  NS_ENSURE_SUCCESS(GetDocViewer(getter_AddRefs(viewer)), NS_ERROR_FAILURE);
  14339  if (viewer) {
  14340    int32_t source;
  14341    (void)viewer->GetReloadEncodingAndSource(&source);
  14342    if (aSource > source) {
  14343      viewer->SetReloadEncodingAndSource(aEncoding, aSource);
  14344      if (eCharsetReloadRequested != mCharsetReloadState) {
  14345        mCharsetReloadState = eCharsetReloadRequested;
  14346        switch (mLoadType) {
  14347          case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
  14348            return Reload(LOAD_FLAGS_CHARSET_CHANGE | LOAD_FLAGS_BYPASS_CACHE |
  14349                          LOAD_FLAGS_BYPASS_PROXY);
  14350          case LOAD_RELOAD_BYPASS_CACHE:
  14351            return Reload(LOAD_FLAGS_CHARSET_CHANGE | LOAD_FLAGS_BYPASS_CACHE);
  14352          default:
  14353            return Reload(LOAD_FLAGS_CHARSET_CHANGE);
  14354        }
  14355      }
  14356    }
  14357  }
  14358  // return failure if this request is not accepted due to mCharsetReloadState
  14359  return NS_ERROR_DOCSHELL_REQUEST_REJECTED;
  14360 }
  14361 
  14362 nsresult nsDocShell::CharsetChangeStopDocumentLoad() {
  14363  if (eCharsetReloadRequested != mCharsetReloadState) {
  14364    Stop(nsIWebNavigation::STOP_ALL);
  14365    return NS_OK;
  14366  }
  14367  // return failer if this request is not accepted due to mCharsetReloadState
  14368  return NS_ERROR_DOCSHELL_REQUEST_REJECTED;
  14369 }
  14370 
  14371 NS_IMETHODIMP nsDocShell::ExitPrintPreview() {
  14372 #if NS_PRINT_PREVIEW
  14373  nsCOMPtr<nsIWebBrowserPrint> viewer = do_QueryInterface(mDocumentViewer);
  14374  return viewer->ExitPrintPreview();
  14375 #else
  14376  return NS_OK;
  14377 #endif
  14378 }
  14379 
  14380 /* [infallible] */
  14381 NS_IMETHODIMP nsDocShell::GetIsTopLevelContentDocShell(
  14382    bool* aIsTopLevelContentDocShell) {
  14383  *aIsTopLevelContentDocShell = false;
  14384 
  14385  if (mItemType == typeContent) {
  14386    *aIsTopLevelContentDocShell = mBrowsingContext->IsTopContent();
  14387  }
  14388 
  14389  return NS_OK;
  14390 }
  14391 
  14392 // Implements nsILoadContext.originAttributes
  14393 NS_IMETHODIMP
  14394 nsDocShell::GetScriptableOriginAttributes(JSContext* aCx,
  14395                                          JS::MutableHandle<JS::Value> aVal) {
  14396  return mBrowsingContext->GetScriptableOriginAttributes(aCx, aVal);
  14397 }
  14398 
  14399 // Implements nsIDocShell.GetOriginAttributes()
  14400 NS_IMETHODIMP
  14401 nsDocShell::GetOriginAttributes(JSContext* aCx,
  14402                                JS::MutableHandle<JS::Value> aVal) {
  14403  return mBrowsingContext->GetScriptableOriginAttributes(aCx, aVal);
  14404 }
  14405 
  14406 bool nsDocShell::ServiceWorkerAllowedToControlWindow(nsIPrincipal* aPrincipal,
  14407                                                     nsIURI* aURI) {
  14408  MOZ_ASSERT(aPrincipal);
  14409  MOZ_ASSERT(aURI);
  14410 
  14411  if (UsePrivateBrowsing() || mBrowsingContext->GetSandboxFlags()) {
  14412    return false;
  14413  }
  14414 
  14415  nsCOMPtr<nsIDocShellTreeItem> parent;
  14416  GetInProcessSameTypeParent(getter_AddRefs(parent));
  14417  nsPIDOMWindowOuter* parentOuter = parent ? parent->GetWindow() : nullptr;
  14418  nsPIDOMWindowInner* parentInner =
  14419      parentOuter ? parentOuter->GetCurrentInnerWindow() : nullptr;
  14420 
  14421  StorageAccess storage =
  14422      StorageAllowedForNewWindow(aPrincipal, aURI, parentInner);
  14423 
  14424  // If the partitioned service worker is enabled, service worker is allowed to
  14425  // control the window if partition is enabled.
  14426  if (StaticPrefs::privacy_partition_serviceWorkers() && parentInner) {
  14427    RefPtr<Document> doc = parentInner->GetExtantDoc();
  14428 
  14429    if (doc && StoragePartitioningEnabled(storage, doc->CookieJarSettings())) {
  14430      return true;
  14431    }
  14432  }
  14433 
  14434  return storage == StorageAccess::eAllow;
  14435 }
  14436 
  14437 nsresult nsDocShell::SetOriginAttributes(const OriginAttributes& aAttrs) {
  14438  MOZ_ASSERT(!mIsBeingDestroyed);
  14439  return mBrowsingContext->SetOriginAttributes(aAttrs);
  14440 }
  14441 
  14442 NS_IMETHODIMP
  14443 nsDocShell::ResumeRedirectedLoad(uint64_t aIdentifier, int32_t aHistoryIndex) {
  14444  RefPtr<nsDocShell> self = this;
  14445  RefPtr<ChildProcessChannelListener> cpcl =
  14446      ChildProcessChannelListener::GetSingleton();
  14447 
  14448  // Call into InternalLoad with the pending channel when it is received.
  14449  cpcl->RegisterCallback(
  14450      aIdentifier, [self, aHistoryIndex](
  14451                       nsDocShellLoadState* aLoadState,
  14452                       nsTArray<Endpoint<extensions::PStreamFilterParent>>&&
  14453                           aStreamFilterEndpoints,
  14454                       nsDOMNavigationTiming* aTiming) {
  14455        MOZ_ASSERT(aLoadState->GetPendingRedirectedChannel());
  14456        if (NS_WARN_IF(self->mIsBeingDestroyed)) {
  14457          aLoadState->GetPendingRedirectedChannel()->CancelWithReason(
  14458              NS_BINDING_ABORTED, "nsDocShell::mIsBeingDestroyed"_ns);
  14459          return NS_BINDING_ABORTED;
  14460        }
  14461 
  14462        self->mLoadType = aLoadState->LoadType();
  14463        nsCOMPtr<nsIURI> previousURI;
  14464        uint32_t previousFlags = 0;
  14465        ExtractLastVisit(aLoadState->GetPendingRedirectedChannel(),
  14466                         getter_AddRefs(previousURI), &previousFlags);
  14467        self->SaveLastVisit(aLoadState->GetPendingRedirectedChannel(),
  14468                            previousURI, previousFlags);
  14469 
  14470        if (aTiming) {
  14471          self->mTiming = new nsDOMNavigationTiming(self, aTiming);
  14472          self->mBlankTiming = false;
  14473        }
  14474 
  14475        // If we're performing a history load, locate the correct history entry,
  14476        // and set the relevant bits on our loadState.
  14477        if (aHistoryIndex >= 0 && self->GetSessionHistory() &&
  14478            !mozilla::SessionHistoryInParent()) {
  14479          nsCOMPtr<nsISHistory> legacySHistory =
  14480              self->GetSessionHistory()->LegacySHistory();
  14481 
  14482          nsCOMPtr<nsISHEntry> entry;
  14483          nsresult rv = legacySHistory->GetEntryAtIndex(aHistoryIndex,
  14484                                                        getter_AddRefs(entry));
  14485          if (NS_SUCCEEDED(rv)) {
  14486            legacySHistory->InternalSetRequestedIndex(aHistoryIndex);
  14487            aLoadState->SetLoadType(LOAD_HISTORY);
  14488            aLoadState->SetSHEntry(entry);
  14489          }
  14490        }
  14491 
  14492        // Prohibit initial about:blank handling e.g. for when a cross-process
  14493        // iframe loads about:blank and becomes same-process. Conceptually, the
  14494        // browsing context isn't new despite the docshell being newly created.
  14495        aLoadState->ProhibitInitialAboutBlankHandling();
  14496 
  14497        self->InternalLoad(aLoadState);
  14498 
  14499        if (aLoadState->GetOriginalURIString().isSome()) {
  14500          // Save URI string in case it's needed later when
  14501          // sending to search engine service in EndPageLoad()
  14502          self->mOriginalUriString = *aLoadState->GetOriginalURIString();
  14503        }
  14504 
  14505        for (auto& endpoint : aStreamFilterEndpoints) {
  14506          extensions::StreamFilterParent::Attach(
  14507              aLoadState->GetPendingRedirectedChannel(), std::move(endpoint));
  14508        }
  14509 
  14510        // If the channel isn't pending, then it means that InternalLoad
  14511        // never connected it, and we shouldn't try to continue. This
  14512        // can happen even if InternalLoad returned NS_OK.
  14513        bool pending = false;
  14514        aLoadState->GetPendingRedirectedChannel()->IsPending(&pending);
  14515        NS_ASSERTION(pending, "We should have connected the pending channel!");
  14516        if (!pending) {
  14517          return NS_BINDING_ABORTED;
  14518        }
  14519        return NS_OK;
  14520      });
  14521  return NS_OK;
  14522 }
  14523 
  14524 NS_IMETHODIMP
  14525 nsDocShell::SetOriginAttributes(JS::Handle<JS::Value> aOriginAttributes,
  14526                                JSContext* aCx) {
  14527  OriginAttributes attrs;
  14528  if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
  14529    return NS_ERROR_INVALID_ARG;
  14530  }
  14531 
  14532  return SetOriginAttributes(attrs);
  14533 }
  14534 
  14535 NS_IMETHODIMP
  14536 nsDocShell::GetAsyncPanZoomEnabled(bool* aOut) {
  14537  if (PresShell* presShell = GetPresShell()) {
  14538    *aOut = presShell->AsyncPanZoomEnabled();
  14539    return NS_OK;
  14540  }
  14541 
  14542  // If we don't have a presShell, fall back to the default platform value of
  14543  // whether or not APZ is enabled.
  14544  *aOut = gfxPlatform::AsyncPanZoomEnabled();
  14545  return NS_OK;
  14546 }
  14547 
  14548 bool nsDocShell::HasUnloadedParent() {
  14549  for (WindowContext* wc = GetBrowsingContext()->GetParentWindowContext(); wc;
  14550       wc = wc->GetParentWindowContext()) {
  14551    if (!wc->IsCurrent() || wc->IsDiscarded() ||
  14552        wc->GetBrowsingContext()->IsDiscarded()) {
  14553      // If a parent is OOP and the parent WindowContext is no
  14554      // longer current, we can assume the parent was unloaded.
  14555      return true;
  14556    }
  14557 
  14558    if (wc->GetBrowsingContext()->IsInProcess() &&
  14559        (!wc->GetBrowsingContext()->GetDocShell() ||
  14560         wc->GetBrowsingContext()->GetDocShell()->GetIsInUnload())) {
  14561      return true;
  14562    }
  14563  }
  14564  return false;
  14565 }
  14566 
  14567 /* static */
  14568 bool nsDocShell::ShouldUpdateGlobalHistory(uint32_t aLoadType) {
  14569  return !(aLoadType == LOAD_BYPASS_HISTORY || aLoadType == LOAD_ERROR_PAGE ||
  14570           aLoadType & LOAD_CMD_HISTORY);
  14571 }
  14572 
  14573 void nsDocShell::UpdateGlobalHistoryTitle(nsIURI* aURI) {
  14574  if (!mBrowsingContext->GetUseGlobalHistory() || UsePrivateBrowsing()) {
  14575    return;
  14576  }
  14577 
  14578  // Global history is interested into sub-frame visits only for link-coloring
  14579  // purposes, thus title updates are skipped for those.
  14580  //
  14581  // Moreover, some iframe documents (such as the ones created via
  14582  // document.open()) inherit the document uri of the caller, which would cause
  14583  // us to override a previously set page title with one from the subframe.
  14584  if (IsSubframe()) {
  14585    return;
  14586  }
  14587 
  14588  if (nsCOMPtr<IHistory> history = components::History::Service()) {
  14589    history->SetURITitle(aURI, mTitle);
  14590  }
  14591 }
  14592 
  14593 bool nsDocShell::IsInvisible() { return mInvisible; }
  14594 
  14595 void nsDocShell::SetInvisible(bool aInvisible) { mInvisible = aInvisible; }
  14596 
  14597 /* static */
  14598 void nsDocShell::MaybeNotifyKeywordSearchLoading(const nsString& aProvider,
  14599                                                 const nsString& aKeyword) {
  14600  if (aProvider.IsEmpty()) {
  14601    return;
  14602  }
  14603  nsresult rv;
  14604  nsCOMPtr<nsISupportsString> isupportsString =
  14605      do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
  14606  NS_ENSURE_SUCCESS_VOID(rv);
  14607 
  14608  rv = isupportsString->SetData(aProvider);
  14609  NS_ENSURE_SUCCESS_VOID(rv);
  14610 
  14611  nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
  14612  if (obsSvc) {
  14613    // Note that "keyword-search" refers to a search via the url
  14614    // bar, not a bookmarks keyword search.
  14615    obsSvc->NotifyObservers(isupportsString, "keyword-search", aKeyword.get());
  14616  }
  14617 }
  14618 
  14619 NS_IMETHODIMP
  14620 nsDocShell::ShouldPrepareForIntercept(nsIURI* aURI, nsIChannel* aChannel,
  14621                                      bool* aShouldIntercept) {
  14622  return mInterceptController->ShouldPrepareForIntercept(aURI, aChannel,
  14623                                                         aShouldIntercept);
  14624 }
  14625 
  14626 NS_IMETHODIMP
  14627 nsDocShell::ChannelIntercepted(nsIInterceptedChannel* aChannel) {
  14628  return mInterceptController->ChannelIntercepted(aChannel);
  14629 }
  14630 
  14631 bool nsDocShell::InFrameSwap() {
  14632  RefPtr<nsDocShell> shell = this;
  14633  do {
  14634    if (shell->mInFrameSwap) {
  14635      return true;
  14636    }
  14637    shell = shell->GetInProcessParentDocshell();
  14638  } while (shell);
  14639  return false;
  14640 }
  14641 
  14642 UniquePtr<ClientSource> nsDocShell::TakeInitialClientSource() {
  14643  return std::move(mInitialClientSource);
  14644 }
  14645 
  14646 NS_IMETHODIMP
  14647 nsDocShell::GetEditingSession(nsIEditingSession** aEditSession) {
  14648  if (!NS_SUCCEEDED(EnsureEditorData())) {
  14649    return NS_ERROR_FAILURE;
  14650  }
  14651 
  14652  *aEditSession = do_AddRef(mEditorData->GetEditingSession()).take();
  14653  return *aEditSession ? NS_OK : NS_ERROR_FAILURE;
  14654 }
  14655 
  14656 NS_IMETHODIMP
  14657 nsDocShell::GetScriptableBrowserChild(nsIBrowserChild** aBrowserChild) {
  14658  *aBrowserChild = GetBrowserChild().take();
  14659  return *aBrowserChild ? NS_OK : NS_ERROR_FAILURE;
  14660 }
  14661 
  14662 already_AddRefed<nsIBrowserChild> nsDocShell::GetBrowserChild() {
  14663  nsCOMPtr<nsIBrowserChild> tc = do_QueryReferent(mBrowserChild);
  14664  return tc.forget();
  14665 }
  14666 
  14667 nsCommandManager* nsDocShell::GetCommandManager() {
  14668  NS_ENSURE_SUCCESS(EnsureCommandHandler(), nullptr);
  14669  return mCommandManager;
  14670 }
  14671 
  14672 NS_IMETHODIMP_(void)
  14673 nsDocShell::GetOriginAttributes(mozilla::OriginAttributes& aAttrs) {
  14674  mBrowsingContext->GetOriginAttributes(aAttrs);
  14675 }
  14676 
  14677 HTMLEditor* nsIDocShell::GetHTMLEditor() {
  14678  nsDocShell* docShell = static_cast<nsDocShell*>(this);
  14679  return docShell->GetHTMLEditorInternal();
  14680 }
  14681 
  14682 nsresult nsIDocShell::SetHTMLEditor(HTMLEditor* aHTMLEditor) {
  14683  nsDocShell* docShell = static_cast<nsDocShell*>(this);
  14684  return docShell->SetHTMLEditorInternal(aHTMLEditor);
  14685 }
  14686 
  14687 #define MATRIX_LENGTH 20
  14688 
  14689 NS_IMETHODIMP
  14690 nsDocShell::SetColorMatrix(const nsTArray<float>& aMatrix) {
  14691  if (aMatrix.Length() == MATRIX_LENGTH) {
  14692    mColorMatrix.reset(new gfx::Matrix5x4());
  14693    static_assert(
  14694        MATRIX_LENGTH * sizeof(float) == sizeof(mColorMatrix->components),
  14695        "Size mismatch for our memcpy");
  14696    memcpy(mColorMatrix->components, aMatrix.Elements(),
  14697           sizeof(mColorMatrix->components));
  14698  } else if (aMatrix.Length() == 0) {
  14699    mColorMatrix.reset();
  14700  } else {
  14701    return NS_ERROR_INVALID_ARG;
  14702  }
  14703 
  14704  PresShell* presShell = GetPresShell();
  14705  if (!presShell) {
  14706    return NS_ERROR_FAILURE;
  14707  }
  14708 
  14709  nsIFrame* frame = presShell->GetRootFrame();
  14710  if (!frame) {
  14711    return NS_ERROR_FAILURE;
  14712  }
  14713 
  14714  frame->SchedulePaint();
  14715 
  14716  return NS_OK;
  14717 }
  14718 
  14719 NS_IMETHODIMP
  14720 nsDocShell::GetColorMatrix(nsTArray<float>& aMatrix) {
  14721  if (mColorMatrix) {
  14722    aMatrix.SetLength(MATRIX_LENGTH);
  14723    static_assert(
  14724        MATRIX_LENGTH * sizeof(float) == sizeof(mColorMatrix->components),
  14725        "Size mismatch for our memcpy");
  14726    memcpy(aMatrix.Elements(), mColorMatrix->components,
  14727           MATRIX_LENGTH * sizeof(float));
  14728  }
  14729 
  14730  return NS_OK;
  14731 }
  14732 
  14733 #undef MATRIX_LENGTH
  14734 
  14735 NS_IMETHODIMP
  14736 nsDocShell::GetIsForceReloading(bool* aForceReload) {
  14737  *aForceReload = IsForceReloading();
  14738  return NS_OK;
  14739 }
  14740 
  14741 bool nsDocShell::IsForceReloading() { return IsForceReloadType(mLoadType); }
  14742 
  14743 NS_IMETHODIMP
  14744 nsDocShell::GetBrowsingContextXPCOM(BrowsingContext** aBrowsingContext) {
  14745  *aBrowsingContext = do_AddRef(mBrowsingContext).take();
  14746  return NS_OK;
  14747 }
  14748 
  14749 BrowsingContext* nsDocShell::GetBrowsingContext() { return mBrowsingContext; }
  14750 
  14751 bool nsDocShell::GetIsAttemptingToNavigate() {
  14752  // XXXbz the document.open spec says to abort even if there's just a
  14753  // queued navigation task, sort of.  It's not clear whether browsers
  14754  // actually do that, and we didn't use to do it, so for now let's
  14755  // not do that.
  14756  // https://github.com/whatwg/html/issues/3447 tracks the spec side of this.
  14757  if (mDocumentRequest) {
  14758    // There's definitely a navigation in progress.
  14759    return true;
  14760  }
  14761 
  14762  // javascript: channels have slightly weird behavior: they're LOAD_BACKGROUND
  14763  // until the script runs, which means they're not sending loadgroup
  14764  // notifications and hence not getting set as mDocumentRequest.  Look through
  14765  // our loadgroup for document-level javascript: loads.
  14766  if (!mLoadGroup) {
  14767    return false;
  14768  }
  14769 
  14770  nsCOMPtr<nsISimpleEnumerator> requests;
  14771  mLoadGroup->GetRequests(getter_AddRefs(requests));
  14772  bool hasMore = false;
  14773  while (NS_SUCCEEDED(requests->HasMoreElements(&hasMore)) && hasMore) {
  14774    nsCOMPtr<nsISupports> elem;
  14775    requests->GetNext(getter_AddRefs(elem));
  14776    nsCOMPtr<nsIScriptChannel> scriptChannel(do_QueryInterface(elem));
  14777    if (!scriptChannel) {
  14778      continue;
  14779    }
  14780 
  14781    if (scriptChannel->GetIsDocumentLoad()) {
  14782      // This is a javascript: load that might lead to a new document,
  14783      // hence a navigation.
  14784      return true;
  14785    }
  14786  }
  14787 
  14788  return mCheckingSessionHistory;
  14789 }
  14790 
  14791 mozilla::dom::SessionHistoryInfo* nsDocShell::GetActiveSessionHistoryInfo()
  14792    const {
  14793  return mActiveEntry.get();
  14794 }
  14795 
  14796 void nsDocShell::SetLoadingSessionHistoryInfo(
  14797    const mozilla::dom::LoadingSessionHistoryInfo& aLoadingInfo,
  14798    bool aNeedToReportActiveAfterLoadingBecomesActive) {
  14799  // FIXME Would like to assert this, but can't yet.
  14800  // MOZ_ASSERT(!mLoadingEntry);
  14801  MOZ_LOG(gSHLog, LogLevel::Debug,
  14802          ("Setting the loading entry on nsDocShell %p to %s", this,
  14803           aLoadingInfo.mInfo.GetURI()->GetSpecOrDefault().get()));
  14804  mLoadingEntry = MakeUnique<LoadingSessionHistoryInfo>(aLoadingInfo);
  14805  mNeedToReportActiveAfterLoadingBecomesActive =
  14806      aNeedToReportActiveAfterLoadingBecomesActive;
  14807 }
  14808 
  14809 void nsDocShell::MoveLoadingToActiveEntry(bool aExpired, uint32_t aCacheKey,
  14810                                          nsIURI* aPreviousURI) {
  14811  MOZ_ASSERT(mozilla::SessionHistoryInParent());
  14812 
  14813  MOZ_LOG(gSHLog, LogLevel::Debug,
  14814          ("nsDocShell %p MoveLoadingToActiveEntry", this));
  14815 
  14816  UniquePtr<SessionHistoryInfo> previousActiveEntry(mActiveEntry.release());
  14817  mozilla::UniquePtr<mozilla::dom::LoadingSessionHistoryInfo> loadingEntry;
  14818  mActiveEntryIsLoadingFromSessionHistory =
  14819      mLoadingEntry && mLoadingEntry->mLoadIsFromSessionHistory;
  14820  if (mLoadingEntry) {
  14821    MOZ_LOG(gSHLog, LogLevel::Debug,
  14822            ("Moving the loading entry to the active entry on nsDocShell %p "
  14823             "to %s",
  14824             this, mLoadingEntry->mInfo.GetURI()->GetSpecOrDefault().get()));
  14825    mActiveEntry = MakeUnique<SessionHistoryInfo>(mLoadingEntry->mInfo);
  14826    mLoadingEntry.swap(loadingEntry);
  14827    if (!mActiveEntryIsLoadingFromSessionHistory) {
  14828      if (mNeedToReportActiveAfterLoadingBecomesActive) {
  14829        // Needed to pass various history length WPTs.
  14830        mBrowsingContext->SetActiveSessionHistoryEntry(
  14831            mozilla::Nothing(), mActiveEntry.get(), previousActiveEntry.get(),
  14832            mLoadType,
  14833            /* aUpdatedCacheKey = */ 0, false);
  14834      }
  14835      if (!(previousActiveEntry && previousActiveEntry->IsTransient())) {
  14836        mBrowsingContext->IncrementHistoryEntryCountForBrowsingContext();
  14837      }
  14838    }
  14839  }
  14840  mNeedToReportActiveAfterLoadingBecomesActive = false;
  14841 
  14842  if (mActiveEntry) {
  14843    if (aCacheKey != 0) {
  14844      mActiveEntry->SetCacheKey(aCacheKey);
  14845    }
  14846 
  14847    MOZ_ASSERT(loadingEntry);
  14848    uint32_t loadType =
  14849        mLoadType == LOAD_ERROR_PAGE ? mFailedLoadType : mLoadType;
  14850 
  14851    if (loadingEntry->mLoadId != UINT64_MAX) {
  14852      // We're passing in mCurrentURI, which could be null. SessionHistoryCommit
  14853      // does require a non-null uri if this is for a refresh load of the same
  14854      // URI, but in that case mCurrentURI won't be null here.
  14855      mBrowsingContext->SessionHistoryCommit(
  14856          *loadingEntry, loadType, aPreviousURI, previousActiveEntry.get(),
  14857          false, aExpired, aCacheKey);
  14858    }
  14859 
  14860    // Only update navigation if the new entry will be persisted (i.e., is not
  14861    // an about: page).
  14862    if (!loadingEntry->mInfo.IsTransient() && GetWindow() &&
  14863        GetWindow()->GetCurrentInnerWindow()) {
  14864      if (RefPtr navigation =
  14865              GetWindow()->GetCurrentInnerWindow()->Navigation()) {
  14866        navigation->InitializeHistoryEntries(loadingEntry->mContiguousEntries,
  14867                                             mActiveEntry.get());
  14868 
  14869        MOZ_LOG_FMT(gNavigationAPILog, LogLevel::Debug,
  14870                    "Before creating NavigationActivation, "
  14871                    "triggeringEntry={}, triggeringType={}",
  14872                    fmt::ptr(loadingEntry->mTriggeringEntry
  14873                                 .map([](auto& entry) { return &entry; })
  14874                                 .valueOr(nullptr)),
  14875                    loadingEntry->mTriggeringNavigationType
  14876                        .map([](NavigationType type) {
  14877                          return fmt::format(FMT_STRING("{}"), type);
  14878                        })
  14879                        .valueOr("none"));
  14880        if (loadingEntry->mTriggeringEntry &&
  14881            loadingEntry->mTriggeringNavigationType) {
  14882          navigation->CreateNavigationActivationFrom(
  14883              &*loadingEntry->mTriggeringEntry,
  14884              *loadingEntry->mTriggeringNavigationType);
  14885        }
  14886      }
  14887    }
  14888  }
  14889 }
  14890 
  14891 static bool IsFaviconLoad(nsIRequest* aRequest) {
  14892  nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
  14893  if (!channel) {
  14894    return false;
  14895  }
  14896 
  14897  nsCOMPtr<nsILoadInfo> li = channel->LoadInfo();
  14898  return li && li->InternalContentPolicyType() ==
  14899                   nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON;
  14900 }
  14901 
  14902 void nsDocShell::RecordSingleChannelId(bool aStartRequest,
  14903                                       nsIRequest* aRequest) {
  14904  // Ignore favicon loads, they don't need to block caching.
  14905  if (IsFaviconLoad(aRequest)) {
  14906    return;
  14907  }
  14908 
  14909  MOZ_ASSERT_IF(!aStartRequest, mRequestForBlockingFromBFCacheCount > 0);
  14910 
  14911  mRequestForBlockingFromBFCacheCount += aStartRequest ? 1 : -1;
  14912 
  14913  if (mBrowsingContext->GetCurrentWindowContext()) {
  14914    // We have three states: no request, one request with an id and
  14915    // eiher one request without an id or multiple requests. Nothing() is no
  14916    // request, Some(non-zero) is one request with an id and Some(0) is one
  14917    // request without an id or multiple requests.
  14918    Maybe<uint64_t> singleChannelId;
  14919    if (mRequestForBlockingFromBFCacheCount > 1) {
  14920      singleChannelId = Some(0);
  14921    } else if (mRequestForBlockingFromBFCacheCount == 1) {
  14922      nsCOMPtr<nsIIdentChannel> identChannel;
  14923      if (aStartRequest) {
  14924        identChannel = do_QueryInterface(aRequest);
  14925      } else {
  14926        // aChannel is the channel that's being removed, but we need to check if
  14927        // the remaining channel in the loadgroup has an id.
  14928        nsCOMPtr<nsISimpleEnumerator> requests;
  14929        mLoadGroup->GetRequests(getter_AddRefs(requests));
  14930        for (const auto& request : SimpleEnumerator<nsIRequest>(requests)) {
  14931          if (!IsFaviconLoad(request) &&
  14932              !!(identChannel = do_QueryInterface(request))) {
  14933            break;
  14934          }
  14935        }
  14936      }
  14937 
  14938      if (identChannel) {
  14939        singleChannelId = Some(identChannel->ChannelId());
  14940      } else {
  14941        singleChannelId = Some(0);
  14942      }
  14943    } else {
  14944      MOZ_ASSERT(mRequestForBlockingFromBFCacheCount == 0);
  14945      singleChannelId = Nothing();
  14946    }
  14947 
  14948    if (MOZ_UNLIKELY(MOZ_LOG_TEST(gSHIPBFCacheLog, LogLevel::Verbose))) {
  14949      nsAutoCString uri("[no uri]");
  14950      if (mCurrentURI) {
  14951        uri = mCurrentURI->GetSpecOrDefault();
  14952      }
  14953      if (singleChannelId.isNothing()) {
  14954        MOZ_LOG(gSHIPBFCacheLog, LogLevel::Verbose,
  14955                ("Loadgroup for %s doesn't have any requests relevant for "
  14956                 "blocking BFCache",
  14957                 uri.get()));
  14958      } else if (singleChannelId.value() == 0) {
  14959        MOZ_LOG(gSHIPBFCacheLog, LogLevel::Verbose,
  14960                ("Loadgroup for %s has multiple requests relevant for blocking "
  14961                 "BFCache",
  14962                 uri.get()));
  14963      } else {
  14964        MOZ_LOG(gSHIPBFCacheLog, LogLevel::Verbose,
  14965                ("Loadgroup for %s has one request with id %" PRIu64
  14966                 " relevant for blocking BFCache",
  14967                 uri.get(), singleChannelId.value()));
  14968      }
  14969    }
  14970 
  14971    if (mSingleChannelId != singleChannelId) {
  14972      mSingleChannelId = singleChannelId;
  14973      WindowGlobalChild* wgc =
  14974          mBrowsingContext->GetCurrentWindowContext()->GetWindowGlobalChild();
  14975      if (wgc) {
  14976        wgc->SendSetSingleChannelId(singleChannelId);
  14977      }
  14978    }
  14979  }
  14980 }
  14981 
  14982 NS_IMETHODIMP
  14983 nsDocShell::OnStartRequest(nsIRequest* aRequest) {
  14984  if (MOZ_UNLIKELY(MOZ_LOG_TEST(gSHIPBFCacheLog, LogLevel::Verbose))) {
  14985    nsAutoCString uri("[no uri]");
  14986    if (mCurrentURI) {
  14987      uri = mCurrentURI->GetSpecOrDefault();
  14988    }
  14989    nsAutoCString name;
  14990    aRequest->GetName(name);
  14991    MOZ_LOG(gSHIPBFCacheLog, LogLevel::Verbose,
  14992            ("Adding request %s to loadgroup for %s", name.get(), uri.get()));
  14993  }
  14994  RecordSingleChannelId(true, aRequest);
  14995  return nsDocLoader::OnStartRequest(aRequest);
  14996 }
  14997 
  14998 NS_IMETHODIMP
  14999 nsDocShell::OnStopRequest(nsIRequest* aRequest, nsresult aStatusCode) {
  15000  if (MOZ_UNLIKELY(MOZ_LOG_TEST(gSHIPBFCacheLog, LogLevel::Verbose))) {
  15001    nsAutoCString uri("[no uri]");
  15002    if (mCurrentURI) {
  15003      uri = mCurrentURI->GetSpecOrDefault();
  15004    }
  15005    nsAutoCString name;
  15006    aRequest->GetName(name);
  15007    MOZ_LOG(
  15008        gSHIPBFCacheLog, LogLevel::Verbose,
  15009        ("Removing request %s from loadgroup for %s", name.get(), uri.get()));
  15010  }
  15011  RecordSingleChannelId(false, aRequest);
  15012  return nsDocLoader::OnStopRequest(aRequest, aStatusCode);
  15013 }
  15014 
  15015 void nsDocShell::MaybeDisconnectChildListenersOnPageHide() {
  15016  MOZ_RELEASE_ASSERT(XRE_IsContentProcess());
  15017 
  15018  if (mChannelToDisconnectOnPageHide != 0 && mLoadGroup) {
  15019    nsCOMPtr<nsISimpleEnumerator> requests;
  15020    mLoadGroup->GetRequests(getter_AddRefs(requests));
  15021    for (const auto& request : SimpleEnumerator<nsIRequest>(requests)) {
  15022      RefPtr<DocumentChannel> channel = do_QueryObject(request);
  15023      if (channel && channel->ChannelId() == mChannelToDisconnectOnPageHide) {
  15024        static_cast<DocumentChannelChild*>(channel.get())
  15025            ->DisconnectChildListeners(NS_BINDING_ABORTED, NS_BINDING_ABORTED);
  15026      }
  15027    }
  15028    mChannelToDisconnectOnPageHide = 0;
  15029  }
  15030 }
  15031 
  15032 bool nsDocShell::IsSameDocumentAsActiveEntry(
  15033    const mozilla::dom::SessionHistoryInfo& aSHInfo) {
  15034  return mActiveEntry ? mActiveEntry->SharesDocumentWith(aSHInfo) : false;
  15035 }
  15036 
  15037 // https://html.spec.whatwg.org/#nav-window
  15038 nsPIDOMWindowInner* nsDocShell::GetActiveWindow() {
  15039  nsPIDOMWindowOuter* outer = GetWindow();
  15040  return outer ? outer->GetCurrentInnerWindow() : nullptr;
  15041 }
  15042 
  15043 // https://html.spec.whatwg.org/#inform-the-navigation-api-about-aborting-navigation
  15044 void nsDocShell::InformNavigationAPIAboutAbortingNavigation() {
  15045  // Step 1
  15046  // We really have no idea what this means.
  15047 
  15048  // No ongoing navigations if we don't have a window.
  15049  RefPtr<nsPIDOMWindowInner> window = GetActiveWindow();
  15050  if (!window) {
  15051    return;
  15052  }
  15053 
  15054  // Step 2
  15055  RefPtr<Navigation> navigation = window->Navigation();
  15056  if (!navigation) {
  15057    return;
  15058  }
  15059 
  15060  AutoJSAPI jsapi;
  15061  if (!jsapi.Init(navigation->GetOwnerGlobal())) {
  15062    return;
  15063  }
  15064 
  15065  // Steps 3 & 4
  15066  // See https://github.com/whatwg/html/issues/11579
  15067  navigation->InnerInformAboutAbortingNavigation(jsapi.cx());
  15068 }
  15069 
  15070 // https://html.spec.whatwg.org/#inform-the-navigation-api-about-child-navigable-destruction
  15071 void nsDocShell::InformNavigationAPIAboutChildNavigableDestruction() {
  15072  // Step 1
  15073  InformNavigationAPIAboutAbortingNavigation();
  15074 
  15075  // No ongoing navigations if we don't have a window.
  15076  RefPtr<nsPIDOMWindowInner> window = GetActiveWindow();
  15077  if (!window) {
  15078    return;
  15079  }
  15080 
  15081  // Step 2
  15082  RefPtr<Navigation> navigation = window->Navigation();
  15083  if (!navigation) {
  15084    return;
  15085  }
  15086 
  15087  AutoJSAPI jsapi;
  15088  if (!jsapi.Init(navigation->GetOwnerGlobal())) {
  15089    return;
  15090  }
  15091 
  15092  navigation->InformAboutChildNavigableDestruction(jsapi.cx());
  15093 }
  15094 
  15095 // https://html.spec.whatwg.org/#set-the-ongoing-navigation
  15096 void nsDocShell::SetOngoingNavigation(
  15097    const Maybe<OngoingNavigation>& aOngoingNavigation) {
  15098  // We currently only use #set-the-ongoing-navigation to call,
  15099  // #inform-the-navigation-api-about-aborting-navigation, but really it should
  15100  // be used for more. The spec keeps a piece of state on the navigable:
  15101  // https://html.spec.whatwg.org/#ongoing-navigation. Spec uses it for several
  15102  // things, for example right here in #set-the-ongoing-navigation to make sure
  15103  // that we don't call #inform-the-navigation-api-about-aborting-navigation if
  15104  // we're setting it to the same value. We currently only care about aborting
  15105  // the currently firing navigate event. Also, in reality, this is very much
  15106  // related to nsDocShell::GetIsAttemptingToNavigate() which is what we
  15107  // currently use to determine if we need to stop an ongoing navigation in
  15108  // Document::Open, whereas the spec checks if the ongoing navigation is a
  15109  // NavigationID.
  15110 
  15111  // Step 1, with the exception that we assume setting the ongoing navigation to
  15112  // an id always means a fresh id.
  15113  if (aOngoingNavigation == mOngoingNavigation &&
  15114      aOngoingNavigation != Some(OngoingNavigation::NavigationID)) {
  15115    return;
  15116  }
  15117 
  15118  // Step 2
  15119  InformNavigationAPIAboutAbortingNavigation();
  15120 
  15121  // Step 3
  15122  mOngoingNavigation = aOngoingNavigation;
  15123 }