tor-browser

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

nsGlobalWindowOuter.cpp (260625B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "nsGlobalWindowOuter.h"
      8 
      9 #include <algorithm>
     10 
     11 #include "mozilla/Assertions.h"
     12 #include "mozilla/ScopeExit.h"
     13 #include "nsDeviceContext.h"
     14 #include "nsGlobalWindowInner.h"
     15 
     16 // Local Includes
     17 #include "Navigator.h"
     18 #include "WindowDestroyedEvent.h"
     19 #include "WindowNamedPropertiesHandler.h"
     20 #include "mozilla/AntiTrackingUtils.h"
     21 #include "mozilla/IntegerPrintfMacros.h"
     22 #include "mozilla/Result.h"
     23 #include "mozilla/StorageAccessAPIHelper.h"
     24 #include "mozilla/dom/AutoPrintEventDispatcher.h"
     25 #include "mozilla/dom/BindingUtils.h"
     26 #include "mozilla/dom/BrowserChild.h"
     27 #include "mozilla/dom/BrowsingContextBinding.h"
     28 #include "mozilla/dom/CanonicalBrowsingContext.h"
     29 #include "mozilla/dom/ContentChild.h"
     30 #include "mozilla/dom/ContentFrameMessageManager.h"
     31 #include "mozilla/dom/DocumentInlines.h"
     32 #include "mozilla/dom/DocumentPictureInPicture.h"
     33 #include "mozilla/dom/EventTarget.h"
     34 #include "mozilla/dom/HTMLIFrameElement.h"
     35 #include "mozilla/dom/LSObject.h"
     36 #include "mozilla/dom/LocalStorage.h"
     37 #include "mozilla/dom/MaybeCrossOriginObject.h"
     38 #include "mozilla/dom/Performance.h"
     39 #include "mozilla/dom/ProxyHandlerUtils.h"
     40 #include "mozilla/dom/Storage.h"
     41 #include "mozilla/dom/StorageEvent.h"
     42 #include "mozilla/dom/StorageEventBinding.h"
     43 #include "mozilla/dom/StorageNotifierService.h"
     44 #include "mozilla/dom/StorageUtils.h"
     45 #include "mozilla/dom/Timeout.h"
     46 #include "mozilla/dom/TimeoutHandler.h"
     47 #include "mozilla/dom/TimeoutManager.h"
     48 #include "mozilla/dom/UserActivation.h"
     49 #include "mozilla/dom/WindowContext.h"
     50 #include "mozilla/dom/WindowFeatures.h"  // WindowFeatures
     51 #include "mozilla/dom/WindowGlobalChild.h"
     52 #include "mozilla/dom/WindowProxyHolder.h"
     53 #include "mozilla/intl/LocaleService.h"
     54 #include "nsArrayUtils.h"
     55 #include "nsBaseCommandController.h"
     56 #include "nsContentSecurityManager.h"
     57 #include "nsDOMJSUtils.h"
     58 #include "nsDOMNavigationTiming.h"
     59 #include "nsDocShellLoadState.h"
     60 #include "nsError.h"
     61 #include "nsFrameSelection.h"
     62 #include "nsGlobalWindowOuter.h"
     63 #include "nsHistory.h"
     64 #include "nsICookieService.h"
     65 #include "nsIDOMStorageManager.h"
     66 #include "nsIDocShellTreeOwner.h"
     67 #include "nsIInterfaceRequestorUtils.h"
     68 #include "nsILoadGroup.h"
     69 #include "nsIPermissionManager.h"
     70 #include "nsIScriptContext.h"
     71 #include "nsISecureBrowserUI.h"
     72 #include "nsISizeOfEventTarget.h"
     73 #include "nsIWebProgressListener.h"
     74 #include "nsNetUtil.h"
     75 #include "nsPrintfCString.h"
     76 #include "nsScreen.h"
     77 #include "nsVariant.h"
     78 #include "nsWindowMemoryReporter.h"
     79 #include "nsWindowSizes.h"
     80 #include "nsWindowWatcher.h"
     81 
     82 // Helper Classes
     83 #include "js/CallAndConstruct.h"    // JS::Call
     84 #include "js/PropertyAndElement.h"  // JS_DefineObject, JS_GetProperty
     85 #include "js/PropertySpec.h"
     86 #include "js/RealmIterators.h"
     87 #include "js/Wrapper.h"
     88 #include "js/friend/StackLimits.h"  // js::AutoCheckRecursionLimit
     89 #include "js/friend/WindowProxy.h"  // js::IsWindowProxy, js::SetWindowProxy
     90 #include "jsapi.h"
     91 #include "jsfriendapi.h"
     92 #include "mozilla/Likely.h"
     93 #include "mozilla/Preferences.h"
     94 #include "mozilla/SchedulerGroup.h"
     95 #include "mozilla/SpinEventLoopUntil.h"
     96 #include "mozilla/Sprintf.h"
     97 #include "mozilla/dom/ScriptSettings.h"
     98 #include "nsJSEnvironment.h"
     99 #include "nsJSUtils.h"
    100 #include "nsLayoutUtils.h"
    101 #include "nsReadableUtils.h"
    102 
    103 // Other Classes
    104 #include "AudioChannelService.h"
    105 #include "PostMessageEvent.h"
    106 #include "mozilla/Attributes.h"
    107 #include "mozilla/Components.h"
    108 #include "mozilla/Debug.h"
    109 #include "mozilla/EventListenerManager.h"
    110 #include "mozilla/MouseEvents.h"
    111 #include "mozilla/PresShell.h"
    112 #include "mozilla/ProcessHangMonitor.h"
    113 #include "mozilla/StaticPrefs_dom.h"
    114 #include "mozilla/StaticPrefs_fission.h"
    115 #include "mozilla/StaticPrefs_full_screen_api.h"
    116 #include "mozilla/StaticPrefs_print.h"
    117 #include "mozilla/ThrottledEventQueue.h"
    118 #include "mozilla/dom/BarProps.h"
    119 #include "mozilla/dom/DocGroup.h"
    120 #include "mozilla/dom/ToJSValue.h"
    121 #include "mozilla/dom/WorkerCommon.h"
    122 #include "mozilla/net/CookieJarSettings.h"
    123 #include "nsAboutProtocolUtils.h"
    124 #include "nsCCUncollectableMarker.h"
    125 #include "nsCharTraits.h"  // NS_IS_HIGH/LOW_SURROGATE
    126 #include "nsJSPrincipals.h"
    127 #include "nsLayoutStatics.h"
    128 
    129 // Interfaces Needed
    130 #include "Crypto.h"
    131 #include "mozilla/EventDispatcher.h"
    132 #include "mozilla/EventStateManager.h"
    133 #include "mozilla/ScrollContainerFrame.h"
    134 #include "mozilla/dom/CustomEvent.h"
    135 #include "mozilla/dom/Document.h"
    136 #include "nsCSSProps.h"
    137 #include "nsCanvasFrame.h"
    138 #include "nsComputedDOMStyle.h"
    139 #include "nsContentUtils.h"
    140 #include "nsDOMCID.h"
    141 #include "nsDOMString.h"
    142 #include "nsDOMWindowUtils.h"
    143 #include "nsFocusManager.h"
    144 #include "nsGlobalWindowCommands.h"
    145 #include "nsIAppWindow.h"
    146 #include "nsIBaseWindow.h"
    147 #include "nsIClassifiedChannel.h"
    148 #include "nsIContent.h"
    149 #include "nsIControllers.h"
    150 #include "nsIDeviceSensors.h"
    151 #include "nsIDocShell.h"
    152 #include "nsIDocumentViewer.h"
    153 #include "nsIFrame.h"
    154 #include "nsILoadContext.h"
    155 #include "nsIObserverService.h"
    156 #include "nsIPrompt.h"
    157 #include "nsIPromptFactory.h"
    158 #include "nsIPromptService.h"
    159 #include "nsISHistory.h"
    160 #include "nsIScreenManager.h"
    161 #include "nsIScriptError.h"
    162 #include "nsIURIFixup.h"
    163 #include "nsIURIMutator.h"
    164 #include "nsIWebBrowserChrome.h"
    165 #include "nsIWebBrowserFind.h"  // For window.find()
    166 #include "nsIWebNavigation.h"
    167 #include "nsIWidget.h"
    168 #include "nsIWidgetListener.h"
    169 #include "nsIWindowWatcher.h"
    170 #include "nsIWritablePropertyBag2.h"
    171 #include "nsIXULRuntime.h"
    172 #include "nsPIWindowWatcher.h"
    173 #include "nsQueryObject.h"
    174 #include "nsServiceManagerUtils.h"
    175 #include "nsThreadUtils.h"
    176 #include "xpcprivate.h"
    177 
    178 #ifdef NS_PRINTING
    179 #  include "nsIPrintSettings.h"
    180 #  include "nsIPrintSettingsService.h"
    181 #  include "nsIWebBrowserPrint.h"
    182 #endif
    183 
    184 #include "AccessCheck.h"
    185 #include "FxRWindowManager.h"
    186 #include "VRShMem.h"
    187 #include "gfxVR.h"
    188 #include "mozilla/BasePrincipal.h"
    189 #include "mozilla/DOMEventTargetHelper.h"
    190 #include "mozilla/GlobalKeyListener.h"
    191 #include "mozilla/Logging.h"
    192 #include "mozilla/ProfilerMarkers.h"
    193 #include "mozilla/Services.h"
    194 #include "mozilla/dom/AudioContext.h"
    195 #include "mozilla/dom/BrowsingContextGroup.h"
    196 #include "mozilla/dom/Console.h"
    197 #include "mozilla/dom/Element.h"
    198 #include "mozilla/dom/Fetch.h"
    199 #include "mozilla/dom/FunctionBinding.h"
    200 #include "mozilla/dom/Gamepad.h"
    201 #include "mozilla/dom/GamepadManager.h"
    202 #include "mozilla/dom/HashChangeEvent.h"
    203 #include "mozilla/dom/IDBFactory.h"
    204 #include "mozilla/dom/ImageBitmap.h"
    205 #include "mozilla/dom/ImageBitmapBinding.h"
    206 #include "mozilla/dom/IntlUtils.h"
    207 #include "mozilla/dom/Location.h"
    208 #include "mozilla/dom/MediaQueryList.h"
    209 #include "mozilla/dom/MessageChannel.h"
    210 #include "mozilla/dom/NavigatorBinding.h"
    211 #include "mozilla/dom/PopStateEvent.h"
    212 #include "mozilla/dom/PopupBlockedEvent.h"
    213 #include "mozilla/dom/PrimitiveConversions.h"
    214 #include "mozilla/dom/Promise.h"
    215 #include "mozilla/dom/RedirectBlockedEvent.h"
    216 #include "mozilla/dom/Selection.h"
    217 #include "mozilla/dom/ServiceWorkerRegistration.h"
    218 #include "mozilla/dom/VRDisplay.h"
    219 #include "mozilla/dom/VRDisplayEvent.h"
    220 #include "mozilla/dom/VRDisplayEventBinding.h"
    221 #include "mozilla/dom/VREventObserver.h"
    222 #include "mozilla/dom/WebIDLGlobalNameHash.h"
    223 #include "mozilla/dom/WindowBinding.h"
    224 #include "mozilla/dom/Worklet.h"
    225 #include "mozilla/dom/cache/CacheStorage.h"
    226 #include "mozilla/extensions/WebExtensionPolicy.h"
    227 #include "mozilla/glean/DomMetrics.h"
    228 #include "nsFrameLoader.h"
    229 #include "nsFrameLoaderOwner.h"
    230 #include "nsHTMLDocument.h"
    231 #include "nsIArray.h"
    232 #include "nsIBrowserChild.h"
    233 #include "nsIDOMXULCommandDispatcher.h"
    234 #include "nsIDragService.h"
    235 #include "nsNetCID.h"
    236 #include "nsRefreshDriver.h"
    237 #include "nsSandboxFlags.h"
    238 #include "nsWindowRoot.h"
    239 #include "nsWrapperCacheInlines.h"
    240 #include "nsXPCOMCID.h"
    241 #include "nsXULControllers.h"
    242 #include "prenv.h"
    243 #include "prrng.h"
    244 
    245 #ifdef MOZ_WEBSPEECH
    246 #  include "mozilla/dom/SpeechSynthesis.h"
    247 #endif
    248 
    249 #ifdef ANDROID
    250 #  include <android/log.h>
    251 #endif
    252 
    253 #ifdef XP_WIN
    254 #  include <process.h>
    255 #  define getpid _getpid
    256 #else
    257 #  include <unistd.h>  // for getpid()
    258 #endif
    259 
    260 using namespace mozilla;
    261 using namespace mozilla::dom;
    262 using namespace mozilla::dom::ipc;
    263 using mozilla::BasePrincipal;
    264 using mozilla::OriginAttributes;
    265 using mozilla::TimeStamp;
    266 using mozilla::layout::RemotePrintJobChild;
    267 
    268 static inline nsGlobalWindowInner* GetCurrentInnerWindowInternal(
    269    const nsGlobalWindowOuter* aOuter) {
    270  return nsGlobalWindowInner::Cast(aOuter->GetCurrentInnerWindow());
    271 }
    272 
    273 #define FORWARD_TO_INNER(method, args, err_rval)           \
    274  PR_BEGIN_MACRO                                           \
    275  if (!mInnerWindow) {                                     \
    276    NS_WARNING("No inner window available!");              \
    277    return err_rval;                                       \
    278  }                                                        \
    279  return GetCurrentInnerWindowInternal(this)->method args; \
    280  PR_END_MACRO
    281 
    282 #define FORWARD_TO_INNER_WITH_STRONG_REF(method, args, err_rval)           \
    283  PR_BEGIN_MACRO                                                           \
    284  if (!mInnerWindow) {                                                     \
    285    NS_WARNING("No inner window available!");                              \
    286    return err_rval;                                                       \
    287  }                                                                        \
    288  RefPtr<nsGlobalWindowInner> inner = GetCurrentInnerWindowInternal(this); \
    289  return inner->method args;                                               \
    290  PR_END_MACRO
    291 
    292 #define FORWARD_TO_INNER_VOID(method, args)         \
    293  PR_BEGIN_MACRO                                    \
    294  if (!mInnerWindow) {                              \
    295    NS_WARNING("No inner window available!");       \
    296    return;                                         \
    297  }                                                 \
    298  GetCurrentInnerWindowInternal(this)->method args; \
    299  return;                                           \
    300  PR_END_MACRO
    301 
    302 // Same as FORWARD_TO_INNER, but this will create a fresh inner if an
    303 // inner doesn't already exists.
    304 #define FORWARD_TO_INNER_CREATE(method, args, err_rval)    \
    305  PR_BEGIN_MACRO                                           \
    306  if (!mInnerWindow) {                                     \
    307    if (mIsClosed) {                                       \
    308      return err_rval;                                     \
    309    }                                                      \
    310    nsCOMPtr<Document> kungFuDeathGrip = GetDoc();         \
    311    (void)kungFuDeathGrip;                                 \
    312    if (!mInnerWindow) {                                   \
    313      return err_rval;                                     \
    314    }                                                      \
    315  }                                                        \
    316  return GetCurrentInnerWindowInternal(this)->method args; \
    317  PR_END_MACRO
    318 
    319 static LazyLogModule gDOMLeakPRLogOuter("DOMLeakOuter");
    320 extern LazyLogModule gPageCacheLog;
    321 
    322 #ifdef DEBUG
    323 static LazyLogModule gDocShellAndDOMWindowLeakLogging(
    324    "DocShellAndDOMWindowLeak");
    325 #endif
    326 
    327 nsGlobalWindowOuter::OuterWindowByIdTable*
    328    nsGlobalWindowOuter::sOuterWindowsById = nullptr;
    329 
    330 /* static */
    331 nsPIDOMWindowOuter* nsPIDOMWindowOuter::GetFromCurrentInner(
    332    nsPIDOMWindowInner* aInner) {
    333  if (!aInner) {
    334    return nullptr;
    335  }
    336 
    337  nsPIDOMWindowOuter* outer = aInner->GetOuterWindow();
    338  if (!outer || outer->GetCurrentInnerWindow() != aInner) {
    339    return nullptr;
    340  }
    341 
    342  return outer;
    343 }
    344 
    345 //*****************************************************************************
    346 // nsOuterWindowProxy: Outer Window Proxy
    347 //*****************************************************************************
    348 
    349 // Give OuterWindowProxyClass 2 reserved slots, like the other wrappers, so
    350 // JSObject::swap can swap it with CrossCompartmentWrappers without requiring
    351 // malloc.
    352 //
    353 // We store the nsGlobalWindowOuter* in our first slot.
    354 //
    355 // We store our holder weakmap in the second slot.
    356 const JSClass OuterWindowProxyClass = PROXY_CLASS_DEF(
    357    "Proxy", JSCLASS_HAS_RESERVED_SLOTS(2)); /* additional class flags */
    358 
    359 static const size_t OUTER_WINDOW_SLOT = 0;
    360 static const size_t HOLDER_WEAKMAP_SLOT = 1;
    361 
    362 class nsOuterWindowProxy : public MaybeCrossOriginObject<js::Wrapper> {
    363  using Base = MaybeCrossOriginObject<js::Wrapper>;
    364 
    365 public:
    366  constexpr nsOuterWindowProxy() : Base(0) {}
    367 
    368  bool finalizeInBackground(const JS::Value& priv) const override {
    369    return false;
    370  }
    371 
    372  // Standard internal methods
    373  /**
    374   * Implementation of [[GetOwnProperty]] as defined at
    375   * https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-getownproperty
    376   *
    377   * "proxy" is the WindowProxy object involved.  It may not be same-compartment
    378   * with cx.
    379   */
    380  bool getOwnPropertyDescriptor(
    381      JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
    382      JS::MutableHandle<Maybe<JS::PropertyDescriptor>> desc) const override;
    383 
    384  /*
    385   * Implementation of the same-origin case of
    386   * <https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-getownproperty>.
    387   */
    388  bool definePropertySameOrigin(JSContext* cx, JS::Handle<JSObject*> proxy,
    389                                JS::Handle<jsid> id,
    390                                JS::Handle<JS::PropertyDescriptor> desc,
    391                                JS::ObjectOpResult& result) const override;
    392 
    393  /**
    394   * Implementation of [[OwnPropertyKeys]] as defined at
    395   *
    396   * https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-ownpropertykeys
    397   *
    398   * "proxy" is the WindowProxy object involved.  It may not be same-compartment
    399   * with cx.
    400   */
    401  bool ownPropertyKeys(JSContext* cx, JS::Handle<JSObject*> proxy,
    402                       JS::MutableHandleVector<jsid> props) const override;
    403  /**
    404   * Implementation of [[Delete]] as defined at
    405   * https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-delete
    406   *
    407   * "proxy" is the WindowProxy object involved.  It may not be same-compartment
    408   * with cx.
    409   */
    410  bool delete_(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
    411               JS::ObjectOpResult& result) const override;
    412 
    413  /**
    414   * Implementaton of hook for superclass getPrototype() method.
    415   */
    416  JSObject* getSameOriginPrototype(JSContext* cx) const override;
    417 
    418  /**
    419   * Implementation of [[HasProperty]] internal method as defined at
    420   * https://tc39.github.io/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-hasproperty-p
    421   *
    422   * "proxy" is the WindowProxy object involved.  It may not be same-compartment
    423   * with cx.
    424   *
    425   * Note that the HTML spec does not define an override for this internal
    426   * method, so we just want the "normal object" behavior.  We have to override
    427   * it, because js::Wrapper also overrides, with "not normal" behavior.
    428   */
    429  bool has(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
    430           bool* bp) const override;
    431 
    432  /**
    433   * Implementation of [[Get]] internal method as defined at
    434   * <https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-get>.
    435   *
    436   * "proxy" is the WindowProxy object involved.  It may or may not be
    437   * same-compartment with "cx".
    438   *
    439   * "receiver" is the receiver ("this") for the get.  It will be
    440   * same-compartment with "cx".
    441   *
    442   * "vp" is the return value.  It will be same-compartment with "cx".
    443   */
    444  bool get(JSContext* cx, JS::Handle<JSObject*> proxy,
    445           JS::Handle<JS::Value> receiver, JS::Handle<jsid> id,
    446           JS::MutableHandle<JS::Value> vp) const override;
    447 
    448  /**
    449   * Implementation of [[Set]] internal method as defined at
    450   * <https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-set>.
    451   *
    452   * "proxy" is the WindowProxy object involved.  It may or may not be
    453   * same-compartment with "cx".
    454   *
    455   * "v" is the value being set.  It will be same-compartment with "cx".
    456   *
    457   * "receiver" is the receiver ("this") for the set.  It will be
    458   * same-compartment with "cx".
    459   */
    460  bool set(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
    461           JS::Handle<JS::Value> v, JS::Handle<JS::Value> receiver,
    462           JS::ObjectOpResult& result) const override;
    463 
    464  // SpiderMonkey extensions
    465  /**
    466   * Implementation of SpiderMonkey extension which just checks whether this
    467   * object has the property.  Basically Object.getOwnPropertyDescriptor(obj,
    468   * prop) !== undefined. but does not require reifying the descriptor.
    469   *
    470   * We have to override this because js::Wrapper overrides it, but we want
    471   * different behavior from js::Wrapper.
    472   *
    473   * "proxy" is the WindowProxy object involved.  It may not be same-compartment
    474   * with cx.
    475   */
    476  bool hasOwn(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
    477              bool* bp) const override;
    478 
    479  /**
    480   * Implementation of SpiderMonkey extension which is used as a fast path for
    481   * enumerating.
    482   *
    483   * We have to override this because js::Wrapper overrides it, but we want
    484   * different behavior from js::Wrapper.
    485   *
    486   * "proxy" is the WindowProxy object involved.  It may not be same-compartment
    487   * with cx.
    488   */
    489  bool getOwnEnumerablePropertyKeys(
    490      JSContext* cx, JS::Handle<JSObject*> proxy,
    491      JS::MutableHandleVector<jsid> props) const override;
    492 
    493  /**
    494   * Hook used by SpiderMonkey to implement Object.prototype.toString.
    495   */
    496  const char* className(JSContext* cx,
    497                        JS::Handle<JSObject*> wrapper) const override;
    498 
    499  void finalize(JS::GCContext* gcx, JSObject* proxy) const override;
    500  size_t objectMoved(JSObject* proxy, JSObject* old) const override;
    501 
    502  bool isCallable(JSObject* obj) const override { return false; }
    503  bool isConstructor(JSObject* obj) const override { return false; }
    504 
    505  static const nsOuterWindowProxy singleton;
    506 
    507  static nsGlobalWindowOuter* GetOuterWindow(JSObject* proxy) {
    508    nsGlobalWindowOuter* outerWindow =
    509        nsGlobalWindowOuter::FromSupports(static_cast<nsISupports*>(
    510            js::GetProxyReservedSlot(proxy, OUTER_WINDOW_SLOT).toPrivate()));
    511    return outerWindow;
    512  }
    513 
    514 protected:
    515  // False return value means we threw an exception.  True return value
    516  // but false "found" means we didn't have a subframe at that index.
    517  bool GetSubframeWindow(JSContext* cx, JS::Handle<JSObject*> proxy,
    518                         JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp,
    519                         bool& found) const;
    520 
    521  // Returns a non-null window only if id is an index and we have a
    522  // window at that index.
    523  Nullable<WindowProxyHolder> GetSubframeWindow(JSContext* cx,
    524                                                JS::Handle<JSObject*> proxy,
    525                                                JS::Handle<jsid> id) const;
    526 
    527  bool AppendIndexedPropertyNames(JSObject* proxy,
    528                                  JS::MutableHandleVector<jsid> props) const;
    529 
    530  using MaybeCrossOriginObjectMixins::EnsureHolder;
    531  bool EnsureHolder(JSContext* cx, JS::Handle<JSObject*> proxy,
    532                    JS::MutableHandle<JSObject*> holder) const override;
    533 
    534  // Helper method for creating a special "print" method that allows printing
    535  // our PDF-viewer documents even if you're not same-origin with them.
    536  //
    537  // aProxy must be our nsOuterWindowProxy.  It will not be same-compartment
    538  // with aCx, since we only use this on the different-origin codepath!
    539  //
    540  // Can return true without filling in aDesc, which corresponds to not exposing
    541  // a "print" method.
    542  static bool MaybeGetPDFJSPrintMethod(
    543      JSContext* cx, JS::Handle<JSObject*> proxy,
    544      JS::MutableHandle<Maybe<JS::PropertyDescriptor>> desc);
    545 
    546  // The actual "print" method we use for the PDFJS case.
    547  static bool PDFJSPrintMethod(JSContext* cx, unsigned argc, JS::Value* vp);
    548 
    549  // Helper method to get the pre-PDF-viewer-messing-with-it principal from an
    550  // inner window.  Will return null if this is not a PDF-viewer inner or if the
    551  // principal could not be found for some reason.
    552  static already_AddRefed<nsIPrincipal> GetNoPDFJSPrincipal(
    553      nsGlobalWindowInner* inner);
    554 };
    555 
    556 const char* nsOuterWindowProxy::className(JSContext* cx,
    557                                          JS::Handle<JSObject*> proxy) const {
    558  MOZ_ASSERT(js::IsProxy(proxy));
    559 
    560  if (!IsPlatformObjectSameOrigin(cx, proxy)) {
    561    return "Object";
    562  }
    563 
    564  return "Window";
    565 }
    566 
    567 void nsOuterWindowProxy::finalize(JS::GCContext* gcx, JSObject* proxy) const {
    568  nsGlobalWindowOuter* outerWindow = GetOuterWindow(proxy);
    569  if (outerWindow) {
    570    outerWindow->ClearWrapper(proxy);
    571    BrowsingContext* bc = outerWindow->GetBrowsingContext();
    572    if (bc) {
    573      bc->ClearWindowProxy();
    574    }
    575 
    576    // Ideally we would use OnFinalize here, but it's possible that
    577    // EnsureScriptEnvironment will later be called on the window, and we don't
    578    // want to create a new script object in that case. Therefore, we need to
    579    // write a non-null value that will reliably crash when dereferenced.
    580    outerWindow->PoisonOuterWindowProxy(proxy);
    581  }
    582 }
    583 
    584 bool nsOuterWindowProxy::getOwnPropertyDescriptor(
    585    JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
    586    JS::MutableHandle<Maybe<JS::PropertyDescriptor>> desc) const {
    587  // First check for indexed access.  This is
    588  // https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-getownproperty
    589  // step 2, mostly.
    590  JS::Rooted<JS::Value> subframe(cx);
    591  bool found;
    592  if (!GetSubframeWindow(cx, proxy, id, &subframe, found)) {
    593    return false;
    594  }
    595  if (found) {
    596    // Step 2.4.
    597 
    598    desc.set(Some(JS::PropertyDescriptor::Data(
    599        subframe, {
    600                      JS::PropertyAttribute::Configurable,
    601                      JS::PropertyAttribute::Enumerable,
    602                  })));
    603    return true;
    604  }
    605 
    606  bool isSameOrigin = IsPlatformObjectSameOrigin(cx, proxy);
    607 
    608  // If we did not find a subframe, we could still have an indexed property
    609  // access.  In that case we should throw a SecurityError in the cross-origin
    610  // case.
    611  if (!isSameOrigin && IsArrayIndex(GetArrayIndexFromId(id))) {
    612    // Step 2.5.2.
    613    return ReportCrossOriginDenial(cx, id, "access"_ns);
    614  }
    615 
    616  // Step 2.5.1 is handled via the forwarding to js::Wrapper; it saves us an
    617  // IsArrayIndex(GetArrayIndexFromId(id)) here.  We'll never have a property on
    618  // the Window whose name is an index, because our defineProperty doesn't pass
    619  // those on to the Window.
    620 
    621  // Step 3.
    622  if (isSameOrigin) {
    623    if (StaticPrefs::dom_missing_prop_counters_enabled() && id.isAtom()) {
    624      Window_Binding::CountMaybeMissingProperty(proxy, id);
    625    }
    626 
    627    // Fall through to js::Wrapper.
    628    {  // Scope for JSAutoRealm while we are dealing with js::Wrapper.
    629      // When forwarding to js::Wrapper, we should just enter the Realm of proxy
    630      // for now.  That's what js::Wrapper expects, and since we're same-origin
    631      // anyway this is not changing any security behavior.
    632      JSAutoRealm ar(cx, proxy);
    633      JS_MarkCrossZoneId(cx, id);
    634      bool ok = js::Wrapper::getOwnPropertyDescriptor(cx, proxy, id, desc);
    635      if (!ok) {
    636        return false;
    637      }
    638 
    639 #if 0
    640      // See https://github.com/tc39/ecma262/issues/672 for more information.
    641      if (desc.isSome() &&
    642          !IsNonConfigurableReadonlyPrimitiveGlobalProp(cx, id)) {
    643        (*desc).setConfigurable(true);
    644      }
    645 #endif
    646    }
    647 
    648    // Now wrap our descriptor back into the Realm that asked for it.
    649    return JS_WrapPropertyDescriptor(cx, desc);
    650  }
    651 
    652  // Step 4.
    653  if (!CrossOriginGetOwnPropertyHelper(cx, proxy, id, desc)) {
    654    return false;
    655  }
    656 
    657  // Step 5
    658  if (desc.isSome()) {
    659    return true;
    660  }
    661 
    662  // Non-spec step for the PDF viewer's window.print().  This comes before we
    663  // check for named subframes, because in the same-origin case print() would
    664  // shadow those.
    665  if (id == GetJSIDByIndex(cx, XPCJSContext::IDX_PRINT)) {
    666    if (!MaybeGetPDFJSPrintMethod(cx, proxy, desc)) {
    667      return false;
    668    }
    669 
    670    if (desc.isSome()) {
    671      return true;
    672    }
    673  }
    674 
    675  // Step 6 -- check for named subframes.
    676  if (id.isString()) {
    677    nsAutoJSString name;
    678    if (!name.init(cx, id.toString())) {
    679      return false;
    680    }
    681    nsGlobalWindowOuter* win = GetOuterWindow(proxy);
    682    if (RefPtr<BrowsingContext> childDOMWin = win->GetChildWindow(name)) {
    683      JS::Rooted<JS::Value> childValue(cx);
    684      if (!ToJSValue(cx, WindowProxyHolder(childDOMWin), &childValue)) {
    685        return false;
    686      }
    687      desc.set(Some(JS::PropertyDescriptor::Data(
    688          childValue, {JS::PropertyAttribute::Configurable})));
    689      return true;
    690    }
    691  }
    692 
    693  // And step 7.
    694  return CrossOriginPropertyFallback(cx, proxy, id, desc);
    695 }
    696 
    697 bool nsOuterWindowProxy::definePropertySameOrigin(
    698    JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
    699    JS::Handle<JS::PropertyDescriptor> desc, JS::ObjectOpResult& result) const {
    700  if (IsArrayIndex(GetArrayIndexFromId(id))) {
    701    // Spec says to Reject whether this is a supported index or not,
    702    // since we have no indexed setter or indexed creator.  It is up
    703    // to the caller to decide whether to throw a TypeError.
    704    return result.failCantDefineWindowElement();
    705  }
    706 
    707  JS::ObjectOpResult ourResult;
    708  bool ok = js::Wrapper::defineProperty(cx, proxy, id, desc, ourResult);
    709  if (!ok) {
    710    return false;
    711  }
    712 
    713  if (!ourResult.ok()) {
    714    // It's possible that this failed because the page got the existing
    715    // descriptor (which we force to claim to be configurable) and then tried to
    716    // redefine the property with the descriptor it got but a different value.
    717    // We want to allow this case to succeed, so check for it and if we're in
    718    // that case try again but now with an attempt to define a non-configurable
    719    // property.
    720    if (!desc.hasConfigurable() || !desc.configurable()) {
    721      // The incoming descriptor was not explicitly marked "configurable: true",
    722      // so it failed for some other reason.  Just propagate that reason out.
    723      result = ourResult;
    724      return true;
    725    }
    726 
    727    JS::Rooted<Maybe<JS::PropertyDescriptor>> existingDesc(cx);
    728    ok = js::Wrapper::getOwnPropertyDescriptor(cx, proxy, id, &existingDesc);
    729    if (!ok) {
    730      return false;
    731    }
    732    if (existingDesc.isNothing() || existingDesc->configurable()) {
    733      // We have no existing property, or its descriptor is already configurable
    734      // (on the Window itself, where things really can be non-configurable).
    735      // So we failed for some other reason, which we should propagate out.
    736      result = ourResult;
    737      return true;
    738    }
    739 
    740    JS::Rooted<JS::PropertyDescriptor> updatedDesc(cx, desc);
    741    updatedDesc.setConfigurable(false);
    742 
    743    JS::ObjectOpResult ourNewResult;
    744    ok = js::Wrapper::defineProperty(cx, proxy, id, updatedDesc, ourNewResult);
    745    if (!ok) {
    746      return false;
    747    }
    748 
    749    if (!ourNewResult.ok()) {
    750      // Twiddling the configurable flag didn't help.  Just return this failure
    751      // out to the caller.
    752      result = ourNewResult;
    753      return true;
    754    }
    755  }
    756 
    757 #if 0
    758  // See https://github.com/tc39/ecma262/issues/672 for more information.
    759  if (desc.hasConfigurable() && !desc.configurable() &&
    760      !IsNonConfigurableReadonlyPrimitiveGlobalProp(cx, id)) {
    761    // Give callers a way to detect that they failed to "really" define a
    762    // non-configurable property.
    763    result.failCantDefineWindowNonConfigurable();
    764    return true;
    765  }
    766 #endif
    767 
    768  result.succeed();
    769  return true;
    770 }
    771 
    772 bool nsOuterWindowProxy::ownPropertyKeys(
    773    JSContext* cx, JS::Handle<JSObject*> proxy,
    774    JS::MutableHandleVector<jsid> props) const {
    775  // Just our indexed stuff followed by our "normal" own property names.
    776  if (!AppendIndexedPropertyNames(proxy, props)) {
    777    return false;
    778  }
    779 
    780  if (IsPlatformObjectSameOrigin(cx, proxy)) {
    781    // When forwarding to js::Wrapper, we should just enter the Realm of proxy
    782    // for now.  That's what js::Wrapper expects, and since we're same-origin
    783    // anyway this is not changing any security behavior.
    784    JS::RootedVector<jsid> innerProps(cx);
    785    {  // Scope for JSAutoRealm so we can mark the ids once we exit it
    786      JSAutoRealm ar(cx, proxy);
    787      if (!js::Wrapper::ownPropertyKeys(cx, proxy, &innerProps)) {
    788        return false;
    789      }
    790    }
    791    for (auto& id : innerProps) {
    792      JS_MarkCrossZoneId(cx, id);
    793    }
    794    return js::AppendUnique(cx, props, innerProps);
    795  }
    796 
    797  // In the cross-origin case we purposefully exclude subframe names from the
    798  // list of property names we report here.
    799  JS::Rooted<JSObject*> holder(cx);
    800  if (!EnsureHolder(cx, proxy, &holder)) {
    801    return false;
    802  }
    803 
    804  JS::RootedVector<jsid> crossOriginProps(cx);
    805  if (!js::GetPropertyKeys(cx, holder,
    806                           JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS,
    807                           &crossOriginProps) ||
    808      !js::AppendUnique(cx, props, crossOriginProps)) {
    809    return false;
    810  }
    811 
    812  // Add the "print" property if needed.
    813  nsGlobalWindowOuter* outer = GetOuterWindow(proxy);
    814  nsGlobalWindowInner* inner =
    815      nsGlobalWindowInner::Cast(outer->GetCurrentInnerWindow());
    816  if (inner) {
    817    nsCOMPtr<nsIPrincipal> targetPrincipal = GetNoPDFJSPrincipal(inner);
    818    if (targetPrincipal &&
    819        nsContentUtils::SubjectPrincipal(cx)->Equals(targetPrincipal)) {
    820      JS::RootedVector<jsid> printProp(cx);
    821      if (!printProp.append(GetJSIDByIndex(cx, XPCJSContext::IDX_PRINT)) ||
    822          !js::AppendUnique(cx, props, printProp)) {
    823        return false;
    824      }
    825    }
    826  }
    827 
    828  return xpc::AppendCrossOriginWhitelistedPropNames(cx, props);
    829 }
    830 
    831 bool nsOuterWindowProxy::delete_(JSContext* cx, JS::Handle<JSObject*> proxy,
    832                                 JS::Handle<jsid> id,
    833                                 JS::ObjectOpResult& result) const {
    834  if (!IsPlatformObjectSameOrigin(cx, proxy)) {
    835    return ReportCrossOriginDenial(cx, id, "delete"_ns);
    836  }
    837 
    838  if (!GetSubframeWindow(cx, proxy, id).IsNull()) {
    839    // Fail (which means throw if strict, else return false).
    840    return result.failCantDeleteWindowElement();
    841  }
    842 
    843  if (IsArrayIndex(GetArrayIndexFromId(id))) {
    844    // Indexed, but not supported.  Spec says return true.
    845    return result.succeed();
    846  }
    847 
    848  // We're same-origin, so it should be safe to enter the Realm of "proxy".
    849  // Let's do that, just in case, to avoid cross-compartment issues in our
    850  // js::Wrapper caller..
    851  JSAutoRealm ar(cx, proxy);
    852  JS_MarkCrossZoneId(cx, id);
    853  return js::Wrapper::delete_(cx, proxy, id, result);
    854 }
    855 
    856 JSObject* nsOuterWindowProxy::getSameOriginPrototype(JSContext* cx) const {
    857  return Window_Binding::GetProtoObjectHandle(cx);
    858 }
    859 
    860 bool nsOuterWindowProxy::has(JSContext* cx, JS::Handle<JSObject*> proxy,
    861                             JS::Handle<jsid> id, bool* bp) const {
    862  // We could just directly forward this method to js::BaseProxyHandler, but
    863  // that involves reifying the actual property descriptor, which might be more
    864  // work than we have to do for has() on the Window.
    865 
    866  if (!IsPlatformObjectSameOrigin(cx, proxy)) {
    867    // In the cross-origin case we only have own properties.  Just call hasOwn
    868    // directly.
    869    return hasOwn(cx, proxy, id, bp);
    870  }
    871 
    872  if (!GetSubframeWindow(cx, proxy, id).IsNull()) {
    873    *bp = true;
    874    return true;
    875  }
    876 
    877  // Just to be safe in terms of compartment asserts, enter the Realm of
    878  // "proxy".  We're same-origin with it, so this should be safe.
    879  JSAutoRealm ar(cx, proxy);
    880  JS_MarkCrossZoneId(cx, id);
    881  return js::Wrapper::has(cx, proxy, id, bp);
    882 }
    883 
    884 bool nsOuterWindowProxy::hasOwn(JSContext* cx, JS::Handle<JSObject*> proxy,
    885                                JS::Handle<jsid> id, bool* bp) const {
    886  // We could just directly forward this method to js::BaseProxyHandler, but
    887  // that involves reifying the actual property descriptor, which might be more
    888  // work than we have to do for hasOwn() on the Window.
    889 
    890  if (!IsPlatformObjectSameOrigin(cx, proxy)) {
    891    // Avoiding reifying the property descriptor here would require duplicating
    892    // a bunch of "is this property exposed cross-origin" logic, which is
    893    // probably not worth it.  Just forward this along to the base
    894    // implementation.
    895    //
    896    // It's very important to not forward this to js::Wrapper, because that will
    897    // not do the right security and cross-origin checks and will pass through
    898    // the call to the Window.
    899    //
    900    // The BaseProxyHandler code is OK with this happening without entering the
    901    // compartment of "proxy".
    902    return js::BaseProxyHandler::hasOwn(cx, proxy, id, bp);
    903  }
    904 
    905  if (!GetSubframeWindow(cx, proxy, id).IsNull()) {
    906    *bp = true;
    907    return true;
    908  }
    909 
    910  // Just to be safe in terms of compartment asserts, enter the Realm of
    911  // "proxy".  We're same-origin with it, so this should be safe.
    912  JSAutoRealm ar(cx, proxy);
    913  JS_MarkCrossZoneId(cx, id);
    914  return js::Wrapper::hasOwn(cx, proxy, id, bp);
    915 }
    916 
    917 bool nsOuterWindowProxy::get(JSContext* cx, JS::Handle<JSObject*> proxy,
    918                             JS::Handle<JS::Value> receiver,
    919                             JS::Handle<jsid> id,
    920                             JS::MutableHandle<JS::Value> vp) const {
    921  if (id == GetJSIDByIndex(cx, XPCJSContext::IDX_WRAPPED_JSOBJECT) &&
    922      xpc::AccessCheck::isChrome(js::GetContextCompartment(cx))) {
    923    vp.set(JS::ObjectValue(*proxy));
    924    return MaybeWrapValue(cx, vp);
    925  }
    926 
    927  if (!IsPlatformObjectSameOrigin(cx, proxy)) {
    928    return CrossOriginGet(cx, proxy, receiver, id, vp);
    929  }
    930 
    931  bool found;
    932  if (!GetSubframeWindow(cx, proxy, id, vp, found)) {
    933    return false;
    934  }
    935 
    936  if (found) {
    937    return true;
    938  }
    939 
    940  if (StaticPrefs::dom_missing_prop_counters_enabled() && id.isAtom()) {
    941    Window_Binding::CountMaybeMissingProperty(proxy, id);
    942  }
    943 
    944  {  // Scope for JSAutoRealm
    945    // Enter "proxy"'s Realm.  We're in the same-origin case, so this should be
    946    // safe.
    947    JSAutoRealm ar(cx, proxy);
    948 
    949    JS_MarkCrossZoneId(cx, id);
    950 
    951    JS::Rooted<JS::Value> wrappedReceiver(cx, receiver);
    952    if (!MaybeWrapValue(cx, &wrappedReceiver)) {
    953      return false;
    954    }
    955 
    956    // Fall through to js::Wrapper.
    957    if (!js::Wrapper::get(cx, proxy, wrappedReceiver, id, vp)) {
    958      return false;
    959    }
    960  }
    961 
    962  // Make sure our return value is in the caller compartment.
    963  return MaybeWrapValue(cx, vp);
    964 }
    965 
    966 bool nsOuterWindowProxy::set(JSContext* cx, JS::Handle<JSObject*> proxy,
    967                             JS::Handle<jsid> id, JS::Handle<JS::Value> v,
    968                             JS::Handle<JS::Value> receiver,
    969                             JS::ObjectOpResult& result) const {
    970  if (!IsPlatformObjectSameOrigin(cx, proxy)) {
    971    return CrossOriginSet(cx, proxy, id, v, receiver, result);
    972  }
    973 
    974  if (IsArrayIndex(GetArrayIndexFromId(id))) {
    975    // Reject the set.  It's up to the caller to decide whether to throw a
    976    // TypeError.  If the caller is strict mode JS code, it'll throw.
    977    return result.failReadOnly();
    978  }
    979 
    980  // Do the rest in the Realm of "proxy", since we're in the same-origin case.
    981  JSAutoRealm ar(cx, proxy);
    982  JS::Rooted<JS::Value> wrappedArg(cx, v);
    983  if (!MaybeWrapValue(cx, &wrappedArg)) {
    984    return false;
    985  }
    986  JS::Rooted<JS::Value> wrappedReceiver(cx, receiver);
    987  if (!MaybeWrapValue(cx, &wrappedReceiver)) {
    988    return false;
    989  }
    990 
    991  JS_MarkCrossZoneId(cx, id);
    992 
    993  return js::Wrapper::set(cx, proxy, id, wrappedArg, wrappedReceiver, result);
    994 }
    995 
    996 bool nsOuterWindowProxy::getOwnEnumerablePropertyKeys(
    997    JSContext* cx, JS::Handle<JSObject*> proxy,
    998    JS::MutableHandleVector<jsid> props) const {
    999  // We could just stop overring getOwnEnumerablePropertyKeys and let our
   1000  // superclasses deal (by falling back on the BaseProxyHandler implementation
   1001  // that uses a combination of ownPropertyKeys and getOwnPropertyDescriptor to
   1002  // only return the enumerable ones.  But maybe there's value in having
   1003  // somewhat faster for-in iteration on Window objects...
   1004 
   1005  // Like ownPropertyKeys, our indexed stuff followed by our "normal" enumerable
   1006  // own property names.
   1007  if (!AppendIndexedPropertyNames(proxy, props)) {
   1008    return false;
   1009  }
   1010 
   1011  if (!IsPlatformObjectSameOrigin(cx, proxy)) {
   1012    // All the cross-origin properties other than the indexed props are
   1013    // non-enumerable, so we're done here.
   1014    return true;
   1015  }
   1016 
   1017  // When forwarding to js::Wrapper, we should just enter the Realm of proxy
   1018  // for now.  That's what js::Wrapper expects, and since we're same-origin
   1019  // anyway this is not changing any security behavior.
   1020  JS::RootedVector<jsid> innerProps(cx);
   1021  {  // Scope for JSAutoRealm so we can mark the ids once we exit it.
   1022    JSAutoRealm ar(cx, proxy);
   1023    if (!js::Wrapper::getOwnEnumerablePropertyKeys(cx, proxy, &innerProps)) {
   1024      return false;
   1025    }
   1026  }
   1027 
   1028  for (auto& id : innerProps) {
   1029    JS_MarkCrossZoneId(cx, id);
   1030  }
   1031 
   1032  return js::AppendUnique(cx, props, innerProps);
   1033 }
   1034 
   1035 bool nsOuterWindowProxy::GetSubframeWindow(JSContext* cx,
   1036                                           JS::Handle<JSObject*> proxy,
   1037                                           JS::Handle<jsid> id,
   1038                                           JS::MutableHandle<JS::Value> vp,
   1039                                           bool& found) const {
   1040  Nullable<WindowProxyHolder> frame = GetSubframeWindow(cx, proxy, id);
   1041  if (frame.IsNull()) {
   1042    found = false;
   1043    return true;
   1044  }
   1045 
   1046  found = true;
   1047  return WrapObject(cx, frame.Value(), vp);
   1048 }
   1049 
   1050 Nullable<WindowProxyHolder> nsOuterWindowProxy::GetSubframeWindow(
   1051    JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id) const {
   1052  uint32_t index = GetArrayIndexFromId(id);
   1053  if (!IsArrayIndex(index)) {
   1054    return nullptr;
   1055  }
   1056 
   1057  nsGlobalWindowOuter* win = GetOuterWindow(proxy);
   1058  return win->IndexedGetterOuter(index);
   1059 }
   1060 
   1061 bool nsOuterWindowProxy::AppendIndexedPropertyNames(
   1062    JSObject* proxy, JS::MutableHandleVector<jsid> props) const {
   1063  uint32_t length = GetOuterWindow(proxy)->Length();
   1064  MOZ_ASSERT(int32_t(length) >= 0);
   1065  if (!props.reserve(props.length() + length)) {
   1066    return false;
   1067  }
   1068  for (int32_t i = 0; i < int32_t(length); ++i) {
   1069    if (!props.append(JS::PropertyKey::Int(i))) {
   1070      return false;
   1071    }
   1072  }
   1073 
   1074  return true;
   1075 }
   1076 
   1077 bool nsOuterWindowProxy::EnsureHolder(
   1078    JSContext* cx, JS::Handle<JSObject*> proxy,
   1079    JS::MutableHandle<JSObject*> holder) const {
   1080  return EnsureHolder(cx, proxy, HOLDER_WEAKMAP_SLOT,
   1081                      Window_Binding::sCrossOriginProperties, holder);
   1082 }
   1083 
   1084 size_t nsOuterWindowProxy::objectMoved(JSObject* obj, JSObject* old) const {
   1085  nsGlobalWindowOuter* outerWindow = GetOuterWindow(obj);
   1086  if (outerWindow) {
   1087    outerWindow->UpdateWrapper(obj, old);
   1088    BrowsingContext* bc = outerWindow->GetBrowsingContext();
   1089    if (bc) {
   1090      bc->UpdateWindowProxy(obj, old);
   1091    }
   1092  }
   1093  return 0;
   1094 }
   1095 
   1096 enum { PDFJS_SLOT_CALLEE = 0 };
   1097 
   1098 // static
   1099 bool nsOuterWindowProxy::MaybeGetPDFJSPrintMethod(
   1100    JSContext* cx, JS::Handle<JSObject*> proxy,
   1101    JS::MutableHandle<Maybe<JS::PropertyDescriptor>> desc) {
   1102  MOZ_ASSERT(proxy);
   1103  MOZ_ASSERT(!desc.isSome());
   1104 
   1105  nsGlobalWindowOuter* outer = GetOuterWindow(proxy);
   1106  nsGlobalWindowInner* inner =
   1107      nsGlobalWindowInner::Cast(outer->GetCurrentInnerWindow());
   1108  if (!inner) {
   1109    // No print method to expose.
   1110    return true;
   1111  }
   1112 
   1113  nsCOMPtr<nsIPrincipal> targetPrincipal = GetNoPDFJSPrincipal(inner);
   1114  if (!targetPrincipal) {
   1115    // Nothing special to be done.
   1116    return true;
   1117  }
   1118 
   1119  if (!nsContentUtils::SubjectPrincipal(cx)->Equals(targetPrincipal)) {
   1120    // Not our origin's PDF document.
   1121    return true;
   1122  }
   1123 
   1124  // Get the function we plan to actually call.
   1125  JS::Rooted<JSObject*> innerObj(cx, inner->GetGlobalJSObject());
   1126  if (!innerObj) {
   1127    // Really should not happen, but ok, let's just return.
   1128    return true;
   1129  }
   1130 
   1131  JS::Rooted<JS::Value> targetFunc(cx);
   1132  {
   1133    JSAutoRealm ar(cx, innerObj);
   1134    if (!JS_GetProperty(cx, innerObj, "print", &targetFunc)) {
   1135      return false;
   1136    }
   1137  }
   1138 
   1139  if (!targetFunc.isObject()) {
   1140    // Who knows what's going on.  Just return.
   1141    return true;
   1142  }
   1143 
   1144  // The Realm of cx is the realm our caller is in and the realm we
   1145  // should create our function in.  Note that we can't use the
   1146  // standard XPConnect function forwarder machinery because our
   1147  // "this" is cross-origin, so we have to do thus by hand.
   1148 
   1149  // Make sure targetFunc is wrapped into the right compartment.
   1150  if (!MaybeWrapValue(cx, &targetFunc)) {
   1151    return false;
   1152  }
   1153 
   1154  JSFunction* fun =
   1155      js::NewFunctionWithReserved(cx, PDFJSPrintMethod, 0, 0, "print");
   1156  if (!fun) {
   1157    return false;
   1158  }
   1159 
   1160  JS::Rooted<JSObject*> funObj(cx, JS_GetFunctionObject(fun));
   1161  js::SetFunctionNativeReserved(funObj, PDFJS_SLOT_CALLEE, targetFunc);
   1162 
   1163  // { value: <print>, writable: true, enumerable: true, configurable: true }
   1164  // because that's what it would have been in the same-origin case without
   1165  // the PDF viewer messing with things.
   1166  desc.set(Some(JS::PropertyDescriptor::Data(
   1167      JS::ObjectValue(*funObj),
   1168      {JS::PropertyAttribute::Configurable, JS::PropertyAttribute::Enumerable,
   1169       JS::PropertyAttribute::Writable})));
   1170  return true;
   1171 }
   1172 
   1173 // static
   1174 bool nsOuterWindowProxy::PDFJSPrintMethod(JSContext* cx, unsigned argc,
   1175                                          JS::Value* vp) {
   1176  JS::CallArgs args = CallArgsFromVp(argc, vp);
   1177 
   1178  JS::Rooted<JSObject*> realCallee(
   1179      cx, &js::GetFunctionNativeReserved(&args.callee(), PDFJS_SLOT_CALLEE)
   1180               .toObject());
   1181  // Unchecked unwrap, because we want to extract the thing we really had
   1182  // before.
   1183  realCallee = js::UncheckedUnwrap(realCallee);
   1184 
   1185  JS::Rooted<JS::Value> thisv(cx, args.thisv());
   1186  if (thisv.isNullOrUndefined()) {
   1187    // Replace it with the global of our stashed callee, simulating the
   1188    // global-assuming behavior of DOM methods.
   1189    JS::Rooted<JSObject*> global(cx, JS::GetNonCCWObjectGlobal(realCallee));
   1190    if (!MaybeWrapObject(cx, &global)) {
   1191      return false;
   1192    }
   1193    thisv.setObject(*global);
   1194  } else if (!thisv.isObject()) {
   1195    return ThrowInvalidThis(cx, args, false, prototypes::id::Window);
   1196  }
   1197 
   1198  // We want to do an UncheckedUnwrap here, because we're going to directly
   1199  // examine the principal of the inner window, if we have an inner window.
   1200  JS::Rooted<JSObject*> unwrappedObj(cx,
   1201                                     js::UncheckedUnwrap(&thisv.toObject()));
   1202  nsGlobalWindowInner* inner = nullptr;
   1203  {
   1204    // Do the unwrap in the Realm of the object we're looking at.
   1205    JSAutoRealm ar(cx, unwrappedObj);
   1206    UNWRAP_MAYBE_CROSS_ORIGIN_OBJECT(Window, &unwrappedObj, inner, cx);
   1207  }
   1208  if (!inner) {
   1209    return ThrowInvalidThis(cx, args, false, prototypes::id::Window);
   1210  }
   1211 
   1212  nsIPrincipal* callerPrincipal = nsContentUtils::SubjectPrincipal(cx);
   1213  if (!callerPrincipal->SubsumesConsideringDomain(inner->GetPrincipal())) {
   1214    // Check whether it's a PDF viewer from our origin.
   1215    nsCOMPtr<nsIPrincipal> pdfPrincipal = GetNoPDFJSPrincipal(inner);
   1216    if (!pdfPrincipal || !callerPrincipal->Equals(pdfPrincipal)) {
   1217      // Security error.
   1218      return ThrowInvalidThis(cx, args, true, prototypes::id::Window);
   1219    }
   1220  }
   1221 
   1222  // Go ahead and enter the Realm of our real callee to call it.  We'll pass it
   1223  // our "thisv", just in case someone grabs a "print" method off one PDF
   1224  // document and .call()s it on another one.
   1225  {
   1226    JSAutoRealm ar(cx, realCallee);
   1227    if (!MaybeWrapValue(cx, &thisv)) {
   1228      return false;
   1229    }
   1230 
   1231    // Don't bother passing through the args; they will get ignored anyway.
   1232 
   1233    if (!JS::Call(cx, thisv, realCallee, JS::HandleValueArray::empty(),
   1234                  args.rval())) {
   1235      return false;
   1236    }
   1237  }
   1238 
   1239  // Wrap the return value (not that there should be any!) into the right
   1240  // compartment.
   1241  return MaybeWrapValue(cx, args.rval());
   1242 }
   1243 
   1244 // static
   1245 already_AddRefed<nsIPrincipal> nsOuterWindowProxy::GetNoPDFJSPrincipal(
   1246    nsGlobalWindowInner* inner) {
   1247  if (!nsContentUtils::IsPDFJS(inner->GetPrincipal())) {
   1248    return nullptr;
   1249  }
   1250 
   1251  if (Document* doc = inner->GetExtantDoc()) {
   1252    if (nsCOMPtr<nsIPropertyBag2> propBag =
   1253            do_QueryInterface(doc->GetChannel())) {
   1254      nsCOMPtr<nsIPrincipal> principal(
   1255          do_GetProperty(propBag, u"noPDFJSPrincipal"_ns));
   1256      return principal.forget();
   1257    }
   1258  }
   1259  return nullptr;
   1260 }
   1261 
   1262 const nsOuterWindowProxy nsOuterWindowProxy::singleton;
   1263 
   1264 class nsChromeOuterWindowProxy : public nsOuterWindowProxy {
   1265 public:
   1266  constexpr nsChromeOuterWindowProxy() = default;
   1267 
   1268  const char* className(JSContext* cx,
   1269                        JS::Handle<JSObject*> wrapper) const override;
   1270 
   1271  static const nsChromeOuterWindowProxy singleton;
   1272 };
   1273 
   1274 const char* nsChromeOuterWindowProxy::className(
   1275    JSContext* cx, JS::Handle<JSObject*> proxy) const {
   1276  MOZ_ASSERT(js::IsProxy(proxy));
   1277 
   1278  return "ChromeWindow";
   1279 }
   1280 
   1281 const nsChromeOuterWindowProxy nsChromeOuterWindowProxy::singleton;
   1282 
   1283 static JSObject* NewOuterWindowProxy(JSContext* cx,
   1284                                     JS::Handle<JSObject*> global,
   1285                                     bool isChrome) {
   1286  MOZ_ASSERT(JS_IsGlobalObject(global));
   1287 
   1288  JSAutoRealm ar(cx, global);
   1289 
   1290  js::WrapperOptions options;
   1291  options.setClass(&OuterWindowProxyClass);
   1292  JSObject* obj =
   1293      js::Wrapper::New(cx, global,
   1294                       isChrome ? &nsChromeOuterWindowProxy::singleton
   1295                                : &nsOuterWindowProxy::singleton,
   1296                       options);
   1297  MOZ_ASSERT_IF(obj, js::IsWindowProxy(obj));
   1298  return obj;
   1299 }
   1300 
   1301 //*****************************************************************************
   1302 //***    nsGlobalWindowOuter: Object Management
   1303 //*****************************************************************************
   1304 
   1305 nsGlobalWindowOuter::nsGlobalWindowOuter(uint64_t aWindowID)
   1306    : nsPIDOMWindowOuter(aWindowID),
   1307      mFullscreenHasChangedDuringProcessing(false),
   1308      mForceFullScreenInWidget(false),
   1309      mIsClosed(false),
   1310      mInClose(false),
   1311      mHavePendingClose(false),
   1312      mBlockScriptedClosingFlag(false),
   1313      mWasOffline(false),
   1314      mCreatingInnerWindow(false),
   1315      mIsChrome(false),
   1316      mAllowScriptsToClose(false),
   1317      mTopLevelOuterContentWindow(false),
   1318      mDelayedPrintUntilAfterLoad(false),
   1319      mDelayedCloseForPrinting(false),
   1320      mShouldDelayPrintUntilAfterLoad(false),
   1321 #ifdef DEBUG
   1322      mSerial(0),
   1323      mSetOpenerWindowCalled(false),
   1324 #endif
   1325      mCleanedUp(false),
   1326      mCanSkipCCGeneration(0),
   1327      mAutoActivateVRDisplayID(0) {
   1328  AssertIsOnMainThread();
   1329  SetIsOnMainThread();
   1330  nsLayoutStatics::AddRef();
   1331 
   1332  // Initialize the PRCList (this).
   1333  PR_INIT_CLIST(this);
   1334 
   1335  // |this| is an outer window. Outer windows start out frozen and
   1336  // remain frozen until they get an inner window.
   1337  MOZ_ASSERT(IsFrozen());
   1338 
   1339  // We could have failed the first time through trying
   1340  // to create the entropy collector, so we should
   1341  // try to get one until we succeed.
   1342 
   1343 #ifdef DEBUG
   1344  mSerial = nsContentUtils::InnerOrOuterWindowCreated();
   1345 
   1346  MOZ_LOG(gDocShellAndDOMWindowLeakLogging, LogLevel::Info,
   1347          ("++DOMWINDOW == %d (%p) [pid = %d] [serial = %d] [outer = %p]\n",
   1348           nsContentUtils::GetCurrentInnerOrOuterWindowCount(),
   1349           static_cast<void*>(ToCanonicalSupports(this)), getpid(), mSerial,
   1350           nullptr));
   1351 #endif
   1352 
   1353  MOZ_LOG(gDOMLeakPRLogOuter, LogLevel::Debug,
   1354          ("DOMWINDOW %p created outer=nullptr", this));
   1355 
   1356  // Add ourselves to the outer windows list.
   1357  MOZ_ASSERT(sOuterWindowsById, "Outer Windows hash table must be created!");
   1358 
   1359  // |this| is an outer window, add to the outer windows list.
   1360  MOZ_ASSERT(!sOuterWindowsById->Contains(mWindowID),
   1361             "This window shouldn't be in the hash table yet!");
   1362  // We seem to see crashes in release builds because of null
   1363  // |sOuterWindowsById|.
   1364  if (sOuterWindowsById) {
   1365    sOuterWindowsById->InsertOrUpdate(mWindowID, this);
   1366  }
   1367 }
   1368 
   1369 #ifdef DEBUG
   1370 
   1371 /* static */
   1372 void nsGlobalWindowOuter::AssertIsOnMainThread() {
   1373  MOZ_ASSERT(NS_IsMainThread());
   1374 }
   1375 
   1376 #endif  // DEBUG
   1377 
   1378 /* static */
   1379 void nsGlobalWindowOuter::Init() {
   1380  AssertIsOnMainThread();
   1381 
   1382  NS_ASSERTION(gDOMLeakPRLogOuter,
   1383               "gDOMLeakPRLogOuter should have been initialized!");
   1384 
   1385  sOuterWindowsById = new OuterWindowByIdTable();
   1386 }
   1387 
   1388 nsGlobalWindowOuter::~nsGlobalWindowOuter() {
   1389  AssertIsOnMainThread();
   1390 
   1391  if (sOuterWindowsById) {
   1392    sOuterWindowsById->Remove(mWindowID);
   1393  }
   1394 
   1395  nsContentUtils::InnerOrOuterWindowDestroyed();
   1396 
   1397 #ifdef DEBUG
   1398  if (MOZ_LOG_TEST(gDocShellAndDOMWindowLeakLogging, LogLevel::Info)) {
   1399    nsAutoCString url;
   1400    if (mLastOpenedURI) {
   1401      url = mLastOpenedURI->GetSpecOrDefault();
   1402 
   1403      // Data URLs can be very long, so truncate to avoid flooding the log.
   1404      const uint32_t maxURLLength = 1000;
   1405      if (url.Length() > maxURLLength) {
   1406        url.Truncate(maxURLLength);
   1407      }
   1408    }
   1409 
   1410    MOZ_LOG(
   1411        gDocShellAndDOMWindowLeakLogging, LogLevel::Info,
   1412        ("--DOMWINDOW == %d (%p) [pid = %d] [serial = %d] [outer = %p] [url = "
   1413         "%s]\n",
   1414         nsContentUtils::GetCurrentInnerOrOuterWindowCount(),
   1415         static_cast<void*>(ToCanonicalSupports(this)), getpid(), mSerial,
   1416         nullptr, url.get()));
   1417  }
   1418 #endif
   1419 
   1420  MOZ_LOG(gDOMLeakPRLogOuter, LogLevel::Debug,
   1421          ("DOMWINDOW %p destroyed", this));
   1422 
   1423  JSObject* proxy = GetWrapperMaybeDead();
   1424  if (proxy) {
   1425    if (mBrowsingContext && mBrowsingContext->GetUnbarrieredWindowProxy()) {
   1426      nsGlobalWindowOuter* outer = nsOuterWindowProxy::GetOuterWindow(
   1427          mBrowsingContext->GetUnbarrieredWindowProxy());
   1428      // Check that the current WindowProxy object corresponds to this
   1429      // nsGlobalWindowOuter, because we don't want to clear the WindowProxy if
   1430      // we've replaced it with a cross-process WindowProxy.
   1431      if (outer == this) {
   1432        mBrowsingContext->ClearWindowProxy();
   1433      }
   1434    }
   1435    js::SetProxyReservedSlot(proxy, OUTER_WINDOW_SLOT,
   1436                             JS::PrivateValue(nullptr));
   1437  }
   1438 
   1439  // An outer window is destroyed with inner windows still possibly
   1440  // alive, iterate through the inner windows and null out their
   1441  // back pointer to this outer, and pull them out of the list of
   1442  // inner windows.
   1443  //
   1444  // Our linked list of inner windows both contains (an nsGlobalWindowOuter),
   1445  // and our inner windows (nsGlobalWindowInners). This means that we need to
   1446  // use PRCList*. We can then compare that PRCList* to `this` to see if its an
   1447  // inner or outer window.
   1448  PRCList* w;
   1449  while ((w = PR_LIST_HEAD(this)) != this) {
   1450    PR_REMOVE_AND_INIT_LINK(w);
   1451  }
   1452 
   1453  DropOuterWindowDocs();
   1454 
   1455  // Outer windows are always supposed to call CleanUp before letting themselves
   1456  // be destroyed.
   1457  MOZ_ASSERT(mCleanedUp);
   1458 
   1459  nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
   1460  if (ac) ac->RemoveWindowAsListener(this);
   1461 
   1462  nsLayoutStatics::Release();
   1463 }
   1464 
   1465 // static
   1466 void nsGlobalWindowOuter::ShutDown() {
   1467  AssertIsOnMainThread();
   1468 
   1469  delete sOuterWindowsById;
   1470  sOuterWindowsById = nullptr;
   1471 }
   1472 
   1473 void nsGlobalWindowOuter::DropOuterWindowDocs() {
   1474  MOZ_ASSERT_IF(mDoc, !mDoc->EventHandlingSuppressed());
   1475  mDoc = nullptr;
   1476  mSuspendedDocs.Clear();
   1477 }
   1478 
   1479 void nsGlobalWindowOuter::CleanUp() {
   1480  // Guarantee idempotence.
   1481  if (mCleanedUp) return;
   1482  mCleanedUp = true;
   1483 
   1484  StartDying();
   1485 
   1486  mWindowUtils = nullptr;
   1487 
   1488  ClearControllers();
   1489 
   1490  mContext = nullptr;             // Forces Release
   1491  mChromeEventHandler = nullptr;  // Forces Release
   1492  mParentTarget = nullptr;
   1493  mMessageManager = nullptr;
   1494 
   1495  mArguments = nullptr;
   1496 }
   1497 
   1498 void nsGlobalWindowOuter::ClearControllers() {
   1499  if (mControllers) {
   1500    uint32_t count;
   1501    mControllers->GetControllerCount(&count);
   1502 
   1503    while (count--) {
   1504      nsCOMPtr<nsIController> controller;
   1505      mControllers->GetControllerAt(count, getter_AddRefs(controller));
   1506      if (nsCOMPtr<nsBaseCommandController> context =
   1507              do_QueryInterface(controller)) {
   1508        context->SetContext(nullptr);
   1509      }
   1510    }
   1511 
   1512    mControllers = nullptr;
   1513  }
   1514 }
   1515 
   1516 //*****************************************************************************
   1517 // nsGlobalWindowOuter::nsISupports
   1518 //*****************************************************************************
   1519 
   1520 // QueryInterface implementation for nsGlobalWindowOuter
   1521 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsGlobalWindowOuter)
   1522  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   1523  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, EventTarget)
   1524  NS_INTERFACE_MAP_ENTRY(nsIDOMWindow)
   1525  NS_INTERFACE_MAP_ENTRY(nsIGlobalObject)
   1526  NS_INTERFACE_MAP_ENTRY(nsIScriptGlobalObject)
   1527  NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal)
   1528  NS_INTERFACE_MAP_ENTRY(mozilla::dom::EventTarget)
   1529  NS_INTERFACE_MAP_ENTRY(nsPIDOMWindowOuter)
   1530  NS_INTERFACE_MAP_ENTRY(mozIDOMWindowProxy)
   1531  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
   1532  NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
   1533 NS_INTERFACE_MAP_END
   1534 
   1535 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGlobalWindowOuter)
   1536 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsGlobalWindowOuter)
   1537 
   1538 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsGlobalWindowOuter)
   1539  if (tmp->IsBlackForCC(false)) {
   1540    if (nsCCUncollectableMarker::InGeneration(tmp->mCanSkipCCGeneration)) {
   1541      return true;
   1542    }
   1543    tmp->mCanSkipCCGeneration = nsCCUncollectableMarker::sGeneration;
   1544    if (EventListenerManager* elm = tmp->GetExistingListenerManager()) {
   1545      elm->MarkForCC();
   1546    }
   1547    return true;
   1548  }
   1549 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
   1550 
   1551 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsGlobalWindowOuter)
   1552  return tmp->IsBlackForCC(true);
   1553 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
   1554 
   1555 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsGlobalWindowOuter)
   1556  return tmp->IsBlackForCC(false);
   1557 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
   1558 
   1559 NS_IMPL_CYCLE_COLLECTION_CLASS(nsGlobalWindowOuter)
   1560 
   1561 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsGlobalWindowOuter)
   1562  if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
   1563    char name[512];
   1564    nsAutoCString uri;
   1565    if (tmp->mDoc && tmp->mDoc->GetDocumentURI()) {
   1566      uri = tmp->mDoc->GetDocumentURI()->GetSpecOrDefault();
   1567    }
   1568    SprintfLiteral(name, "nsGlobalWindowOuter # %" PRIu64 " outer %s",
   1569                   tmp->mWindowID, uri.get());
   1570    cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
   1571  } else {
   1572    NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsGlobalWindowOuter, tmp->mRefCnt.get())
   1573  }
   1574 
   1575  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext)
   1576 
   1577  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mControllers)
   1578  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mArguments)
   1579 
   1580  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocalStorage)
   1581  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSuspendedDocs)
   1582  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentPrincipal)
   1583  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentCookiePrincipal)
   1584  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentStoragePrincipal)
   1585  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentPartitionedPrincipal)
   1586  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDoc)
   1587 
   1588  // Traverse stuff from nsPIDOMWindow
   1589  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeEventHandler)
   1590  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParentTarget)
   1591  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessageManager)
   1592  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameElement)
   1593 
   1594  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocShell)
   1595  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowsingContext)
   1596 
   1597  tmp->TraverseObjectsInGlobal(cb);
   1598 
   1599  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeFields.mBrowserDOMWindow)
   1600 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
   1601 
   1602 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindowOuter)
   1603  NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE
   1604  if (sOuterWindowsById) {
   1605    sOuterWindowsById->Remove(tmp->mWindowID);
   1606  }
   1607 
   1608  NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext)
   1609 
   1610  NS_IMPL_CYCLE_COLLECTION_UNLINK(mControllers)
   1611  NS_IMPL_CYCLE_COLLECTION_UNLINK(mArguments)
   1612 
   1613  NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocalStorage)
   1614  NS_IMPL_CYCLE_COLLECTION_UNLINK(mSuspendedDocs)
   1615  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentPrincipal)
   1616  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentCookiePrincipal)
   1617  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentStoragePrincipal)
   1618  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentPartitionedPrincipal)
   1619  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDoc)
   1620 
   1621  // Unlink stuff from nsPIDOMWindow
   1622  NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeEventHandler)
   1623  NS_IMPL_CYCLE_COLLECTION_UNLINK(mParentTarget)
   1624  NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageManager)
   1625  NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameElement)
   1626 
   1627  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocShell)
   1628  if (tmp->mBrowsingContext) {
   1629    if (tmp->mBrowsingContext->GetUnbarrieredWindowProxy()) {
   1630      nsGlobalWindowOuter* outer = nsOuterWindowProxy::GetOuterWindow(
   1631          tmp->mBrowsingContext->GetUnbarrieredWindowProxy());
   1632      // Check that the current WindowProxy object corresponds to this
   1633      // nsGlobalWindowOuter, because we don't want to clear the WindowProxy if
   1634      // we've replaced it with a cross-process WindowProxy.
   1635      if (outer == tmp) {
   1636        tmp->mBrowsingContext->ClearWindowProxy();
   1637      }
   1638    }
   1639    tmp->mBrowsingContext = nullptr;
   1640  }
   1641 
   1642  tmp->UnlinkObjectsInGlobal();
   1643 
   1644  if (tmp->IsChromeWindow()) {
   1645    NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeFields.mBrowserDOMWindow)
   1646  }
   1647 
   1648  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
   1649 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
   1650 
   1651 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsGlobalWindowOuter)
   1652  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
   1653 NS_IMPL_CYCLE_COLLECTION_TRACE_END
   1654 
   1655 bool nsGlobalWindowOuter::IsBlackForCC(bool aTracingNeeded) {
   1656  if (!nsCCUncollectableMarker::sGeneration) {
   1657    return false;
   1658  }
   1659 
   1660  // Unlike most wrappers, the outer window wrapper is not a wrapper for
   1661  // the outer window. Instead, the outer window wrapper holds the inner
   1662  // window binding object, which in turn holds the nsGlobalWindowInner, which
   1663  // has a strong reference to the nsGlobalWindowOuter. We're using the
   1664  // mInnerWindow pointer as a flag for that whole chain.
   1665  return (nsCCUncollectableMarker::InGeneration(GetMarkedCCGeneration()) ||
   1666          (mInnerWindow && HasKnownLiveWrapper())) &&
   1667         (!aTracingNeeded || HasNothingToTrace(ToSupports(this)));
   1668 }
   1669 
   1670 //*****************************************************************************
   1671 // nsGlobalWindowOuter::nsIScriptGlobalObject
   1672 //*****************************************************************************
   1673 
   1674 bool nsGlobalWindowOuter::ShouldResistFingerprinting(RFPTarget aTarget) const {
   1675  if (mDoc) {
   1676    return mDoc->ShouldResistFingerprinting(aTarget);
   1677  }
   1678  return nsContentUtils::ShouldResistFingerprinting(
   1679      "If we do not have a document then we do not have any context"
   1680      "to make an informed RFP choice, so we fall back to the global pref",
   1681      aTarget);
   1682 }
   1683 
   1684 OriginTrials nsGlobalWindowOuter::Trials() const {
   1685  return mInnerWindow ? nsGlobalWindowInner::Cast(mInnerWindow)->Trials()
   1686                      : OriginTrials();
   1687 }
   1688 
   1689 FontFaceSet* nsGlobalWindowOuter::GetFonts() {
   1690  if (mDoc) {
   1691    return mDoc->Fonts();
   1692  }
   1693  return nullptr;
   1694 }
   1695 
   1696 nsresult nsGlobalWindowOuter::EnsureScriptEnvironment() {
   1697  if (GetWrapperPreserveColor()) {
   1698    return NS_OK;
   1699  }
   1700 
   1701  NS_ENSURE_STATE(!mCleanedUp);
   1702 
   1703  NS_ASSERTION(!GetCurrentInnerWindowInternal(this),
   1704               "No cached wrapper, but we have an inner window?");
   1705  NS_ASSERTION(!mContext, "Will overwrite mContext!");
   1706 
   1707  // If this window is an [i]frame, don't bother GC'ing when the frame's context
   1708  // is destroyed since a GC will happen when the frameset or host document is
   1709  // destroyed anyway.
   1710  mContext = new nsJSContext(mBrowsingContext->IsTop(), this);
   1711  return NS_OK;
   1712 }
   1713 
   1714 nsIScriptContext* nsGlobalWindowOuter::GetScriptContext() { return mContext; }
   1715 
   1716 bool nsGlobalWindowOuter::WouldReuseInnerWindow(Document* aNewDocument) {
   1717  // We reuse the inner window when:
   1718  // a. The current document is transient, i.e. a temporary placeholder while
   1719  //    an async load is ongoing. This is equivalent to the uncommitted initial
   1720  //    document.
   1721  // b. At least one of the following conditions are true:
   1722  // -- The new document is the same as the old document. This means that we're
   1723  //    getting called from document.open().
   1724  // -- The new document has the same origin as what we have loaded right now.
   1725 
   1726  if (!mDoc || !aNewDocument) {
   1727    return false;
   1728  }
   1729 
   1730  if (!mDoc->IsUncommittedInitialDocument()) {
   1731    return false;
   1732  }
   1733 
   1734 #ifdef DEBUG
   1735  {
   1736    nsCOMPtr<nsIURI> uri;
   1737    NS_GetURIWithoutRef(mDoc->GetDocumentURI(), getter_AddRefs(uri));
   1738    NS_ASSERTION(NS_IsAboutBlank(uri), "How'd this happen?");
   1739  }
   1740 #endif
   1741 
   1742  // Great, we're the original document, check for one of the other
   1743  // conditions.
   1744 
   1745  if (mDoc == aNewDocument) {
   1746    return true;
   1747  }
   1748 
   1749  if (aNewDocument->IsStaticDocument()) {
   1750    return false;
   1751  }
   1752 
   1753  if (BasePrincipal::Cast(mDoc->NodePrincipal())
   1754          ->FastEqualsConsideringDomain(aNewDocument->NodePrincipal())) {
   1755    // The origin is the same.
   1756    return true;
   1757  }
   1758 
   1759  return false;
   1760 }
   1761 
   1762 void nsGlobalWindowOuter::SetInitialPrincipal(
   1763    nsIPrincipal* aNewWindowPrincipal) {
   1764  // We should never create windows with an expanded principal.
   1765  // If we have a system principal, make sure we're not using it for a content
   1766  // docshell.
   1767  // NOTE: Please keep this logic in sync with
   1768  // nsAppShellService::JustCreateTopWindow
   1769  if (nsContentUtils::IsExpandedPrincipal(aNewWindowPrincipal) ||
   1770      (aNewWindowPrincipal->IsSystemPrincipal() &&
   1771       GetBrowsingContext()->IsContent())) {
   1772    aNewWindowPrincipal = nullptr;
   1773  }
   1774 
   1775  MOZ_ASSERT(mDoc, "Some document should've been eagerly created");
   1776 
   1777  // Bail if the existing document is (a) not initial
   1778  if (!mDoc->IsUncommittedInitialDocument()) return;
   1779  // or (b) already has the correct principal.
   1780  if (mDoc->NodePrincipal() == aNewWindowPrincipal) return;
   1781 
   1782 #ifdef DEBUG
   1783  // The current document should be a dummy and therefore have a null principal
   1784  bool isNullPrincipal;
   1785  MOZ_ASSERT(NS_SUCCEEDED(
   1786                 mDoc->NodePrincipal()->GetIsNullPrincipal(&isNullPrincipal)) &&
   1787             isNullPrincipal);
   1788 #endif
   1789 
   1790  // Use the subject (or system) principal as the storage principal too until
   1791  // the new window finishes navigating and gets a real storage principal.
   1792  nsDocShell::Cast(GetDocShell())
   1793      ->CreateAboutBlankDocumentViewer(
   1794          aNewWindowPrincipal, aNewWindowPrincipal, mDoc->GetPolicyContainer(),
   1795          mDoc->GetDocBaseURI(),
   1796          /* aIsInitialDocument */ true, mDoc->GetEmbedderPolicy());
   1797 
   1798  if (mDoc) {
   1799    MOZ_ASSERT(mDoc->IsInitialDocument(),
   1800               "document should be initial document");
   1801  }
   1802 
   1803  RefPtr<PresShell> presShell = GetDocShell()->GetPresShell();
   1804  if (presShell && !presShell->DidInitialize()) {
   1805    // Ensure that if someone plays with this document they will get
   1806    // layout happening.
   1807    presShell->Initialize();
   1808  }
   1809 }
   1810 
   1811 #define WINDOWSTATEHOLDER_IID \
   1812  {0x0b917c3e, 0xbd50, 0x4683, {0xaf, 0xc9, 0xc7, 0x81, 0x07, 0xae, 0x33, 0x26}}
   1813 
   1814 class WindowStateHolder final : public nsISupports {
   1815 public:
   1816  NS_INLINE_DECL_STATIC_IID(WINDOWSTATEHOLDER_IID)
   1817  NS_DECL_ISUPPORTS
   1818 
   1819  explicit WindowStateHolder(nsGlobalWindowInner* aWindow);
   1820 
   1821  nsGlobalWindowInner* GetInnerWindow() { return mInnerWindow; }
   1822 
   1823  void DidRestoreWindow() {
   1824    mInnerWindow = nullptr;
   1825    mInnerWindowReflector = nullptr;
   1826  }
   1827 
   1828 protected:
   1829  ~WindowStateHolder();
   1830 
   1831  nsGlobalWindowInner* mInnerWindow;
   1832  // We hold onto this to make sure the inner window doesn't go away. The outer
   1833  // window ends up recalculating it anyway.
   1834  JS::PersistentRooted<JSObject*> mInnerWindowReflector;
   1835 };
   1836 
   1837 WindowStateHolder::WindowStateHolder(nsGlobalWindowInner* aWindow)
   1838    : mInnerWindow(aWindow),
   1839      mInnerWindowReflector(RootingCx(), aWindow->GetWrapper()) {
   1840  MOZ_ASSERT(aWindow, "null window");
   1841 
   1842  aWindow->Suspend();
   1843 
   1844  // When a global goes into the bfcache, we disable script.
   1845  xpc::Scriptability::Get(mInnerWindowReflector).SetWindowAllowsScript(false);
   1846 }
   1847 
   1848 WindowStateHolder::~WindowStateHolder() {
   1849  if (mInnerWindow) {
   1850    // This window was left in the bfcache and is now going away. We need to
   1851    // free it up.
   1852    // Note that FreeInnerObjects may already have been called on the
   1853    // inner window if its outer has already had SetDocShell(null)
   1854    // called.
   1855    mInnerWindow->FreeInnerObjects();
   1856  }
   1857 }
   1858 
   1859 NS_IMPL_ISUPPORTS(WindowStateHolder, WindowStateHolder)
   1860 
   1861 bool nsGlobalWindowOuter::ComputeIsSecureContext(Document* aDocument,
   1862                                                 SecureContextFlags aFlags) {
   1863  nsCOMPtr<nsIPrincipal> principal = aDocument->NodePrincipal();
   1864  if (principal->IsSystemPrincipal()) {
   1865    return true;
   1866  }
   1867 
   1868  // Implement https://w3c.github.io/webappsec-secure-contexts/#settings-object
   1869  // With some modifications to allow for aFlags.
   1870 
   1871  bool hadNonSecureContextCreator = false;
   1872 
   1873  if (WindowContext* parentWindow =
   1874          GetBrowsingContext()->GetParentWindowContext()) {
   1875    hadNonSecureContextCreator = !parentWindow->GetIsSecureContext();
   1876  }
   1877 
   1878  if (hadNonSecureContextCreator) {
   1879    return false;
   1880  }
   1881 
   1882  if (nsContentUtils::HttpsStateIsModern(aDocument) ||
   1883      nsContentUtils::DocumentHasOnionURI(aDocument)) {
   1884    return true;
   1885  }
   1886 
   1887  if (principal->GetIsNullPrincipal()) {
   1888    // If the NullPrincipal has a valid precursor URI we want to use it to
   1889    // construct the principal otherwise we fall back to the original document
   1890    // URI.
   1891    nsCOMPtr<nsIPrincipal> precursorPrin = principal->GetPrecursorPrincipal();
   1892    nsCOMPtr<nsIURI> uri = precursorPrin ? precursorPrin->GetURI() : nullptr;
   1893    if (!uri) {
   1894      uri = aDocument->GetOriginalURI();
   1895    }
   1896    // IsOriginPotentiallyTrustworthy doesn't care about origin attributes so
   1897    // it doesn't actually matter what we use here, but reusing the document
   1898    // principal's attributes is convenient.
   1899    const OriginAttributes& attrs = principal->OriginAttributesRef();
   1900    // CreateContentPrincipal correctly gets a useful principal for blob: and
   1901    // other URI_INHERITS_SECURITY_CONTEXT URIs.
   1902    principal = BasePrincipal::CreateContentPrincipal(uri, attrs);
   1903    if (NS_WARN_IF(!principal)) {
   1904      return false;
   1905    }
   1906  }
   1907 
   1908  return principal->GetIsOriginPotentiallyTrustworthy();
   1909 }
   1910 
   1911 static bool InitializeLegacyNetscapeObject(JSContext* aCx,
   1912                                           JS::Handle<JSObject*> aGlobal) {
   1913  JSAutoRealm ar(aCx, aGlobal);
   1914 
   1915  // Note: MathJax depends on window.netscape being exposed. See bug 791526.
   1916  JS::Rooted<JSObject*> obj(aCx);
   1917  obj = JS_DefineObject(aCx, aGlobal, "netscape", nullptr);
   1918  NS_ENSURE_TRUE(obj, false);
   1919 
   1920  obj = JS_DefineObject(aCx, obj, "security", nullptr);
   1921  NS_ENSURE_TRUE(obj, false);
   1922 
   1923  return true;
   1924 }
   1925 
   1926 struct MOZ_STACK_CLASS CompartmentFinderState {
   1927  explicit CompartmentFinderState(nsIPrincipal* aPrincipal)
   1928      : principal(aPrincipal), compartment(nullptr) {}
   1929 
   1930  // Input: we look for a compartment which is same-origin with the
   1931  // given principal.
   1932  nsIPrincipal* principal;
   1933 
   1934  // Output: We set this member if we find a compartment.
   1935  JS::Compartment* compartment;
   1936 };
   1937 
   1938 static JS::CompartmentIterResult FindSameOriginCompartment(
   1939    JSContext* aCx, void* aData, JS::Compartment* aCompartment) {
   1940  auto* data = static_cast<CompartmentFinderState*>(aData);
   1941  MOZ_ASSERT(!data->compartment, "Why are we getting called?");
   1942 
   1943  // If this compartment is not safe to share across globals, don't do
   1944  // anything with it; in particular we should not be getting a
   1945  // CompartmentPrivate from such a compartment, because it may be in
   1946  // the middle of being collected and its CompartmentPrivate may no
   1947  // longer be valid.
   1948  if (!js::IsSharableCompartment(aCompartment)) {
   1949    return JS::CompartmentIterResult::KeepGoing;
   1950  }
   1951 
   1952  auto* compartmentPrivate = xpc::CompartmentPrivate::Get(aCompartment);
   1953  if (!compartmentPrivate ||
   1954      !compartmentPrivate->CanShareCompartmentWith(data->principal)) {
   1955    // Can't reuse this one, keep going.
   1956    return JS::CompartmentIterResult::KeepGoing;
   1957  }
   1958 
   1959  // We have a winner!
   1960  data->compartment = aCompartment;
   1961  return JS::CompartmentIterResult::Stop;
   1962 }
   1963 
   1964 static JS::RealmCreationOptions& SelectZone(
   1965    JSContext* aCx, nsIPrincipal* aPrincipal, nsGlobalWindowInner* aNewInner,
   1966    JS::RealmCreationOptions& aOptions) {
   1967  // Use the shared system compartment for chrome windows.
   1968  if (aPrincipal->IsSystemPrincipal()) {
   1969    return aOptions.setExistingCompartment(xpc::PrivilegedJunkScope());
   1970  }
   1971 
   1972  BrowsingContext* bc = aNewInner->GetBrowsingContext();
   1973  if (bc->IsTop()) {
   1974    // We're a toplevel load.  Use a new zone.  This way, when we do
   1975    // zone-based compartment sharing we won't share compartments
   1976    // across navigations.
   1977    return aOptions.setNewCompartmentAndZone();
   1978  }
   1979 
   1980  // Find the in-process ancestor highest in the hierarchy.
   1981  nsGlobalWindowInner* ancestor = nullptr;
   1982  for (WindowContext* wc = bc->GetParentWindowContext(); wc;
   1983       wc = wc->GetParentWindowContext()) {
   1984    if (nsGlobalWindowInner* win = wc->GetInnerWindow()) {
   1985      ancestor = win;
   1986    }
   1987  }
   1988 
   1989  // If we have an ancestor window, use its zone.
   1990  if (ancestor && ancestor->GetGlobalJSObject()) {
   1991    JS::Zone* zone = JS::GetObjectZone(ancestor->GetGlobalJSObject());
   1992    // Now try to find an existing compartment that's same-origin
   1993    // with our principal.
   1994    CompartmentFinderState data(aPrincipal);
   1995    JS_IterateCompartmentsInZone(aCx, zone, &data, FindSameOriginCompartment);
   1996    if (data.compartment) {
   1997      return aOptions.setExistingCompartment(data.compartment);
   1998    }
   1999    return aOptions.setNewCompartmentInExistingZone(
   2000        ancestor->GetGlobalJSObject());
   2001  }
   2002 
   2003  return aOptions.setNewCompartmentAndZone();
   2004 }
   2005 
   2006 /**
   2007 * Create a new global object that will be used for an inner window.
   2008 * Return the native global and an nsISupports 'holder' that can be used
   2009 * to manage the lifetime of it.
   2010 */
   2011 static nsresult CreateNativeGlobalForInner(
   2012    JSContext* aCx, nsGlobalWindowInner* aNewInner, Document* aDocument,
   2013    JS::MutableHandle<JSObject*> aGlobal, bool aIsSecureContext,
   2014    bool aDefineSharedArrayBufferConstructor) {
   2015  MOZ_ASSERT(aCx);
   2016  MOZ_ASSERT(aNewInner);
   2017 
   2018  nsCOMPtr<nsIURI> uri = aDocument->GetDocumentURI();
   2019  nsCOMPtr<nsIPrincipal> principal = aDocument->NodePrincipal();
   2020  MOZ_ASSERT(principal);
   2021 
   2022  // DOMWindow with nsEP is not supported, we have to make sure
   2023  // no one creates one accidentally.
   2024  nsCOMPtr<nsIExpandedPrincipal> nsEP = do_QueryInterface(principal);
   2025  MOZ_RELEASE_ASSERT(!nsEP, "DOMWindow with nsEP is not supported");
   2026 
   2027  JS::RealmOptions options;
   2028  JS::RealmCreationOptions& creationOptions = options.creationOptions();
   2029 
   2030  SelectZone(aCx, principal, aNewInner, creationOptions);
   2031 
   2032  // Define the SharedArrayBuffer global constructor property only if shared
   2033  // memory may be used and structured-cloned (e.g. through postMessage).
   2034  //
   2035  // When the global constructor property isn't defined, the SharedArrayBuffer
   2036  // constructor can still be reached through Web Assembly.  Omitting the global
   2037  // property just prevents feature-tests from being misled.  See bug 1624266.
   2038  creationOptions.setDefineSharedArrayBufferConstructor(
   2039      aDefineSharedArrayBufferConstructor);
   2040 
   2041  xpc::InitGlobalObjectOptions(
   2042      options, principal->IsSystemPrincipal(), aIsSecureContext,
   2043      aDocument->ShouldResistFingerprinting(RFPTarget::JSDateTimeUTC),
   2044      aDocument->ShouldResistFingerprinting(RFPTarget::JSMathFdlibm),
   2045      aDocument->ShouldResistFingerprinting(RFPTarget::JSLocale),
   2046      aDocument->GetBrowsingContext()->Top()->GetLanguageOverride(),
   2047      aDocument->GetBrowsingContext()->Top()->GetTimezoneOverride());
   2048 
   2049  // Determine if we need the Components object.
   2050  bool needComponents = principal->IsSystemPrincipal();
   2051  uint32_t flags = needComponents ? 0 : xpc::OMIT_COMPONENTS_OBJECT;
   2052  flags |= xpc::DONT_FIRE_ONNEWGLOBALHOOK;
   2053 
   2054  if (!Window_Binding::Wrap(aCx, aNewInner, aNewInner, options,
   2055                            nsJSPrincipals::get(principal), aGlobal) ||
   2056      !xpc::InitGlobalObject(aCx, aGlobal, flags)) {
   2057    return NS_ERROR_FAILURE;
   2058  }
   2059 
   2060  MOZ_ASSERT(aNewInner->GetWrapperPreserveColor() == aGlobal);
   2061 
   2062  // Set the location information for the new global, so that tools like
   2063  // about:memory may use that information
   2064  xpc::SetLocationForGlobal(aGlobal, uri);
   2065 
   2066  if (!InitializeLegacyNetscapeObject(aCx, aGlobal)) {
   2067    return NS_ERROR_FAILURE;
   2068  }
   2069 
   2070  return NS_OK;
   2071 }
   2072 
   2073 nsresult nsGlobalWindowOuter::SetNewDocument(Document* aDocument,
   2074                                             nsISupports* aState,
   2075                                             bool aForceReuseInnerWindow,
   2076                                             WindowGlobalChild* aActor) {
   2077  MOZ_ASSERT(mDocumentPrincipal == nullptr,
   2078             "mDocumentPrincipal prematurely set!");
   2079  MOZ_ASSERT(mDocumentCookiePrincipal == nullptr,
   2080             "mDocumentCookiePrincipal prematurely set!");
   2081  MOZ_ASSERT(mDocumentStoragePrincipal == nullptr,
   2082             "mDocumentStoragePrincipal prematurely set!");
   2083  MOZ_ASSERT(mDocumentPartitionedPrincipal == nullptr,
   2084             "mDocumentPartitionedPrincipal prematurely set!");
   2085  MOZ_ASSERT(aDocument);
   2086 
   2087  // Bail out early if we're in process of closing down the window.
   2088  NS_ENSURE_STATE(!mCleanedUp);
   2089 
   2090  NS_ASSERTION(!GetCurrentInnerWindow() ||
   2091                   GetCurrentInnerWindow()->GetExtantDoc() == mDoc,
   2092               "Uh, mDoc doesn't match the current inner window "
   2093               "document!");
   2094  bool wouldReuseInnerWindow = WouldReuseInnerWindow(aDocument);
   2095  if (aForceReuseInnerWindow && !wouldReuseInnerWindow && mDoc &&
   2096      mDoc->NodePrincipal() != aDocument->NodePrincipal()) {
   2097    NS_ERROR("Attempted forced inner window reuse while changing principal");
   2098    return NS_ERROR_UNEXPECTED;
   2099  }
   2100 
   2101  if (!mBrowsingContext->AncestorsAreCurrent()) {
   2102    return NS_ERROR_NOT_AVAILABLE;
   2103  }
   2104 
   2105  RefPtr<Document> oldDoc = mDoc;
   2106  MOZ_RELEASE_ASSERT(oldDoc != aDocument);
   2107 
   2108  AutoJSAPI jsapi;
   2109  jsapi.Init();
   2110  JSContext* cx = jsapi.cx();
   2111 
   2112  // Check if we're anywhere near the stack limit before we reach the
   2113  // transplanting code, since it has no good way to handle errors. This uses
   2114  // the untrusted script limit, which is not strictly necessary since no
   2115  // actual script should run.
   2116  js::AutoCheckRecursionLimit recursion(cx);
   2117  if (!recursion.checkConservativeDontReport(cx)) {
   2118    NS_WARNING("Overrecursion in SetNewDocument");
   2119    return NS_ERROR_FAILURE;
   2120  }
   2121 
   2122  if (!mDoc) {
   2123    // First document load.
   2124 
   2125    // Get our private root. If it is equal to us, then we need to
   2126    // attach our global key bindings that handles browser scrolling
   2127    // and other browser commands.
   2128    nsPIDOMWindowOuter* privateRoot = GetPrivateRoot();
   2129 
   2130    if (privateRoot == this) {
   2131      RootWindowGlobalKeyListener::AttachKeyHandler(mChromeEventHandler);
   2132    }
   2133  }
   2134 
   2135  MaybeResetWindowName(aDocument);
   2136 
   2137  /* No mDocShell means we're already been partially closed down.  When that
   2138     happens, setting status isn't a big requirement, so don't. (Doesn't happen
   2139     under normal circumstances, but bug 49615 describes a case.) */
   2140 
   2141  nsContentUtils::AddScriptRunner(
   2142      NewRunnableMethod("nsGlobalWindowOuter::ClearStatus", this,
   2143                        &nsGlobalWindowOuter::ClearStatus));
   2144 
   2145  // Sometimes, WouldReuseInnerWindow() returns true even if there's no inner
   2146  // window (see bug 776497). Be safe.
   2147  bool reUseInnerWindow = (aForceReuseInnerWindow || wouldReuseInnerWindow) &&
   2148                          GetCurrentInnerWindowInternal(this);
   2149 
   2150  nsresult rv;
   2151 
   2152  // We set mDoc even though this is an outer window to avoid
   2153  // having to *always* reach into the inner window to find the
   2154  // document.
   2155  mDoc = aDocument;
   2156 
   2157  nsDocShell::Cast(mDocShell)->MaybeRestoreWindowName();
   2158 
   2159  // We drop the print request for the old document on the floor, it never made
   2160  // it. We don't close the window here either even if we were asked to.
   2161  mShouldDelayPrintUntilAfterLoad = true;
   2162  mDelayedCloseForPrinting = false;
   2163  mDelayedPrintUntilAfterLoad = false;
   2164 
   2165  // Take this opportunity to clear mSuspendedDocs. Our old inner window is now
   2166  // responsible for unsuspending it.
   2167  mSuspendedDocs.Clear();
   2168 
   2169 #ifdef DEBUG
   2170  mLastOpenedURI = aDocument->GetDocumentURI();
   2171 #endif
   2172 
   2173  RefPtr<nsGlobalWindowInner> currentInner =
   2174      GetCurrentInnerWindowInternal(this);
   2175 
   2176  if (currentInner && currentInner->mNavigator) {
   2177    currentInner->mNavigator->OnNavigation();
   2178  }
   2179 
   2180  RefPtr<nsGlobalWindowInner> newInnerWindow;
   2181  bool createdInnerWindow = false;
   2182 
   2183  bool thisChrome = IsChromeWindow();
   2184 
   2185  nsCOMPtr<WindowStateHolder> wsh = do_QueryInterface(aState);
   2186  NS_ASSERTION(!aState || wsh,
   2187               "What kind of weird state are you giving me here?");
   2188 
   2189  bool doomCurrentInner = false;
   2190 
   2191  // Only non-gray (i.e. exposed to JS) objects should be assigned to
   2192  // newInnerGlobal.
   2193  JS::Rooted<JSObject*> newInnerGlobal(cx);
   2194  if (reUseInnerWindow) {
   2195    // We're reusing the current inner window.
   2196    NS_ASSERTION(!currentInner->IsFrozen(),
   2197                 "We should never be reusing a shared inner window");
   2198    newInnerWindow = currentInner;
   2199    newInnerGlobal = currentInner->GetWrapper();
   2200 
   2201    // We're reusing the inner window, but this still counts as a navigation,
   2202    // so all expandos and such defined on the outer window should go away.
   2203    // Force all Xray wrappers to be recomputed.
   2204    JS::Rooted<JSObject*> rootedObject(cx, GetWrapper());
   2205    if (!JS_RefreshCrossCompartmentWrappers(cx, rootedObject)) {
   2206      return NS_ERROR_FAILURE;
   2207    }
   2208 
   2209    // Inner windows are only reused for same-origin principals, but the
   2210    // principals don't necessarily match exactly. Update the principal on the
   2211    // realm to match the new document. NB: We don't just call
   2212    // currentInner->RefreshRealmPrincipals() here because we haven't yet set
   2213    // its mDoc to aDocument.
   2214    JS::Realm* realm = js::GetNonCCWObjectRealm(newInnerGlobal);
   2215 #ifdef DEBUG
   2216    bool sameOrigin = false;
   2217    nsIPrincipal* existing = nsJSPrincipals::get(JS::GetRealmPrincipals(realm));
   2218    aDocument->NodePrincipal()->Equals(existing, &sameOrigin);
   2219    MOZ_ASSERT(sameOrigin);
   2220 #endif
   2221    JS::SetRealmPrincipals(realm,
   2222                           nsJSPrincipals::get(aDocument->NodePrincipal()));
   2223  } else {
   2224    if (aState) {
   2225      newInnerWindow = wsh->GetInnerWindow();
   2226      newInnerGlobal = newInnerWindow->GetWrapper();
   2227    } else {
   2228      newInnerWindow = nsGlobalWindowInner::Create(this, thisChrome, aActor);
   2229      if (StaticPrefs::dom_timeout_defer_during_load() &&
   2230          !aDocument->NodePrincipal()->IsURIInPrefList(
   2231              "dom.timeout.defer_during_load.force-disable")) {
   2232        // ensure the initial loading state is known
   2233        newInnerWindow->SetActiveLoadingState(
   2234            aDocument->GetReadyStateEnum() ==
   2235            Document::ReadyState::READYSTATE_LOADING);
   2236      }
   2237 
   2238      // The outer window is automatically treated as frozen when we
   2239      // null out the inner window. As a result, initializing classes
   2240      // on the new inner won't end up reaching into the old inner
   2241      // window for classes etc.
   2242      //
   2243      // [This happens with Object.prototype when XPConnect creates
   2244      // a temporary global while initializing classes; the reason
   2245      // being that xpconnect creates the temp global w/o a parent
   2246      // and proto, which makes the JS engine look up classes in
   2247      // cx->globalObject, i.e. this outer window].
   2248 
   2249      mInnerWindow = nullptr;
   2250 
   2251      mCreatingInnerWindow = true;
   2252 
   2253      // The SharedArrayBuffer global constructor property should not be present
   2254      // in a fresh global object when shared memory objects aren't allowed
   2255      // (because COOP/COEP support isn't enabled, or because COOP/COEP don't
   2256      // act to isolate this page to a separate process).
   2257 
   2258      // Every script context we are initialized with must create a
   2259      // new global.
   2260      rv = CreateNativeGlobalForInner(
   2261          cx, newInnerWindow, aDocument, &newInnerGlobal,
   2262          ComputeIsSecureContext(aDocument),
   2263          newInnerWindow->IsSharedMemoryAllowedInternal(
   2264              aDocument->NodePrincipal()));
   2265      NS_ASSERTION(
   2266          NS_SUCCEEDED(rv) && newInnerGlobal &&
   2267              newInnerWindow->GetWrapperPreserveColor() == newInnerGlobal,
   2268          "Failed to get script global");
   2269 
   2270      mCreatingInnerWindow = false;
   2271      createdInnerWindow = true;
   2272 
   2273      NS_ENSURE_SUCCESS(rv, rv);
   2274    }
   2275 
   2276    if (currentInner && currentInner->GetWrapperPreserveColor()) {
   2277      // Don't free objects on our current inner window if it's going to be
   2278      // held in the bfcache.
   2279      if (!currentInner->IsFrozen()) {
   2280        doomCurrentInner = true;
   2281      }
   2282    }
   2283 
   2284    mInnerWindow = newInnerWindow;
   2285    MOZ_ASSERT(mInnerWindow);
   2286    mInnerWindow->TryToCacheTopInnerWindow();
   2287 
   2288    if (!GetWrapperPreserveColor()) {
   2289      JS::Rooted<JSObject*> outer(
   2290          cx, NewOuterWindowProxy(cx, newInnerGlobal, thisChrome));
   2291      NS_ENSURE_TRUE(outer, NS_ERROR_FAILURE);
   2292 
   2293      mBrowsingContext->CleanUpDanglingRemoteOuterWindowProxies(cx, &outer);
   2294      MOZ_ASSERT(js::IsWindowProxy(outer));
   2295 
   2296      js::SetProxyReservedSlot(outer, OUTER_WINDOW_SLOT,
   2297                               JS::PrivateValue(ToSupports(this)));
   2298 
   2299      // Inform the nsJSContext, which is the canonical holder of the outer.
   2300      mContext->SetWindowProxy(outer);
   2301 
   2302      SetWrapper(mContext->GetWindowProxy());
   2303    } else {
   2304      JS::Rooted<JSObject*> outerObject(
   2305          cx, NewOuterWindowProxy(cx, newInnerGlobal, thisChrome));
   2306      if (!outerObject) {
   2307        NS_ERROR("out of memory");
   2308        return NS_ERROR_FAILURE;
   2309      }
   2310 
   2311      JS::Rooted<JSObject*> obj(cx, GetWrapper());
   2312 
   2313      MOZ_ASSERT(js::IsWindowProxy(obj));
   2314 
   2315      js::SetProxyReservedSlot(obj, OUTER_WINDOW_SLOT,
   2316                               JS::PrivateValue(nullptr));
   2317      js::SetProxyReservedSlot(outerObject, OUTER_WINDOW_SLOT,
   2318                               JS::PrivateValue(nullptr));
   2319      js::SetProxyReservedSlot(obj, HOLDER_WEAKMAP_SLOT, JS::UndefinedValue());
   2320 
   2321      outerObject = xpc::TransplantObjectNukingXrayWaiver(cx, obj, outerObject);
   2322 
   2323      if (!outerObject) {
   2324        mBrowsingContext->ClearWindowProxy();
   2325        NS_ERROR("unable to transplant wrappers, probably OOM");
   2326        return NS_ERROR_FAILURE;
   2327      }
   2328 
   2329      js::SetProxyReservedSlot(outerObject, OUTER_WINDOW_SLOT,
   2330                               JS::PrivateValue(ToSupports(this)));
   2331 
   2332      SetWrapper(outerObject);
   2333 
   2334      MOZ_ASSERT(JS::GetNonCCWObjectGlobal(outerObject) == newInnerGlobal);
   2335 
   2336      // Inform the nsJSContext, which is the canonical holder of the outer.
   2337      mContext->SetWindowProxy(outerObject);
   2338    }
   2339 
   2340    // Enter the new global's realm.
   2341    JSAutoRealm ar(cx, GetWrapperPreserveColor());
   2342 
   2343    {
   2344      JS::Rooted<JSObject*> outer(cx, GetWrapperPreserveColor());
   2345      js::SetWindowProxy(cx, newInnerGlobal, outer);
   2346      mBrowsingContext->SetWindowProxy(outer);
   2347    }
   2348 
   2349    // Set scriptability based on the state of the WindowContext.
   2350    WindowContext* wc = mInnerWindow->GetWindowContext();
   2351    bool allow =
   2352        wc ? wc->CanExecuteScripts() : mBrowsingContext->CanExecuteScripts();
   2353    xpc::Scriptability::Get(GetWrapperPreserveColor())
   2354        .SetWindowAllowsScript(allow);
   2355 
   2356    if (!aState) {
   2357      // Get the "window" property once so it will be cached on our inner.  We
   2358      // have to do this here, not in binding code, because this has to happen
   2359      // after we've created the outer window proxy and stashed it in the outer
   2360      // nsGlobalWindowOuter, so GetWrapperPreserveColor() on that outer
   2361      // nsGlobalWindowOuter doesn't return null and
   2362      // nsGlobalWindowOuter::OuterObject works correctly.
   2363      JS::Rooted<JS::Value> unused(cx);
   2364      if (!JS_GetProperty(cx, newInnerGlobal, "window", &unused)) {
   2365        NS_ERROR("can't create the 'window' property");
   2366        return NS_ERROR_FAILURE;
   2367      }
   2368 
   2369      // And same thing for the "self" property.
   2370      if (!JS_GetProperty(cx, newInnerGlobal, "self", &unused)) {
   2371        NS_ERROR("can't create the 'self' property");
   2372        return NS_ERROR_FAILURE;
   2373      }
   2374    }
   2375  }
   2376 
   2377  JSAutoRealm ar(cx, GetWrapperPreserveColor());
   2378 
   2379  if (!aState && !reUseInnerWindow) {
   2380    // Loading a new page and creating a new inner window, *not*
   2381    // restoring from session history.
   2382 
   2383    // Now that both the the inner and outer windows are initialized
   2384    // let the script context do its magic to hook them together.
   2385    MOZ_ASSERT(mContext->GetWindowProxy() == GetWrapperPreserveColor());
   2386 #ifdef DEBUG
   2387    JS::Rooted<JSObject*> rootedJSObject(cx, GetWrapperPreserveColor());
   2388    JS::Rooted<JSObject*> proto1(cx), proto2(cx);
   2389    JS_GetPrototype(cx, rootedJSObject, &proto1);
   2390    JS_GetPrototype(cx, newInnerGlobal, &proto2);
   2391    NS_ASSERTION(proto1 == proto2,
   2392                 "outer and inner globals should have the same prototype");
   2393 #endif
   2394 
   2395    mInnerWindow->SyncStateFromParentWindow();
   2396  }
   2397 
   2398  // Add an extra ref in case we release mContext during GC.
   2399  nsCOMPtr<nsIScriptContext> kungFuDeathGrip(mContext);
   2400 
   2401  // Make sure the inner's document is set correctly before we call
   2402  // SetScriptGlobalObject, because that might try to examine document-dependent
   2403  // state.  Unfortunately, we can't do some of the other clearing/resetting
   2404  // work we do below until after SetScriptGlobalObject(), because it might
   2405  // depend on the document having the right scope object.
   2406  if (aState) {
   2407    MOZ_RELEASE_ASSERT(newInnerWindow->mDoc == aDocument);
   2408  } else {
   2409    if (reUseInnerWindow) {
   2410      MOZ_RELEASE_ASSERT(newInnerWindow->mDoc != aDocument);
   2411    }
   2412    newInnerWindow->mDoc = aDocument;
   2413  }
   2414 
   2415  aDocument->SetScriptGlobalObject(newInnerWindow);
   2416 
   2417  MOZ_RELEASE_ASSERT(newInnerWindow->mDoc == aDocument);
   2418 
   2419  if (mBrowsingContext->IsTopContent()) {
   2420    net::CookieJarSettings::Cast(aDocument->CookieJarSettings())
   2421        ->SetTopLevelWindowContextId(aDocument->InnerWindowID());
   2422  }
   2423 
   2424  newInnerWindow->RefreshReduceTimerPrecisionCallerType();
   2425 
   2426  if (!aState) {
   2427    if (reUseInnerWindow) {
   2428      // The StorageAccess state may have changed. Invalidate the cached
   2429      // StorageAllowed field, so that the next call to StorageAllowedForWindow
   2430      // recomputes it.
   2431      newInnerWindow->ClearStorageAllowedCache();
   2432 
   2433      // The storage objects contain the URL of the window. We have to
   2434      // recreate them when the innerWindow is reused.
   2435      newInnerWindow->mLocalStorage = nullptr;
   2436      newInnerWindow->mSessionStorage = nullptr;
   2437      newInnerWindow->mPerformance = nullptr;
   2438 
   2439      // This must be called after nullifying the internal objects because
   2440      // here we could recreate them, calling the getter methods, and store
   2441      // them into the JS slots. If we nullify them after, the slot values and
   2442      // the objects will be out of sync.
   2443      newInnerWindow->ClearDocumentDependentSlots(cx);
   2444    } else {
   2445      newInnerWindow->InitDocumentDependentState(cx);
   2446 
   2447      // Initialize DOM classes etc on the inner window.
   2448      JS::Rooted<JSObject*> obj(cx, newInnerGlobal);
   2449      rv = kungFuDeathGrip->InitClasses(obj);
   2450      NS_ENSURE_SUCCESS(rv, rv);
   2451    }
   2452 
   2453    // When replacing an initial about:blank document we call
   2454    // ExecutionReady again to update the client creation URL.
   2455    rv = newInnerWindow->ExecutionReady();
   2456    NS_ENSURE_SUCCESS(rv, rv);
   2457 
   2458    if (mArguments) {
   2459      newInnerWindow->DefineArgumentsProperty(mArguments);
   2460      mArguments = nullptr;
   2461    }
   2462 
   2463    // Give the new inner window our chrome event handler (since it
   2464    // doesn't have one).
   2465    newInnerWindow->mChromeEventHandler = mChromeEventHandler;
   2466  }
   2467 
   2468  if (!aState && reUseInnerWindow) {
   2469    // Notify our WindowGlobalChild that it has a new document. If `aState` was
   2470    // passed, we're restoring the window from the BFCache, so the document
   2471    // hasn't changed.
   2472    // If we didn't have a window global child before, then initializing
   2473    // it will have set all the required state, so we don't need to do
   2474    // it again.
   2475    mInnerWindow->GetWindowGlobalChild()->OnNewDocument(aDocument);
   2476  }
   2477 
   2478  // Update the current window for our BrowsingContext.
   2479  RefPtr<BrowsingContext> bc = GetBrowsingContext();
   2480 
   2481  if (bc->IsOwnedByProcess()) {
   2482    MOZ_ALWAYS_SUCCEEDS(bc->SetCurrentInnerWindowId(mInnerWindow->WindowID()));
   2483  }
   2484 
   2485  // We no longer need the old inner window.  Start its destruction if
   2486  // its not being reused and clear our reference.
   2487  if (doomCurrentInner) {
   2488    currentInner->FreeInnerObjects();
   2489  }
   2490  currentInner = nullptr;
   2491 
   2492  // We wait to fire the debugger hook until the window is all set up and hooked
   2493  // up with the outer. See bug 969156.
   2494  if (createdInnerWindow) {
   2495    nsContentUtils::AddScriptRunner(NewRunnableMethod(
   2496        "nsGlobalWindowInner::FireOnNewGlobalObject", newInnerWindow,
   2497        &nsGlobalWindowInner::FireOnNewGlobalObject));
   2498  }
   2499 
   2500  if (!newInnerWindow->mHasNotifiedGlobalCreated && mDoc) {
   2501    // We should probably notify, except if we have the initial about:blank
   2502    // in a chrome docshell, defer notification until the first non-initial
   2503    // document.
   2504    const bool isAboutBlankInChromeDocshell = [&] {
   2505      if (!mDocShell) {
   2506        return false;
   2507      }
   2508 
   2509      RefPtr<BrowsingContext> bc = mDocShell->GetBrowsingContext();
   2510      if (!bc || bc->GetType() != BrowsingContext::Type::Chrome) {
   2511        return false;
   2512      }
   2513 
   2514      return mDoc->IsInitialDocument();
   2515    }();
   2516 
   2517    if (!isAboutBlankInChromeDocshell) {
   2518      newInnerWindow->mHasNotifiedGlobalCreated = true;
   2519      nsContentUtils::AddScriptRunner(NewRunnableMethod(
   2520          "nsGlobalWindowOuter::DispatchDOMWindowCreated", this,
   2521          &nsGlobalWindowOuter::DispatchDOMWindowCreated));
   2522    }
   2523  }
   2524 
   2525  PreloadLocalStorage();
   2526 
   2527  // Do this here rather than in say the Document constructor, since
   2528  // we need a WindowContext available.
   2529  mDoc->InitUseCounters();
   2530 
   2531  return NS_OK;
   2532 }
   2533 
   2534 /* static */
   2535 void nsGlobalWindowOuter::PrepareForProcessChange(JSObject* aProxy) {
   2536  JS::Rooted<JSObject*> localProxy(RootingCx(), aProxy);
   2537  MOZ_ASSERT(js::IsWindowProxy(localProxy));
   2538 
   2539  RefPtr<nsGlobalWindowOuter> outerWindow =
   2540      nsOuterWindowProxy::GetOuterWindow(localProxy);
   2541  if (!outerWindow) {
   2542    return;
   2543  }
   2544 
   2545  AutoJSAPI jsapi;
   2546  jsapi.Init();
   2547  JSContext* cx = jsapi.cx();
   2548 
   2549  JSAutoRealm ar(cx, localProxy);
   2550 
   2551  // Clear out existing references from the browsing context and outer window to
   2552  // the proxy, and from the proxy to the outer window. These references will
   2553  // become invalid once the proxy is transplanted. Clearing the window proxy
   2554  // from the browsing context is also necessary to indicate that it is for an
   2555  // out of process window.
   2556  outerWindow->ClearWrapper(localProxy);
   2557  RefPtr<BrowsingContext> bc = outerWindow->GetBrowsingContext();
   2558  MOZ_ASSERT(bc);
   2559  MOZ_ASSERT(bc->GetWindowProxy() == localProxy);
   2560  bc->ClearWindowProxy();
   2561  js::SetProxyReservedSlot(localProxy, OUTER_WINDOW_SLOT,
   2562                           JS::PrivateValue(nullptr));
   2563  js::SetProxyReservedSlot(localProxy, HOLDER_WEAKMAP_SLOT,
   2564                           JS::UndefinedValue());
   2565 
   2566  // Create a new remote outer window proxy, and transplant to it.
   2567  JS::Rooted<JSObject*> remoteProxy(cx);
   2568 
   2569  if (!mozilla::dom::GetRemoteOuterWindowProxy(cx, bc, localProxy,
   2570                                               &remoteProxy)) {
   2571    MOZ_CRASH("PrepareForProcessChange GetRemoteOuterWindowProxy");
   2572  }
   2573 
   2574  if (!xpc::TransplantObjectNukingXrayWaiver(cx, localProxy, remoteProxy)) {
   2575    MOZ_CRASH("PrepareForProcessChange TransplantObject");
   2576  }
   2577 }
   2578 
   2579 void nsGlobalWindowOuter::PreloadLocalStorage() {
   2580  if (!Storage::StoragePrefIsEnabled()) {
   2581    return;
   2582  }
   2583 
   2584  if (IsChromeWindow()) {
   2585    return;
   2586  }
   2587 
   2588  nsIPrincipal* principal = GetPrincipal();
   2589  nsIPrincipal* storagePrincipal = GetEffectiveStoragePrincipal();
   2590  if (!principal || !storagePrincipal) {
   2591    return;
   2592  }
   2593 
   2594  nsresult rv;
   2595 
   2596  nsCOMPtr<nsIDOMStorageManager> storageManager =
   2597      do_GetService("@mozilla.org/dom/localStorage-manager;1", &rv);
   2598  if (NS_FAILED(rv)) {
   2599    return;
   2600  }
   2601 
   2602  // private browsing windows do not persist local storage to disk so we should
   2603  // only try to precache storage when we're not a private browsing window.
   2604  if (!principal->GetIsInPrivateBrowsing()) {
   2605    RefPtr<Storage> storage;
   2606    rv = storageManager->PrecacheStorage(principal, storagePrincipal,
   2607                                         getter_AddRefs(storage));
   2608    if (NS_SUCCEEDED(rv)) {
   2609      mLocalStorage = storage;
   2610    }
   2611  }
   2612 }
   2613 
   2614 void nsGlobalWindowOuter::DispatchDOMWindowCreated() {
   2615  if (!mDoc) {
   2616    return;
   2617  }
   2618 
   2619  // Fire DOMWindowCreated at chrome event listeners
   2620  nsContentUtils::DispatchChromeEvent(mDoc, mDoc, u"DOMWindowCreated"_ns,
   2621                                      CanBubble::eYes, Cancelable::eNo);
   2622 
   2623  nsCOMPtr<nsIObserverService> observerService =
   2624      mozilla::services::GetObserverService();
   2625 
   2626  // The event dispatching could possibly cause docshell destory, and
   2627  // consequently cause mDoc to be set to nullptr by DropOuterWindowDocs(),
   2628  // so check it again here.
   2629  if (observerService && mDoc) {
   2630    nsAutoString origin;
   2631    nsIPrincipal* principal = mDoc->NodePrincipal();
   2632    nsContentUtils::GetWebExposedOriginSerialization(principal, origin);
   2633    observerService->NotifyObservers(static_cast<nsIDOMWindow*>(this),
   2634                                     principal->IsSystemPrincipal()
   2635                                         ? "chrome-document-global-created"
   2636                                         : "content-document-global-created",
   2637                                     origin.get());
   2638  }
   2639 }
   2640 
   2641 void nsGlobalWindowOuter::ClearStatus() { SetStatusOuter(u""_ns); }
   2642 
   2643 void nsGlobalWindowOuter::SetDocShell(nsDocShell* aDocShell) {
   2644  MOZ_ASSERT(aDocShell);
   2645 
   2646  if (aDocShell == mDocShell) {
   2647    return;
   2648  }
   2649 
   2650  mDocShell = aDocShell;
   2651  mBrowsingContext = aDocShell->GetBrowsingContext();
   2652 
   2653  RefPtr<BrowsingContext> parentContext = mBrowsingContext->GetParent();
   2654 
   2655  MOZ_RELEASE_ASSERT(!parentContext ||
   2656                     GetBrowsingContextGroup() == parentContext->Group());
   2657 
   2658  mTopLevelOuterContentWindow = mBrowsingContext->IsTopContent();
   2659 
   2660  // Get our enclosing chrome shell and retrieve its global window impl, so
   2661  // that we can do some forwarding to the chrome document.
   2662  RefPtr<EventTarget> chromeEventHandler;
   2663  mDocShell->GetChromeEventHandler(getter_AddRefs(chromeEventHandler));
   2664  mChromeEventHandler = chromeEventHandler;
   2665  if (!mChromeEventHandler) {
   2666    // We have no chrome event handler. If we have a parent,
   2667    // get our chrome event handler from the parent. If
   2668    // we don't have a parent, then we need to make a new
   2669    // window root object that will function as a chrome event
   2670    // handler and receive all events that occur anywhere inside
   2671    // our window.
   2672    nsCOMPtr<nsPIDOMWindowOuter> parentWindow = GetInProcessParent();
   2673    if (parentWindow.get() != this) {
   2674      mChromeEventHandler = parentWindow->GetChromeEventHandler();
   2675    } else {
   2676      mChromeEventHandler = NS_NewWindowRoot(this);
   2677      mIsRootOuterWindow = true;
   2678    }
   2679  }
   2680 
   2681  SetIsBackgroundInternal(!mBrowsingContext->IsActive());
   2682 }
   2683 
   2684 void nsGlobalWindowOuter::DetachFromDocShell(bool aIsBeingDiscarded) {
   2685  // DetachFromDocShell means the window is being torn down. Drop our
   2686  // reference to the script context, allowing it to be deleted
   2687  // later. Meanwhile, keep our weak reference to the script object
   2688  // so that it can be retrieved later (until it is finalized by the JS GC).
   2689 
   2690  // Call FreeInnerObjects on all inner windows, not just the current
   2691  // one, since some could be held by WindowStateHolder objects that
   2692  // are GC-owned.
   2693  RefPtr<nsGlobalWindowInner> inner;
   2694  for (PRCList* node = PR_LIST_HEAD(this); node != this;
   2695       node = PR_NEXT_LINK(inner)) {
   2696    // This cast is safe because `node != this`. Non-this nodes are inner
   2697    // windows.
   2698    inner = static_cast<nsGlobalWindowInner*>(node);
   2699    MOZ_ASSERT(!inner->mOuterWindow || inner->mOuterWindow == this);
   2700    inner->FreeInnerObjects();
   2701  }
   2702 
   2703  // Don't report that we were detached to the nsWindowMemoryReporter, as it
   2704  // only tracks inner windows.
   2705 
   2706  NotifyWindowIDDestroyed("outer-window-destroyed");
   2707 
   2708  nsGlobalWindowInner* currentInner = GetCurrentInnerWindowInternal(this);
   2709 
   2710  if (currentInner) {
   2711    NS_ASSERTION(mDoc, "Must have doc!");
   2712 
   2713    // Remember the document's principal and URI.
   2714    mDocumentPrincipal = mDoc->NodePrincipal();
   2715    mDocumentCookiePrincipal = mDoc->EffectiveCookiePrincipal();
   2716    mDocumentStoragePrincipal = mDoc->EffectiveStoragePrincipal();
   2717    mDocumentPartitionedPrincipal = mDoc->PartitionedPrincipal();
   2718    mDocumentURI = mDoc->GetDocumentURI();
   2719 
   2720    // Release our document reference
   2721    DropOuterWindowDocs();
   2722  }
   2723 
   2724  ClearControllers();
   2725 
   2726  mChromeEventHandler = nullptr;  // force release now
   2727 
   2728  if (mContext) {
   2729    // When we're about to destroy a top level content window
   2730    // (for example a tab), we trigger a full GC by passing null as the last
   2731    // param. We also trigger a full GC for chrome windows.
   2732    nsJSContext::PokeGC(JS::GCReason::SET_DOC_SHELL,
   2733                        (mTopLevelOuterContentWindow || mIsChrome)
   2734                            ? nullptr
   2735                            : GetWrapperPreserveColor());
   2736    mContext = nullptr;
   2737  }
   2738 
   2739  if (aIsBeingDiscarded) {
   2740    // If our BrowsingContext is being discarded, make a note that our current
   2741    // inner window was active at the time it went away.
   2742    if (nsGlobalWindowInner* currentInner =
   2743            GetCurrentInnerWindowInternal(this)) {
   2744      currentInner->SetWasCurrentInnerWindow();
   2745    }
   2746  }
   2747 
   2748  mDocShell = nullptr;
   2749  mBrowsingContext->ClearDocShell();
   2750 
   2751  CleanUp();
   2752 }
   2753 
   2754 void nsGlobalWindowOuter::UpdateParentTarget() {
   2755  // NOTE: This method is nearly identical to
   2756  // nsGlobalWindowInner::UpdateParentTarget(). IF YOU UPDATE THIS METHOD,
   2757  // UPDATE THE OTHER ONE TOO!  The one difference is that this method updates
   2758  // mMessageManager as well, which inner windows don't have.
   2759 
   2760  // Try to get our frame element's tab child global (its in-process message
   2761  // manager).  If that fails, fall back to the chrome event handler's tab
   2762  // child global, and if it doesn't have one, just use the chrome event
   2763  // handler itself.
   2764 
   2765  nsCOMPtr<Element> frameElement = GetFrameElementInternal();
   2766  mMessageManager = nsContentUtils::TryGetBrowserChildGlobal(frameElement);
   2767 
   2768  if (!mMessageManager) {
   2769    nsGlobalWindowOuter* topWin = GetInProcessScriptableTopInternal();
   2770    if (topWin) {
   2771      frameElement = topWin->GetFrameElementInternal();
   2772      mMessageManager = nsContentUtils::TryGetBrowserChildGlobal(frameElement);
   2773    }
   2774  }
   2775 
   2776  if (!mMessageManager) {
   2777    mMessageManager =
   2778        nsContentUtils::TryGetBrowserChildGlobal(mChromeEventHandler);
   2779  }
   2780 
   2781  if (mMessageManager) {
   2782    mParentTarget = mMessageManager;
   2783  } else {
   2784    mParentTarget = mChromeEventHandler;
   2785  }
   2786 }
   2787 
   2788 EventTarget* nsGlobalWindowOuter::GetTargetForEventTargetChain() {
   2789  return GetCurrentInnerWindowInternal(this);
   2790 }
   2791 
   2792 void nsGlobalWindowOuter::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
   2793  MOZ_CRASH("The outer window should not be part of an event path");
   2794 }
   2795 
   2796 bool nsGlobalWindowOuter::ShouldPromptToBlockDialogs() {
   2797  if (!nsContentUtils::GetCurrentJSContext()) {
   2798    return false;  // non-scripted caller.
   2799  }
   2800 
   2801  BrowsingContextGroup* group = GetBrowsingContextGroup();
   2802  if (!group) {
   2803    return true;
   2804  }
   2805 
   2806  return group->DialogsAreBeingAbused();
   2807 }
   2808 
   2809 bool nsGlobalWindowOuter::AreDialogsEnabled() {
   2810  BrowsingContextGroup* group = mBrowsingContext->Group();
   2811  if (!group) {
   2812    NS_ERROR("AreDialogsEnabled() called without a browsing context group?");
   2813    return false;
   2814  }
   2815 
   2816  // Dialogs are blocked if the content viewer is hidden
   2817  if (mDocShell) {
   2818    nsCOMPtr<nsIDocumentViewer> viewer;
   2819    mDocShell->GetDocViewer(getter_AddRefs(viewer));
   2820 
   2821    bool isHidden;
   2822    viewer->GetIsHidden(&isHidden);
   2823    if (isHidden) {
   2824      return false;
   2825    }
   2826  }
   2827 
   2828  // Dialogs are also blocked if the document is sandboxed with SANDBOXED_MODALS
   2829  // (or if we have no document, of course).  Which document?  Who knows; the
   2830  // spec is daft.  See <https://github.com/whatwg/html/issues/1206>.  For now
   2831  // just go ahead and check mDoc, since in everything except edge cases in
   2832  // which a frame is allow-same-origin but not allow-scripts and is being poked
   2833  // at by some other window this should be the right thing anyway.
   2834  if (!mDoc || (mDoc->GetSandboxFlags() & SANDBOXED_MODALS)) {
   2835    return false;
   2836  }
   2837 
   2838  return group->GetAreDialogsEnabled();
   2839 }
   2840 
   2841 void nsGlobalWindowOuter::DisableDialogs() {
   2842  BrowsingContextGroup* group = mBrowsingContext->Group();
   2843  if (!group) {
   2844    NS_ERROR("DisableDialogs() called without a browsing context group?");
   2845    return;
   2846  }
   2847 
   2848  if (group) {
   2849    group->SetAreDialogsEnabled(false);
   2850  }
   2851 }
   2852 
   2853 void nsGlobalWindowOuter::EnableDialogs() {
   2854  BrowsingContextGroup* group = mBrowsingContext->Group();
   2855  if (!group) {
   2856    NS_ERROR("EnableDialogs() called without a browsing context group?");
   2857    return;
   2858  }
   2859 
   2860  if (group) {
   2861    group->SetAreDialogsEnabled(true);
   2862  }
   2863 }
   2864 
   2865 nsresult nsGlobalWindowOuter::PostHandleEvent(EventChainPostVisitor& aVisitor) {
   2866  MOZ_CRASH("The outer window should not be part of an event path");
   2867 }
   2868 
   2869 void nsGlobalWindowOuter::PoisonOuterWindowProxy(JSObject* aObject) {
   2870  if (aObject == GetWrapperMaybeDead()) {
   2871    PoisonWrapper();
   2872  }
   2873 }
   2874 
   2875 nsresult nsGlobalWindowOuter::SetArguments(nsIArray* aArguments) {
   2876  nsresult rv;
   2877 
   2878  // We've now mostly separated them, but the difference is still opaque to
   2879  // nsWindowWatcher (the caller of SetArguments in this little back-and-forth
   2880  // embedding waltz we do here).
   2881  //
   2882  // So we need to demultiplex the two cases here.
   2883  nsGlobalWindowInner* currentInner = GetCurrentInnerWindowInternal(this);
   2884 
   2885  mArguments = aArguments;
   2886  rv = currentInner->DefineArgumentsProperty(aArguments);
   2887  NS_ENSURE_SUCCESS(rv, rv);
   2888 
   2889  return NS_OK;
   2890 }
   2891 
   2892 //*****************************************************************************
   2893 // nsGlobalWindowOuter::nsIScriptObjectPrincipal
   2894 //*****************************************************************************
   2895 
   2896 nsIPrincipal* nsGlobalWindowOuter::GetPrincipal() {
   2897  if (mDoc) {
   2898    // If we have a document, get the principal from the document
   2899    return mDoc->NodePrincipal();
   2900  }
   2901 
   2902  if (mDocumentPrincipal) {
   2903    return mDocumentPrincipal;
   2904  }
   2905 
   2906  // If we don't have a principal and we don't have a document we
   2907  // ask the parent window for the principal. This can happen when
   2908  // loading a frameset that has a <frame src="javascript:xxx">, in
   2909  // that case the global window is used in JS before we've loaded
   2910  // a document into the window.
   2911 
   2912  nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
   2913      do_QueryInterface(GetInProcessParentInternal());
   2914 
   2915  if (objPrincipal) {
   2916    return objPrincipal->GetPrincipal();
   2917  }
   2918 
   2919  return nullptr;
   2920 }
   2921 
   2922 nsIPrincipal* nsGlobalWindowOuter::GetEffectiveCookiePrincipal() {
   2923  if (mDoc) {
   2924    // If we have a document, get the principal from the document
   2925    return mDoc->EffectiveCookiePrincipal();
   2926  }
   2927 
   2928  if (mDocumentCookiePrincipal) {
   2929    return mDocumentCookiePrincipal;
   2930  }
   2931 
   2932  // If we don't have a cookie principal and we don't have a document we ask
   2933  // the parent window for the cookie principal.
   2934 
   2935  nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
   2936      do_QueryInterface(GetInProcessParentInternal());
   2937 
   2938  if (objPrincipal) {
   2939    return objPrincipal->GetEffectiveCookiePrincipal();
   2940  }
   2941 
   2942  return nullptr;
   2943 }
   2944 
   2945 nsIPrincipal* nsGlobalWindowOuter::GetEffectiveStoragePrincipal() {
   2946  if (mDoc) {
   2947    // If we have a document, get the principal from the document
   2948    return mDoc->EffectiveStoragePrincipal();
   2949  }
   2950 
   2951  if (mDocumentStoragePrincipal) {
   2952    return mDocumentStoragePrincipal;
   2953  }
   2954 
   2955  // If we don't have a storage principal and we don't have a document we ask
   2956  // the parent window for the storage principal.
   2957 
   2958  nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
   2959      do_QueryInterface(GetInProcessParentInternal());
   2960 
   2961  if (objPrincipal) {
   2962    return objPrincipal->GetEffectiveStoragePrincipal();
   2963  }
   2964 
   2965  return nullptr;
   2966 }
   2967 
   2968 nsIPrincipal* nsGlobalWindowOuter::PartitionedPrincipal() {
   2969  if (mDoc) {
   2970    // If we have a document, get the principal from the document
   2971    return mDoc->PartitionedPrincipal();
   2972  }
   2973 
   2974  if (mDocumentPartitionedPrincipal) {
   2975    return mDocumentPartitionedPrincipal;
   2976  }
   2977 
   2978  // If we don't have a partitioned principal and we don't have a document we
   2979  // ask the parent window for the partitioned principal.
   2980 
   2981  nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
   2982      do_QueryInterface(GetInProcessParentInternal());
   2983 
   2984  if (objPrincipal) {
   2985    return objPrincipal->PartitionedPrincipal();
   2986  }
   2987 
   2988  return nullptr;
   2989 }
   2990 
   2991 //*****************************************************************************
   2992 // nsGlobalWindowOuter::nsIDOMWindow
   2993 //*****************************************************************************
   2994 
   2995 Element* nsPIDOMWindowOuter::GetFrameElementInternal() const {
   2996  return mFrameElement;
   2997 }
   2998 
   2999 void nsPIDOMWindowOuter::SetFrameElementInternal(Element* aFrameElement) {
   3000  mFrameElement = aFrameElement;
   3001 }
   3002 
   3003 Navigator* nsGlobalWindowOuter::GetNavigator() {
   3004  FORWARD_TO_INNER(Navigator, (), nullptr);
   3005 }
   3006 
   3007 nsScreen* nsGlobalWindowOuter::GetScreen() {
   3008  FORWARD_TO_INNER(Screen, (), nullptr);
   3009 }
   3010 
   3011 void nsPIDOMWindowOuter::ActivateMediaComponents() {
   3012  if (!ShouldDelayMediaFromStart()) {
   3013    return;
   3014  }
   3015  MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
   3016          ("nsPIDOMWindowOuter, ActiveMediaComponents, "
   3017           "no longer to delay media from start, this = %p\n",
   3018           this));
   3019  if (BrowsingContext* bc = GetBrowsingContext()) {
   3020    (void)bc->Top()->SetShouldDelayMediaFromStart(false);
   3021  }
   3022  NotifyResumingDelayedMedia();
   3023 }
   3024 
   3025 bool nsPIDOMWindowOuter::ShouldDelayMediaFromStart() const {
   3026  BrowsingContext* bc = GetBrowsingContext();
   3027  return bc && bc->Top()->GetShouldDelayMediaFromStart();
   3028 }
   3029 
   3030 void nsPIDOMWindowOuter::NotifyResumingDelayedMedia() {
   3031  RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
   3032  if (service) {
   3033    service->NotifyResumingDelayedMedia(this);
   3034  }
   3035 }
   3036 
   3037 bool nsPIDOMWindowOuter::GetAudioMuted() const {
   3038  BrowsingContext* bc = GetBrowsingContext();
   3039  return bc && bc->Top()->GetMuted();
   3040 }
   3041 
   3042 void nsPIDOMWindowOuter::RefreshMediaElementsVolume() {
   3043  RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
   3044  if (service) {
   3045    // TODO: RefreshAgentsVolume can probably be simplified further.
   3046    service->RefreshAgentsVolume(this, 1.0f, GetAudioMuted());
   3047  }
   3048 }
   3049 
   3050 mozilla::dom::BrowsingContextGroup*
   3051 nsPIDOMWindowOuter::GetBrowsingContextGroup() const {
   3052  return mBrowsingContext ? mBrowsingContext->Group() : nullptr;
   3053 }
   3054 
   3055 Nullable<WindowProxyHolder> nsGlobalWindowOuter::GetParentOuter() {
   3056  BrowsingContext* bc = GetBrowsingContext();
   3057  return bc ? bc->GetParent(IgnoreErrors()) : nullptr;
   3058 }
   3059 
   3060 /**
   3061 * GetInProcessScriptableParent used to be called when a script read
   3062 * window.parent. Under Fission, that is now handled by
   3063 * BrowsingContext::GetParent, and the result is a WindowProxyHolder rather than
   3064 * an actual global window. This method still exists for legacy callers which
   3065 * relied on the old logic, and require in-process windows. However, it only
   3066 * works correctly when no out-of-process frames exist between this window and
   3067 * the top-level window, so it should not be used in new code.
   3068 *
   3069 * In contrast to GetRealParent, GetInProcessScriptableParent respects <iframe
   3070 * mozbrowser> boundaries, so if |this| is contained by an <iframe
   3071 * mozbrowser>, we will return |this| as its own parent.
   3072 */
   3073 nsPIDOMWindowOuter* nsGlobalWindowOuter::GetInProcessScriptableParent() {
   3074  if (!mDocShell) {
   3075    return nullptr;
   3076  }
   3077 
   3078  if (BrowsingContext* parentBC = GetBrowsingContext()->GetParent()) {
   3079    if (nsCOMPtr<nsPIDOMWindowOuter> parent = parentBC->GetDOMWindow()) {
   3080      return parent;
   3081    }
   3082  }
   3083  return this;
   3084 }
   3085 
   3086 /**
   3087 * Behavies identically to GetInProcessScriptableParent extept that it returns
   3088 * null if GetInProcessScriptableParent would return this window.
   3089 */
   3090 nsPIDOMWindowOuter* nsGlobalWindowOuter::GetInProcessScriptableParentOrNull() {
   3091  nsPIDOMWindowOuter* parent = GetInProcessScriptableParent();
   3092  return (nsGlobalWindowOuter::Cast(parent) == this) ? nullptr : parent;
   3093 }
   3094 
   3095 already_AddRefed<nsPIDOMWindowOuter> nsGlobalWindowOuter::GetInProcessParent() {
   3096  if (!mDocShell) {
   3097    return nullptr;
   3098  }
   3099 
   3100  if (auto* parentBC = GetBrowsingContext()->GetParent()) {
   3101    if (auto* parent = parentBC->GetDOMWindow()) {
   3102      return do_AddRef(parent);
   3103    }
   3104  }
   3105  return do_AddRef(this);
   3106 }
   3107 
   3108 static nsresult GetTopImpl(nsGlobalWindowOuter* aWin, nsIURI* aURIBeingLoaded,
   3109                           nsPIDOMWindowOuter** aTop, bool aScriptable,
   3110                           bool aExcludingExtensionAccessibleContentFrames) {
   3111  *aTop = nullptr;
   3112 
   3113  MOZ_ASSERT_IF(aExcludingExtensionAccessibleContentFrames, !aScriptable);
   3114 
   3115  // Walk up the parent chain.
   3116 
   3117  nsCOMPtr<nsPIDOMWindowOuter> prevParent = aWin;
   3118  nsCOMPtr<nsPIDOMWindowOuter> parent = aWin;
   3119  do {
   3120    if (!parent) {
   3121      break;
   3122    }
   3123 
   3124    prevParent = parent;
   3125 
   3126    if (aScriptable) {
   3127      parent = parent->GetInProcessScriptableParent();
   3128    } else {
   3129      parent = parent->GetInProcessParent();
   3130    }
   3131 
   3132    if (aExcludingExtensionAccessibleContentFrames) {
   3133      if (auto* p = nsGlobalWindowOuter::Cast(parent)) {
   3134        nsGlobalWindowInner* currentInner = GetCurrentInnerWindowInternal(p);
   3135        nsIURI* uri = prevParent->GetDocumentURI();
   3136        if (!uri) {
   3137          // If our parent doesn't have a URI yet, we have a document that is in
   3138          // the process of being loaded.  In that case, our caller is
   3139          // responsible for passing in the URI for the document that is being
   3140          // loaded, so we fall back to using that URI here.
   3141          uri = aURIBeingLoaded;
   3142        }
   3143 
   3144        if (currentInner && uri) {
   3145          // If we find an inner window, we better find the uri for the current
   3146          // window we're looking at.  If we can't find it directly, it is the
   3147          // responsibility of our caller to provide it to us.
   3148          MOZ_DIAGNOSTIC_ASSERT(uri);
   3149 
   3150          // If the new parent has permission to load the current page, we're
   3151          // at a moz-extension:// frame which has a host permission that allows
   3152          // it to load the document that we've loaded.  In that case, stop at
   3153          // this frame and consider it the top-level frame.
   3154          //
   3155          // Note that it's possible for the set of URIs accepted by
   3156          // AddonAllowsLoad() to change at runtime, but we don't need to cache
   3157          // the result of this check, since the important consumer of this code
   3158          // (which is nsIHttpChannelInternal.topWindowURI) already caches the
   3159          // result after computing it the first time.
   3160          if (BasePrincipal::Cast(p->GetPrincipal())
   3161                  ->AddonAllowsLoad(uri, true)) {
   3162            parent = prevParent;
   3163            break;
   3164          }
   3165        }
   3166      }
   3167    }
   3168 
   3169  } while (parent != prevParent);
   3170 
   3171  if (parent) {
   3172    parent.swap(*aTop);
   3173  }
   3174 
   3175  return NS_OK;
   3176 }
   3177 
   3178 /**
   3179 * GetInProcessScriptableTop used to be called when a script read window.top.
   3180 * Under Fission, that is now handled by BrowsingContext::Top, and the result is
   3181 * a WindowProxyHolder rather than an actual global window. This method still
   3182 * exists for legacy callers which relied on the old logic, and require
   3183 * in-process windows. However, it only works correctly when no out-of-process
   3184 * frames exist between this window and the top-level window, so it should not
   3185 * be used in new code.
   3186 *
   3187 * In contrast to GetRealTop, GetInProcessScriptableTop respects <iframe
   3188 * mozbrowser> boundaries.  If we encounter a window owned by an <iframe
   3189 * mozbrowser> while walking up the window hierarchy, we'll stop and return that
   3190 * window.
   3191 */
   3192 nsPIDOMWindowOuter* nsGlobalWindowOuter::GetInProcessScriptableTop() {
   3193  nsCOMPtr<nsPIDOMWindowOuter> window;
   3194  GetTopImpl(this, /* aURIBeingLoaded = */ nullptr, getter_AddRefs(window),
   3195             /* aScriptable = */ true,
   3196             /* aExcludingExtensionAccessibleContentFrames = */ false);
   3197  return window.get();
   3198 }
   3199 
   3200 already_AddRefed<nsPIDOMWindowOuter> nsGlobalWindowOuter::GetInProcessTop() {
   3201  nsCOMPtr<nsPIDOMWindowOuter> window;
   3202  GetTopImpl(this, /* aURIBeingLoaded = */ nullptr, getter_AddRefs(window),
   3203             /* aScriptable = */ false,
   3204             /* aExcludingExtensionAccessibleContentFrames = */ false);
   3205  return window.forget();
   3206 }
   3207 
   3208 already_AddRefed<nsPIDOMWindowOuter>
   3209 nsGlobalWindowOuter::GetTopExcludingExtensionAccessibleContentFrames(
   3210    nsIURI* aURIBeingLoaded) {
   3211  // There is a parent-process equivalent of this in DocumentLoadListener.cpp
   3212  // GetTopWindowExcludingExtensionAccessibleContentFrames
   3213  nsCOMPtr<nsPIDOMWindowOuter> window;
   3214  GetTopImpl(this, aURIBeingLoaded, getter_AddRefs(window),
   3215             /* aScriptable = */ false,
   3216             /* aExcludingExtensionAccessibleContentFrames = */ true);
   3217  return window.forget();
   3218 }
   3219 
   3220 void nsGlobalWindowOuter::GetContentOuter(JSContext* aCx,
   3221                                          JS::MutableHandle<JSObject*> aRetval,
   3222                                          CallerType aCallerType,
   3223                                          ErrorResult& aError) {
   3224  RefPtr<BrowsingContext> content = GetContentInternal(aCallerType, aError);
   3225  if (aError.Failed()) {
   3226    return;
   3227  }
   3228 
   3229  if (!content) {
   3230    aRetval.set(nullptr);
   3231    return;
   3232  }
   3233 
   3234  JS::Rooted<JS::Value> val(aCx);
   3235  if (!ToJSValue(aCx, WindowProxyHolder{content}, &val)) {
   3236    aError.Throw(NS_ERROR_UNEXPECTED);
   3237    return;
   3238  }
   3239 
   3240  MOZ_ASSERT(val.isObjectOrNull());
   3241  aRetval.set(val.toObjectOrNull());
   3242 }
   3243 
   3244 already_AddRefed<BrowsingContext> nsGlobalWindowOuter::GetContentInternal(
   3245    CallerType aCallerType, ErrorResult& aError) {
   3246  // First check for a named frame named "content"
   3247  if (RefPtr<BrowsingContext> named = GetChildWindow(u"content"_ns)) {
   3248    return named.forget();
   3249  }
   3250 
   3251  // If we're in the parent process, and being called by system code, `content`
   3252  // should return the current primary content frame (if it's in-process).
   3253  //
   3254  // We return `nullptr` if the current primary content frame is out-of-process,
   3255  // rather than a remote window proxy, as that is the existing behaviour as of
   3256  // bug 1597437.
   3257  if (XRE_IsParentProcess() && aCallerType == CallerType::System) {
   3258    nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner();
   3259    if (!treeOwner) {
   3260      aError.Throw(NS_ERROR_FAILURE);
   3261      return nullptr;
   3262    }
   3263 
   3264    nsCOMPtr<nsIDocShellTreeItem> primaryContent;
   3265    treeOwner->GetPrimaryContentShell(getter_AddRefs(primaryContent));
   3266    if (!primaryContent) {
   3267      return nullptr;
   3268    }
   3269 
   3270    return do_AddRef(primaryContent->GetBrowsingContext());
   3271  }
   3272 
   3273  // For legacy untrusted callers we always return the same value as
   3274  // `window.top`
   3275  if (mDoc && aCallerType != CallerType::System) {
   3276    mDoc->WarnOnceAbout(DeprecatedOperations::eWindowContentUntrusted);
   3277  }
   3278 
   3279  MOZ_ASSERT(mBrowsingContext->IsContent());
   3280  return do_AddRef(mBrowsingContext->Top());
   3281 }
   3282 
   3283 nsresult nsGlobalWindowOuter::GetPrompter(nsIPrompt** aPrompt) {
   3284  if (!mDocShell) return NS_ERROR_FAILURE;
   3285 
   3286  nsCOMPtr<nsIPrompt> prompter(do_GetInterface(mDocShell));
   3287  NS_ENSURE_TRUE(prompter, NS_ERROR_NO_INTERFACE);
   3288 
   3289  prompter.forget(aPrompt);
   3290  return NS_OK;
   3291 }
   3292 
   3293 bool nsGlobalWindowOuter::GetClosedOuter() {
   3294  // If someone called close(), or if we don't have a docshell, we're closed.
   3295  return mIsClosed || !mDocShell;
   3296 }
   3297 
   3298 bool nsGlobalWindowOuter::Closed() { return GetClosedOuter(); }
   3299 
   3300 Nullable<WindowProxyHolder> nsGlobalWindowOuter::IndexedGetterOuter(
   3301    uint32_t aIndex) {
   3302  BrowsingContext* bc = GetBrowsingContext();
   3303  NS_ENSURE_TRUE(bc, nullptr);
   3304 
   3305  BrowsingContext* child = bc->NonSyntheticLightDOMChildAt(aIndex);
   3306 
   3307  if (child) {
   3308    return WindowProxyHolder(child);
   3309  }
   3310  return nullptr;
   3311 }
   3312 
   3313 nsIControllers* nsGlobalWindowOuter::GetControllersOuter(ErrorResult& aError) {
   3314  if (!mControllers) {
   3315    mControllers = new nsXULControllers();
   3316    if (!mControllers) {
   3317      aError.Throw(NS_ERROR_FAILURE);
   3318      return nullptr;
   3319    }
   3320 
   3321    // Add in the default controller
   3322    RefPtr<nsBaseCommandController> commandController =
   3323        nsBaseCommandController::CreateWindowController();
   3324    if (!commandController) {
   3325      aError.Throw(NS_ERROR_FAILURE);
   3326      return nullptr;
   3327    }
   3328 
   3329    mControllers->InsertControllerAt(0, commandController);
   3330    commandController->SetContext(this);
   3331  }
   3332 
   3333  return mControllers;
   3334 }
   3335 
   3336 nsresult nsGlobalWindowOuter::GetControllers(nsIControllers** aResult) {
   3337  FORWARD_TO_INNER(GetControllers, (aResult), NS_ERROR_UNEXPECTED);
   3338 }
   3339 
   3340 already_AddRefed<BrowsingContext>
   3341 nsGlobalWindowOuter::GetOpenerBrowsingContext() {
   3342  RefPtr<BrowsingContext> opener = GetBrowsingContext()->GetOpener();
   3343  MOZ_DIAGNOSTIC_ASSERT(!opener ||
   3344                        opener->Group() == GetBrowsingContext()->Group());
   3345  if (!opener || opener->Group() != GetBrowsingContext()->Group()) {
   3346    return nullptr;
   3347  }
   3348 
   3349  // Catch the case where we're chrome but the opener is not...
   3350  if (nsContentUtils::LegacyIsCallerChromeOrNativeCode() &&
   3351      GetPrincipal() == nsContentUtils::GetSystemPrincipal()) {
   3352    auto* openerWin = nsGlobalWindowOuter::Cast(opener->GetDOMWindow());
   3353    if (!openerWin ||
   3354        openerWin->GetPrincipal() != nsContentUtils::GetSystemPrincipal()) {
   3355      return nullptr;
   3356    }
   3357  }
   3358 
   3359  return opener.forget();
   3360 }
   3361 
   3362 nsPIDOMWindowOuter* nsGlobalWindowOuter::GetSameProcessOpener() {
   3363  if (RefPtr<BrowsingContext> opener = GetOpenerBrowsingContext()) {
   3364    return opener->GetDOMWindow();
   3365  }
   3366  return nullptr;
   3367 }
   3368 
   3369 Nullable<WindowProxyHolder> nsGlobalWindowOuter::GetOpenerWindowOuter() {
   3370  if (RefPtr<BrowsingContext> opener = GetOpenerBrowsingContext()) {
   3371    return WindowProxyHolder(std::move(opener));
   3372  }
   3373  return nullptr;
   3374 }
   3375 
   3376 Nullable<WindowProxyHolder> nsGlobalWindowOuter::GetOpener() {
   3377  return GetOpenerWindowOuter();
   3378 }
   3379 
   3380 void nsGlobalWindowOuter::GetStatusOuter(nsAString& aStatus) {
   3381  aStatus = mStatus;
   3382 }
   3383 
   3384 void nsGlobalWindowOuter::SetStatusOuter(const nsAString& aStatus) {
   3385  mStatus = aStatus;
   3386 
   3387  // We don't support displaying window.status in the UI, so there's nothing
   3388  // left to do here.
   3389 }
   3390 
   3391 void nsGlobalWindowOuter::GetNameOuter(nsAString& aName) {
   3392  if (mDocShell) {
   3393    mDocShell->GetName(aName);
   3394  }
   3395 }
   3396 
   3397 void nsGlobalWindowOuter::SetNameOuter(const nsAString& aName,
   3398                                       mozilla::ErrorResult& aError) {
   3399  if (mDocShell) {
   3400    aError = mDocShell->SetName(aName);
   3401  }
   3402 }
   3403 
   3404 // NOTE: The idea of this function is that it should return the same as
   3405 // nsPresContext::CSSToDeviceScale() if it was in aWindow synchronously. For
   3406 // that, we use the UnscaledDevicePixelsPerCSSPixel() (which contains the device
   3407 // scale and the OS zoom scale) and then account for the browsing context full
   3408 // zoom. See the declaration of this function for context about why this is
   3409 // needed.
   3410 CSSToLayoutDeviceScale nsGlobalWindowOuter::CSSToDevScaleForBaseWindow(
   3411    nsIBaseWindow* aWindow) {
   3412  MOZ_ASSERT(aWindow);
   3413  auto scale = aWindow->UnscaledDevicePixelsPerCSSPixel();
   3414  if (mBrowsingContext) {
   3415    scale.scale *= mBrowsingContext->FullZoom();
   3416  }
   3417  return scale;
   3418 }
   3419 
   3420 nsresult nsGlobalWindowOuter::GetInnerSize(CSSSize& aSize) {
   3421  if (mDoc && mDoc->IsTopLevelContentDocument() &&
   3422      nsLayoutUtils::ShouldHandleMetaViewport(mDoc)) {
   3423    // Window.inner{Width,Height} depend on minimum-scale size and to get the
   3424    // up-to-date minimum-scale size we need to flush layout.
   3425    FlushPendingNotifications(FlushType::Layout);
   3426  } else {
   3427    EnsureSizeAndPositionUpToDate();
   3428  }
   3429 
   3430  NS_ENSURE_STATE(mDocShell);
   3431 
   3432  RefPtr<PresShell> presShell = mDocShell->GetPresShell();
   3433  if (!presShell) {
   3434    aSize = {};
   3435    return NS_OK;
   3436  }
   3437 
   3438  // Whether or not the css viewport has been overridden, we can get the
   3439  // correct value by looking at the visible area of the presContext.
   3440  presShell->FlushDelayedResize();
   3441 
   3442  nsPresContext* pc = presShell->GetPresContext();
   3443  if (NS_WARN_IF(!pc)) {
   3444    aSize = {};
   3445    return NS_OK;
   3446  }
   3447 
   3448  nsSize innerSize = presShell->GetInnerSize();
   3449  if (pc->GetDynamicToolbarState() == DynamicToolbarState::Collapsed) {
   3450    innerSize = nsLayoutUtils::ExpandHeightForViewportUnits(pc, innerSize);
   3451  }
   3452 
   3453  aSize = CSSPixel::FromAppUnits(innerSize);
   3454 
   3455  switch (StaticPrefs::dom_innerSize_rounding()) {
   3456    case 1:
   3457      aSize.width = std::roundf(aSize.width);
   3458      aSize.height = std::roundf(aSize.height);
   3459      break;
   3460    case 2:
   3461      aSize.width = std::truncf(aSize.width);
   3462      aSize.height = std::truncf(aSize.height);
   3463      break;
   3464    default:
   3465      break;
   3466  }
   3467 
   3468  return NS_OK;
   3469 }
   3470 
   3471 double nsGlobalWindowOuter::GetInnerWidthOuter(ErrorResult& aError) {
   3472  CSSSize size;
   3473  aError = GetInnerSize(size);
   3474  return size.width;
   3475 }
   3476 
   3477 nsresult nsGlobalWindowOuter::GetInnerWidth(double* aInnerWidth) {
   3478  FORWARD_TO_INNER_WITH_STRONG_REF(GetInnerWidth, (aInnerWidth),
   3479                                   NS_ERROR_UNEXPECTED);
   3480 }
   3481 
   3482 double nsGlobalWindowOuter::GetInnerHeightOuter(ErrorResult& aError) {
   3483  CSSSize size;
   3484  aError = GetInnerSize(size);
   3485  return size.height;
   3486 }
   3487 
   3488 nsresult nsGlobalWindowOuter::GetInnerHeight(double* aInnerHeight) {
   3489  FORWARD_TO_INNER_WITH_STRONG_REF(GetInnerHeight, (aInnerHeight),
   3490                                   NS_ERROR_UNEXPECTED);
   3491 }
   3492 
   3493 CSSIntSize nsGlobalWindowOuter::GetOuterSize(CallerType aCallerType,
   3494                                             ErrorResult& aError) {
   3495  if (nsIGlobalObject::ShouldResistFingerprinting(aCallerType,
   3496                                                  RFPTarget::WindowOuterSize)) {
   3497    if (BrowsingContext* bc = GetBrowsingContext()) {
   3498      return bc->Top()->GetTopInnerSizeForRFP();
   3499    }
   3500    return {};
   3501  }
   3502 
   3503  // Windows showing documents in RDM panes and any subframes within them
   3504  // return the simulated device size.
   3505  if (mDoc) {
   3506    Maybe<CSSIntSize> deviceSize = GetRDMDeviceSize(*mDoc);
   3507    if (deviceSize.isSome()) {
   3508      return *deviceSize;
   3509    }
   3510  }
   3511 
   3512  nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
   3513  if (!treeOwnerAsWin) {
   3514    aError.Throw(NS_ERROR_FAILURE);
   3515    return {};
   3516  }
   3517 
   3518  return RoundedToInt(treeOwnerAsWin->GetSize() /
   3519                      CSSToDevScaleForBaseWindow(treeOwnerAsWin));
   3520 }
   3521 
   3522 int32_t nsGlobalWindowOuter::GetOuterWidthOuter(CallerType aCallerType,
   3523                                                ErrorResult& aError) {
   3524  return GetOuterSize(aCallerType, aError).width;
   3525 }
   3526 
   3527 int32_t nsGlobalWindowOuter::GetOuterHeightOuter(CallerType aCallerType,
   3528                                                 ErrorResult& aError) {
   3529  return GetOuterSize(aCallerType, aError).height;
   3530 }
   3531 
   3532 CSSPoint nsGlobalWindowOuter::ScreenEdgeSlop() {
   3533  if (NS_WARN_IF(!mDocShell)) {
   3534    return {};
   3535  }
   3536  RefPtr<nsPresContext> pc = mDocShell->GetPresContext();
   3537  if (NS_WARN_IF(!pc)) {
   3538    return {};
   3539  }
   3540  nsCOMPtr<nsIWidget> widget = GetMainWidget();
   3541  if (NS_WARN_IF(!widget)) {
   3542    return {};
   3543  }
   3544  LayoutDeviceIntPoint pt = widget->GetScreenEdgeSlop();
   3545  auto auPoint =
   3546      LayoutDeviceIntPoint::ToAppUnits(pt, pc->AppUnitsPerDevPixel());
   3547  return CSSPoint::FromAppUnits(auPoint);
   3548 }
   3549 
   3550 CSSIntPoint nsGlobalWindowOuter::GetScreenXY(CallerType aCallerType,
   3551                                             ErrorResult& aError) {
   3552  // When resisting fingerprinting, always return (0,0)
   3553  if (nsIGlobalObject::ShouldResistFingerprinting(aCallerType,
   3554                                                  RFPTarget::WindowScreenXY)) {
   3555    return CSSIntPoint(0, 0);
   3556  }
   3557 
   3558  nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
   3559  if (!treeOwnerAsWin) {
   3560    aError.Throw(NS_ERROR_FAILURE);
   3561    return CSSIntPoint(0, 0);
   3562  }
   3563 
   3564  LayoutDeviceIntPoint windowPos;
   3565  aError = treeOwnerAsWin->GetPosition(&windowPos.x.value, &windowPos.y.value);
   3566 
   3567  RefPtr<nsPresContext> presContext = mDocShell->GetPresContext();
   3568  if (!presContext) {
   3569    // XXX Fishy LayoutDevice to CSS conversion?
   3570    return CSSIntPoint(windowPos.x, windowPos.y);
   3571  }
   3572 
   3573  nsDeviceContext* context = presContext->DeviceContext();
   3574  auto windowPosAppUnits = LayoutDeviceIntPoint::ToAppUnits(
   3575      windowPos, context->AppUnitsPerDevPixel());
   3576  return CSSIntPoint::FromAppUnitsRounded(windowPosAppUnits);
   3577 }
   3578 
   3579 int32_t nsGlobalWindowOuter::GetScreenXOuter(CallerType aCallerType,
   3580                                             ErrorResult& aError) {
   3581  return GetScreenXY(aCallerType, aError).x;
   3582 }
   3583 
   3584 nsRect nsGlobalWindowOuter::GetInnerScreenRect() {
   3585  if (!mDocShell) {
   3586    return nsRect();
   3587  }
   3588 
   3589  EnsureSizeAndPositionUpToDate();
   3590 
   3591  if (!mDocShell) {
   3592    return nsRect();
   3593  }
   3594 
   3595  PresShell* presShell = mDocShell->GetPresShell();
   3596  if (!presShell) {
   3597    return nsRect();
   3598  }
   3599  nsIFrame* rootFrame = presShell->GetRootFrame();
   3600  if (!rootFrame) {
   3601    return nsRect();
   3602  }
   3603 
   3604  return rootFrame->GetScreenRectInAppUnits();
   3605 }
   3606 
   3607 Maybe<CSSIntSize> nsGlobalWindowOuter::GetRDMDeviceSize(
   3608    const Document& aDocument) {
   3609  // RDM device size should reflect the simulated device resolution, and
   3610  // be independent of any full zoom or resolution zoom applied to the
   3611  // content. To get this value, we get the "unscaled" browser child size,
   3612  // and divide by the full zoom. "Unscaled" in this case means unscaled
   3613  // from device to screen but it has been affected (multiplied) by the
   3614  // full zoom and we need to compensate for that.
   3615  MOZ_RELEASE_ASSERT(NS_IsMainThread());
   3616 
   3617  // Bug 1576256: This does not work for cross-process subframes.
   3618  const Document* topInProcessContentDoc =
   3619      aDocument.GetTopLevelContentDocumentIfSameProcess();
   3620  BrowsingContext* bc = topInProcessContentDoc
   3621                            ? topInProcessContentDoc->GetBrowsingContext()
   3622                            : nullptr;
   3623  if (bc && bc->InRDMPane()) {
   3624    nsIDocShell* docShell = topInProcessContentDoc->GetDocShell();
   3625    if (docShell) {
   3626      nsPresContext* presContext = docShell->GetPresContext();
   3627      if (presContext) {
   3628        nsCOMPtr<nsIBrowserChild> child = docShell->GetBrowserChild();
   3629        if (child) {
   3630          // We intentionally use GetFullZoom here instead of
   3631          // GetDeviceFullZoom, because the unscaledInnerSize is based
   3632          // on the full zoom and not the device full zoom (which is
   3633          // rounded to result in integer device pixels).
   3634          float zoom = presContext->GetFullZoom();
   3635          BrowserChild* bc = static_cast<BrowserChild*>(child.get());
   3636          CSSSize unscaledSize = bc->GetUnscaledInnerSize();
   3637          return Some(CSSIntSize(gfx::RoundedToInt(unscaledSize / zoom)));
   3638        }
   3639      }
   3640    }
   3641  }
   3642  return Nothing();
   3643 }
   3644 
   3645 float nsGlobalWindowOuter::GetMozInnerScreenXOuter(CallerType aCallerType) {
   3646  // When resisting fingerprinting, always return 0.
   3647  if (nsIGlobalObject::ShouldResistFingerprinting(
   3648          aCallerType, RFPTarget::WindowInnerScreenXY)) {
   3649    return 0.0;
   3650  }
   3651 
   3652  nsRect r = GetInnerScreenRect();
   3653  return nsPresContext::AppUnitsToFloatCSSPixels(r.x);
   3654 }
   3655 
   3656 float nsGlobalWindowOuter::GetMozInnerScreenYOuter(CallerType aCallerType) {
   3657  // Return 0 to prevent fingerprinting.
   3658  if (nsIGlobalObject::ShouldResistFingerprinting(
   3659          aCallerType, RFPTarget::WindowInnerScreenXY)) {
   3660    return 0.0;
   3661  }
   3662 
   3663  nsRect r = GetInnerScreenRect();
   3664  return nsPresContext::AppUnitsToFloatCSSPixels(r.y);
   3665 }
   3666 
   3667 int32_t nsGlobalWindowOuter::GetScreenYOuter(CallerType aCallerType,
   3668                                             ErrorResult& aError) {
   3669  return GetScreenXY(aCallerType, aError).y;
   3670 }
   3671 
   3672 // NOTE: Arguments to this function should have values scaled to
   3673 // CSS pixels, not device pixels.
   3674 void nsGlobalWindowOuter::CheckSecurityWidthAndHeight(int32_t* aWidth,
   3675                                                      int32_t* aHeight,
   3676                                                      CallerType aCallerType) {
   3677  if (aCallerType != CallerType::System) {
   3678    // if attempting to resize the window, hide any open popups
   3679    nsContentUtils::HidePopupsInDocument(mDoc);
   3680  }
   3681 
   3682  // This one is easy. Just ensure the variable is greater than 100;
   3683  if ((aWidth && *aWidth < 100) || (aHeight && *aHeight < 100)) {
   3684    // Check security state for use in determing window dimensions
   3685 
   3686    if (aCallerType != CallerType::System) {
   3687      // sec check failed
   3688      if (aWidth && *aWidth < 100) {
   3689        *aWidth = 100;
   3690      }
   3691      if (aHeight && *aHeight < 100) {
   3692        *aHeight = 100;
   3693      }
   3694    }
   3695  }
   3696 }
   3697 
   3698 // NOTE: Arguments to this function should have values in app units
   3699 void nsGlobalWindowOuter::SetCSSViewportWidthAndHeight(nscoord aInnerWidth,
   3700                                                       nscoord aInnerHeight) {
   3701  RefPtr<nsPresContext> presContext = mDocShell->GetPresContext();
   3702 
   3703  nsRect shellArea = presContext->GetVisibleArea();
   3704  shellArea.SetHeight(aInnerHeight);
   3705  shellArea.SetWidth(aInnerWidth);
   3706 
   3707  // FIXME(emilio): This doesn't seem to be ok, this doesn't reflow or
   3708  // anything... Should go through PresShell::ResizeReflow.
   3709  //
   3710  // But I don't think this can be reached by content, as we don't allow to set
   3711  // inner{Width,Height}.
   3712  presContext->SetVisibleArea(shellArea);
   3713 }
   3714 
   3715 // NOTE: Arguments to this function should have values scaled to
   3716 // CSS pixels, not device pixels.
   3717 void nsGlobalWindowOuter::CheckSecurityLeftAndTop(int32_t* aLeft, int32_t* aTop,
   3718                                                  CallerType aCallerType) {
   3719  // This one is harder. We have to get the screen size and window dimensions.
   3720 
   3721  // Check security state for use in determing window dimensions
   3722 
   3723  if (aCallerType != CallerType::System) {
   3724    // if attempting to move the window, hide any open popups
   3725    nsContentUtils::HidePopupsInDocument(mDoc);
   3726 
   3727    if (nsGlobalWindowOuter* rootWindow =
   3728            nsGlobalWindowOuter::Cast(GetPrivateRoot())) {
   3729      rootWindow->FlushPendingNotifications(FlushType::Layout);
   3730    }
   3731 
   3732    nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
   3733 
   3734    RefPtr<nsScreen> screen = GetScreen();
   3735 
   3736    if (treeOwnerAsWin && screen) {
   3737      CSSToLayoutDeviceScale scale = CSSToDevScaleForBaseWindow(treeOwnerAsWin);
   3738      CSSIntRect winRect =
   3739          CSSIntRect::Round(treeOwnerAsWin->GetPositionAndSize() / scale);
   3740 
   3741      // Get the screen dimensions
   3742      // XXX This should use nsIScreenManager once it's fully fleshed out.
   3743      int32_t screenLeft = screen->AvailLeft();
   3744      int32_t screenWidth = screen->AvailWidth();
   3745      int32_t screenHeight = screen->AvailHeight();
   3746 #if defined(XP_MACOSX)
   3747      /* The mac's coordinate system is different from the assumed Windows'
   3748         system. It offsets by the height of the menubar so that a window
   3749         placed at (0,0) will be entirely visible. Unfortunately that
   3750         correction is made elsewhere (in Widget) and the meaning of
   3751         the Avail... coordinates is overloaded. Here we allow a window
   3752         to be placed at (0,0) because it does make sense to do so.
   3753      */
   3754      int32_t screenTop = screen->Top();
   3755 #else
   3756      int32_t screenTop = screen->AvailTop();
   3757 #endif
   3758 
   3759      if (aLeft) {
   3760        if (screenLeft + screenWidth < *aLeft + winRect.width)
   3761          *aLeft = screenLeft + screenWidth - winRect.width;
   3762        if (screenLeft > *aLeft) *aLeft = screenLeft;
   3763      }
   3764      if (aTop) {
   3765        if (screenTop + screenHeight < *aTop + winRect.height)
   3766          *aTop = screenTop + screenHeight - winRect.height;
   3767        if (screenTop > *aTop) *aTop = screenTop;
   3768      }
   3769    } else {
   3770      if (aLeft) *aLeft = 0;
   3771      if (aTop) *aTop = 0;
   3772    }
   3773  }
   3774 }
   3775 
   3776 int32_t nsGlobalWindowOuter::GetScrollBoundaryOuter(Side aSide) {
   3777  FlushPendingNotifications(FlushType::Layout);
   3778  if (ScrollContainerFrame* sf = GetScrollContainerFrame()) {
   3779    return nsPresContext::AppUnitsToIntCSSPixels(
   3780        sf->GetScrollRange().Edge(aSide));
   3781  }
   3782  return 0;
   3783 }
   3784 
   3785 CSSPoint nsGlobalWindowOuter::GetScrollXY(bool aDoFlush) {
   3786  if (aDoFlush) {
   3787    FlushPendingNotifications(FlushType::Layout);
   3788  } else {
   3789    EnsureSizeAndPositionUpToDate();
   3790  }
   3791 
   3792  ScrollContainerFrame* sf = GetScrollContainerFrame();
   3793  if (!sf) {
   3794    return CSSIntPoint(0, 0);
   3795  }
   3796 
   3797  nsPoint scrollPos = sf->GetScrollPosition();
   3798  if (scrollPos != nsPoint(0, 0) && !aDoFlush) {
   3799    // Oh, well.  This is the expensive case -- the window is scrolled and we
   3800    // didn't actually flush yet.  Repeat, but with a flush, since the content
   3801    // may get shorter and hence our scroll position may decrease.
   3802    return GetScrollXY(true);
   3803  }
   3804 
   3805  return CSSPoint::FromAppUnits(scrollPos);
   3806 }
   3807 
   3808 double nsGlobalWindowOuter::GetScrollXOuter() { return GetScrollXY(false).x; }
   3809 
   3810 double nsGlobalWindowOuter::GetScrollYOuter() { return GetScrollXY(false).y; }
   3811 
   3812 uint32_t nsGlobalWindowOuter::Length() {
   3813  BrowsingContext* bc = GetBrowsingContext();
   3814  NS_ENSURE_TRUE(bc, 0);
   3815  return bc->NonSyntheticLightDOMChildrenCount();
   3816 }
   3817 
   3818 Nullable<WindowProxyHolder> nsGlobalWindowOuter::GetTopOuter() {
   3819  BrowsingContext* bc = GetBrowsingContext();
   3820  return bc ? bc->GetTop(IgnoreErrors()) : nullptr;
   3821 }
   3822 
   3823 already_AddRefed<BrowsingContext> nsGlobalWindowOuter::GetChildWindow(
   3824    const nsAString& aName) {
   3825  NS_ENSURE_TRUE(mBrowsingContext, nullptr);
   3826  NS_ENSURE_TRUE(mInnerWindow, nullptr);
   3827  NS_ENSURE_TRUE(mInnerWindow->GetWindowGlobalChild(), nullptr);
   3828 
   3829  return do_AddRef(mBrowsingContext->FindChildWithName(
   3830      aName, *mInnerWindow->GetWindowGlobalChild()));
   3831 }
   3832 
   3833 bool nsGlobalWindowOuter::DispatchCustomEvent(
   3834    const nsAString& aEventName, ChromeOnlyDispatch aChromeOnlyDispatch) {
   3835  bool defaultActionEnabled = true;
   3836 
   3837  if (aChromeOnlyDispatch == ChromeOnlyDispatch::eYes) {
   3838    nsContentUtils::DispatchEventOnlyToChrome(mDoc, this, aEventName,
   3839                                              CanBubble::eYes, Cancelable::eYes,
   3840                                              &defaultActionEnabled);
   3841  } else {
   3842    nsContentUtils::DispatchTrustedEvent(mDoc, this, aEventName,
   3843                                         CanBubble::eYes, Cancelable::eYes,
   3844                                         &defaultActionEnabled);
   3845  }
   3846 
   3847  return defaultActionEnabled;
   3848 }
   3849 
   3850 bool nsGlobalWindowOuter::WindowExists(const nsAString& aName,
   3851                                       bool aForceNoOpener,
   3852                                       bool aLookForCallerOnJSStack) {
   3853  MOZ_ASSERT(mDocShell, "Must have docshell");
   3854 
   3855  if (aForceNoOpener) {
   3856    return aName.LowerCaseEqualsLiteral("_self") ||
   3857           aName.LowerCaseEqualsLiteral("_top") ||
   3858           aName.LowerCaseEqualsLiteral("_parent");
   3859  }
   3860 
   3861  if (WindowGlobalChild* wgc = mInnerWindow->GetWindowGlobalChild()) {
   3862    return wgc->FindBrowsingContextWithName(aName, aLookForCallerOnJSStack);
   3863  }
   3864  return false;
   3865 }
   3866 
   3867 already_AddRefed<nsIWidget> nsGlobalWindowOuter::GetMainWidget() {
   3868  nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
   3869 
   3870  nsCOMPtr<nsIWidget> widget;
   3871 
   3872  if (treeOwnerAsWin) {
   3873    treeOwnerAsWin->GetMainWidget(getter_AddRefs(widget));
   3874  }
   3875 
   3876  return widget.forget();
   3877 }
   3878 
   3879 nsIWidget* nsGlobalWindowOuter::GetNearestWidget() const {
   3880  nsIDocShell* docShell = GetDocShell();
   3881  if (!docShell) {
   3882    return nullptr;
   3883  }
   3884  PresShell* presShell = docShell->GetPresShell();
   3885  if (!presShell) {
   3886    return nullptr;
   3887  }
   3888  nsIFrame* rootFrame = presShell->GetRootFrame();
   3889  if (!rootFrame) {
   3890    return nullptr;
   3891  }
   3892  return rootFrame->GetNearestWidget();
   3893 }
   3894 
   3895 void nsGlobalWindowOuter::SetFullscreenOuter(bool aFullscreen,
   3896                                             mozilla::ErrorResult& aError) {
   3897  aError =
   3898      SetFullscreenInternal(FullscreenReason::ForFullscreenMode, aFullscreen);
   3899 }
   3900 
   3901 nsresult nsGlobalWindowOuter::SetFullScreen(bool aFullscreen) {
   3902  return SetFullscreenInternal(FullscreenReason::ForFullscreenMode,
   3903                               aFullscreen);
   3904 }
   3905 
   3906 static void FinishDOMFullscreenChange(Document* aDoc, bool aInDOMFullscreen) {
   3907  if (aInDOMFullscreen) {
   3908    // Ask the document to handle any pending DOM fullscreen change.
   3909    if (!Document::HandlePendingFullscreenRequests(aDoc)) {
   3910      // If we don't end up having anything in fullscreen,
   3911      // async request exiting fullscreen.
   3912      Document::AsyncExitFullscreen(aDoc);
   3913    }
   3914  } else {
   3915    // If the window is leaving fullscreen state, also ask the document
   3916    // to exit from DOM Fullscreen.
   3917    Document::ExitFullscreenInDocTree(aDoc);
   3918  }
   3919 }
   3920 
   3921 struct FullscreenTransitionDuration {
   3922  // The unit of the durations is millisecond
   3923  uint16_t mFadeIn = 0;
   3924  uint16_t mFadeOut = 0;
   3925  bool IsSuppressed() const { return mFadeIn == 0 && mFadeOut == 0; }
   3926 };
   3927 
   3928 static void GetFullscreenTransitionDuration(
   3929    bool aEnterFullscreen, FullscreenTransitionDuration* aDuration) {
   3930  const char* pref = aEnterFullscreen
   3931                         ? "full-screen-api.transition-duration.enter"
   3932                         : "full-screen-api.transition-duration.leave";
   3933  nsAutoCString prefValue;
   3934  Preferences::GetCString(pref, prefValue);
   3935  if (!prefValue.IsEmpty()) {
   3936    sscanf(prefValue.get(), "%hu%hu", &aDuration->mFadeIn,
   3937           &aDuration->mFadeOut);
   3938  }
   3939 }
   3940 
   3941 class FullscreenTransitionTask : public Runnable {
   3942 public:
   3943  FullscreenTransitionTask(const FullscreenTransitionDuration& aDuration,
   3944                           nsGlobalWindowOuter* aWindow, bool aFullscreen,
   3945                           nsIWidget* aWidget, nsISupports* aTransitionData)
   3946      : mozilla::Runnable("FullscreenTransitionTask"),
   3947        mWindow(aWindow),
   3948        mWidget(aWidget),
   3949        mTransitionData(aTransitionData),
   3950        mDuration(aDuration),
   3951        mStage(eBeforeToggle),
   3952        mFullscreen(aFullscreen) {}
   3953 
   3954  NS_IMETHOD Run() override;
   3955 
   3956 private:
   3957  ~FullscreenTransitionTask() override = default;
   3958 
   3959  /**
   3960   * The flow of fullscreen transition:
   3961   *
   3962   *         parent process         |         child process
   3963   * ----------------------------------------------------------------
   3964   *
   3965   *                                    | request/exit fullscreen
   3966   *                              <-----|
   3967   *         BeforeToggle stage |
   3968   *                            |
   3969   *  ToggleFullscreen stage *1 |----->
   3970   *                                    | HandleFullscreenRequests
   3971   *                                    |
   3972   *                              <-----| MozAfterPaint event
   3973   *       AfterToggle stage *2 |
   3974   *                            |
   3975   *                  End stage |
   3976   *
   3977   * Note we also start a timer at *1 so that if we don't get MozAfterPaint
   3978   * from the child process in time, we continue going to *2.
   3979   */
   3980  enum Stage {
   3981    // BeforeToggle stage happens before we enter or leave fullscreen
   3982    // state. In this stage, the task triggers the pre-toggle fullscreen
   3983    // transition on the widget.
   3984    eBeforeToggle,
   3985    // ToggleFullscreen stage actually executes the fullscreen toggle,
   3986    // and wait for the next paint on the content to continue.
   3987    eToggleFullscreen,
   3988    // AfterToggle stage happens after we toggle the fullscreen state.
   3989    // In this stage, the task triggers the post-toggle fullscreen
   3990    // transition on the widget.
   3991    eAfterToggle,
   3992    // End stage is triggered after the final transition finishes.
   3993    eEnd
   3994  };
   3995 
   3996  class Observer final : public nsIObserver, public nsINamed {
   3997   public:
   3998    NS_DECL_ISUPPORTS
   3999    NS_DECL_NSIOBSERVER
   4000    NS_DECL_NSINAMED
   4001 
   4002    explicit Observer(FullscreenTransitionTask* aTask) : mTask(aTask) {}
   4003 
   4004   private:
   4005    ~Observer() = default;
   4006 
   4007    RefPtr<FullscreenTransitionTask> mTask;
   4008  };
   4009 
   4010  static const char* const kPaintedTopic;
   4011 
   4012  RefPtr<nsGlobalWindowOuter> mWindow;
   4013  nsCOMPtr<nsIWidget> mWidget;
   4014  nsCOMPtr<nsITimer> mTimer;
   4015  nsCOMPtr<nsISupports> mTransitionData;
   4016 
   4017  TimeStamp mFullscreenChangeStartTime;
   4018  FullscreenTransitionDuration mDuration;
   4019  Stage mStage;
   4020  bool mFullscreen;
   4021 };
   4022 
   4023 const char* const FullscreenTransitionTask::kPaintedTopic =
   4024    "fullscreen-painted";
   4025 
   4026 NS_IMETHODIMP
   4027 FullscreenTransitionTask::Run() {
   4028  Stage stage = mStage;
   4029  mStage = Stage(mStage + 1);
   4030  if (MOZ_UNLIKELY(mWidget->Destroyed())) {
   4031    // If the widget has been destroyed before we get here, don't try to
   4032    // do anything more. Just let it go and release ourselves.
   4033    NS_WARNING("The widget to fullscreen has been destroyed");
   4034    mWindow->mIsInFullScreenTransition = false;
   4035    return NS_OK;
   4036  }
   4037  if (stage == eBeforeToggle) {
   4038    PROFILER_MARKER_UNTYPED("Fullscreen transition start", DOM);
   4039 
   4040    mWindow->mIsInFullScreenTransition = true;
   4041 
   4042    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   4043    NS_ENSURE_TRUE(obs, NS_ERROR_FAILURE);
   4044    obs->NotifyObservers(nullptr, "fullscreen-transition-start", nullptr);
   4045 
   4046    mWidget->PerformFullscreenTransition(nsIWidget::eBeforeFullscreenToggle,
   4047                                         mDuration.mFadeIn, mTransitionData,
   4048                                         this);
   4049  } else if (stage == eToggleFullscreen) {
   4050    PROFILER_MARKER_UNTYPED("Fullscreen toggle start", DOM);
   4051    mFullscreenChangeStartTime = TimeStamp::Now();
   4052    // Toggle the fullscreen state on the widget
   4053    if (!mWindow->SetWidgetFullscreen(FullscreenReason::ForFullscreenAPI,
   4054                                      mFullscreen, mWidget)) {
   4055      // Fail to setup the widget, call FinishFullscreenChange to
   4056      // complete fullscreen change directly.
   4057      mWindow->FinishFullscreenChange(mFullscreen);
   4058    }
   4059    // Set observer for the next content paint.
   4060    nsCOMPtr<nsIObserver> observer = new Observer(this);
   4061    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   4062    obs->AddObserver(observer, kPaintedTopic, false);
   4063    // There are several edge cases where we may never get the paint
   4064    // notification, including:
   4065    // 1. the window/tab is closed before the next paint;
   4066    // 2. the user has switched to another tab before we get here.
   4067    // Completely fixing those cases seems to be tricky, and since they
   4068    // should rarely happen, it probably isn't worth to fix. Hence we
   4069    // simply add a timeout here to ensure we never hang forever.
   4070    // In addition, if the page is complicated or the machine is less
   4071    // powerful, layout could take a long time, in which case, staying
   4072    // in black screen for that long could hurt user experience even
   4073    // more than exposing an intermediate state.
   4074    uint32_t timeout =
   4075        Preferences::GetUint("full-screen-api.transition.timeout", 1000);
   4076    NS_NewTimerWithObserver(getter_AddRefs(mTimer), observer, timeout,
   4077                            nsITimer::TYPE_ONE_SHOT);
   4078  } else if (stage == eAfterToggle) {
   4079    glean::dom::fullscreen_transition_black.AccumulateRawDuration(
   4080        TimeStamp::Now() - mFullscreenChangeStartTime);
   4081    mWidget->PerformFullscreenTransition(nsIWidget::eAfterFullscreenToggle,
   4082                                         mDuration.mFadeOut, mTransitionData,
   4083                                         this);
   4084  } else if (stage == eEnd) {
   4085    PROFILER_MARKER_UNTYPED("Fullscreen transition end", DOM);
   4086 
   4087    mWindow->mIsInFullScreenTransition = false;
   4088 
   4089    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   4090    NS_ENSURE_TRUE(obs, NS_ERROR_FAILURE);
   4091    obs->NotifyObservers(nullptr, "fullscreen-transition-end", nullptr);
   4092 
   4093    mWidget->CleanupFullscreenTransition();
   4094  }
   4095  return NS_OK;
   4096 }
   4097 
   4098 NS_IMPL_ISUPPORTS(FullscreenTransitionTask::Observer, nsIObserver, nsINamed)
   4099 
   4100 NS_IMETHODIMP
   4101 FullscreenTransitionTask::Observer::Observe(nsISupports* aSubject,
   4102                                            const char* aTopic,
   4103                                            const char16_t* aData) {
   4104  bool shouldContinue = false;
   4105  if (strcmp(aTopic, FullscreenTransitionTask::kPaintedTopic) == 0) {
   4106    nsCOMPtr<nsPIDOMWindowInner> win(do_QueryInterface(aSubject));
   4107    nsCOMPtr<nsIWidget> widget =
   4108        win ? nsGlobalWindowInner::Cast(win)->GetMainWidget() : nullptr;
   4109    if (widget == mTask->mWidget) {
   4110      // The paint notification arrives first. Cancel the timer.
   4111      mTask->mTimer->Cancel();
   4112      shouldContinue = true;
   4113      PROFILER_MARKER_UNTYPED("Fullscreen toggle end", DOM);
   4114    }
   4115  } else {
   4116 #ifdef DEBUG
   4117    MOZ_ASSERT(strcmp(aTopic, NS_TIMER_CALLBACK_TOPIC) == 0,
   4118               "Should only get fullscreen-painted or timer-callback");
   4119    nsCOMPtr<nsITimer> timer(do_QueryInterface(aSubject));
   4120    MOZ_ASSERT(timer && timer == mTask->mTimer,
   4121               "Should only trigger this with the timer the task created");
   4122 #endif
   4123    shouldContinue = true;
   4124    PROFILER_MARKER_UNTYPED("Fullscreen toggle timeout", DOM);
   4125  }
   4126  if (shouldContinue) {
   4127    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   4128    obs->RemoveObserver(this, kPaintedTopic);
   4129    mTask->mTimer = nullptr;
   4130    mTask->Run();
   4131  }
   4132  return NS_OK;
   4133 }
   4134 
   4135 NS_IMETHODIMP
   4136 FullscreenTransitionTask::Observer::GetName(nsACString& aName) {
   4137  aName.AssignLiteral("FullscreenTransitionTask");
   4138  return NS_OK;
   4139 }
   4140 
   4141 static bool MakeWidgetFullscreen(nsGlobalWindowOuter* aWindow,
   4142                                 FullscreenReason aReason, bool aFullscreen) {
   4143  nsCOMPtr<nsIWidget> widget = aWindow->GetMainWidget();
   4144  if (!widget) {
   4145    return false;
   4146  }
   4147 
   4148  FullscreenTransitionDuration duration;
   4149  bool performTransition = false;
   4150  nsCOMPtr<nsISupports> transitionData;
   4151  if (aReason == FullscreenReason::ForFullscreenAPI) {
   4152    GetFullscreenTransitionDuration(aFullscreen, &duration);
   4153    if (!duration.IsSuppressed()) {
   4154      performTransition = widget->PrepareForFullscreenTransition(
   4155          getter_AddRefs(transitionData));
   4156    }
   4157  }
   4158 
   4159  if (!performTransition) {
   4160    return aWindow->SetWidgetFullscreen(aReason, aFullscreen, widget);
   4161  }
   4162 
   4163  nsCOMPtr<nsIRunnable> task = new FullscreenTransitionTask(
   4164      duration, aWindow, aFullscreen, widget, transitionData);
   4165  task->Run();
   4166  return true;
   4167 }
   4168 
   4169 nsresult nsGlobalWindowOuter::ProcessWidgetFullscreenRequest(
   4170    FullscreenReason aReason, bool aFullscreen) {
   4171  mInProcessFullscreenRequest.emplace(aReason, aFullscreen);
   4172 
   4173  // Prevent chrome documents which are still loading from resizing
   4174  // the window after we set fullscreen mode.
   4175  nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
   4176  nsCOMPtr<nsIAppWindow> appWin(do_GetInterface(treeOwnerAsWin));
   4177  if (aFullscreen && appWin) {
   4178    appWin->SetIntrinsicallySized(false);
   4179  }
   4180 
   4181  // Sometimes we don't want the top-level widget to actually go fullscreen:
   4182  // - in the B2G desktop client, we don't want the emulated screen dimensions
   4183  //   to appear to increase when entering fullscreen mode; we just want the
   4184  //   content to fill the entire client area of the emulator window.
   4185  // - in FxR Desktop, we don't want fullscreen to take over the monitor, but
   4186  //   instead we want fullscreen to fill the FxR window in the the headset.
   4187  if (!StaticPrefs::full_screen_api_ignore_widgets() &&
   4188      !mForceFullScreenInWidget) {
   4189    if (MakeWidgetFullscreen(this, aReason, aFullscreen)) {
   4190      // The rest of code for switching fullscreen is in nsGlobalWindowOuter::
   4191      // FinishFullscreenChange() which will be called after sizemodechange
   4192      // event is dispatched.
   4193      return NS_OK;
   4194    }
   4195  }
   4196 
   4197 #if defined(NIGHTLY_BUILD) && defined(XP_WIN)
   4198  if (FxRWindowManager::GetInstance()->IsFxRWindow(mWindowID)) {
   4199    mozilla::gfx::VRShMem shmem(nullptr, true /*aRequiresMutex*/);
   4200    shmem.SendFullscreenState(mWindowID, aFullscreen);
   4201  }
   4202 #endif  // NIGHTLY_BUILD && XP_WIN
   4203  FinishFullscreenChange(aFullscreen);
   4204  return NS_OK;
   4205 }
   4206 
   4207 nsresult nsGlobalWindowOuter::SetFullscreenInternal(FullscreenReason aReason,
   4208                                                    bool aFullscreen) {
   4209  MOZ_ASSERT(nsContentUtils::IsSafeToRunScript(),
   4210             "Requires safe to run script as it "
   4211             "may call FinishDOMFullscreenChange");
   4212 
   4213  NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE);
   4214 
   4215  MOZ_ASSERT(
   4216      aReason != FullscreenReason::ForForceExitFullscreen || !aFullscreen,
   4217      "FullscreenReason::ForForceExitFullscreen can "
   4218      "only be used with exiting fullscreen");
   4219 
   4220  // Only chrome can change our fullscreen mode. Otherwise, the state
   4221  // can only be changed for DOM fullscreen.
   4222  if (aReason == FullscreenReason::ForFullscreenMode &&
   4223      !nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
   4224    return NS_OK;
   4225  }
   4226 
   4227  // Element.requestFullscreen() is already blocked, but also block
   4228  // fullscreening for other callers, especially the chrome window.
   4229  if (GetBrowsingContext()->Top()->GetIsDocumentPiP()) {
   4230    return NS_OK;
   4231  }
   4232 
   4233  // SetFullscreen needs to be called on the root window, so get that
   4234  // via the DocShell tree, and if we are not already the root,
   4235  // call SetFullscreen on that window instead.
   4236  nsCOMPtr<nsIDocShellTreeItem> rootItem;
   4237  mDocShell->GetInProcessRootTreeItem(getter_AddRefs(rootItem));
   4238  nsCOMPtr<nsPIDOMWindowOuter> window =
   4239      rootItem ? rootItem->GetWindow() : nullptr;
   4240  if (!window) return NS_ERROR_FAILURE;
   4241  if (rootItem != mDocShell)
   4242    return window->SetFullscreenInternal(aReason, aFullscreen);
   4243 
   4244  // make sure we don't try to set full screen on a non-chrome window,
   4245  // which might happen in embedding world
   4246  if (mDocShell->ItemType() != nsIDocShellTreeItem::typeChrome)
   4247    return NS_ERROR_FAILURE;
   4248 
   4249  // FullscreenReason::ForForceExitFullscreen can only be used with exiting
   4250  // fullscreen
   4251  MOZ_ASSERT_IF(
   4252      mFullscreen.isSome(),
   4253      mFullscreen.value() != FullscreenReason::ForForceExitFullscreen);
   4254 
   4255  // If we are already in full screen mode, just return, we don't care about the
   4256  // reason here, because,
   4257  // - If we are in fullscreen mode due to browser fullscreen mode, requesting
   4258  //   DOM fullscreen does not change anything.
   4259  // - If we are in fullscreen mode due to DOM fullscreen, requesting browser
   4260  //   fullscreen should not change anything, either. Note that we should not
   4261  //   update reason to ForFullscreenMode, otherwise the subsequent DOM
   4262  //   fullscreen exit will be ignored and user will be confused. And ideally
   4263  //   this should never happen as `window.fullscreen` returns `true` for DOM
   4264  //   fullscreen as well.
   4265  if (mFullscreen.isSome() == aFullscreen) {
   4266    // How come we get browser fullscreen request while we are already in DOM
   4267    // fullscreen?
   4268    MOZ_ASSERT_IF(aFullscreen && aReason == FullscreenReason::ForFullscreenMode,
   4269                  mFullscreen.value() != FullscreenReason::ForFullscreenAPI);
   4270    return NS_OK;
   4271  }
   4272 
   4273  // Note that although entering DOM fullscreen could also cause
   4274  // consequential calls to this method, those calls will be skipped
   4275  // at the condition above.
   4276  if (aReason == FullscreenReason::ForFullscreenMode) {
   4277    if (!aFullscreen && mFullscreen &&
   4278        mFullscreen.value() == FullscreenReason::ForFullscreenAPI) {
   4279      // If we are exiting fullscreen mode, but we actually didn't
   4280      // entered browser fullscreen mode, the fullscreen state was only for
   4281      // the Fullscreen API. Change the reason here so that we can
   4282      // perform transition for it.
   4283      aReason = FullscreenReason::ForFullscreenAPI;
   4284    }
   4285  } else {
   4286    // If we are exiting from DOM fullscreen while we initially make
   4287    // the window fullscreen because of browser fullscreen mode, don't restore
   4288    // the window. But we still need to exit the DOM fullscreen state.
   4289    if (!aFullscreen && mFullscreen &&
   4290        mFullscreen.value() == FullscreenReason::ForFullscreenMode) {
   4291      // If there is a in-process fullscreen request, FinishDOMFullscreenChange
   4292      // will be called when the request is finished.
   4293      if (!mInProcessFullscreenRequest.isSome()) {
   4294        FinishDOMFullscreenChange(mDoc, false);
   4295      }
   4296      return NS_OK;
   4297    }
   4298  }
   4299 
   4300  // Set this before so if widget sends an event indicating its
   4301  // gone full screen, the state trap above works.
   4302  if (aFullscreen) {
   4303    mFullscreen.emplace(aReason);
   4304  } else {
   4305    mFullscreen.reset();
   4306  }
   4307 
   4308  // If we are in process of fullscreen request, only keep the latest fullscreen
   4309  // state, we will sync up later while the processing request is finished.
   4310  if (mInProcessFullscreenRequest.isSome()) {
   4311    mFullscreenHasChangedDuringProcessing = true;
   4312    return NS_OK;
   4313  }
   4314 
   4315  return ProcessWidgetFullscreenRequest(aReason, aFullscreen);
   4316 }
   4317 
   4318 // Support a per-window, dynamic equivalent of enabling
   4319 // full-screen-api.ignore-widgets
   4320 void nsGlobalWindowOuter::ForceFullScreenInWidget() {
   4321  MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
   4322 
   4323  mForceFullScreenInWidget = true;
   4324 }
   4325 
   4326 bool nsGlobalWindowOuter::SetWidgetFullscreen(FullscreenReason aReason,
   4327                                              bool aIsFullscreen,
   4328                                              nsIWidget* aWidget) {
   4329  MOZ_ASSERT(this == GetInProcessTopInternal(),
   4330             "Only topmost window should call this");
   4331  MOZ_ASSERT(!GetFrameElementInternal(), "Content window should not call this");
   4332  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
   4333 
   4334  if (!NS_WARN_IF(!IsChromeWindow())) {
   4335    if (!NS_WARN_IF(mChromeFields.mFullscreenPresShell)) {
   4336      if (PresShell* presShell = mDocShell->GetPresShell()) {
   4337        if (nsRefreshDriver* rd = presShell->GetRefreshDriver()) {
   4338          mChromeFields.mFullscreenPresShell = do_GetWeakReference(presShell);
   4339          MOZ_ASSERT(mChromeFields.mFullscreenPresShell);
   4340          rd->SetIsResizeSuppressed();
   4341          rd->Freeze();
   4342        }
   4343      }
   4344    }
   4345  }
   4346  nsresult rv = aReason == FullscreenReason::ForFullscreenMode
   4347                    ?
   4348                    // If we enter fullscreen for fullscreen mode, we want
   4349                    // the native system behavior.
   4350                    aWidget->MakeFullScreenWithNativeTransition(aIsFullscreen)
   4351                    : aWidget->MakeFullScreen(aIsFullscreen);
   4352  return NS_SUCCEEDED(rv);
   4353 }
   4354 
   4355 /* virtual */
   4356 void nsGlobalWindowOuter::FullscreenWillChange(bool aIsFullscreen) {
   4357  if (!mInProcessFullscreenRequest.isSome()) {
   4358    // If there is no in-process fullscreen request, the fullscreen state change
   4359    // is triggered from the OS directly, e.g. user use built-in window button
   4360    // to enter/exit fullscreen on macOS.
   4361    mInProcessFullscreenRequest.emplace(FullscreenReason::ForFullscreenMode,
   4362                                        aIsFullscreen);
   4363    if (mFullscreen.isSome() != aIsFullscreen) {
   4364      if (aIsFullscreen) {
   4365        mFullscreen.emplace(FullscreenReason::ForFullscreenMode);
   4366      } else {
   4367        mFullscreen.reset();
   4368      }
   4369    } else {
   4370      // It is possible that FullscreenWillChange is notified with current
   4371      // fullscreen state, e.g. browser goes into fullscreen when widget
   4372      // fullscreen is prevented, and then user triggers fullscreen from the OS
   4373      // directly again.
   4374      MOZ_ASSERT(StaticPrefs::full_screen_api_ignore_widgets() ||
   4375                     mForceFullScreenInWidget,
   4376                 "This should only happen when widget fullscreen is prevented");
   4377    }
   4378  }
   4379  if (aIsFullscreen) {
   4380    DispatchCustomEvent(u"willenterfullscreen"_ns, ChromeOnlyDispatch::eYes);
   4381  } else {
   4382    DispatchCustomEvent(u"willexitfullscreen"_ns, ChromeOnlyDispatch::eYes);
   4383  }
   4384 }
   4385 
   4386 /* virtual */
   4387 void nsGlobalWindowOuter::FinishFullscreenChange(bool aIsFullscreen) {
   4388  mozilla::Maybe<FullscreenRequest> currentInProcessRequest =
   4389      std::move(mInProcessFullscreenRequest);
   4390  if (!mFullscreenHasChangedDuringProcessing &&
   4391      aIsFullscreen != mFullscreen.isSome()) {
   4392    NS_WARNING("Failed to toggle fullscreen state of the widget");
   4393    // We failed to make the widget enter fullscreen.
   4394    // Stop further changes and restore the state.
   4395    if (!aIsFullscreen) {
   4396      mFullscreen.reset();
   4397    } else {
   4398 #ifndef XP_MACOSX
   4399      MOZ_ASSERT_UNREACHABLE("Failed to exit fullscreen?");
   4400 #endif
   4401      // Restore fullscreen state with FullscreenReason::ForFullscreenAPI reason
   4402      // in order to make subsequent DOM fullscreen exit request can exit
   4403      // browser fullscreen mode.
   4404      mFullscreen.emplace(FullscreenReason::ForFullscreenAPI);
   4405    }
   4406    return;
   4407  }
   4408 
   4409  // Note that we must call this to toggle the DOM fullscreen state
   4410  // of the document before dispatching the "fullscreen" event, so
   4411  // that the chrome can distinguish between browser fullscreen mode
   4412  // and DOM fullscreen.
   4413  FinishDOMFullscreenChange(mDoc, aIsFullscreen);
   4414 
   4415  // dispatch a "fullscreen" DOM event so that XUL apps can
   4416  // respond visually if we are kicked into full screen mode
   4417  DispatchCustomEvent(u"fullscreen"_ns, ChromeOnlyDispatch::eYes);
   4418 
   4419  if (!NS_WARN_IF(!IsChromeWindow())) {
   4420    if (RefPtr<PresShell> presShell =
   4421            do_QueryReferent(mChromeFields.mFullscreenPresShell)) {
   4422      if (nsRefreshDriver* rd = presShell->GetRefreshDriver()) {
   4423        rd->Thaw();
   4424      }
   4425      mChromeFields.mFullscreenPresShell = nullptr;
   4426    }
   4427  }
   4428 
   4429  // If fullscreen state has changed during processing fullscreen request, we
   4430  // need to ensure widget matches our latest fullscreen state here.
   4431  if (mFullscreenHasChangedDuringProcessing) {
   4432    mFullscreenHasChangedDuringProcessing = false;
   4433    // Widget doesn't care about the reason that makes it entering/exiting
   4434    // fullscreen, so here we just need to ensure the fullscreen state is
   4435    // matched.
   4436    if (aIsFullscreen != mFullscreen.isSome()) {
   4437      // If we end up need to exit fullscreen, use the same reason that brings
   4438      // us into fullscreen mode, so that we will perform the same fullscreen
   4439      // transistion effect for exiting.
   4440      ProcessWidgetFullscreenRequest(
   4441          mFullscreen.isSome() ? mFullscreen.value()
   4442                               : currentInProcessRequest.value().mReason,
   4443          mFullscreen.isSome());
   4444    }
   4445  }
   4446 }
   4447 
   4448 /* virtual */
   4449 void nsGlobalWindowOuter::MacFullscreenMenubarOverlapChanged(
   4450    mozilla::DesktopCoord aOverlapAmount) {
   4451  ErrorResult res;
   4452  RefPtr<Event> domEvent =
   4453      mDoc->CreateEvent(u"CustomEvent"_ns, CallerType::System, res);
   4454  if (res.Failed()) {
   4455    return;
   4456  }
   4457 
   4458  AutoJSAPI jsapi;
   4459  jsapi.Init();
   4460  JSContext* cx = jsapi.cx();
   4461  JSAutoRealm ar(cx, GetWrapperPreserveColor());
   4462 
   4463  JS::Rooted<JS::Value> detailValue(cx);
   4464  if (!ToJSValue(cx, aOverlapAmount, &detailValue)) {
   4465    return;
   4466  }
   4467 
   4468  CustomEvent* customEvent = static_cast<CustomEvent*>(domEvent.get());
   4469  customEvent->InitCustomEvent(cx, u"MacFullscreenMenubarRevealUpdate"_ns,
   4470                               /* aCanBubble = */ true,
   4471                               /* aCancelable = */ true, detailValue);
   4472  domEvent->SetTrusted(true);
   4473  domEvent->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true;
   4474 
   4475  nsCOMPtr<EventTarget> target = this;
   4476  domEvent->SetTarget(target);
   4477 
   4478  target->DispatchEvent(*domEvent, CallerType::System, IgnoreErrors());
   4479 }
   4480 
   4481 bool nsGlobalWindowOuter::Fullscreen() const {
   4482  NS_ENSURE_TRUE(mDocShell, mFullscreen.isSome());
   4483 
   4484  // Get the fullscreen value of the root window, to always have the value
   4485  // accurate, even when called from content.
   4486  nsCOMPtr<nsIDocShellTreeItem> rootItem;
   4487  mDocShell->GetInProcessRootTreeItem(getter_AddRefs(rootItem));
   4488  if (rootItem == mDocShell) {
   4489    if (!XRE_IsContentProcess()) {
   4490      // We are the root window. Return our internal value.
   4491      return mFullscreen.isSome();
   4492    }
   4493    if (nsCOMPtr<nsIWidget> widget = GetNearestWidget()) {
   4494      // We are in content process, figure out the value from
   4495      // the sizemode of the puppet widget.
   4496      return widget->SizeMode() == nsSizeMode_Fullscreen;
   4497    }
   4498    return false;
   4499  }
   4500 
   4501  nsCOMPtr<nsPIDOMWindowOuter> window = rootItem->GetWindow();
   4502  NS_ENSURE_TRUE(window, mFullscreen.isSome());
   4503 
   4504  return nsGlobalWindowOuter::Cast(window)->Fullscreen();
   4505 }
   4506 
   4507 bool nsGlobalWindowOuter::GetFullscreenOuter() { return Fullscreen(); }
   4508 
   4509 bool nsGlobalWindowOuter::GetFullScreen() {
   4510  FORWARD_TO_INNER(GetFullScreen, (), false);
   4511 }
   4512 
   4513 void nsGlobalWindowOuter::EnsureReflowFlushAndPaint() {
   4514  NS_ASSERTION(mDocShell,
   4515               "EnsureReflowFlushAndPaint() called with no "
   4516               "docshell!");
   4517 
   4518  if (!mDocShell) return;
   4519 
   4520  RefPtr<PresShell> presShell = mDocShell->GetPresShell();
   4521  if (!presShell) {
   4522    return;
   4523  }
   4524 
   4525  // Flush pending reflows.
   4526  if (mDoc) {
   4527    mDoc->FlushPendingNotifications(FlushType::Layout);
   4528  }
   4529 
   4530  // Unsuppress painting.
   4531  presShell->UnsuppressPainting();
   4532 }
   4533 
   4534 // static
   4535 void nsGlobalWindowOuter::MakeMessageWithPrincipal(
   4536    nsAString& aOutMessage, nsIPrincipal* aSubjectPrincipal, bool aUseHostPort,
   4537    const char* aNullMessage, const char* aContentMessage,
   4538    const char* aFallbackMessage) {
   4539  MOZ_ASSERT(aSubjectPrincipal);
   4540 
   4541  aOutMessage.Truncate();
   4542 
   4543  // Try to get a host from the running principal -- this will do the
   4544  // right thing for javascript: and data: documents.
   4545 
   4546  nsAutoCString contentDesc;
   4547 
   4548  if (aSubjectPrincipal->GetIsNullPrincipal()) {
   4549    nsContentUtils::GetLocalizedString(
   4550        nsContentUtils::eCOMMON_DIALOG_PROPERTIES, aNullMessage, aOutMessage);
   4551  } else {
   4552    auto* addonPolicy = BasePrincipal::Cast(aSubjectPrincipal)->AddonPolicy();
   4553    if (addonPolicy) {
   4554      nsContentUtils::FormatLocalizedString(
   4555          aOutMessage, nsContentUtils::eCOMMON_DIALOG_PROPERTIES,
   4556          aContentMessage, addonPolicy->Name());
   4557    } else {
   4558      nsresult rv = NS_ERROR_FAILURE;
   4559      if (aUseHostPort) {
   4560        nsCOMPtr<nsIURI> uri = aSubjectPrincipal->GetURI();
   4561        if (uri) {
   4562          rv = uri->GetDisplayHostPort(contentDesc);
   4563        }
   4564      }
   4565      if (!aUseHostPort || NS_FAILED(rv)) {
   4566        rv = aSubjectPrincipal->GetExposablePrePath(contentDesc);
   4567      }
   4568      if (NS_SUCCEEDED(rv) && !contentDesc.IsEmpty()) {
   4569        NS_ConvertUTF8toUTF16 ucsPrePath(contentDesc);
   4570        nsContentUtils::FormatLocalizedString(
   4571            aOutMessage, nsContentUtils::eCOMMON_DIALOG_PROPERTIES,
   4572            aContentMessage, ucsPrePath);
   4573      }
   4574    }
   4575  }
   4576 
   4577  if (aOutMessage.IsEmpty()) {
   4578    // We didn't find a host so use the generic heading
   4579    nsContentUtils::GetLocalizedString(
   4580        nsContentUtils::eCOMMON_DIALOG_PROPERTIES, aFallbackMessage,
   4581        aOutMessage);
   4582  }
   4583 
   4584  // Just in case
   4585  if (aOutMessage.IsEmpty()) {
   4586    NS_WARNING(
   4587        "could not get ScriptDlgGenericHeading string from string bundle");
   4588    aOutMessage.AssignLiteral("[Script]");
   4589  }
   4590 }
   4591 
   4592 bool nsGlobalWindowOuter::CanMoveResizeWindows(CallerType aCallerType,
   4593                                               bool aIsMove,
   4594                                               ErrorResult& aError) {
   4595  if (mBrowsingContext->IsSubframe()) {
   4596    return false;
   4597  }
   4598 
   4599  // When called from chrome, we can avoid the following checks.
   4600  if (aCallerType != CallerType::System) {
   4601    // Don't allow scripts to move or resize windows that were not opened by a
   4602    // script.
   4603    if (!mBrowsingContext->GetTopLevelCreatedByWebContent()) {
   4604      return false;
   4605    }
   4606 
   4607    if (!CanSetProperty("dom.disable_window_move_resize")) {
   4608      return false;
   4609    }
   4610 
   4611    // Ignore the request if we have more than one tab in the window.
   4612    if (mBrowsingContext->Top()->HasSiblings()) {
   4613      return false;
   4614    }
   4615  }
   4616 
   4617  if (mDocShell) {
   4618    bool allow;
   4619    nsresult rv = mDocShell->GetAllowWindowControl(&allow);
   4620    if (NS_SUCCEEDED(rv) && !allow) return false;
   4621  }
   4622 
   4623  if (mBrowsingContext->GetIsDocumentPiP()) {
   4624    // https://wicg.github.io/document-picture-in-picture/#positioning
   4625    if (aIsMove) {
   4626      nsLiteralString errorMsg(
   4627          u"Picture-in-Picture windows cannot be moved by script.");
   4628      nsContentUtils::ReportToConsoleNonLocalized(
   4629          errorMsg, nsIScriptError::warningFlag, "Window"_ns, GetDocument());
   4630      return false;
   4631    }
   4632 
   4633    // https://wicg.github.io/document-picture-in-picture/#resizing-the-pip-window
   4634    WindowContext* wc = mInnerWindow->GetWindowContext();
   4635    if (!wc || !wc->ConsumeTransientUserGestureActivation()) {
   4636      aError.ThrowNotAllowedError(
   4637          "Resizing a Picture-in-Picture window requires transient activation");
   4638      return false;
   4639    }
   4640  }
   4641 
   4642  if (nsGlobalWindowInner::sMouseDown &&
   4643      !nsGlobalWindowInner::sDragServiceDisabled) {
   4644    nsCOMPtr<nsIDragService> ds =
   4645        do_GetService("@mozilla.org/widget/dragservice;1");
   4646    if (ds) {
   4647      nsGlobalWindowInner::sDragServiceDisabled = true;
   4648      ds->Suppress();
   4649    }
   4650  }
   4651  return true;
   4652 }
   4653 
   4654 bool nsGlobalWindowOuter::AlertOrConfirm(bool aAlert, const nsAString& aMessage,
   4655                                         nsIPrincipal& aSubjectPrincipal,
   4656                                         ErrorResult& aError) {
   4657  // XXX This method is very similar to nsGlobalWindowOuter::Prompt, make
   4658  // sure any modifications here don't need to happen over there!
   4659  if (!AreDialogsEnabled()) {
   4660    // Just silently return.  In the case of alert(), the return value is
   4661    // ignored.  In the case of confirm(), returning false is the same thing as
   4662    // would happen if the user cancels.
   4663    return false;
   4664  }
   4665 
   4666  // Reset popup state while opening a modal dialog, and firing events
   4667  // about the dialog, to prevent the current state from being active
   4668  // the whole time a modal dialog is open.
   4669  AutoPopupStatePusher popupStatePusher(PopupBlocker::openAbused, true);
   4670 
   4671  // Before bringing up the window, unsuppress painting and flush
   4672  // pending reflows.
   4673  EnsureReflowFlushAndPaint();
   4674 
   4675  nsAutoString title;
   4676  MakeMessageWithPrincipal(title, &aSubjectPrincipal, false,
   4677                           "ScriptDlgNullPrincipalHeading", "ScriptDlgHeading",
   4678                           "ScriptDlgGenericHeading");
   4679 
   4680  // Remove non-terminating null characters from the
   4681  // string. See bug #310037.
   4682  nsAutoString final;
   4683  nsContentUtils::StripNullChars(aMessage, final);
   4684  nsContentUtils::PlatformToDOMLineBreaks(final);
   4685 
   4686  nsresult rv;
   4687  nsCOMPtr<nsIPromptFactory> promptFac =
   4688      do_GetService("@mozilla.org/prompter;1", &rv);
   4689  if (NS_FAILED(rv)) {
   4690    aError.Throw(rv);
   4691    return false;
   4692  }
   4693 
   4694  nsCOMPtr<nsIPrompt> prompt;
   4695  aError =
   4696      promptFac->GetPrompt(this, NS_GET_IID(nsIPrompt), getter_AddRefs(prompt));
   4697  if (aError.Failed()) {
   4698    return false;
   4699  }
   4700 
   4701  // Always allow content modal prompts for alert and confirm.
   4702  if (nsCOMPtr<nsIWritablePropertyBag2> promptBag = do_QueryInterface(prompt)) {
   4703    promptBag->SetPropertyAsUint32(u"modalType"_ns,
   4704                                   nsIPrompt::MODAL_TYPE_CONTENT);
   4705  }
   4706 
   4707  bool result = false;
   4708  nsAutoSyncOperation sync(mDoc, SyncOperationBehavior::eSuspendInput);
   4709  if (ShouldPromptToBlockDialogs()) {
   4710    bool disallowDialog = false;
   4711    nsAutoString label;
   4712    MakeMessageWithPrincipal(
   4713        label, &aSubjectPrincipal, true, "ScriptDialogLabelNullPrincipal",
   4714        "ScriptDialogLabelContentPrincipal", "ScriptDialogLabelNullPrincipal");
   4715 
   4716    aError = aAlert
   4717                 ? prompt->AlertCheck(title.get(), final.get(), label.get(),
   4718                                      &disallowDialog)
   4719                 : prompt->ConfirmCheck(title.get(), final.get(), label.get(),
   4720                                        &disallowDialog, &result);
   4721 
   4722    if (disallowDialog) {
   4723      DisableDialogs();
   4724    }
   4725  } else {
   4726    aError = aAlert ? prompt->Alert(title.get(), final.get())
   4727                    : prompt->Confirm(title.get(), final.get(), &result);
   4728  }
   4729 
   4730  return result;
   4731 }
   4732 
   4733 void nsGlobalWindowOuter::AlertOuter(const nsAString& aMessage,
   4734                                     nsIPrincipal& aSubjectPrincipal,
   4735                                     ErrorResult& aError) {
   4736  AlertOrConfirm(/* aAlert = */ true, aMessage, aSubjectPrincipal, aError);
   4737 }
   4738 
   4739 bool nsGlobalWindowOuter::ConfirmOuter(const nsAString& aMessage,
   4740                                       nsIPrincipal& aSubjectPrincipal,
   4741                                       ErrorResult& aError) {
   4742  return AlertOrConfirm(/* aAlert = */ false, aMessage, aSubjectPrincipal,
   4743                        aError);
   4744 }
   4745 
   4746 void nsGlobalWindowOuter::PromptOuter(const nsAString& aMessage,
   4747                                      const nsAString& aInitial,
   4748                                      nsAString& aReturn,
   4749                                      nsIPrincipal& aSubjectPrincipal,
   4750                                      ErrorResult& aError) {
   4751  // XXX This method is very similar to nsGlobalWindowOuter::AlertOrConfirm,
   4752  // make sure any modifications here don't need to happen over there!
   4753  SetDOMStringToNull(aReturn);
   4754 
   4755  if (!AreDialogsEnabled()) {
   4756    // Return null, as if the user just canceled the prompt.
   4757    return;
   4758  }
   4759 
   4760  // Reset popup state while opening a modal dialog, and firing events
   4761  // about the dialog, to prevent the current state from being active
   4762  // the whole time a modal dialog is open.
   4763  AutoPopupStatePusher popupStatePusher(PopupBlocker::openAbused, true);
   4764 
   4765  // Before bringing up the window, unsuppress painting and flush
   4766  // pending reflows.
   4767  EnsureReflowFlushAndPaint();
   4768 
   4769  nsAutoString title;
   4770  MakeMessageWithPrincipal(title, &aSubjectPrincipal, false,
   4771                           "ScriptDlgNullPrincipalHeading", "ScriptDlgHeading",
   4772                           "ScriptDlgGenericHeading");
   4773 
   4774  // Remove non-terminating null characters from the
   4775  // string. See bug #310037.
   4776  nsAutoString fixedMessage, fixedInitial;
   4777  nsContentUtils::StripNullChars(aMessage, fixedMessage);
   4778  nsContentUtils::PlatformToDOMLineBreaks(fixedMessage);
   4779  nsContentUtils::StripNullChars(aInitial, fixedInitial);
   4780 
   4781  nsresult rv;
   4782  nsCOMPtr<nsIPromptFactory> promptFac =
   4783      do_GetService("@mozilla.org/prompter;1", &rv);
   4784  if (NS_FAILED(rv)) {
   4785    aError.Throw(rv);
   4786    return;
   4787  }
   4788 
   4789  nsCOMPtr<nsIPrompt> prompt;
   4790  aError =
   4791      promptFac->GetPrompt(this, NS_GET_IID(nsIPrompt), getter_AddRefs(prompt));
   4792  if (aError.Failed()) {
   4793    return;
   4794  }
   4795 
   4796  // Always allow content modal prompts for prompt.
   4797  if (nsCOMPtr<nsIWritablePropertyBag2> promptBag = do_QueryInterface(prompt)) {
   4798    promptBag->SetPropertyAsUint32(u"modalType"_ns,
   4799                                   nsIPrompt::MODAL_TYPE_CONTENT);
   4800  }
   4801 
   4802  // Pass in the default value, if any.
   4803  char16_t* inoutValue = ToNewUnicode(fixedInitial);
   4804  bool disallowDialog = false;
   4805 
   4806  nsAutoString label;
   4807  label.SetIsVoid(true);
   4808  if (ShouldPromptToBlockDialogs()) {
   4809    nsContentUtils::GetLocalizedString(
   4810        nsContentUtils::eCOMMON_DIALOG_PROPERTIES, "ScriptDialogLabel", label);
   4811  }
   4812 
   4813  nsAutoSyncOperation sync(mDoc, SyncOperationBehavior::eSuspendInput);
   4814  bool ok;
   4815  aError = prompt->Prompt(title.get(), fixedMessage.get(), &inoutValue,
   4816                          label.IsVoid() ? nullptr : label.get(),
   4817                          &disallowDialog, &ok);
   4818 
   4819  if (disallowDialog) {
   4820    DisableDialogs();
   4821  }
   4822 
   4823  // XXX Doesn't this leak inoutValue?
   4824  if (aError.Failed()) {
   4825    return;
   4826  }
   4827 
   4828  nsString outValue;
   4829  outValue.Adopt(inoutValue);
   4830  if (ok && inoutValue) {
   4831    aReturn = std::move(outValue);
   4832  }
   4833 }
   4834 
   4835 void nsGlobalWindowOuter::FocusOuter(CallerType aCallerType,
   4836                                     bool aFromOtherProcess,
   4837                                     uint64_t aActionId) {
   4838  RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager();
   4839  if (MOZ_UNLIKELY(!fm)) {
   4840    return;
   4841  }
   4842 
   4843  auto [canFocus, isActive] = GetBrowsingContext()->CanFocusCheck(aCallerType);
   4844  if (aFromOtherProcess) {
   4845    // We trust that the check passed in a process that's, in principle,
   4846    // untrusted, because we don't have the required caller context available
   4847    // here. Also, the worst that the other process can do in this case is to
   4848    // raise a window it's not supposed to be allowed to raise.
   4849    // https://bugzilla.mozilla.org/show_bug.cgi?id=1677899
   4850    MOZ_ASSERT(XRE_IsContentProcess(),
   4851               "Parent should not trust other processes.");
   4852    canFocus = true;
   4853  }
   4854 
   4855  nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
   4856  if (treeOwnerAsWin && (canFocus || isActive)) {
   4857    bool isEnabled = true;
   4858    if (NS_SUCCEEDED(treeOwnerAsWin->GetEnabled(&isEnabled)) && !isEnabled) {
   4859      NS_WARNING("Should not try to set the focus on a disabled window");
   4860      return;
   4861    }
   4862  }
   4863 
   4864  if (!mDocShell) {
   4865    return;
   4866  }
   4867 
   4868  // If the window has a child frame focused, clear the focus. This
   4869  // ensures that focus will be in this frame and not in a child.
   4870  if (nsIContent* content = GetFocusedElement()) {
   4871    if (HTMLIFrameElement::FromNode(content)) {
   4872      fm->ClearFocus(this);
   4873    }
   4874  }
   4875 
   4876  RefPtr<BrowsingContext> parent;
   4877  BrowsingContext* bc = GetBrowsingContext();
   4878  if (bc) {
   4879    parent = bc->GetParent();
   4880    if (!parent && XRE_IsParentProcess()) {
   4881      parent = bc->Canonical()->GetParentCrossChromeBoundary();
   4882    }
   4883  }
   4884  if (parent) {
   4885    if (!parent->IsInProcess()) {
   4886      if (isActive) {
   4887        OwningNonNull<nsGlobalWindowOuter> kungFuDeathGrip(*this);
   4888        fm->WindowRaised(kungFuDeathGrip, aActionId);
   4889      } else {
   4890        ContentChild* contentChild = ContentChild::GetSingleton();
   4891        MOZ_ASSERT(contentChild);
   4892        contentChild->SendFinalizeFocusOuter(bc, canFocus, aCallerType);
   4893      }
   4894      return;
   4895    }
   4896 
   4897    MOZ_ASSERT(mDoc, "Call chain should have ensured document creation.");
   4898    if (mDoc) {
   4899      if (Element* frame = mDoc->GetEmbedderElement()) {
   4900        nsContentUtils::RequestFrameFocus(*frame, canFocus, aCallerType);
   4901      }
   4902    }
   4903    return;
   4904  }
   4905 
   4906  if (canFocus) {
   4907    // if there is no parent, this must be a toplevel window, so raise the
   4908    // window if canFocus is true. If this is a child process, the raise
   4909    // window request will get forwarded to the parent by the puppet widget.
   4910    OwningNonNull<nsGlobalWindowOuter> kungFuDeathGrip(*this);
   4911    fm->RaiseWindow(kungFuDeathGrip, aCallerType, aActionId);
   4912  }
   4913 }
   4914 
   4915 nsresult nsGlobalWindowOuter::Focus(CallerType aCallerType) {
   4916  FORWARD_TO_INNER(Focus, (aCallerType), NS_ERROR_UNEXPECTED);
   4917 }
   4918 
   4919 void nsGlobalWindowOuter::BlurOuter(CallerType aCallerType) {
   4920  if (!GetBrowsingContext()->CanBlurCheck(aCallerType)) {
   4921    return;
   4922  }
   4923 
   4924  nsCOMPtr<nsIWebBrowserChrome> chrome = GetWebBrowserChrome();
   4925  if (chrome) {
   4926    chrome->Blur();
   4927  }
   4928 }
   4929 
   4930 void nsGlobalWindowOuter::StopOuter(ErrorResult& aError) {
   4931  // IsNavigationAllowed checks are usually done in nsDocShell directly,
   4932  // however nsDocShell::Stop has a bunch of internal users that would fail
   4933  // the IsNavigationAllowed check.
   4934  if (!mDocShell || !nsDocShell::Cast(mDocShell)->IsNavigationAllowed()) {
   4935    return;
   4936  }
   4937 
   4938  nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mDocShell));
   4939  if (webNav) {
   4940    aError = webNav->Stop(nsIWebNavigation::STOP_ALL);
   4941  }
   4942 }
   4943 
   4944 void nsGlobalWindowOuter::PrintOuter(ErrorResult& aError) {
   4945  if (!AreDialogsEnabled()) {
   4946    // Per spec, silently return. https://github.com/whatwg/html/commit/21a1de1
   4947    return;
   4948  }
   4949 
   4950  // Printing is disabled, silently return.
   4951  if (!StaticPrefs::print_enabled()) {
   4952    return;
   4953  }
   4954 
   4955  // If we're loading, queue the print for later. This is a special-case that
   4956  // only applies to the window.print() call, for compat with other engines and
   4957  // pre-existing behavior.
   4958  if (mShouldDelayPrintUntilAfterLoad) {
   4959    if (nsIDocShell* docShell = GetDocShell()) {
   4960      if (docShell->GetBusyFlags() & nsIDocShell::BUSY_FLAGS_PAGE_LOADING) {
   4961        mDelayedPrintUntilAfterLoad = true;
   4962        return;
   4963      }
   4964    }
   4965  }
   4966 
   4967 #ifdef NS_PRINTING
   4968  RefPtr<BrowsingContext> top =
   4969      mBrowsingContext ? mBrowsingContext->Top() : nullptr;
   4970  if (NS_WARN_IF(top && top->GetIsPrinting())) {
   4971    return;
   4972  }
   4973 
   4974  if (top) {
   4975    (void)top->SetIsPrinting(true);
   4976  }
   4977 
   4978  auto unset = MakeScopeExit([&] {
   4979    if (top) {
   4980      (void)top->SetIsPrinting(false);
   4981    }
   4982  });
   4983 
   4984  const bool forPreview = !StaticPrefs::print_always_print_silent() &&
   4985                          !StaticPrefs::print_prefer_system_dialog();
   4986  Print(nullptr, nullptr, nullptr, nullptr, IsPreview(forPreview),
   4987        IsForWindowDotPrint::Yes, nullptr, nullptr, aError);
   4988 #endif
   4989 }
   4990 
   4991 class MOZ_RAII AutoModalState {
   4992 public:
   4993  explicit AutoModalState(nsGlobalWindowOuter& aWin)
   4994      : mModalStateWin(aWin.EnterModalState()) {}
   4995 
   4996  ~AutoModalState() {
   4997    if (mModalStateWin) {
   4998      mModalStateWin->LeaveModalState();
   4999    }
   5000  }
   5001 
   5002  RefPtr<nsGlobalWindowOuter> mModalStateWin;
   5003 };
   5004 
   5005 Nullable<WindowProxyHolder> nsGlobalWindowOuter::Print(
   5006    nsIPrintSettings* aPrintSettings, RemotePrintJobChild* aRemotePrintJob,
   5007    nsIWebProgressListener* aListener, nsIDocShell* aDocShellToCloneInto,
   5008    IsPreview aIsPreview, IsForWindowDotPrint aForWindowDotPrint,
   5009    PrintPreviewResolver&& aPrintPreviewCallback,
   5010    RefPtr<BrowsingContext>* aCachedBrowsingContext, ErrorResult& aError) {
   5011 #ifdef NS_PRINTING
   5012  nsCOMPtr<nsIPrintSettingsService> printSettingsService =
   5013      do_GetService("@mozilla.org/gfx/printsettings-service;1");
   5014  if (!printSettingsService) {
   5015    // we currently return here in headless mode - should we?
   5016    aError.ThrowNotSupportedError("No print settings service");
   5017    return nullptr;
   5018  }
   5019 
   5020  nsCOMPtr<nsIPrintSettings> ps = aPrintSettings;
   5021  if (!ps) {
   5022    // We shouldn't need this once bug 1776169 is fixed.
   5023    printSettingsService->GetDefaultPrintSettingsForPrinting(
   5024        getter_AddRefs(ps));
   5025  }
   5026 
   5027  RefPtr<Document> docToPrint = mDoc;
   5028  if (NS_WARN_IF(!docToPrint)) {
   5029    aError.ThrowNotSupportedError("Document is gone");
   5030    return nullptr;
   5031  }
   5032 
   5033  RefPtr<BrowsingContext> sourceBC = docToPrint->GetBrowsingContext();
   5034  MOZ_DIAGNOSTIC_ASSERT(sourceBC);
   5035  if (!sourceBC) {
   5036    aError.ThrowNotSupportedError("No browsing context for source document");
   5037    return nullptr;
   5038  }
   5039 
   5040  nsAutoSyncOperation sync(docToPrint, SyncOperationBehavior::eAllowInput);
   5041  AutoModalState modalState(*this);
   5042 
   5043  nsCOMPtr<nsIDocumentViewer> viewer;
   5044  RefPtr<BrowsingContext> bc;
   5045  bool hasPrintCallbacks = false;
   5046  bool wasStaticDocument = docToPrint->IsStaticDocument();
   5047  bool usingCachedBrowsingContext = false;
   5048  if (aCachedBrowsingContext && *aCachedBrowsingContext) {
   5049    MOZ_ASSERT(!wasStaticDocument,
   5050               "Why pass in non-empty aCachedBrowsingContext if original "
   5051               "document is already static?");
   5052    if (!wasStaticDocument) {
   5053      // The passed in document is not a static clone and the caller passed in a
   5054      // static clone to reuse, so swap it in.
   5055      docToPrint = (*aCachedBrowsingContext)->GetDocument();
   5056      MOZ_ASSERT(docToPrint);
   5057      MOZ_ASSERT(docToPrint->IsStaticDocument());
   5058      wasStaticDocument = true;
   5059      usingCachedBrowsingContext = true;
   5060    }
   5061  }
   5062  if (wasStaticDocument) {
   5063    if (aForWindowDotPrint == IsForWindowDotPrint::Yes) {
   5064      aError.ThrowNotSupportedError(
   5065          "Calling print() from a print preview is unsupported, did you intend "
   5066          "to call printPreview() instead?");
   5067      return nullptr;
   5068    }
   5069    if (usingCachedBrowsingContext) {
   5070      bc = docToPrint->GetBrowsingContext();
   5071    } else {
   5072      // We're already a print preview window, just reuse our browsing context /
   5073      // content viewer.
   5074      bc = sourceBC;
   5075    }
   5076    nsCOMPtr<nsIDocShell> docShell = bc->GetDocShell();
   5077    if (!docShell) {
   5078      aError.ThrowNotSupportedError("No docshell");
   5079      return nullptr;
   5080    }
   5081    // We could handle this if needed.
   5082    if (aDocShellToCloneInto && aDocShellToCloneInto != docShell) {
   5083      aError.ThrowNotSupportedError(
   5084          "We don't handle cloning a print preview doc into a different "
   5085          "docshell");
   5086      return nullptr;
   5087    }
   5088    docShell->GetDocViewer(getter_AddRefs(viewer));
   5089    MOZ_DIAGNOSTIC_ASSERT(viewer);
   5090  } else {
   5091    if (aDocShellToCloneInto) {
   5092      // Ensure the content viewer is created if needed.
   5093      (void)aDocShellToCloneInto->GetDocument();
   5094      bc = aDocShellToCloneInto->GetBrowsingContext();
   5095    } else {
   5096      AutoNoJSAPI nojsapi;
   5097      auto printKind = aForWindowDotPrint == IsForWindowDotPrint::Yes
   5098                           ? PrintKind::WindowDotPrint
   5099                           : PrintKind::InternalPrint;
   5100      // For PrintKind::WindowDotPrint, this call will not only make the parent
   5101      // process create a CanonicalBrowsingContext for the returned `bc`, but
   5102      // it will also make the parent process initiate the print/print preview.
   5103      // See the handling of OPEN_PRINT_BROWSER in browser.js.
   5104      aError = OpenInternal(""_ns, u""_ns, u""_ns,
   5105                            false,    // aDialog
   5106                            true,     // aCalledNoScript
   5107                            false,    // aDoJSFixups
   5108                            true,     // aNavigate
   5109                            nullptr,  // No args
   5110                            nullptr,  // aLoadState
   5111                            false,    // aForceNoOpener
   5112                            printKind, getter_AddRefs(bc));
   5113      if (NS_WARN_IF(aError.Failed())) {
   5114        return nullptr;
   5115      }
   5116      if (aCachedBrowsingContext) {
   5117        MOZ_ASSERT(!*aCachedBrowsingContext);
   5118        *aCachedBrowsingContext = bc;
   5119      }
   5120    }
   5121    if (!bc) {
   5122      aError.ThrowNotAllowedError("No browsing context");
   5123      return nullptr;
   5124    }
   5125 
   5126    (void)bc->Top()->SetIsPrinting(true);
   5127    nsCOMPtr<nsIDocShell> cloneDocShell = bc->GetDocShell();
   5128    MOZ_DIAGNOSTIC_ASSERT(cloneDocShell);
   5129    cloneDocShell->GetDocViewer(getter_AddRefs(viewer));
   5130    MOZ_DIAGNOSTIC_ASSERT(viewer);
   5131    if (!viewer) {
   5132      aError.ThrowNotSupportedError("Didn't end up with a content viewer");
   5133      return nullptr;
   5134    }
   5135 
   5136    if (bc != sourceBC) {
   5137      MOZ_ASSERT(bc->IsTopContent());
   5138      // If we are cloning from a document in a different BrowsingContext, we
   5139      // need to make sure to copy over our opener policy information from that
   5140      // BrowsingContext. In the case where the source is an iframe, this
   5141      // information needs to be copied from the toplevel source
   5142      // BrowsingContext, as we may be making a static clone of a single
   5143      // subframe.
   5144      MOZ_ALWAYS_SUCCEEDS(
   5145          bc->SetOpenerPolicy(sourceBC->Top()->GetOpenerPolicy()));
   5146      MOZ_DIAGNOSTIC_ASSERT(bc->Group() == sourceBC->Group());
   5147    }
   5148 
   5149    if (RefPtr<Document> doc = viewer->GetDocument()) {
   5150      if (doc->IsShowing()) {
   5151        // We're going to drop this document on the floor, in the SetDocument
   5152        // call below. Make sure to run OnPageHide() to keep state consistent
   5153        // and avoids assertions in the document destructor.
   5154        doc->OnPageHide(false, nullptr);
   5155      }
   5156    }
   5157 
   5158    AutoPrintEventDispatcher dispatcher(*docToPrint);
   5159 
   5160    nsAutoScriptBlocker blockScripts;
   5161    RefPtr<Document> clone = docToPrint->CreateStaticClone(
   5162        cloneDocShell, viewer, ps, &hasPrintCallbacks);
   5163    if (!clone) {
   5164      aError.ThrowNotSupportedError("Clone operation for printing failed");
   5165      return nullptr;
   5166    }
   5167  }
   5168 
   5169  nsCOMPtr<nsIWebBrowserPrint> webBrowserPrint = do_QueryInterface(viewer);
   5170  if (!webBrowserPrint) {
   5171    aError.ThrowNotSupportedError(
   5172        "Content viewer didn't implement nsIWebBrowserPrint");
   5173    return nullptr;
   5174  }
   5175  bool closeWindowAfterPrint;
   5176  if (wasStaticDocument) {
   5177    // Here the document was a static clone to begin with that this code did not
   5178    // create, so we should not clean it up.
   5179    // The exception is if we're using the passed-in aCachedBrowsingContext, in
   5180    // which case this is the second print with this static document clone that
   5181    // we created the first time through, and we are responsible for cleaning it
   5182    // up. There's also an exception if we're directly using the system print
   5183    // dialog rather than our preview panel, because in this case the preview
   5184    // will not take care of cleaning up the cloned doc.
   5185    closeWindowAfterPrint =
   5186        usingCachedBrowsingContext || StaticPrefs::print_prefer_system_dialog();
   5187  } else {
   5188    // In this case the document was not a static clone, so we made a static
   5189    // clone for printing purposes and must clean it up after the print is done.
   5190    // The exception is if aCachedBrowsingContext is non-NULL, meaning the
   5191    // caller is intending to print this document again, so we need to defer the
   5192    // cleanup until after the second print.
   5193    closeWindowAfterPrint = !aCachedBrowsingContext;
   5194  }
   5195  webBrowserPrint->SetCloseWindowAfterPrint(closeWindowAfterPrint);
   5196 
   5197  // For window.print(), we postpone making these calls until the round-trip to
   5198  // the parent process (triggered by the OpenInternal call above) calls us
   5199  // again. Only a call from the parent can provide a valid nsPrintSettings
   5200  // object and RemotePrintJobChild object.
   5201  if (aForWindowDotPrint == IsForWindowDotPrint::No) {
   5202    if (aIsPreview == IsPreview::Yes) {
   5203      aError = webBrowserPrint->PrintPreview(ps, aListener,
   5204                                             std::move(aPrintPreviewCallback));
   5205      if (aError.Failed()) {
   5206        return nullptr;
   5207      }
   5208    } else {
   5209      // Historically we've eaten this error.
   5210      webBrowserPrint->Print(ps, aRemotePrintJob, aListener);
   5211    }
   5212  }
   5213 
   5214  // Check whether we're in a case where we need to block in order for
   5215  // window.print() to function properly:
   5216  const bool shouldBlock = [&] {
   5217    if (aForWindowDotPrint == IsForWindowDotPrint::No) {
   5218      // We're not doing window.print; no need to block.
   5219      return false;
   5220    }
   5221 
   5222    // When window.print() spawns a print dialog (either our own tab-modal
   5223    // dialog or the system-print dialog), we usually want window.print() to
   5224    // block until the print dialog is hidden. But we can't really do that if
   5225    // we have print callbacks (mozPrintCallback), because we are inside a sync
   5226    // operation, and we want to run microtasks / etc that the print callbacks
   5227    // may create. It is really awkward to have this subtle behavior
   5228    // difference...
   5229    if (aIsPreview == IsPreview::Yes ||
   5230        StaticPrefs::print_prefer_system_dialog()) {
   5231      return !hasPrintCallbacks;
   5232    }
   5233 
   5234    // We also want to allow window.print() to block for fuzzing, so that
   5235    // fuzzers can test either behavior without needing to interact with a
   5236    // dialog.
   5237    return StaticPrefs::dom_window_print_fuzzing_block_while_printing();
   5238  }();
   5239 
   5240  if (shouldBlock) {
   5241    SpinEventLoopUntil("nsGlobalWindowOuter::Print"_ns,
   5242                       [&] { return bc->IsDiscarded(); });
   5243  }
   5244 
   5245  return WindowProxyHolder(std::move(bc));
   5246 #else
   5247  return nullptr;
   5248 #endif  // NS_PRINTING
   5249 }
   5250 
   5251 void nsGlobalWindowOuter::MoveToOuter(int32_t aXPos, int32_t aYPos,
   5252                                      CallerType aCallerType,
   5253                                      ErrorResult& aError) {
   5254  /*
   5255   * If caller is not chrome and the user has not explicitly exempted the site,
   5256   * prevent window.moveTo() by exiting early
   5257   */
   5258 
   5259  if (!CanMoveResizeWindows(aCallerType, true, aError)) {
   5260    return;
   5261  }
   5262 
   5263  nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
   5264  if (!treeOwnerAsWin) {
   5265    aError.Throw(NS_ERROR_FAILURE);
   5266    return;
   5267  }
   5268 
   5269  // We need to do the same transformation GetScreenXY does.
   5270  RefPtr<nsPresContext> presContext = mDocShell->GetPresContext();
   5271  if (!presContext) {
   5272    return;
   5273  }
   5274 
   5275  CSSIntPoint cssPos(aXPos, aYPos);
   5276  CheckSecurityLeftAndTop(&cssPos.x.value, &cssPos.y.value, aCallerType);
   5277 
   5278  nsDeviceContext* context = presContext->DeviceContext();
   5279 
   5280  auto devPos = LayoutDeviceIntPoint::FromAppUnitsRounded(
   5281      CSSIntPoint::ToAppUnits(cssPos), context->AppUnitsPerDevPixel());
   5282 
   5283  aError = treeOwnerAsWin->SetPosition(devPos.x, devPos.y);
   5284  CheckForDPIChange();
   5285 }
   5286 
   5287 void nsGlobalWindowOuter::MoveByOuter(int32_t aXDif, int32_t aYDif,
   5288                                      CallerType aCallerType,
   5289                                      ErrorResult& aError) {
   5290  /*
   5291   * If caller is not chrome and the user has not explicitly exempted the site,
   5292   * prevent window.moveBy() by exiting early
   5293   */
   5294 
   5295  if (!CanMoveResizeWindows(aCallerType, true, aError)) {
   5296    return;
   5297  }
   5298 
   5299  nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
   5300  if (!treeOwnerAsWin) {
   5301    aError.Throw(NS_ERROR_FAILURE);
   5302    return;
   5303  }
   5304 
   5305  // To do this correctly we have to convert what we get from GetPosition
   5306  // into CSS pixels, add the arguments, do the security check, and
   5307  // then convert back to device pixels for the call to SetPosition.
   5308 
   5309  int32_t x, y;
   5310  aError = treeOwnerAsWin->GetPosition(&x, &y);
   5311  if (aError.Failed()) {
   5312    return;
   5313  }
   5314 
   5315  auto cssScale = CSSToDevScaleForBaseWindow(treeOwnerAsWin);
   5316  CSSIntPoint cssPos = RoundedToInt(treeOwnerAsWin->GetPosition() / cssScale);
   5317 
   5318  cssPos.x += aXDif;
   5319  cssPos.y += aYDif;
   5320 
   5321  CheckSecurityLeftAndTop(&cssPos.x.value, &cssPos.y.value, aCallerType);
   5322 
   5323  LayoutDeviceIntPoint newDevPos = RoundedToInt(cssPos * cssScale);
   5324  aError = treeOwnerAsWin->SetPosition(newDevPos.x, newDevPos.y);
   5325 
   5326  CheckForDPIChange();
   5327 }
   5328 
   5329 nsresult nsGlobalWindowOuter::MoveBy(int32_t aXDif, int32_t aYDif) {
   5330  ErrorResult rv;
   5331  MoveByOuter(aXDif, aYDif, CallerType::System, rv);
   5332 
   5333  return rv.StealNSResult();
   5334 }
   5335 
   5336 void nsGlobalWindowOuter::ResizeToOuter(int32_t aWidth, int32_t aHeight,
   5337                                        CallerType aCallerType,
   5338                                        ErrorResult& aError) {
   5339  /*
   5340   * If caller is not chrome and the user has not explicitly exempted the site,
   5341   * prevent window.resizeTo() by exiting early
   5342   */
   5343 
   5344  if (!CanMoveResizeWindows(aCallerType, false, aError)) {
   5345    return;
   5346  }
   5347 
   5348  nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
   5349  if (!treeOwnerAsWin) {
   5350    aError.Throw(NS_ERROR_FAILURE);
   5351    return;
   5352  }
   5353 
   5354  CSSIntSize cssSize(aWidth, aHeight);
   5355 
   5356  if (mBrowsingContext->GetIsDocumentPiP()) {
   5357    if (Maybe<CSSIntRect> screen =
   5358            DocumentPictureInPicture::GetScreenRect(this)) {
   5359      CSSIntSize maxSize =
   5360          DocumentPictureInPicture::CalcMaxDimensions(screen.value());
   5361      cssSize.width = std::min(cssSize.width, maxSize.width);
   5362      cssSize.height = std::min(cssSize.height, maxSize.height);
   5363    } else {
   5364      aError.Throw(NS_ERROR_FAILURE);
   5365      return;
   5366    }
   5367  }
   5368 
   5369  CheckSecurityWidthAndHeight(&cssSize.width, &cssSize.height, aCallerType);
   5370 
   5371  LayoutDeviceIntSize devSize =
   5372      RoundedToInt(cssSize * CSSToDevScaleForBaseWindow(treeOwnerAsWin));
   5373  aError = treeOwnerAsWin->SetSize(devSize.width, devSize.height, true);
   5374 
   5375  CheckForDPIChange();
   5376 }
   5377 
   5378 void nsGlobalWindowOuter::ResizeByOuter(int32_t aWidthDif, int32_t aHeightDif,
   5379                                        CallerType aCallerType,
   5380                                        ErrorResult& aError) {
   5381  /*
   5382   * If caller is not chrome and the user has not explicitly exempted the site,
   5383   * prevent window.resizeBy() by exiting early
   5384   */
   5385 
   5386  if (!CanMoveResizeWindows(aCallerType, false, aError)) {
   5387    return;
   5388  }
   5389 
   5390  nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
   5391  if (!treeOwnerAsWin) {
   5392    aError.Throw(NS_ERROR_FAILURE);
   5393    return;
   5394  }
   5395 
   5396  LayoutDeviceIntSize size = treeOwnerAsWin->GetSize();
   5397 
   5398  // To do this correctly we have to convert what we got from GetSize
   5399  // into CSS pixels, add the arguments, do the security check, and
   5400  // then convert back to device pixels for the call to SetSize.
   5401 
   5402  auto scale = CSSToDevScaleForBaseWindow(treeOwnerAsWin);
   5403  CSSIntSize cssSize = RoundedToInt(size / scale);
   5404 
   5405  cssSize.width += aWidthDif;
   5406  cssSize.height += aHeightDif;
   5407 
   5408  if (mBrowsingContext->GetIsDocumentPiP()) {
   5409    if (Maybe<CSSIntRect> screen =
   5410            DocumentPictureInPicture::GetScreenRect(this)) {
   5411      CSSIntSize maxSize =
   5412          DocumentPictureInPicture::CalcMaxDimensions(screen.value());
   5413      cssSize.width = std::min(cssSize.width, maxSize.width);
   5414      cssSize.height = std::min(cssSize.height, maxSize.height);
   5415    } else {
   5416      aError.Throw(NS_ERROR_FAILURE);
   5417      return;
   5418    }
   5419  }
   5420 
   5421  CheckSecurityWidthAndHeight(&cssSize.width, &cssSize.height, aCallerType);
   5422 
   5423  LayoutDeviceIntSize newDevSize = RoundedToInt(cssSize * scale);
   5424 
   5425  aError = treeOwnerAsWin->SetSize(newDevSize.width, newDevSize.height, true);
   5426 
   5427  CheckForDPIChange();
   5428 }
   5429 
   5430 void nsGlobalWindowOuter::SizeToContentOuter(
   5431    const SizeToContentConstraints& aConstraints, ErrorResult& aError) {
   5432  if (!mDocShell) {
   5433    return;
   5434  }
   5435 
   5436  if (mBrowsingContext->IsSubframe()) {
   5437    return;
   5438  }
   5439 
   5440  // The content viewer does a check to make sure that it's a content
   5441  // viewer for a toplevel docshell.
   5442  nsCOMPtr<nsIDocumentViewer> viewer;
   5443  mDocShell->GetDocViewer(getter_AddRefs(viewer));
   5444  if (!viewer) {
   5445    return aError.Throw(NS_ERROR_FAILURE);
   5446  }
   5447 
   5448  auto contentSize = viewer->GetContentSize(
   5449      aConstraints.mMaxWidth, aConstraints.mMaxHeight, aConstraints.mPrefWidth);
   5450  if (!contentSize) {
   5451    return aError.Throw(NS_ERROR_FAILURE);
   5452  }
   5453 
   5454  nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner();
   5455  if (!treeOwner) {
   5456    return aError.Throw(NS_ERROR_FAILURE);
   5457  }
   5458 
   5459  // Don't use DevToCSSIntPixelsForBaseWindow() nor
   5460  // CSSToDevIntPixelsForBaseWindow() here because contentSize is comes from
   5461  // nsIDocumentViewer::GetContentSize() and it's computed with nsPresContext so
   5462  // that we need to work with nsPresContext here too.
   5463  RefPtr<nsPresContext> presContext = viewer->GetPresContext();
   5464  MOZ_ASSERT(
   5465      presContext,
   5466      "Should be non-nullptr if nsIDocumentViewer::GetContentSize() succeeded");
   5467  CSSIntSize cssSize = *contentSize;
   5468 
   5469  LayoutDeviceIntSize newDevSize(
   5470      presContext->CSSPixelsToDevPixels(cssSize.width),
   5471      presContext->CSSPixelsToDevPixels(cssSize.height));
   5472 
   5473  nsCOMPtr<nsIDocShell> docShell = mDocShell;
   5474  aError =
   5475      treeOwner->SizeShellTo(docShell, newDevSize.width, newDevSize.height);
   5476 }
   5477 
   5478 already_AddRefed<nsPIWindowRoot> nsGlobalWindowOuter::GetTopWindowRoot() {
   5479  nsPIDOMWindowOuter* piWin = GetPrivateRoot();
   5480  if (!piWin) {
   5481    return nullptr;
   5482  }
   5483 
   5484  nsCOMPtr<nsPIWindowRoot> window =
   5485      do_QueryInterface(piWin->GetChromeEventHandler());
   5486  return window.forget();
   5487 }
   5488 
   5489 void nsGlobalWindowOuter::FirePopupBlockedEvent(
   5490    nsIURI* aPopupURI, const nsAString& aPopupWindowName,
   5491    const nsAString& aPopupWindowFeatures) {
   5492  Document* doc = GetDoc();
   5493  if (!doc) {
   5494    return;
   5495  }
   5496 
   5497  // Fire a "DOMPopupBlocked" event so that the UI can hear about
   5498  // blocked popups.
   5499  PopupBlockedEventInit init;
   5500  init.mBubbles = true;
   5501  init.mCancelable = true;
   5502  // XXX: This is a different object, but webidl requires an inner window here
   5503  // now.
   5504  init.mRequestingWindow = GetCurrentInnerWindowInternal(this);
   5505  init.mPopupWindowURI = aPopupURI;
   5506  init.mPopupWindowName = aPopupWindowName;
   5507  init.mPopupWindowFeatures = aPopupWindowFeatures;
   5508 
   5509  RefPtr<PopupBlockedEvent> event =
   5510      PopupBlockedEvent::Constructor(doc, u"DOMPopupBlocked"_ns, init);
   5511  event->SetTrusted(true);
   5512 
   5513  doc->DispatchEvent(*event);
   5514 }
   5515 
   5516 void nsGlobalWindowOuter::FireRedirectBlockedEvent(nsIURI* aRedirectURI) {
   5517  Document* doc = GetDoc();
   5518  if (!doc) {
   5519    return;
   5520  }
   5521 
   5522  // Fire a "DOMRedirectBlocked" event so that the UI can hear about
   5523  // blocked redirects.
   5524  RedirectBlockedEventInit init;
   5525  init.mBubbles = true;
   5526  init.mCancelable = true;
   5527  init.mRequestingWindow = GetCurrentInnerWindowInternal(this);
   5528  init.mRedirectURI = aRedirectURI;
   5529 
   5530  RefPtr<RedirectBlockedEvent> event =
   5531      RedirectBlockedEvent::Constructor(doc, u"DOMRedirectBlocked"_ns, init);
   5532  event->SetTrusted(true);
   5533  event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true;
   5534 
   5535  doc->DispatchEvent(*event);
   5536 }
   5537 
   5538 // static
   5539 bool nsGlobalWindowOuter::CanSetProperty(const char* aPrefName) {
   5540  // Chrome can set any property.
   5541  if (nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
   5542    return true;
   5543  }
   5544 
   5545  // If the pref is set to true, we can not set the property
   5546  // and vice versa.
   5547  return !Preferences::GetBool(aPrefName, true);
   5548 }
   5549 
   5550 Nullable<WindowProxyHolder> nsGlobalWindowOuter::OpenOuter(
   5551    const nsAString& aUrl, const nsAString& aName, const nsAString& aOptions,
   5552    ErrorResult& aError) {
   5553  RefPtr<BrowsingContext> bc;
   5554  NS_ConvertUTF16toUTF8 url(aUrl);
   5555  nsresult rv = OpenJS(url, aName, aOptions, getter_AddRefs(bc));
   5556  if (rv == NS_ERROR_MALFORMED_URI) {
   5557    aError.ThrowSyntaxError("Unable to open a window with invalid URL '"_ns +
   5558                            url + "'."_ns);
   5559    return nullptr;
   5560  }
   5561 
   5562  // XXX Is it possible that some internal errors are thrown here?
   5563  aError = rv;
   5564 
   5565  if (!bc) {
   5566    return nullptr;
   5567  }
   5568  return WindowProxyHolder(std::move(bc));
   5569 }
   5570 
   5571 nsresult nsGlobalWindowOuter::Open(const nsACString& aUrl,
   5572                                   const nsAString& aName,
   5573                                   const nsAString& aOptions,
   5574                                   nsDocShellLoadState* aLoadState,
   5575                                   bool aForceNoOpener,
   5576                                   BrowsingContext** _retval) {
   5577  return OpenInternal(aUrl, aName, aOptions,
   5578                      false,    // aDialog
   5579                      true,     // aCalledNoScript
   5580                      false,    // aDoJSFixups
   5581                      true,     // aNavigate
   5582                      nullptr,  // No args
   5583                      aLoadState, aForceNoOpener, PrintKind::None, _retval);
   5584 }
   5585 
   5586 nsresult nsGlobalWindowOuter::OpenJS(const nsACString& aUrl,
   5587                                     const nsAString& aName,
   5588                                     const nsAString& aOptions,
   5589                                     BrowsingContext** _retval) {
   5590  return OpenInternal(aUrl, aName, aOptions,
   5591                      false,    // aDialog
   5592                      false,    // aCalledNoScript
   5593                      true,     // aDoJSFixups
   5594                      true,     // aNavigate
   5595                      nullptr,  // No args
   5596                      nullptr,  // aLoadState
   5597                      false,    // aForceNoOpener
   5598                      PrintKind::None, _retval);
   5599 }
   5600 
   5601 // like Open, but attaches to the new window any extra parameters past
   5602 // [features] as a JS property named "arguments"
   5603 nsresult nsGlobalWindowOuter::OpenDialog(const nsACString& aUrl,
   5604                                         const nsAString& aName,
   5605                                         const nsAString& aOptions,
   5606                                         nsIArray* aArguments,
   5607                                         BrowsingContext** _retval) {
   5608  return OpenInternal(aUrl, aName, aOptions,
   5609                      true,        // aDialog
   5610                      true,        // aCalledNoScript
   5611                      false,       // aDoJSFixups
   5612                      true,        // aNavigate
   5613                      aArguments,  // Arguments
   5614                      nullptr,     // aLoadState
   5615                      false,       // aForceNoOpener
   5616                      PrintKind::None, _retval);
   5617 }
   5618 
   5619 // Like Open, but passes aNavigate=false.
   5620 /* virtual */
   5621 nsresult nsGlobalWindowOuter::OpenNoNavigate(const nsACString& aUrl,
   5622                                             const nsAString& aName,
   5623                                             const nsAString& aOptions,
   5624                                             BrowsingContext** _retval) {
   5625  return OpenInternal(aUrl, aName, aOptions,
   5626                      false,    // aDialog
   5627                      true,     // aCalledNoScript
   5628                      false,    // aDoJSFixups
   5629                      false,    // aNavigate
   5630                      nullptr,  // No args
   5631                      nullptr,  // aLoadState
   5632                      false,    // aForceNoOpener
   5633                      PrintKind::None, _retval);
   5634 }
   5635 
   5636 Nullable<WindowProxyHolder> nsGlobalWindowOuter::OpenDialogOuter(
   5637    JSContext* aCx, const nsAString& aUrl, const nsAString& aName,
   5638    const nsAString& aOptions, const Sequence<JS::Value>& aExtraArgument,
   5639    ErrorResult& aError) {
   5640  nsCOMPtr<nsIJSArgArray> argvArray;
   5641  aError =
   5642      NS_CreateJSArgv(aCx, aExtraArgument.Length(), aExtraArgument.Elements(),
   5643                      getter_AddRefs(argvArray));
   5644  if (aError.Failed()) {
   5645    return nullptr;
   5646  }
   5647 
   5648  RefPtr<BrowsingContext> dialog;
   5649  aError = OpenInternal(NS_ConvertUTF16toUTF8(aUrl), aName, aOptions,
   5650                        true,       // aDialog
   5651                        false,      // aCalledNoScript
   5652                        false,      // aDoJSFixups
   5653                        true,       // aNavigate
   5654                        argvArray,  // Arguments
   5655                        nullptr,    // aLoadState
   5656                        false,      // aForceNoOpener
   5657                        PrintKind::None, getter_AddRefs(dialog));
   5658  if (!dialog) {
   5659    return nullptr;
   5660  }
   5661  return WindowProxyHolder(std::move(dialog));
   5662 }
   5663 
   5664 WindowProxyHolder nsGlobalWindowOuter::GetFramesOuter() {
   5665  RefPtr<nsPIDOMWindowOuter> frames(this);
   5666  FlushPendingNotifications(FlushType::ContentAndNotify);
   5667  return WindowProxyHolder(mBrowsingContext);
   5668 }
   5669 
   5670 /* static */
   5671 bool nsGlobalWindowOuter::GatherPostMessageData(
   5672    JSContext* aCx, const nsAString& aTargetOrigin, BrowsingContext** aSource,
   5673    nsAString& aOrigin, nsIURI** aTargetOriginURI,
   5674    nsIPrincipal** aCallerPrincipal, nsGlobalWindowInner** aCallerInnerWindow,
   5675    nsIURI** aCallerURI, Maybe<nsID>* aCallerAgentClusterId,
   5676    nsACString* aScriptLocation, ErrorResult& aError) {
   5677  //
   5678  // Window.postMessage is an intentional subversion of the same-origin policy.
   5679  // As such, this code must be particularly careful in the information it
   5680  // exposes to calling code.
   5681  //
   5682  // http://www.whatwg.org/specs/web-apps/current-work/multipage/section-crossDocumentMessages.html
   5683  //
   5684 
   5685  // First, get the caller's window
   5686  RefPtr<nsGlobalWindowInner> callerInnerWin =
   5687      nsContentUtils::IncumbentInnerWindow();
   5688  nsIPrincipal* callerPrin;
   5689  if (callerInnerWin) {
   5690    RefPtr<Document> doc = callerInnerWin->GetExtantDoc();
   5691    if (!doc) {
   5692      return false;
   5693    }
   5694    NS_IF_ADDREF(*aCallerURI = doc->GetDocumentURI());
   5695 
   5696    // Compute the caller's origin either from its principal or, in the case the
   5697    // principal doesn't carry a URI (e.g. the system principal), the caller's
   5698    // document.  We must get this now instead of when the event is created and
   5699    // dispatched, because ultimately it is the identity of the calling window
   5700    // *now* that determines who sent the message (and not an identity which
   5701    // might have changed due to intervening navigations).
   5702    callerPrin = callerInnerWin->GetPrincipal();
   5703  } else {
   5704    // In case the global is not a window, it can be a sandbox, and the
   5705    // sandbox's principal can be used for the security check.
   5706    nsIGlobalObject* global = GetIncumbentGlobal();
   5707    NS_ASSERTION(global, "Why is there no global object?");
   5708    callerPrin = global->PrincipalOrNull();
   5709    if (callerPrin) {
   5710      BasePrincipal::Cast(callerPrin)->GetScriptLocation(*aScriptLocation);
   5711    }
   5712  }
   5713  if (!callerPrin) {
   5714    return false;
   5715  }
   5716 
   5717  // if the principal has a URI, use that to generate the origin
   5718  if (!callerPrin->IsSystemPrincipal()) {
   5719    nsAutoCString webExposedOriginSerialization;
   5720    callerPrin->GetWebExposedOriginSerialization(webExposedOriginSerialization);
   5721    CopyUTF8toUTF16(webExposedOriginSerialization, aOrigin);
   5722  } else if (callerInnerWin) {
   5723    if (!*aCallerURI) {
   5724      return false;
   5725    }
   5726    // otherwise use the URI of the document to generate origin
   5727    nsContentUtils::GetWebExposedOriginSerialization(*aCallerURI, aOrigin);
   5728  } else {
   5729    // in case of a sandbox with a system principal origin can be empty
   5730    if (!callerPrin->IsSystemPrincipal()) {
   5731      return false;
   5732    }
   5733  }
   5734  NS_IF_ADDREF(*aCallerPrincipal = callerPrin);
   5735 
   5736  // "/" indicates same origin as caller, "*" indicates no specific origin is
   5737  // required.
   5738  if (!aTargetOrigin.EqualsASCII("/") && !aTargetOrigin.EqualsASCII("*")) {
   5739    nsCOMPtr<nsIURI> targetOriginURI;
   5740    if (NS_FAILED(NS_NewURI(getter_AddRefs(targetOriginURI), aTargetOrigin))) {
   5741      aError.Throw(NS_ERROR_DOM_SYNTAX_ERR);
   5742      return false;
   5743    }
   5744 
   5745    nsresult rv = NS_MutateURI(targetOriginURI)
   5746                      .SetUserPass(""_ns)
   5747                      .SetPathQueryRef(""_ns)
   5748                      .Finalize(aTargetOriginURI);
   5749    if (NS_FAILED(rv)) {
   5750      return false;
   5751    }
   5752  }
   5753 
   5754  if (!nsContentUtils::IsCallerChrome() && callerInnerWin &&
   5755      callerInnerWin->GetOuterWindowInternal()) {
   5756    NS_ADDREF(*aSource = callerInnerWin->GetOuterWindowInternal()
   5757                             ->GetBrowsingContext());
   5758  } else {
   5759    *aSource = nullptr;
   5760  }
   5761 
   5762  if (aCallerAgentClusterId && callerInnerWin &&
   5763      callerInnerWin->GetDocGroup()) {
   5764    *aCallerAgentClusterId =
   5765        Some(callerInnerWin->GetDocGroup()->AgentClusterId());
   5766  }
   5767 
   5768  callerInnerWin.forget(aCallerInnerWindow);
   5769 
   5770  return true;
   5771 }
   5772 
   5773 bool nsGlobalWindowOuter::GetPrincipalForPostMessage(
   5774    const nsAString& aTargetOrigin, nsIURI* aTargetOriginURI,
   5775    nsIPrincipal* aCallerPrincipal, nsIPrincipal& aSubjectPrincipal,
   5776    nsIPrincipal** aProvidedPrincipal) {
   5777  //
   5778  // Window.postMessage is an intentional subversion of the same-origin policy.
   5779  // As such, this code must be particularly careful in the information it
   5780  // exposes to calling code.
   5781  //
   5782  // http://www.whatwg.org/specs/web-apps/current-work/multipage/section-crossDocumentMessages.html
   5783  //
   5784 
   5785  // Convert the provided origin string into a URI for comparison purposes.
   5786  nsCOMPtr<nsIPrincipal> providedPrincipal;
   5787 
   5788  if (aTargetOrigin.EqualsASCII("/")) {
   5789    providedPrincipal = aCallerPrincipal;
   5790  }
   5791  // "*" indicates no specific origin is required.
   5792  else if (!aTargetOrigin.EqualsASCII("*")) {
   5793    OriginAttributes attrs = aSubjectPrincipal.OriginAttributesRef();
   5794    if (aSubjectPrincipal.IsSystemPrincipal()) {
   5795      auto principal = BasePrincipal::Cast(GetPrincipal());
   5796 
   5797      if (attrs != principal->OriginAttributesRef()) {
   5798        nsAutoCString targetURL;
   5799        nsAutoCString sourceOrigin;
   5800        nsAutoCString targetOrigin;
   5801 
   5802        if (NS_FAILED(principal->GetAsciiSpec(targetURL)) ||
   5803            NS_FAILED(principal->GetOrigin(targetOrigin)) ||
   5804            NS_FAILED(aSubjectPrincipal.GetOrigin(sourceOrigin))) {
   5805          NS_WARNING("Failed to get source and target origins");
   5806          return false;
   5807        }
   5808 
   5809        nsContentUtils::LogSimpleConsoleError(
   5810            NS_ConvertUTF8toUTF16(nsPrintfCString(
   5811                R"(Attempting to post a message to window with url "%s" and )"
   5812                R"(origin "%s" from a system principal scope with mismatched )"
   5813                R"(origin "%s".)",
   5814                targetURL.get(), targetOrigin.get(), sourceOrigin.get())),
   5815            "DOM"_ns, !!principal->PrivateBrowsingId(),
   5816            principal->IsSystemPrincipal());
   5817 
   5818        attrs = principal->OriginAttributesRef();
   5819      }
   5820    }
   5821 
   5822    // Create a nsIPrincipal inheriting the app/browser attributes from the
   5823    // caller.
   5824    providedPrincipal =
   5825        BasePrincipal::CreateContentPrincipal(aTargetOriginURI, attrs);
   5826    if (NS_WARN_IF(!providedPrincipal)) {
   5827      return false;
   5828    }
   5829  } else {
   5830    // We still need to check the originAttributes if the target origin is '*'.
   5831    // But we will ingore the FPD here since the FPDs are possible to be
   5832    // different.
   5833    auto principal = BasePrincipal::Cast(GetPrincipal());
   5834    NS_ENSURE_TRUE(principal, false);
   5835 
   5836    OriginAttributes targetAttrs = principal->OriginAttributesRef();
   5837    OriginAttributes sourceAttrs = aSubjectPrincipal.OriginAttributesRef();
   5838    // We have to exempt the check of OA if the subject prioncipal is a system
   5839    // principal since there are many tests try to post messages to content from
   5840    // chrome with a mismatch OA. For example, using the ContentTask.spawn() to
   5841    // post a message into a private browsing window. The injected code in
   5842    // ContentTask.spawn() will be executed under the system principal and the
   5843    // OA of the system principal mismatches with the OA of a private browsing
   5844    // window.
   5845    MOZ_DIAGNOSTIC_ASSERT(aSubjectPrincipal.IsSystemPrincipal() ||
   5846                          sourceAttrs.EqualsIgnoringFPD(targetAttrs));
   5847 
   5848    // If 'privacy.firstparty.isolate.block_post_message' is true, we will block
   5849    // postMessage across different first party domains.
   5850    if (OriginAttributes::IsBlockPostMessageForFPI() &&
   5851        !aSubjectPrincipal.IsSystemPrincipal() &&
   5852        sourceAttrs.mFirstPartyDomain != targetAttrs.mFirstPartyDomain) {
   5853      return false;
   5854    }
   5855  }
   5856 
   5857  providedPrincipal.forget(aProvidedPrincipal);
   5858  return true;
   5859 }
   5860 
   5861 void nsGlobalWindowOuter::PostMessageMozOuter(JSContext* aCx,
   5862                                              JS::Handle<JS::Value> aMessage,
   5863                                              const nsAString& aTargetOrigin,
   5864                                              JS::Handle<JS::Value> aTransfer,
   5865                                              nsIPrincipal& aSubjectPrincipal,
   5866                                              ErrorResult& aError) {
   5867  RefPtr<BrowsingContext> sourceBc;
   5868  nsAutoString origin;
   5869  nsCOMPtr<nsIURI> targetOriginURI;
   5870  nsCOMPtr<nsIPrincipal> callerPrincipal;
   5871  RefPtr<nsGlobalWindowInner> callerInnerWindow;
   5872  nsCOMPtr<nsIURI> callerURI;
   5873  Maybe<nsID> callerAgentClusterId = Nothing();
   5874  nsAutoCString scriptLocation;
   5875  if (!GatherPostMessageData(
   5876          aCx, aTargetOrigin, getter_AddRefs(sourceBc), origin,
   5877          getter_AddRefs(targetOriginURI), getter_AddRefs(callerPrincipal),
   5878          getter_AddRefs(callerInnerWindow), getter_AddRefs(callerURI),
   5879          &callerAgentClusterId, &scriptLocation, aError)) {
   5880    return;
   5881  }
   5882 
   5883  nsCOMPtr<nsIPrincipal> providedPrincipal;
   5884  if (!GetPrincipalForPostMessage(aTargetOrigin, targetOriginURI,
   5885                                  callerPrincipal, aSubjectPrincipal,
   5886                                  getter_AddRefs(providedPrincipal))) {
   5887    return;
   5888  }
   5889 
   5890  // Create and asynchronously dispatch a runnable which will handle actual DOM
   5891  // event creation and dispatch.
   5892  RefPtr<PostMessageEvent> event = new PostMessageEvent(
   5893      sourceBc, origin, this, providedPrincipal,
   5894      callerInnerWindow ? callerInnerWindow->WindowID() : 0, callerURI,
   5895      scriptLocation, callerAgentClusterId);
   5896 
   5897  JS::CloneDataPolicy clonePolicy;
   5898 
   5899  if (GetDocGroup() && callerAgentClusterId.isSome() &&
   5900      GetDocGroup()->AgentClusterId().Equals(callerAgentClusterId.value())) {
   5901    clonePolicy.allowIntraClusterClonableSharedObjects();
   5902  }
   5903 
   5904  if (callerInnerWindow && callerInnerWindow->IsSharedMemoryAllowed()) {
   5905    clonePolicy.allowSharedMemoryObjects();
   5906  }
   5907 
   5908  event->Write(aCx, aMessage, aTransfer, clonePolicy, aError);
   5909  if (NS_WARN_IF(aError.Failed())) {
   5910    return;
   5911  }
   5912 
   5913  event->DispatchToTargetThread(aError);
   5914 }
   5915 
   5916 class nsCloseEvent : public Runnable {
   5917  RefPtr<nsGlobalWindowOuter> mWindow;
   5918  bool mIndirect;
   5919 
   5920  nsCloseEvent(nsGlobalWindowOuter* aWindow, bool aIndirect)
   5921      : mozilla::Runnable("nsCloseEvent"),
   5922        mWindow(aWindow),
   5923        mIndirect(aIndirect) {}
   5924 
   5925 public:
   5926  static nsresult PostCloseEvent(nsGlobalWindowOuter* aWindow, bool aIndirect) {
   5927    nsCOMPtr<nsIRunnable> ev = new nsCloseEvent(aWindow, aIndirect);
   5928    return aWindow->Dispatch(ev.forget());
   5929  }
   5930 
   5931  NS_IMETHOD Run() override {
   5932    if (mWindow) {
   5933      if (mIndirect) {
   5934        return PostCloseEvent(mWindow, false);
   5935      }
   5936      mWindow->ReallyCloseWindow();
   5937    }
   5938    return NS_OK;
   5939  }
   5940 };
   5941 
   5942 bool nsGlobalWindowOuter::CanClose() {
   5943  if (mIsChrome) {
   5944    nsCOMPtr<nsIBrowserDOMWindow> bwin = GetBrowserDOMWindow();
   5945 
   5946    bool canClose = true;
   5947    if (bwin && NS_SUCCEEDED(bwin->CanClose(&canClose))) {
   5948      return canClose;
   5949    }
   5950  }
   5951 
   5952  if (!mDocShell) {
   5953    return true;
   5954  }
   5955 
   5956  nsCOMPtr<nsIDocumentViewer> viewer;
   5957  mDocShell->GetDocViewer(getter_AddRefs(viewer));
   5958  if (viewer) {
   5959    bool canClose;
   5960    nsresult rv = viewer->PermitUnload(&canClose);
   5961    // PermitUnload can destroy the docshell.
   5962    if (!mDocShell || mDocShell->IsBeingDestroyed()) {
   5963      return true;
   5964    }
   5965    if (NS_SUCCEEDED(rv) && !canClose) {
   5966      return false;
   5967    }
   5968  }
   5969 
   5970  // If we still have to print, we delay the closing until print has happened.
   5971  if (mShouldDelayPrintUntilAfterLoad && mDelayedPrintUntilAfterLoad) {
   5972    mDelayedCloseForPrinting = true;
   5973    return false;
   5974  }
   5975 
   5976  return true;
   5977 }
   5978 
   5979 void nsGlobalWindowOuter::CloseOuter(bool aTrustedCaller) {
   5980  if (!mDocShell || IsInModalState() || mBrowsingContext->IsSubframe()) {
   5981    // window.close() is called on a frame in a frameset, on a window
   5982    // that's already closed, or on a window for which there's
   5983    // currently a modal dialog open. Ignore such calls.
   5984    return;
   5985  }
   5986 
   5987  if (mHavePendingClose) {
   5988    // We're going to be closed anyway; do nothing since we don't want
   5989    // to double-close
   5990    return;
   5991  }
   5992 
   5993  if (mBlockScriptedClosingFlag) {
   5994    // A script's popup has been blocked and we don't want
   5995    // the window to be closed directly after this event,
   5996    // so the user can see that there was a blocked popup.
   5997    return;
   5998  }
   5999 
   6000  // Don't allow scripts from content to close non-neterror windows that
   6001  // were not opened by script.
   6002  if (mDoc) {
   6003    nsAutoString url;
   6004    nsresult rv = mDoc->GetURL(url);
   6005    NS_ENSURE_SUCCESS_VOID(rv);
   6006 
   6007    RefPtr<ChildSHistory> csh =
   6008        nsDocShell::Cast(mDocShell)->GetSessionHistory();
   6009 
   6010    if (!StringBeginsWith(url, u"about:neterror"_ns) &&
   6011        // we want about:torconnect pages to be able to close themselves after
   6012        // bootstrap
   6013        !StringBeginsWith(url, u"about:torconnect"_ns) &&
   6014        !mBrowsingContext->GetTopLevelCreatedByWebContent() &&
   6015        !aTrustedCaller && csh && csh->Count() > 1) {
   6016      bool allowClose =
   6017          mAllowScriptsToClose ||
   6018          Preferences::GetBool("dom.allow_scripts_to_close_windows", true);
   6019      if (!allowClose) {
   6020        // We're blocking the close operation
   6021        // report localized error msg in JS console
   6022        nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
   6023                                        "DOM Window"_ns,
   6024                                        mDoc,  // Better name for the category?
   6025                                        nsContentUtils::eDOM_PROPERTIES,
   6026                                        "WindowCloseByScriptBlockedWarning");
   6027 
   6028        return;
   6029      }
   6030    }
   6031  }
   6032 
   6033  if (!mInClose && !mIsClosed && !CanClose()) {
   6034    return;
   6035  }
   6036 
   6037  // Fire a DOM event notifying listeners that this window is about to
   6038  // be closed. The tab UI code may choose to cancel the default
   6039  // action for this event, if so, we won't actually close the window
   6040  // (since the tab UI code will close the tab in stead). Sure, this
   6041  // could be abused by content code, but do we care? I don't think
   6042  // so...
   6043 
   6044  bool wasInClose = mInClose;
   6045  mInClose = true;
   6046 
   6047  if (!DispatchCustomEvent(u"DOMWindowClose"_ns, ChromeOnlyDispatch::eYes)) {
   6048    // Someone chose to prevent the default action for this event, if
   6049    // so, let's not close this window after all...
   6050 
   6051    mInClose = wasInClose;
   6052    return;
   6053  }
   6054 
   6055  FinalClose();
   6056 }
   6057 
   6058 nsresult nsGlobalWindowOuter::Close() {
   6059  CloseOuter(/* aTrustedCaller = */ true);
   6060  return NS_OK;
   6061 }
   6062 
   6063 void nsGlobalWindowOuter::ForceClose() {
   6064  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
   6065 
   6066  if (mBrowsingContext->IsSubframe() || !mDocShell) {
   6067    // This may be a frame in a frameset, or a window that's already closed.
   6068    // Ignore such calls.
   6069    return;
   6070  }
   6071 
   6072  if (mHavePendingClose) {
   6073    // We're going to be closed anyway; do nothing since we don't want
   6074    // to double-close
   6075    return;
   6076  }
   6077 
   6078  mInClose = true;
   6079 
   6080  DispatchCustomEvent(u"DOMWindowClose"_ns, ChromeOnlyDispatch::eYes);
   6081 
   6082  FinalClose();
   6083 }
   6084 
   6085 void nsGlobalWindowOuter::FinalClose() {
   6086  // Flag that we were closed.
   6087  mIsClosed = true;
   6088 
   6089  if (!mBrowsingContext->IsDiscarded()) {
   6090    MOZ_ALWAYS_SUCCEEDS(mBrowsingContext->SetClosed(true));
   6091  }
   6092 
   6093  // If we get here from CloseOuter then it means that the parent process is
   6094  // going to close our window for us. It's just important to set mIsClosed.
   6095  if (XRE_GetProcessType() == GeckoProcessType_Content) {
   6096    return;
   6097  }
   6098 
   6099  // This stuff is non-sensical but incredibly fragile. The reasons for the
   6100  // behavior here don't make sense today and may not have ever made sense,
   6101  // but various bits of frontend code break when you change them. If you need
   6102  // to fix up this behavior, feel free to. It's a righteous task, but involves
   6103  // wrestling with various download manager tests, frontend code, and possible
   6104  // broken addons. The chrome tests in toolkit/mozapps/downloads are a good
   6105  // testing ground.
   6106  //
   6107  // In particular, if some inner of |win| is the entry global, we must
   6108  // complete _two_ round-trips to the event loop before the call to
   6109  // ReallyCloseWindow. This allows setTimeout handlers that are set after
   6110  // FinalClose() is called to run before the window is torn down.
   6111  nsCOMPtr<nsPIDOMWindowInner> entryWindow =
   6112      do_QueryInterface(GetEntryGlobal());
   6113  bool indirect = entryWindow && entryWindow->GetOuterWindow() == this;
   6114  if (NS_FAILED(nsCloseEvent::PostCloseEvent(this, indirect))) {
   6115    ReallyCloseWindow();
   6116  } else {
   6117    mHavePendingClose = true;
   6118  }
   6119 }
   6120 
   6121 void nsGlobalWindowOuter::ReallyCloseWindow() {
   6122  // Make sure we never reenter this method.
   6123  mHavePendingClose = true;
   6124 
   6125  nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
   6126  if (!treeOwnerAsWin) {
   6127    return;
   6128  }
   6129 
   6130  treeOwnerAsWin->Destroy();
   6131  CleanUp();
   6132 }
   6133 
   6134 void nsGlobalWindowOuter::SuppressEventHandling() {
   6135  if (mSuppressEventHandlingDepth == 0) {
   6136    if (BrowsingContext* bc = GetBrowsingContext()) {
   6137      bc->PreOrderWalk([&](BrowsingContext* aBC) {
   6138        if (nsCOMPtr<nsPIDOMWindowOuter> win = aBC->GetDOMWindow()) {
   6139          if (RefPtr<Document> doc = win->GetExtantDoc()) {
   6140            mSuspendedDocs.AppendElement(doc);
   6141            // Note: Document::SuppressEventHandling will also automatically
   6142            // suppress event handling for any in-process sub-documents.
   6143            // However, since we need to deal with cases where remote
   6144            // BrowsingContexts may be interleaved with in-process ones, we
   6145            // still need to walk the entire tree ourselves. This may be
   6146            // slightly redundant in some cases, but since event handling
   6147            // suppressions maintain a count of current blockers, it does not
   6148            // cause any problems.
   6149            doc->SuppressEventHandling();
   6150          }
   6151        }
   6152      });
   6153    }
   6154  }
   6155  mSuppressEventHandlingDepth++;
   6156 }
   6157 
   6158 void nsGlobalWindowOuter::UnsuppressEventHandling() {
   6159  MOZ_ASSERT(mSuppressEventHandlingDepth != 0);
   6160  mSuppressEventHandlingDepth--;
   6161 
   6162  if (mSuppressEventHandlingDepth == 0 && mSuspendedDocs.Length()) {
   6163    RefPtr<Document> currentDoc = GetExtantDoc();
   6164    bool fireEvent = currentDoc == mSuspendedDocs[0];
   6165    nsTArray<RefPtr<Document>> suspendedDocs = std::move(mSuspendedDocs);
   6166    for (const auto& doc : suspendedDocs) {
   6167      doc->UnsuppressEventHandlingAndFireEvents(fireEvent);
   6168    }
   6169  }
   6170 }
   6171 
   6172 nsGlobalWindowOuter* nsGlobalWindowOuter::EnterModalState() {
   6173  // GetInProcessScriptableTop, not GetInProcessTop, so that EnterModalState
   6174  // works properly with <iframe mozbrowser>.
   6175  nsGlobalWindowOuter* topWin = GetInProcessScriptableTopInternal();
   6176 
   6177  if (!topWin) {
   6178    NS_ERROR("Uh, EnterModalState() called w/o a reachable top window?");
   6179    return nullptr;
   6180  }
   6181 
   6182  // If there is an active ESM in this window, clear it. Otherwise, this can
   6183  // cause a problem if a modal state is entered during a mouseup event.
   6184  EventStateManager* activeESM = static_cast<EventStateManager*>(
   6185      EventStateManager::GetActiveEventStateManager());
   6186  if (activeESM && activeESM->GetPresContext()) {
   6187    PresShell* activePresShell = activeESM->GetPresContext()->GetPresShell();
   6188    if (activePresShell && (nsContentUtils::ContentIsCrossDocDescendantOf(
   6189                                activePresShell->GetDocument(), mDoc) ||
   6190                            nsContentUtils::ContentIsCrossDocDescendantOf(
   6191                                mDoc, activePresShell->GetDocument()))) {
   6192      EventStateManager::ClearGlobalActiveContent(activeESM);
   6193 
   6194      PresShell::ReleaseCapturingContent();
   6195 
   6196      if (activePresShell) {
   6197        RefPtr<nsFrameSelection> frameSelection =
   6198            activePresShell->FrameSelection();
   6199        frameSelection->SetDragState(false);
   6200      }
   6201    }
   6202  }
   6203 
   6204  // If there are any drag and drop operations in flight, try to end them.
   6205  nsCOMPtr<nsIDragService> ds =
   6206      do_GetService("@mozilla.org/widget/dragservice;1");
   6207  if (ds && topWin->GetDocShell()) {
   6208    if (PresShell* presShell = topWin->GetDocShell()->GetPresShell()) {
   6209      if (RefPtr<nsIWidget> widget = presShell->GetRootWidget()) {
   6210        if (nsCOMPtr<nsIDragSession> session = ds->GetCurrentSession(widget)) {
   6211          session->EndDragSession(true, 0);
   6212        }
   6213      }
   6214    }
   6215  }
   6216 
   6217  // Clear the capturing content if it is under topDoc.
   6218  // Usually the activeESM check above does that, but there are cases when
   6219  // we don't have activeESM, or it is for different document.
   6220  Document* topDoc = topWin->GetExtantDoc();
   6221  nsIContent* capturingContent = PresShell::GetCapturingContent();
   6222  if (capturingContent && topDoc &&
   6223      nsContentUtils::ContentIsCrossDocDescendantOf(capturingContent, topDoc)) {
   6224    PresShell::ReleaseCapturingContent();
   6225  }
   6226 
   6227  if (topWin->mModalStateDepth == 0) {
   6228    topWin->SuppressEventHandling();
   6229 
   6230    if (nsGlobalWindowInner* inner = GetCurrentInnerWindowInternal(topWin)) {
   6231      inner->Suspend();
   6232    }
   6233  }
   6234  topWin->mModalStateDepth++;
   6235  return topWin;
   6236 }
   6237 
   6238 void nsGlobalWindowOuter::LeaveModalState() {
   6239  {
   6240    nsGlobalWindowOuter* topWin = GetInProcessScriptableTopInternal();
   6241    if (!topWin) {
   6242      NS_WARNING("Uh, LeaveModalState() called w/o a reachable top window?");
   6243      return;
   6244    }
   6245 
   6246    if (topWin != this) {
   6247      MOZ_ASSERT(IsSuspended());
   6248      return topWin->LeaveModalState();
   6249    }
   6250  }
   6251 
   6252  MOZ_ASSERT(mModalStateDepth != 0);
   6253  MOZ_ASSERT(IsSuspended());
   6254  mModalStateDepth--;
   6255 
   6256  nsGlobalWindowInner* inner = GetCurrentInnerWindowInternal(this);
   6257  if (mModalStateDepth == 0) {
   6258    if (inner) {
   6259      inner->Resume();
   6260    }
   6261 
   6262    UnsuppressEventHandling();
   6263  }
   6264 
   6265  // Remember the time of the last dialog quit.
   6266  if (auto* bcg = GetBrowsingContextGroup()) {
   6267    bcg->SetLastDialogQuitTime(TimeStamp::Now());
   6268  }
   6269 
   6270  if (mModalStateDepth == 0) {
   6271    RefPtr<Event> event = NS_NewDOMEvent(inner, nullptr, nullptr);
   6272    event->InitEvent(u"endmodalstate"_ns, true, false);
   6273    event->SetTrusted(true);
   6274    event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true;
   6275    DispatchEvent(*event);
   6276  }
   6277 }
   6278 
   6279 bool nsGlobalWindowOuter::IsInModalState() {
   6280  nsGlobalWindowOuter* topWin = GetInProcessScriptableTopInternal();
   6281 
   6282  if (!topWin) {
   6283    // IsInModalState() getting called w/o a reachable top window is a bit
   6284    // iffy, but valid enough not to make noise about it.  See bug 404828
   6285    return false;
   6286  }
   6287 
   6288  return topWin->mModalStateDepth != 0;
   6289 }
   6290 
   6291 void nsGlobalWindowOuter::NotifyWindowIDDestroyed(const char* aTopic) {
   6292  nsCOMPtr<nsIRunnable> runnable =
   6293      new WindowDestroyedEvent(this, mWindowID, aTopic);
   6294  Dispatch(runnable.forget());
   6295 }
   6296 
   6297 Element* nsGlobalWindowOuter::GetFrameElement(nsIPrincipal& aSubjectPrincipal) {
   6298  // Per HTML5, the frameElement getter returns null in cross-origin situations.
   6299  Element* element = GetFrameElement();
   6300  if (!element) {
   6301    return nullptr;
   6302  }
   6303 
   6304  if (!aSubjectPrincipal.SubsumesConsideringDomain(element->NodePrincipal())) {
   6305    return nullptr;
   6306  }
   6307 
   6308  return element;
   6309 }
   6310 
   6311 Element* nsGlobalWindowOuter::GetFrameElement() {
   6312  if (!mBrowsingContext || mBrowsingContext->IsTop()) {
   6313    return nullptr;
   6314  }
   6315  return mBrowsingContext->GetEmbedderElement();
   6316 }
   6317 
   6318 namespace {
   6319 class ChildCommandDispatcher : public Runnable {
   6320 public:
   6321  ChildCommandDispatcher(nsPIWindowRoot* aRoot, nsIBrowserChild* aBrowserChild,
   6322                         nsPIDOMWindowOuter* aWindow, const nsAString& aAction)
   6323      : mozilla::Runnable("ChildCommandDispatcher"),
   6324        mRoot(aRoot),
   6325        mBrowserChild(aBrowserChild),
   6326        mWindow(aWindow),
   6327        mAction(aAction) {}
   6328 
   6329  NS_IMETHOD Run() override {
   6330    AutoTArray<nsCString, 70> enabledCommands, disabledCommands;
   6331    mRoot->GetEnabledDisabledCommands(enabledCommands, disabledCommands);
   6332    if (enabledCommands.Length() || disabledCommands.Length()) {
   6333      BrowserChild* bc = static_cast<BrowserChild*>(mBrowserChild.get());
   6334      bc->SendEnableDisableCommands(mWindow->GetBrowsingContext(), mAction,
   6335                                    enabledCommands, disabledCommands);
   6336    }
   6337 
   6338    return NS_OK;
   6339  }
   6340 
   6341 private:
   6342  nsCOMPtr<nsPIWindowRoot> mRoot;
   6343  nsCOMPtr<nsIBrowserChild> mBrowserChild;
   6344  nsCOMPtr<nsPIDOMWindowOuter> mWindow;
   6345  nsString mAction;
   6346 };
   6347 
   6348 class CommandDispatcher : public Runnable {
   6349 public:
   6350  CommandDispatcher(nsIDOMXULCommandDispatcher* aDispatcher,
   6351                    const nsAString& aAction)
   6352      : mozilla::Runnable("CommandDispatcher"),
   6353        mDispatcher(aDispatcher),
   6354        mAction(aAction) {}
   6355 
   6356  // TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230, bug 1535398)
   6357  MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD Run() override {
   6358    return mDispatcher->UpdateCommands(mAction);
   6359  }
   6360 
   6361  const nsCOMPtr<nsIDOMXULCommandDispatcher> mDispatcher;
   6362  nsString mAction;
   6363 };
   6364 }  // anonymous namespace
   6365 
   6366 void nsGlobalWindowOuter::UpdateCommands(const nsAString& anAction) {
   6367  // If this is a child process, redirect to the parent process.
   6368  if (nsIDocShell* docShell = GetDocShell()) {
   6369    if (nsCOMPtr<nsIBrowserChild> child = docShell->GetBrowserChild()) {
   6370      nsCOMPtr<nsPIWindowRoot> root = GetTopWindowRoot();
   6371      if (root) {
   6372        nsContentUtils::AddScriptRunner(
   6373            new ChildCommandDispatcher(root, child, this, anAction));
   6374      }
   6375      return;
   6376    }
   6377  }
   6378 
   6379  nsPIDOMWindowOuter* rootWindow = GetPrivateRoot();
   6380  if (!rootWindow) {
   6381    return;
   6382  }
   6383 
   6384  Document* doc = rootWindow->GetExtantDoc();
   6385 
   6386  if (!doc) {
   6387    return;
   6388  }
   6389 
   6390  // Retrieve the command dispatcher and call updateCommands on it.
   6391  nsIDOMXULCommandDispatcher* xulCommandDispatcher =
   6392      doc->GetCommandDispatcher();
   6393  if (xulCommandDispatcher) {
   6394    nsContentUtils::AddScriptRunner(
   6395        new CommandDispatcher(xulCommandDispatcher, anAction));
   6396  }
   6397 }
   6398 
   6399 Selection* nsGlobalWindowOuter::GetSelectionOuter() {
   6400  if (!mDocShell) {
   6401    return nullptr;
   6402  }
   6403 
   6404  PresShell* presShell = mDocShell->GetPresShell();
   6405  if (!presShell) {
   6406    // Force layout of the containing frame.
   6407    // layout/reftests/selection/modify-range.html goes
   6408    // through here.
   6409    EnsureSizeAndPositionUpToDate();
   6410    if (!mDocShell) {
   6411      return nullptr;
   6412    }
   6413    presShell = mDocShell->GetPresShell();
   6414    if (!presShell) {
   6415      return nullptr;
   6416    }
   6417  }
   6418  return presShell->GetCurrentSelection(SelectionType::eNormal);
   6419 }
   6420 
   6421 already_AddRefed<Selection> nsGlobalWindowOuter::GetSelection() {
   6422  RefPtr<Selection> selection = GetSelectionOuter();
   6423  return selection.forget();
   6424 }
   6425 
   6426 bool nsGlobalWindowOuter::FindOuter(const nsAString& aString,
   6427                                    bool aCaseSensitive, bool aBackwards,
   6428                                    bool aWrapAround, bool aWholeWord,
   6429                                    bool aSearchInFrames, bool aShowDialog,
   6430                                    ErrorResult& aError) {
   6431  (void)aShowDialog;
   6432 
   6433  nsCOMPtr<nsIWebBrowserFind> finder(do_GetInterface(mDocShell));
   6434  if (!finder) {
   6435    aError.Throw(NS_ERROR_NOT_AVAILABLE);
   6436    return false;
   6437  }
   6438 
   6439  // Set the options of the search
   6440  aError = finder->SetSearchString(aString);
   6441  if (aError.Failed()) {
   6442    return false;
   6443  }
   6444  finder->SetMatchCase(aCaseSensitive);
   6445  finder->SetFindBackwards(aBackwards);
   6446  finder->SetWrapFind(aWrapAround);
   6447  finder->SetEntireWord(aWholeWord);
   6448  finder->SetSearchFrames(aSearchInFrames);
   6449 
   6450  // the nsIWebBrowserFind is initialized to use this window
   6451  // as the search root, but uses focus to set the current search
   6452  // frame. If we're being called from JS (as here), this window
   6453  // should be the current search frame.
   6454  nsCOMPtr<nsIWebBrowserFindInFrames> framesFinder(do_QueryInterface(finder));
   6455  if (framesFinder) {
   6456    framesFinder->SetRootSearchFrame(this);  // paranoia
   6457    framesFinder->SetCurrentSearchFrame(this);
   6458  }
   6459 
   6460  if (aString.IsEmpty()) {
   6461    return false;
   6462  }
   6463 
   6464  // Launch the search with the passed in search string
   6465  bool didFind = false;
   6466  aError = finder->FindNext(&didFind);
   6467  return didFind;
   6468 }
   6469 
   6470 //*****************************************************************************
   6471 // EventTarget
   6472 //*****************************************************************************
   6473 
   6474 nsPIDOMWindowOuter* nsGlobalWindowOuter::GetOwnerGlobalForBindingsInternal() {
   6475  return this;
   6476 }
   6477 
   6478 nsIGlobalObject* nsGlobalWindowOuter::GetOwnerGlobal() const {
   6479  return GetCurrentInnerWindowInternal(this);
   6480 }
   6481 
   6482 bool nsGlobalWindowOuter::DispatchEvent(Event& aEvent, CallerType aCallerType,
   6483                                        ErrorResult& aRv) {
   6484  FORWARD_TO_INNER(DispatchEvent, (aEvent, aCallerType, aRv), false);
   6485 }
   6486 
   6487 bool nsGlobalWindowOuter::ComputeDefaultWantsUntrusted(ErrorResult& aRv) {
   6488  // It's OK that we just return false here on failure to create an
   6489  // inner.  GetOrCreateListenerManager() will likewise fail, and then
   6490  // we won't be adding any listeners anyway.
   6491  FORWARD_TO_INNER_CREATE(ComputeDefaultWantsUntrusted, (aRv), false);
   6492 }
   6493 
   6494 EventListenerManager* nsGlobalWindowOuter::GetOrCreateListenerManager() {
   6495  FORWARD_TO_INNER_CREATE(GetOrCreateListenerManager, (), nullptr);
   6496 }
   6497 
   6498 EventListenerManager* nsGlobalWindowOuter::GetExistingListenerManager() const {
   6499  FORWARD_TO_INNER(GetExistingListenerManager, (), nullptr);
   6500 }
   6501 
   6502 //*****************************************************************************
   6503 // nsGlobalWindowOuter::nsPIDOMWindow
   6504 //*****************************************************************************
   6505 
   6506 nsPIDOMWindowOuter* nsGlobalWindowOuter::GetPrivateParent() {
   6507  nsCOMPtr<nsPIDOMWindowOuter> parent = GetInProcessParent();
   6508 
   6509  if (this == parent) {
   6510    nsCOMPtr<nsIContent> chromeElement(do_QueryInterface(mChromeEventHandler));
   6511    if (!chromeElement)
   6512      return nullptr;  // This is ok, just means a null parent.
   6513 
   6514    Document* doc = chromeElement->GetComposedDoc();
   6515    if (!doc) return nullptr;  // This is ok, just means a null parent.
   6516 
   6517    return doc->GetWindow();
   6518  }
   6519 
   6520  return parent;
   6521 }
   6522 
   6523 nsPIDOMWindowOuter* nsGlobalWindowOuter::GetPrivateRoot() {
   6524  nsCOMPtr<nsPIDOMWindowOuter> top = GetInProcessTop();
   6525 
   6526  nsCOMPtr<nsIContent> chromeElement(do_QueryInterface(mChromeEventHandler));
   6527  if (chromeElement) {
   6528    Document* doc = chromeElement->GetComposedDoc();
   6529    if (doc) {
   6530      nsCOMPtr<nsPIDOMWindowOuter> parent = doc->GetWindow();
   6531      if (parent) {
   6532        top = parent->GetInProcessTop();
   6533      }
   6534    }
   6535  }
   6536 
   6537  return top;
   6538 }
   6539 
   6540 // This has a caller in Windows-only code (nsNativeAppSupportWin).
   6541 Location* nsGlobalWindowOuter::GetLocation() {
   6542  // This method can be called on the outer window as well.
   6543  FORWARD_TO_INNER(Location, (), nullptr);
   6544 }
   6545 
   6546 void nsGlobalWindowOuter::SetIsBackground(bool aIsBackground) {
   6547  const bool changed = aIsBackground != IsBackground();
   6548  SetIsBackgroundInternal(aIsBackground);
   6549 
   6550  nsGlobalWindowInner* inner = GetCurrentInnerWindowInternal(this);
   6551  if (!inner) {
   6552    return;
   6553  }
   6554 
   6555  if (changed) {
   6556    inner->UpdateBackgroundState();
   6557    if (aIsBackground) {
   6558      // Notify gamepadManager we are at the background window,
   6559      // we need to stop vibrate.
   6560      // Stop the vr telemery time spent when it switches to
   6561      // the background window.
   6562      inner->StopGamepadHaptics();
   6563      inner->StopVRActivity();
   6564      return;
   6565    }
   6566  }
   6567  // FIXME: Why doing this even if not changed?
   6568  inner->SyncGamepadState();
   6569  inner->StartVRActivity();
   6570 }
   6571 
   6572 void nsGlobalWindowOuter::SetIsBackgroundInternal(bool aIsBackground) {
   6573  mIsBackground = aIsBackground;
   6574 }
   6575 
   6576 void nsGlobalWindowOuter::SetChromeEventHandler(
   6577    EventTarget* aChromeEventHandler) {
   6578  SetChromeEventHandlerInternal(aChromeEventHandler);
   6579  // update the chrome event handler on all our inner windows
   6580  RefPtr<nsGlobalWindowInner> inner;
   6581  for (PRCList* node = PR_LIST_HEAD(this); node != this;
   6582       node = PR_NEXT_LINK(inner)) {
   6583    // This cast is only safe if `node != this`, as nsGlobalWindowOuter is also
   6584    // in the list.
   6585    inner = static_cast<nsGlobalWindowInner*>(node);
   6586    NS_ASSERTION(!inner->mOuterWindow || inner->mOuterWindow == this,
   6587                 "bad outer window pointer");
   6588    inner->SetChromeEventHandlerInternal(aChromeEventHandler);
   6589  }
   6590 }
   6591 
   6592 void nsGlobalWindowOuter::SetFocusedElement(Element* aElement,
   6593                                            uint32_t aFocusMethod,
   6594                                            bool aNeedsFocus) {
   6595  FORWARD_TO_INNER_VOID(SetFocusedElement,
   6596                        (aElement, aFocusMethod, aNeedsFocus));
   6597 }
   6598 
   6599 uint32_t nsGlobalWindowOuter::GetFocusMethod() {
   6600  FORWARD_TO_INNER(GetFocusMethod, (), 0);
   6601 }
   6602 
   6603 bool nsGlobalWindowOuter::ShouldShowFocusRing() {
   6604  FORWARD_TO_INNER(ShouldShowFocusRing, (), false);
   6605 }
   6606 
   6607 bool nsGlobalWindowOuter::TakeFocus(bool aFocus, uint32_t aFocusMethod) {
   6608  FORWARD_TO_INNER(TakeFocus, (aFocus, aFocusMethod), false);
   6609 }
   6610 
   6611 void nsGlobalWindowOuter::SetReadyForFocus() {
   6612  FORWARD_TO_INNER_VOID(SetReadyForFocus, ());
   6613 }
   6614 
   6615 void nsGlobalWindowOuter::PageHidden(bool aIsEnteringBFCacheInParent) {
   6616  FORWARD_TO_INNER_VOID(PageHidden, (aIsEnteringBFCacheInParent));
   6617 }
   6618 
   6619 already_AddRefed<nsDOMCSSDeclaration>
   6620 nsGlobalWindowOuter::GetComputedStyleHelperOuter(Element& aElt,
   6621                                                 const nsAString& aPseudoElt,
   6622                                                 bool aDefaultStylesOnly,
   6623                                                 ErrorResult& aRv) {
   6624  if (!mDoc) {
   6625    return nullptr;
   6626  }
   6627 
   6628  RefPtr<nsDOMCSSDeclaration> compStyle = NS_NewComputedDOMStyle(
   6629      &aElt, aPseudoElt, mDoc,
   6630      aDefaultStylesOnly ? nsComputedDOMStyle::StyleType::DefaultOnly
   6631                         : nsComputedDOMStyle::StyleType::All,
   6632      aRv);
   6633 
   6634  return compStyle.forget();
   6635 }
   6636 
   6637 //*****************************************************************************
   6638 // nsGlobalWindowOuter::nsIInterfaceRequestor
   6639 //*****************************************************************************
   6640 
   6641 nsresult nsGlobalWindowOuter::GetInterfaceInternal(const nsIID& aIID,
   6642                                                   void** aSink) {
   6643  NS_ENSURE_ARG_POINTER(aSink);
   6644  *aSink = nullptr;
   6645 
   6646  if (aIID.Equals(NS_GET_IID(nsIWebNavigation))) {
   6647    nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mDocShell));
   6648    webNav.forget(aSink);
   6649  } else if (aIID.Equals(NS_GET_IID(nsIDocShell))) {
   6650    nsCOMPtr<nsIDocShell> docShell = mDocShell;
   6651    docShell.forget(aSink);
   6652  }
   6653 #ifdef NS_PRINTING
   6654  else if (aIID.Equals(NS_GET_IID(nsIWebBrowserPrint))) {
   6655    if (mDocShell) {
   6656      nsCOMPtr<nsIDocumentViewer> viewer;
   6657      mDocShell->GetDocViewer(getter_AddRefs(viewer));
   6658      if (viewer) {
   6659        nsCOMPtr<nsIWebBrowserPrint> webBrowserPrint(do_QueryInterface(viewer));
   6660        webBrowserPrint.forget(aSink);
   6661      }
   6662    }
   6663  }
   6664 #endif
   6665  else if (aIID.Equals(NS_GET_IID(nsILoadContext))) {
   6666    nsCOMPtr<nsILoadContext> loadContext(do_QueryInterface(mDocShell));
   6667    loadContext.forget(aSink);
   6668  }
   6669 
   6670  return *aSink ? NS_OK : NS_ERROR_NO_INTERFACE;
   6671 }
   6672 
   6673 NS_IMETHODIMP
   6674 nsGlobalWindowOuter::GetInterface(const nsIID& aIID, void** aSink) {
   6675  nsresult rv = GetInterfaceInternal(aIID, aSink);
   6676  if (rv == NS_ERROR_NO_INTERFACE) {
   6677    return QueryInterface(aIID, aSink);
   6678  }
   6679  return rv;
   6680 }
   6681 
   6682 bool nsGlobalWindowOuter::IsSuspended() const {
   6683  MOZ_ASSERT(NS_IsMainThread());
   6684  // No inner means we are effectively suspended
   6685  if (!mInnerWindow) {
   6686    return true;
   6687  }
   6688  return nsGlobalWindowInner::Cast(mInnerWindow)->IsSuspended();
   6689 }
   6690 
   6691 bool nsGlobalWindowOuter::IsFrozen() const {
   6692  MOZ_ASSERT(NS_IsMainThread());
   6693  // No inner means we are effectively frozen
   6694  if (!mInnerWindow) {
   6695    return true;
   6696  }
   6697  return nsGlobalWindowInner::Cast(mInnerWindow)->IsFrozen();
   6698 }
   6699 
   6700 nsresult nsGlobalWindowOuter::FireDelayedDOMEvents(bool aIncludeSubWindows) {
   6701  FORWARD_TO_INNER(FireDelayedDOMEvents, (aIncludeSubWindows),
   6702                   NS_ERROR_UNEXPECTED);
   6703 }
   6704 
   6705 //*****************************************************************************
   6706 // nsGlobalWindowOuter: Window Control Functions
   6707 //*****************************************************************************
   6708 
   6709 nsPIDOMWindowOuter* nsGlobalWindowOuter::GetInProcessParentInternal() {
   6710  nsCOMPtr<nsPIDOMWindowOuter> parent = GetInProcessParent();
   6711 
   6712  if (parent && parent != this) {
   6713    return parent;
   6714  }
   6715 
   6716  return nullptr;
   6717 }
   6718 
   6719 void nsGlobalWindowOuter::UnblockScriptedClosing() {
   6720  mBlockScriptedClosingFlag = false;
   6721 }
   6722 
   6723 class AutoUnblockScriptClosing {
   6724 private:
   6725  RefPtr<nsGlobalWindowOuter> mWin;
   6726 
   6727 public:
   6728  explicit AutoUnblockScriptClosing(nsGlobalWindowOuter* aWin) : mWin(aWin) {
   6729    MOZ_ASSERT(mWin);
   6730  }
   6731  ~AutoUnblockScriptClosing() {
   6732    void (nsGlobalWindowOuter::*run)() =
   6733        &nsGlobalWindowOuter::UnblockScriptedClosing;
   6734    nsCOMPtr<nsIRunnable> caller = NewRunnableMethod(
   6735        "AutoUnblockScriptClosing::~AutoUnblockScriptClosing", mWin, run);
   6736    mWin->Dispatch(caller.forget());
   6737  }
   6738 };
   6739 
   6740 nsresult nsGlobalWindowOuter::OpenInternal(
   6741    const nsACString& aUrl, const nsAString& aName, const nsAString& aOptions,
   6742    bool aDialog, bool aCalledNoScript, bool aDoJSFixups, bool aNavigate,
   6743    nsIArray* aArguments, nsDocShellLoadState* aLoadState, bool aForceNoOpener,
   6744    PrintKind aPrintKind, BrowsingContext** aReturn) {
   6745  mozilla::Maybe<AutoUnblockScriptClosing> closeUnblocker;
   6746 
   6747  // Calls to window.open from script should navigate.
   6748  MOZ_ASSERT(aCalledNoScript || aNavigate);
   6749 
   6750  *aReturn = nullptr;
   6751 
   6752  nsCOMPtr<nsIWebBrowserChrome> chrome = GetWebBrowserChrome();
   6753  if (!chrome) {
   6754    // No chrome means we don't want to go through with this open call
   6755    // -- see nsIWindowWatcher.idl
   6756    return NS_ERROR_NOT_AVAILABLE;
   6757  }
   6758 
   6759  NS_ASSERTION(mDocShell, "Must have docshell here");
   6760 
   6761  NS_ConvertUTF16toUTF8 optionsUtf8(aOptions);
   6762 
   6763  WindowFeatures features;
   6764  if (!features.Tokenize(optionsUtf8)) {
   6765    return NS_ERROR_FAILURE;
   6766  }
   6767 
   6768  bool forceNoOpener = aForceNoOpener;
   6769  if (features.Exists("noopener")) {
   6770    forceNoOpener = features.GetBool("noopener");
   6771    features.Remove("noopener");
   6772  }
   6773 
   6774  bool forceNoReferrer = false;
   6775  if (features.Exists("noreferrer")) {
   6776    forceNoReferrer = features.GetBool("noreferrer");
   6777    if (forceNoReferrer) {
   6778      // noreferrer implies noopener
   6779      forceNoOpener = true;
   6780    }
   6781    features.Remove("noreferrer");
   6782  }
   6783 
   6784  nsAutoCString options;
   6785  features.Stringify(options);
   6786 
   6787  // If noopener is force-enabled for the current document, then set noopener to
   6788  // true, and clear the name to "_blank".
   6789  nsAutoString windowName(aName);
   6790  if (nsDocShell::Cast(GetDocShell())->NoopenerForceEnabled() &&
   6791      aPrintKind == PrintKind::None) {
   6792    MOZ_DIAGNOSTIC_ASSERT(aNavigate,
   6793                          "cannot OpenNoNavigate if noopener is force-enabled");
   6794 
   6795    forceNoOpener = true;
   6796    windowName = u"_blank"_ns;
   6797  }
   6798 
   6799  bool windowExists = WindowExists(windowName, forceNoOpener, !aCalledNoScript);
   6800 
   6801  // XXXbz When this gets fixed to not use LegacyIsCallerNativeCode()
   6802  // (indirectly) maybe we can nix the AutoJSAPI usage OnLinkClickEvent::Run.
   6803  // But note that if you change this to GetEntryGlobal(), say, then
   6804  // OnLinkClickEvent::Run will need a full-blown AutoEntryScript. (Bug 1930445)
   6805  const bool checkForPopup = [&]() {
   6806    if (aDialog) {
   6807      return false;
   6808    }
   6809    if (windowExists) {
   6810      return false;
   6811    }
   6812    if (aLoadState && aLoadState->IsFormSubmission()) {
   6813      return true;
   6814    }
   6815    return !nsContentUtils::LegacyIsCallerChromeOrNativeCode();
   6816  }();
   6817 
   6818  nsCOMPtr<nsIURI> uri;
   6819 
   6820  // It's important to do this security check before determining whether this
   6821  // window opening should be blocked, to ensure that we don't
   6822  // FirePopupBlockedEvent for a window opening that wouldn't have
   6823  // succeeded in the first place.
   6824  if (!aUrl.IsEmpty()) {
   6825    // It's safe to skip the security check below if we're a dialog because
   6826    // window.openDialog is not callable from content script. See bug 56851.
   6827    //
   6828    // If we're not navigating, we assume that whoever *does* navigate the
   6829    // window will do a security check of their own.
   6830    auto result =
   6831        URIfromURLAndMaybeDoSecurityCheck(aUrl, !aDialog && aNavigate);
   6832    if (result.isErr()) {
   6833      return result.unwrapErr();
   6834    }
   6835 
   6836    uri = result.unwrap();
   6837  } else if (mDoc) {
   6838    mDoc->SetUseCounter(eUseCounter_custom_WindowOpenEmptyUrl);
   6839  }
   6840 
   6841  UserActivation::Modifiers modifiers;
   6842  mBrowsingContext->GetUserActivationModifiersForPopup(&modifiers);
   6843 
   6844  // Need to create loadState before the user activation is consumed in
   6845  // BrowsingContext::RevisePopupAbuseLevel() below.
   6846  RefPtr<nsDocShellLoadState> loadState = aLoadState;
   6847  if (!loadState && aNavigate && uri) {
   6848    loadState = nsWindowWatcher::CreateLoadState(uri, this, aDoJSFixups);
   6849  }
   6850 
   6851  PopupBlocker::PopupControlState abuseLevel =
   6852      PopupBlocker::GetPopupControlState();
   6853  if (checkForPopup) {
   6854    abuseLevel = mBrowsingContext->RevisePopupAbuseLevel(abuseLevel);
   6855    if (abuseLevel >= PopupBlocker::openBlocked) {
   6856      if (!aCalledNoScript) {
   6857        // If script in some other window is doing a window.open on us and
   6858        // it's being blocked, then it's OK to close us afterwards, probably.
   6859        // But if we're doing a window.open on ourselves and block the popup,
   6860        // prevent this window from closing until after this script terminates
   6861        // so that whatever popup blocker UI the app has will be visible.
   6862        nsCOMPtr<nsPIDOMWindowInner> entryWindow =
   6863            do_QueryInterface(GetEntryGlobal());
   6864        // Note that entryWindow can be null here if some JS component was the
   6865        // place where script was entered for this JS execution.
   6866        if (entryWindow && entryWindow->GetOuterWindow() == this) {
   6867          mBlockScriptedClosingFlag = true;
   6868          closeUnblocker.emplace(this);
   6869        }
   6870      }
   6871 
   6872      FirePopupBlockedEvent(uri, windowName, aOptions);
   6873      return aDoJSFixups ? NS_OK : NS_ERROR_FAILURE;
   6874    }
   6875  }
   6876 
   6877  // Per https://github.com/whatwg/html/pull/10547, we should always consume
   6878  // user activation when opening a new window, even if the popup blocker is
   6879  // disabled or the website has popup permission.
   6880  if (!windowExists && mDoc) {
   6881    mDoc->ConsumeTransientUserGestureActivation();
   6882  }
   6883 
   6884  RefPtr<BrowsingContext> domReturn;
   6885 
   6886  nsresult rv = NS_OK;
   6887  nsCOMPtr<nsIWindowWatcher> wwatch =
   6888      do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
   6889  NS_ENSURE_TRUE(wwatch, rv);
   6890 
   6891  NS_ConvertUTF16toUTF8 name(windowName);
   6892 
   6893  nsCOMPtr<nsPIWindowWatcher> pwwatch(do_QueryInterface(wwatch));
   6894  NS_ENSURE_STATE(pwwatch);
   6895 
   6896  MOZ_ASSERT_IF(checkForPopup, abuseLevel < PopupBlocker::openBlocked);
   6897  // At this point we should know for a fact that if checkForPopup then
   6898  // abuseLevel < PopupBlocker::openBlocked, so we could just check for
   6899  // abuseLevel == PopupBlocker::openControlled.  But let's be defensive just in
   6900  // case and treat anything that fails the above assert as a spam popup too, if
   6901  // it ever happens.
   6902  bool isPopupSpamWindow =
   6903      checkForPopup && (abuseLevel >= PopupBlocker::openControlled);
   6904 
   6905  const auto wwPrintKind = [&] {
   6906    switch (aPrintKind) {
   6907      case PrintKind::None:
   6908        return nsPIWindowWatcher::PRINT_NONE;
   6909      case PrintKind::InternalPrint:
   6910        return nsPIWindowWatcher::PRINT_INTERNAL;
   6911      case PrintKind::WindowDotPrint:
   6912        return nsPIWindowWatcher::PRINT_WINDOW_DOT_PRINT;
   6913    }
   6914    MOZ_ASSERT_UNREACHABLE("Wat");
   6915    return nsPIWindowWatcher::PRINT_NONE;
   6916  }();
   6917 
   6918  {
   6919    // Reset popup state while opening a window to prevent the
   6920    // current state from being active the whole time a modal
   6921    // dialog is open.
   6922    AutoPopupStatePusher popupStatePusher(PopupBlocker::openAbused, true);
   6923 
   6924    if (!aCalledNoScript) {
   6925      // We asserted at the top of this function that aNavigate is true for
   6926      // !aCalledNoScript.
   6927      rv = pwwatch->OpenWindow2(this, uri, name, options, modifiers,
   6928                                /* aCalledFromScript = */ true, aDialog,
   6929                                aNavigate, aArguments, isPopupSpamWindow,
   6930                                forceNoOpener, forceNoReferrer, wwPrintKind,
   6931                                loadState, getter_AddRefs(domReturn));
   6932    } else {
   6933      // Force a system caller here so that the window watcher won't screw us
   6934      // up.  We do NOT want this case looking at the JS context on the stack
   6935      // when searching.  Compare comments on
   6936      // nsIDOMWindow::OpenWindow and nsIWindowWatcher::OpenWindow.
   6937 
   6938      // Note: Because nsWindowWatcher is so broken, it's actually important
   6939      // that we don't force a system caller here, because that screws it up
   6940      // when it tries to compute the caller principal to associate with dialog
   6941      // arguments. That whole setup just really needs to be rewritten. :-(
   6942      AutoNoJSAPI nojsapi;
   6943      rv = pwwatch->OpenWindow2(this, uri, name, options, modifiers,
   6944                                /* aCalledFromScript = */ false, aDialog,
   6945                                aNavigate, aArguments, isPopupSpamWindow,
   6946                                forceNoOpener, forceNoReferrer, wwPrintKind,
   6947                                loadState, getter_AddRefs(domReturn));
   6948    }
   6949  }
   6950 
   6951  NS_ENSURE_SUCCESS(rv, rv);
   6952 
   6953  // success!
   6954 
   6955  if (!aCalledNoScript && !windowExists && uri && !forceNoOpener) {
   6956    MaybeAllowStorageForOpenedWindow(uri);
   6957  }
   6958 
   6959  if (domReturn && aDoJSFixups) {
   6960    nsPIDOMWindowOuter* outer = domReturn->GetDOMWindow();
   6961    if (outer && !nsGlobalWindowOuter::Cast(outer)->IsChromeWindow()) {
   6962      // A new non-chrome window was created from a call to
   6963      // window.open() from JavaScript, make sure there's a document in
   6964      // the new window. We do this by simply asking the new window for
   6965      // its document, this will synchronously create an empty document
   6966      // if there is no document in the window.
   6967      // XXXbz should this just use EnsureInnerWindow()?
   6968 
   6969      // Force document creation.
   6970      nsCOMPtr<Document> doc = outer->GetDoc();
   6971      (void)doc;
   6972    }
   6973  }
   6974 
   6975  domReturn.forget(aReturn);
   6976  return NS_OK;
   6977 }
   6978 
   6979 void nsGlobalWindowOuter::MaybeAllowStorageForOpenedWindow(nsIURI* aURI) {
   6980  nsGlobalWindowInner* inner = GetCurrentInnerWindowInternal(this);
   6981  if (NS_WARN_IF(!inner)) {
   6982    return;
   6983  }
   6984 
   6985  // Don't trigger the heuristic for third-party trackers.
   6986  if (StaticPrefs::
   6987          privacy_restrict3rdpartystorage_heuristic_exclude_third_party_trackers() &&
   6988      nsContentUtils::IsThirdPartyTrackingResourceWindow(inner)) {
   6989    return;
   6990  }
   6991 
   6992  // No 3rd party URL/window.
   6993  if (!AntiTrackingUtils::IsThirdPartyWindow(inner, aURI)) {
   6994    return;
   6995  }
   6996 
   6997  Document* doc = inner->GetDoc();
   6998  if (!doc) {
   6999    return;
   7000  }
   7001  nsCOMPtr<nsIPrincipal> principal = BasePrincipal::CreateContentPrincipal(
   7002      aURI, doc->NodePrincipal()->OriginAttributesRef());
   7003 
   7004  // We don't care when the asynchronous work finishes here.
   7005  // Without e10s or fission enabled this is run in the parent process.
   7006  if (XRE_IsParentProcess()) {
   7007    (void)StorageAccessAPIHelper::AllowAccessForOnParentProcess(
   7008        principal, GetBrowsingContext(), ContentBlockingNotifier::eOpener);
   7009  } else {
   7010    (void)StorageAccessAPIHelper::AllowAccessForOnChildProcess(
   7011        principal, GetBrowsingContext(), ContentBlockingNotifier::eOpener);
   7012  }
   7013 }
   7014 
   7015 //*****************************************************************************
   7016 // nsGlobalWindowOuter: Helper Functions
   7017 //*****************************************************************************
   7018 
   7019 already_AddRefed<nsIDocShellTreeOwner> nsPIDOMWindowOuter::GetTreeOwner() {
   7020  // If there's no docShellAsItem, this window must have been closed,
   7021  // in that case there is no tree owner.
   7022 
   7023  if (!mDocShell) {
   7024    return nullptr;
   7025  }
   7026 
   7027  nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
   7028  mDocShell->GetTreeOwner(getter_AddRefs(treeOwner));
   7029  return treeOwner.forget();
   7030 }
   7031 
   7032 already_AddRefed<nsIBaseWindow> nsPIDOMWindowOuter::GetTreeOwnerWindow() {
   7033  nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
   7034 
   7035  // If there's no mDocShell, this window must have been closed,
   7036  // in that case there is no tree owner.
   7037 
   7038  if (mDocShell) {
   7039    mDocShell->GetTreeOwner(getter_AddRefs(treeOwner));
   7040  }
   7041 
   7042  nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(treeOwner);
   7043  return baseWindow.forget();
   7044 }
   7045 
   7046 already_AddRefed<nsIWebBrowserChrome>
   7047 nsPIDOMWindowOuter::GetWebBrowserChrome() {
   7048  nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner();
   7049 
   7050  nsCOMPtr<nsIWebBrowserChrome> browserChrome = do_GetInterface(treeOwner);
   7051  return browserChrome.forget();
   7052 }
   7053 
   7054 ScrollContainerFrame* nsGlobalWindowOuter::GetScrollContainerFrame() {
   7055  if (!mDocShell) {
   7056    return nullptr;
   7057  }
   7058 
   7059  PresShell* presShell = mDocShell->GetPresShell();
   7060  if (presShell) {
   7061    return presShell->GetRootScrollContainerFrame();
   7062  }
   7063  return nullptr;
   7064 }
   7065 
   7066 Result<already_AddRefed<nsIURI>, nsresult>
   7067 nsGlobalWindowOuter::URIfromURLAndMaybeDoSecurityCheck(const nsACString& aURL,
   7068                                                       bool aSecurityCheck) {
   7069  nsCOMPtr<nsPIDOMWindowInner> sourceWindow =
   7070      do_QueryInterface(GetEntryGlobal());
   7071  if (!sourceWindow) {
   7072    sourceWindow = GetCurrentInnerWindow();
   7073  }
   7074 
   7075  // Resolve the baseURI, which could be relative to the calling window.
   7076  //
   7077  // Note the algorithm to get the base URI should match the one
   7078  // used to actually kick off the load in nsWindowWatcher.cpp.
   7079  nsCOMPtr<Document> doc = sourceWindow->GetDoc();
   7080  nsIURI* baseURI = nullptr;
   7081  auto encoding = UTF_8_ENCODING;  // default to utf-8
   7082  if (doc) {
   7083    baseURI = doc->GetDocBaseURI();
   7084    encoding = doc->GetDocumentCharacterSet();
   7085  }
   7086  nsCOMPtr<nsIURI> uri;
   7087  nsresult rv = NS_NewURI(getter_AddRefs(uri), aURL, encoding, baseURI);
   7088  if (NS_WARN_IF(NS_FAILED(rv))) {
   7089    return Err(NS_ERROR_DOM_SYNTAX_ERR);
   7090  }
   7091 
   7092  if (aSecurityCheck) {
   7093    AutoJSContext cx;
   7094    nsGlobalWindowInner* sourceWin = nsGlobalWindowInner::Cast(sourceWindow);
   7095    JSAutoRealm ar(cx, sourceWin->GetGlobalJSObject());
   7096 
   7097    if (NS_FAILED(nsContentUtils::GetSecurityManager()->CheckLoadURIFromScript(
   7098            cx, uri))) {
   7099      return Err(NS_ERROR_FAILURE);
   7100    }
   7101  }
   7102 
   7103  return uri.forget();
   7104 }
   7105 
   7106 void nsGlobalWindowOuter::FlushPendingNotifications(FlushType aType) {
   7107  if (mDoc) {
   7108    mDoc->FlushPendingNotifications(aType);
   7109  }
   7110 }
   7111 
   7112 void nsGlobalWindowOuter::EnsureSizeAndPositionUpToDate() {
   7113  // If we're a subframe, make sure our size is up to date.  Make sure to go
   7114  // through the document chain rather than the window chain to not flush on
   7115  // detached iframes, see bug 1545516.
   7116  if (mDoc && mDoc->StyleOrLayoutObservablyDependsOnParentDocumentLayout()) {
   7117    RefPtr<Document> parent = mDoc->GetInProcessParentDocument();
   7118    parent->FlushPendingNotifications(FlushType::Layout);
   7119  }
   7120 }
   7121 
   7122 already_AddRefed<nsISupports> nsGlobalWindowOuter::SaveWindowState() {
   7123  MOZ_ASSERT(!mozilla::SessionHistoryInParent());
   7124 
   7125  if (!mContext || !GetWrapperPreserveColor()) {
   7126    // The window may be getting torn down; don't bother saving state.
   7127    return nullptr;
   7128  }
   7129 
   7130  nsGlobalWindowInner* inner = GetCurrentInnerWindowInternal(this);
   7131  NS_ASSERTION(inner, "No inner window to save");
   7132 
   7133  if (WindowContext* wc = inner->GetWindowContext()) {
   7134    MOZ_ASSERT(!wc->GetWindowStateSaved());
   7135    (void)wc->SetWindowStateSaved(true);
   7136  }
   7137 
   7138  // Don't do anything else to this inner window! After this point, all
   7139  // calls to SetTimeoutOrInterval will create entries in the timeout
   7140  // list that will only run after this window has come out of the bfcache.
   7141  // Also, while we're frozen, we won't dispatch online/offline events
   7142  // to the page.
   7143  inner->Freeze();
   7144 
   7145  nsCOMPtr<nsISupports> state = new WindowStateHolder(inner);
   7146 
   7147  MOZ_LOG(gPageCacheLog, LogLevel::Debug,
   7148          ("saving window state, state = %p", (void*)state));
   7149 
   7150  return state.forget();
   7151 }
   7152 
   7153 nsresult nsGlobalWindowOuter::RestoreWindowState(nsISupports* aState) {
   7154  MOZ_ASSERT(!mozilla::SessionHistoryInParent());
   7155 
   7156  if (!mContext || !GetWrapperPreserveColor()) {
   7157    // The window may be getting torn down; don't bother restoring state.
   7158    return NS_OK;
   7159  }
   7160 
   7161  nsCOMPtr<WindowStateHolder> holder = do_QueryInterface(aState);
   7162  NS_ENSURE_TRUE(holder, NS_ERROR_FAILURE);
   7163 
   7164  MOZ_LOG(gPageCacheLog, LogLevel::Debug,
   7165          ("restoring window state, state = %p", (void*)holder));
   7166 
   7167  // And we're ready to go!
   7168  nsGlobalWindowInner* inner = GetCurrentInnerWindowInternal(this);
   7169 
   7170  // if a link is focused, refocus with the FLAG_SHOWRING flag set. This makes
   7171  // it easy to tell which link was last clicked when going back a page.
   7172  RefPtr<Element> focusedElement = inner->GetFocusedElement();
   7173  if (nsContentUtils::ContentIsLink(focusedElement)) {
   7174    if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
   7175      fm->SetFocus(focusedElement, nsIFocusManager::FLAG_NOSCROLL |
   7176                                       nsIFocusManager::FLAG_SHOWRING);
   7177    }
   7178  }
   7179 
   7180  if (WindowContext* wc = inner->GetWindowContext()) {
   7181    MOZ_ASSERT(wc->GetWindowStateSaved());
   7182    (void)wc->SetWindowStateSaved(false);
   7183  }
   7184 
   7185  inner->Thaw();
   7186 
   7187  holder->DidRestoreWindow();
   7188 
   7189  return NS_OK;
   7190 }
   7191 
   7192 void nsGlobalWindowOuter::AddSizeOfIncludingThis(
   7193    nsWindowSizes& aWindowSizes) const {
   7194  aWindowSizes.mDOMSizes.mDOMOtherSize +=
   7195      aWindowSizes.mState.mMallocSizeOf(this);
   7196 }
   7197 
   7198 uint32_t nsGlobalWindowOuter::GetAutoActivateVRDisplayID() {
   7199  uint32_t retVal = mAutoActivateVRDisplayID;
   7200  mAutoActivateVRDisplayID = 0;
   7201  return retVal;
   7202 }
   7203 
   7204 void nsGlobalWindowOuter::SetAutoActivateVRDisplayID(
   7205    uint32_t aAutoActivateVRDisplayID) {
   7206  mAutoActivateVRDisplayID = aAutoActivateVRDisplayID;
   7207 }
   7208 
   7209 already_AddRefed<nsWindowRoot> nsGlobalWindowOuter::GetWindowRootOuter() {
   7210  nsCOMPtr<nsPIWindowRoot> root = GetTopWindowRoot();
   7211  return root.forget().downcast<nsWindowRoot>();
   7212 }
   7213 
   7214 nsIDOMWindowUtils* nsGlobalWindowOuter::WindowUtils() {
   7215  if (!mWindowUtils) {
   7216    mWindowUtils = new nsDOMWindowUtils(this);
   7217  }
   7218  return mWindowUtils;
   7219 }
   7220 
   7221 bool nsGlobalWindowOuter::IsInSyncOperation() {
   7222  return GetExtantDoc() && GetExtantDoc()->IsInSyncOperation();
   7223 }
   7224 
   7225 // Note: This call will lock the cursor, it will not change as it moves.
   7226 // To unlock, the cursor must be set back to Auto.
   7227 void nsGlobalWindowOuter::SetCursorOuter(const nsACString& aCursor,
   7228                                         ErrorResult& aError) {
   7229  auto cursor = StyleCursorKind::Auto;
   7230  if (!Servo_CursorKind_Parse(&aCursor, &cursor)) {
   7231    // FIXME: It's a bit weird that this doesn't throw but stuff below does, but
   7232    // matches previous behavior so...
   7233    return;
   7234  }
   7235 
   7236  RefPtr<nsPresContext> presContext;
   7237  if (mDocShell) {
   7238    presContext = mDocShell->GetPresContext();
   7239  }
   7240 
   7241  if (presContext) {
   7242    PresShell* presShell = mDocShell->GetPresShell();
   7243    if (!presShell) {
   7244      aError.Throw(NS_ERROR_FAILURE);
   7245      return;
   7246    }
   7247 
   7248    // Need root widget.
   7249    nsIWidget* widget = presShell->GetRootWidget();
   7250    if (!widget) {
   7251      aError.Throw(NS_ERROR_FAILURE);
   7252      return;
   7253    }
   7254 
   7255    // Call esm and set cursor.
   7256    aError = presContext->EventStateManager()->SetCursor(
   7257        cursor, nullptr, {}, Nothing(), widget, true);
   7258  }
   7259 }
   7260 
   7261 nsIBrowserDOMWindow* nsGlobalWindowOuter::GetBrowserDOMWindow() {
   7262  MOZ_RELEASE_ASSERT(IsChromeWindow());
   7263  return mChromeFields.mBrowserDOMWindow;
   7264 }
   7265 
   7266 void nsGlobalWindowOuter::SetBrowserDOMWindowOuter(
   7267    nsIBrowserDOMWindow* aBrowserWindow) {
   7268  MOZ_ASSERT(IsChromeWindow());
   7269  mChromeFields.mBrowserDOMWindow = aBrowserWindow;
   7270 }
   7271 
   7272 ChromeMessageBroadcaster* nsGlobalWindowOuter::GetMessageManager() {
   7273  if (!mInnerWindow) {
   7274    NS_WARNING("No inner window available!");
   7275    return nullptr;
   7276  }
   7277  return GetCurrentInnerWindowInternal(this)->MessageManager();
   7278 }
   7279 
   7280 ChromeMessageBroadcaster* nsGlobalWindowOuter::GetGroupMessageManager(
   7281    const nsAString& aGroup) {
   7282  if (!mInnerWindow) {
   7283    NS_WARNING("No inner window available!");
   7284    return nullptr;
   7285  }
   7286  return GetCurrentInnerWindowInternal(this)->GetGroupMessageManager(aGroup);
   7287 }
   7288 
   7289 void nsGlobalWindowOuter::InitWasOffline() { mWasOffline = NS_IsOffline(); }
   7290 
   7291 #if defined(_WINDOWS_) && !defined(MOZ_WRAPPED_WINDOWS_H)
   7292 #  pragma message( \
   7293      "wrapper failure reason: " MOZ_WINDOWS_WRAPPER_DISABLED_REASON)
   7294 #  error "Never include unwrapped windows.h in this file!"
   7295 #endif
   7296 
   7297 // Helper called by methods that move/resize the window,
   7298 // to ensure the presContext (if any) is aware of resolution
   7299 // change that may happen in multi-monitor configuration.
   7300 void nsGlobalWindowOuter::CheckForDPIChange() {
   7301  if (mDocShell) {
   7302    RefPtr<nsPresContext> presContext = mDocShell->GetPresContext();
   7303    if (presContext) {
   7304      if (presContext->DeviceContext()->CheckDPIChange()) {
   7305        presContext->UIResolutionChanged();
   7306      }
   7307    }
   7308  }
   7309 }
   7310 
   7311 nsresult nsGlobalWindowOuter::Dispatch(
   7312    already_AddRefed<nsIRunnable>&& aRunnable) const {
   7313  MOZ_RELEASE_ASSERT(NS_IsMainThread());
   7314  return NS_DispatchToCurrentThread(std::move(aRunnable));
   7315 }
   7316 
   7317 nsISerialEventTarget* nsGlobalWindowOuter::SerialEventTarget() const {
   7318  MOZ_RELEASE_ASSERT(NS_IsMainThread());
   7319  return GetMainThreadSerialEventTarget();
   7320 }
   7321 
   7322 void nsGlobalWindowOuter::MaybeResetWindowName(Document* aNewDocument) {
   7323  MOZ_ASSERT(aNewDocument);
   7324 
   7325  if (!StaticPrefs::privacy_window_name_update_enabled()) {
   7326    return;
   7327  }
   7328 
   7329  const LoadingSessionHistoryInfo* info =
   7330      nsDocShell::Cast(mDocShell)->GetLoadingSessionHistoryInfo();
   7331  if (!info || info->mForceMaybeResetName.isNothing()) {
   7332    // We only reset the window name for the top-level content as well as
   7333    // storing in session entries.
   7334    if (!GetBrowsingContext()->IsTopContent()) {
   7335      return;
   7336    }
   7337 
   7338    // Following implements https://html.spec.whatwg.org/#history-traversal:
   7339    // Step 4.2. Check if the loading document has a different origin than the
   7340    // previous document.
   7341 
   7342    // We don't need to do anything if we haven't loaded a non-initial document.
   7343    if (!GetBrowsingContext()->GetHasLoadedNonInitialDocument()) {
   7344      return;
   7345    }
   7346 
   7347    // If we have an existing document, directly check the document prinicpals
   7348    // with the new document to know if it is cross-origin.
   7349    //
   7350    // Note that there will be an issue of initial document handling in Fission
   7351    // when running the WPT unset_context_name-1.html. In the test, the first
   7352    // about:blank page would be loaded with the principal of the testing domain
   7353    // in Fission and the window.name will be set there. Then, The window.name
   7354    // won't be reset after navigating to the testing page because the principal
   7355    // is the same. But, it won't be the case for non-Fission mode that the
   7356    // first about:blank will be loaded with a null principal and the
   7357    // window.name will be reset when loading the test page.
   7358    if (mDoc && mDoc->NodePrincipal()->Equals(aNewDocument->NodePrincipal())) {
   7359      return;
   7360    }
   7361 
   7362    // If we don't have an existing document, and if it's not the initial
   7363    // about:blank, we could be loading a document because of the
   7364    // process-switching. In this case, this should be a cross-origin
   7365    // navigation.
   7366  } else if (!info->mForceMaybeResetName.ref()) {
   7367    return;
   7368  }
   7369 
   7370  // Step 4.2.2 Store the window.name into all session history entries that have
   7371  // the same origin as the previous document.
   7372  nsDocShell::Cast(mDocShell)->StoreWindowNameToSHEntries();
   7373 
   7374  // Step 4.2.3 Clear the window.name if the browsing context is the top-level
   7375  // content and doesn't have an opener.
   7376 
   7377  // We need to reset the window name in case of a cross-origin navigation,
   7378  // without an opener.
   7379  RefPtr<BrowsingContext> opener = GetOpenerBrowsingContext();
   7380  if (opener) {
   7381    return;
   7382  }
   7383 
   7384  (void)mBrowsingContext->SetName(EmptyString());
   7385 }
   7386 
   7387 nsGlobalWindowOuter::TemporarilyDisableDialogs::TemporarilyDisableDialogs(
   7388    BrowsingContext* aBC) {
   7389  BrowsingContextGroup* group = aBC->Group();
   7390  if (!group) {
   7391    NS_ERROR(
   7392        "nsGlobalWindowOuter::TemporarilyDisableDialogs called without a "
   7393        "browsing context group?");
   7394    return;
   7395  }
   7396 
   7397  if (group) {
   7398    mGroup = group;
   7399    mSavedDialogsEnabled = group->GetAreDialogsEnabled();
   7400    group->SetAreDialogsEnabled(false);
   7401  }
   7402 }
   7403 
   7404 nsGlobalWindowOuter::TemporarilyDisableDialogs::~TemporarilyDisableDialogs() {
   7405  if (mGroup) {
   7406    mGroup->SetAreDialogsEnabled(mSavedDialogsEnabled);
   7407  }
   7408 }
   7409 
   7410 /* static */
   7411 already_AddRefed<nsGlobalWindowOuter> nsGlobalWindowOuter::Create(
   7412    nsDocShell* aDocShell, bool aIsChrome) {
   7413  uint64_t outerWindowID = aDocShell->GetOuterWindowID();
   7414  RefPtr<nsGlobalWindowOuter> window = new nsGlobalWindowOuter(outerWindowID);
   7415  if (aIsChrome) {
   7416    window->mIsChrome = true;
   7417  }
   7418  window->SetDocShell(aDocShell);
   7419 
   7420  window->InitWasOffline();
   7421  return window.forget();
   7422 }
   7423 
   7424 nsIURI* nsPIDOMWindowOuter::GetDocumentURI() const {
   7425  return mDoc ? mDoc->GetDocumentURI() : mDocumentURI.get();
   7426 }
   7427 
   7428 void nsPIDOMWindowOuter::MaybeCreateDoc() {
   7429  MOZ_ASSERT(!mDoc);
   7430  if (nsIDocShell* docShell = GetDocShell()) {
   7431    // Note that |document| here is the same thing as our mDoc, but we
   7432    // don't have to explicitly set the member variable because the docshell
   7433    // has already called SetNewDocument().
   7434    nsCOMPtr<Document> document = docShell->GetDocument();
   7435    (void)document;
   7436  }
   7437 }
   7438 
   7439 void nsPIDOMWindowOuter::SetChromeEventHandlerInternal(
   7440    EventTarget* aChromeEventHandler) {
   7441  // Out-of-line so we don't need to include ContentFrameMessageManager.h in
   7442  // nsPIDOMWindow.h.
   7443  mChromeEventHandler = aChromeEventHandler;
   7444 
   7445  // mParentTarget and mMessageManager will be set when the next event is
   7446  // dispatched or someone asks for our message manager.
   7447  mParentTarget = nullptr;
   7448  mMessageManager = nullptr;
   7449 }
   7450 
   7451 mozilla::dom::DocGroup* nsPIDOMWindowOuter::GetDocGroup() const {
   7452  Document* doc = GetExtantDoc();
   7453  if (doc) {
   7454    return doc->GetDocGroup();
   7455  }
   7456  return nullptr;
   7457 }
   7458 
   7459 nsPIDOMWindowOuter::nsPIDOMWindowOuter(uint64_t aWindowID)
   7460    : mFrameElement(nullptr),
   7461      mModalStateDepth(0),
   7462      mSuppressEventHandlingDepth(0),
   7463      mIsBackground(false),
   7464      mIsRootOuterWindow(false),
   7465      mInnerWindow(nullptr),
   7466      mWindowID(aWindowID),
   7467      mMarkedCCGeneration(0) {}
   7468 
   7469 nsPIDOMWindowOuter::~nsPIDOMWindowOuter() = default;