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