dom/base/nsGlobalWindowInner.cpp
author Nika Layzell <nika@thelayzells.com>
Wed, 15 Nov 2017 11:34:04 -0500
changeset 436480 ba47537d18440f8d9df9351d3461d734beb9e867
parent 436479 862355bc5c589cc22420526d5bbf10e7f6f7ba78
child 436481 2d5ba51ff7e36562bb2534ed47e99af3b4e8c98d
permissions -rw-r--r--
Bug 1416384 - Part 3: Remove nsPIDOMWindow<nsISupports>, r=smaug This was needed before as the base to nsGlobalWindow, but now that nsGlobalWindow doesn't exist, and we only have specific versions, we no longer need this type. MozReview-Commit-ID: 6IJmJtnSkMr

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

nsGlobalWindowInner::InnerWindowByIdTable *nsGlobalWindowInner::sInnerWindowsById = nullptr;

/**
 * An indirect observer object that means we don't have to implement nsIObserver
 * on nsGlobalWindow, where any script could see it.
 */
class nsGlobalWindowObserver final : public nsIObserver
                                   , public nsIInterfaceRequestor
                                   , public StorageNotificationObserver
{
public:
  explicit nsGlobalWindowObserver(nsGlobalWindowInner* aWindow) : mWindow(aWindow) {}
  NS_DECL_ISUPPORTS
  NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) override
  {
    if (!mWindow)
      return NS_OK;
    return mWindow->Observe(aSubject, aTopic, aData);
  }
  void Forget() { mWindow = nullptr; }
  NS_IMETHOD GetInterface(const nsIID& aIID, void** aResult) override
  {
    if (mWindow && aIID.Equals(NS_GET_IID(nsIDOMWindow)) && mWindow) {
      return mWindow->QueryInterface(aIID, aResult);
    }
    return NS_NOINTERFACE;
  }

  void
  ObserveStorageNotification(StorageEvent* aEvent,
                             const char16_t* aStorageType,
                             bool aPrivateBrowsing) override
  {
    if (mWindow) {
      mWindow->ObserveStorageNotification(aEvent, aStorageType,
                                          aPrivateBrowsing);
    }
  }

  nsIPrincipal*
  GetPrincipal() const override
  {
    return mWindow ? mWindow->GetPrincipal() : nullptr;
  }

  bool
  IsPrivateBrowsing() const override
  {
    return mWindow ? mWindow->IsPrivateBrowsing() : false;
  }

  nsIEventTarget*
  GetEventTarget() const override
  {
    return mWindow ? mWindow->EventTargetFor(TaskCategory::Other) : nullptr;
  }

private:
  ~nsGlobalWindowObserver() = default;

  // This reference is non-owning and safe because it's cleared by
  // nsGlobalWindowInner::CleanUp().
  nsGlobalWindowInner* MOZ_NON_OWNING_REF mWindow;
};

NS_IMPL_ISUPPORTS(nsGlobalWindowObserver, nsIObserver, nsIInterfaceRequestor)

class IdleRequestExecutor;

class IdleRequestExecutorTimeoutHandler final : public TimeoutHandler
{
public:
  explicit IdleRequestExecutorTimeoutHandler(IdleRequestExecutor* aExecutor)
    : mExecutor(aExecutor)
  {
  }

  NS_DECL_ISUPPORTS_INHERITED
  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IdleRequestExecutorTimeoutHandler,
                                           TimeoutHandler)

  nsresult Call() override;

private:
  ~IdleRequestExecutorTimeoutHandler() override {}
  RefPtr<IdleRequestExecutor> mExecutor;
};

NS_IMPL_CYCLE_COLLECTION_INHERITED(IdleRequestExecutorTimeoutHandler, TimeoutHandler, mExecutor)

NS_IMPL_ADDREF_INHERITED(IdleRequestExecutorTimeoutHandler, TimeoutHandler)
NS_IMPL_RELEASE_INHERITED(IdleRequestExecutorTimeoutHandler, TimeoutHandler)

NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequestExecutorTimeoutHandler)
NS_INTERFACE_MAP_END_INHERITING(TimeoutHandler)


class IdleRequestExecutor final : public nsIRunnable
                                , public nsICancelableRunnable
                                , public nsINamed
                                , public nsIIdleRunnable
{
public:
  explicit IdleRequestExecutor(nsGlobalWindowInner* aWindow)
    : mDispatched(false)
    , mDeadline(TimeStamp::Now())
    , mWindow(aWindow)
  {
    MOZ_DIAGNOSTIC_ASSERT(mWindow);
    MOZ_DIAGNOSTIC_ASSERT(mWindow->IsInnerWindow());

    mIdlePeriodLimit = { mDeadline, mWindow->LastIdleRequestHandle() };
    mDelayedExecutorDispatcher = new IdleRequestExecutorTimeoutHandler(this);
  }

  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(IdleRequestExecutor, nsIRunnable)

  NS_DECL_NSIRUNNABLE
  NS_DECL_NSINAMED
  nsresult Cancel() override;
  void SetDeadline(TimeStamp aDeadline) override;

  bool IsCancelled() const { return !mWindow || mWindow->AsInner()->InnerObjectsFreed(); }
  // Checks if aRequest shouldn't execute in the current idle period
  // since it has been queued from a chained call to
  // requestIdleCallback from within a running idle callback.
  bool IneligibleForCurrentIdlePeriod(IdleRequest* aRequest) const
  {
    return aRequest->Handle() >= mIdlePeriodLimit.mLastRequestIdInIdlePeriod &&
           TimeStamp::Now() <= mIdlePeriodLimit.mEndOfIdlePeriod;
  }

  void MaybeUpdateIdlePeriodLimit();

  // Maybe dispatch the IdleRequestExecutor. MabyeDispatch will
  // schedule a delayed dispatch if the associated window is in the
  // background or if given a time to wait until dispatching.
  void MaybeDispatch(TimeStamp aDelayUntil = TimeStamp());
  void ScheduleDispatch();
private:
  struct IdlePeriodLimit
  {
    TimeStamp mEndOfIdlePeriod;
    uint32_t mLastRequestIdInIdlePeriod;
  };

  void DelayedDispatch(uint32_t aDelay);

  ~IdleRequestExecutor() override {}

  bool mDispatched;
  TimeStamp mDeadline;
  IdlePeriodLimit mIdlePeriodLimit;
  RefPtr<nsGlobalWindowInner> mWindow;
  // The timeout handler responsible for dispatching this executor in
  // the case of immediate dispatch to the idle queue isn't
  // desirable. This is used if we've dispatched all idle callbacks
  // that are allowed to run in the current idle period, or if the
  // associated window is currently in the background.
  nsCOMPtr<nsITimeoutHandler> mDelayedExecutorDispatcher;
  // If not Nothing() then this value is the handle to the currently
  // scheduled delayed executor dispatcher. This is needed to be able
  // to cancel the timeout handler in case of the executor being
  // cancelled.
  Maybe<int32_t> mDelayedExecutorHandle;
};

NS_IMPL_CYCLE_COLLECTION_CLASS(IdleRequestExecutor)

NS_IMPL_CYCLE_COLLECTING_ADDREF(IdleRequestExecutor)
NS_IMPL_CYCLE_COLLECTING_RELEASE(IdleRequestExecutor)

NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IdleRequestExecutor)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDelayedExecutorDispatcher)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END

NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IdleRequestExecutor)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDelayedExecutorDispatcher)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END

NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequestExecutor)
  NS_INTERFACE_MAP_ENTRY(nsIRunnable)
  NS_INTERFACE_MAP_ENTRY(nsICancelableRunnable)
  NS_INTERFACE_MAP_ENTRY(nsINamed)
  NS_INTERFACE_MAP_ENTRY(nsIIdleRunnable)
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRunnable)
NS_INTERFACE_MAP_END

NS_IMETHODIMP
IdleRequestExecutor::GetName(nsACString& aName)
{
    aName.AssignASCII("IdleRequestExecutor");
    return NS_OK;
}

NS_IMETHODIMP
IdleRequestExecutor::Run()
{
  MOZ_ASSERT(NS_IsMainThread());

  mDispatched = false;
  if (mWindow) {
    return mWindow->ExecuteIdleRequest(mDeadline);
  }

  return NS_OK;
}

nsresult
IdleRequestExecutor::Cancel()
{
  MOZ_ASSERT(NS_IsMainThread());

  if (mDelayedExecutorHandle && mWindow) {
    mWindow->AsInner()->TimeoutManager().ClearTimeout(
      mDelayedExecutorHandle.value(),
      Timeout::Reason::eIdleCallbackTimeout);
  }

  mWindow = nullptr;
  return NS_OK;
}

void
IdleRequestExecutor::SetDeadline(TimeStamp aDeadline)
{
  MOZ_ASSERT(NS_IsMainThread());

  if (!mWindow) {
    return;
  }

  mDeadline = aDeadline;
}

void
IdleRequestExecutor::MaybeUpdateIdlePeriodLimit()
{
  if (TimeStamp::Now() > mIdlePeriodLimit.mEndOfIdlePeriod) {
    mIdlePeriodLimit = { mDeadline, mWindow->LastIdleRequestHandle() };
  }
}

void
IdleRequestExecutor::MaybeDispatch(TimeStamp aDelayUntil)
{
  // If we've already dispatched the executor we don't want to do it
  // again. Also, if we've called IdleRequestExecutor::Cancel mWindow
  // will be null, which indicates that we shouldn't dispatch this
  // executor either.
  if (mDispatched || IsCancelled()) {
    return;
  }

  mDispatched = true;

  nsPIDOMWindowOuter* outer = mWindow->GetOuterWindow();
  if (outer && outer->AsOuter()->IsBackground()) {
    // Set a timeout handler with a timeout of 0 ms to throttle idle
    // callback requests coming from a backround window using
    // background timeout throttling.
    DelayedDispatch(0);
    return;
  }

  TimeStamp now = TimeStamp::Now();
  if (!aDelayUntil || aDelayUntil < now) {
    ScheduleDispatch();
    return;
  }

  TimeDuration delay = aDelayUntil - now;
  DelayedDispatch(static_cast<uint32_t>(delay.ToMilliseconds()));
}

void
IdleRequestExecutor::ScheduleDispatch()
{
  MOZ_ASSERT(mWindow);
  mDelayedExecutorHandle = Nothing();
  RefPtr<IdleRequestExecutor> request = this;
  NS_IdleDispatchToCurrentThread(request.forget());
}

void
IdleRequestExecutor::DelayedDispatch(uint32_t aDelay)
{
  MOZ_ASSERT(mWindow);
  MOZ_ASSERT(mDelayedExecutorHandle.isNothing());
  int32_t handle;
  mWindow->AsInner()->TimeoutManager().SetTimeout(
    mDelayedExecutorDispatcher, aDelay, false, Timeout::Reason::eIdleCallbackTimeout, &handle);
  mDelayedExecutorHandle = Some(handle);
}

nsresult
IdleRequestExecutorTimeoutHandler::Call()
{
  if (!mExecutor->IsCancelled()) {
    mExecutor->ScheduleDispatch();
  }
  return NS_OK;
}

void
nsGlobalWindowInner::ScheduleIdleRequestDispatch()
{
  AssertIsOnMainThread();

  if (!mIdleRequestExecutor) {
    mIdleRequestExecutor = new IdleRequestExecutor(AssertInner());
  }

  mIdleRequestExecutor->MaybeDispatch();
}

void
nsGlobalWindowInner::SuspendIdleRequests()
{
  if (mIdleRequestExecutor) {
    mIdleRequestExecutor->Cancel();
    mIdleRequestExecutor = nullptr;
  }
}

void
nsGlobalWindowInner::ResumeIdleRequests()
{
  MOZ_ASSERT(!mIdleRequestExecutor);

  ScheduleIdleRequestDispatch();
}

void
nsGlobalWindowInner::InsertIdleCallback(IdleRequest* aRequest)
{
  AssertIsOnMainThread();
  mIdleRequestCallbacks.insertBack(aRequest);
  aRequest->AddRef();
}

void
nsGlobalWindowInner::RemoveIdleCallback(mozilla::dom::IdleRequest* aRequest)
{
  AssertIsOnMainThread();

  if (aRequest->HasTimeout()) {
    mTimeoutManager->ClearTimeout(aRequest->GetTimeoutHandle(),
                                  Timeout::Reason::eIdleCallbackTimeout);
  }

  aRequest->removeFrom(mIdleRequestCallbacks);
  aRequest->Release();
}

nsresult
nsGlobalWindowInner::RunIdleRequest(IdleRequest* aRequest,
                               DOMHighResTimeStamp aDeadline,
                               bool aDidTimeout)
{
  AssertIsOnMainThread();
  RefPtr<IdleRequest> request(aRequest);
  RemoveIdleCallback(request);
  return request->IdleRun(AsInner(), aDeadline, aDidTimeout);
}

nsresult
nsGlobalWindowInner::ExecuteIdleRequest(TimeStamp aDeadline)
{
  AssertIsOnMainThread();
  RefPtr<IdleRequest> request = mIdleRequestCallbacks.getFirst();

  if (!request) {
    // There are no more idle requests, so stop scheduling idle
    // request callbacks.
    return NS_OK;
  }

  // If the request that we're trying to execute has been queued
  // during the current idle period, then dispatch it again at the end
  // of the idle period.
  if (mIdleRequestExecutor->IneligibleForCurrentIdlePeriod(request)) {
    mIdleRequestExecutor->MaybeDispatch(aDeadline);
    return NS_OK;
  }

  DOMHighResTimeStamp deadline = 0.0;

  if (Performance* perf = GetPerformance()) {
    deadline = perf->GetDOMTiming()->TimeStampToDOMHighRes(aDeadline);
  }

  mIdleRequestExecutor->MaybeUpdateIdlePeriodLimit();
  nsresult result = RunIdleRequest(request, deadline, false);

  // Running the idle callback could've suspended the window, in which
  // case mIdleRequestExecutor will be null.
  if (mIdleRequestExecutor) {
    mIdleRequestExecutor->MaybeDispatch();
  }
  return result;
}

class IdleRequestTimeoutHandler final : public TimeoutHandler
{
public:
  IdleRequestTimeoutHandler(JSContext* aCx,
                            IdleRequest* aIdleRequest,
                            nsPIDOMWindowInner* aWindow)
    : TimeoutHandler(aCx)
    , mIdleRequest(aIdleRequest)
    , mWindow(aWindow)
  {
  }

  NS_DECL_ISUPPORTS_INHERITED
  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IdleRequestTimeoutHandler,
                                           TimeoutHandler)

  nsresult Call() override
  {
    return nsGlobalWindowInner::Cast(mWindow)->RunIdleRequest(mIdleRequest, 0.0, true);
  }

private:
  ~IdleRequestTimeoutHandler() override {}

  RefPtr<IdleRequest> mIdleRequest;
  nsCOMPtr<nsPIDOMWindowInner> mWindow;
};

NS_IMPL_CYCLE_COLLECTION_INHERITED(IdleRequestTimeoutHandler,
                                   TimeoutHandler,
                                   mIdleRequest,
                                   mWindow)

NS_IMPL_ADDREF_INHERITED(IdleRequestTimeoutHandler, TimeoutHandler)
NS_IMPL_RELEASE_INHERITED(IdleRequestTimeoutHandler, TimeoutHandler)

NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequestTimeoutHandler)
NS_INTERFACE_MAP_END_INHERITING(TimeoutHandler)

uint32_t
nsGlobalWindowInner::RequestIdleCallback(JSContext* aCx,
                                    IdleRequestCallback& aCallback,
                                    const IdleRequestOptions& aOptions,
                                    ErrorResult& aError)
{
  MOZ_RELEASE_ASSERT(IsInnerWindow());
  AssertIsOnMainThread();

  uint32_t handle = mIdleRequestCallbackCounter++;

  RefPtr<IdleRequest> request =
    new IdleRequest(&aCallback, handle);

  if (aOptions.mTimeout.WasPassed()) {
    int32_t timeoutHandle;
    nsCOMPtr<nsITimeoutHandler> handler(new IdleRequestTimeoutHandler(aCx, request, AsInner()));

    nsresult rv = mTimeoutManager->SetTimeout(
        handler, aOptions.mTimeout.Value(), false,
        Timeout::Reason::eIdleCallbackTimeout, &timeoutHandle);

    if (NS_WARN_IF(NS_FAILED(rv))) {
      return 0;
    }

    request->SetTimeoutHandle(timeoutHandle);
  }

  // mIdleRequestCallbacks now owns request
  InsertIdleCallback(request);

  if (!IsSuspended()) {
    ScheduleIdleRequestDispatch();
  }

  return handle;
}

void
nsGlobalWindowInner::CancelIdleCallback(uint32_t aHandle)
{
  MOZ_RELEASE_ASSERT(IsInnerWindow());

  for (IdleRequest* r : mIdleRequestCallbacks) {
    if (r->Handle() == aHandle) {
      RemoveIdleCallback(r);
      break;
    }
  }
}

void
nsGlobalWindowInner::DisableIdleCallbackRequests()
{
  if (mIdleRequestExecutor) {
    mIdleRequestExecutor->Cancel();
    mIdleRequestExecutor = nullptr;
  }

  while (!mIdleRequestCallbacks.isEmpty()) {
    RefPtr<IdleRequest> request = mIdleRequestCallbacks.getFirst();
    RemoveIdleCallback(request);
  }
}

bool
nsGlobalWindowInner::IsBackgroundInternal() const
{
  return !mOuterWindow || mOuterWindow->IsBackground();
}

//*****************************************************************************
//***    nsGlobalWindowInner: Object Management
//*****************************************************************************

nsGlobalWindowInner::nsGlobalWindowInner(nsGlobalWindowOuter *aOuterWindow)
  : nsPIDOMWindowInner(aOuterWindow->AsOuter()),
    mIdleFuzzFactor(0),
    mIdleCallbackIndex(-1),
    mCurrentlyIdle(false),
    mAddActiveEventFuzzTime(true),
    mFullScreen(false),
    mFullscreenMode(false),
    mIsClosed(false),
    mInClose(false),
    mHavePendingClose(false),
    mHadOriginalOpener(false),
    mOriginalOpenerWasSecureContext(false),
    mIsPopupSpam(false),
    mBlockScriptedClosingFlag(false),
    mWasOffline(false),
    mHasHadSlowScript(false),
    mNotifyIdleObserversIdleOnThaw(false),
    mNotifyIdleObserversActiveOnThaw(false),
    mCreatingInnerWindow(false),
    mIsChrome(false),
    mCleanMessageManager(false),
    mNeedsFocus(true),
    mHasFocus(false),
    mShowFocusRingForContent(false),
    mFocusByKeyOccurred(false),
    mHasGamepad(false),
    mHasVREvents(false),
    mHasVRDisplayActivateEvents(false),
    mHasSeenGamepadInput(false),
    mNotifiedIDDestroyed(false),
    mAllowScriptsToClose(false),
    mTopLevelOuterContentWindow(false),
    mSuspendDepth(0),
    mFreezeDepth(0),
    mFocusMethod(0),
    mSerial(0),
    mIdleRequestCallbackCounter(1),
    mIdleRequestExecutor(nullptr),
#ifdef DEBUG
    mSetOpenerWindowCalled(false),
#endif
    mCleanedUp(false),
    mDialogAbuseCount(0),
    mAreDialogsEnabled(true),
#ifdef DEBUG
    mIsValidatingTabGroup(false),
#endif
    mCanSkipCCGeneration(0),
    mAutoActivateVRDisplayID(0),
    mBeforeUnloadListenerCount(0)
{
  AssertIsOnMainThread();

  nsLayoutStatics::AddRef();

  // Initialize the PRCList (this).
  PR_INIT_CLIST(this);

  if (aOuterWindow) {
    // |this| is an inner window, add this inner window to the outer
    // window list of inners.
    PR_INSERT_AFTER(this, aOuterWindow);

    mObserver = new nsGlobalWindowObserver(AssertInner());
    if (mObserver) {
      nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
      if (os) {
        // Watch for online/offline status changes so we can fire events. Use
        // a strong reference.
        os->AddObserver(mObserver, NS_IOSERVICE_OFFLINE_STATUS_TOPIC,
                        false);

        os->AddObserver(mObserver, MEMORY_PRESSURE_OBSERVER_TOPIC, false);
      }

      Preferences::AddStrongObserver(mObserver, "intl.accept_languages");

      // Watch for storage notifications so we can fire storage events.
      RefPtr<StorageNotifierService> sns =
        StorageNotifierService::GetOrCreate();
      if (sns) {
        sns->Register(mObserver);
      }
    }
  } else {
    // |this| is an outer window. Outer windows start out frozen and
    // remain frozen until they get an inner window.
    MOZ_ASSERT(IsFrozen());
  }

  if (XRE_IsContentProcess()) {
    nsCOMPtr<nsIDocShell> docShell = GetDocShell();
    if (docShell) {
      mTabChild = docShell->GetTabChild();
    }
  }

  // We could have failed the first time through trying
  // to create the entropy collector, so we should
  // try to get one until we succeed.

  gRefCnt++;

  EnsurePrefCaches();

  if (gDumpFile == nullptr) {
    nsAutoCString fname;
    Preferences::GetCString("browser.dom.window.dump.file", fname);
    if (!fname.IsEmpty()) {
      // If this fails to open, Dump() knows to just go to stdout on null.
      gDumpFile = fopen(fname.get(), "wb+");
    } else {
      gDumpFile = stdout;
    }
  }

  mSerial = ++gSerialCounter;

#ifdef DEBUG
  if (!PR_GetEnv("MOZ_QUIET")) {
    printf_stderr("++DOMWINDOW == %d (%p) [pid = %d] [serial = %d] [outer = %p]\n",
                  gRefCnt,
                  static_cast<void*>(ToCanonicalSupports(this)),
                  getpid(),
                  gSerialCounter,
                  static_cast<void*>(ToCanonicalSupports(aOuterWindow)));
  }
#endif

  MOZ_LOG(gDOMLeakPRLog, LogLevel::Debug,
          ("DOMWINDOW %p created outer=%p", this, aOuterWindow));

  // Add ourselves to the inner windows list.
  MOZ_ASSERT(sInnerWindowsById, "Inner Windows hash table must be created!");
  MOZ_ASSERT(!sInnerWindowsById->Get(mWindowID),
             "This window shouldn't be in the hash table yet!");
  // We seem to see crashes in release builds because of null |sInnerWindowsById|.
  if (sInnerWindowsById) {
    sInnerWindowsById->Put(mWindowID, AssertInner());
  }
}

#ifdef DEBUG

/* static */
void
nsGlobalWindowInner::AssertIsOnMainThread()
{
  MOZ_ASSERT(NS_IsMainThread());
}

#endif // DEBUG

/* static */
void
nsGlobalWindowInner::Init()
{
  AssertIsOnMainThread();

  NS_ASSERTION(gDOMLeakPRLog, "gDOMLeakPRLog should have been initialized!");

  sInnerWindowsById = new InnerWindowByIdTable();
}

nsGlobalWindowInner::~nsGlobalWindowInner()
{
  AssertIsOnMainThread();

  if (IsChromeWindow()) {
    MOZ_ASSERT(mCleanMessageManager,
              "chrome windows may always disconnect the msg manager");

    DisconnectAndClearGroupMessageManagers();

    if (mChromeFields.mMessageManager) {
      static_cast<nsFrameMessageManager *>(
        mChromeFields.mMessageManager.get())->Disconnect();
    }

    mCleanMessageManager = false;
  }

  DisconnectEventTargetObjects();

  if (IsOuterWindow()) {
    if (nsGlobalWindowOuter::sOuterWindowsById) {
      MOZ_ASSERT(nsGlobalWindowOuter::sOuterWindowsById->Get(mWindowID),
                 "This window should be in the hash table");
      nsGlobalWindowOuter::sOuterWindowsById->Remove(mWindowID);
    }
  } else {
    if (nsGlobalWindowInner::sInnerWindowsById) {
      MOZ_ASSERT(nsGlobalWindowInner::sInnerWindowsById->Get(mWindowID),
                 "This window should be in the hash table");
      nsGlobalWindowInner::sInnerWindowsById->Remove(mWindowID);
    }
  }

  --gRefCnt;

#ifdef DEBUG
  if (!PR_GetEnv("MOZ_QUIET")) {
    nsAutoCString url;
    if (mLastOpenedURI) {
      url = mLastOpenedURI->GetSpecOrDefault();

      // Data URLs can be very long, so truncate to avoid flooding the log.
      const uint32_t maxURLLength = 1000;
      if (url.Length() > maxURLLength) {
        url.Truncate(maxURLLength);
      }
    }

    nsGlobalWindowOuter* outer = nsGlobalWindowOuter::Cast(mOuterWindow);
    printf_stderr("--DOMWINDOW == %d (%p) [pid = %d] [serial = %d] [outer = %p] [url = %s]\n",
                  gRefCnt,
                  static_cast<void*>(ToCanonicalSupports(this)),
                  getpid(),
                  mSerial,
                  static_cast<void*>(ToCanonicalSupports(outer)),
                  url.get());
  }
#endif

  MOZ_LOG(gDOMLeakPRLog, LogLevel::Debug, ("DOMWINDOW %p destroyed", this));

  if (IsOuterWindow()) {
    JSObject *proxy = GetWrapperMaybeDead();
    if (proxy) {
      js::SetProxyReservedSlot(proxy, 0, js::PrivateValue(nullptr));
    }

    // An outer window is destroyed with inner windows still possibly
    // alive, iterate through the inner windows and null out their
    // back pointer to this outer, and pull them out of the list of
    // inner windows.
    //
    // Our linked list of inner windows both contains (an nsGlobalWindowOuter),
    // and our inner windows (nsGlobalWindowInners). This means that we need to
    // use PRCList*. We can then compare that PRCList* to `this` to see if its an
    // inner or outer window.
    PRCList* w;
    while ((w = PR_LIST_HEAD(this)) != this) {
      PR_REMOVE_AND_INIT_LINK(w);
    }

    DropOuterWindowDocs();
  } else {
    Telemetry::Accumulate(Telemetry::INNERWINDOWS_WITH_MUTATION_LISTENERS,
                          mMutationBits ? 1 : 0);

    if (mListenerManager) {
      mListenerManager->Disconnect();
      mListenerManager = nullptr;
    }

    // An inner window is destroyed, pull it out of the outer window's
    // list if inner windows.

    PR_REMOVE_LINK(this);

    // If our outer window's inner window is this window, null out the
    // outer window's reference to this window that's being deleted.
    nsGlobalWindowOuter *outer = GetOuterWindowInternal();
    if (outer) {
      outer->MaybeClearInnerWindow(AssertInner());
    }
  }

  // We don't have to leave the tab group if we are an inner window.
  if (mTabGroup && IsOuterWindow()) {
    mTabGroup->Leave(AsOuter());
  }

  // Outer windows are always supposed to call CleanUp before letting themselves
  // be destroyed. And while CleanUp generally seems to be intended to clean up
  // outers, we've historically called it for both. Changing this would probably
  // involve auditing all of the references that inners and outers can have, and
  // separating the handling into CleanUp() and FreeInnerObjects.
  if (IsInnerWindow()) {
    CleanUp();
  } else {
    MOZ_ASSERT(mCleanedUp);
  }

  nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
  if (ac)
    ac->RemoveWindowAsListener(this);

  nsLayoutStatics::Release();
}

void
nsGlobalWindowInner::AddEventTargetObject(DOMEventTargetHelper* aObject)
{
  MOZ_ASSERT(IsInnerWindow());
  mEventTargetObjects.PutEntry(aObject);
}

void
nsGlobalWindowInner::RemoveEventTargetObject(DOMEventTargetHelper* aObject)
{
  MOZ_ASSERT(IsInnerWindow());
  mEventTargetObjects.RemoveEntry(aObject);
}

void
nsGlobalWindowInner::DisconnectEventTargetObjects()
{
  for (auto iter = mEventTargetObjects.ConstIter(); !iter.Done();
       iter.Next()) {
    RefPtr<DOMEventTargetHelper> target = iter.Get()->GetKey();
    target->DisconnectFromOwner();
  }
  mEventTargetObjects.Clear();
}

// static
void
nsGlobalWindowInner::ShutDown()
{
  AssertIsOnMainThread();

  // nsGlobalWindowOuter::ShutDown() handles closing gDumpFile.
  delete sInnerWindowsById;
  sInnerWindowsById = nullptr;
}

// static
void
nsGlobalWindowInner::CleanupCachedXBLHandlers()
{
  if (mCachedXBLPrototypeHandlers &&
      mCachedXBLPrototypeHandlers->Count() > 0) {
    mCachedXBLPrototypeHandlers->Clear();
  }
}

void
nsGlobalWindowInner::MaybeForgiveSpamCount()
{
  if (IsOuterWindow() &&
      IsPopupSpamWindow()) {
    SetIsPopupSpamWindow(false);
  }
}

void
nsGlobalWindowInner::SetIsPopupSpamWindow(bool aIsPopupSpam)
{
  MOZ_ASSERT(IsOuterWindow());

  mIsPopupSpam = aIsPopupSpam;
  if (aIsPopupSpam) {
    ++gOpenPopupSpamCount;
  } else {
    --gOpenPopupSpamCount;
    NS_ASSERTION(gOpenPopupSpamCount >= 0,
                 "Unbalanced decrement of gOpenPopupSpamCount");
  }
}

void
nsGlobalWindowInner::DropOuterWindowDocs()
{
  MOZ_ASSERT(IsOuterWindow());
  MOZ_ASSERT_IF(mDoc, !mDoc->EventHandlingSuppressed());
  mDoc = nullptr;
  mSuspendedDoc = nullptr;
}

void
nsGlobalWindowInner::CleanUp()
{
  // Guarantee idempotence.
  if (mCleanedUp)
    return;
  mCleanedUp = true;

  StartDying();

  DisconnectEventTargetObjects();

  if (mObserver) {
    nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
    if (os) {
      os->RemoveObserver(mObserver, NS_IOSERVICE_OFFLINE_STATUS_TOPIC);
      os->RemoveObserver(mObserver, MEMORY_PRESSURE_OBSERVER_TOPIC);
    }

    RefPtr<StorageNotifierService> sns = StorageNotifierService::GetOrCreate();
    if (sns) {
     sns->Unregister(mObserver);
    }

    if (mIdleService) {
      mIdleService->RemoveIdleObserver(mObserver, MIN_IDLE_NOTIFICATION_TIME_S);
    }

    Preferences::RemoveObserver(mObserver, "intl.accept_languages");

    // Drop its reference to this dying window, in case for some bogus reason
    // the object stays around.
    mObserver->Forget();
  }

  if (mNavigator) {
    mNavigator->Invalidate();
    mNavigator = nullptr;
  }

  mScreen = nullptr;
  mMenubar = nullptr;
  mToolbar = nullptr;
  mLocationbar = nullptr;
  mPersonalbar = nullptr;
  mStatusbar = nullptr;
  mScrollbars = nullptr;
  mHistory = nullptr;
  mCustomElements = nullptr;
  mFrames = nullptr;
  mWindowUtils = nullptr;
  mApplicationCache = nullptr;
  mIndexedDB = nullptr;

  mConsole = nullptr;

  mAudioWorklet = nullptr;
  mPaintWorklet = nullptr;

  mExternal = nullptr;

  mMozSelfSupport = nullptr;

  mPerformance = nullptr;

#ifdef MOZ_WEBSPEECH
  mSpeechSynthesis = nullptr;
#endif

#if defined(MOZ_WIDGET_ANDROID)
  mOrientationChangeObserver = nullptr;
#endif

  ClearControllers();

  mOpener = nullptr;             // Forces Release
  if (mContext) {
    mContext = nullptr;            // Forces Release
  }
  mChromeEventHandler = nullptr; // Forces Release
  mParentTarget = nullptr;

  if (IsOuterWindow()) {
    nsGlobalWindowInner* inner = GetCurrentInnerWindowInternal();
    if (inner) {
      inner->CleanUp();
    }
  }

  if (IsInnerWindow()) {
    DisableGamepadUpdates();
    mHasGamepad = false;
    DisableVRUpdates();
    mHasVREvents = false;
    mHasVRDisplayActivateEvents = false;
    DisableIdleCallbackRequests();
  } else {
    MOZ_ASSERT(!mHasGamepad);
    MOZ_ASSERT(!mHasVREvents);
    MOZ_ASSERT(!mHasVRDisplayActivateEvents);
  }

  if (mCleanMessageManager) {
    MOZ_ASSERT(mIsChrome, "only chrome should have msg manager cleaned");
    if (mChromeFields.mMessageManager) {
      static_cast<nsFrameMessageManager*>(
        mChromeFields.mMessageManager.get())->Disconnect();
    }
  }

  mArguments = nullptr;

  CleanupCachedXBLHandlers();

  for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
    mAudioContexts[i]->Shutdown();
  }
  mAudioContexts.Clear();

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

  mServiceWorkerRegistrationTable.Clear();

  mIntlUtils = nullptr;
}

void
nsGlobalWindowInner::ClearControllers()
{
  if (mControllers) {
    uint32_t count;
    mControllers->GetControllerCount(&count);

    while (count--) {
      nsCOMPtr<nsIController> controller;
      mControllers->GetControllerAt(count, getter_AddRefs(controller));

      nsCOMPtr<nsIControllerContext> context = do_QueryInterface(controller);
      if (context)
        context->SetCommandContext(nullptr);
    }

    mControllers = nullptr;
  }
}

void
nsGlobalWindowInner::FreeInnerObjects()
{
  NS_ASSERTION(IsInnerWindow(), "Don't free inner objects on an outer window");

  // Make sure that this is called before we null out the document and
  // other members that the window destroyed observers could
  // re-create.
  NotifyDOMWindowDestroyed(AssertInner());
  if (auto* reporter = nsWindowMemoryReporter::Get()) {
    reporter->ObserveDOMWindowDetached(AssertInner());
  }

  mInnerObjectsFreed = true;

  // Kill all of the workers for this window.
  mozilla::dom::workers::CancelWorkersForWindow(AsInner());

  if (mTimeoutManager) {
    mTimeoutManager->ClearAllTimeouts();
  }

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

  mIdleObservers.Clear();

  DisableIdleCallbackRequests();

  mChromeEventHandler = nullptr;

  if (mListenerManager) {
    mListenerManager->Disconnect();
    mListenerManager = nullptr;
  }

  mHistory = nullptr;
  mCustomElements = nullptr;

  if (mNavigator) {
    mNavigator->OnNavigation();
    mNavigator->Invalidate();
    mNavigator = nullptr;
  }

  if (mScreen) {
    mScreen = nullptr;
  }

#if defined(MOZ_WIDGET_ANDROID)
  mOrientationChangeObserver = nullptr;
#endif

  if (mDoc) {
    // Remember the document's principal and URI.
    mDocumentPrincipal = mDoc->NodePrincipal();
    mDocumentURI = mDoc->GetDocumentURI();
    mDocBaseURI = mDoc->GetDocBaseURI();

    while (mDoc->EventHandlingSuppressed()) {
      mDoc->UnsuppressEventHandlingAndFireEvents(false);
    }
  }

  // Remove our reference to the document and the document principal.
  mFocusedNode = nullptr;

  if (mApplicationCache) {
    static_cast<nsDOMOfflineResourceList*>(mApplicationCache.get())->Disconnect();
    mApplicationCache = nullptr;
  }

  mIndexedDB = nullptr;

  UnlinkHostObjectURIs();

  NotifyWindowIDDestroyed("inner-window-destroyed");

  CleanupCachedXBLHandlers();

  for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
    mAudioContexts[i]->Shutdown();
  }
  mAudioContexts.Clear();

  DisableGamepadUpdates();
  mHasGamepad = false;
  mGamepads.Clear();
  DisableVRUpdates();
  mHasVREvents = false;
  mHasVRDisplayActivateEvents = false;
  mVRDisplays.Clear();

  if (mTabChild) {
    while (mBeforeUnloadListenerCount-- > 0) {
      mTabChild->BeforeUnloadRemoved();
    }
  }
}

//*****************************************************************************
// nsGlobalWindowInner::nsISupports
//*****************************************************************************

// QueryInterface implementation for nsGlobalWindowInner
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsGlobalWindowInner)
  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
  // Make sure this matches the cast in nsGlobalWindowInner::FromWrapper()
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMEventTarget)
  NS_INTERFACE_MAP_ENTRY(nsIDOMWindow)
  NS_INTERFACE_MAP_ENTRY(nsIGlobalObject)
  NS_INTERFACE_MAP_ENTRY(nsIScriptGlobalObject)
  NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal)
  NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget)
  NS_INTERFACE_MAP_ENTRY(mozilla::dom::EventTarget)
  if (aIID.Equals(NS_GET_IID(nsPIDOMWindowInner))) {
    foundInterface = AsInner();
  } else
  if (aIID.Equals(NS_GET_IID(mozIDOMWindow)) && IsInnerWindow()) {
    foundInterface = AsInner();
  } else
  if (aIID.Equals(NS_GET_IID(nsPIDOMWindowOuter))) {
    foundInterface = AsOuter();
  } else
  if (aIID.Equals(NS_GET_IID(mozIDOMWindowProxy)) && IsOuterWindow()) {
    foundInterface = AsOuter();
  } else
  if (aIID.Equals(NS_GET_IID(nsIDOMChromeWindow)) && IsChromeWindow()) {
    foundInterface = static_cast<nsIDOMChromeWindow*>(this);
  } else
  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
  NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
NS_INTERFACE_MAP_END


NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGlobalWindowInner)
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsGlobalWindowInner)

NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsGlobalWindowInner)
  if (tmp->IsBlackForCC(false)) {
    if (nsCCUncollectableMarker::InGeneration(tmp->mCanSkipCCGeneration)) {
      return true;
    }
    tmp->mCanSkipCCGeneration = nsCCUncollectableMarker::sGeneration;
    if (tmp->mCachedXBLPrototypeHandlers) {
      for (auto iter = tmp->mCachedXBLPrototypeHandlers->Iter();
           !iter.Done();
           iter.Next()) {
        iter.Data().exposeToActiveJS();
      }
    }
    if (EventListenerManager* elm = tmp->GetExistingListenerManager()) {
      elm->MarkForCC();
    }
    if (tmp->mTimeoutManager) {
      tmp->mTimeoutManager->UnmarkGrayTimers();
    }
    return true;
  }
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END

NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsGlobalWindowInner)
  return tmp->IsBlackForCC(true);
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END

NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsGlobalWindowInner)
  return tmp->IsBlackForCC(false);
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END

NS_IMPL_CYCLE_COLLECTION_CLASS(nsGlobalWindowInner)

NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsGlobalWindowInner)
  if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
    char name[512];
    nsAutoCString uri;
    if (tmp->mDoc && tmp->mDoc->GetDocumentURI()) {
      uri = tmp->mDoc->GetDocumentURI()->GetSpecOrDefault();
    }
    SprintfLiteral(name, "nsGlobalWindowInner # %" PRIu64 " %s %s", tmp->mWindowID,
                   tmp->IsInnerWindow() ? "inner" : "outer", uri.get());
    cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
  } else {
    NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsGlobalWindowInner, tmp->mRefCnt.get())
  }

  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext)

  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mControllers)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mArguments)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReturnValue)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNavigator)

  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPerformance)

  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mServiceWorkerRegistrationTable)

#ifdef MOZ_WEBSPEECH
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSpeechSynthesis)
#endif

  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOuterWindow)

  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTopInnerWindow)

  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager)

  if (tmp->mTimeoutManager) {
    tmp->mTimeoutManager->ForEachUnorderedTimeout([&cb](Timeout* timeout) {
      cb.NoteNativeChild(timeout, NS_CYCLE_COLLECTION_PARTICIPANT(Timeout));
    });
  }

  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocation)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHistory)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCustomElements)

  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocalStorage)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSessionStorage)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mApplicationCache)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSuspendedDoc)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIndexedDB)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentPrincipal)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTabChild)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDoc)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleService)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWakeLock)

  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleRequestExecutor)
  for (IdleRequest* request : tmp->mIdleRequestCallbacks) {
    cb.NoteNativeChild(request, NS_CYCLE_COLLECTION_PARTICIPANT(IdleRequest));
  }

  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleObservers)

  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGamepads)

  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCacheStorage)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVRDisplays)

  // Traverse stuff from nsPIDOMWindow
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeEventHandler)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParentTarget)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameElement)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFocusedNode)

  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMenubar)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mToolbar)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocationbar)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPersonalbar)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStatusbar)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScrollbars)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCrypto)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mU2F)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsole)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAudioWorklet)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPaintWorklet)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExternal)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMozSelfSupport)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIntlUtils)

  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocShell)

  tmp->TraverseHostObjectURIs(cb);

  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeFields.mBrowserDOMWindow)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeFields.mMessageManager)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeFields.mGroupMessageManagers)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeFields.mOpenerForInitialContentBrowser)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END

NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindowInner)
  tmp->CleanupCachedXBLHandlers();

  NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext)

  NS_IMPL_CYCLE_COLLECTION_UNLINK(mControllers)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mArguments)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mReturnValue)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mNavigator)

  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPerformance)

  NS_IMPL_CYCLE_COLLECTION_UNLINK(mServiceWorkerRegistrationTable)

#ifdef MOZ_WEBSPEECH
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mSpeechSynthesis)
#endif

  if (tmp->mOuterWindow) {
    nsGlobalWindowOuter::Cast(tmp->mOuterWindow)->
      MaybeClearInnerWindow(tmp->AssertInner());
    NS_IMPL_CYCLE_COLLECTION_UNLINK(mOuterWindow)
  }

  if (tmp->mListenerManager) {
    tmp->mListenerManager->Disconnect();
    NS_IMPL_CYCLE_COLLECTION_UNLINK(mListenerManager)
  }

  tmp->UpdateTopInnerWindow();
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mTopInnerWindow)

  NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocation)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mHistory)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mCustomElements)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocalStorage)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mSessionStorage)
  if (tmp->mApplicationCache) {
    static_cast<nsDOMOfflineResourceList*>(tmp->mApplicationCache.get())->Disconnect();
    NS_IMPL_CYCLE_COLLECTION_UNLINK(mApplicationCache)
  }
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mSuspendedDoc)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mIndexedDB)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentPrincipal)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mTabChild)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDoc)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleService)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mWakeLock)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleObservers)

  NS_IMPL_CYCLE_COLLECTION_UNLINK(mGamepads)

  NS_IMPL_CYCLE_COLLECTION_UNLINK(mCacheStorage)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mVRDisplays)

  // Unlink stuff from nsPIDOMWindow
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeEventHandler)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mParentTarget)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameElement)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mFocusedNode)

  NS_IMPL_CYCLE_COLLECTION_UNLINK(mMenubar)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mToolbar)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocationbar)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPersonalbar)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mStatusbar)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mScrollbars)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mCrypto)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mU2F)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsole)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mAudioWorklet)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPaintWorklet)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mExternal)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mMozSelfSupport)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mIntlUtils)

  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocShell)

  tmp->UnlinkHostObjectURIs();

  NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleRequestExecutor)
  tmp->DisableIdleCallbackRequests();

  if (tmp->IsChromeWindow()) {
    NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeFields.mBrowserDOMWindow)
    if (tmp->mChromeFields.mMessageManager) {
      static_cast<nsFrameMessageManager*>(
        tmp->mChromeFields.mMessageManager.get())->Disconnect();
      NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeFields.mMessageManager)
    }
    tmp->DisconnectAndClearGroupMessageManagers();
    NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeFields.mGroupMessageManagers)
    NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeFields.mOpenerForInitialContentBrowser)
  }

  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_UNLINK_END

#ifdef DEBUG
void
nsGlobalWindowInner::RiskyUnlink()
{
  NS_CYCLE_COLLECTION_INNERNAME.Unlink(this);
}
#endif

NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsGlobalWindowInner)
  if (tmp->mCachedXBLPrototypeHandlers) {
    for (auto iter = tmp->mCachedXBLPrototypeHandlers->Iter();
         !iter.Done();
         iter.Next()) {
      aCallbacks.Trace(&iter.Data(), "Cached XBL prototype handler", aClosure);
    }
  }
  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_TRACE_END

bool
nsGlobalWindowInner::IsBlackForCC(bool aTracingNeeded)
{
  if (!nsCCUncollectableMarker::sGeneration) {
    return false;
  }

  return (nsCCUncollectableMarker::InGeneration(GetMarkedCCGeneration()) ||
          HasKnownLiveWrapper()) &&
         (!aTracingNeeded ||
          HasNothingToTrace(static_cast<nsIDOMEventTarget*>(this)));
}

//*****************************************************************************
// nsGlobalWindowInner::nsIScriptGlobalObject
//*****************************************************************************

nsresult
nsGlobalWindowInner::EnsureScriptEnvironment()
{
  nsGlobalWindowOuter* outer = GetOuterWindowInternal();
  if (!outer) {
    NS_WARNING("No outer window available!");
    return NS_ERROR_FAILURE;
  }

  if (outer->GetWrapperPreserveColor()) {
    return NS_OK;
  }

  NS_ASSERTION(!outer->GetCurrentInnerWindowInternal(),
               "No cached wrapper, but we have an inner window?");

  // If this window is a [i]frame, don't bother GC'ing when the frame's context
  // is destroyed since a GC will happen when the frameset or host document is
  // destroyed anyway.
  nsCOMPtr<nsIScriptContext> context = new nsJSContext(!IsFrame(), outer);

  NS_ASSERTION(!outer->mContext, "Will overwrite mContext!");

  // should probably assert the context is clean???
  context->WillInitializeContext();

  nsresult rv = context->InitContext();
  NS_ENSURE_SUCCESS(rv, rv);

  outer->mContext = context;
  return NS_OK;
}

nsIScriptContext *
nsGlobalWindowInner::GetScriptContext()
{
  nsGlobalWindowOuter* outer = GetOuterWindowInternal();
  if (!outer) {
    return nullptr;
  }
  return outer->mContext;
}

JSObject *
nsGlobalWindowInner::GetGlobalJSObject()
{
  return FastGetGlobalJSObject();
}

void
nsGlobalWindowInner::TraceGlobalJSObject(JSTracer* aTrc)
{
  TraceWrapper(aTrc, "active window global");
}

bool
nsGlobalWindowInner::WouldReuseInnerWindow(nsIDocument* aNewDocument)
{
  MOZ_ASSERT(IsOuterWindow());

  // We reuse the inner window when:
  // a. We are currently at our original document.
  // b. At least one of the following conditions are true:
  // -- The new document is the same as the old document. This means that we're
  //    getting called from document.open().
  // -- The new document has the same origin as what we have loaded right now.

  if (!mDoc || !aNewDocument) {
    return false;
  }

  if (!mDoc->IsInitialDocument()) {
    return false;
  }

#ifdef DEBUG
{
  nsCOMPtr<nsIURI> uri;
  mDoc->GetDocumentURI()->CloneIgnoringRef(getter_AddRefs(uri));
  NS_ASSERTION(NS_IsAboutBlank(uri), "How'd this happen?");
}
#endif

  // Great, we're the original document, check for one of the other
  // conditions.

  if (mDoc == aNewDocument) {
    return true;
  }

  bool equal;
  if (NS_SUCCEEDED(mDoc->NodePrincipal()->Equals(aNewDocument->NodePrincipal(),
                                                 &equal)) &&
      equal) {
    // The origin is the same.
    return true;
  }

  return false;
}

void
nsGlobalWindowInner::SetInitialPrincipalToSubject()
{
  MOZ_ASSERT(IsOuterWindow());

  // First, grab the subject principal.
  nsCOMPtr<nsIPrincipal> newWindowPrincipal = nsContentUtils::SubjectPrincipalOrSystemIfNativeCaller();

  // We should never create windows with an expanded principal.
  // If we have a system principal, make sure we're not using it for a content
  // docshell.
  // NOTE: Please keep this logic in sync with nsWebShellWindow::Initialize().
  if (nsContentUtils::IsExpandedPrincipal(newWindowPrincipal) ||
      (nsContentUtils::IsSystemPrincipal(newWindowPrincipal) &&
       GetDocShell()->ItemType() != nsIDocShellTreeItem::typeChrome)) {
    newWindowPrincipal = nullptr;
  }

  // If there's an existing document, bail if it either:
  if (mDoc) {
    // (a) is not an initial about:blank document, or
    if (!mDoc->IsInitialDocument())
      return;
    // (b) already has the correct principal.
    if (mDoc->NodePrincipal() == newWindowPrincipal)
      return;

#ifdef DEBUG
    // If we have a document loaded at this point, it had better be about:blank.
    // Otherwise, something is really weird. An about:blank page has a
    // NullPrincipal.
    bool isNullPrincipal;
    MOZ_ASSERT(NS_SUCCEEDED(mDoc->NodePrincipal()->GetIsNullPrincipal(&isNullPrincipal)) &&
               isNullPrincipal);
#endif
  }

  GetDocShell()->CreateAboutBlankContentViewer(newWindowPrincipal);

  if (mDoc) {
    mDoc->SetIsInitialDocument(true);
  }

  nsCOMPtr<nsIPresShell> shell = GetDocShell()->GetPresShell();

  if (shell && !shell->DidInitialize()) {
    // Ensure that if someone plays with this document they will get
    // layout happening.
    nsRect r = shell->GetPresContext()->GetVisibleArea();
    shell->Initialize(r.Width(), r.Height());
  }
}

PopupControlState
nsGlobalWindowInner::PushPopupControlState(PopupControlState aState,
                                      bool aForce) const
{
  return ::PushPopupControlState(aState, aForce);
}

void
nsGlobalWindowInner::PopPopupControlState(PopupControlState aState) const
{
  ::PopPopupControlState(aState);
}

PopupControlState
nsGlobalWindowInner::GetPopupControlState() const
{
  MOZ_ASSERT(NS_IsMainThread());
  return gPopupControlState;
}

bool
nsGlobalWindowInner::ComputeIsSecureContext(nsIDocument* aDocument, SecureContextFlags aFlags)
{
  MOZ_CRASH("Outer Window Only - notbuilding");
}

nsresult
nsGlobalWindowInner::SetNewDocument(nsIDocument* aDocument,
                               nsISupports* aState,
                               bool aForceReuseInnerWindow)
{
  NS_PRECONDITION(mDocumentPrincipal == nullptr,
                  "mDocumentPrincipal prematurely set!");
  MOZ_ASSERT(aDocument);

  if (!mOuterWindow) {
    return NS_ERROR_NOT_INITIALIZED;
  }

  // Refuse to set a new document if the call came from an inner
  // window that's not the current inner window.
  if (mOuterWindow->GetCurrentInnerWindow() != AsInner()) {
    return NS_ERROR_NOT_AVAILABLE;
  }

  return GetOuterWindowInternal()->SetNewDocument(aDocument, aState,
                                                  aForceReuseInnerWindow);
}

void
nsGlobalWindowInner::PreloadLocalStorage()
{
  MOZ_ASSERT(IsOuterWindow());

  if (!Preferences::GetBool(kStorageEnabled)) {
    return;
  }

  if (IsChromeWindow()) {
    return;
  }

  nsIPrincipal* principal = GetPrincipal();
  if (!principal) {
    return;
  }

  nsresult rv;

  nsCOMPtr<nsIDOMStorageManager> storageManager =
    do_GetService("@mozilla.org/dom/localStorage-manager;1", &rv);
  if (NS_FAILED(rv)) {
    return;
  }

  // private browsing windows do not persist local storage to disk so we should
  // only try to precache storage when we're not a private browsing window.
  if (principal->GetPrivateBrowsingId() == 0) {
    nsCOMPtr<nsIDOMStorage> storage;
    rv = storageManager->PrecacheStorage(principal, getter_AddRefs(storage));
    if (NS_SUCCEEDED(rv)) {
      mLocalStorage = static_cast<Storage*>(storage.get());
    }
  }
}

void
nsGlobalWindowInner::DispatchDOMWindowCreated()
{
  MOZ_ASSERT(IsOuterWindow());

  if (!mDoc) {
    return;
  }

  // Fire DOMWindowCreated at chrome event listeners
  nsContentUtils::DispatchChromeEvent(mDoc, mDoc, NS_LITERAL_STRING("DOMWindowCreated"),
                                      true /* bubbles */,
                                      false /* not cancellable */);

  nsCOMPtr<nsIObserverService> observerService =
    mozilla::services::GetObserverService();

  // The event dispatching could possibly cause docshell destory, and
  // consequently cause mDoc to be set to nullptr by DropOuterWindowDocs(),
  // so check it again here.
  if (observerService && mDoc) {
    nsAutoString origin;
    nsIPrincipal* principal = mDoc->NodePrincipal();
    nsContentUtils::GetUTFOrigin(principal, origin);
    observerService->
      NotifyObservers(static_cast<nsIDOMWindow*>(this),
                      nsContentUtils::IsSystemPrincipal(principal) ?
                        "chrome-document-global-created" :
                        "content-document-global-created",
                      origin.get());
  }
}

void
nsGlobalWindowInner::ClearStatus()
{
  SetStatusOuter(EmptyString());
}

void
nsGlobalWindowInner::InnerSetNewDocument(JSContext* aCx, nsIDocument* aDocument)
{
  NS_PRECONDITION(IsInnerWindow(), "Must only be called on inner windows");
  MOZ_ASSERT(aDocument);

  if (MOZ_LOG_TEST(gDOMLeakPRLog, LogLevel::Debug)) {
    nsIURI *uri = aDocument->GetDocumentURI();
    MOZ_LOG(gDOMLeakPRLog, LogLevel::Debug,
            ("DOMWINDOW %p SetNewDocument %s",
             this, uri ? uri->GetSpecOrDefault().get() : ""));
  }

  mDoc = aDocument;
  ClearDocumentDependentSlots(aCx);
  mFocusedNode = nullptr;
  mLocalStorage = nullptr;
  mSessionStorage = nullptr;

#ifdef DEBUG
  mLastOpenedURI = aDocument->GetDocumentURI();
#endif

  Telemetry::Accumulate(Telemetry::INNERWINDOWS_WITH_MUTATION_LISTENERS,
                        mMutationBits ? 1 : 0);

  // Clear our mutation bitfield.
  mMutationBits = 0;
}

void
nsGlobalWindowInner::SetDocShell(nsIDocShell* aDocShell)
{
  MOZ_CRASH("Outer Window Only");
}

void
nsGlobalWindowInner::DetachFromDocShell()
{
  NS_ASSERTION(IsOuterWindow(), "Uh, DetachFromDocShell() called on inner window!");

  // DetachFromDocShell means the window is being torn down. Drop our
  // reference to the script context, allowing it to be deleted
  // later. Meanwhile, keep our weak reference to the script object
  // so that it can be retrieved later (until it is finalized by the JS GC).

  // Call FreeInnerObjects on all inner windows, not just the current
  // one, since some could be held by WindowStateHolder objects that
  // are GC-owned.
  RefPtr<nsGlobalWindowInner> inner;
  for (PRCList* node = PR_LIST_HEAD(this);
       node != this;
       node = PR_NEXT_LINK(inner)) {
    // This cast is safe because `node != this`. Non-this nodes are inner windows.
    inner = static_cast<nsGlobalWindowInner*>(node);
    MOZ_ASSERT(inner->IsInnerWindow());
    MOZ_ASSERT(!inner->mOuterWindow || inner->mOuterWindow == AsOuter());
    inner->FreeInnerObjects();
  }

  // Don't report that we were detached to the nsWindowMemoryReporter, as it
  // only tracks inner windows.

  NotifyWindowIDDestroyed("outer-window-destroyed");

  nsGlobalWindowInner *currentInner = GetCurrentInnerWindowInternal();

  if (currentInner) {
    NS_ASSERTION(mDoc, "Must have doc!");

    // Remember the document's principal and URI.
    mDocumentPrincipal = mDoc->NodePrincipal();
    mDocumentURI = mDoc->GetDocumentURI();
    mDocBaseURI = mDoc->GetDocBaseURI();

    // Release our document reference
    DropOuterWindowDocs();
    mFocusedNode = nullptr;
  }

  ClearControllers();

  mChromeEventHandler = nullptr; // force release now

  if (mContext) {
    // When we're about to destroy a top level content window
    // (for example a tab), we trigger a full GC by passing null as the last
    // param. We also trigger a full GC for chrome windows.
    nsJSContext::PokeGC(JS::gcreason::SET_DOC_SHELL,
                        (mTopLevelOuterContentWindow || mIsChrome) ?
                          nullptr : GetWrapperPreserveColor());
    mContext = nullptr;
  }

  mDocShell = nullptr; // Weak Reference

  NS_ASSERTION(!mNavigator, "Non-null mNavigator in outer window!");

  if (mFrames) {
    mFrames->SetDocShell(nullptr);
  }

  MaybeForgiveSpamCount();
  CleanUp();
}

void
nsGlobalWindowInner::SetOpenerWindow(nsPIDOMWindowOuter* aOpener,
                                bool aOriginalOpener)
{
  FORWARD_TO_OUTER_VOID(SetOpenerWindow, (aOpener, aOriginalOpener));

  nsWeakPtr opener = do_GetWeakReference(aOpener);
  if (opener == mOpener) {
    return;
  }

  NS_ASSERTION(!aOriginalOpener || !mSetOpenerWindowCalled,
               "aOriginalOpener is true, but not first call to "
               "SetOpenerWindow!");
  NS_ASSERTION(aOpener || !aOriginalOpener,
               "Shouldn't set mHadOriginalOpener if aOpener is null");

  mOpener = opener.forget();
  NS_ASSERTION(mOpener || !aOpener, "Opener must support weak references!");

  // Check that the js visible opener matches! We currently don't depend on this
  // being true outside of nightly, so we disable the assertion in optimized
  // release / beta builds.
  nsPIDOMWindowOuter* contentOpener = GetSanitizedOpener(aOpener);

  // contentOpener is not used when the DIAGNOSTIC_ASSERT is compiled out.
  mozilla::Unused << contentOpener;
  MOZ_DIAGNOSTIC_ASSERT(!contentOpener || !mTabGroup ||
    mTabGroup == nsGlobalWindowOuter::Cast(contentOpener)->mTabGroup);

  if (aOriginalOpener) {
    MOZ_ASSERT(!mHadOriginalOpener,
               "Probably too late to call ComputeIsSecureContext again");
    mHadOriginalOpener = true;
    mOriginalOpenerWasSecureContext =
      aOpener->GetCurrentInnerWindow()->IsSecureContext();
  }

#ifdef DEBUG
  mSetOpenerWindowCalled = true;
#endif
}

void
nsGlobalWindowInner::UpdateParentTarget()
{
  // Try to get our frame element's tab child global (its in-process message
  // manager).  If that fails, fall back to the chrome event handler's tab
  // child global, and if it doesn't have one, just use the chrome event
  // handler itself.

  nsCOMPtr<Element> frameElement = GetOuterWindow()->GetFrameElementInternal();
  nsCOMPtr<EventTarget> eventTarget =
    TryGetTabChildGlobalAsEventTarget(frameElement);

  if (!eventTarget) {
    nsGlobalWindowOuter* topWin = GetScriptableTopInternal();
    if (topWin) {
      frameElement = topWin->AsOuter()->GetFrameElementInternal();
      eventTarget = TryGetTabChildGlobalAsEventTarget(frameElement);
    }
  }

  if (!eventTarget) {
    eventTarget = TryGetTabChildGlobalAsEventTarget(mChromeEventHandler);
  }

  if (!eventTarget) {
    eventTarget = mChromeEventHandler;
  }

  mParentTarget = eventTarget;
}

EventTarget*
nsGlobalWindowInner::GetTargetForDOMEvent()
{
  return GetOuterWindowInternal();
}

EventTarget*
nsGlobalWindowInner::GetTargetForEventTargetChain()
{
  return IsInnerWindow() ? this : GetCurrentInnerWindowInternal();
}

nsresult
nsGlobalWindowInner::WillHandleEvent(EventChainPostVisitor& aVisitor)
{
  return NS_OK;
}

nsresult
nsGlobalWindowInner::GetEventTargetParent(EventChainPreVisitor& aVisitor)
{
  NS_PRECONDITION(IsInnerWindow(),
                  "GetEventTargetParent is used on outer window!?");
  EventMessage msg = aVisitor.mEvent->mMessage;

  aVisitor.mCanHandle = true;
  aVisitor.mForceContentDispatch = true; //FIXME! Bug 329119
  if (msg == eResize && aVisitor.mEvent->IsTrusted()) {
    // QIing to window so that we can keep the old behavior also in case
    // a child window is handling resize.
    nsCOMPtr<nsPIDOMWindowInner> window =
      do_QueryInterface(aVisitor.mEvent->mOriginalTarget);
    if (window) {
      mIsHandlingResizeEvent = true;
    }
  } else if (msg == eMouseDown && aVisitor.mEvent->IsTrusted()) {
    gMouseDown = true;
  } else if ((msg == eMouseUp || msg == eDragEnd) &&
             aVisitor.mEvent->IsTrusted()) {
    gMouseDown = false;
    if (gDragServiceDisabled) {
      nsCOMPtr<nsIDragService> ds =
        do_GetService("@mozilla.org/widget/dragservice;1");
      if (ds) {
        gDragServiceDisabled = false;
        ds->Unsuppress();
      }
    }
  }

  aVisitor.mParentTarget = GetParentTarget();

  // Handle 'active' event.
  if (!mIdleObservers.IsEmpty() &&
      aVisitor.mEvent->IsTrusted() &&
      (aVisitor.mEvent->HasMouseEventMessage() ||
       aVisitor.mEvent->HasDragEventMessage())) {
    mAddActiveEventFuzzTime = false;
  }

  return NS_OK;
}

bool
nsGlobalWindowInner::ShouldPromptToBlockDialogs()
{
  MOZ_ASSERT(IsOuterWindow());

  nsGlobalWindowOuter *topWindowOuter = GetScriptableTopInternal();
  if (!topWindowOuter) {
    NS_ASSERTION(!mDocShell, "ShouldPromptToBlockDialogs() called without a top window?");
    return true;
  }

  nsGlobalWindowInner* topWindow = topWindowOuter->GetCurrentInnerWindowInternal();
  if (!topWindow) {
    return true;
  }

  return topWindow->DialogsAreBeingAbused();
}

bool
nsGlobalWindowInner::AreDialogsEnabled()
{
  MOZ_ASSERT(IsOuterWindow());

  nsGlobalWindowOuter *topWindowOuter = GetScriptableTopInternal();
  if (!topWindowOuter) {
    NS_ERROR("AreDialogsEnabled() called without a top window?");
    return false;
  }

  // TODO: Warn if no top window?
  nsGlobalWindowInner* topWindow = topWindowOuter->GetCurrentInnerWindowInternal();
  if (!topWindow) {
    return false;
  }

  // Dialogs are blocked if the content viewer is hidden
  if (mDocShell) {
    nsCOMPtr<nsIContentViewer> cv;
    mDocShell->GetContentViewer(getter_AddRefs(cv));

    bool isHidden;
    cv->GetIsHidden(&isHidden);
    if (isHidden) {
      return false;
    }
  }

  // Dialogs are also blocked if the document is sandboxed with SANDBOXED_MODALS
  // (or if we have no document, of course).  Which document?  Who knows; the
  // spec is daft.  See <https://github.com/whatwg/html/issues/1206>.  For now
  // just go ahead and check mDoc, since in everything except edge cases in
  // which a frame is allow-same-origin but not allow-scripts and is being poked
  // at by some other window this should be the right thing anyway.
  if (!mDoc || (mDoc->GetSandboxFlags() & SANDBOXED_MODALS)) {
    return false;
  }

  return topWindow->mAreDialogsEnabled;
}

bool
nsGlobalWindowInner::DialogsAreBeingAbused()
{
  MOZ_ASSERT(IsInnerWindow());
  NS_ASSERTION(GetScriptableTopInternal() &&
               GetScriptableTopInternal()->GetCurrentInnerWindowInternal() == this,
               "DialogsAreBeingAbused called with invalid window");

  if (mLastDialogQuitTime.IsNull() ||
      nsContentUtils::IsCallerChrome()) {
    return false;
  }

  TimeDuration dialogInterval(TimeStamp::Now() - mLastDialogQuitTime);
  if (dialogInterval.ToSeconds() <
      Preferences::GetInt("dom.successive_dialog_time_limit",
                          DEFAULT_SUCCESSIVE_DIALOG_TIME_LIMIT)) {
    mDialogAbuseCount++;

    return GetPopupControlState() > openAllowed ||
           mDialogAbuseCount > MAX_SUCCESSIVE_DIALOG_COUNT;
  }

  // Reset the abuse counter
  mDialogAbuseCount = 0;

  return false;
}

bool
nsGlobalWindowInner::ConfirmDialogIfNeeded()
{
  MOZ_ASSERT(IsOuterWindow());

  NS_ENSURE_TRUE(mDocShell, false);
  nsCOMPtr<nsIPromptService> promptSvc =
    do_GetService("@mozilla.org/embedcomp/prompt-service;1");

  if (!promptSvc) {
    return true;
  }

  // Reset popup state while opening a modal dialog, and firing events
  // about the dialog, to prevent the current state from being active
  // the whole time a modal dialog is open.
  nsAutoPopupStatePusher popupStatePusher(openAbused, true);

  bool disableDialog = false;
  nsAutoString label, title;
  nsContentUtils::GetLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES,
                                     "ScriptDialogLabel", label);
  nsContentUtils::GetLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES,
                                     "ScriptDialogPreventTitle", title);
  promptSvc->Confirm(AsOuter(), title.get(), label.get(), &disableDialog);
  if (disableDialog) {
    DisableDialogs();
    return false;
  }

  return true;
}

void
nsGlobalWindowInner::DisableDialogs()
{
  nsGlobalWindowOuter *topWindowOuter = GetScriptableTopInternal();
  if (!topWindowOuter) {
    NS_ERROR("DisableDialogs() called without a top window?");
    return;
  }

  nsGlobalWindowInner* topWindow = topWindowOuter->GetCurrentInnerWindowInternal();
  // TODO: Warn if no top window?
  if (topWindow) {
    topWindow->mAreDialogsEnabled = false;
  }
}

void
nsGlobalWindowInner::EnableDialogs()
{
  nsGlobalWindowOuter *topWindowOuter = GetScriptableTopInternal();
  if (!topWindowOuter) {
    NS_ERROR("EnableDialogs() called without a top window?");
    return;
  }

  // TODO: Warn if no top window?
  nsGlobalWindowInner* topWindow = topWindowOuter->GetCurrentInnerWindowInternal();
  if (topWindow) {
    topWindow->mAreDialogsEnabled = true;
  }
}

nsresult
nsGlobalWindowInner::PostHandleEvent(EventChainPostVisitor& aVisitor)
{
  NS_PRECONDITION(IsInnerWindow(), "PostHandleEvent is used on outer window!?");

  // Return early if there is nothing to do.
  switch (aVisitor.mEvent->mMessage) {
    case eResize:
    case eUnload:
    case eLoad:
      break;
    default:
      return NS_OK;
  }

  /* mChromeEventHandler and mContext go dangling in the middle of this
   function under some circumstances (events that destroy the window)
   without this addref. */
  nsCOMPtr<nsIDOMEventTarget> kungFuDeathGrip1(mChromeEventHandler);
  mozilla::Unused << kungFuDeathGrip1; // These aren't referred to through the function
  nsCOMPtr<nsIScriptContext> kungFuDeathGrip2(GetContextInternal());
  mozilla::Unused << kungFuDeathGrip2; // These aren't referred to through the function


  if (aVisitor.mEvent->mMessage == eResize) {
    mIsHandlingResizeEvent = false;
  } else if (aVisitor.mEvent->mMessage == eUnload &&
             aVisitor.mEvent->IsTrusted()) {

    // If any VR display presentation is active at unload, the next page
    // will receive a vrdisplayactive event to indicate that it should
    // immediately begin vr presentation. This should occur when navigating
    // forwards, navigating backwards, and on page reload.
    for (const auto& display : mVRDisplays) {
      if (display->IsPresenting()) {
        // Save this VR display ID to trigger vrdisplayactivate event
        // after the next load event.
        nsGlobalWindowOuter* outer = GetOuterWindowInternal();
        if (outer) {
          outer->SetAutoActivateVRDisplayID(display->DisplayId());
        }

        // XXX The WebVR 1.1 spec does not define which of multiple VR
        // presenting VR displays will be chosen during navigation.
        // As the underlying platform VR API's currently only allow a single
        // VR display, it is safe to choose the first VR display for now.
        break;
      }
    }
    // Execute bindingdetached handlers before we tear ourselves
    // down.
    if (mDoc) {
      mDoc->BindingManager()->ExecuteDetachedHandlers();
    }
    mIsDocumentLoaded = false;
  } else if (aVisitor.mEvent->mMessage == eLoad &&
             aVisitor.mEvent->IsTrusted()) {
    // This is page load event since load events don't propagate to |window|.
    // @see nsDocument::GetEventTargetParent.
    mIsDocumentLoaded = true;

    mTimeoutManager->OnDocumentLoaded();

    nsCOMPtr<Element> element = GetOuterWindow()->GetFrameElementInternal();
    nsIDocShell* docShell = GetDocShell();
    if (element && GetParentInternal() &&
        docShell && docShell->ItemType() != nsIDocShellTreeItem::typeChrome) {
      // If we're not in chrome, or at a chrome boundary, fire the
      // onload event for the frame element.

      nsEventStatus status = nsEventStatus_eIgnore;
      WidgetEvent event(aVisitor.mEvent->IsTrusted(), eLoad);
      event.mFlags.mBubbles = false;
      event.mFlags.mCancelable = false;

      // Most of the time we could get a pres context to pass in here,
      // but not always (i.e. if this window is not shown there won't
      // be a pres context available). Since we're not firing a GUI
      // event we don't need a pres context anyway so we just pass
      // null as the pres context all the time here.
      EventDispatcher::Dispatch(element, nullptr, &event, nullptr, &status);
    }

    uint32_t autoActivateVRDisplayID = 0;
    nsGlobalWindowOuter* outer = GetOuterWindowInternal();
    if (outer) {
      autoActivateVRDisplayID = outer->GetAutoActivateVRDisplayID();
    }
    if (autoActivateVRDisplayID) {
      DispatchVRDisplayActivate(autoActivateVRDisplayID,
                                VRDisplayEventReason::Navigation);
    }
  }

  return NS_OK;
}

void
nsGlobalWindowInner::PoisonOuterWindowProxy(JSObject *aObject)
{
  MOZ_ASSERT(IsOuterWindow());
  if (aObject == GetWrapperMaybeDead()) {
    PoisonWrapper();
  }
}

nsresult
nsGlobalWindowInner::SetArguments(nsIArray *aArguments)
{
  MOZ_ASSERT(IsOuterWindow());
  nsresult rv;

  // Historically, we've used the same machinery to handle openDialog arguments
  // (exposed via window.arguments) and showModalDialog arguments (exposed via
  // window.dialogArguments), even though the former is XUL-only and uses an XPCOM
  // array while the latter is web-exposed and uses an arbitrary JS value.
  // Moreover, per-spec |dialogArguments| is a property of the browsing context
  // (outer), whereas |arguments| lives on the inner.
  //
  // We've now mostly separated them, but the difference is still opaque to
  // nsWindowWatcher (the caller of SetArguments in this little back-and-forth
  // embedding waltz we do here).
  //
  // So we need to demultiplex the two cases here.
  nsGlobalWindowInner *currentInner = GetCurrentInnerWindowInternal();

  mArguments = aArguments;
  rv = currentInner->DefineArgumentsProperty(aArguments);
  NS_ENSURE_SUCCESS(rv, rv);

  return NS_OK;
}

nsresult
nsGlobalWindowInner::DefineArgumentsProperty(nsIArray *aArguments)
{
  MOZ_ASSERT(IsInnerWindow());

  nsIScriptContext *ctx = GetOuterWindowInternal()->mContext;
  NS_ENSURE_TRUE(aArguments && ctx, NS_ERROR_NOT_INITIALIZED);

  JS::Rooted<JSObject*> obj(RootingCx(), GetWrapperPreserveColor());
  return ctx->SetProperty(obj, "arguments", aArguments);
}

//*****************************************************************************
// nsGlobalWindowInner::nsIScriptObjectPrincipal
//*****************************************************************************

nsIPrincipal*
nsGlobalWindowInner::GetPrincipal()
{
  if (mDoc) {
    // If we have a document, get the principal from the document
    return mDoc->NodePrincipal();
  }

  if (mDocumentPrincipal) {
    return mDocumentPrincipal;
  }

  // If we don't have a principal and we don't have a document we
  // ask the parent window for the principal. This can happen when
  // loading a frameset that has a <frame src="javascript:xxx">, in
  // that case the global window is used in JS before we've loaded
  // a document into the window.

  nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
    do_QueryInterface(GetParentInternal());

  if (objPrincipal) {
    return objPrincipal->GetPrincipal();
  }

  return nullptr;
}

//*****************************************************************************
// nsGlobalWindowInner::nsIDOMWindow
//*****************************************************************************

bool
nsPIDOMWindowInner::AddAudioContext(AudioContext* aAudioContext)
{
  MOZ_ASSERT(IsInnerWindow());

  mAudioContexts.AppendElement(aAudioContext);

  // Return true if the context should be muted and false if not.
  nsIDocShell* docShell = GetDocShell();
  return docShell && !docShell->GetAllowMedia() && !aAudioContext->IsOffline();
}

void
nsPIDOMWindowInner::RemoveAudioContext(AudioContext* aAudioContext)
{
  MOZ_ASSERT(IsInnerWindow());

  mAudioContexts.RemoveElement(aAudioContext);
}

void
nsPIDOMWindowInner::MuteAudioContexts()
{
  MOZ_ASSERT(IsInnerWindow());

  for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
    if (!mAudioContexts[i]->IsOffline()) {
      mAudioContexts[i]->Mute();
    }
  }
}

void
nsPIDOMWindowInner::UnmuteAudioContexts()
{
  MOZ_ASSERT(IsInnerWindow());

  for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
    if (!mAudioContexts[i]->IsOffline()) {
      mAudioContexts[i]->Unmute();
    }
  }
}

nsGlobalWindowInner*
nsGlobalWindowInner::Window()
{
  return AssertInner();
}

nsGlobalWindowInner*
nsGlobalWindowInner::Self()
{
  return AssertInner();
}

Navigator*
nsGlobalWindowInner::Navigator()
{
  MOZ_RELEASE_ASSERT(IsInnerWindow());

  if (!mNavigator) {
    mNavigator = new mozilla::dom::Navigator(AsInner());
  }

  return mNavigator;
}

nsIDOMNavigator*
nsGlobalWindowInner::GetNavigator()
{
  FORWARD_TO_INNER(GetNavigator, (), nullptr);

  return Navigator();
}

nsScreen*
nsGlobalWindowInner::GetScreen(ErrorResult& aError)
{
  MOZ_RELEASE_ASSERT(IsInnerWindow());

  if (!mScreen) {
    mScreen = nsScreen::Create(AsInner());
    if (!mScreen) {
      aError.Throw(NS_ERROR_UNEXPECTED);
      return nullptr;
    }
  }

  return mScreen;
}

nsIDOMScreen*
nsGlobalWindowInner::GetScreen()
{
  FORWARD_TO_INNER(GetScreen, (), nullptr);

  ErrorResult dummy;
  nsIDOMScreen* screen = GetScreen(dummy);
  dummy.SuppressException();
  return screen;
}

nsHistory*
nsGlobalWindowInner::GetHistory(ErrorResult& aError)
{
  MOZ_RELEASE_ASSERT(IsInnerWindow());

  if (!mHistory) {
    mHistory = new nsHistory(AsInner());
  }

  return mHistory;
}

CustomElementRegistry*
nsGlobalWindowInner::CustomElements()
{
  MOZ_RELEASE_ASSERT(IsInnerWindow());

  if (!mCustomElements) {
    mCustomElements = new CustomElementRegistry(AsInner());
  }

  return mCustomElements;
}

Performance*
nsPIDOMWindowInner::GetPerformance()
{
  MOZ_ASSERT(IsInnerWindow());
  CreatePerformanceObjectIfNeeded();
  return mPerformance;
}

Performance*
nsGlobalWindowInner::GetPerformance()
{
  return AsInner()->GetPerformance();
}

void
nsPIDOMWindowInner::CreatePerformanceObjectIfNeeded()
{
  MOZ_ASSERT(IsInnerWindow());

  if (mPerformance || !mDoc) {
    return;
  }
  RefPtr<nsDOMNavigationTiming> timing = mDoc->GetNavigationTiming();
  nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(mDoc->GetChannel()));
  bool timingEnabled = false;
  if (!timedChannel ||
      !NS_SUCCEEDED(timedChannel->GetTimingEnabled(&timingEnabled)) ||
      !timingEnabled) {
    timedChannel = nullptr;
  }
  if (timing) {
    mPerformance = Performance::CreateForMainThread(this, timing, timedChannel);
  }
}

bool
nsPIDOMWindowInner::IsSecureContext() const
{
  return nsGlobalWindowInner::Cast(this)->IsSecureContext();
}

bool
nsPIDOMWindowInner::IsSecureContextIfOpenerIgnored() const
{
  return nsGlobalWindowInner::Cast(this)->IsSecureContextIfOpenerIgnored();
}

void
nsPIDOMWindowInner::Suspend()
{
  nsGlobalWindowInner::Cast(this)->Suspend();
}

void
nsPIDOMWindowInner::Resume()
{
  nsGlobalWindowInner::Cast(this)->Resume();
}

void
nsPIDOMWindowInner::Freeze()
{
  nsGlobalWindowInner::Cast(this)->Freeze();
}

void
nsPIDOMWindowInner::Thaw()
{
  nsGlobalWindowInner::Cast(this)->Thaw();
}

void
nsPIDOMWindowInner::SyncStateFromParentWindow()
{
  nsGlobalWindowInner::Cast(this)->SyncStateFromParentWindow();
}

void
nsGlobalWindowInner::UpdateTopInnerWindow()
{
  if (!IsInnerWindow() || AsInner()->IsTopInnerWindow() || !mTopInnerWindow) {
    return;
  }

  mTopInnerWindow->UpdateWebSocketCount(-(int32_t)mNumOfOpenWebSockets);
}

void
nsPIDOMWindowInner::AddPeerConnection()
{
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(IsInnerWindow());
  mTopInnerWindow ? mTopInnerWindow->mActivePeerConnections++
                  : mActivePeerConnections++;
}

void
nsPIDOMWindowInner::RemovePeerConnection()
{
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(IsInnerWindow());
  MOZ_ASSERT(mTopInnerWindow ? mTopInnerWindow->mActivePeerConnections
                             : mActivePeerConnections);

  mTopInnerWindow ? mTopInnerWindow->mActivePeerConnections--
                  : mActivePeerConnections--;
}

bool
nsPIDOMWindowInner::HasActivePeerConnections()
{
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(IsInnerWindow());
  return mTopInnerWindow ? mTopInnerWindow->mActivePeerConnections
                         : mActivePeerConnections;
}

bool
nsPIDOMWindowInner::IsPlayingAudio()
{
  for (uint32_t i = 0; i < mAudioContexts.Length(); i++) {
    if (mAudioContexts[i]->IsRunning()) {
      return true;
    }
  }
  RefPtr<AudioChannelService> acs = AudioChannelService::Get();
  if (!acs) {
    return false;
  }
  auto outer = GetOuterWindow();
  if (!outer) {
    // We've been unlinked and are about to die.  Not a good time to pretend to
    // be playing audio.
    return false;
  }
  return acs->IsWindowActive(outer);
}

bool
nsPIDOMWindowInner::IsDocumentLoaded() const
{
  return mIsDocumentLoaded;
}

mozilla::dom::TimeoutManager&
nsPIDOMWindowInner::TimeoutManager()
{
  return *mTimeoutManager;
}

bool
nsPIDOMWindowInner::IsRunningTimeout()
{
  return TimeoutManager().IsRunningTimeout();
}

void
nsPIDOMWindowInner::TryToCacheTopInnerWindow()
{
  if (mHasTriedToCacheTopInnerWindow) {
    return;
  }

  MOZ_ASSERT(!mInnerObjectsFreed);

  mHasTriedToCacheTopInnerWindow = true;

  nsGlobalWindowInner* window = nsGlobalWindowInner::Cast(AsInner());

  MOZ_ASSERT(window);

  if (nsCOMPtr<nsPIDOMWindowOuter> topOutter = window->GetScriptableTop()) {
    mTopInnerWindow = topOutter->GetCurrentInnerWindow();
  }
}

void
nsPIDOMWindowInner::UpdateActiveIndexedDBTransactionCount(int32_t aDelta)
{
  MOZ_ASSERT(NS_IsMainThread());

  if (aDelta == 0) {
    return;
  }

  TabGroup()->IndexedDBTransactionCounter() += aDelta;
}

void
nsPIDOMWindowInner::UpdateActiveIndexedDBDatabaseCount(int32_t aDelta)
{
  MOZ_ASSERT(NS_IsMainThread());

  if (aDelta == 0) {
    return;
  }

  // We count databases but not transactions because only active databases
  // could block throttling.
  uint32_t& counter = mTopInnerWindow ?
    mTopInnerWindow->mNumOfIndexedDBDatabases : mNumOfIndexedDBDatabases;

  counter+= aDelta;

  TabGroup()->IndexedDBDatabaseCounter() += aDelta;
}

bool
nsPIDOMWindowInner::HasActiveIndexedDBDatabases()
{
  MOZ_ASSERT(NS_IsMainThread());

  return mTopInnerWindow ?
    mTopInnerWindow->mNumOfIndexedDBDatabases > 0 :
    mNumOfIndexedDBDatabases > 0;
}

void
nsPIDOMWindowInner::UpdateWebSocketCount(int32_t aDelta)
{
  MOZ_ASSERT(NS_IsMainThread());

  if (aDelta == 0) {
    return;
  }

  if (mTopInnerWindow && !IsTopInnerWindow()) {
    mTopInnerWindow->UpdateWebSocketCount(aDelta);
  }

  MOZ_DIAGNOSTIC_ASSERT(
    aDelta > 0 || ((aDelta + mNumOfOpenWebSockets) < mNumOfOpenWebSockets));

  mNumOfOpenWebSockets += aDelta;
}

bool
nsPIDOMWindowInner::HasOpenWebSockets() const
{
  MOZ_ASSERT(NS_IsMainThread());

  return mNumOfOpenWebSockets ||
         (mTopInnerWindow && mTopInnerWindow->mNumOfOpenWebSockets);
}

bool
nsPIDOMWindowInner::GetAudioCaptured() const
{
  MOZ_ASSERT(IsInnerWindow());
  return mAudioCaptured;
}

nsresult
nsPIDOMWindowInner::SetAudioCapture(bool aCapture)
{
  MOZ_ASSERT(IsInnerWindow());

  mAudioCaptured = aCapture;

  RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
  if (service) {
    service->SetWindowAudioCaptured(GetOuterWindow(), mWindowID, aCapture);
  }

  return NS_OK;
}

// nsISpeechSynthesisGetter

#ifdef MOZ_WEBSPEECH
SpeechSynthesis*
nsGlobalWindowInner::GetSpeechSynthesis(ErrorResult& aError)
{
  MOZ_RELEASE_ASSERT(IsInnerWindow());

  if (!mSpeechSynthesis) {
    mSpeechSynthesis = new SpeechSynthesis(AsInner());
  }

  return mSpeechSynthesis;
}

bool
nsGlobalWindowInner::HasActiveSpeechSynthesis()
{
  MOZ_ASSERT(IsInnerWindow());

  if (mSpeechSynthesis) {
    return !mSpeechSynthesis->HasEmptyQueue();
  }

  return false;
}

#endif

already_AddRefed<nsPIDOMWindowOuter>
nsGlobalWindowInner::GetParentOuter()
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());

  if (!mDocShell) {
    return nullptr;
  }

  nsCOMPtr<nsPIDOMWindowOuter> parent;
  if (mDocShell->GetIsMozBrowser()) {
    parent = AsOuter();
  } else {
    parent = GetParent();
  }

  return parent.forget();
}

already_AddRefed<nsPIDOMWindowOuter>
nsGlobalWindowInner::GetParent(ErrorResult& aError)
{
  FORWARD_TO_OUTER_OR_THROW(GetParentOuter, (), aError, nullptr);
}

/**
 * GetScriptableParent is called when script reads window.parent.
 *
 * In contrast to GetRealParent, GetScriptableParent respects <iframe
 * mozbrowser> boundaries, so if |this| is contained by an <iframe
 * mozbrowser>, we will return |this| as its own parent.
 */
nsPIDOMWindowOuter*
nsGlobalWindowInner::GetScriptableParent()
{
  FORWARD_TO_OUTER(GetScriptableParent, (), nullptr);

  nsCOMPtr<nsPIDOMWindowOuter> parent = GetParentOuter();
  return parent.get();
}

/**
 * Behavies identically to GetScriptableParent extept that it returns null
 * if GetScriptableParent would return this window.
 */
nsPIDOMWindowOuter*
nsGlobalWindowInner::GetScriptableParentOrNull()
{
  FORWARD_TO_OUTER(GetScriptableParentOrNull, (), nullptr);

  MOZ_CRASH("Should be in outer by now");
}

/**
 * nsPIDOMWindow::GetParent (when called from C++) is just a wrapper around
 * GetRealParent.
 */
already_AddRefed<nsPIDOMWindowOuter>
nsGlobalWindowInner::GetParent()
{
  MOZ_ASSERT(IsOuterWindow());

  if (!mDocShell) {
    return nullptr;
  }

  nsCOMPtr<nsIDocShell> parent;
  mDocShell->GetSameTypeParentIgnoreBrowserBoundaries(getter_AddRefs(parent));

  if (parent) {
    nsCOMPtr<nsPIDOMWindowOuter> win = parent->GetWindow();
    return win.forget();
  }

  nsCOMPtr<nsPIDOMWindowOuter> win(AsOuter());
  return win.forget();
}

/**
 * GetScriptableTop is called when script reads window.top.
 *
 * In contrast to GetRealTop, GetScriptableTop respects <iframe mozbrowser>
 * boundaries.  If we encounter a window owned by an <iframe mozbrowser> while
 * walking up the window hierarchy, we'll stop and return that window.
 */
nsPIDOMWindowOuter*
nsGlobalWindowInner::GetScriptableTop()
{
  FORWARD_TO_OUTER(GetScriptableTop, (), nullptr);
  MOZ_CRASH("Outer window expected!");
}

already_AddRefed<nsPIDOMWindowOuter>
nsGlobalWindowInner::GetTop()
{
  MOZ_CRASH("Outer window only");
}

void
nsGlobalWindowInner::GetContentOuter(JSContext* aCx,
                                JS::MutableHandle<JSObject*> aRetval,
                                CallerType aCallerType,
                                ErrorResult& aError)
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());

  nsCOMPtr<nsPIDOMWindowOuter> content =
    GetContentInternal(aError, aCallerType);
  if (aError.Failed()) {
    return;
  }

  if (content) {
    JS::Rooted<JS::Value> val(aCx);
    aError = nsContentUtils::WrapNative(aCx, content, &val);
    if (aError.Failed()) {
      return;
    }

    aRetval.set(&val.toObject());
    return;
  }

  aRetval.set(nullptr);
}

void
nsGlobalWindowInner::GetContent(JSContext* aCx,
                           JS::MutableHandle<JSObject*> aRetval,
                           CallerType aCallerType,
                           ErrorResult& aError)
{
  FORWARD_TO_OUTER_OR_THROW(GetContentOuter,
                            (aCx, aRetval, aCallerType, aError), aError, );
}

already_AddRefed<nsPIDOMWindowOuter>
nsGlobalWindowInner::GetContentInternal(ErrorResult& aError, CallerType aCallerType)
{
  MOZ_ASSERT(IsOuterWindow());

  // First check for a named frame named "content"
  nsCOMPtr<nsPIDOMWindowOuter> domWindow =
    GetChildWindow(NS_LITERAL_STRING("content"));
  if (domWindow) {
    return domWindow.forget();
  }

  // If we're contained in <iframe mozbrowser>, then GetContent is the same as
  // window.top.
  if (mDocShell && mDocShell->GetIsInMozBrowser()) {
    return GetTopOuter();
  }

  nsCOMPtr<nsIDocShellTreeItem> primaryContent;
  if (aCallerType != CallerType::System) {
    if (mDoc) {
      mDoc->WarnOnceAbout(nsIDocument::eWindowContentUntrusted);
    }
    // If we're called by non-chrome code, make sure we don't return
    // the primary content window if the calling tab is hidden. In
    // such a case we return the same-type root in the hidden tab,
    // which is "good enough", for now.
    nsCOMPtr<nsIBaseWindow> baseWin(do_QueryInterface(mDocShell));

    if (baseWin) {
      bool visible = false;
      baseWin->GetVisibility(&visible);

      if (!visible) {
        mDocShell->GetSameTypeRootTreeItem(getter_AddRefs(primaryContent));
      }
    }
  }

  if (!primaryContent) {
    nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner();
    if (!treeOwner) {
      aError.Throw(NS_ERROR_FAILURE);
      return nullptr;
    }

    treeOwner->GetPrimaryContentShell(getter_AddRefs(primaryContent));
  }

  if (!primaryContent) {
    return nullptr;
  }

  domWindow = primaryContent->GetWindow();
  return domWindow.forget();
}

MozSelfSupport*
nsGlobalWindowInner::GetMozSelfSupport(ErrorResult& aError)
{
  MOZ_ASSERT(IsInnerWindow());

  if (mMozSelfSupport) {
    return mMozSelfSupport;
  }

  // We're called from JS and want to use out existing JSContext (and,
  // importantly, its compartment!) here.
  AutoJSContext cx;
  GlobalObject global(cx, FastGetGlobalJSObject());
  mMozSelfSupport = MozSelfSupport::Constructor(global, cx, aError);
  return mMozSelfSupport;
}

nsresult
nsGlobalWindowInner::GetPrompter(nsIPrompt** aPrompt)
{
  if (IsInnerWindow()) {
    nsGlobalWindowOuter* outer = GetOuterWindowInternal();
    if (!outer) {
      NS_WARNING("No outer window available!");
      return NS_ERROR_NOT_INITIALIZED;
    }
    return outer->GetPrompter(aPrompt);
  }

  if (!mDocShell)
    return NS_ERROR_FAILURE;

  nsCOMPtr<nsIPrompt> prompter(do_GetInterface(mDocShell));
  NS_ENSURE_TRUE(prompter, NS_ERROR_NO_INTERFACE);

  prompter.forget(aPrompt);
  return NS_OK;
}

BarProp*
nsGlobalWindowInner::GetMenubar(ErrorResult& aError)
{
  MOZ_RELEASE_ASSERT(IsInnerWindow());

  if (!mMenubar) {
    mMenubar = new MenubarProp(AssertInner());
  }

  return mMenubar;
}

BarProp*
nsGlobalWindowInner::GetToolbar(ErrorResult& aError)
{
  MOZ_RELEASE_ASSERT(IsInnerWindow());

  if (!mToolbar) {
    mToolbar = new ToolbarProp(AssertInner());
  }

  return mToolbar;
}

BarProp*
nsGlobalWindowInner::GetLocationbar(ErrorResult& aError)
{
  MOZ_RELEASE_ASSERT(IsInnerWindow());

  if (!mLocationbar) {
    mLocationbar = new LocationbarProp(AssertInner());
  }
  return mLocationbar;
}

BarProp*
nsGlobalWindowInner::GetPersonalbar(ErrorResult& aError)
{
  MOZ_RELEASE_ASSERT(IsInnerWindow());

  if (!mPersonalbar) {
    mPersonalbar = new PersonalbarProp(AssertInner());
  }
  return mPersonalbar;
}

BarProp*
nsGlobalWindowInner::GetStatusbar(ErrorResult& aError)
{
  MOZ_RELEASE_ASSERT(IsInnerWindow());

  if (!mStatusbar) {
    mStatusbar = new StatusbarProp(AssertInner());
  }
  return mStatusbar;
}

BarProp*
nsGlobalWindowInner::GetScrollbars(ErrorResult& aError)
{
  MOZ_RELEASE_ASSERT(IsInnerWindow());

  if (!mScrollbars) {
    mScrollbars = new ScrollbarsProp(AssertInner());
  }

  return mScrollbars;
}

bool
nsGlobalWindowInner::GetClosedOuter()
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());

  // If someone called close(), or if we don't have a docshell, we're closed.
  return mIsClosed || !mDocShell;
}

bool
nsGlobalWindowInner::GetClosed(ErrorResult& aError)
{
  FORWARD_TO_OUTER_OR_THROW(GetClosedOuter, (), aError, false);
}

bool
nsGlobalWindowInner::Closed()
{
  MOZ_ASSERT(IsOuterWindow());

  return GetClosedOuter();
}

nsDOMWindowList*
nsGlobalWindowInner::GetWindowList()
{
  MOZ_ASSERT(IsOuterWindow());

  if (!mFrames && mDocShell) {
    mFrames = new nsDOMWindowList(mDocShell);
  }

  return mFrames;
}

already_AddRefed<nsIDOMWindowCollection>
nsGlobalWindowInner::GetFrames()
{
  FORWARD_TO_OUTER(GetFrames, (), nullptr);

  nsCOMPtr<nsIDOMWindowCollection> frames = GetWindowList();
  return frames.forget();
}

already_AddRefed<nsPIDOMWindowOuter>
nsGlobalWindowInner::IndexedGetterOuter(uint32_t aIndex)
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());

  nsDOMWindowList* windows = GetWindowList();
  NS_ENSURE_TRUE(windows, nullptr);

  return windows->IndexedGetter(aIndex);
}

already_AddRefed<nsPIDOMWindowOuter>
nsGlobalWindowInner::IndexedGetter(uint32_t aIndex)
{
  FORWARD_TO_OUTER(IndexedGetterOuter, (aIndex), nullptr);
  MOZ_CRASH();
}

bool
nsGlobalWindowInner::DoResolve(JSContext* aCx, JS::Handle<JSObject*> aObj,
                          JS::Handle<jsid> aId,
                          JS::MutableHandle<JS::PropertyDescriptor> aDesc)
{
  MOZ_ASSERT(IsInnerWindow());

  // Note: Keep this in sync with MayResolve.

  // Note: The infallibleInit call in GlobalResolve depends on this check.
  if (!JSID_IS_STRING(aId)) {
    return true;
  }

  bool found;
  if (!WebIDLGlobalNameHash::DefineIfEnabled(aCx, aObj, aId, aDesc, &found)) {
    return false;
  }

  if (found) {
    return true;
  }

  nsresult rv = nsWindowSH::GlobalResolve(AssertInner(), aCx, aObj, aId, aDesc);
  if (NS_FAILED(rv)) {
    return Throw(aCx, rv);
  }

  return true;
}

/* static */
bool
nsGlobalWindowInner::MayResolve(jsid aId)
{
  // Note: This function does not fail and may not have any side-effects.
  // Note: Keep this in sync with DoResolve.
  if (!JSID_IS_STRING(aId)) {
    return false;
  }

  if (aId == XPCJSRuntime::Get()->GetStringID(XPCJSContext::IDX_COMPONENTS)) {
    return true;
  }

  if (aId == XPCJSRuntime::Get()->GetStringID(XPCJSContext::IDX_CONTROLLERS) ||
      aId == XPCJSRuntime::Get()->GetStringID(XPCJSContext::IDX_CONTROLLERS_CLASS)) {
    // We only resolve .controllers/.Controllers in release builds and on non-chrome
    // windows, but let's not worry about any of that stuff.
    return true;
  }

  if (WebIDLGlobalNameHash::MayResolve(aId)) {
    return true;
  }

  nsScriptNameSpaceManager *nameSpaceManager = PeekNameSpaceManager();
  if (!nameSpaceManager) {
    // Really shouldn't happen.  Fail safe.
    return true;
  }

  nsAutoString name;
  AssignJSFlatString(name, JSID_TO_FLAT_STRING(aId));

  return nameSpaceManager->LookupName(name);
}

void
nsGlobalWindowInner::GetOwnPropertyNames(JSContext* aCx, JS::AutoIdVector& aNames,
                                    bool aEnumerableOnly, ErrorResult& aRv)
{
  if (aEnumerableOnly) {
    // The names we would return from here get defined on the window via one of
    // two codepaths.  The ones coming from the WebIDLGlobalNameHash will end up
    // in the DefineConstructor function in BindingUtils, which always defines
    // things as non-enumerable.  The ones coming from the script namespace
    // manager get defined by nsDOMClassInfo::PostCreatePrototype calling
    // ResolvePrototype and using the resulting descriptot to define the
    // property.  ResolvePrototype always passes 0 to the FillPropertyDescriptor
    // for the property attributes, so all those are non-enumerable as well.
    //
    // So in the aEnumerableOnly case we have nothing to do.
    return;
  }

  MOZ_ASSERT(IsInnerWindow());
  // "Components" is marked as enumerable but only resolved on demand :-/.
  //aNames.AppendElement(NS_LITERAL_STRING("Components"));

  nsScriptNameSpaceManager* nameSpaceManager = GetNameSpaceManager();
  if (nameSpaceManager) {
    JS::Rooted<JSObject*> wrapper(aCx, GetWrapper());

    // There are actually two ways we can get called here: For normal
    // enumeration or for Xray enumeration.  In the latter case, we want to
    // return all possible WebIDL names, because we don't really support
    // deleting these names off our Xray; trying to resolve them will just make
    // them come back.  In the former case, we want to avoid returning deleted
    // names.  But the JS engine already knows about the non-deleted
    // already-resolved names, so we can just return the so-far-unresolved ones.
    //
    // We can tell which case we're in by whether aCx is in our wrapper's
    // compartment.  If not, we're in the Xray case.
    WebIDLGlobalNameHash::NameType nameType =
      js::IsObjectInContextCompartment(wrapper, aCx) ?
        WebIDLGlobalNameHash::UnresolvedNamesOnly :
        WebIDLGlobalNameHash::AllNames;
    if (!WebIDLGlobalNameHash::GetNames(aCx, wrapper, nameType, aNames)) {
      aRv.NoteJSContextException(aCx);
    }

    for (auto i = nameSpaceManager->GlobalNameIter(); !i.Done(); i.Next()) {
      const GlobalNameMapEntry* entry = i.Get();
      if (nsWindowSH::NameStructEnabled(aCx, AssertInner(), entry->mKey,
                                        entry->mGlobalName)) {
        // Just append all of these; even if they get deleted our resolve hook
        // just goes ahead and recreates them.
        JSString* str = JS_AtomizeUCStringN(aCx,
                                            entry->mKey.BeginReading(),
                                            entry->mKey.Length());
        if (!str || !aNames.append(NON_INTEGER_ATOM_TO_JSID(str))) {
          aRv.NoteJSContextException(aCx);
          return;
        }
      }
    }
  }
}

/* static */ bool
nsGlobalWindowInner::IsPrivilegedChromeWindow(JSContext* aCx, JSObject* aObj)
{
  // For now, have to deal with XPConnect objects here.
  return xpc::WindowOrNull(aObj)->IsChromeWindow() &&
         nsContentUtils::ObjectPrincipal(aObj) == nsContentUtils::GetSystemPrincipal();
}

/* static */ bool
nsGlobalWindowInner::IsRequestIdleCallbackEnabled(JSContext* aCx, JSObject* aObj)
{
  // The requestIdleCallback should always be enabled for system code.
  return nsContentUtils::RequestIdleCallbackEnabled() ||
         nsContentUtils::IsSystemCaller(aCx);
}

nsIDOMOfflineResourceList*
nsGlobalWindowInner::GetApplicationCache(ErrorResult& aError)
{
  MOZ_RELEASE_ASSERT(IsInnerWindow());

  if (!mApplicationCache) {
    nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(GetDocShell()));
    if (!webNav || !mDoc) {
      aError.Throw(NS_ERROR_FAILURE);
      return nullptr;
    }

    nsCOMPtr<nsIURI> uri;
    aError = webNav->GetCurrentURI(getter_AddRefs(uri));
    if (aError.Failed()) {
      return nullptr;
    }

    nsCOMPtr<nsIURI> manifestURI;
    nsContentUtils::GetOfflineAppManifest(mDoc, getter_AddRefs(manifestURI));

    RefPtr<nsDOMOfflineResourceList> applicationCache =
      new nsDOMOfflineResourceList(manifestURI, uri, mDoc->NodePrincipal(),
                                   AsInner());

    applicationCache->Init();

    mApplicationCache = applicationCache;
  }

  return mApplicationCache;
}

already_AddRefed<nsIDOMOfflineResourceList>
nsGlobalWindowInner::GetApplicationCache()
{
  FORWARD_TO_INNER(GetApplicationCache, (), nullptr);

  ErrorResult dummy;
  nsCOMPtr<nsIDOMOfflineResourceList> applicationCache =
    GetApplicationCache(dummy);
  dummy.SuppressException();
  return applicationCache.forget();
}

Crypto*
nsGlobalWindowInner::GetCrypto(ErrorResult& aError)
{
  MOZ_RELEASE_ASSERT(IsInnerWindow());

  if (!mCrypto) {
    mCrypto = new Crypto();
    mCrypto->Init(this);
  }
  return mCrypto;
}

mozilla::dom::U2F*
nsGlobalWindowInner::GetU2f(ErrorResult& aError)
{
  MOZ_RELEASE_ASSERT(IsInnerWindow());

  if (!mU2F) {
    RefPtr<U2F> u2f = new U2F(AsInner());
    u2f->Init(aError);
    if (NS_WARN_IF(aError.Failed())) {
      return nullptr;
    }

    mU2F = u2f;
  }
  return mU2F;
}

nsIControllers*
nsGlobalWindowInner::GetControllersOuter(ErrorResult& aError)
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());

  if (!mControllers) {
    nsresult rv;
    mControllers = do_CreateInstance(kXULControllersCID, &rv);
    if (NS_FAILED(rv)) {
      aError.Throw(rv);
      return nullptr;
    }

    // Add in the default controller
    nsCOMPtr<nsIController> controller = do_CreateInstance(
                               NS_WINDOWCONTROLLER_CONTRACTID, &rv);
    if (NS_FAILED(rv)) {
      aError.Throw(rv);
      return nullptr;
    }

    mControllers->InsertControllerAt(0, controller);
    nsCOMPtr<nsIControllerContext> controllerContext = do_QueryInterface(controller);
    if (!controllerContext) {
      aError.Throw(NS_ERROR_FAILURE);
      return nullptr;
    }

    controllerContext->SetCommandContext(static_cast<nsIDOMWindow*>(this));
  }

  return mControllers;
}

nsIControllers*
nsGlobalWindowInner::GetControllers(ErrorResult& aError)
{
  FORWARD_TO_OUTER_OR_THROW(GetControllersOuter, (aError), aError, nullptr);
}

nsresult
nsGlobalWindowInner::GetControllers(nsIControllers** aResult)
{
  FORWARD_TO_INNER(GetControllers, (aResult), NS_ERROR_UNEXPECTED);

  ErrorResult rv;
  nsCOMPtr<nsIControllers> controllers = GetControllers(rv);
  controllers.forget(aResult);

  return rv.StealNSResult();
}

nsPIDOMWindowOuter*
nsGlobalWindowInner::GetSanitizedOpener(nsPIDOMWindowOuter* aOpener)
{
  if (!aOpener) {
    return nullptr;
  }

  nsGlobalWindowOuter* win = nsGlobalWindowOuter::Cast(aOpener);

  // First, ensure that we're not handing back a chrome window to content:
  if (win->IsChromeWindow()) {
    return nullptr;
  }

  // We don't want to reveal the opener if the opener is a mail window,
  // because opener can be used to spoof the contents of a message (bug 105050).
  // So, we look in the opener's root docshell to see if it's a mail window.
  nsCOMPtr<nsIDocShell> openerDocShell = aOpener->GetDocShell();

  if (openerDocShell) {
    nsCOMPtr<nsIDocShellTreeItem> openerRootItem;
    openerDocShell->GetRootTreeItem(getter_AddRefs(openerRootItem));
    nsCOMPtr<nsIDocShell> openerRootDocShell(do_QueryInterface(openerRootItem));
    if (openerRootDocShell) {
      uint32_t appType;
      nsresult rv = openerRootDocShell->GetAppType(&appType);
      if (NS_SUCCEEDED(rv) && appType != nsIDocShell::APP_TYPE_MAIL) {
        return aOpener;
      }
    }
  }

  return nullptr;
}

nsPIDOMWindowOuter*
nsGlobalWindowInner::GetOpenerWindowOuter()
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());

  nsCOMPtr<nsPIDOMWindowOuter> opener = do_QueryReferent(mOpener);

  if (!opener) {
    return nullptr;
  }

  // First, check if we were called from a privileged chrome script
  if (nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
    // Catch the case where we're chrome but the opener is not...
    if (GetPrincipal() == nsContentUtils::GetSystemPrincipal() &&
        nsGlobalWindowOuter::Cast(opener)->GetPrincipal() != nsContentUtils::GetSystemPrincipal()) {
      return nullptr;
    }
    return opener;
  }

  return GetSanitizedOpener(opener);
}

nsPIDOMWindowOuter*
nsGlobalWindowInner::GetOpenerWindow(ErrorResult& aError)
{
  FORWARD_TO_OUTER_OR_THROW(GetOpenerWindowOuter, (), aError, nullptr);
}

void
nsGlobalWindowInner::GetOpener(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval,
                          ErrorResult& aError)
{
  MOZ_ASSERT(IsInnerWindow());

  nsCOMPtr<nsPIDOMWindowOuter> opener = GetOpenerWindow(aError);
  if (aError.Failed() || !opener) {
    aRetval.setNull();
    return;
  }

  aError = nsContentUtils::WrapNative(aCx, opener, aRetval);
}

already_AddRefed<nsPIDOMWindowOuter>
nsGlobalWindowInner::GetOpener()
{
  FORWARD_TO_OUTER(GetOpener, (), nullptr);

  nsCOMPtr<nsPIDOMWindowOuter> opener = GetOpenerWindowOuter();
  return opener.forget();
}

void
nsGlobalWindowInner::SetOpener(JSContext* aCx, JS::Handle<JS::Value> aOpener,
                          ErrorResult& aError)
{
  // Check if we were called from a privileged chrome script.  If not, and if
  // aOpener is not null, just define aOpener on our inner window's JS object,
  // wrapped into the current compartment so that for Xrays we define on the
  // Xray expando object, but don't set it on the outer window, so that it'll
  // get reset on navigation.  This is just like replaceable properties, but
  // we're not quite readonly.
  if (!aOpener.isNull() && !nsContentUtils::IsCallerChrome()) {
    RedefineProperty(aCx, "opener", aOpener, aError);
    return;
  }

  if (!aOpener.isObjectOrNull()) {
    // Chrome code trying to set some random value as opener
    aError.Throw(NS_ERROR_INVALID_ARG);
    return;
  }

  nsPIDOMWindowInner* win = nullptr;
  if (aOpener.isObject()) {
    JSObject* unwrapped = js::CheckedUnwrap(&aOpener.toObject(),
                                            /* stopAtWindowProxy = */ false);
    if (!unwrapped) {
      aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
      return;
    }

    auto* globalWindow = xpc::WindowOrNull(unwrapped);
    if (!globalWindow) {
      // Wasn't a window
      aError.Throw(NS_ERROR_INVALID_ARG);
      return;
    }

    win = globalWindow->AsInner();
  }

  nsPIDOMWindowOuter* outer = nullptr;
  if (win) {
    if (!win->IsCurrentInnerWindow()) {
      aError.Throw(NS_ERROR_FAILURE);
      return;
    }
    outer = win->GetOuterWindow();
  }

  SetOpenerWindow(outer, false);
}

void
nsGlobalWindowInner::GetStatusOuter(nsAString& aStatus)
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());

  aStatus = mStatus;
}

void
nsGlobalWindowInner::GetStatus(nsAString& aStatus, ErrorResult& aError)
{
  FORWARD_TO_OUTER_OR_THROW(GetStatusOuter, (aStatus), aError, );
}

void
nsGlobalWindowInner::SetStatusOuter(const nsAString& aStatus)
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());

  mStatus = aStatus;

  /*
   * If caller is not chrome and dom.disable_window_status_change is true,
   * prevent propagating window.status to the UI by exiting early
   */

  if (!CanSetProperty("dom.disable_window_status_change")) {
    return;
  }

  nsCOMPtr<nsIWebBrowserChrome> browserChrome = GetWebBrowserChrome();
  if (browserChrome) {
    browserChrome->SetStatus(nsIWebBrowserChrome::STATUS_SCRIPT,
                             PromiseFlatString(aStatus).get());
  }
}

void
nsGlobalWindowInner::SetStatus(const nsAString& aStatus, ErrorResult& aError)
{
  FORWARD_TO_OUTER_OR_THROW(SetStatusOuter, (aStatus), aError, );
}

void
nsGlobalWindowInner::GetNameOuter(nsAString& aName)
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());

  if (mDocShell) {
    mDocShell->GetName(aName);
  }
}

void
nsGlobalWindowInner::GetName(nsAString& aName, ErrorResult& aError)
{
  FORWARD_TO_OUTER_OR_THROW(GetNameOuter, (aName), aError, );
}

void
nsGlobalWindowInner::SetNameOuter(const nsAString& aName, mozilla::ErrorResult& aError)
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());

  if (mDocShell) {
    aError = mDocShell->SetName(aName);
  }
}

void
nsGlobalWindowInner::SetName(const nsAString& aName, mozilla::ErrorResult& aError)
{
  FORWARD_TO_OUTER_OR_THROW(SetNameOuter, (aName, aError), aError, );
}

// Helper functions used by many methods below.
int32_t
nsGlobalWindowInner::DevToCSSIntPixels(int32_t px)
{
  if (!mDocShell)
    return px; // assume 1:1

  RefPtr<nsPresContext> presContext;
  mDocShell->GetPresContext(getter_AddRefs(presContext));
  if (!presContext)
    return px;

  return presContext->DevPixelsToIntCSSPixels(px);
}

int32_t
nsGlobalWindowInner::CSSToDevIntPixels(int32_t px)
{
  if (!mDocShell)
    return px; // assume 1:1

  RefPtr<nsPresContext> presContext;
  mDocShell->GetPresContext(getter_AddRefs(presContext));
  if (!presContext)
    return px;

  return presContext->CSSPixelsToDevPixels(px);
}

nsIntSize
nsGlobalWindowInner::DevToCSSIntPixels(nsIntSize px)
{
  if (!mDocShell)
    return px; // assume 1:1

  RefPtr<nsPresContext> presContext;
  mDocShell->GetPresContext(getter_AddRefs(presContext));
  if (!presContext)
    return px;

  return nsIntSize(
      presContext->DevPixelsToIntCSSPixels(px.width),
      presContext->DevPixelsToIntCSSPixels(px.height));
}

nsIntSize
nsGlobalWindowInner::CSSToDevIntPixels(nsIntSize px)
{
  if (!mDocShell)
    return px; // assume 1:1

  RefPtr<nsPresContext> presContext;
  mDocShell->GetPresContext(getter_AddRefs(presContext));
  if (!presContext)
    return px;

  return nsIntSize(
    presContext->CSSPixelsToDevPixels(px.width),
    presContext->CSSPixelsToDevPixels(px.height));
}

nsresult
nsGlobalWindowInner::GetInnerSize(CSSIntSize& aSize)
{
  MOZ_ASSERT(IsOuterWindow());

  EnsureSizeAndPositionUpToDate();

  NS_ENSURE_STATE(mDocShell);

  RefPtr<nsPresContext> presContext;
  mDocShell->GetPresContext(getter_AddRefs(presContext));
  RefPtr<nsIPresShell> presShell = mDocShell->GetPresShell();

  if (!presContext || !presShell) {
    aSize = CSSIntSize(0, 0);
    return NS_OK;
  }

  /*
   * On platforms with resolution-based zooming, the CSS viewport
   * and visual viewport may not be the same. The inner size should
   * be the visual viewport, but we fall back to the CSS viewport
   * if it is not set.
   */
  if (presShell->IsScrollPositionClampingScrollPortSizeSet()) {
    aSize = CSSIntRect::FromAppUnitsRounded(
      presShell->GetScrollPositionClampingScrollPortSize());
  } else {
    RefPtr<nsViewManager> viewManager = presShell->GetViewManager();
    if (viewManager) {
      viewManager->FlushDelayedResize(false);
    }

    aSize = CSSIntRect::FromAppUnitsRounded(
      presContext->GetVisibleArea().Size());
  }
  return NS_OK;
}

int32_t
nsGlobalWindowInner::GetInnerWidthOuter(ErrorResult& aError)
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());

  CSSIntSize size;
  aError = GetInnerSize(size);
  return size.width;
}

int32_t
nsGlobalWindowInner::GetInnerWidth(CallerType aCallerType, ErrorResult& aError)
{
  // We ignore aCallerType; we only have that argument because some other things
  // called by GetReplaceableWindowCoord need it.  If this ever changes, fix
  //   nsresult nsGlobalWindowInner::GetInnerWidth(int32_t* aInnerWidth)
  // to actually take a useful CallerType and pass it in here.
  FORWARD_TO_OUTER_OR_THROW(GetInnerWidthOuter, (aError), aError, 0);
}

void
nsGlobalWindowInner::GetInnerWidth(JSContext* aCx,
                              JS::MutableHandle<JS::Value> aValue,
                              CallerType aCallerType,
                              ErrorResult& aError)
{
  GetReplaceableWindowCoord(aCx, &nsGlobalWindowInner::GetInnerWidth, aValue,
                            aCallerType, aError);
}

nsresult
nsGlobalWindowInner::GetInnerWidth(int32_t* aInnerWidth)
{
  FORWARD_TO_INNER(GetInnerWidth, (aInnerWidth), NS_ERROR_UNEXPECTED);

  ErrorResult rv;
  // Callee doesn't care about the caller type, but play it safe.
  *aInnerWidth = GetInnerWidth(CallerType::NonSystem, rv);

  return rv.StealNSResult();
}

void
nsGlobalWindowInner::SetInnerWidthOuter(int32_t aInnerWidth,
                                   CallerType aCallerType,
                                   ErrorResult& aError)
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());

  if (!mDocShell) {
    aError.Throw(NS_ERROR_UNEXPECTED);
    return;
  }

  CheckSecurityWidthAndHeight(&aInnerWidth, nullptr, aCallerType);

  RefPtr<nsIPresShell> presShell = mDocShell->GetPresShell();

  if (presShell && presShell->GetIsViewportOverridden())
  {
    nscoord height = 0;

    RefPtr<nsPresContext> presContext;
    presContext = presShell->GetPresContext();

    nsRect shellArea = presContext->GetVisibleArea();
    height = shellArea.Height();
    SetCSSViewportWidthAndHeight(nsPresContext::CSSPixelsToAppUnits(aInnerWidth),
                                 height);
    return;
  }

  int32_t height = 0;
  int32_t unused  = 0;

  nsCOMPtr<nsIBaseWindow> docShellAsWin(do_QueryInterface(mDocShell));
  docShellAsWin->GetSize(&unused, &height);
  aError = SetDocShellWidthAndHeight(CSSToDevIntPixels(aInnerWidth), height);
}

void
nsGlobalWindowInner::SetInnerWidth(int32_t aInnerWidth, CallerType aCallerType,
                              ErrorResult& aError)
{
  FORWARD_TO_OUTER_OR_THROW(SetInnerWidthOuter,
                            (aInnerWidth, aCallerType, aError), aError, );
}

void
nsGlobalWindowInner::SetInnerWidth(JSContext* aCx, JS::Handle<JS::Value> aValue,
                              CallerType aCallerType,
                              ErrorResult& aError)
{
  SetReplaceableWindowCoord(aCx, &nsGlobalWindowInner::SetInnerWidth,
                            aValue, "innerWidth", aCallerType, aError);
}

int32_t
nsGlobalWindowInner::GetInnerHeightOuter(ErrorResult& aError)
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());

  CSSIntSize size;
  aError = GetInnerSize(size);
  return size.height;
}

int32_t
nsGlobalWindowInner::GetInnerHeight(CallerType aCallerType, ErrorResult& aError)
{
  // We ignore aCallerType; we only have that argument because some other things
  // called by GetReplaceableWindowCoord need it.  If this ever changes, fix
  //   nsresult nsGlobalWindowInner::GetInnerHeight(int32_t* aInnerWidth)
  // to actually take a useful CallerType and pass it in here.
  FORWARD_TO_OUTER_OR_THROW(GetInnerHeightOuter, (aError), aError, 0);
}

void
nsGlobalWindowInner::GetInnerHeight(JSContext* aCx,
                              JS::MutableHandle<JS::Value> aValue,
                              CallerType aCallerType,
                              ErrorResult& aError)
{
  GetReplaceableWindowCoord(aCx, &nsGlobalWindowInner::GetInnerHeight, aValue,
                            aCallerType, aError);
}

nsresult
nsGlobalWindowInner::GetInnerHeight(int32_t* aInnerHeight)
{
  FORWARD_TO_INNER(GetInnerHeight, (aInnerHeight), NS_ERROR_UNEXPECTED);

  ErrorResult rv;
  // Callee doesn't care about the caller type, but play it safe.
  *aInnerHeight = GetInnerHeight(CallerType::NonSystem, rv);

  return rv.StealNSResult();
}

void
nsGlobalWindowInner::SetInnerHeightOuter(int32_t aInnerHeight,
                                    CallerType aCallerType,
                                    ErrorResult& aError)
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());

  if (!mDocShell) {
    aError.Throw(NS_ERROR_UNEXPECTED);
    return;
  }

  RefPtr<nsIPresShell> presShell = mDocShell->GetPresShell();

  if (presShell && presShell->GetIsViewportOverridden())
  {
    RefPtr<nsPresContext> presContext;
    presContext = presShell->GetPresContext();

    nsRect shellArea = presContext->GetVisibleArea();
    nscoord height = aInnerHeight;
    nscoord width = shellArea.Width();
    CheckSecurityWidthAndHeight(nullptr, &height, aCallerType);
    SetCSSViewportWidthAndHeight(width,
                                 nsPresContext::CSSPixelsToAppUnits(height));
    return;
  }

  int32_t height = 0;
  int32_t width  = 0;

  nsCOMPtr<nsIBaseWindow> docShellAsWin(do_QueryInterface(mDocShell));
  docShellAsWin->GetSize(&width, &height);
  CheckSecurityWidthAndHeight(nullptr, &aInnerHeight, aCallerType);
  aError = SetDocShellWidthAndHeight(width, CSSToDevIntPixels(aInnerHeight));
}

void
nsGlobalWindowInner::SetInnerHeight(int32_t aInnerHeight,
                               CallerType aCallerType,
                               ErrorResult& aError)
{
  FORWARD_TO_OUTER_OR_THROW(SetInnerHeightOuter,
                            (aInnerHeight, aCallerType, aError), aError, );
}

void
nsGlobalWindowInner::SetInnerHeight(JSContext* aCx, JS::Handle<JS::Value> aValue,
                               CallerType aCallerType, ErrorResult& aError)
{
  SetReplaceableWindowCoord(aCx, &nsGlobalWindowInner::SetInnerHeight,
                            aValue, "innerHeight", aCallerType, aError);
}

nsIntSize
nsGlobalWindowInner::GetOuterSize(CallerType aCallerType, ErrorResult& aError)
{
  MOZ_ASSERT(IsOuterWindow());

  if (nsContentUtils::ResistFingerprinting(aCallerType)) {
    CSSIntSize size;
    aError = GetInnerSize(size);
    return nsIntSize(size.width, size.height);
  }

  nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
  if (!treeOwnerAsWin) {
    aError.Throw(NS_ERROR_FAILURE);
    return nsIntSize(0, 0);
  }

  nsIntSize sizeDevPixels;
  aError = treeOwnerAsWin->GetSize(&sizeDevPixels.width, &sizeDevPixels.height);
  if (aError.Failed()) {
    return nsIntSize();
  }

  return DevToCSSIntPixels(sizeDevPixels);
}

int32_t
nsGlobalWindowInner::GetOuterWidthOuter(CallerType aCallerType, ErrorResult& aError)
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());
  return GetOuterSize(aCallerType, aError).width;
}

int32_t
nsGlobalWindowInner::GetOuterWidth(CallerType aCallerType, ErrorResult& aError)
{
  FORWARD_TO_OUTER_OR_THROW(GetOuterWidthOuter, (aCallerType, aError),
                            aError, 0);
}

void
nsGlobalWindowInner::GetOuterWidth(JSContext* aCx,
                              JS::MutableHandle<JS::Value> aValue,
                              CallerType aCallerType,
                              ErrorResult& aError)
{
  GetReplaceableWindowCoord(aCx, &nsGlobalWindowInner::GetOuterWidth, aValue,
                            aCallerType, aError);
}

int32_t
nsGlobalWindowInner::GetOuterHeightOuter(CallerType aCallerType, ErrorResult& aError)
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());
  return GetOuterSize(aCallerType, aError).height;
}

int32_t
nsGlobalWindowInner::GetOuterHeight(CallerType aCallerType, ErrorResult& aError)
{
  FORWARD_TO_OUTER_OR_THROW(GetOuterHeightOuter, (aCallerType, aError),
                            aError, 0);
}

void
nsGlobalWindowInner::GetOuterHeight(JSContext* aCx,
                               JS::MutableHandle<JS::Value> aValue,
                               CallerType aCallerType,
                               ErrorResult& aError)
{
  GetReplaceableWindowCoord(aCx, &nsGlobalWindowInner::GetOuterHeight, aValue,
                            aCallerType, aError);
}

void
nsGlobalWindowInner::SetOuterSize(int32_t aLengthCSSPixels, bool aIsWidth,
                             CallerType aCallerType, ErrorResult& aError)
{
  MOZ_ASSERT(IsOuterWindow());

  nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
  if (!treeOwnerAsWin) {
    aError.Throw(NS_ERROR_FAILURE);
    return;
  }

  CheckSecurityWidthAndHeight(aIsWidth ? &aLengthCSSPixels : nullptr,
                              aIsWidth ? nullptr : &aLengthCSSPixels,
                              aCallerType);

  int32_t width, height;
  aError = treeOwnerAsWin->GetSize(&width, &height);
  if (aError.Failed()) {
    return;
  }

  int32_t lengthDevPixels = CSSToDevIntPixels(aLengthCSSPixels);
  if (aIsWidth) {
    width = lengthDevPixels;
  } else {
    height = lengthDevPixels;
  }
  aError = treeOwnerAsWin->SetSize(width, height, true);

  CheckForDPIChange();
}

void
nsGlobalWindowInner::SetOuterWidthOuter(int32_t aOuterWidth,
                                   CallerType aCallerType,
                                   ErrorResult& aError)
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());

  SetOuterSize(aOuterWidth, true, aCallerType, aError);
}

void
nsGlobalWindowInner::SetOuterWidth(int32_t aOuterWidth,
                              CallerType aCallerType,
                              ErrorResult& aError)
{
  FORWARD_TO_OUTER_OR_THROW(SetOuterWidthOuter,
                            (aOuterWidth, aCallerType, aError), aError, );
}

void
nsGlobalWindowInner::SetOuterWidth(JSContext* aCx, JS::Handle<JS::Value> aValue,
                              CallerType aCallerType,
                              ErrorResult& aError)
{
  SetReplaceableWindowCoord(aCx, &nsGlobalWindowInner::SetOuterWidth,
                            aValue, "outerWidth", aCallerType, aError);
}

void
nsGlobalWindowInner::SetOuterHeightOuter(int32_t aOuterHeight,
                                    CallerType aCallerType,
                                    ErrorResult& aError)
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());

  SetOuterSize(aOuterHeight, false, aCallerType, aError);
}

void
nsGlobalWindowInner::SetOuterHeight(int32_t aOuterHeight,
                               CallerType aCallerType,
                               ErrorResult& aError)
{
  FORWARD_TO_OUTER_OR_THROW(SetOuterHeightOuter,
                            (aOuterHeight, aCallerType, aError), aError, );
}

void
nsGlobalWindowInner::SetOuterHeight(JSContext* aCx, JS::Handle<JS::Value> aValue,
                               CallerType aCallerType,
                               ErrorResult& aError)
{
  SetReplaceableWindowCoord(aCx, &nsGlobalWindowInner::SetOuterHeight,
                            aValue, "outerHeight", aCallerType, aError);
}

CSSIntPoint
nsGlobalWindowInner::GetScreenXY(CallerType aCallerType, ErrorResult& aError)
{
  MOZ_ASSERT(IsOuterWindow());

  // When resisting fingerprinting, always return (0,0)
  if (nsContentUtils::ResistFingerprinting(aCallerType)) {
    return CSSIntPoint(0, 0);
  }

  nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
  if (!treeOwnerAsWin) {
    aError.Throw(NS_ERROR_FAILURE);
    return CSSIntPoint(0, 0);
  }

  int32_t x = 0, y = 0;
  aError = treeOwnerAsWin->GetPosition(&x, &y); // LayoutDevice px values

  RefPtr<nsPresContext> presContext;
  mDocShell->GetPresContext(getter_AddRefs(presContext));
  if (!presContext) {
    return CSSIntPoint(x, y);
  }

  // Find the global desktop coordinate of the top-left of the screen.
  // We'll use this as a "fake origin" when converting to CSS px units,
  // to avoid overlapping coordinates in cases such as a hi-dpi screen
  // placed to the right of a lo-dpi screen on Windows. (Instead, there
  // may be "gaps" in the resulting CSS px coordinates in some cases.)
  nsDeviceContext *dc = presContext->DeviceContext();
  nsRect screenRect;
  dc->GetRect(screenRect);
  LayoutDeviceRect screenRectDev =
    LayoutDevicePixel::FromAppUnits(screenRect, dc->AppUnitsPerDevPixel());

  DesktopToLayoutDeviceScale scale = dc->GetDesktopToDeviceScale();
  DesktopRect screenRectDesk = screenRectDev / scale;

  CSSPoint cssPt =
    LayoutDevicePoint(x - screenRectDev.x, y - screenRectDev.y) /
    presContext->CSSToDevPixelScale();
  cssPt.x += screenRectDesk.x;
  cssPt.y += screenRectDesk.y;

  return CSSIntPoint(NSToIntRound(cssPt.x), NSToIntRound(cssPt.y));
}

int32_t
nsGlobalWindowInner::GetScreenXOuter(CallerType aCallerType, ErrorResult& aError)
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());

  return GetScreenXY(aCallerType, aError).x;
}

int32_t
nsGlobalWindowInner::GetScreenX(CallerType aCallerType, ErrorResult& aError)
{
  FORWARD_TO_OUTER_OR_THROW(GetScreenXOuter, (aCallerType, aError), aError, 0);
}

void
nsGlobalWindowInner::GetScreenX(JSContext* aCx,
                           JS::MutableHandle<JS::Value> aValue,
                           CallerType aCallerType,
                           ErrorResult& aError)
{
  GetReplaceableWindowCoord(aCx, &nsGlobalWindowInner::GetScreenX, aValue,
                            aCallerType, aError);
}

nsRect
nsGlobalWindowInner::GetInnerScreenRect()
{
  MOZ_ASSERT(IsOuterWindow());

  if (!mDocShell) {
    return nsRect();
  }

  EnsureSizeAndPositionUpToDate();

  if (!mDocShell) {
    return nsRect();
  }

  nsCOMPtr<nsIPresShell> presShell = mDocShell->GetPresShell();
  if (!presShell) {
    return nsRect();
  }
  nsIFrame* rootFrame = presShell->GetRootFrame();
  if (!rootFrame) {
    return nsRect();
  }

  return rootFrame->GetScreenRectInAppUnits();
}

float
nsGlobalWindowInner::GetMozInnerScreenXOuter(CallerType aCallerType)
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());

  // When resisting fingerprinting, always return 0.
  if (nsContentUtils::ResistFingerprinting(aCallerType)) {
    return 0.0;
  }

  nsRect r = GetInnerScreenRect();
  return nsPresContext::AppUnitsToFloatCSSPixels(r.x);
}

float
nsGlobalWindowInner::GetMozInnerScreenX(CallerType aCallerType, ErrorResult& aError)
{
  FORWARD_TO_OUTER_OR_THROW(GetMozInnerScreenXOuter, (aCallerType), aError, 0);
}

float
nsGlobalWindowInner::GetMozInnerScreenYOuter(CallerType aCallerType)
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());

  // Return 0 to prevent fingerprinting.
  if (nsContentUtils::ResistFingerprinting(aCallerType)) {
    return 0.0;
  }

  nsRect r = GetInnerScreenRect();
  return nsPresContext::AppUnitsToFloatCSSPixels(r.y);
}

float
nsGlobalWindowInner::GetMozInnerScreenY(CallerType aCallerType, ErrorResult& aError)
{
  FORWARD_TO_OUTER_OR_THROW(GetMozInnerScreenYOuter, (aCallerType), aError, 0);
}

double
nsGlobalWindowInner::GetDevicePixelRatioOuter(CallerType aCallerType)
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());

  if (!mDocShell) {
    return 1.0;
  }

  RefPtr<nsPresContext> presContext;
  mDocShell->GetPresContext(getter_AddRefs(presContext));
  if (!presContext) {
    return 1.0;
  }

  if (nsContentUtils::ResistFingerprinting(aCallerType)) {
    return 1.0;
  }

  float overrideDPPX = presContext->GetOverrideDPPX();

  if (overrideDPPX > 0) {
    return overrideDPPX;
  }

  return double(nsPresContext::AppUnitsPerCSSPixel()) /
         double(presContext->AppUnitsPerDevPixel());
}

double
nsGlobalWindowInner::GetDevicePixelRatio(CallerType aCallerType, ErrorResult& aError)
{
  FORWARD_TO_OUTER_OR_THROW(GetDevicePixelRatioOuter, (aCallerType), aError, 0.0);
}

uint64_t
nsGlobalWindowInner::GetMozPaintCountOuter()
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());

  if (!mDocShell) {
    return 0;
  }

  nsCOMPtr<nsIPresShell> presShell = mDocShell->GetPresShell();
  return presShell ? presShell->GetPaintCount() : 0;
}

uint64_t
nsGlobalWindowInner::GetMozPaintCount(ErrorResult& aError)
{
  FORWARD_TO_OUTER_OR_THROW(GetMozPaintCountOuter, (), aError, 0);
}

int32_t
nsGlobalWindowInner::RequestAnimationFrame(FrameRequestCallback& aCallback,
                                      ErrorResult& aError)
{
  MOZ_RELEASE_ASSERT(IsInnerWindow());

  if (!mDoc) {
    return 0;
  }

  if (GetWrapperPreserveColor()) {
    js::NotifyAnimationActivity(GetWrapperPreserveColor());
  }

  int32_t handle;
  aError = mDoc->ScheduleFrameRequestCallback(aCallback, &handle);
  return handle;
}

void
nsGlobalWindowInner::CancelAnimationFrame(int32_t aHandle, ErrorResult& aError)
{
  MOZ_RELEASE_ASSERT(IsInnerWindow());

  if (!mDoc) {
    return;
  }

  mDoc->CancelFrameRequestCallback(aHandle);
}

already_AddRefed<MediaQueryList>
nsGlobalWindowInner::MatchMediaOuter(const nsAString& aMediaQueryList,
                                CallerType aCallerType)
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());

  if (!mDoc) {
    return nullptr;
  }

  return mDoc->MatchMedia(aMediaQueryList, aCallerType);
}

already_AddRefed<MediaQueryList>
nsGlobalWindowInner::MatchMedia(const nsAString& aMediaQueryList,
                           CallerType aCallerType,
                           ErrorResult& aError)
{
  // FIXME: This whole forward-to-outer and then get a pres
  // shell/context off the docshell dance is sort of silly; it'd make
  // more sense to forward to the inner, but it's what everyone else
  // (GetSelection, GetScrollXY, etc.) does around here.
  FORWARD_TO_OUTER_OR_THROW(MatchMediaOuter, (aMediaQueryList, aCallerType), aError, nullptr);
}

void
nsGlobalWindowInner::SetScreenXOuter(int32_t aScreenX,
                                CallerType aCallerType,
                                ErrorResult& aError)
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());

  nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
  if (!treeOwnerAsWin) {
    aError.Throw(NS_ERROR_FAILURE);
    return;
  }

  int32_t x, y;
  aError = treeOwnerAsWin->GetPosition(&x, &y);
  if (aError.Failed()) {
    return;
  }

  CheckSecurityLeftAndTop(&aScreenX, nullptr, aCallerType);
  x = CSSToDevIntPixels(aScreenX);

  aError = treeOwnerAsWin->SetPosition(x, y);

  CheckForDPIChange();
}

void
nsGlobalWindowInner::SetScreenX(int32_t aScreenX,
                           CallerType aCallerType,
                           ErrorResult& aError)
{
  FORWARD_TO_OUTER_OR_THROW(SetScreenXOuter,
                            (aScreenX, aCallerType, aError), aError, );
}

void
nsGlobalWindowInner::SetScreenX(JSContext* aCx, JS::Handle<JS::Value> aValue,
                           CallerType aCallerType, ErrorResult& aError)
{
  SetReplaceableWindowCoord(aCx, &nsGlobalWindowInner::SetScreenX,
                            aValue, "screenX", aCallerType, aError);
}

int32_t
nsGlobalWindowInner::GetScreenYOuter(CallerType aCallerType, ErrorResult& aError)
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());

  return GetScreenXY(aCallerType, aError).y;
}

int32_t
nsGlobalWindowInner::GetScreenY(CallerType aCallerType, ErrorResult& aError)
{
  FORWARD_TO_OUTER_OR_THROW(GetScreenYOuter, (aCallerType, aError), aError, 0);
}

void
nsGlobalWindowInner::GetScreenY(JSContext* aCx,
                           JS::MutableHandle<JS::Value> aValue,
                           CallerType aCallerType, ErrorResult& aError)
{
  GetReplaceableWindowCoord(aCx, &nsGlobalWindowInner::GetScreenY, aValue,
                            aCallerType, aError);
}

void
nsGlobalWindowInner::SetScreenYOuter(int32_t aScreenY,
                                CallerType aCallerType,
                                ErrorResult& aError)
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());

  nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
  if (!treeOwnerAsWin) {
    aError.Throw(NS_ERROR_FAILURE);
    return;
  }

  int32_t x, y;
  aError = treeOwnerAsWin->GetPosition(&x, &y);
  if (aError.Failed()) {
    return;
  }

  CheckSecurityLeftAndTop(nullptr, &aScreenY, aCallerType);
  y = CSSToDevIntPixels(aScreenY);

  aError = treeOwnerAsWin->SetPosition(x, y);

  CheckForDPIChange();
}

void
nsGlobalWindowInner::SetScreenY(int32_t aScreenY,
                           CallerType aCallerType,
                           ErrorResult& aError)
{
  FORWARD_TO_OUTER_OR_THROW(SetScreenYOuter,
                            (aScreenY, aCallerType, aError), aError, );
}

void
nsGlobalWindowInner::SetScreenY(JSContext* aCx, JS::Handle<JS::Value> aValue,
                           CallerType aCallerType,
                           ErrorResult& aError)
{
  SetReplaceableWindowCoord(aCx, &nsGlobalWindowInner::SetScreenY,
                            aValue, "screenY", aCallerType, aError);
}

// NOTE: Arguments to this function should have values scaled to
// CSS pixels, not device pixels.
void
nsGlobalWindowInner::CheckSecurityWidthAndHeight(int32_t* aWidth, int32_t* aHeight,
                                            CallerType aCallerType)
{
  MOZ_ASSERT(IsOuterWindow());

#ifdef MOZ_XUL
  if (aCallerType != CallerType::System) {
    // if attempting to resize the window, hide any open popups
    nsContentUtils::HidePopupsInDocument(mDoc);
  }
#endif

  // This one is easy. Just ensure the variable is greater than 100;
  if ((aWidth && *aWidth < 100) || (aHeight && *aHeight < 100)) {
    // Check security state for use in determing window dimensions

    if (aCallerType != CallerType::System) {
      //sec check failed
      if (aWidth && *aWidth < 100) {
        *aWidth = 100;
      }
      if (aHeight && *aHeight < 100) {
        *aHeight = 100;
      }
    }
  }
}

// NOTE: Arguments to this function should have values in device pixels
nsresult
nsGlobalWindowInner::SetDocShellWidthAndHeight(int32_t aInnerWidth, int32_t aInnerHeight)
{
  MOZ_ASSERT(IsOuterWindow());

  NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE);

  nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
  mDocShell->GetTreeOwner(getter_AddRefs(treeOwner));
  NS_ENSURE_TRUE(treeOwner, NS_ERROR_FAILURE);

  NS_ENSURE_SUCCESS(treeOwner->SizeShellTo(mDocShell, aInnerWidth, aInnerHeight),
                    NS_ERROR_FAILURE);

  return NS_OK;
}

// NOTE: Arguments to this function should have values in app units
void
nsGlobalWindowInner::SetCSSViewportWidthAndHeight(nscoord aInnerWidth, nscoord aInnerHeight)
{
  MOZ_ASSERT(IsOuterWindow());

  RefPtr<nsPresContext> presContext;
  mDocShell->GetPresContext(getter_AddRefs(presContext));

  nsRect shellArea = presContext->GetVisibleArea();
  shellArea.SetHeight(aInnerHeight);
  shellArea.SetWidth(aInnerWidth);

  presContext->SetVisibleArea(shellArea);
}

// NOTE: Arguments to this function should have values scaled to
// CSS pixels, not device pixels.
void
nsGlobalWindowInner::CheckSecurityLeftAndTop(int32_t* aLeft, int32_t* aTop,
                                        CallerType aCallerType)
{
  MOZ_ASSERT(IsOuterWindow());

  // This one is harder. We have to get the screen size and window dimensions.

  // Check security state for use in determing window dimensions

  if (aCallerType != CallerType::System) {
#ifdef MOZ_XUL
    // if attempting to move the window, hide any open popups
    nsContentUtils::HidePopupsInDocument(mDoc);
#endif

    if (nsGlobalWindowOuter* rootWindow = nsGlobalWindowOuter::Cast(GetPrivateRoot())) {
      rootWindow->FlushPendingNotifications(FlushType::Layout);
    }

    nsCOMPtr<nsIBaseWindow> treeOwner = GetTreeOwnerWindow();

    nsCOMPtr<nsIDOMScreen> screen = GetScreen();

    if (treeOwner && screen) {
      int32_t screenLeft, screenTop, screenWidth, screenHeight;
      int32_t winLeft, winTop, winWidth, winHeight;

      // Get the window size
      treeOwner->GetPositionAndSize(&winLeft, &winTop, &winWidth, &winHeight);

      // convert those values to CSS pixels
      // XXX four separate retrievals of the prescontext
      winLeft   = DevToCSSIntPixels(winLeft);
      winTop    = DevToCSSIntPixels(winTop);
      winWidth  = DevToCSSIntPixels(winWidth);
      winHeight = DevToCSSIntPixels(winHeight);

      // Get the screen dimensions
      // XXX This should use nsIScreenManager once it's fully fleshed out.
      screen->GetAvailLeft(&screenLeft);
      screen->GetAvailWidth(&screenWidth);
      screen->GetAvailHeight(&screenHeight);
#if defined(XP_MACOSX)
      /* The mac's coordinate system is different from the assumed Windows'
         system. It offsets by the height of the menubar so that a window
         placed at (0,0) will be entirely visible. Unfortunately that
         correction is made elsewhere (in Widget) and the meaning of
         the Avail... coordinates is overloaded. Here we allow a window
         to be placed at (0,0) because it does make sense to do so.
      */
      screen->GetTop(&screenTop);
#else
      screen->GetAvailTop(&screenTop);
#endif

      if (aLeft) {
        if (screenLeft+screenWidth < *aLeft+winWidth)
          *aLeft = screenLeft+screenWidth - winWidth;
        if (screenLeft > *aLeft)
          *aLeft = screenLeft;
      }
      if (aTop) {
        if (screenTop+screenHeight < *aTop+winHeight)
          *aTop = screenTop+screenHeight - winHeight;
        if (screenTop > *aTop)
          *aTop = screenTop;
      }
    } else {
      if (aLeft)
        *aLeft = 0;
      if (aTop)
        *aTop = 0;
    }
  }
}

int32_t
nsGlobalWindowInner::GetScrollBoundaryOuter(Side aSide)
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());

  FlushPendingNotifications(FlushType::Layout);
  if (nsIScrollableFrame *sf = GetScrollFrame()) {
    return nsPresContext::
      AppUnitsToIntCSSPixels(sf->GetScrollRange().Edge(aSide));
  }
  return 0;
}

int32_t
nsGlobalWindowInner::GetScrollMinX(ErrorResult& aError)
{
  MOZ_ASSERT(IsInnerWindow());
  FORWARD_TO_OUTER_OR_THROW(GetScrollBoundaryOuter, (eSideLeft), aError, 0);
}

int32_t
nsGlobalWindowInner::GetScrollMinY(ErrorResult& aError)
{
  MOZ_ASSERT(IsInnerWindow());
  FORWARD_TO_OUTER_OR_THROW(GetScrollBoundaryOuter, (eSideTop), aError, 0);
}

int32_t
nsGlobalWindowInner::GetScrollMaxX(ErrorResult& aError)
{
  MOZ_ASSERT(IsInnerWindow());
  FORWARD_TO_OUTER_OR_THROW(GetScrollBoundaryOuter, (eSideRight), aError, 0);
}

int32_t
nsGlobalWindowInner::GetScrollMaxY(ErrorResult& aError)
{
  MOZ_ASSERT(IsInnerWindow());
  FORWARD_TO_OUTER_OR_THROW(GetScrollBoundaryOuter, (eSideBottom), aError, 0);
}

CSSPoint
nsGlobalWindowInner::GetScrollXY(bool aDoFlush)
{
  MOZ_ASSERT(IsOuterWindow());

  if (aDoFlush) {
    FlushPendingNotifications(FlushType::Layout);
  } else {
    EnsureSizeAndPositionUpToDate();
  }

  nsIScrollableFrame *sf = GetScrollFrame();
  if (!sf) {
    return CSSIntPoint(0, 0);
  }

  nsPoint scrollPos = sf->GetScrollPosition();
  if (scrollPos != nsPoint(0,0) && !aDoFlush) {
    // Oh, well.  This is the expensive case -- the window is scrolled and we
    // didn't actually flush yet.  Repeat, but with a flush, since the content
    // may get shorter and hence our scroll position may decrease.
    return GetScrollXY(true);
  }

  return CSSPoint::FromAppUnits(scrollPos);
}

double
nsGlobalWindowInner::GetScrollXOuter()
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());
  return GetScrollXY(false).x;
}

double
nsGlobalWindowInner::GetScrollX(ErrorResult& aError)
{
  FORWARD_TO_OUTER_OR_THROW(GetScrollXOuter, (), aError, 0);
}

double
nsGlobalWindowInner::GetScrollYOuter()
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());
  return GetScrollXY(false).y;
}

double
nsGlobalWindowInner::GetScrollY(ErrorResult& aError)
{
  FORWARD_TO_OUTER_OR_THROW(GetScrollYOuter, (), aError, 0);
}

uint32_t
nsGlobalWindowInner::Length()
{
  FORWARD_TO_OUTER(Length, (), 0);

  nsDOMWindowList* windows = GetWindowList();

  return windows ? windows->GetLength() : 0;
}

already_AddRefed<nsPIDOMWindowOuter>
nsGlobalWindowInner::GetTopOuter()
{
  MOZ_ASSERT(IsOuterWindow());

  nsCOMPtr<nsPIDOMWindowOuter> top = GetScriptableTop();
  return top.forget();
}

already_AddRefed<nsPIDOMWindowOuter>
nsGlobalWindowInner::GetTop(mozilla::ErrorResult& aError)
{
  FORWARD_TO_OUTER_OR_THROW(GetTopOuter, (), aError, nullptr);
}

nsPIDOMWindowOuter*
nsGlobalWindowInner::GetChildWindow(const nsAString& aName)
{
  nsCOMPtr<nsIDocShell> docShell(GetDocShell());
  NS_ENSURE_TRUE(docShell, nullptr);

  nsCOMPtr<nsIDocShellTreeItem> child;
  docShell->FindChildWithName(aName, false, true, nullptr, nullptr,
                              getter_AddRefs(child));

  return child ? child->GetWindow() : nullptr;
}

bool
nsGlobalWindowInner::DispatchCustomEvent(const nsAString& aEventName)
{
  MOZ_ASSERT(IsOuterWindow());

  bool defaultActionEnabled = true;
  nsContentUtils::DispatchTrustedEvent(mDoc, ToSupports(this), aEventName,
                                       true, true, &defaultActionEnabled);

  return defaultActionEnabled;
}

bool
nsGlobalWindowInner::DispatchResizeEvent(const CSSIntSize& aSize)
{
  MOZ_ASSERT(IsOuterWindow());

  ErrorResult res;
  RefPtr<Event> domEvent =
    mDoc->CreateEvent(NS_LITERAL_STRING("CustomEvent"), CallerType::System,
                      res);
  if (res.Failed()) {
    return false;
  }

  // We don't init the AutoJSAPI with ourselves because we don't want it
  // reporting errors to our onerror handlers.
  AutoJSAPI jsapi;
  jsapi.Init();
  JSContext* cx = jsapi.cx();
  JSAutoCompartment ac(cx, GetWrapperPreserveColor());

  DOMWindowResizeEventDetail detail;
  detail.mWidth = aSize.width;
  detail.mHeight = aSize.height;
  JS::Rooted<JS::Value> detailValue(cx);
  if (!ToJSValue(cx, detail, &detailValue)) {
    return false;
  }

  CustomEvent* customEvent = static_cast<CustomEvent*>(domEvent.get());
  customEvent->InitCustomEvent(cx,
                               NS_LITERAL_STRING("DOMWindowResize"),
                               /* aCanBubble = */ true,
                               /* aCancelable = */ true,
                               detailValue,
                               res);
  if (res.Failed()) {
    return false;
  }

  domEvent->SetTrusted(true);
  domEvent->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true;

  nsCOMPtr<EventTarget> target = do_QueryInterface(GetOuterWindow());
  domEvent->SetTarget(target);

  bool defaultActionEnabled = true;
  target->DispatchEvent(domEvent, &defaultActionEnabled);

  return defaultActionEnabled;
}

void
nsGlobalWindowInner::RefreshCompartmentPrincipal()
{
  MOZ_ASSERT(IsInnerWindow());

  JS_SetCompartmentPrincipals(js::GetObjectCompartment(GetWrapperPreserveColor()),
                              nsJSPrincipals::get(mDoc->NodePrincipal()));
}

bool
nsGlobalWindowInner::WindowExists(const nsAString& aName,
                             bool aForceNoOpener,
                             bool aLookForCallerOnJSStack)
{
  MOZ_CRASH("Outer window only");
}

already_AddRefed<nsIWidget>
nsGlobalWindowInner::GetMainWidget()
{
  FORWARD_TO_OUTER(GetMainWidget, (), nullptr);

  nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();

  nsCOMPtr<nsIWidget> widget;

  if (treeOwnerAsWin) {
    treeOwnerAsWin->GetMainWidget(getter_AddRefs(widget));
  }

  return widget.forget();
}

nsIWidget*
nsGlobalWindowInner::GetNearestWidget() const
{
  nsIDocShell* docShell = GetDocShell();
  NS_ENSURE_TRUE(docShell, nullptr);
  nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
  NS_ENSURE_TRUE(presShell, nullptr);
  nsIFrame* rootFrame = presShell->GetRootFrame();
  NS_ENSURE_TRUE(rootFrame, nullptr);
  return rootFrame->GetView()->GetNearestWidget(nullptr);
}

void
nsGlobalWindowInner::SetFullScreenOuter(bool aFullScreen, mozilla::ErrorResult& aError)
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());

  aError = SetFullscreenInternal(FullscreenReason::ForFullscreenMode, aFullScreen);
}

void
nsGlobalWindowInner::SetFullScreen(bool aFullScreen, mozilla::ErrorResult& aError)
{
  FORWARD_TO_OUTER_OR_THROW(SetFullScreenOuter, (aFullScreen, aError), aError, /* void */);
}

nsresult
nsGlobalWindowInner::SetFullScreen(bool aFullScreen)
{
  FORWARD_TO_OUTER(SetFullScreen, (aFullScreen), NS_ERROR_NOT_INITIALIZED);

  return SetFullscreenInternal(FullscreenReason::ForFullscreenMode, aFullScreen);
}

nsresult
nsGlobalWindowInner::SetFullscreenInternal(FullscreenReason aReason,
                                      bool aFullScreen)
{
  MOZ_CRASH("Outer window only");
}

bool
nsGlobalWindowInner::SetWidgetFullscreen(FullscreenReason aReason, bool aIsFullscreen,
                                    nsIWidget* aWidget, nsIScreen* aScreen)
{
  MOZ_CRASH("Only callable on the outer window");
}

/* virtual */ void
nsGlobalWindowInner::FullscreenWillChange(bool aIsFullscreen)
{
  if (aIsFullscreen) {
    DispatchCustomEvent(NS_LITERAL_STRING("willenterfullscreen"));
  } else {
    DispatchCustomEvent(NS_LITERAL_STRING("willexitfullscreen"));
  }
}

/* virtual */ void
nsGlobalWindowInner::FinishFullscreenChange(bool aIsFullscreen)
{
  MOZ_CRASH("Outer window only");
}

bool
nsGlobalWindowInner::FullScreen() const
{
  MOZ_ASSERT(IsOuterWindow());

  NS_ENSURE_TRUE(mDocShell, mFullScreen);

  // Get the fullscreen value of the root window, to always have the value
  // accurate, even when called from content.
  nsCOMPtr<nsIDocShellTreeItem> rootItem;
  mDocShell->GetRootTreeItem(getter_AddRefs(rootItem));
  if (rootItem == mDocShell) {
    if (!XRE_IsContentProcess()) {
      // We are the root window. Return our internal value.
      return mFullScreen;
    }
    if (nsCOMPtr<nsIWidget> widget = GetNearestWidget()) {
      // We are in content process, figure out the value from
      // the sizemode of the puppet widget.
      return widget->SizeMode() == nsSizeMode_Fullscreen;
    }
    return false;
  }

  nsCOMPtr<nsPIDOMWindowOuter> window = rootItem->GetWindow();
  NS_ENSURE_TRUE(window, mFullScreen);

  return nsGlobalWindowOuter::Cast(window)->FullScreen();
}

bool
nsGlobalWindowInner::GetFullScreenOuter()
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());
  return FullScreen();
}

bool
nsGlobalWindowInner::GetFullScreen(ErrorResult& aError)
{
  FORWARD_TO_OUTER_OR_THROW(GetFullScreenOuter, (), aError, false);
}

bool
nsGlobalWindowInner::GetFullScreen()
{
  FORWARD_TO_INNER(GetFullScreen, (), false);

  ErrorResult dummy;
  bool fullscreen = GetFullScreen(dummy);
  dummy.SuppressException();
  return fullscreen;
}

void
nsGlobalWindowInner::Dump(const nsAString& aStr)
{
  if (!nsContentUtils::DOMWindowDumpEnabled()) {
    return;
  }

  char *cstr = ToNewUTF8String(aStr);

#if defined(XP_MACOSX)
  // have to convert \r to \n so that printing to the console works
  char *c = cstr, *cEnd = cstr + strlen(cstr);
  while (c < cEnd) {
    if (*c == '\r')
      *c = '\n';
    c++;
  }
#endif

  if (cstr) {
    MOZ_LOG(nsContentUtils::DOMDumpLog(), LogLevel::Debug, ("[Window.Dump] %s", cstr));
#ifdef XP_WIN
    PrintToDebugger(cstr);
#endif
#ifdef ANDROID
    __android_log_write(ANDROID_LOG_INFO, "GeckoDump", cstr);
#endif
    FILE *fp = gDumpFile ? gDumpFile : stdout;
    fputs(cstr, fp);
    fflush(fp);
    free(cstr);
  }
}

void
nsGlobalWindowInner::EnsureReflowFlushAndPaint()
{
  MOZ_ASSERT(IsOuterWindow());
  NS_ASSERTION(mDocShell, "EnsureReflowFlushAndPaint() called with no "
               "docshell!");

  if (!mDocShell)
    return;

  nsCOMPtr<nsIPresShell> presShell = mDocShell->GetPresShell();

  if (!presShell)
    return;

  // Flush pending reflows.
  if (mDoc) {
    mDoc->FlushPendingNotifications(FlushType::Layout);
  }

  // Unsuppress painting.
  presShell->UnsuppressPainting();
}

// static
void
nsGlobalWindowInner::MakeScriptDialogTitle(nsAString& aOutTitle,
                                      nsIPrincipal* aSubjectPrincipal)
{
  MOZ_ASSERT(aSubjectPrincipal);

  aOutTitle.Truncate();

  // Try to get a host from the running principal -- this will do the
  // right thing for javascript: and data: documents.

  nsCOMPtr<nsIURI> uri;
  nsresult rv = aSubjectPrincipal->GetURI(getter_AddRefs(uri));
  // Note - The check for the current JSContext here isn't necessarily sensical.
  // It's just designed to preserve existing behavior during a mass-conversion
  // patch.
  if (NS_SUCCEEDED(rv) && uri && nsContentUtils::GetCurrentJSContext()) {
    // remove user:pass for privacy and spoof prevention

    nsCOMPtr<nsIURIFixup> fixup(do_GetService(NS_URIFIXUP_CONTRACTID));
    if (fixup) {
      nsCOMPtr<nsIURI> fixedURI;
      rv = fixup->CreateExposableURI(uri, getter_AddRefs(fixedURI));
      if (NS_SUCCEEDED(rv) && fixedURI) {
        nsAutoCString host;
        fixedURI->GetHost(host);

        if (!host.IsEmpty()) {
          // if this URI has a host we'll show it. For other
          // schemes (e.g. file:) we fall back to the localized
          // generic string

          nsAutoCString prepath;
          fixedURI->GetDisplayPrePath(prepath);

          NS_ConvertUTF8toUTF16 ucsPrePath(prepath);
          const char16_t *formatStrings[] = { ucsPrePath.get() };
          nsContentUtils::FormatLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES,
                                                "ScriptDlgHeading",
                                                formatStrings,
                                                aOutTitle);
        }
      }
    }
  }

  if (aOutTitle.IsEmpty()) {
    // We didn't find a host so use the generic heading
    nsContentUtils::GetLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES,
                                       "ScriptDlgGenericHeading",
                                       aOutTitle);
  }

  // Just in case
  if (aOutTitle.IsEmpty()) {
    NS_WARNING("could not get ScriptDlgGenericHeading string from string bundle");
    aOutTitle.AssignLiteral("[Script]");
  }
}

bool
nsGlobalWindowInner::CanMoveResizeWindows(CallerType aCallerType)
{
  MOZ_ASSERT(IsOuterWindow());

  // When called from chrome, we can avoid the following checks.
  if (aCallerType != CallerType::System) {
    // Don't allow scripts to move or resize windows that were not opened by a
    // script.
    if (!mHadOriginalOpener) {
      return false;
    }

    if (!CanSetProperty("dom.disable_window_move_resize")) {
      return false;
    }

    // Ignore the request if we have more than one tab in the window.
    uint32_t itemCount = 0;
    if (XRE_IsContentProcess()) {
      nsCOMPtr<nsIDocShell> docShell = GetDocShell();
      if (docShell) {
        nsCOMPtr<nsITabChild> child = docShell->GetTabChild();
        if (child) {
          child->SendGetTabCount(&itemCount);
        }
      }
    } else {
      nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner();
      if (!treeOwner || NS_FAILED(treeOwner->GetTabCount(&itemCount))) {
        itemCount = 0;
      }
    }
    if (itemCount > 1) {
      return false;
    }
  }

  if (mDocShell) {
    bool allow;
    nsresult rv = mDocShell->GetAllowWindowControl(&allow);
    if (NS_SUCCEEDED(rv) && !allow)
      return false;
  }

  if (gMouseDown && !gDragServiceDisabled) {
    nsCOMPtr<nsIDragService> ds =
      do_GetService("@mozilla.org/widget/dragservice;1");
    if (ds) {
      gDragServiceDisabled = true;
      ds->Suppress();
    }
  }
  return true;
}

bool
nsGlobalWindowInner::AlertOrConfirm(bool aAlert,
                               const nsAString& aMessage,
                               nsIPrincipal& aSubjectPrincipal,
                               ErrorResult& aError)
{
  // XXX This method is very similar to nsGlobalWindowInner::Prompt, make
  // sure any modifications here don't need to happen over there!
  MOZ_ASSERT(IsOuterWindow());

  if (!AreDialogsEnabled()) {
    // Just silently return.  In the case of alert(), the return value is
    // ignored.  In the case of confirm(), returning false is the same thing as
    // would happen if the user cancels.
    return false;
  }

  // Reset popup state while opening a modal dialog, and firing events
  // about the dialog, to prevent the current state from being active
  // the whole time a modal dialog is open.
  nsAutoPopupStatePusher popupStatePusher(openAbused, true);

  // Before bringing up the window, unsuppress painting and flush
  // pending reflows.
  EnsureReflowFlushAndPaint();

  nsAutoString title;
  MakeScriptDialogTitle(title, &aSubjectPrincipal);

  // Remove non-terminating null characters from the
  // string. See bug #310037.
  nsAutoString final;
  nsContentUtils::StripNullChars(aMessage, final);

  nsresult rv;
  nsCOMPtr<nsIPromptFactory> promptFac =
    do_GetService("@mozilla.org/prompter;1", &rv);
  if (NS_FAILED(rv)) {
    aError.Throw(rv);
    return false;
  }

  nsCOMPtr<nsIPrompt> prompt;
  aError = promptFac->GetPrompt(AsOuter(), NS_GET_IID(nsIPrompt),
                                getter_AddRefs(prompt));
  if (aError.Failed()) {
    return false;
  }

  // Always allow tab modal prompts for alert and confirm.
  if (nsCOMPtr<nsIWritablePropertyBag2> promptBag = do_QueryInterface(prompt)) {
    promptBag->SetPropertyAsBool(NS_LITERAL_STRING("allowTabModal"), true);
  }

  bool result = false;
  nsAutoSyncOperation sync(mDoc);
  if (ShouldPromptToBlockDialogs()) {
    bool disallowDialog = false;
    nsAutoString label;
    nsContentUtils::GetLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES,
                                       "ScriptDialogLabel", label);

    aError = aAlert ?
               prompt->AlertCheck(title.get(), final.get(), label.get(),
                                  &disallowDialog) :
               prompt->ConfirmCheck(title.get(), final.get(), label.get(),
                                    &disallowDialog, &result);

    if (disallowDialog)
      DisableDialogs();
  } else {
    aError = aAlert ?
               prompt->Alert(title.get(), final.get()) :
               prompt->Confirm(title.get(), final.get(), &result);
  }

  return result;
}

void
nsGlobalWindowInner::Alert(nsIPrincipal& aSubjectPrincipal,
                      ErrorResult& aError)
{
  MOZ_ASSERT(IsInnerWindow());
  Alert(EmptyString(), aSubjectPrincipal, aError);
}

void
nsGlobalWindowInner::AlertOuter(const nsAString& aMessage,
                           nsIPrincipal& aSubjectPrincipal,
                           ErrorResult& aError)
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());
  AlertOrConfirm(/* aAlert = */ true, aMessage, aSubjectPrincipal, aError);
}

void
nsGlobalWindowInner::Alert(const nsAString& aMessage,
                      nsIPrincipal& aSubjectPrincipal,
                      ErrorResult& aError)
{
  FORWARD_TO_OUTER_OR_THROW(AlertOuter, (aMessage, aSubjectPrincipal, aError),
                            aError, );
}

bool
nsGlobalWindowInner::ConfirmOuter(const nsAString& aMessage,
                             nsIPrincipal& aSubjectPrincipal,
                             ErrorResult& aError)
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());

  return AlertOrConfirm(/* aAlert = */ false, aMessage, aSubjectPrincipal,
                        aError);
}

bool
nsGlobalWindowInner::Confirm(const nsAString& aMessage,
                        nsIPrincipal& aSubjectPrincipal,
                        ErrorResult& aError)
{
  FORWARD_TO_OUTER_OR_THROW(ConfirmOuter, (aMessage, aSubjectPrincipal, aError),
                            aError, false);
}

already_AddRefed<Promise>
nsGlobalWindowInner::Fetch(const RequestOrUSVString& aInput,
                      const RequestInit& aInit,
                      CallerType aCallerType, ErrorResult& aRv)
{
  return FetchRequest(this, aInput, aInit, aCallerType, aRv);
}

void
nsGlobalWindowInner::PromptOuter(const nsAString& aMessage,
                            const nsAString& aInitial,
                            nsAString& aReturn,
                            nsIPrincipal& aSubjectPrincipal,
                            ErrorResult& aError)
{
  // XXX This method is very similar to nsGlobalWindowInner::AlertOrConfirm, make
  // sure any modifications here don't need to happen over there!
  MOZ_RELEASE_ASSERT(IsOuterWindow());

  SetDOMStringToNull(aReturn);

  if (!AreDialogsEnabled()) {
    // Return null, as if the user just canceled the prompt.
    return;
  }

  // Reset popup state while opening a modal dialog, and firing events
  // about the dialog, to prevent the current state from being active
  // the whole time a modal dialog is open.
  nsAutoPopupStatePusher popupStatePusher(openAbused, true);

  // Before bringing up the window, unsuppress painting and flush
  // pending reflows.
  EnsureReflowFlushAndPaint();

  nsAutoString title;
  MakeScriptDialogTitle(title, &aSubjectPrincipal);

  // Remove non-terminating null characters from the
  // string. See bug #310037.
  nsAutoString fixedMessage, fixedInitial;
  nsContentUtils::StripNullChars(aMessage, fixedMessage);
  nsContentUtils::StripNullChars(aInitial, fixedInitial);

  nsresult rv;
  nsCOMPtr<nsIPromptFactory> promptFac =
    do_GetService("@mozilla.org/prompter;1", &rv);
  if (NS_FAILED(rv)) {
    aError.Throw(rv);
    return;
  }

  nsCOMPtr<nsIPrompt> prompt;
  aError = promptFac->GetPrompt(AsOuter(), NS_GET_IID(nsIPrompt),
                                getter_AddRefs(prompt));
  if (aError.Failed()) {
    return;
  }

  // Always allow tab modal prompts for prompt.
  if (nsCOMPtr<nsIWritablePropertyBag2> promptBag = do_QueryInterface(prompt)) {
    promptBag->SetPropertyAsBool(NS_LITERAL_STRING("allowTabModal"), true);
  }

  // Pass in the default value, if any.
  char16_t *inoutValue = ToNewUnicode(fixedInitial);
  bool disallowDialog = false;

  nsAutoString label;
  label.SetIsVoid(true);
  if (ShouldPromptToBlockDialogs()) {
    nsContentUtils::GetLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES,
                                       "ScriptDialogLabel", label);
  }

  nsAutoSyncOperation sync(mDoc);
  bool ok;
  aError = prompt->Prompt(title.get(), fixedMessage.get(), &inoutValue,
                          label.IsVoid() ? nullptr : label.get(),
                          &disallowDialog, &ok);

  if (disallowDialog) {
    DisableDialogs();
  }

  if (aError.Failed()) {
    return;
  }

  nsString outValue;
  outValue.Adopt(inoutValue);

  if (ok && inoutValue) {
    aReturn.Assign(outValue);
  }
}

void
nsGlobalWindowInner::Prompt(const nsAString& aMessage, const nsAString& aInitial,
                       nsAString& aReturn,
                       nsIPrincipal& aSubjectPrincipal,
                       ErrorResult& aError)
{
  FORWARD_TO_OUTER_OR_THROW(PromptOuter,
                            (aMessage, aInitial, aReturn, aSubjectPrincipal,
                             aError),
                            aError, );
}

void
nsGlobalWindowInner::FocusOuter(ErrorResult& aError)
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());

  nsIFocusManager* fm = nsFocusManager::GetFocusManager();
  if (!fm) {
    return;
  }

  nsCOMPtr<nsIBaseWindow> baseWin = do_QueryInterface(mDocShell);

  bool isVisible = false;
  if (baseWin) {
    baseWin->GetVisibility(&isVisible);
  }

  if (!isVisible) {
    // A hidden tab is being focused, ignore this call.
    return;
  }

  nsCOMPtr<nsPIDOMWindowInner> caller = do_QueryInterface(GetEntryGlobal());
  nsPIDOMWindowOuter* callerOuter = caller ? caller->GetOuterWindow() : nullptr;
  nsCOMPtr<nsPIDOMWindowOuter> opener = GetOpener();

  // Enforce dom.disable_window_flip (for non-chrome), but still allow the
  // window which opened us to raise us at times when popups are allowed
  // (bugs 355482 and 369306).
  bool canFocus = CanSetProperty("dom.disable_window_flip") ||
                    (opener == callerOuter &&
                     RevisePopupAbuseLevel(gPopupControlState) < openBlocked);

  nsCOMPtr<mozIDOMWindowProxy> activeDOMWindow;
  fm->GetActiveWindow(getter_AddRefs(activeDOMWindow));

  nsCOMPtr<nsIDocShellTreeItem> rootItem;
  mDocShell->GetRootTreeItem(getter_AddRefs(rootItem));
  nsCOMPtr<nsPIDOMWindowOuter> rootWin = rootItem ? rootItem->GetWindow() : nullptr;
  auto* activeWindow = nsPIDOMWindowOuter::From(activeDOMWindow);
  bool isActive = (rootWin == activeWindow);

  nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
  if (treeOwnerAsWin && (canFocus || isActive)) {
    bool isEnabled = true;
    if (NS_SUCCEEDED(treeOwnerAsWin->GetEnabled(&isEnabled)) && !isEnabled) {
      NS_WARNING( "Should not try to set the focus on a disabled window" );
      return;
    }

    // XXXndeakin not sure what this is for or if it should go somewhere else
    nsCOMPtr<nsIEmbeddingSiteWindow> embeddingWin(do_GetInterface(treeOwnerAsWin));
    if (embeddingWin)
      embeddingWin->SetFocus();
  }

  if (!mDocShell) {
    return;
  }

  nsCOMPtr<nsIPresShell> presShell;
  // Don't look for a presshell if we're a root chrome window that's got
  // about:blank loaded.  We don't want to focus our widget in that case.
  // XXXbz should we really be checking for IsInitialDocument() instead?
  bool lookForPresShell = true;
  if (mDocShell->ItemType() == nsIDocShellTreeItem::typeChrome &&
      GetPrivateRoot() == AsOuter() && mDoc) {
    nsIURI* ourURI = mDoc->GetDocumentURI();
    if (ourURI) {
      lookForPresShell = !NS_IsAboutBlank(ourURI);
    }
  }

  if (lookForPresShell) {
    mDocShell->GetEldestPresShell(getter_AddRefs(presShell));
  }

  nsCOMPtr<nsIDocShellTreeItem> parentDsti;
  mDocShell->GetParent(getter_AddRefs(parentDsti));

  // set the parent's current focus to the frame containing this window.
  nsCOMPtr<nsPIDOMWindowOuter> parent =
    parentDsti ? parentDsti->GetWindow() : nullptr;
  if (parent) {
    nsCOMPtr<nsIDocument> parentdoc = parent->GetDoc();
    if (!parentdoc) {
      return;
    }

    nsIContent* frame = parentdoc->FindContentForSubDocument(mDoc);
    nsCOMPtr<nsIDOMElement> frameElement = do_QueryInterface(frame);
    if (frameElement) {
      uint32_t flags = nsIFocusManager::FLAG_NOSCROLL;
      if (canFocus)
        flags |= nsIFocusManager::FLAG_RAISE;
      aError = fm->SetFocus(frameElement, flags);
    }
    return;
  }

  if (canFocus) {
    // if there is no parent, this must be a toplevel window, so raise the
    // window if canFocus is true. If this is a child process, the raise
    // window request will get forwarded to the parent by the puppet widget.
    aError = fm->SetActiveWindow(AsOuter());
  }
}

void
nsGlobalWindowInner::Focus(ErrorResult& aError)
{
  FORWARD_TO_OUTER_OR_THROW(FocusOuter, (aError), aError, );
}

nsresult
nsGlobalWindowInner::Focus()
{
  FORWARD_TO_INNER(Focus, (), NS_ERROR_UNEXPECTED);

  ErrorResult rv;
  Focus(rv);

  return rv.StealNSResult();
}

void
nsGlobalWindowInner::BlurOuter()
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());

  // If dom.disable_window_flip == true, then content should not be allowed
  // to call this function (this would allow popunders, bug 369306)
  if (!CanSetProperty("dom.disable_window_flip")) {
    return;
  }

  // If embedding apps don't implement nsIEmbeddingSiteWindow, we
  // shouldn't throw exceptions to web content.

  nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner();
  nsCOMPtr<nsIEmbeddingSiteWindow> siteWindow(do_GetInterface(treeOwner));
  if (siteWindow) {
    // This method call may cause mDocShell to become nullptr.
    siteWindow->Blur();

    // if the root is focused, clear the focus
    nsIFocusManager* fm = nsFocusManager::GetFocusManager();
    if (fm && mDoc) {
      nsCOMPtr<nsIDOMElement> element;
      fm->GetFocusedElementForWindow(AsOuter(), false, nullptr, getter_AddRefs(element));
      nsCOMPtr<nsIContent> content = do_QueryInterface(element);
      if (content == mDoc->GetRootElement()) {
        fm->ClearFocus(AsOuter());
      }
    }
  }
}

void
nsGlobalWindowInner::Blur(ErrorResult& aError)
{
  FORWARD_TO_OUTER_OR_THROW(BlurOuter, (), aError, );
}

void
nsGlobalWindowInner::BackOuter(ErrorResult& aError)
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());

  nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mDocShell));
  if (!webNav) {
    aError.Throw(NS_ERROR_FAILURE);
    return;
  }

  aError = webNav->GoBack();
}

void
nsGlobalWindowInner::Back(ErrorResult& aError)
{
  FORWARD_TO_OUTER_OR_THROW(BackOuter, (aError), aError, );
}

void
nsGlobalWindowInner::ForwardOuter(ErrorResult& aError)
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());

  nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mDocShell));
  if (!webNav) {
    aError.Throw(NS_ERROR_FAILURE);
    return;
  }

  aError = webNav->GoForward();
}

void
nsGlobalWindowInner::Forward(ErrorResult& aError)
{
  FORWARD_TO_OUTER_OR_THROW(ForwardOuter, (aError), aError, );
}

void
nsGlobalWindowInner::HomeOuter(nsIPrincipal& aSubjectPrincipal, ErrorResult& aError)
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());

  if (!mDocShell) {
    return;
  }

  nsAutoString homeURL;
  Preferences::GetLocalizedString(PREF_BROWSER_STARTUP_HOMEPAGE, homeURL);

  if (homeURL.IsEmpty()) {
    // if all else fails, use this
#ifdef DEBUG_seth
    printf("all else failed.  using %s as the home page\n", DEFAULT_HOME_PAGE);
#endif
    CopyASCIItoUTF16(DEFAULT_HOME_PAGE, homeURL);
  }

#ifdef MOZ_PHOENIX
  {
    // Firefox lets the user specify multiple home pages to open in
    // individual tabs by separating them with '|'. Since we don't
    // have the machinery in place to easily open new tabs from here,
    // simply truncate the homeURL at the first '|' character to
    // prevent any possibilities of leaking the users list of home
    // pages to the first home page.
    //
    // Once bug https://bugzilla.mozilla.org/show_bug.cgi?id=221445 is
    // fixed we can revisit this.
    int32_t firstPipe = homeURL.FindChar('|');

    if (firstPipe > 0) {
      homeURL.Truncate(firstPipe);
    }
  }
#endif

  nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mDocShell));
  if (!webNav) {
    aError.Throw(NS_ERROR_FAILURE);
    return;
  }

  aError = webNav->LoadURI(homeURL.get(),
                           nsIWebNavigation::LOAD_FLAGS_NONE,
                           nullptr,
                           nullptr,
                           nullptr,
                           &aSubjectPrincipal);
}

void
nsGlobalWindowInner::Home(nsIPrincipal& aSubjectPrincipal, ErrorResult& aError)
{
  FORWARD_TO_OUTER_OR_THROW(HomeOuter, (aSubjectPrincipal, aError), aError, );
}

void
nsGlobalWindowInner::StopOuter(ErrorResult& aError)
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());

  nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mDocShell));
  if (webNav) {
    aError = webNav->Stop(nsIWebNavigation::STOP_ALL);
  }
}

void
nsGlobalWindowInner::Stop(ErrorResult& aError)
{
  FORWARD_TO_OUTER_OR_THROW(StopOuter, (aError), aError, );
}

/* static */
bool
nsGlobalWindowInner::IsWindowPrintEnabled(JSContext*, JSObject*)
{
  static bool called = false;
  static bool printDisabled = false;
  if (!called) {
    called = true;
    Preferences::AddBoolVarCache(&printDisabled, "dom.disable_window_print");
  }
  return !printDisabled;
}

void
nsGlobalWindowInner::PrintOuter(ErrorResult& aError)
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());

#ifdef NS_PRINTING
  if (!AreDialogsEnabled()) {
    // We probably want to keep throwing here; silently doing nothing is a bit
    // weird given the typical use cases of print().
    aError.Throw(NS_ERROR_NOT_AVAILABLE);
    return;
  }

  if (ShouldPromptToBlockDialogs() && !ConfirmDialogIfNeeded()) {
    aError.Throw(NS_ERROR_NOT_AVAILABLE);
    return;
  }

  nsCOMPtr<nsIWebBrowserPrint> webBrowserPrint;
  if (NS_SUCCEEDED(GetInterface(NS_GET_IID(nsIWebBrowserPrint),
                                getter_AddRefs(webBrowserPrint)))) {
    nsAutoSyncOperation sync(GetCurrentInnerWindowInternal() ?
                               GetCurrentInnerWindowInternal()->mDoc.get() :
                               nullptr);

    nsCOMPtr<nsIPrintSettingsService> printSettingsService =
      do_GetService("@mozilla.org/gfx/printsettings-service;1");

    nsCOMPtr<nsIPrintSettings> printSettings;
    if (printSettingsService) {
      bool printSettingsAreGlobal =
        Preferences::GetBool("print.use_global_printsettings", false);

      if (printSettingsAreGlobal) {
        printSettingsService->GetGlobalPrintSettings(getter_AddRefs(printSettings));

        nsAutoString printerName;
        printSettings->GetPrinterName(printerName);

        bool shouldGetDefaultPrinterName = printerName.IsEmpty();
#ifdef MOZ_X11
        // In Linux, GTK backend does not support per printer settings.
        // Calling GetDefaultPrinterName causes a sandbox violation (see Bug 1329216).
        // The printer name is not needed anywhere else on Linux before it gets to the parent.
        // In the parent, we will then query the default printer name if no name is set.
        // Unless we are in the parent, we will skip this part.
        if (!XRE_IsParentProcess()) {
          shouldGetDefaultPrinterName = false;
        }
#endif
        if (shouldGetDefaultPrinterName) {
          printSettingsService->GetDefaultPrinterName(printerName);
          printSettings->SetPrinterName(printerName);
        }
        printSettingsService->InitPrintSettingsFromPrinter(printerName,
                                                           printSettings);
        printSettingsService->InitPrintSettingsFromPrefs(printSettings,
                                                         true,
                                                         nsIPrintSettings::kInitSaveAll);
      } else {
        printSettingsService->GetNewPrintSettings(getter_AddRefs(printSettings));
      }

      EnterModalState();
      webBrowserPrint->Print(printSettings, nullptr);
      LeaveModalState();

      bool savePrintSettings =
        Preferences::GetBool("print.save_print_settings", false);
      if (printSettingsAreGlobal && savePrintSettings) {
        printSettingsService->
          SavePrintSettingsToPrefs(printSettings,
                                   true,
                                   nsIPrintSettings::kInitSaveAll);
        printSettingsService->
          SavePrintSettingsToPrefs(printSettings,
                                   false,
                                   nsIPrintSettings::kInitSavePrinterName);
      }
    } else {
      webBrowserPrint->GetGlobalPrintSettings(getter_AddRefs(printSettings));
      webBrowserPrint->Print(printSettings, nullptr);
    }
  }
#endif //NS_PRINTING
}

void
nsGlobalWindowInner::Print(ErrorResult& aError)
{
  FORWARD_TO_OUTER_OR_THROW(PrintOuter, (aError), aError, );
}

void
nsGlobalWindowInner::MoveToOuter(int32_t aXPos, int32_t aYPos,
                            CallerType aCallerType, ErrorResult& aError)
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());
  /*
   * If caller is not chrome and the user has not explicitly exempted the site,
   * prevent window.moveTo() by exiting early
   */

  if (!CanMoveResizeWindows(aCallerType) || IsFrame()) {
    return;
  }

  nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
  if (!treeOwnerAsWin) {
    aError.Throw(NS_ERROR_FAILURE);
    return;
  }

  nsCOMPtr<nsIScreenManager> screenMgr =
    do_GetService("@mozilla.org/gfx/screenmanager;1");
  nsCOMPtr<nsIScreen> screen;
  if (screenMgr) {
    CSSIntSize size;
    GetInnerSize(size);
    screenMgr->ScreenForRect(aXPos, aYPos, size.width, size.height,
                             getter_AddRefs(screen));
  }

  if (screen) {
    // On secondary displays, the "CSS px" coordinates are offset so that they
    // share their origin with global desktop pixels, to avoid ambiguities in
    // the coordinate space when there are displays with different DPIs.
    // (See the corresponding code in GetScreenXY() above.)
    int32_t screenLeftDeskPx, screenTopDeskPx, w, h;
    screen->GetRectDisplayPix(&screenLeftDeskPx, &screenTopDeskPx, &w, &h);
    CSSIntPoint cssPos(aXPos - screenLeftDeskPx, aYPos - screenTopDeskPx);
    CheckSecurityLeftAndTop(&cssPos.x, &cssPos.y, aCallerType);

    double scale;
    screen->GetDefaultCSSScaleFactor(&scale);
    LayoutDevicePoint devPos = cssPos * CSSToLayoutDeviceScale(scale);

    screen->GetContentsScaleFactor(&scale);
    DesktopPoint deskPos = devPos / DesktopToLayoutDeviceScale(scale);
    aError = treeOwnerAsWin->SetPositionDesktopPix(screenLeftDeskPx + deskPos.x,
                                                   screenTopDeskPx + deskPos.y);
  } else {
    // We couldn't find a screen? Just assume a 1:1 mapping.
    CSSIntPoint cssPos(aXPos, aXPos);
    CheckSecurityLeftAndTop(&cssPos.x, &cssPos.y, aCallerType);
    LayoutDevicePoint devPos = cssPos * CSSToLayoutDeviceScale(1.0);
    aError = treeOwnerAsWin->SetPosition(devPos.x, devPos.y);
  }

  CheckForDPIChange();
}

void
nsGlobalWindowInner::MoveTo(int32_t aXPos, int32_t aYPos,
                       CallerType aCallerType, ErrorResult& aError)
{
  FORWARD_TO_OUTER_OR_THROW(MoveToOuter,
                            (aXPos, aYPos, aCallerType, aError), aError, );
}

void
nsGlobalWindowInner::MoveByOuter(int32_t aXDif, int32_t aYDif,
                            CallerType aCallerType, ErrorResult& aError)
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());

  /*
   * If caller is not chrome and the user has not explicitly exempted the site,
   * prevent window.moveBy() by exiting early
   */

  if (!CanMoveResizeWindows(aCallerType) || IsFrame()) {
    return;
  }

  nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
  if (!treeOwnerAsWin) {
    aError.Throw(NS_ERROR_FAILURE);
    return;
  }

  // To do this correctly we have to convert what we get from GetPosition
  // into CSS pixels, add the arguments, do the security check, and
  // then convert back to device pixels for the call to SetPosition.

  int32_t x, y;
  aError = treeOwnerAsWin->GetPosition(&x, &y);
  if (aError.Failed()) {
    return;
  }

  // mild abuse of a "size" object so we don't need more helper functions
  nsIntSize cssPos(DevToCSSIntPixels(nsIntSize(x, y)));

  cssPos.width += aXDif;
  cssPos.height += aYDif;

  CheckSecurityLeftAndTop(&cssPos.width, &cssPos.height, aCallerType);

  nsIntSize newDevPos(CSSToDevIntPixels(cssPos));

  aError = treeOwnerAsWin->SetPosition(newDevPos.width, newDevPos.height);

  CheckForDPIChange();
}

void
nsGlobalWindowInner::MoveBy(int32_t aXDif, int32_t aYDif,
                       CallerType aCallerType, ErrorResult& aError)
{
  FORWARD_TO_OUTER_OR_THROW(MoveByOuter,
                            (aXDif, aYDif, aCallerType, aError), aError, );
}

nsresult
nsGlobalWindowInner::MoveBy(int32_t aXDif, int32_t aYDif)
{
  FORWARD_TO_OUTER(MoveBy, (aXDif, aYDif), NS_ERROR_UNEXPECTED);

  ErrorResult rv;
  MoveByOuter(aXDif, aYDif, CallerType::System, rv);

  return rv.StealNSResult();
}

void
nsGlobalWindowInner::ResizeToOuter(int32_t aWidth, int32_t aHeight,
                              CallerType aCallerType,
                              ErrorResult& aError)
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());

  /*
   * If caller is a browser-element then dispatch a resize event to
   * the embedder.
   */
  if (mDocShell && mDocShell->GetIsMozBrowser()) {
    CSSIntSize size(aWidth, aHeight);
    if (!DispatchResizeEvent(size)) {
      // The embedder chose to prevent the default action for this
      // event, so let's not resize this window after all...
      return;
    }
  }

  /*
   * If caller is not chrome and the user has not explicitly exempted the site,
   * prevent window.resizeTo() by exiting early
   */

  if (!CanMoveResizeWindows(aCallerType) || IsFrame()) {
    return;
  }

  nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
  if (!treeOwnerAsWin) {
    aError.Throw(NS_ERROR_FAILURE);
    return;
  }

  nsIntSize cssSize(aWidth, aHeight);
  CheckSecurityWidthAndHeight(&cssSize.width, &cssSize.height, aCallerType);

  nsIntSize devSz(CSSToDevIntPixels(cssSize));

  aError = treeOwnerAsWin->SetSize(devSz.width, devSz.height, true);

  CheckForDPIChange();
}

void
nsGlobalWindowInner::ResizeTo(int32_t aWidth, int32_t aHeight,
                         CallerType aCallerType, ErrorResult& aError)
{
  FORWARD_TO_OUTER_OR_THROW(ResizeToOuter,
                            (aWidth, aHeight, aCallerType, aError), aError, );
}

void
nsGlobalWindowInner::ResizeByOuter(int32_t aWidthDif, int32_t aHeightDif,
                              CallerType aCallerType, ErrorResult& aError)
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());

  /*
   * If caller is a browser-element then dispatch a resize event to
   * parent.
   */
  if (mDocShell && mDocShell->GetIsMozBrowser()) {
    CSSIntSize size;
    if (NS_FAILED(GetInnerSize(size))) {
      return;
    }

    size.width += aWidthDif;
    size.height += aHeightDif;

    if (!DispatchResizeEvent(size)) {
      // The embedder chose to prevent the default action for this
      // event, so let's not resize this window after all...
      return;
    }
  }

  /*
   * If caller is not chrome and the user has not explicitly exempted the site,
   * prevent window.resizeBy() by exiting early
   */

  if (!CanMoveResizeWindows(aCallerType) || IsFrame()) {
    return;
  }

  nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
  if (!treeOwnerAsWin) {
    aError.Throw(NS_ERROR_FAILURE);
    return;
  }

  int32_t width, height;
  aError = treeOwnerAsWin->GetSize(&width, &height);
  if (aError.Failed()) {
    return;
  }

  // To do this correctly we have to convert what we got from GetSize
  // into CSS pixels, add the arguments, do the security check, and
  // then convert back to device pixels for the call to SetSize.

  nsIntSize cssSize(DevToCSSIntPixels(nsIntSize(width, height)));

  cssSize.width += aWidthDif;
  cssSize.height += aHeightDif;

  CheckSecurityWidthAndHeight(&cssSize.width, &cssSize.height, aCallerType);

  nsIntSize newDevSize(CSSToDevIntPixels(cssSize));

  aError = treeOwnerAsWin->SetSize(newDevSize.width, newDevSize.height, true);

  CheckForDPIChange();
}

void
nsGlobalWindowInner::ResizeBy(int32_t aWidthDif, int32_t aHeightDif,
                         CallerType aCallerType, ErrorResult& aError)
{
  FORWARD_TO_OUTER_OR_THROW(ResizeByOuter,
                            (aWidthDif, aHeightDif, aCallerType, aError),
                            aError, );
}

void
nsGlobalWindowInner::SizeToContentOuter(CallerType aCallerType, ErrorResult& aError)
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());

  if (!mDocShell) {
    return;
  }

  /*
   * If caller is not chrome and the user has not explicitly exempted the site,
   * prevent window.sizeToContent() by exiting early
   */

  if (!CanMoveResizeWindows(aCallerType) || IsFrame()) {
    return;
  }

  // The content viewer does a check to make sure that it's a content
  // viewer for a toplevel docshell.
  nsCOMPtr<nsIContentViewer> cv;
  mDocShell->GetContentViewer(getter_AddRefs(cv));
  if (!cv) {
    aError.Throw(NS_ERROR_FAILURE);
    return;
  }

  int32_t width, height;
  aError = cv->GetContentSize(&width, &height);
  if (aError.Failed()) {
    return;
  }

  // Make sure the new size is following the CheckSecurityWidthAndHeight
  // rules.
  nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner();
  if (!treeOwner) {
    aError.Throw(NS_ERROR_FAILURE);
    return;
  }

  nsIntSize cssSize(DevToCSSIntPixels(nsIntSize(width, height)));
  CheckSecurityWidthAndHeight(&cssSize.width, &cssSize.height, aCallerType);

  nsIntSize newDevSize(CSSToDevIntPixels(cssSize));

  aError = treeOwner->SizeShellTo(mDocShell, newDevSize.width,
                                  newDevSize.height);
}

void
nsGlobalWindowInner::SizeToContent(CallerType aCallerType, ErrorResult& aError)
{
  FORWARD_TO_OUTER_OR_THROW(SizeToContentOuter, (aCallerType, aError),
                            aError, );
}

already_AddRefed<nsPIWindowRoot>
nsGlobalWindowInner::GetTopWindowRoot()
{
  nsPIDOMWindowOuter* piWin = GetPrivateRoot();
  if (!piWin) {
    return nullptr;
  }

  nsCOMPtr<nsPIWindowRoot> window = do_QueryInterface(piWin->GetChromeEventHandler());
  return window.forget();
}

void
nsGlobalWindowInner::Scroll(double aXScroll, double aYScroll)
{
  // Convert -Inf, Inf, and NaN to 0; otherwise, convert by C-style cast.
  auto scrollPos = CSSIntPoint::Truncate(mozilla::ToZeroIfNonfinite(aXScroll),
                                         mozilla::ToZeroIfNonfinite(aYScroll));
  ScrollTo(scrollPos, ScrollOptions());
}

void
nsGlobalWindowInner::ScrollTo(double aXScroll, double aYScroll)
{
  // Convert -Inf, Inf, and NaN to 0; otherwise, convert by C-style cast.
  auto scrollPos = CSSIntPoint::Truncate(mozilla::ToZeroIfNonfinite(aXScroll),
                                         mozilla::ToZeroIfNonfinite(aYScroll));
  ScrollTo(scrollPos, ScrollOptions());
}

void
nsGlobalWindowInner::ScrollTo(const ScrollToOptions& aOptions)
{
  // When scrolling to a non-zero offset, we need to determine whether that
  // position is within our scrollable range, so we need updated layout
  // information which requires a layout flush, otherwise all we need is to
  // flush frames to be able to access our scrollable frame here.
  FlushType flushType = ((aOptions.mLeft.WasPassed() &&
                          aOptions.mLeft.Value() > 0) ||
                         (aOptions.mTop.WasPassed() &&
                          aOptions.mTop.Value() > 0)) ?
                          FlushType::Layout :
                          FlushType::Frames;
  FlushPendingNotifications(flushType);
  nsIScrollableFrame *sf = GetScrollFrame();

  if (sf) {
    CSSIntPoint scrollPos = sf->GetScrollPositionCSSPixels();
    if (aOptions.mLeft.WasPassed()) {
      scrollPos.x = mozilla::ToZeroIfNonfinite(aOptions.mLeft.Value());
    }
    if (aOptions.mTop.WasPassed()) {
      scrollPos.y = mozilla::ToZeroIfNonfinite(aOptions.mTop.Value());
    }

    ScrollTo(scrollPos, aOptions);
  }
}

void
nsGlobalWindowInner::Scroll(const ScrollToOptions& aOptions)
{
  ScrollTo(aOptions);
}

void
nsGlobalWindowInner::ScrollTo(const CSSIntPoint& aScroll,
                         const ScrollOptions& aOptions)
{
  // When scrolling to a non-zero offset, we need to determine whether that
  // position is within our scrollable range, so we need updated layout
  // information which requires a layout flush, otherwise all we need is to
  // flush frames to be able to access our scrollable frame here.
  FlushType flushType = (aScroll.x || aScroll.y) ?
                          FlushType::Layout :
                          FlushType::Frames;
  FlushPendingNotifications(flushType);
  nsIScrollableFrame *sf = GetScrollFrame();

  if (sf) {
    // Here we calculate what the max pixel value is that we can
    // scroll to, we do this by dividing maxint with the pixel to
    // twips conversion factor, and subtracting 4, the 4 comes from
    // experimenting with this value, anything less makes the view
    // code not scroll correctly, I have no idea why. -- jst
    const int32_t maxpx = nsPresContext::AppUnitsToIntCSSPixels(0x7fffffff) - 4;

    CSSIntPoint scroll(aScroll);
    if (scroll.x > maxpx) {
      scroll.x = maxpx;
    }

    if (scroll.y > maxpx) {
      scroll.y = maxpx;
    }

    bool smoothScroll = sf->GetScrollbarStyles().IsSmoothScroll(aOptions.mBehavior);

    sf->ScrollToCSSPixels(scroll, smoothScroll
                            ? nsIScrollableFrame::SMOOTH_MSD
                            : nsIScrollableFrame::INSTANT);
  }
}

void
nsGlobalWindowInner::ScrollBy(double aXScrollDif, double aYScrollDif)
{
  FlushPendingNotifications(FlushType::Layout);
  nsIScrollableFrame *sf = GetScrollFrame();

  if (sf) {
    // Convert -Inf, Inf, and NaN to 0; otherwise, convert by C-style cast.
    auto scrollDif = CSSIntPoint::Truncate(mozilla::ToZeroIfNonfinite(aXScrollDif),
                                           mozilla::ToZeroIfNonfinite(aYScrollDif));
    // It seems like it would make more sense for ScrollBy to use
    // SMOOTH mode, but tests seem to depend on the synchronous behaviour.
    // Perhaps Web content does too.
    ScrollTo(sf->GetScrollPositionCSSPixels() + scrollDif, ScrollOptions());
  }
}

void
nsGlobalWindowInner::ScrollBy(const ScrollToOptions& aOptions)
{
  FlushPendingNotifications(FlushType::Layout);
  nsIScrollableFrame *sf = GetScrollFrame();

  if (sf) {
    CSSIntPoint scrollPos = sf->GetScrollPositionCSSPixels();
    if (aOptions.mLeft.WasPassed()) {
      scrollPos.x += mozilla::ToZeroIfNonfinite(aOptions.mLeft.Value());
    }
    if (aOptions.mTop.WasPassed()) {
      scrollPos.y += mozilla::ToZeroIfNonfinite(aOptions.mTop.Value());
    }

    ScrollTo(scrollPos, aOptions);
  }
}

void
nsGlobalWindowInner::ScrollByLines(int32_t numLines,
                              const ScrollOptions& aOptions)
{
  MOZ_ASSERT(IsInnerWindow());

  FlushPendingNotifications(FlushType::Layout);
  nsIScrollableFrame *sf = GetScrollFrame();
  if (sf) {
    // It seems like it would make more sense for ScrollByLines to use
    // SMOOTH mode, but tests seem to depend on the synchronous behaviour.
    // Perhaps Web content does too.
    bool smoothScroll = sf->GetScrollbarStyles().IsSmoothScroll(aOptions.mBehavior);

    sf->ScrollBy(nsIntPoint(0, numLines), nsIScrollableFrame::LINES,
                 smoothScroll
                   ? nsIScrollableFrame::SMOOTH_MSD
                   : nsIScrollableFrame::INSTANT);
  }
}

void
nsGlobalWindowInner::ScrollByPages(int32_t numPages,
                              const ScrollOptions& aOptions)
{
  MOZ_ASSERT(IsInnerWindow());

  FlushPendingNotifications(FlushType::Layout);
  nsIScrollableFrame *sf = GetScrollFrame();
  if (sf) {
    // It seems like it would make more sense for ScrollByPages to use
    // SMOOTH mode, but tests seem to depend on the synchronous behaviour.
    // Perhaps Web content does too.
    bool smoothScroll = sf->GetScrollbarStyles().IsSmoothScroll(aOptions.mBehavior);

    sf->ScrollBy(nsIntPoint(0, numPages), nsIScrollableFrame::PAGES,
                 smoothScroll
                   ? nsIScrollableFrame::SMOOTH_MSD
                   : nsIScrollableFrame::INSTANT);
  }
}

void
nsGlobalWindowInner::MozScrollSnap()
{
  MOZ_ASSERT(IsInnerWindow());

  FlushPendingNotifications(FlushType::Layout);
  nsIScrollableFrame *sf = GetScrollFrame();
  if (sf) {
    sf->ScrollSnap();
  }
}

void
nsGlobalWindowInner::ClearTimeout(int32_t aHandle)
{
  MOZ_RELEASE_ASSERT(IsInnerWindow());

  if (aHandle > 0) {
    mTimeoutManager->ClearTimeout(aHandle, Timeout::Reason::eTimeoutOrInterval);
  }
}

void
nsGlobalWindowInner::ClearInterval(int32_t aHandle)
{
  MOZ_RELEASE_ASSERT(IsInnerWindow());

  if (aHandle > 0) {
    mTimeoutManager->ClearTimeout(aHandle, Timeout::Reason::eTimeoutOrInterval);
  }
}

void
nsGlobalWindowInner::SetResizable(bool aResizable) const
{
  // nop
}

void
nsGlobalWindowInner::CaptureEvents()
{
  if (mDoc) {
    mDoc->WarnOnceAbout(nsIDocument::eUseOfCaptureEvents);
  }
}

void
nsGlobalWindowInner::ReleaseEvents()
{
  if (mDoc) {
    mDoc->WarnOnceAbout(nsIDocument::eUseOfReleaseEvents);
  }
}

void
nsGlobalWindowInner::FirePopupBlockedEvent(nsIDocument* aDoc,
                                      nsIURI* aPopupURI,
                                      const nsAString& aPopupWindowName,
                                      const nsAString& aPopupWindowFeatures)
{
  MOZ_ASSERT(IsOuterWindow(), "All callers seem to assume we're an outer window?");
  MOZ_ASSERT(aDoc);

  // Fire a "DOMPopupBlocked" event so that the UI can hear about
  // blocked popups.
  PopupBlockedEventInit init;
  init.mBubbles = true;
  init.mCancelable = true;
  // XXX: This is a different object, but webidl requires an inner window here
  // now.
  init.mRequestingWindow = GetCurrentInnerWindowInternal();
  init.mPopupWindowURI = aPopupURI;
  init.mPopupWindowName = aPopupWindowName;
  init.mPopupWindowFeatures = aPopupWindowFeatures;

  RefPtr<PopupBlockedEvent> event =
    PopupBlockedEvent::Constructor(aDoc,
                                   NS_LITERAL_STRING("DOMPopupBlocked"),
                                   init);

  event->SetTrusted(true);

  bool defaultActionEnabled;
  aDoc->DispatchEvent(event, &defaultActionEnabled);
}

// static
bool
nsGlobalWindowInner::CanSetProperty(const char *aPrefName)
{
  // Chrome can set any property.
  if (nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
    return true;
  }

  // If the pref is set to true, we can not set the property
  // and vice versa.
  return !Preferences::GetBool(aPrefName, true);
}

bool
nsGlobalWindowInner::PopupWhitelisted()
{
  MOZ_CRASH("Outer Window only");
}

/*
 * Examine the current document state to see if we're in a way that is
 * typically abused by web designers. The window.open code uses this
 * routine to determine whether to allow the new window.
 * Returns a value from the PopupControlState enum.
 */
PopupControlState
nsGlobalWindowInner::RevisePopupAbuseLevel(PopupControlState aControl)
{
  MOZ_CRASH("Outer window only");
}

/* If a window open is blocked, fire the appropriate DOM events. */
void
nsGlobalWindowInner::FireAbuseEvents(const nsAString &aPopupURL,
                                const nsAString &aPopupWindowName,
                                const nsAString &aPopupWindowFeatures)
{
  MOZ_ASSERT(IsOuterWindow());
  // fetch the URI of the window requesting the opened window

  nsCOMPtr<nsPIDOMWindowOuter> window = GetTop();
  if (!window) {
    return;
  }

  nsCOMPtr<nsIDocument> topDoc = window->GetDoc();
  nsCOMPtr<nsIURI> popupURI;

  // build the URI of the would-have-been popup window
  // (see nsWindowWatcher::URIfromURL)

  // first, fetch the opener's base URI

  nsIURI *baseURL = nullptr;

  nsCOMPtr<nsIDocument> doc = GetEntryDocument();
  if (doc)
    baseURL = doc->GetDocBaseURI();

  // use the base URI to build what would have been the popup's URI
  nsCOMPtr<nsIIOService> ios(do_GetService(NS_IOSERVICE_CONTRACTID));
  if (ios)
    ios->NewURI(NS_ConvertUTF16toUTF8(aPopupURL), nullptr, baseURL,
                getter_AddRefs(popupURI));

  // fire an event chock full of informative URIs
  FirePopupBlockedEvent(topDoc, popupURI, aPopupWindowName,
                        aPopupWindowFeatures);
}

already_AddRefed<nsPIDOMWindowOuter>
nsGlobalWindowInner::OpenOuter(const nsAString& aUrl, const nsAString& aName,
                          const nsAString& aOptions, ErrorResult& aError)
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());
  nsCOMPtr<nsPIDOMWindowOuter> window;
  aError = OpenJS(aUrl, aName, aOptions, getter_AddRefs(window));
  return window.forget();
}


already_AddRefed<nsPIDOMWindowOuter>
nsGlobalWindowInner::Open(const nsAString& aUrl, const nsAString& aName,
                     const nsAString& aOptions, ErrorResult& aError)
{
  FORWARD_TO_OUTER_OR_THROW(OpenOuter, (aUrl, aName, aOptions, aError), aError,
                            nullptr);
}

nsresult
nsGlobalWindowInner::Open(const nsAString& aUrl, const nsAString& aName,
                     const nsAString& aOptions, nsIDocShellLoadInfo* aLoadInfo,
                     bool aForceNoOpener, nsPIDOMWindowOuter **_retval)
{
  FORWARD_TO_OUTER(Open, (aUrl, aName, aOptions, aLoadInfo, aForceNoOpener,
                          _retval),
                   NS_ERROR_NOT_INITIALIZED);
  return OpenInternal(aUrl, aName, aOptions,
                      false,          // aDialog
                      false,          // aContentModal
                      true,           // aCalledNoScript
                      false,          // aDoJSFixups
                      true,           // aNavigate
                      nullptr, nullptr,  // No args
                      aLoadInfo,
                      aForceNoOpener,
                      _retval);
}

nsresult
nsGlobalWindowInner::OpenJS(const nsAString& aUrl, const nsAString& aName,
                       const nsAString& aOptions, nsPIDOMWindowOuter **_retval)
{
  MOZ_ASSERT(IsOuterWindow());
  return OpenInternal(aUrl, aName, aOptions,
                      false,          // aDialog
                      false,          // aContentModal
                      false,          // aCalledNoScript
                      true,           // aDoJSFixups
                      true,           // aNavigate
                      nullptr, nullptr,  // No args
                      nullptr,        // aLoadInfo
                      false,          // aForceNoOpener
                      _retval);
}

// like Open, but attaches to the new window any extra parameters past
// [features] as a JS property named "arguments"
nsresult
nsGlobalWindowInner::OpenDialog(const nsAString& aUrl, const nsAString& aName,
                           const nsAString& aOptions,
                           nsISupports* aExtraArgument,
                           nsPIDOMWindowOuter** _retval)
{
  MOZ_ASSERT(IsOuterWindow());
  return OpenInternal(aUrl, aName, aOptions,
                      true,                    // aDialog
                      false,                   // aContentModal
                      true,                    // aCalledNoScript
                      false,                   // aDoJSFixups
                      true,                    // aNavigate
                      nullptr, aExtraArgument, // Arguments
                      nullptr,                 // aLoadInfo
                      false,                   // aForceNoOpener
                      _retval);
}

// Like Open, but passes aNavigate=false.
/* virtual */ nsresult
nsGlobalWindowInner::OpenNoNavigate(const nsAString& aUrl,
                               const nsAString& aName,
                               const nsAString& aOptions,
                               nsPIDOMWindowOuter **_retval)
{
  MOZ_ASSERT(IsOuterWindow());
  return OpenInternal(aUrl, aName, aOptions,
                      false,          // aDialog
                      false,          // aContentModal
                      true,           // aCalledNoScript
                      false,          // aDoJSFixups
                      false,          // aNavigate
                      nullptr, nullptr,  // No args
                      nullptr,        // aLoadInfo
                      false,          // aForceNoOpener
                      _retval);

}

already_AddRefed<nsPIDOMWindowOuter>
nsGlobalWindowInner::OpenDialogOuter(JSContext* aCx, const nsAString& aUrl,
                                const nsAString& aName, const nsAString& aOptions,
                                const Sequence<JS::Value>& aExtraArgument,
                                ErrorResult& aError)
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());

  nsCOMPtr<nsIJSArgArray> argvArray;
  aError = NS_CreateJSArgv(aCx, aExtraArgument.Length(),
                           aExtraArgument.Elements(),
                           getter_AddRefs(argvArray));
  if (aError.Failed()) {
    return nullptr;
  }

  nsCOMPtr<nsPIDOMWindowOuter> dialog;
  aError = OpenInternal(aUrl, aName, aOptions,
                        true,             // aDialog
                        false,            // aContentModal
                        false,            // aCalledNoScript
                        false,            // aDoJSFixups
                        true,                // aNavigate
                        argvArray, nullptr,  // Arguments
                        nullptr,          // aLoadInfo
                        false,            // aForceNoOpener
                        getter_AddRefs(dialog));
  return dialog.forget();
}

already_AddRefed<nsPIDOMWindowOuter>
nsGlobalWindowInner::OpenDialog(JSContext* aCx, const nsAString& aUrl,
                           const nsAString& aName, const nsAString& aOptions,
                           const Sequence<JS::Value>& aExtraArgument,
                           ErrorResult& aError)
{
  FORWARD_TO_OUTER_OR_THROW(OpenDialogOuter,
                            (aCx, aUrl, aName, aOptions, aExtraArgument, aError),
                            aError, nullptr);
}

already_AddRefed<nsPIDOMWindowOuter>
nsGlobalWindowInner::GetFramesOuter()
{
  RefPtr<nsPIDOMWindowOuter> frames(AsOuter());
  FlushPendingNotifications(FlushType::ContentAndNotify);
  return frames.forget();
}

already_AddRefed<nsPIDOMWindowOuter>
nsGlobalWindowInner::GetFrames(ErrorResult& aError)
{
  FORWARD_TO_OUTER_OR_THROW(GetFramesOuter, (), aError, nullptr);
}

nsGlobalWindowInner*
nsGlobalWindowInner::CallerInnerWindow()
{
  JSContext *cx = nsContentUtils::GetCurrentJSContext();
  NS_ENSURE_TRUE(cx, nullptr);
  nsIGlobalObject* global = GetIncumbentGlobal();
  NS_ENSURE_TRUE(global, nullptr);
  JS::Rooted<JSObject*> scope(cx, global->GetGlobalJSObject());
  NS_ENSURE_TRUE(scope, nullptr);

  // When Jetpack runs content scripts inside a sandbox, it uses
  // sandboxPrototype to make them appear as though they're running in the
  // scope of the page. So when a content script invokes postMessage, it expects
  // the |source| of the received message to be the window set as the
  // sandboxPrototype. This used to work incidentally for unrelated reasons, but
  // now we need to do some special handling to support it.
  if (xpc::IsSandbox(scope)) {
    JSAutoCompartment ac(cx, scope);
    JS::Rooted<JSObject*> scopeProto(cx);
    bool ok = JS_GetPrototype(cx, scope, &scopeProto);
    NS_ENSURE_TRUE(ok, nullptr);
    if (scopeProto && xpc::IsSandboxPrototypeProxy(scopeProto) &&
        (scopeProto = js::CheckedUnwrap(scopeProto, /* stopAtWindowProxy = */ false)))
    {
      global = xpc::NativeGlobal(scopeProto);
      NS_ENSURE_TRUE(global, nullptr);
    }
  }

  // The calling window must be holding a reference, so we can return a weak
  // pointer.
  nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(global);
  return nsGlobalWindowInner::Cast(win);
}

void
nsGlobalWindowInner::PostMessageMozOuter(JSContext* aCx, JS::Handle<JS::Value> aMessage,
                                    const nsAString& aTargetOrigin,
                                    JS::Handle<JS::Value> aTransfer,
                                    nsIPrincipal& aSubjectPrincipal,
                                    ErrorResult& aError)
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());

  //
  // Window.postMessage is an intentional subversion of the same-origin policy.
  // As such, this code must be particularly careful in the information it
  // exposes to calling code.
  //
  // http://www.whatwg.org/specs/web-apps/current-work/multipage/section-crossDocumentMessages.html
  //

  // First, get the caller's window
  RefPtr<nsGlobalWindowInner> callerInnerWin = CallerInnerWindow();
  nsIPrincipal* callerPrin;
  if (callerInnerWin) {
    MOZ_ASSERT(callerInnerWin->IsInnerWindow(),
               "should have gotten an inner window here");

    // Compute the caller's origin either from its principal or, in the case the
    // principal doesn't carry a URI (e.g. the system principal), the caller's
    // document.  We must get this now instead of when the event is created and
    // dispatched, because ultimately it is the identity of the calling window
    // *now* that determines who sent the message (and not an identity which might
    // have changed due to intervening navigations).
    callerPrin = callerInnerWin->GetPrincipal();
  }
  else {
    // In case the global is not a window, it can be a sandbox, and the sandbox's
    // principal can be used for the security check.
    nsIGlobalObject* global = GetIncumbentGlobal();
    NS_ASSERTION(global, "Why is there no global object?");
    callerPrin = global->PrincipalOrNull();
  }
  if (!callerPrin) {
    return;
  }

  nsCOMPtr<nsIURI> callerOuterURI;
  if (NS_FAILED(callerPrin->GetURI(getter_AddRefs(callerOuterURI)))) {
    return;
  }

  nsAutoString origin;
  if (callerOuterURI) {
    // if the principal has a URI, use that to generate the origin
    nsContentUtils::GetUTFOrigin(callerPrin, origin);
  }
  else if (callerInnerWin) {
    // otherwise use the URI of the document to generate origin
    nsCOMPtr<nsIDocument> doc = callerInnerWin->GetExtantDoc();
    if (!doc) {
      return;
    }
    callerOuterURI = doc->GetDocumentURI();
    // if the principal has a URI, use that to generate the origin
    nsContentUtils::GetUTFOrigin(callerOuterURI, origin);
  }
  else {
    // in case of a sandbox with a system principal origin can be empty
    if (!nsContentUtils::IsSystemPrincipal(callerPrin)) {
      return;
    }
  }

  // Convert the provided origin string into a URI for comparison purposes.
  nsCOMPtr<nsIPrincipal> providedPrincipal;

  if (aTargetOrigin.EqualsASCII("/")) {
    providedPrincipal = callerPrin;
  }
  // "*" indicates no specific origin is required.
  else if (!aTargetOrigin.EqualsASCII("*")) {
    nsCOMPtr<nsIURI> originURI;
    if (NS_FAILED(NS_NewURI(getter_AddRefs(originURI), aTargetOrigin))) {
      aError.Throw(NS_ERROR_DOM_SYNTAX_ERR);
      return;
    }

    if (NS_FAILED(originURI->SetUserPass(EmptyCString())) ||
        NS_FAILED(originURI->SetPathQueryRef(EmptyCString()))) {
      return;
    }

    OriginAttributes attrs = aSubjectPrincipal.OriginAttributesRef();
    if (aSubjectPrincipal.GetIsSystemPrincipal()) {
      auto principal = BasePrincipal::Cast(GetPrincipal());

      if (attrs != principal->OriginAttributesRef()) {
        nsCOMPtr<nsIURI> targetURI;
        nsAutoCString targetURL;
        nsAutoCString sourceOrigin;
        nsAutoCString targetOrigin;

        if (NS_FAILED(principal->GetURI(getter_AddRefs(targetURI))) ||
            NS_FAILED(targetURI->GetAsciiSpec(targetURL)) ||
            NS_FAILED(principal->GetOrigin(targetOrigin)) ||
            NS_FAILED(aSubjectPrincipal.GetOrigin(sourceOrigin))) {
          NS_WARNING("Failed to get source and target origins");
          return;
        }

        nsContentUtils::LogSimpleConsoleError(
          NS_ConvertUTF8toUTF16(nsPrintfCString(
            R"(Attempting to post a message to window with url "%s" and )"
            R"(origin "%s" from a system principal scope with mismatched )"
            R"(origin "%s".)",
            targetURL.get(), targetOrigin.get(), sourceOrigin.get())),
          "DOM");

        attrs = principal->OriginAttributesRef();
      }
    }

    // Create a nsIPrincipal inheriting the app/browser attributes from the
    // caller.
    providedPrincipal = BasePrincipal::CreateCodebasePrincipal(originURI, attrs);
    if (NS_WARN_IF(!providedPrincipal)) {
      return;
    }
  }

  // Create and asynchronously dispatch a runnable which will handle actual DOM
  // event creation and dispatch.
  RefPtr<PostMessageEvent> event =
    new PostMessageEvent(nsContentUtils::IsCallerChrome() || !callerInnerWin
                         ? nullptr
                         : callerInnerWin->GetOuterWindowInternal(),
                         origin,
                         AssertOuter(),
                         providedPrincipal,
                         callerInnerWin
                         ? callerInnerWin->GetDoc()
                         : nullptr,
                         nsContentUtils::IsCallerChrome());

  JS::Rooted<JS::Value> message(aCx, aMessage);
  JS::Rooted<JS::Value> transfer(aCx, aTransfer);

  event->Write(aCx, message, transfer, JS::CloneDataPolicy(), aError);
  if (NS_WARN_IF(aError.Failed())) {
    return;
  }

  aError = Dispatch(TaskCategory::Other, event.forget());
}

void
nsGlobalWindowInner::PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
                               const nsAString& aTargetOrigin,
                               JS::Handle<JS::Value> aTransfer,
                               nsIPrincipal& aSubjectPrincipal,
                               ErrorResult& aError)
{
  FORWARD_TO_OUTER_OR_THROW(PostMessageMozOuter,
                            (aCx, aMessage, aTargetOrigin, aTransfer,
                             aSubjectPrincipal, aError),
                            aError, );
}

void
nsGlobalWindowInner::PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
                               const nsAString& aTargetOrigin,
                               const Sequence<JSObject*>& aTransfer,
                               nsIPrincipal& aSubjectPrincipal,
                               ErrorResult& aRv)
{
  JS::Rooted<JS::Value> transferArray(aCx, JS::UndefinedValue());

  aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransfer,
                                                          &transferArray);
  if (NS_WARN_IF(aRv.Failed())) {
    return;
  }

  PostMessageMoz(aCx, aMessage, aTargetOrigin, transferArray,
                 aSubjectPrincipal, aRv);
}

bool
nsGlobalWindowInner::CanClose()
{
  MOZ_ASSERT(IsOuterWindow());

  if (mIsChrome) {
    nsCOMPtr<nsIBrowserDOMWindow> bwin;
    GetBrowserDOMWindow(getter_AddRefs(bwin));

    bool canClose = true;
    if (bwin && NS_SUCCEEDED(bwin->CanClose(&canClose))) {
      return canClose;
    }
  }

  if (!mDocShell) {
    return true;
  }

  // Ask the content viewer whether the toplevel window can close.
  // If the content viewer returns false, it is responsible for calling
  // Close() as soon as it is possible for the window to close.
  // This allows us to not close the window while printing is happening.

  nsCOMPtr<nsIContentViewer> cv;
  mDocShell->GetContentViewer(getter_AddRefs(cv));
  if (cv) {
    bool canClose;
    nsresult rv = cv->PermitUnload(&canClose);
    if (NS_SUCCEEDED(rv) && !canClose)
      return false;

    rv = cv->RequestWindowClose(&canClose);
    if (NS_SUCCEEDED(rv) && !canClose)
      return false;
  }

  return true;
}

void
nsGlobalWindowInner::CloseOuter(bool aTrustedCaller)
{
  MOZ_RELEASE_ASSERT(IsOuterWindow());

  if (!mDocShell || IsInModalState() ||
      (IsFrame() && !mDocShell->GetIsMozBrowser())) {
    // window.close() is called on a frame in a frameset, on a window
    // that's already closed, or on a window for which there's
    // currently a modal dialog open. Ignore such calls.
    return;
  }

  if (mHavePendingClose) {
    // We're going to be closed anyway; do nothing since we don't want
    // to double-close
    return;
  }

  if (mBlockScriptedClosingFlag)
  {
    // A script's popup has been blocked and we don't want
    // the window to be closed directly after this event,
    // so the user can see that there was a blocked popup.
    return;
  }

  // Don't allow scripts from content to close non-neterror windows that
  // were not opened by script.
  nsAutoString url;
  nsresult rv = mDoc->GetURL(url);
  NS_ENSURE_SUCCESS_VOID(rv);

  if (!StringBeginsWith(url, NS_LITERAL_STRING("about:neterror")) &&
      !mHadOriginalOpener && !aTrustedCaller) {
    bool allowClose = mAllowScriptsToClose ||
      Preferences::GetBool("dom.allow_scripts_to_close_windows", true