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