Bug 1700051: part 35) Reduce accessibility of `mSoftText.mDOMMapping` to `private...
[gecko.git] / dom / base / nsGlobalWindowOuter.cpp
blobd0243a1b7ba0871970c1f3b5ca3a57b97630b649
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/. */
7 #include "mozilla/Assertions.h"
8 #include "mozilla/ScopeExit.h"
9 #include "nsGlobalWindow.h"
11 #include <algorithm>
13 #include "mozilla/MemoryReporting.h"
15 // Local Includes
16 #include "Navigator.h"
17 #include "mozilla/Encoding.h"
18 #include "nsContentSecurityManager.h"
19 #include "nsGlobalWindowOuter.h"
20 #include "nsScreen.h"
21 #include "nsHistory.h"
22 #include "nsDOMNavigationTiming.h"
23 #include "nsIDOMStorageManager.h"
24 #include "nsISecureBrowserUI.h"
25 #include "nsIWebProgressListener.h"
26 #include "mozilla/AntiTrackingUtils.h"
27 #include "mozilla/ContentBlocking.h"
28 #include "mozilla/dom/AutoPrintEventDispatcher.h"
29 #include "mozilla/dom/BindingUtils.h"
30 #include "mozilla/dom/BrowserChild.h"
31 #include "mozilla/dom/BrowsingContextBinding.h"
32 #include "mozilla/dom/CanonicalBrowsingContext.h"
33 #include "mozilla/dom/ContentChild.h"
34 #include "mozilla/dom/ContentFrameMessageManager.h"
35 #include "mozilla/dom/DocumentInlines.h"
36 #include "mozilla/dom/EventTarget.h"
37 #include "mozilla/dom/LocalStorage.h"
38 #include "mozilla/dom/LSObject.h"
39 #include "mozilla/dom/Storage.h"
40 #include "mozilla/dom/MaybeCrossOriginObject.h"
41 #include "mozilla/dom/Performance.h"
42 #include "mozilla/dom/StorageEvent.h"
43 #include "mozilla/dom/StorageEventBinding.h"
44 #include "mozilla/dom/StorageNotifierService.h"
45 #include "mozilla/dom/StorageUtils.h"
46 #include "mozilla/dom/Timeout.h"
47 #include "mozilla/dom/TimeoutHandler.h"
48 #include "mozilla/dom/TimeoutManager.h"
49 #include "mozilla/dom/WindowContext.h"
50 #include "mozilla/dom/WindowFeatures.h" // WindowFeatures
51 #include "mozilla/dom/WindowProxyHolder.h"
52 #include "mozilla/IntegerPrintfMacros.h"
53 #if defined(MOZ_WIDGET_ANDROID)
54 # include "mozilla/dom/WindowOrientationObserver.h"
55 #endif
56 #include "nsBaseCommandController.h"
57 #include "nsError.h"
58 #include "nsICookieService.h"
59 #include "nsISizeOfEventTarget.h"
60 #include "nsDOMJSUtils.h"
61 #include "nsArrayUtils.h"
62 #include "nsIDocShellTreeOwner.h"
63 #include "nsIInterfaceRequestorUtils.h"
64 #include "nsIPermissionManager.h"
65 #include "nsIScriptContext.h"
66 #include "nsWindowMemoryReporter.h"
67 #include "nsWindowSizes.h"
68 #include "WindowNamedPropertiesHandler.h"
69 #include "nsFrameSelection.h"
70 #include "nsNetUtil.h"
71 #include "nsVariant.h"
72 #include "nsPrintfCString.h"
73 #include "mozilla/intl/LocaleService.h"
74 #include "WindowDestroyedEvent.h"
75 #include "nsDocShellLoadState.h"
76 #include "mozilla/dom/WindowGlobalChild.h"
78 // Helper Classes
79 #include "nsJSUtils.h"
80 #include "jsapi.h"
81 #include "jsfriendapi.h"
82 #include "js/friend/StackLimits.h" // js::AutoCheckRecursionLimit
83 #include "js/friend/WindowProxy.h" // js::IsWindowProxy, js::SetWindowProxy
84 #include "js/PropertySpec.h"
85 #include "js/Wrapper.h"
86 #include "nsLayoutUtils.h"
87 #include "nsReadableUtils.h"
88 #include "nsJSEnvironment.h"
89 #include "mozilla/dom/ScriptSettings.h"
90 #include "mozilla/Preferences.h"
91 #include "mozilla/Likely.h"
92 #include "mozilla/SpinEventLoopUntil.h"
93 #include "mozilla/Sprintf.h"
94 #include "mozilla/Unused.h"
96 // Other Classes
97 #include "mozilla/dom/BarProps.h"
98 #include "nsContentCID.h"
99 #include "nsLayoutStatics.h"
100 #include "nsCCUncollectableMarker.h"
101 #include "mozilla/dom/WorkerCommon.h"
102 #include "mozilla/dom/ToJSValue.h"
103 #include "nsJSPrincipals.h"
104 #include "mozilla/Attributes.h"
105 #include "mozilla/Components.h"
106 #include "mozilla/Debug.h"
107 #include "mozilla/EventListenerManager.h"
108 #include "mozilla/EventStates.h"
109 #include "mozilla/MouseEvents.h"
110 #include "mozilla/PresShell.h"
111 #include "mozilla/ProcessHangMonitor.h"
112 #include "mozilla/StaticPrefs_dom.h"
113 #include "mozilla/StaticPrefs_print.h"
114 #include "mozilla/StaticPrefs_fission.h"
115 #include "mozilla/ThrottledEventQueue.h"
116 #include "AudioChannelService.h"
117 #include "nsAboutProtocolUtils.h"
118 #include "nsCharTraits.h" // NS_IS_HIGH/LOW_SURROGATE
119 #include "PostMessageEvent.h"
120 #include "mozilla/dom/DocGroup.h"
121 #include "mozilla/net/CookieJarSettings.h"
123 // Interfaces Needed
124 #include "nsIFrame.h"
125 #include "nsCanvasFrame.h"
126 #include "nsIWidget.h"
127 #include "nsIWidgetListener.h"
128 #include "nsIBaseWindow.h"
129 #include "nsIDeviceSensors.h"
130 #include "nsIContent.h"
131 #include "nsIDocShell.h"
132 #include "mozilla/dom/Document.h"
133 #include "Crypto.h"
134 #include "nsDOMString.h"
135 #include "nsIEmbeddingSiteWindow.h"
136 #include "nsThreadUtils.h"
137 #include "nsILoadContext.h"
138 #include "nsIScrollableFrame.h"
139 #include "nsView.h"
140 #include "nsViewManager.h"
141 #include "nsIPrompt.h"
142 #include "nsIPromptService.h"
143 #include "nsIPromptFactory.h"
144 #include "nsIWritablePropertyBag2.h"
145 #include "nsIWebNavigation.h"
146 #include "nsIWebBrowserChrome.h"
147 #include "nsIWebBrowserFind.h" // For window.find()
148 #include "nsComputedDOMStyle.h"
149 #include "nsDOMCID.h"
150 #include "nsDOMWindowUtils.h"
151 #include "nsIWindowWatcher.h"
152 #include "nsPIWindowWatcher.h"
153 #include "nsIContentViewer.h"
154 #include "nsIScriptError.h"
155 #include "nsISHistory.h"
156 #include "nsIControllers.h"
157 #include "nsGlobalWindowCommands.h"
158 #include "nsQueryObject.h"
159 #include "nsContentUtils.h"
160 #include "nsCSSProps.h"
161 #include "nsIURIFixup.h"
162 #include "nsIURIMutator.h"
163 #include "mozilla/EventDispatcher.h"
164 #include "mozilla/EventStateManager.h"
165 #include "nsIObserverService.h"
166 #include "nsFocusManager.h"
167 #include "nsIAppWindow.h"
168 #include "nsServiceManagerUtils.h"
169 #include "mozilla/dom/CustomEvent.h"
170 #include "nsIScreenManager.h"
171 #include "nsIClassifiedChannel.h"
172 #include "nsIXULRuntime.h"
173 #include "xpcprivate.h"
175 #ifdef NS_PRINTING
176 # include "nsIPrintSettings.h"
177 # include "nsIPrintSettingsService.h"
178 # include "nsIWebBrowserPrint.h"
179 #endif
181 #include "nsWindowRoot.h"
182 #include "nsNetCID.h"
183 #include "nsIArray.h"
185 #include "nsIDOMXULCommandDispatcher.h"
187 #include "mozilla/GlobalKeyListener.h"
189 #include "nsIDragService.h"
190 #include "mozilla/dom/Element.h"
191 #include "mozilla/dom/Selection.h"
192 #include "nsFrameLoader.h"
193 #include "nsFrameLoaderOwner.h"
194 #include "nsXPCOMCID.h"
195 #include "mozilla/Logging.h"
196 #include "mozilla/ProfilerMarkers.h"
197 #include "prenv.h"
199 #include "mozilla/dom/IDBFactory.h"
200 #include "mozilla/dom/MessageChannel.h"
201 #include "mozilla/dom/Promise.h"
203 #include "mozilla/dom/Gamepad.h"
204 #include "mozilla/dom/GamepadManager.h"
206 #include "gfxVR.h"
207 #include "VRShMem.h"
208 #include "FxRWindowManager.h"
209 #include "mozilla/dom/VRDisplay.h"
210 #include "mozilla/dom/VRDisplayEvent.h"
211 #include "mozilla/dom/VRDisplayEventBinding.h"
212 #include "mozilla/dom/VREventObserver.h"
214 #include "nsRefreshDriver.h"
215 #include "Layers.h"
217 #include "mozilla/extensions/WebExtensionPolicy.h"
219 #include "mozilla/BasePrincipal.h"
220 #include "mozilla/Services.h"
221 #include "mozilla/Telemetry.h"
222 #include "mozilla/dom/Location.h"
223 #include "nsHTMLDocument.h"
224 #include "nsWrapperCacheInlines.h"
225 #include "mozilla/DOMEventTargetHelper.h"
226 #include "prrng.h"
227 #include "nsSandboxFlags.h"
228 #include "nsBaseCommandController.h"
229 #include "nsXULControllers.h"
230 #include "mozilla/dom/AudioContext.h"
231 #include "mozilla/dom/BrowserElementDictionariesBinding.h"
232 #include "mozilla/dom/BrowsingContextGroup.h"
233 #include "mozilla/dom/cache/CacheStorage.h"
234 #include "mozilla/dom/Console.h"
235 #include "mozilla/dom/Fetch.h"
236 #include "mozilla/dom/FunctionBinding.h"
237 #include "mozilla/dom/HashChangeEvent.h"
238 #include "mozilla/dom/IntlUtils.h"
239 #include "mozilla/dom/PopStateEvent.h"
240 #include "mozilla/dom/PopupBlockedEvent.h"
241 #include "mozilla/dom/PrimitiveConversions.h"
242 #include "mozilla/dom/WindowBinding.h"
243 #include "nsIBrowserChild.h"
244 #include "mozilla/dom/MediaQueryList.h"
245 #include "mozilla/dom/ScriptSettings.h"
246 #include "mozilla/dom/NavigatorBinding.h"
247 #include "mozilla/dom/ImageBitmap.h"
248 #include "mozilla/dom/ImageBitmapBinding.h"
249 #include "mozilla/dom/ServiceWorkerRegistration.h"
250 #include "mozilla/dom/U2F.h"
251 #include "mozilla/dom/WebIDLGlobalNameHash.h"
252 #include "mozilla/dom/Worklet.h"
253 #include "AccessCheck.h"
255 #ifdef HAVE_SIDEBAR
256 # include "mozilla/dom/ExternalBinding.h"
257 #endif
259 #ifdef MOZ_WEBSPEECH
260 # include "mozilla/dom/SpeechSynthesis.h"
261 #endif
263 #ifdef ANDROID
264 # include <android/log.h>
265 #endif
267 #ifdef XP_WIN
268 # include <process.h>
269 # define getpid _getpid
270 #else
271 # include <unistd.h> // for getpid()
272 #endif
274 using namespace mozilla;
275 using namespace mozilla::dom;
276 using namespace mozilla::dom::ipc;
277 using mozilla::BasePrincipal;
278 using mozilla::OriginAttributes;
279 using mozilla::TimeStamp;
281 #define FORWARD_TO_INNER(method, args, err_rval) \
282 PR_BEGIN_MACRO \
283 if (!mInnerWindow) { \
284 NS_WARNING("No inner window available!"); \
285 return err_rval; \
287 return GetCurrentInnerWindowInternal()->method args; \
288 PR_END_MACRO
290 #define FORWARD_TO_INNER_VOID(method, args) \
291 PR_BEGIN_MACRO \
292 if (!mInnerWindow) { \
293 NS_WARNING("No inner window available!"); \
294 return; \
296 GetCurrentInnerWindowInternal()->method args; \
297 return; \
298 PR_END_MACRO
300 // Same as FORWARD_TO_INNER, but this will create a fresh inner if an
301 // inner doesn't already exists.
302 #define FORWARD_TO_INNER_CREATE(method, args, err_rval) \
303 PR_BEGIN_MACRO \
304 if (!mInnerWindow) { \
305 if (mIsClosed) { \
306 return err_rval; \
308 nsCOMPtr<Document> kungFuDeathGrip = GetDoc(); \
309 ::mozilla::Unused << kungFuDeathGrip; \
310 if (!mInnerWindow) { \
311 return err_rval; \
314 return GetCurrentInnerWindowInternal()->method args; \
315 PR_END_MACRO
317 static LazyLogModule gDOMLeakPRLogOuter("DOMLeakOuter");
318 extern LazyLogModule gPageCacheLog;
320 #ifdef DEBUG
321 static LazyLogModule gDocShellAndDOMWindowLeakLogging(
322 "DocShellAndDOMWindowLeak");
323 #endif
325 nsGlobalWindowOuter::OuterWindowByIdTable*
326 nsGlobalWindowOuter::sOuterWindowsById = nullptr;
328 /* static */
329 nsPIDOMWindowOuter* nsPIDOMWindowOuter::GetFromCurrentInner(
330 nsPIDOMWindowInner* aInner) {
331 if (!aInner) {
332 return nullptr;
335 nsPIDOMWindowOuter* outer = aInner->GetOuterWindow();
336 if (!outer || outer->GetCurrentInnerWindow() != aInner) {
337 return nullptr;
340 return outer;
343 //*****************************************************************************
344 // nsOuterWindowProxy: Outer Window Proxy
345 //*****************************************************************************
347 // Give OuterWindowProxyClass 2 reserved slots, like the other wrappers, so
348 // JSObject::swap can swap it with CrossCompartmentWrappers without requiring
349 // malloc.
351 // We store the nsGlobalWindowOuter* in our first slot.
353 // We store our holder weakmap in the second slot.
354 const JSClass OuterWindowProxyClass = PROXY_CLASS_DEF(
355 "Proxy", JSCLASS_HAS_RESERVED_SLOTS(2)); /* additional class flags */
357 static const size_t OUTER_WINDOW_SLOT = 0;
358 static const size_t HOLDER_WEAKMAP_SLOT = 1;
360 class nsOuterWindowProxy : public MaybeCrossOriginObject<js::Wrapper> {
361 typedef MaybeCrossOriginObject<js::Wrapper> Base;
363 public:
364 constexpr nsOuterWindowProxy() : Base(0) {}
366 bool finalizeInBackground(const JS::Value& priv) const override {
367 return false;
370 // Standard internal methods
372 * Implementation of [[GetOwnProperty]] as defined at
373 * https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-getownproperty
375 * "proxy" is the WindowProxy object involved. It may not be same-compartment
376 * with cx.
378 bool getOwnPropertyDescriptor(
379 JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
380 JS::MutableHandle<Maybe<JS::PropertyDescriptor>> desc) const override;
383 * Implementation of the same-origin case of
384 * <https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-getownproperty>.
386 bool definePropertySameOrigin(JSContext* cx, JS::Handle<JSObject*> proxy,
387 JS::Handle<jsid> id,
388 JS::Handle<JS::PropertyDescriptor> desc,
389 JS::ObjectOpResult& result) const override;
392 * Implementation of [[OwnPropertyKeys]] as defined at
394 * https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-ownpropertykeys
396 * "proxy" is the WindowProxy object involved. It may not be same-compartment
397 * with cx.
399 bool ownPropertyKeys(JSContext* cx, JS::Handle<JSObject*> proxy,
400 JS::MutableHandleVector<jsid> props) const override;
402 * Implementation of [[Delete]] as defined at
403 * https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-delete
405 * "proxy" is the WindowProxy object involved. It may not be same-compartment
406 * with cx.
408 bool delete_(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
409 JS::ObjectOpResult& result) const override;
412 * Implementaton of hook for superclass getPrototype() method.
414 JSObject* getSameOriginPrototype(JSContext* cx) const override;
417 * Implementation of [[HasProperty]] internal method as defined at
418 * https://tc39.github.io/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-hasproperty-p
420 * "proxy" is the WindowProxy object involved. It may not be same-compartment
421 * with cx.
423 * Note that the HTML spec does not define an override for this internal
424 * method, so we just want the "normal object" behavior. We have to override
425 * it, because js::Wrapper also overrides, with "not normal" behavior.
427 bool has(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
428 bool* bp) const override;
431 * Implementation of [[Get]] internal method as defined at
432 * <https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-get>.
434 * "proxy" is the WindowProxy object involved. It may or may not be
435 * same-compartment with "cx".
437 * "receiver" is the receiver ("this") for the get. It will be
438 * same-compartment with "cx".
440 * "vp" is the return value. It will be same-compartment with "cx".
442 bool get(JSContext* cx, JS::Handle<JSObject*> proxy,
443 JS::Handle<JS::Value> receiver, JS::Handle<jsid> id,
444 JS::MutableHandle<JS::Value> vp) const override;
447 * Implementation of [[Set]] internal method as defined at
448 * <https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-set>.
450 * "proxy" is the WindowProxy object involved. It may or may not be
451 * same-compartment with "cx".
453 * "v" is the value being set. It will be same-compartment with "cx".
455 * "receiver" is the receiver ("this") for the set. It will be
456 * same-compartment with "cx".
458 bool set(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
459 JS::Handle<JS::Value> v, JS::Handle<JS::Value> receiver,
460 JS::ObjectOpResult& result) const override;
462 // SpiderMonkey extensions
464 * Implementation of SpiderMonkey extension which just checks whether this
465 * object has the property. Basically Object.getOwnPropertyDescriptor(obj,
466 * prop) !== undefined. but does not require reifying the descriptor.
468 * We have to override this because js::Wrapper overrides it, but we want
469 * different behavior from js::Wrapper.
471 * "proxy" is the WindowProxy object involved. It may not be same-compartment
472 * with cx.
474 bool hasOwn(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
475 bool* bp) const override;
478 * Implementation of SpiderMonkey extension which is used as a fast path for
479 * enumerating.
481 * We have to override this because js::Wrapper overrides it, but we want
482 * different behavior from js::Wrapper.
484 * "proxy" is the WindowProxy object involved. It may not be same-compartment
485 * with cx.
487 bool getOwnEnumerablePropertyKeys(
488 JSContext* cx, JS::Handle<JSObject*> proxy,
489 JS::MutableHandleVector<jsid> props) const override;
492 * Hook used by SpiderMonkey to implement Object.prototype.toString.
494 const char* className(JSContext* cx,
495 JS::Handle<JSObject*> wrapper) const override;
497 void finalize(JSFreeOp* fop, JSObject* proxy) const override;
498 size_t objectMoved(JSObject* proxy, JSObject* old) const override;
500 bool isCallable(JSObject* obj) const override { return false; }
501 bool isConstructor(JSObject* obj) const override { return false; }
503 static const nsOuterWindowProxy singleton;
505 static nsGlobalWindowOuter* GetOuterWindow(JSObject* proxy) {
506 nsGlobalWindowOuter* outerWindow =
507 nsGlobalWindowOuter::FromSupports(static_cast<nsISupports*>(
508 js::GetProxyReservedSlot(proxy, OUTER_WINDOW_SLOT).toPrivate()));
509 return outerWindow;
512 protected:
513 // False return value means we threw an exception. True return value
514 // but false "found" means we didn't have a subframe at that index.
515 bool GetSubframeWindow(JSContext* cx, JS::Handle<JSObject*> proxy,
516 JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp,
517 bool& found) const;
519 // Returns a non-null window only if id is an index and we have a
520 // window at that index.
521 Nullable<WindowProxyHolder> GetSubframeWindow(JSContext* cx,
522 JS::Handle<JSObject*> proxy,
523 JS::Handle<jsid> id) const;
525 bool AppendIndexedPropertyNames(JSObject* proxy,
526 JS::MutableHandleVector<jsid> props) const;
528 using MaybeCrossOriginObjectMixins::EnsureHolder;
529 bool EnsureHolder(JSContext* cx, JS::Handle<JSObject*> proxy,
530 JS::MutableHandle<JSObject*> holder) const override;
532 // Helper method for creating a special "print" method that allows printing
533 // our PDF-viewer documents even if you're not same-origin with them.
535 // aProxy must be our nsOuterWindowProxy. It will not be same-compartment
536 // with aCx, since we only use this on the different-origin codepath!
538 // Can return true without filling in aDesc, which corresponds to not exposing
539 // a "print" method.
540 static bool MaybeGetPDFJSPrintMethod(
541 JSContext* cx, JS::Handle<JSObject*> proxy,
542 JS::MutableHandle<Maybe<JS::PropertyDescriptor>> desc);
544 // The actual "print" method we use for the PDFJS case.
545 static bool PDFJSPrintMethod(JSContext* cx, unsigned argc, JS::Value* vp);
547 // Helper method to get the pre-PDF-viewer-messing-with-it principal from an
548 // inner window. Will return null if this is not a PDF-viewer inner or if the
549 // principal could not be found for some reason.
550 static already_AddRefed<nsIPrincipal> GetNoPDFJSPrincipal(
551 nsGlobalWindowInner* inner);
554 const char* nsOuterWindowProxy::className(JSContext* cx,
555 JS::Handle<JSObject*> proxy) const {
556 MOZ_ASSERT(js::IsProxy(proxy));
558 if (!IsPlatformObjectSameOrigin(cx, proxy)) {
559 return "Object";
562 return "Window";
565 void nsOuterWindowProxy::finalize(JSFreeOp* fop, JSObject* proxy) const {
566 nsGlobalWindowOuter* outerWindow = GetOuterWindow(proxy);
567 if (outerWindow) {
568 outerWindow->ClearWrapper(proxy);
569 BrowsingContext* bc = outerWindow->GetBrowsingContext();
570 if (bc) {
571 bc->ClearWindowProxy();
574 // Ideally we would use OnFinalize here, but it's possible that
575 // EnsureScriptEnvironment will later be called on the window, and we don't
576 // want to create a new script object in that case. Therefore, we need to
577 // write a non-null value that will reliably crash when dereferenced.
578 outerWindow->PoisonOuterWindowProxy(proxy);
583 * IsNonConfigurableReadonlyPrimitiveGlobalProp returns true for
584 * property names that fit the following criteria:
586 * 1) The ES spec defines a property with that name on globals.
587 * 2) The property is non-configurable.
588 * 3) The property is non-writable (readonly).
589 * 4) The value of the property is a primitive (so doesn't change
590 * observably on when navigation happens).
592 * Such properties can act as actual non-configurable properties on a
593 * WindowProxy, because they are not affected by navigation.
595 #ifndef RELEASE_OR_BETA
596 static bool IsNonConfigurableReadonlyPrimitiveGlobalProp(JSContext* cx,
597 JS::Handle<jsid> id) {
598 return id == GetJSIDByIndex(cx, XPCJSContext::IDX_NAN) ||
599 id == GetJSIDByIndex(cx, XPCJSContext::IDX_UNDEFINED) ||
600 id == GetJSIDByIndex(cx, XPCJSContext::IDX_INFINITY);
602 #endif
604 bool nsOuterWindowProxy::getOwnPropertyDescriptor(
605 JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
606 JS::MutableHandle<Maybe<JS::PropertyDescriptor>> desc) const {
607 // First check for indexed access. This is
608 // https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-getownproperty
609 // step 2, mostly.
610 JS::Rooted<JS::Value> subframe(cx);
611 bool found;
612 if (!GetSubframeWindow(cx, proxy, id, &subframe, found)) {
613 return false;
615 if (found) {
616 // Step 2.4.
617 FillPropertyDescriptor(cx, desc, proxy, subframe, true);
618 return true;
621 bool isSameOrigin = IsPlatformObjectSameOrigin(cx, proxy);
623 // If we did not find a subframe, we could still have an indexed property
624 // access. In that case we should throw a SecurityError in the cross-origin
625 // case.
626 if (!isSameOrigin && IsArrayIndex(GetArrayIndexFromId(id))) {
627 // Step 2.5.2.
628 return ReportCrossOriginDenial(cx, id, "access"_ns);
631 // Step 2.5.1 is handled via the forwarding to js::Wrapper; it saves us an
632 // IsArrayIndex(GetArrayIndexFromId(id)) here. We'll never have a property on
633 // the Window whose name is an index, because our defineProperty doesn't pass
634 // those on to the Window.
636 // Step 3.
637 if (isSameOrigin) {
638 if (StaticPrefs::dom_missing_prop_counters_enabled() && JSID_IS_ATOM(id)) {
639 Window_Binding::CountMaybeMissingProperty(proxy, id);
642 // Fall through to js::Wrapper.
643 { // Scope for JSAutoRealm while we are dealing with js::Wrapper.
644 // When forwarding to js::Wrapper, we should just enter the Realm of proxy
645 // for now. That's what js::Wrapper expects, and since we're same-origin
646 // anyway this is not changing any security behavior.
647 JSAutoRealm ar(cx, proxy);
648 JS_MarkCrossZoneId(cx, id);
649 bool ok = js::Wrapper::getOwnPropertyDescriptor(cx, proxy, id, desc);
650 if (!ok) {
651 return false;
654 #ifndef RELEASE_OR_BETA // To be turned on in bug 1496510.
655 if (desc.isSome() &&
656 !IsNonConfigurableReadonlyPrimitiveGlobalProp(cx, id)) {
657 (*desc).setConfigurable(true);
659 #endif
662 // Now wrap our descriptor back into the Realm that asked for it.
663 return JS_WrapPropertyDescriptor(cx, desc);
666 // Step 4.
667 if (!CrossOriginGetOwnPropertyHelper(cx, proxy, id, desc)) {
668 return false;
671 // Step 5
672 if (desc.isSome()) {
673 return true;
676 // Non-spec step for the PDF viewer's window.print(). This comes before we
677 // check for named subframes, because in the same-origin case print() would
678 // shadow those.
679 if (id == GetJSIDByIndex(cx, XPCJSContext::IDX_PRINT)) {
680 if (!MaybeGetPDFJSPrintMethod(cx, proxy, desc)) {
681 return false;
684 if (desc.isSome()) {
685 return true;
689 // Step 6 -- check for named subframes.
690 if (JSID_IS_STRING(id)) {
691 nsAutoJSString name;
692 if (!name.init(cx, JSID_TO_STRING(id))) {
693 return false;
695 nsGlobalWindowOuter* win = GetOuterWindow(proxy);
696 if (RefPtr<BrowsingContext> childDOMWin = win->GetChildWindow(name)) {
697 JS::Rooted<JS::Value> childValue(cx);
698 if (!ToJSValue(cx, WindowProxyHolder(childDOMWin), &childValue)) {
699 return false;
701 FillPropertyDescriptor(cx, desc, proxy, childValue,
702 /* readonly = */ true,
703 /* enumerable = */ false);
704 return true;
708 // And step 7.
709 return CrossOriginPropertyFallback(cx, proxy, id, desc);
712 bool nsOuterWindowProxy::definePropertySameOrigin(
713 JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
714 JS::Handle<JS::PropertyDescriptor> desc, JS::ObjectOpResult& result) const {
715 if (IsArrayIndex(GetArrayIndexFromId(id))) {
716 // Spec says to Reject whether this is a supported index or not,
717 // since we have no indexed setter or indexed creator. It is up
718 // to the caller to decide whether to throw a TypeError.
719 return result.failCantDefineWindowElement();
722 JS::ObjectOpResult ourResult;
723 bool ok = js::Wrapper::defineProperty(cx, proxy, id, desc, ourResult);
724 if (!ok) {
725 return false;
728 if (!ourResult.ok()) {
729 // It's possible that this failed because the page got the existing
730 // descriptor (which we force to claim to be configurable) and then tried to
731 // redefine the property with the descriptor it got but a different value.
732 // We want to allow this case to succeed, so check for it and if we're in
733 // that case try again but now with an attempt to define a non-configurable
734 // property.
735 if (!desc.hasConfigurable() || !desc.configurable()) {
736 // The incoming descriptor was not explicitly marked "configurable: true",
737 // so it failed for some other reason. Just propagate that reason out.
738 result = ourResult;
739 return true;
742 JS::Rooted<Maybe<JS::PropertyDescriptor>> existingDesc(cx);
743 ok = js::Wrapper::getOwnPropertyDescriptor(cx, proxy, id, &existingDesc);
744 if (!ok) {
745 return false;
747 if (existingDesc.isNothing() || existingDesc->configurable()) {
748 // We have no existing property, or its descriptor is already configurable
749 // (on the Window itself, where things really can be non-configurable).
750 // So we failed for some other reason, which we should propagate out.
751 result = ourResult;
752 return true;
755 JS::Rooted<JS::PropertyDescriptor> updatedDesc(cx, desc);
756 updatedDesc.setConfigurable(false);
758 JS::ObjectOpResult ourNewResult;
759 ok = js::Wrapper::defineProperty(cx, proxy, id, updatedDesc, ourNewResult);
760 if (!ok) {
761 return false;
764 if (!ourNewResult.ok()) {
765 // Twiddling the configurable flag didn't help. Just return this failure
766 // out to the caller.
767 result = ourNewResult;
768 return true;
772 #ifndef RELEASE_OR_BETA // To be turned on in bug 1496510.
773 if (desc.hasConfigurable() && !desc.configurable() &&
774 !IsNonConfigurableReadonlyPrimitiveGlobalProp(cx, id)) {
775 // Give callers a way to detect that they failed to "really" define a
776 // non-configurable property.
777 result.failCantDefineWindowNonConfigurable();
778 return true;
780 #endif
782 result.succeed();
783 return true;
786 bool nsOuterWindowProxy::ownPropertyKeys(
787 JSContext* cx, JS::Handle<JSObject*> proxy,
788 JS::MutableHandleVector<jsid> props) const {
789 // Just our indexed stuff followed by our "normal" own property names.
790 if (!AppendIndexedPropertyNames(proxy, props)) {
791 return false;
794 if (IsPlatformObjectSameOrigin(cx, proxy)) {
795 // When forwarding to js::Wrapper, we should just enter the Realm of proxy
796 // for now. That's what js::Wrapper expects, and since we're same-origin
797 // anyway this is not changing any security behavior.
798 JS::RootedVector<jsid> innerProps(cx);
799 { // Scope for JSAutoRealm so we can mark the ids once we exit it
800 JSAutoRealm ar(cx, proxy);
801 if (!js::Wrapper::ownPropertyKeys(cx, proxy, &innerProps)) {
802 return false;
805 for (auto& id : innerProps) {
806 JS_MarkCrossZoneId(cx, id);
808 return js::AppendUnique(cx, props, innerProps);
811 // In the cross-origin case we purposefully exclude subframe names from the
812 // list of property names we report here.
813 JS::Rooted<JSObject*> holder(cx);
814 if (!EnsureHolder(cx, proxy, &holder)) {
815 return false;
818 JS::RootedVector<jsid> crossOriginProps(cx);
819 if (!js::GetPropertyKeys(cx, holder,
820 JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS,
821 &crossOriginProps) ||
822 !js::AppendUnique(cx, props, crossOriginProps)) {
823 return false;
826 // Add the "print" property if needed.
827 nsGlobalWindowOuter* outer = GetOuterWindow(proxy);
828 nsGlobalWindowInner* inner =
829 nsGlobalWindowInner::Cast(outer->GetCurrentInnerWindow());
830 if (inner) {
831 nsCOMPtr<nsIPrincipal> targetPrincipal = GetNoPDFJSPrincipal(inner);
832 if (targetPrincipal &&
833 nsContentUtils::SubjectPrincipal(cx)->Equals(targetPrincipal)) {
834 JS::RootedVector<jsid> printProp(cx);
835 if (!printProp.append(GetJSIDByIndex(cx, XPCJSContext::IDX_PRINT)) ||
836 !js::AppendUnique(cx, props, printProp)) {
837 return false;
842 return xpc::AppendCrossOriginWhitelistedPropNames(cx, props);
845 bool nsOuterWindowProxy::delete_(JSContext* cx, JS::Handle<JSObject*> proxy,
846 JS::Handle<jsid> id,
847 JS::ObjectOpResult& result) const {
848 if (!IsPlatformObjectSameOrigin(cx, proxy)) {
849 return ReportCrossOriginDenial(cx, id, "delete"_ns);
852 if (!GetSubframeWindow(cx, proxy, id).IsNull()) {
853 // Fail (which means throw if strict, else return false).
854 return result.failCantDeleteWindowElement();
857 if (IsArrayIndex(GetArrayIndexFromId(id))) {
858 // Indexed, but not supported. Spec says return true.
859 return result.succeed();
862 // We're same-origin, so it should be safe to enter the Realm of "proxy".
863 // Let's do that, just in case, to avoid cross-compartment issues in our
864 // js::Wrapper caller..
865 JSAutoRealm ar(cx, proxy);
866 JS_MarkCrossZoneId(cx, id);
867 return js::Wrapper::delete_(cx, proxy, id, result);
870 JSObject* nsOuterWindowProxy::getSameOriginPrototype(JSContext* cx) const {
871 return Window_Binding::GetProtoObjectHandle(cx);
874 bool nsOuterWindowProxy::has(JSContext* cx, JS::Handle<JSObject*> proxy,
875 JS::Handle<jsid> id, bool* bp) const {
876 // We could just directly forward this method to js::BaseProxyHandler, but
877 // that involves reifying the actual property descriptor, which might be more
878 // work than we have to do for has() on the Window.
880 if (!IsPlatformObjectSameOrigin(cx, proxy)) {
881 // In the cross-origin case we only have own properties. Just call hasOwn
882 // directly.
883 return hasOwn(cx, proxy, id, bp);
886 if (!GetSubframeWindow(cx, proxy, id).IsNull()) {
887 *bp = true;
888 return true;
891 // Just to be safe in terms of compartment asserts, enter the Realm of
892 // "proxy". We're same-origin with it, so this should be safe.
893 JSAutoRealm ar(cx, proxy);
894 JS_MarkCrossZoneId(cx, id);
895 return js::Wrapper::has(cx, proxy, id, bp);
898 bool nsOuterWindowProxy::hasOwn(JSContext* cx, JS::Handle<JSObject*> proxy,
899 JS::Handle<jsid> id, bool* bp) const {
900 // We could just directly forward this method to js::BaseProxyHandler, but
901 // that involves reifying the actual property descriptor, which might be more
902 // work than we have to do for hasOwn() on the Window.
904 if (!IsPlatformObjectSameOrigin(cx, proxy)) {
905 // Avoiding reifying the property descriptor here would require duplicating
906 // a bunch of "is this property exposed cross-origin" logic, which is
907 // probably not worth it. Just forward this along to the base
908 // implementation.
910 // It's very important to not forward this to js::Wrapper, because that will
911 // not do the right security and cross-origin checks and will pass through
912 // the call to the Window.
914 // The BaseProxyHandler code is OK with this happening without entering the
915 // compartment of "proxy".
916 return js::BaseProxyHandler::hasOwn(cx, proxy, id, bp);
919 if (!GetSubframeWindow(cx, proxy, id).IsNull()) {
920 *bp = true;
921 return true;
924 // Just to be safe in terms of compartment asserts, enter the Realm of
925 // "proxy". We're same-origin with it, so this should be safe.
926 JSAutoRealm ar(cx, proxy);
927 JS_MarkCrossZoneId(cx, id);
928 return js::Wrapper::hasOwn(cx, proxy, id, bp);
931 bool nsOuterWindowProxy::get(JSContext* cx, JS::Handle<JSObject*> proxy,
932 JS::Handle<JS::Value> receiver,
933 JS::Handle<jsid> id,
934 JS::MutableHandle<JS::Value> vp) const {
935 if (id == GetJSIDByIndex(cx, XPCJSContext::IDX_WRAPPED_JSOBJECT) &&
936 xpc::AccessCheck::isChrome(js::GetContextCompartment(cx))) {
937 vp.set(JS::ObjectValue(*proxy));
938 return MaybeWrapValue(cx, vp);
941 if (!IsPlatformObjectSameOrigin(cx, proxy)) {
942 return CrossOriginGet(cx, proxy, receiver, id, vp);
945 bool found;
946 if (!GetSubframeWindow(cx, proxy, id, vp, found)) {
947 return false;
950 if (found) {
951 return true;
954 if (StaticPrefs::dom_missing_prop_counters_enabled() && JSID_IS_ATOM(id)) {
955 Window_Binding::CountMaybeMissingProperty(proxy, id);
958 { // Scope for JSAutoRealm
959 // Enter "proxy"'s Realm. We're in the same-origin case, so this should be
960 // safe.
961 JSAutoRealm ar(cx, proxy);
963 JS_MarkCrossZoneId(cx, id);
965 JS::Rooted<JS::Value> wrappedReceiver(cx, receiver);
966 if (!MaybeWrapValue(cx, &wrappedReceiver)) {
967 return false;
970 // Fall through to js::Wrapper.
971 if (!js::Wrapper::get(cx, proxy, wrappedReceiver, id, vp)) {
972 return false;
976 // Make sure our return value is in the caller compartment.
977 return MaybeWrapValue(cx, vp);
980 bool nsOuterWindowProxy::set(JSContext* cx, JS::Handle<JSObject*> proxy,
981 JS::Handle<jsid> id, JS::Handle<JS::Value> v,
982 JS::Handle<JS::Value> receiver,
983 JS::ObjectOpResult& result) const {
984 if (!IsPlatformObjectSameOrigin(cx, proxy)) {
985 return CrossOriginSet(cx, proxy, id, v, receiver, result);
988 if (IsArrayIndex(GetArrayIndexFromId(id))) {
989 // Reject the set. It's up to the caller to decide whether to throw a
990 // TypeError. If the caller is strict mode JS code, it'll throw.
991 return result.failReadOnly();
994 // Do the rest in the Realm of "proxy", since we're in the same-origin case.
995 JSAutoRealm ar(cx, proxy);
996 JS::Rooted<JS::Value> wrappedArg(cx, v);
997 if (!MaybeWrapValue(cx, &wrappedArg)) {
998 return false;
1000 JS::Rooted<JS::Value> wrappedReceiver(cx, receiver);
1001 if (!MaybeWrapValue(cx, &wrappedReceiver)) {
1002 return false;
1005 JS_MarkCrossZoneId(cx, id);
1007 return js::Wrapper::set(cx, proxy, id, wrappedArg, wrappedReceiver, result);
1010 bool nsOuterWindowProxy::getOwnEnumerablePropertyKeys(
1011 JSContext* cx, JS::Handle<JSObject*> proxy,
1012 JS::MutableHandleVector<jsid> props) const {
1013 // We could just stop overring getOwnEnumerablePropertyKeys and let our
1014 // superclasses deal (by falling back on the BaseProxyHandler implementation
1015 // that uses a combination of ownPropertyKeys and getOwnPropertyDescriptor to
1016 // only return the enumerable ones. But maybe there's value in having
1017 // somewhat faster for-in iteration on Window objects...
1019 // Like ownPropertyKeys, our indexed stuff followed by our "normal" enumerable
1020 // own property names.
1021 if (!AppendIndexedPropertyNames(proxy, props)) {
1022 return false;
1025 if (!IsPlatformObjectSameOrigin(cx, proxy)) {
1026 // All the cross-origin properties other than the indexed props are
1027 // non-enumerable, so we're done here.
1028 return true;
1031 // When forwarding to js::Wrapper, we should just enter the Realm of proxy
1032 // for now. That's what js::Wrapper expects, and since we're same-origin
1033 // anyway this is not changing any security behavior.
1034 JS::RootedVector<jsid> innerProps(cx);
1035 { // Scope for JSAutoRealm so we can mark the ids once we exit it.
1036 JSAutoRealm ar(cx, proxy);
1037 if (!js::Wrapper::getOwnEnumerablePropertyKeys(cx, proxy, &innerProps)) {
1038 return false;
1042 for (auto& id : innerProps) {
1043 JS_MarkCrossZoneId(cx, id);
1046 return js::AppendUnique(cx, props, innerProps);
1049 bool nsOuterWindowProxy::GetSubframeWindow(JSContext* cx,
1050 JS::Handle<JSObject*> proxy,
1051 JS::Handle<jsid> id,
1052 JS::MutableHandle<JS::Value> vp,
1053 bool& found) const {
1054 Nullable<WindowProxyHolder> frame = GetSubframeWindow(cx, proxy, id);
1055 if (frame.IsNull()) {
1056 found = false;
1057 return true;
1060 found = true;
1061 return WrapObject(cx, frame.Value(), vp);
1064 Nullable<WindowProxyHolder> nsOuterWindowProxy::GetSubframeWindow(
1065 JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id) const {
1066 uint32_t index = GetArrayIndexFromId(id);
1067 if (!IsArrayIndex(index)) {
1068 return nullptr;
1071 nsGlobalWindowOuter* win = GetOuterWindow(proxy);
1072 return win->IndexedGetterOuter(index);
1075 bool nsOuterWindowProxy::AppendIndexedPropertyNames(
1076 JSObject* proxy, JS::MutableHandleVector<jsid> props) const {
1077 uint32_t length = GetOuterWindow(proxy)->Length();
1078 MOZ_ASSERT(int32_t(length) >= 0);
1079 if (!props.reserve(props.length() + length)) {
1080 return false;
1082 for (int32_t i = 0; i < int32_t(length); ++i) {
1083 if (!props.append(INT_TO_JSID(i))) {
1084 return false;
1088 return true;
1091 bool nsOuterWindowProxy::EnsureHolder(
1092 JSContext* cx, JS::Handle<JSObject*> proxy,
1093 JS::MutableHandle<JSObject*> holder) const {
1094 return EnsureHolder(cx, proxy, HOLDER_WEAKMAP_SLOT,
1095 Window_Binding::sCrossOriginProperties, holder);
1098 size_t nsOuterWindowProxy::objectMoved(JSObject* obj, JSObject* old) const {
1099 nsGlobalWindowOuter* outerWindow = GetOuterWindow(obj);
1100 if (outerWindow) {
1101 outerWindow->UpdateWrapper(obj, old);
1102 BrowsingContext* bc = outerWindow->GetBrowsingContext();
1103 if (bc) {
1104 bc->UpdateWindowProxy(obj, old);
1107 return 0;
1110 enum { PDFJS_SLOT_CALLEE = 0 };
1112 // static
1113 bool nsOuterWindowProxy::MaybeGetPDFJSPrintMethod(
1114 JSContext* cx, JS::Handle<JSObject*> proxy,
1115 JS::MutableHandle<Maybe<JS::PropertyDescriptor>> desc) {
1116 MOZ_ASSERT(proxy);
1117 MOZ_ASSERT(!desc.isSome());
1119 nsGlobalWindowOuter* outer = GetOuterWindow(proxy);
1120 nsGlobalWindowInner* inner =
1121 nsGlobalWindowInner::Cast(outer->GetCurrentInnerWindow());
1122 if (!inner) {
1123 // No print method to expose.
1124 return true;
1127 nsCOMPtr<nsIPrincipal> targetPrincipal = GetNoPDFJSPrincipal(inner);
1128 if (!targetPrincipal) {
1129 // Nothing special to be done.
1130 return true;
1133 if (!nsContentUtils::SubjectPrincipal(cx)->Equals(targetPrincipal)) {
1134 // Not our origin's PDF document.
1135 return true;
1138 // Get the function we plan to actually call.
1139 JS::Rooted<JSObject*> innerObj(cx, inner->GetGlobalJSObject());
1140 if (!innerObj) {
1141 // Really should not happen, but ok, let's just return.
1142 return true;
1145 JS::Rooted<JS::Value> targetFunc(cx);
1147 JSAutoRealm ar(cx, innerObj);
1148 if (!JS_GetProperty(cx, innerObj, "print", &targetFunc)) {
1149 return false;
1153 if (!targetFunc.isObject()) {
1154 // Who knows what's going on. Just return.
1155 return true;
1158 // The Realm of cx is the realm our caller is in and the realm we
1159 // should create our function in. Note that we can't use the
1160 // standard XPConnect function forwarder machinery because our
1161 // "this" is cross-origin, so we have to do thus by hand.
1163 // Make sure targetFunc is wrapped into the right compartment.
1164 if (!MaybeWrapValue(cx, &targetFunc)) {
1165 return false;
1168 JSFunction* fun =
1169 js::NewFunctionWithReserved(cx, PDFJSPrintMethod, 0, 0, "print");
1170 if (!fun) {
1171 return false;
1174 JS::Rooted<JSObject*> funObj(cx, JS_GetFunctionObject(fun));
1175 js::SetFunctionNativeReserved(funObj, PDFJS_SLOT_CALLEE, targetFunc);
1177 JS::Rooted<JS::Value> funVal(cx, JS::ObjectValue(*funObj));
1178 JS::Rooted<JS::PropertyDescriptor> pd(cx);
1179 // JSPROP_ENUMERATE because that's what it would have been in the same-origin
1180 // case without the PDF viewer messing with things.
1181 pd.setDataDescriptor(funVal, JSPROP_ENUMERATE);
1182 pd.object().set(proxy);
1183 desc.set(Some(pd.get()));
1184 return true;
1187 // static
1188 bool nsOuterWindowProxy::PDFJSPrintMethod(JSContext* cx, unsigned argc,
1189 JS::Value* vp) {
1190 JS::CallArgs args = CallArgsFromVp(argc, vp);
1192 JS::Rooted<JSObject*> realCallee(
1193 cx, &js::GetFunctionNativeReserved(&args.callee(), PDFJS_SLOT_CALLEE)
1194 .toObject());
1195 // Unchecked unwrap, because we want to extract the thing we really had
1196 // before.
1197 realCallee = js::UncheckedUnwrap(realCallee);
1199 JS::Rooted<JS::Value> thisv(cx, args.thisv());
1200 if (thisv.isNullOrUndefined()) {
1201 // Replace it with the global of our stashed callee, simulating the
1202 // global-assuming behavior of DOM methods.
1203 JS::Rooted<JSObject*> global(cx, JS::GetNonCCWObjectGlobal(realCallee));
1204 if (!MaybeWrapObject(cx, &global)) {
1205 return false;
1207 thisv.setObject(*global);
1208 } else if (!thisv.isObject()) {
1209 return ThrowInvalidThis(cx, args, false, prototypes::id::Window);
1212 // We want to do an UncheckedUnwrap here, because we're going to directly
1213 // examine the principal of the inner window, if we have an inner window.
1214 JS::Rooted<JSObject*> unwrappedObj(cx,
1215 js::UncheckedUnwrap(&thisv.toObject()));
1216 nsGlobalWindowInner* inner = nullptr;
1218 // Do the unwrap in the Realm of the object we're looking at.
1219 JSAutoRealm ar(cx, unwrappedObj);
1220 UNWRAP_MAYBE_CROSS_ORIGIN_OBJECT(Window, &unwrappedObj, inner, cx);
1222 if (!inner) {
1223 return ThrowInvalidThis(cx, args, false, prototypes::id::Window);
1226 nsIPrincipal* callerPrincipal = nsContentUtils::SubjectPrincipal(cx);
1227 if (!callerPrincipal->SubsumesConsideringDomain(inner->GetPrincipal())) {
1228 // Check whether it's a PDF viewer from our origin.
1229 nsCOMPtr<nsIPrincipal> pdfPrincipal = GetNoPDFJSPrincipal(inner);
1230 if (!pdfPrincipal || !callerPrincipal->Equals(pdfPrincipal)) {
1231 // Security error.
1232 return ThrowInvalidThis(cx, args, true, prototypes::id::Window);
1236 // Go ahead and enter the Realm of our real callee to call it. We'll pass it
1237 // our "thisv", just in case someone grabs a "print" method off one PDF
1238 // document and .call()s it on another one.
1240 JSAutoRealm ar(cx, realCallee);
1241 if (!MaybeWrapValue(cx, &thisv)) {
1242 return false;
1245 // Don't bother passing through the args; they will get ignored anyway.
1247 if (!JS::Call(cx, thisv, realCallee, JS::HandleValueArray::empty(),
1248 args.rval())) {
1249 return false;
1253 // Wrap the return value (not that there should be any!) into the right
1254 // compartment.
1255 return MaybeWrapValue(cx, args.rval());
1258 // static
1259 already_AddRefed<nsIPrincipal> nsOuterWindowProxy::GetNoPDFJSPrincipal(
1260 nsGlobalWindowInner* inner) {
1261 if (!nsContentUtils::IsPDFJS(inner->GetPrincipal())) {
1262 return nullptr;
1265 if (Document* doc = inner->GetExtantDoc()) {
1266 if (nsCOMPtr<nsIPropertyBag2> propBag =
1267 do_QueryInterface(doc->GetChannel())) {
1268 nsCOMPtr<nsIPrincipal> principal(
1269 do_GetProperty(propBag, u"noPDFJSPrincipal"_ns));
1270 return principal.forget();
1273 return nullptr;
1276 const nsOuterWindowProxy nsOuterWindowProxy::singleton;
1278 class nsChromeOuterWindowProxy : public nsOuterWindowProxy {
1279 public:
1280 constexpr nsChromeOuterWindowProxy() : nsOuterWindowProxy() {}
1282 const char* className(JSContext* cx,
1283 JS::Handle<JSObject*> wrapper) const override;
1285 static const nsChromeOuterWindowProxy singleton;
1288 const char* nsChromeOuterWindowProxy::className(
1289 JSContext* cx, JS::Handle<JSObject*> proxy) const {
1290 MOZ_ASSERT(js::IsProxy(proxy));
1292 return "ChromeWindow";
1295 const nsChromeOuterWindowProxy nsChromeOuterWindowProxy::singleton;
1297 static JSObject* NewOuterWindowProxy(JSContext* cx,
1298 JS::Handle<JSObject*> global,
1299 bool isChrome) {
1300 MOZ_ASSERT(JS_IsGlobalObject(global));
1302 JSAutoRealm ar(cx, global);
1304 js::WrapperOptions options;
1305 options.setClass(&OuterWindowProxyClass);
1306 JSObject* obj =
1307 js::Wrapper::New(cx, global,
1308 isChrome ? &nsChromeOuterWindowProxy::singleton
1309 : &nsOuterWindowProxy::singleton,
1310 options);
1311 MOZ_ASSERT_IF(obj, js::IsWindowProxy(obj));
1312 return obj;
1315 //*****************************************************************************
1316 //*** nsGlobalWindowOuter: Object Management
1317 //*****************************************************************************
1319 nsGlobalWindowOuter::nsGlobalWindowOuter(uint64_t aWindowID)
1320 : nsPIDOMWindowOuter(aWindowID),
1321 mFullscreen(false),
1322 mFullscreenMode(false),
1323 mForceFullScreenInWidget(false),
1324 mIsClosed(false),
1325 mInClose(false),
1326 mHavePendingClose(false),
1327 mBlockScriptedClosingFlag(false),
1328 mWasOffline(false),
1329 mCreatingInnerWindow(false),
1330 mIsChrome(false),
1331 mAllowScriptsToClose(false),
1332 mTopLevelOuterContentWindow(false),
1333 mStorageAccessPermissionGranted(false),
1334 mDelayedPrintUntilAfterLoad(false),
1335 mDelayedCloseForPrinting(false),
1336 mShouldDelayPrintUntilAfterLoad(false),
1337 #ifdef DEBUG
1338 mSerial(0),
1339 mSetOpenerWindowCalled(false),
1340 #endif
1341 mCleanedUp(false),
1342 mCanSkipCCGeneration(0),
1343 mAutoActivateVRDisplayID(0) {
1344 AssertIsOnMainThread();
1346 nsLayoutStatics::AddRef();
1348 // Initialize the PRCList (this).
1349 PR_INIT_CLIST(this);
1351 // |this| is an outer window. Outer windows start out frozen and
1352 // remain frozen until they get an inner window.
1353 MOZ_ASSERT(IsFrozen());
1355 // We could have failed the first time through trying
1356 // to create the entropy collector, so we should
1357 // try to get one until we succeed.
1359 #ifdef DEBUG
1360 mSerial = nsContentUtils::InnerOrOuterWindowCreated();
1362 MOZ_LOG(gDocShellAndDOMWindowLeakLogging, LogLevel::Info,
1363 ("++DOMWINDOW == %d (%p) [pid = %d] [serial = %d] [outer = %p]\n",
1364 nsContentUtils::GetCurrentInnerOrOuterWindowCount(),
1365 static_cast<void*>(ToCanonicalSupports(this)), getpid(), mSerial,
1366 nullptr));
1367 #endif
1369 MOZ_LOG(gDOMLeakPRLogOuter, LogLevel::Debug,
1370 ("DOMWINDOW %p created outer=nullptr", this));
1372 // Add ourselves to the outer windows list.
1373 MOZ_ASSERT(sOuterWindowsById, "Outer Windows hash table must be created!");
1375 // |this| is an outer window, add to the outer windows list.
1376 MOZ_ASSERT(!sOuterWindowsById->Contains(mWindowID),
1377 "This window shouldn't be in the hash table yet!");
1378 // We seem to see crashes in release builds because of null
1379 // |sOuterWindowsById|.
1380 if (sOuterWindowsById) {
1381 sOuterWindowsById->InsertOrUpdate(mWindowID, this);
1385 #ifdef DEBUG
1387 /* static */
1388 void nsGlobalWindowOuter::AssertIsOnMainThread() {
1389 MOZ_ASSERT(NS_IsMainThread());
1392 #endif // DEBUG
1394 /* static */
1395 void nsGlobalWindowOuter::Init() {
1396 AssertIsOnMainThread();
1398 NS_ASSERTION(gDOMLeakPRLogOuter,
1399 "gDOMLeakPRLogOuter should have been initialized!");
1401 sOuterWindowsById = new OuterWindowByIdTable();
1404 nsGlobalWindowOuter::~nsGlobalWindowOuter() {
1405 AssertIsOnMainThread();
1407 if (sOuterWindowsById) {
1408 sOuterWindowsById->Remove(mWindowID);
1411 nsContentUtils::InnerOrOuterWindowDestroyed();
1413 #ifdef DEBUG
1414 if (MOZ_LOG_TEST(gDocShellAndDOMWindowLeakLogging, LogLevel::Info)) {
1415 nsAutoCString url;
1416 if (mLastOpenedURI) {
1417 url = mLastOpenedURI->GetSpecOrDefault();
1419 // Data URLs can be very long, so truncate to avoid flooding the log.
1420 const uint32_t maxURLLength = 1000;
1421 if (url.Length() > maxURLLength) {
1422 url.Truncate(maxURLLength);
1426 MOZ_LOG(
1427 gDocShellAndDOMWindowLeakLogging, LogLevel::Info,
1428 ("--DOMWINDOW == %d (%p) [pid = %d] [serial = %d] [outer = %p] [url = "
1429 "%s]\n",
1430 nsContentUtils::GetCurrentInnerOrOuterWindowCount(),
1431 static_cast<void*>(ToCanonicalSupports(this)), getpid(), mSerial,
1432 nullptr, url.get()));
1434 #endif
1436 MOZ_LOG(gDOMLeakPRLogOuter, LogLevel::Debug,
1437 ("DOMWINDOW %p destroyed", this));
1439 JSObject* proxy = GetWrapperMaybeDead();
1440 if (proxy) {
1441 if (mBrowsingContext && mBrowsingContext->GetUnbarrieredWindowProxy()) {
1442 nsGlobalWindowOuter* outer = nsOuterWindowProxy::GetOuterWindow(
1443 mBrowsingContext->GetUnbarrieredWindowProxy());
1444 // Check that the current WindowProxy object corresponds to this
1445 // nsGlobalWindowOuter, because we don't want to clear the WindowProxy if
1446 // we've replaced it with a cross-process WindowProxy.
1447 if (outer == this) {
1448 mBrowsingContext->ClearWindowProxy();
1451 js::SetProxyReservedSlot(proxy, OUTER_WINDOW_SLOT,
1452 JS::PrivateValue(nullptr));
1455 // An outer window is destroyed with inner windows still possibly
1456 // alive, iterate through the inner windows and null out their
1457 // back pointer to this outer, and pull them out of the list of
1458 // inner windows.
1460 // Our linked list of inner windows both contains (an nsGlobalWindowOuter),
1461 // and our inner windows (nsGlobalWindowInners). This means that we need to
1462 // use PRCList*. We can then compare that PRCList* to `this` to see if its an
1463 // inner or outer window.
1464 PRCList* w;
1465 while ((w = PR_LIST_HEAD(this)) != this) {
1466 PR_REMOVE_AND_INIT_LINK(w);
1469 DropOuterWindowDocs();
1471 // Outer windows are always supposed to call CleanUp before letting themselves
1472 // be destroyed.
1473 MOZ_ASSERT(mCleanedUp);
1475 nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
1476 if (ac) ac->RemoveWindowAsListener(this);
1478 nsLayoutStatics::Release();
1481 // static
1482 void nsGlobalWindowOuter::ShutDown() {
1483 AssertIsOnMainThread();
1485 delete sOuterWindowsById;
1486 sOuterWindowsById = nullptr;
1489 void nsGlobalWindowOuter::DropOuterWindowDocs() {
1490 MOZ_ASSERT_IF(mDoc, !mDoc->EventHandlingSuppressed());
1491 mDoc = nullptr;
1492 mSuspendedDoc = nullptr;
1495 void nsGlobalWindowOuter::CleanUp() {
1496 // Guarantee idempotence.
1497 if (mCleanedUp) return;
1498 mCleanedUp = true;
1500 StartDying();
1502 mWindowUtils = nullptr;
1504 ClearControllers();
1506 mContext = nullptr; // Forces Release
1507 mChromeEventHandler = nullptr; // Forces Release
1508 mParentTarget = nullptr;
1509 mMessageManager = nullptr;
1511 mArguments = nullptr;
1514 void nsGlobalWindowOuter::ClearControllers() {
1515 if (mControllers) {
1516 uint32_t count;
1517 mControllers->GetControllerCount(&count);
1519 while (count--) {
1520 nsCOMPtr<nsIController> controller;
1521 mControllers->GetControllerAt(count, getter_AddRefs(controller));
1523 nsCOMPtr<nsIControllerContext> context = do_QueryInterface(controller);
1524 if (context) context->SetCommandContext(nullptr);
1527 mControllers = nullptr;
1531 //*****************************************************************************
1532 // nsGlobalWindowOuter::nsISupports
1533 //*****************************************************************************
1535 // QueryInterface implementation for nsGlobalWindowOuter
1536 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsGlobalWindowOuter)
1537 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
1538 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, EventTarget)
1539 NS_INTERFACE_MAP_ENTRY(nsIDOMWindow)
1540 NS_INTERFACE_MAP_ENTRY(nsIGlobalObject)
1541 NS_INTERFACE_MAP_ENTRY(nsIScriptGlobalObject)
1542 NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal)
1543 NS_INTERFACE_MAP_ENTRY(mozilla::dom::EventTarget)
1544 NS_INTERFACE_MAP_ENTRY(nsPIDOMWindowOuter)
1545 NS_INTERFACE_MAP_ENTRY(mozIDOMWindowProxy)
1546 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIDOMChromeWindow, IsChromeWindow())
1547 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
1548 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
1549 NS_INTERFACE_MAP_END
1551 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGlobalWindowOuter)
1552 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsGlobalWindowOuter)
1554 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsGlobalWindowOuter)
1555 if (tmp->IsBlackForCC(false)) {
1556 if (nsCCUncollectableMarker::InGeneration(tmp->mCanSkipCCGeneration)) {
1557 return true;
1559 tmp->mCanSkipCCGeneration = nsCCUncollectableMarker::sGeneration;
1560 if (EventListenerManager* elm = tmp->GetExistingListenerManager()) {
1561 elm->MarkForCC();
1563 return true;
1565 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
1567 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsGlobalWindowOuter)
1568 return tmp->IsBlackForCC(true);
1569 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
1571 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsGlobalWindowOuter)
1572 return tmp->IsBlackForCC(false);
1573 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
1575 NS_IMPL_CYCLE_COLLECTION_CLASS(nsGlobalWindowOuter)
1577 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsGlobalWindowOuter)
1578 if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
1579 char name[512];
1580 nsAutoCString uri;
1581 if (tmp->mDoc && tmp->mDoc->GetDocumentURI()) {
1582 uri = tmp->mDoc->GetDocumentURI()->GetSpecOrDefault();
1584 SprintfLiteral(name, "nsGlobalWindowOuter # %" PRIu64 " outer %s",
1585 tmp->mWindowID, uri.get());
1586 cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
1587 } else {
1588 NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsGlobalWindowOuter, tmp->mRefCnt.get())
1591 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext)
1593 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mControllers)
1594 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mArguments)
1596 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocalStorage)
1597 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSuspendedDoc)
1598 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentPrincipal)
1599 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentStoragePrincipal)
1600 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentPartitionedPrincipal)
1601 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDoc)
1603 // Traverse stuff from nsPIDOMWindow
1604 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeEventHandler)
1605 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParentTarget)
1606 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessageManager)
1607 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameElement)
1609 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocShell)
1610 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowsingContext)
1612 tmp->TraverseObjectsInGlobal(cb);
1614 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeFields.mBrowserDOMWindow)
1615 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1617 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindowOuter)
1618 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE
1619 if (sOuterWindowsById) {
1620 sOuterWindowsById->Remove(tmp->mWindowID);
1623 NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext)
1625 NS_IMPL_CYCLE_COLLECTION_UNLINK(mControllers)
1626 NS_IMPL_CYCLE_COLLECTION_UNLINK(mArguments)
1628 NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocalStorage)
1629 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSuspendedDoc)
1630 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentPrincipal)
1631 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentStoragePrincipal)
1632 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentPartitionedPrincipal)
1633 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDoc)
1635 // Unlink stuff from nsPIDOMWindow
1636 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeEventHandler)
1637 NS_IMPL_CYCLE_COLLECTION_UNLINK(mParentTarget)
1638 NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageManager)
1639 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameElement)
1641 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocShell)
1642 if (tmp->mBrowsingContext) {
1643 if (tmp->mBrowsingContext->GetUnbarrieredWindowProxy()) {
1644 nsGlobalWindowOuter* outer = nsOuterWindowProxy::GetOuterWindow(
1645 tmp->mBrowsingContext->GetUnbarrieredWindowProxy());
1646 // Check that the current WindowProxy object corresponds to this
1647 // nsGlobalWindowOuter, because we don't want to clear the WindowProxy if
1648 // we've replaced it with a cross-process WindowProxy.
1649 if (outer == tmp) {
1650 tmp->mBrowsingContext->ClearWindowProxy();
1653 tmp->mBrowsingContext = nullptr;
1656 tmp->UnlinkObjectsInGlobal();
1658 if (tmp->IsChromeWindow()) {
1659 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeFields.mBrowserDOMWindow)
1662 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
1663 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1665 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsGlobalWindowOuter)
1666 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
1667 NS_IMPL_CYCLE_COLLECTION_TRACE_END
1669 bool nsGlobalWindowOuter::IsBlackForCC(bool aTracingNeeded) {
1670 if (!nsCCUncollectableMarker::sGeneration) {
1671 return false;
1674 // Unlike most wrappers, the outer window wrapper is not a wrapper for
1675 // the outer window. Instead, the outer window wrapper holds the inner
1676 // window binding object, which in turn holds the nsGlobalWindowInner, which
1677 // has a strong reference to the nsGlobalWindowOuter. We're using the
1678 // mInnerWindow pointer as a flag for that whole chain.
1679 return (nsCCUncollectableMarker::InGeneration(GetMarkedCCGeneration()) ||
1680 (mInnerWindow && HasKnownLiveWrapper())) &&
1681 (!aTracingNeeded || HasNothingToTrace(ToSupports(this)));
1684 //*****************************************************************************
1685 // nsGlobalWindowOuter::nsIScriptGlobalObject
1686 //*****************************************************************************
1688 nsresult nsGlobalWindowOuter::EnsureScriptEnvironment() {
1689 if (GetWrapperPreserveColor()) {
1690 return NS_OK;
1693 NS_ENSURE_STATE(!mCleanedUp);
1695 NS_ASSERTION(!GetCurrentInnerWindowInternal(),
1696 "No cached wrapper, but we have an inner window?");
1697 NS_ASSERTION(!mContext, "Will overwrite mContext!");
1699 // If this window is an [i]frame, don't bother GC'ing when the frame's context
1700 // is destroyed since a GC will happen when the frameset or host document is
1701 // destroyed anyway.
1702 mContext = new nsJSContext(mBrowsingContext->IsTop(), this);
1703 return NS_OK;
1706 nsIScriptContext* nsGlobalWindowOuter::GetScriptContext() { return mContext; }
1708 bool nsGlobalWindowOuter::WouldReuseInnerWindow(Document* aNewDocument) {
1709 // We reuse the inner window when:
1710 // a. We are currently at our original document.
1711 // b. At least one of the following conditions are true:
1712 // -- The new document is the same as the old document. This means that we're
1713 // getting called from document.open().
1714 // -- The new document has the same origin as what we have loaded right now.
1716 if (!mDoc || !aNewDocument) {
1717 return false;
1720 if (!mDoc->IsInitialDocument()) {
1721 return false;
1724 #ifdef DEBUG
1726 nsCOMPtr<nsIURI> uri;
1727 NS_GetURIWithoutRef(mDoc->GetDocumentURI(), getter_AddRefs(uri));
1728 NS_ASSERTION(NS_IsAboutBlank(uri), "How'd this happen?");
1730 #endif
1732 // Great, we're the original document, check for one of the other
1733 // conditions.
1735 if (mDoc == aNewDocument) {
1736 return true;
1739 if (aNewDocument->IsStaticDocument()) {
1740 return false;
1743 if (BasePrincipal::Cast(mDoc->NodePrincipal())
1744 ->FastEqualsConsideringDomain(aNewDocument->NodePrincipal())) {
1745 // The origin is the same.
1746 return true;
1749 return false;
1752 void nsGlobalWindowOuter::SetInitialPrincipalToSubject(
1753 nsIContentSecurityPolicy* aCSP,
1754 const Maybe<nsILoadInfo::CrossOriginEmbedderPolicy>& aCOEP) {
1755 // First, grab the subject principal.
1756 nsCOMPtr<nsIPrincipal> newWindowPrincipal =
1757 nsContentUtils::SubjectPrincipalOrSystemIfNativeCaller();
1759 // We should never create windows with an expanded principal.
1760 // If we have a system principal, make sure we're not using it for a content
1761 // docshell.
1762 // NOTE: Please keep this logic in sync with
1763 // nsAppShellService::JustCreateTopWindow
1764 if (nsContentUtils::IsExpandedPrincipal(newWindowPrincipal) ||
1765 (newWindowPrincipal->IsSystemPrincipal() &&
1766 GetBrowsingContext()->IsContent())) {
1767 newWindowPrincipal = nullptr;
1770 // If there's an existing document, bail if it either:
1771 if (mDoc) {
1772 // (a) is not an initial about:blank document, or
1773 if (!mDoc->IsInitialDocument()) return;
1774 // (b) already has the correct principal.
1775 if (mDoc->NodePrincipal() == newWindowPrincipal) return;
1777 #ifdef DEBUG
1778 // If we have a document loaded at this point, it had better be about:blank.
1779 // Otherwise, something is really weird. An about:blank page has a
1780 // NullPrincipal.
1781 bool isNullPrincipal;
1782 MOZ_ASSERT(NS_SUCCEEDED(mDoc->NodePrincipal()->GetIsNullPrincipal(
1783 &isNullPrincipal)) &&
1784 isNullPrincipal);
1785 #endif
1788 // Use the subject (or system) principal as the storage principal too until
1789 // the new window finishes navigating and gets a real storage principal.
1790 nsDocShell::Cast(GetDocShell())
1791 ->CreateAboutBlankContentViewer(newWindowPrincipal, newWindowPrincipal,
1792 aCSP, nullptr, aCOEP);
1794 if (mDoc) {
1795 mDoc->SetIsInitialDocument(true);
1798 RefPtr<PresShell> presShell = GetDocShell()->GetPresShell();
1799 if (presShell && !presShell->DidInitialize()) {
1800 // Ensure that if someone plays with this document they will get
1801 // layout happening.
1802 presShell->Initialize();
1806 #define WINDOWSTATEHOLDER_IID \
1808 0x0b917c3e, 0xbd50, 0x4683, { \
1809 0xaf, 0xc9, 0xc7, 0x81, 0x07, 0xae, 0x33, 0x26 \
1813 class WindowStateHolder final : public nsISupports {
1814 public:
1815 NS_DECLARE_STATIC_IID_ACCESSOR(WINDOWSTATEHOLDER_IID)
1816 NS_DECL_ISUPPORTS
1818 explicit WindowStateHolder(nsGlobalWindowInner* aWindow);
1820 nsGlobalWindowInner* GetInnerWindow() { return mInnerWindow; }
1822 void DidRestoreWindow() {
1823 mInnerWindow = nullptr;
1824 mInnerWindowReflector = nullptr;
1827 protected:
1828 ~WindowStateHolder();
1830 nsGlobalWindowInner* mInnerWindow;
1831 // We hold onto this to make sure the inner window doesn't go away. The outer
1832 // window ends up recalculating it anyway.
1833 JS::PersistentRooted<JSObject*> mInnerWindowReflector;
1836 NS_DEFINE_STATIC_IID_ACCESSOR(WindowStateHolder, WINDOWSTATEHOLDER_IID)
1838 WindowStateHolder::WindowStateHolder(nsGlobalWindowInner* aWindow)
1839 : mInnerWindow(aWindow),
1840 mInnerWindowReflector(RootingCx(), aWindow->GetWrapper()) {
1841 MOZ_ASSERT(aWindow, "null window");
1843 aWindow->Suspend();
1845 // When a global goes into the bfcache, we disable script.
1846 xpc::Scriptability::Get(mInnerWindowReflector).SetDocShellAllowsScript(false);
1849 WindowStateHolder::~WindowStateHolder() {
1850 if (mInnerWindow) {
1851 // This window was left in the bfcache and is now going away. We need to
1852 // free it up.
1853 // Note that FreeInnerObjects may already have been called on the
1854 // inner window if its outer has already had SetDocShell(null)
1855 // called.
1856 mInnerWindow->FreeInnerObjects();
1860 NS_IMPL_ISUPPORTS(WindowStateHolder, WindowStateHolder)
1862 bool nsGlobalWindowOuter::ComputeIsSecureContext(Document* aDocument,
1863 SecureContextFlags aFlags) {
1864 nsCOMPtr<nsIPrincipal> principal = aDocument->NodePrincipal();
1865 if (principal->IsSystemPrincipal()) {
1866 return true;
1869 // Implement https://w3c.github.io/webappsec-secure-contexts/#settings-object
1870 // With some modifications to allow for aFlags.
1872 bool hadNonSecureContextCreator = false;
1874 if (WindowContext* parentWindow =
1875 GetBrowsingContext()->GetParentWindowContext()) {
1876 hadNonSecureContextCreator = !parentWindow->GetIsSecureContext();
1879 if (hadNonSecureContextCreator) {
1880 return false;
1883 if (nsContentUtils::HttpsStateIsModern(aDocument)) {
1884 return true;
1887 if (principal->GetIsNullPrincipal()) {
1888 nsCOMPtr<nsIURI> uri = aDocument->GetOriginalURI();
1889 // IsOriginPotentiallyTrustworthy doesn't care about origin attributes so
1890 // it doesn't actually matter what we use here, but reusing the document
1891 // principal's attributes is convenient.
1892 const OriginAttributes& attrs = principal->OriginAttributesRef();
1893 // CreateContentPrincipal correctly gets a useful principal for blob: and
1894 // other URI_INHERITS_SECURITY_CONTEXT URIs.
1895 principal = BasePrincipal::CreateContentPrincipal(uri, attrs);
1896 if (NS_WARN_IF(!principal)) {
1897 return false;
1901 return principal->GetIsOriginPotentiallyTrustworthy();
1904 static bool InitializeLegacyNetscapeObject(JSContext* aCx,
1905 JS::Handle<JSObject*> aGlobal) {
1906 JSAutoRealm ar(aCx, aGlobal);
1908 // Note: MathJax depends on window.netscape being exposed. See bug 791526.
1909 JS::Rooted<JSObject*> obj(aCx);
1910 obj = JS_DefineObject(aCx, aGlobal, "netscape", nullptr);
1911 NS_ENSURE_TRUE(obj, false);
1913 obj = JS_DefineObject(aCx, obj, "security", nullptr);
1914 NS_ENSURE_TRUE(obj, false);
1916 return true;
1919 struct MOZ_STACK_CLASS CompartmentFinderState {
1920 explicit CompartmentFinderState(nsIPrincipal* aPrincipal)
1921 : principal(aPrincipal), compartment(nullptr) {}
1923 // Input: we look for a compartment which is same-origin with the
1924 // given principal.
1925 nsIPrincipal* principal;
1927 // Output: We set this member if we find a compartment.
1928 JS::Compartment* compartment;
1931 static JS::CompartmentIterResult FindSameOriginCompartment(
1932 JSContext* aCx, void* aData, JS::Compartment* aCompartment) {
1933 auto* data = static_cast<CompartmentFinderState*>(aData);
1934 MOZ_ASSERT(!data->compartment, "Why are we getting called?");
1936 // If this compartment is not safe to share across globals, don't do
1937 // anything with it; in particular we should not be getting a
1938 // CompartmentPrivate from such a compartment, because it may be in
1939 // the middle of being collected and its CompartmentPrivate may no
1940 // longer be valid.
1941 if (!js::IsSharableCompartment(aCompartment)) {
1942 return JS::CompartmentIterResult::KeepGoing;
1945 auto* compartmentPrivate = xpc::CompartmentPrivate::Get(aCompartment);
1946 if (!compartmentPrivate->CanShareCompartmentWith(data->principal)) {
1947 // Can't reuse this one, keep going.
1948 return JS::CompartmentIterResult::KeepGoing;
1951 // We have a winner!
1952 data->compartment = aCompartment;
1953 return JS::CompartmentIterResult::Stop;
1956 static JS::RealmCreationOptions& SelectZone(
1957 JSContext* aCx, nsIPrincipal* aPrincipal, nsGlobalWindowInner* aNewInner,
1958 JS::RealmCreationOptions& aOptions) {
1959 // Use the shared system compartment for chrome windows.
1960 if (aPrincipal->IsSystemPrincipal()) {
1961 return aOptions.setExistingCompartment(xpc::PrivilegedJunkScope());
1964 BrowsingContext* bc = aNewInner->GetBrowsingContext();
1965 if (bc->IsTop()) {
1966 // We're a toplevel load. Use a new zone. This way, when we do
1967 // zone-based compartment sharing we won't share compartments
1968 // across navigations.
1969 return aOptions.setNewCompartmentAndZone();
1972 // Find the in-process ancestor highest in the hierarchy.
1973 nsGlobalWindowInner* ancestor = nullptr;
1974 for (WindowContext* wc = bc->GetParentWindowContext(); wc;
1975 wc = wc->GetParentWindowContext()) {
1976 if (nsGlobalWindowInner* win = wc->GetInnerWindow()) {
1977 ancestor = win;
1981 // If we have an ancestor window, use its zone.
1982 if (ancestor && ancestor->GetGlobalJSObject()) {
1983 JS::Zone* zone = JS::GetObjectZone(ancestor->GetGlobalJSObject());
1984 // Now try to find an existing compartment that's same-origin
1985 // with our principal.
1986 CompartmentFinderState data(aPrincipal);
1987 JS_IterateCompartmentsInZone(aCx, zone, &data, FindSameOriginCompartment);
1988 if (data.compartment) {
1989 return aOptions.setExistingCompartment(data.compartment);
1991 return aOptions.setNewCompartmentInExistingZone(
1992 ancestor->GetGlobalJSObject());
1995 return aOptions.setNewCompartmentAndZone();
1999 * Create a new global object that will be used for an inner window.
2000 * Return the native global and an nsISupports 'holder' that can be used
2001 * to manage the lifetime of it.
2003 static nsresult CreateNativeGlobalForInner(
2004 JSContext* aCx, nsGlobalWindowInner* aNewInner, nsIURI* aURI,
2005 nsIPrincipal* aPrincipal, JS::MutableHandle<JSObject*> aGlobal,
2006 bool aIsSecureContext, bool aDefineSharedArrayBufferConstructor) {
2007 MOZ_ASSERT(aCx);
2008 MOZ_ASSERT(aNewInner);
2009 MOZ_ASSERT(aPrincipal);
2011 // DOMWindow with nsEP is not supported, we have to make sure
2012 // no one creates one accidentally.
2013 nsCOMPtr<nsIExpandedPrincipal> nsEP = do_QueryInterface(aPrincipal);
2014 MOZ_RELEASE_ASSERT(!nsEP, "DOMWindow with nsEP is not supported");
2016 JS::RealmOptions options;
2017 JS::RealmCreationOptions& creationOptions = options.creationOptions();
2019 SelectZone(aCx, aPrincipal, aNewInner, creationOptions);
2021 creationOptions.setSecureContext(aIsSecureContext);
2023 // Define the SharedArrayBuffer global constructor property only if shared
2024 // memory may be used and structured-cloned (e.g. through postMessage).
2026 // When the global constructor property isn't defined, the SharedArrayBuffer
2027 // constructor can still be reached through Web Assembly. Omitting the global
2028 // property just prevents feature-tests from being misled. See bug 1624266.
2029 creationOptions.setDefineSharedArrayBufferConstructor(
2030 aDefineSharedArrayBufferConstructor);
2032 xpc::InitGlobalObjectOptions(options, aPrincipal);
2034 // Determine if we need the Components object.
2035 bool needComponents = aPrincipal->IsSystemPrincipal();
2036 uint32_t flags = needComponents ? 0 : xpc::OMIT_COMPONENTS_OBJECT;
2037 flags |= xpc::DONT_FIRE_ONNEWGLOBALHOOK;
2039 if (!Window_Binding::Wrap(aCx, aNewInner, aNewInner, options,
2040 nsJSPrincipals::get(aPrincipal), false, aGlobal) ||
2041 !xpc::InitGlobalObject(aCx, aGlobal, flags)) {
2042 return NS_ERROR_FAILURE;
2045 MOZ_ASSERT(aNewInner->GetWrapperPreserveColor() == aGlobal);
2047 // Set the location information for the new global, so that tools like
2048 // about:memory may use that information
2049 xpc::SetLocationForGlobal(aGlobal, aURI);
2051 if (!InitializeLegacyNetscapeObject(aCx, aGlobal)) {
2052 return NS_ERROR_FAILURE;
2055 return NS_OK;
2058 nsresult nsGlobalWindowOuter::SetNewDocument(Document* aDocument,
2059 nsISupports* aState,
2060 bool aForceReuseInnerWindow,
2061 WindowGlobalChild* aActor) {
2062 MOZ_ASSERT(mDocumentPrincipal == nullptr,
2063 "mDocumentPrincipal prematurely set!");
2064 MOZ_ASSERT(mDocumentStoragePrincipal == nullptr,
2065 "mDocumentStoragePrincipal prematurely set!");
2066 MOZ_ASSERT(mDocumentPartitionedPrincipal == nullptr,
2067 "mDocumentPartitionedPrincipal prematurely set!");
2068 MOZ_ASSERT(aDocument);
2070 // Bail out early if we're in process of closing down the window.
2071 NS_ENSURE_STATE(!mCleanedUp);
2073 NS_ASSERTION(!GetCurrentInnerWindow() ||
2074 GetCurrentInnerWindow()->GetExtantDoc() == mDoc,
2075 "Uh, mDoc doesn't match the current inner window "
2076 "document!");
2077 bool wouldReuseInnerWindow = WouldReuseInnerWindow(aDocument);
2078 if (aForceReuseInnerWindow && !wouldReuseInnerWindow && mDoc &&
2079 mDoc->NodePrincipal() != aDocument->NodePrincipal()) {
2080 NS_ERROR("Attempted forced inner window reuse while changing principal");
2081 return NS_ERROR_UNEXPECTED;
2084 if (!mBrowsingContext->AncestorsAreCurrent()) {
2085 return NS_ERROR_NOT_AVAILABLE;
2088 RefPtr<Document> oldDoc = mDoc;
2089 MOZ_RELEASE_ASSERT(oldDoc != aDocument);
2091 AutoJSAPI jsapi;
2092 jsapi.Init();
2093 JSContext* cx = jsapi.cx();
2095 // Check if we're anywhere near the stack limit before we reach the
2096 // transplanting code, since it has no good way to handle errors. This uses
2097 // the untrusted script limit, which is not strictly necessary since no
2098 // actual script should run.
2099 js::AutoCheckRecursionLimit recursion(cx);
2100 if (!recursion.checkConservativeDontReport(cx)) {
2101 NS_WARNING("Overrecursion in SetNewDocument");
2102 return NS_ERROR_FAILURE;
2105 if (!mDoc) {
2106 // First document load.
2108 // Get our private root. If it is equal to us, then we need to
2109 // attach our global key bindings that handles browser scrolling
2110 // and other browser commands.
2111 nsPIDOMWindowOuter* privateRoot = GetPrivateRoot();
2113 if (privateRoot == this) {
2114 RootWindowGlobalKeyListener::AttachKeyHandler(mChromeEventHandler);
2118 MaybeResetWindowName(aDocument);
2120 /* No mDocShell means we're already been partially closed down. When that
2121 happens, setting status isn't a big requirement, so don't. (Doesn't happen
2122 under normal circumstances, but bug 49615 describes a case.) */
2124 nsContentUtils::AddScriptRunner(
2125 NewRunnableMethod("nsGlobalWindowOuter::ClearStatus", this,
2126 &nsGlobalWindowOuter::ClearStatus));
2128 // Sometimes, WouldReuseInnerWindow() returns true even if there's no inner
2129 // window (see bug 776497). Be safe.
2130 bool reUseInnerWindow = (aForceReuseInnerWindow || wouldReuseInnerWindow) &&
2131 GetCurrentInnerWindowInternal();
2133 nsresult rv;
2135 // We set mDoc even though this is an outer window to avoid
2136 // having to *always* reach into the inner window to find the
2137 // document.
2138 mDoc = aDocument;
2140 nsDocShell::Cast(mDocShell)->MaybeRestoreWindowName();
2142 // We drop the print request for the old document on the floor, it never made
2143 // it. We don't close the window here either even if we were asked to.
2144 mShouldDelayPrintUntilAfterLoad = true;
2145 mDelayedCloseForPrinting = false;
2146 mDelayedPrintUntilAfterLoad = false;
2148 // Take this opportunity to clear mSuspendedDoc. Our old inner window is now
2149 // responsible for unsuspending it.
2150 mSuspendedDoc = nullptr;
2152 #ifdef DEBUG
2153 mLastOpenedURI = aDocument->GetDocumentURI();
2154 #endif
2156 RefPtr<nsGlobalWindowInner> currentInner = GetCurrentInnerWindowInternal();
2158 if (currentInner && currentInner->mNavigator) {
2159 currentInner->mNavigator->OnNavigation();
2162 RefPtr<nsGlobalWindowInner> newInnerWindow;
2163 bool createdInnerWindow = false;
2165 bool thisChrome = IsChromeWindow();
2167 nsCOMPtr<WindowStateHolder> wsh = do_QueryInterface(aState);
2168 NS_ASSERTION(!aState || wsh,
2169 "What kind of weird state are you giving me here?");
2171 bool doomCurrentInner = false;
2173 // Only non-gray (i.e. exposed to JS) objects should be assigned to
2174 // newInnerGlobal.
2175 JS::Rooted<JSObject*> newInnerGlobal(cx);
2176 if (reUseInnerWindow) {
2177 // We're reusing the current inner window.
2178 NS_ASSERTION(!currentInner->IsFrozen(),
2179 "We should never be reusing a shared inner window");
2180 newInnerWindow = currentInner;
2181 newInnerGlobal = currentInner->GetWrapper();
2183 // We're reusing the inner window, but this still counts as a navigation,
2184 // so all expandos and such defined on the outer window should go away.
2185 // Force all Xray wrappers to be recomputed.
2186 JS::Rooted<JSObject*> rootedObject(cx, GetWrapper());
2187 if (!JS_RefreshCrossCompartmentWrappers(cx, rootedObject)) {
2188 return NS_ERROR_FAILURE;
2191 // Inner windows are only reused for same-origin principals, but the
2192 // principals don't necessarily match exactly. Update the principal on the
2193 // realm to match the new document. NB: We don't just call
2194 // currentInner->RefreshRealmPrincipals() here because we haven't yet set
2195 // its mDoc to aDocument.
2196 JS::Realm* realm = js::GetNonCCWObjectRealm(newInnerGlobal);
2197 #ifdef DEBUG
2198 bool sameOrigin = false;
2199 nsIPrincipal* existing = nsJSPrincipals::get(JS::GetRealmPrincipals(realm));
2200 aDocument->NodePrincipal()->Equals(existing, &sameOrigin);
2201 MOZ_ASSERT(sameOrigin);
2202 #endif
2203 JS::SetRealmPrincipals(realm,
2204 nsJSPrincipals::get(aDocument->NodePrincipal()));
2205 } else {
2206 if (aState) {
2207 newInnerWindow = wsh->GetInnerWindow();
2208 newInnerGlobal = newInnerWindow->GetWrapper();
2209 } else {
2210 newInnerWindow = nsGlobalWindowInner::Create(this, thisChrome, aActor);
2211 if (StaticPrefs::dom_timeout_defer_during_load()) {
2212 // ensure the initial loading state is known
2213 newInnerWindow->SetActiveLoadingState(
2214 aDocument->GetReadyStateEnum() ==
2215 Document::ReadyState::READYSTATE_LOADING);
2218 // The outer window is automatically treated as frozen when we
2219 // null out the inner window. As a result, initializing classes
2220 // on the new inner won't end up reaching into the old inner
2221 // window for classes etc.
2223 // [This happens with Object.prototype when XPConnect creates
2224 // a temporary global while initializing classes; the reason
2225 // being that xpconnect creates the temp global w/o a parent
2226 // and proto, which makes the JS engine look up classes in
2227 // cx->globalObject, i.e. this outer window].
2229 mInnerWindow = nullptr;
2231 mCreatingInnerWindow = true;
2233 // The SharedArrayBuffer global constructor property should not be present
2234 // in a fresh global object when shared memory objects aren't allowed
2235 // (because COOP/COEP support isn't enabled, or because COOP/COEP don't
2236 // act to isolate this page to a separate process).
2238 // Every script context we are initialized with must create a
2239 // new global.
2240 rv = CreateNativeGlobalForInner(
2241 cx, newInnerWindow, aDocument->GetDocumentURI(),
2242 aDocument->NodePrincipal(), &newInnerGlobal,
2243 ComputeIsSecureContext(aDocument),
2244 newInnerWindow->IsSharedMemoryAllowed());
2245 NS_ASSERTION(
2246 NS_SUCCEEDED(rv) && newInnerGlobal &&
2247 newInnerWindow->GetWrapperPreserveColor() == newInnerGlobal,
2248 "Failed to get script global");
2250 mCreatingInnerWindow = false;
2251 createdInnerWindow = true;
2253 NS_ENSURE_SUCCESS(rv, rv);
2256 if (currentInner && currentInner->GetWrapperPreserveColor()) {
2257 // Don't free objects on our current inner window if it's going to be
2258 // held in the bfcache.
2259 if (!currentInner->IsFrozen()) {
2260 doomCurrentInner = true;
2264 mInnerWindow = newInnerWindow;
2265 MOZ_ASSERT(mInnerWindow);
2266 mInnerWindow->TryToCacheTopInnerWindow();
2268 if (!GetWrapperPreserveColor()) {
2269 JS::Rooted<JSObject*> outer(
2270 cx, NewOuterWindowProxy(cx, newInnerGlobal, thisChrome));
2271 NS_ENSURE_TRUE(outer, NS_ERROR_FAILURE);
2273 mBrowsingContext->CleanUpDanglingRemoteOuterWindowProxies(cx, &outer);
2274 MOZ_ASSERT(js::IsWindowProxy(outer));
2276 js::SetProxyReservedSlot(outer, OUTER_WINDOW_SLOT,
2277 JS::PrivateValue(ToSupports(this)));
2279 // Inform the nsJSContext, which is the canonical holder of the outer.
2280 mContext->SetWindowProxy(outer);
2282 SetWrapper(mContext->GetWindowProxy());
2283 } else {
2284 JS::Rooted<JSObject*> outerObject(
2285 cx, NewOuterWindowProxy(cx, newInnerGlobal, thisChrome));
2286 if (!outerObject) {
2287 NS_ERROR("out of memory");
2288 return NS_ERROR_FAILURE;
2291 JS::Rooted<JSObject*> obj(cx, GetWrapper());
2293 MOZ_ASSERT(js::IsWindowProxy(obj));
2295 js::SetProxyReservedSlot(obj, OUTER_WINDOW_SLOT,
2296 JS::PrivateValue(nullptr));
2297 js::SetProxyReservedSlot(outerObject, OUTER_WINDOW_SLOT,
2298 JS::PrivateValue(nullptr));
2299 js::SetProxyReservedSlot(obj, HOLDER_WEAKMAP_SLOT, JS::UndefinedValue());
2301 outerObject = xpc::TransplantObjectNukingXrayWaiver(cx, obj, outerObject);
2303 if (!outerObject) {
2304 mBrowsingContext->ClearWindowProxy();
2305 NS_ERROR("unable to transplant wrappers, probably OOM");
2306 return NS_ERROR_FAILURE;
2309 js::SetProxyReservedSlot(outerObject, OUTER_WINDOW_SLOT,
2310 JS::PrivateValue(ToSupports(this)));
2312 SetWrapper(outerObject);
2314 MOZ_ASSERT(JS::GetNonCCWObjectGlobal(outerObject) == newInnerGlobal);
2316 // Inform the nsJSContext, which is the canonical holder of the outer.
2317 mContext->SetWindowProxy(outerObject);
2320 // Enter the new global's realm.
2321 JSAutoRealm ar(cx, GetWrapperPreserveColor());
2324 JS::Rooted<JSObject*> outer(cx, GetWrapperPreserveColor());
2325 js::SetWindowProxy(cx, newInnerGlobal, outer);
2326 mBrowsingContext->SetWindowProxy(outer);
2329 // Set scriptability based on the state of the docshell.
2330 bool allow = GetDocShell()->GetCanExecuteScripts();
2331 xpc::Scriptability::Get(GetWrapperPreserveColor())
2332 .SetDocShellAllowsScript(allow);
2334 if (!aState) {
2335 // Get the "window" property once so it will be cached on our inner. We
2336 // have to do this here, not in binding code, because this has to happen
2337 // after we've created the outer window proxy and stashed it in the outer
2338 // nsGlobalWindowOuter, so GetWrapperPreserveColor() on that outer
2339 // nsGlobalWindowOuter doesn't return null and
2340 // nsGlobalWindowOuter::OuterObject works correctly.
2341 JS::Rooted<JS::Value> unused(cx);
2342 if (!JS_GetProperty(cx, newInnerGlobal, "window", &unused)) {
2343 NS_ERROR("can't create the 'window' property");
2344 return NS_ERROR_FAILURE;
2347 // And same thing for the "self" property.
2348 if (!JS_GetProperty(cx, newInnerGlobal, "self", &unused)) {
2349 NS_ERROR("can't create the 'self' property");
2350 return NS_ERROR_FAILURE;
2355 JSAutoRealm ar(cx, GetWrapperPreserveColor());
2357 if (!aState && !reUseInnerWindow) {
2358 // Loading a new page and creating a new inner window, *not*
2359 // restoring from session history.
2361 // Now that both the the inner and outer windows are initialized
2362 // let the script context do its magic to hook them together.
2363 MOZ_ASSERT(mContext->GetWindowProxy() == GetWrapperPreserveColor());
2364 #ifdef DEBUG
2365 JS::Rooted<JSObject*> rootedJSObject(cx, GetWrapperPreserveColor());
2366 JS::Rooted<JSObject*> proto1(cx), proto2(cx);
2367 JS_GetPrototype(cx, rootedJSObject, &proto1);
2368 JS_GetPrototype(cx, newInnerGlobal, &proto2);
2369 NS_ASSERTION(proto1 == proto2,
2370 "outer and inner globals should have the same prototype");
2371 #endif
2373 mInnerWindow->SyncStateFromParentWindow();
2376 // Add an extra ref in case we release mContext during GC.
2377 nsCOMPtr<nsIScriptContext> kungFuDeathGrip(mContext);
2379 // Make sure the inner's document is set correctly before we call
2380 // SetScriptGlobalObject, because that might try to examine document-dependent
2381 // state. Unfortunately, we can't do some of the other clearing/resetting
2382 // work we do below until after SetScriptGlobalObject(), because it might
2383 // depend on the document having the right scope object.
2384 if (aState) {
2385 MOZ_RELEASE_ASSERT(newInnerWindow->mDoc == aDocument);
2386 } else {
2387 if (reUseInnerWindow) {
2388 MOZ_RELEASE_ASSERT(newInnerWindow->mDoc != aDocument);
2390 newInnerWindow->mDoc = aDocument;
2393 aDocument->SetScriptGlobalObject(newInnerWindow);
2395 MOZ_RELEASE_ASSERT(newInnerWindow->mDoc == aDocument);
2397 if (!aState) {
2398 if (reUseInnerWindow) {
2399 // The storage objects contain the URL of the window. We have to
2400 // recreate them when the innerWindow is reused.
2401 newInnerWindow->mLocalStorage = nullptr;
2402 newInnerWindow->mSessionStorage = nullptr;
2403 newInnerWindow->mPerformance = nullptr;
2405 // This must be called after nullifying the internal objects because
2406 // here we could recreate them, calling the getter methods, and store
2407 // them into the JS slots. If we nullify them after, the slot values and
2408 // the objects will be out of sync.
2409 newInnerWindow->ClearDocumentDependentSlots(cx);
2410 } else {
2411 newInnerWindow->InitDocumentDependentState(cx);
2413 // Initialize DOM classes etc on the inner window.
2414 JS::Rooted<JSObject*> obj(cx, newInnerGlobal);
2415 rv = kungFuDeathGrip->InitClasses(obj);
2416 NS_ENSURE_SUCCESS(rv, rv);
2419 // When replacing an initial about:blank document we call
2420 // ExecutionReady again to update the client creation URL.
2421 rv = newInnerWindow->ExecutionReady();
2422 NS_ENSURE_SUCCESS(rv, rv);
2424 if (mArguments) {
2425 newInnerWindow->DefineArgumentsProperty(mArguments);
2426 mArguments = nullptr;
2429 // Give the new inner window our chrome event handler (since it
2430 // doesn't have one).
2431 newInnerWindow->mChromeEventHandler = mChromeEventHandler;
2434 if (!aState && reUseInnerWindow) {
2435 // Notify our WindowGlobalChild that it has a new document. If `aState` was
2436 // passed, we're restoring the window from the BFCache, so the document
2437 // hasn't changed.
2438 // If we didn't have a window global child before, then initializing
2439 // it will have set all the required state, so we don't need to do
2440 // it again.
2441 mInnerWindow->GetWindowGlobalChild()->OnNewDocument(aDocument);
2444 // Update the current window for our BrowsingContext.
2445 RefPtr<BrowsingContext> bc = GetBrowsingContext();
2447 if (bc->IsOwnedByProcess()) {
2448 MOZ_ALWAYS_SUCCEEDS(bc->SetCurrentInnerWindowId(mInnerWindow->WindowID()));
2451 // We no longer need the old inner window. Start its destruction if
2452 // its not being reused and clear our reference.
2453 if (doomCurrentInner) {
2454 currentInner->FreeInnerObjects();
2456 currentInner = nullptr;
2458 // We wait to fire the debugger hook until the window is all set up and hooked
2459 // up with the outer. See bug 969156.
2460 if (createdInnerWindow) {
2461 nsContentUtils::AddScriptRunner(NewRunnableMethod(
2462 "nsGlobalWindowInner::FireOnNewGlobalObject", newInnerWindow,
2463 &nsGlobalWindowInner::FireOnNewGlobalObject));
2466 if (newInnerWindow && !newInnerWindow->mHasNotifiedGlobalCreated && mDoc) {
2467 // We should probably notify. However if this is the, arguably bad,
2468 // situation when we're creating a temporary non-chrome-about-blank
2469 // document in a chrome docshell, don't notify just yet. Instead wait
2470 // until we have a real chrome doc.
2471 const bool isContentAboutBlankInChromeDocshell = [&] {
2472 if (!mDocShell) {
2473 return false;
2476 RefPtr<BrowsingContext> bc = mDocShell->GetBrowsingContext();
2477 if (!bc || bc->GetType() != BrowsingContext::Type::Chrome) {
2478 return false;
2481 return !mDoc->NodePrincipal()->IsSystemPrincipal();
2482 }();
2484 if (!isContentAboutBlankInChromeDocshell) {
2485 newInnerWindow->mHasNotifiedGlobalCreated = true;
2486 nsContentUtils::AddScriptRunner(NewRunnableMethod(
2487 "nsGlobalWindowOuter::DispatchDOMWindowCreated", this,
2488 &nsGlobalWindowOuter::DispatchDOMWindowCreated));
2492 PreloadLocalStorage();
2494 mStorageAccessPermissionGranted = ContentBlocking::ShouldAllowAccessFor(
2495 newInnerWindow, aDocument->GetDocumentURI(), nullptr);
2497 // Do this here rather than in say the Document constructor, since
2498 // we need a WindowContext available.
2499 mDoc->InitUseCounters();
2501 return NS_OK;
2504 /* static */
2505 void nsGlobalWindowOuter::PrepareForProcessChange(JSObject* aProxy) {
2506 JS::Rooted<JSObject*> localProxy(RootingCx(), aProxy);
2507 MOZ_ASSERT(js::IsWindowProxy(localProxy));
2509 RefPtr<nsGlobalWindowOuter> outerWindow =
2510 nsOuterWindowProxy::GetOuterWindow(localProxy);
2511 if (!outerWindow) {
2512 return;
2515 AutoJSAPI jsapi;
2516 jsapi.Init();
2517 JSContext* cx = jsapi.cx();
2519 JSAutoRealm ar(cx, localProxy);
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());
2536 // Create a new remote outer window proxy, and transplant to it.
2537 JS::Rooted<JSObject*> remoteProxy(cx);
2539 if (!mozilla::dom::GetRemoteOuterWindowProxy(cx, bc, localProxy,
2540 &remoteProxy)) {
2541 MOZ_CRASH("PrepareForProcessChange GetRemoteOuterWindowProxy");
2544 if (!xpc::TransplantObjectNukingXrayWaiver(cx, localProxy, remoteProxy)) {
2545 MOZ_CRASH("PrepareForProcessChange TransplantObject");
2549 void nsGlobalWindowOuter::PreloadLocalStorage() {
2550 if (!Storage::StoragePrefIsEnabled()) {
2551 return;
2554 if (IsChromeWindow()) {
2555 return;
2558 nsIPrincipal* principal = GetPrincipal();
2559 nsIPrincipal* storagePrincipal = GetEffectiveStoragePrincipal();
2560 if (!principal || !storagePrincipal) {
2561 return;
2564 nsresult rv;
2566 nsCOMPtr<nsIDOMStorageManager> storageManager =
2567 do_GetService("@mozilla.org/dom/localStorage-manager;1", &rv);
2568 if (NS_FAILED(rv)) {
2569 return;
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;
2584 void nsGlobalWindowOuter::DispatchDOMWindowCreated() {
2585 if (!mDoc) {
2586 return;
2589 // Fire DOMWindowCreated at chrome event listeners
2590 nsContentUtils::DispatchChromeEvent(mDoc, ToSupports(mDoc),
2591 u"DOMWindowCreated"_ns, CanBubble::eYes,
2592 Cancelable::eNo);
2594 nsCOMPtr<nsIObserverService> observerService =
2595 mozilla::services::GetObserverService();
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());
2612 void nsGlobalWindowOuter::ClearStatus() { SetStatusOuter(u""_ns); }
2614 void nsGlobalWindowOuter::SetDocShell(nsDocShell* aDocShell) {
2615 MOZ_ASSERT(aDocShell);
2617 if (aDocShell == mDocShell) {
2618 return;
2621 mDocShell = aDocShell;
2622 mBrowsingContext = aDocShell->GetBrowsingContext();
2624 RefPtr<BrowsingContext> parentContext = mBrowsingContext->GetParent();
2626 MOZ_RELEASE_ASSERT(!parentContext ||
2627 GetBrowsingContextGroup() == parentContext->Group());
2629 mTopLevelOuterContentWindow = mBrowsingContext->IsTopContent();
2631 // Get our enclosing chrome shell and retrieve its global window impl, so
2632 // that we can do some forwarding to the chrome document.
2633 RefPtr<EventTarget> chromeEventHandler;
2634 mDocShell->GetChromeEventHandler(getter_AddRefs(chromeEventHandler));
2635 mChromeEventHandler = chromeEventHandler;
2636 if (!mChromeEventHandler) {
2637 // We have no chrome event handler. If we have a parent,
2638 // get our chrome event handler from the parent. If
2639 // we don't have a parent, then we need to make a new
2640 // window root object that will function as a chrome event
2641 // handler and receive all events that occur anywhere inside
2642 // our window.
2643 nsCOMPtr<nsPIDOMWindowOuter> parentWindow = GetInProcessParent();
2644 if (parentWindow.get() != this) {
2645 mChromeEventHandler = parentWindow->GetChromeEventHandler();
2646 } else {
2647 mChromeEventHandler = NS_NewWindowRoot(this);
2648 mIsRootOuterWindow = true;
2652 SetIsBackgroundInternal(!mBrowsingContext->IsActive());
2655 void nsGlobalWindowOuter::DetachFromDocShell(bool aIsBeingDiscarded) {
2656 // DetachFromDocShell means the window is being torn down. Drop our
2657 // reference to the script context, allowing it to be deleted
2658 // later. Meanwhile, keep our weak reference to the script object
2659 // so that it can be retrieved later (until it is finalized by the JS GC).
2661 if (mDoc && DocGroup::TryToLoadIframesInBackground()) {
2662 DocGroup* docGroup = GetDocGroup();
2663 RefPtr<nsIDocShell> docShell = GetDocShell();
2664 RefPtr<nsDocShell> dShell = nsDocShell::Cast(docShell);
2665 if (dShell) {
2666 docGroup->TryFlushIframePostMessages(dShell->GetOuterWindowID());
2670 // Call FreeInnerObjects on all inner windows, not just the current
2671 // one, since some could be held by WindowStateHolder objects that
2672 // are GC-owned.
2673 RefPtr<nsGlobalWindowInner> inner;
2674 for (PRCList* node = PR_LIST_HEAD(this); node != this;
2675 node = PR_NEXT_LINK(inner)) {
2676 // This cast is safe because `node != this`. Non-this nodes are inner
2677 // windows.
2678 inner = static_cast<nsGlobalWindowInner*>(node);
2679 MOZ_ASSERT(!inner->mOuterWindow || inner->mOuterWindow == this);
2680 inner->FreeInnerObjects();
2683 // Don't report that we were detached to the nsWindowMemoryReporter, as it
2684 // only tracks inner windows.
2686 NotifyWindowIDDestroyed("outer-window-destroyed");
2688 nsGlobalWindowInner* currentInner = GetCurrentInnerWindowInternal();
2690 if (currentInner) {
2691 NS_ASSERTION(mDoc, "Must have doc!");
2693 // Remember the document's principal and URI.
2694 mDocumentPrincipal = mDoc->NodePrincipal();
2695 mDocumentStoragePrincipal = mDoc->EffectiveStoragePrincipal();
2696 mDocumentPartitionedPrincipal = mDoc->PartitionedPrincipal();
2697 mDocumentURI = mDoc->GetDocumentURI();
2699 // Release our document reference
2700 DropOuterWindowDocs();
2703 ClearControllers();
2705 mChromeEventHandler = nullptr; // force release now
2707 if (mContext) {
2708 // When we're about to destroy a top level content window
2709 // (for example a tab), we trigger a full GC by passing null as the last
2710 // param. We also trigger a full GC for chrome windows.
2711 nsJSContext::PokeGC(JS::GCReason::SET_DOC_SHELL,
2712 (mTopLevelOuterContentWindow || mIsChrome)
2713 ? nullptr
2714 : GetWrapperPreserveColor());
2715 mContext = nullptr;
2718 if (aIsBeingDiscarded) {
2719 // If our BrowsingContext is being discarded, make a note that our current
2720 // inner window was active at the time it went away.
2721 if (GetCurrentInnerWindow()) {
2722 GetCurrentInnerWindowInternal()->SetWasCurrentInnerWindow();
2726 mDocShell = nullptr;
2727 mBrowsingContext->ClearDocShell();
2729 CleanUp();
2732 void nsGlobalWindowOuter::UpdateParentTarget() {
2733 // NOTE: This method is nearly identical to
2734 // nsGlobalWindowInner::UpdateParentTarget(). IF YOU UPDATE THIS METHOD,
2735 // UPDATE THE OTHER ONE TOO! The one difference is that this method updates
2736 // mMessageManager as well, which inner windows don't have.
2738 // Try to get our frame element's tab child global (its in-process message
2739 // manager). If that fails, fall back to the chrome event handler's tab
2740 // child global, and if it doesn't have one, just use the chrome event
2741 // handler itself.
2743 nsCOMPtr<Element> frameElement = GetFrameElementInternal();
2744 mMessageManager = nsContentUtils::TryGetBrowserChildGlobal(frameElement);
2746 if (!mMessageManager) {
2747 nsGlobalWindowOuter* topWin = GetInProcessScriptableTopInternal();
2748 if (topWin) {
2749 frameElement = topWin->GetFrameElementInternal();
2750 mMessageManager = nsContentUtils::TryGetBrowserChildGlobal(frameElement);
2754 if (!mMessageManager) {
2755 mMessageManager =
2756 nsContentUtils::TryGetBrowserChildGlobal(mChromeEventHandler);
2759 if (mMessageManager) {
2760 mParentTarget = mMessageManager;
2761 } else {
2762 mParentTarget = mChromeEventHandler;
2766 EventTarget* nsGlobalWindowOuter::GetTargetForEventTargetChain() {
2767 return GetCurrentInnerWindowInternal();
2770 void nsGlobalWindowOuter::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
2771 MOZ_CRASH("The outer window should not be part of an event path");
2774 bool nsGlobalWindowOuter::ShouldPromptToBlockDialogs() {
2775 if (!nsContentUtils::GetCurrentJSContext()) {
2776 return false; // non-scripted caller.
2779 nsGlobalWindowOuter* topWindowOuter = GetInProcessScriptableTopInternal();
2780 if (!topWindowOuter) {
2781 NS_ASSERTION(!mDocShell,
2782 "ShouldPromptToBlockDialogs() called without a top window?");
2783 return true;
2786 nsGlobalWindowInner* topWindow =
2787 topWindowOuter->GetCurrentInnerWindowInternal();
2788 if (!topWindow) {
2789 return true;
2792 return topWindow->DialogsAreBeingAbused();
2795 bool nsGlobalWindowOuter::AreDialogsEnabled() {
2796 nsGlobalWindowOuter* topWindowOuter = GetInProcessScriptableTopInternal();
2797 if (!topWindowOuter) {
2798 NS_ERROR("AreDialogsEnabled() called without a top window?");
2799 return false;
2802 // TODO: Warn if no top window?
2803 nsGlobalWindowInner* topWindow =
2804 topWindowOuter->GetCurrentInnerWindowInternal();
2805 if (!topWindow) {
2806 return false;
2809 // Dialogs are blocked if the content viewer is hidden
2810 if (mDocShell) {
2811 nsCOMPtr<nsIContentViewer> cv;
2812 mDocShell->GetContentViewer(getter_AddRefs(cv));
2814 bool isHidden;
2815 cv->GetIsHidden(&isHidden);
2816 if (isHidden) {
2817 return false;
2821 // Dialogs are also blocked if the document is sandboxed with SANDBOXED_MODALS
2822 // (or if we have no document, of course). Which document? Who knows; the
2823 // spec is daft. See <https://github.com/whatwg/html/issues/1206>. For now
2824 // just go ahead and check mDoc, since in everything except edge cases in
2825 // which a frame is allow-same-origin but not allow-scripts and is being poked
2826 // at by some other window this should be the right thing anyway.
2827 if (!mDoc || (mDoc->GetSandboxFlags() & SANDBOXED_MODALS)) {
2828 return false;
2831 return topWindow->mAreDialogsEnabled;
2834 bool nsGlobalWindowOuter::ConfirmDialogIfNeeded() {
2835 NS_ENSURE_TRUE(mDocShell, false);
2836 nsCOMPtr<nsIPromptService> promptSvc =
2837 do_GetService("@mozilla.org/embedcomp/prompt-service;1");
2839 if (!promptSvc) {
2840 return true;
2843 // Reset popup state while opening a modal dialog, and firing events
2844 // about the dialog, to prevent the current state from being active
2845 // the whole time a modal dialog is open.
2846 AutoPopupStatePusher popupStatePusher(PopupBlocker::openAbused, true);
2848 bool disableDialog = false;
2849 nsAutoString label, title;
2850 nsContentUtils::GetLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES,
2851 "ScriptDialogLabel", label);
2852 nsContentUtils::GetLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES,
2853 "ScriptDialogPreventTitle", title);
2854 promptSvc->Confirm(this, title.get(), label.get(), &disableDialog);
2855 if (disableDialog) {
2856 DisableDialogs();
2857 return false;
2860 return true;
2863 void nsGlobalWindowOuter::DisableDialogs() {
2864 nsGlobalWindowOuter* topWindowOuter = GetInProcessScriptableTopInternal();
2865 if (!topWindowOuter) {
2866 NS_ERROR("DisableDialogs() called without a top window?");
2867 return;
2870 nsGlobalWindowInner* topWindow =
2871 topWindowOuter->GetCurrentInnerWindowInternal();
2872 // TODO: Warn if no top window?
2873 if (topWindow) {
2874 topWindow->mAreDialogsEnabled = false;
2878 void nsGlobalWindowOuter::EnableDialogs() {
2879 nsGlobalWindowOuter* topWindowOuter = GetInProcessScriptableTopInternal();
2880 if (!topWindowOuter) {
2881 NS_ERROR("EnableDialogs() called without a top window?");
2882 return;
2885 // TODO: Warn if no top window?
2886 nsGlobalWindowInner* topWindow =
2887 topWindowOuter->GetCurrentInnerWindowInternal();
2888 if (topWindow) {
2889 topWindow->mAreDialogsEnabled = true;
2893 nsresult nsGlobalWindowOuter::PostHandleEvent(EventChainPostVisitor& aVisitor) {
2894 MOZ_CRASH("The outer window should not be part of an event path");
2897 void nsGlobalWindowOuter::PoisonOuterWindowProxy(JSObject* aObject) {
2898 if (aObject == GetWrapperMaybeDead()) {
2899 PoisonWrapper();
2903 nsresult nsGlobalWindowOuter::SetArguments(nsIArray* aArguments) {
2904 nsresult rv;
2906 // We've now mostly separated them, but the difference is still opaque to
2907 // nsWindowWatcher (the caller of SetArguments in this little back-and-forth
2908 // embedding waltz we do here).
2910 // So we need to demultiplex the two cases here.
2911 nsGlobalWindowInner* currentInner = GetCurrentInnerWindowInternal();
2913 mArguments = aArguments;
2914 rv = currentInner->DefineArgumentsProperty(aArguments);
2915 NS_ENSURE_SUCCESS(rv, rv);
2917 return NS_OK;
2920 //*****************************************************************************
2921 // nsGlobalWindowOuter::nsIScriptObjectPrincipal
2922 //*****************************************************************************
2924 nsIPrincipal* nsGlobalWindowOuter::GetPrincipal() {
2925 if (mDoc) {
2926 // If we have a document, get the principal from the document
2927 return mDoc->NodePrincipal();
2930 if (mDocumentPrincipal) {
2931 return mDocumentPrincipal;
2934 // If we don't have a principal and we don't have a document we
2935 // ask the parent window for the principal. This can happen when
2936 // loading a frameset that has a <frame src="javascript:xxx">, in
2937 // that case the global window is used in JS before we've loaded
2938 // a document into the window.
2940 nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
2941 do_QueryInterface(GetInProcessParentInternal());
2943 if (objPrincipal) {
2944 return objPrincipal->GetPrincipal();
2947 return nullptr;
2950 nsIPrincipal* nsGlobalWindowOuter::GetEffectiveStoragePrincipal() {
2951 if (mDoc) {
2952 // If we have a document, get the principal from the document
2953 return mDoc->EffectiveStoragePrincipal();
2956 if (mDocumentStoragePrincipal) {
2957 return mDocumentStoragePrincipal;
2960 // If we don't have a storage principal and we don't have a document we ask
2961 // the parent window for the storage principal.
2963 nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
2964 do_QueryInterface(GetInProcessParentInternal());
2966 if (objPrincipal) {
2967 return objPrincipal->GetEffectiveStoragePrincipal();
2970 return nullptr;
2973 nsIPrincipal* nsGlobalWindowOuter::PartitionedPrincipal() {
2974 if (mDoc) {
2975 // If we have a document, get the principal from the document
2976 return mDoc->PartitionedPrincipal();
2979 if (mDocumentPartitionedPrincipal) {
2980 return mDocumentPartitionedPrincipal;
2983 // If we don't have a partitioned principal and we don't have a document we
2984 // ask the parent window for the partitioned principal.
2986 nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
2987 do_QueryInterface(GetInProcessParentInternal());
2989 if (objPrincipal) {
2990 return objPrincipal->PartitionedPrincipal();
2993 return nullptr;
2996 //*****************************************************************************
2997 // nsGlobalWindowOuter::nsIDOMWindow
2998 //*****************************************************************************
3000 void nsPIDOMWindowOuter::SetInitialKeyboardIndicators(
3001 UIStateChangeType aShowFocusRings) {
3002 MOZ_ASSERT(!GetCurrentInnerWindow());
3004 nsPIDOMWindowOuter* piWin = GetPrivateRoot();
3005 if (!piWin) {
3006 return;
3009 MOZ_ASSERT(piWin == this);
3011 // only change the flags that have been modified
3012 nsCOMPtr<nsPIWindowRoot> windowRoot = do_QueryInterface(mChromeEventHandler);
3013 if (!windowRoot) {
3014 return;
3017 if (aShowFocusRings != UIStateChangeType_NoChange) {
3018 windowRoot->SetShowFocusRings(aShowFocusRings == UIStateChangeType_Set);
3021 nsContentUtils::SetKeyboardIndicatorsOnRemoteChildren(this, aShowFocusRings);
3024 Element* nsPIDOMWindowOuter::GetFrameElementInternal() const {
3025 return mFrameElement;
3028 void nsPIDOMWindowOuter::SetFrameElementInternal(Element* aFrameElement) {
3029 mFrameElement = aFrameElement;
3032 Navigator* nsGlobalWindowOuter::GetNavigator() {
3033 FORWARD_TO_INNER(Navigator, (), nullptr);
3036 nsScreen* nsGlobalWindowOuter::GetScreen() {
3037 FORWARD_TO_INNER(GetScreen, (IgnoreErrors()), nullptr);
3040 void nsPIDOMWindowOuter::MaybeActiveMediaComponents() {
3041 if (mMediaSuspend != nsISuspendedTypes::SUSPENDED_BLOCK) {
3042 return;
3045 MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
3046 ("nsPIDOMWindowOuter, MaybeActiveMediaComponents, "
3047 "resume the window from blocked, this = %p\n",
3048 this));
3050 SetMediaSuspend(nsISuspendedTypes::NONE_SUSPENDED);
3053 SuspendTypes nsPIDOMWindowOuter::GetMediaSuspend() const {
3054 return mMediaSuspend;
3057 void nsPIDOMWindowOuter::SetMediaSuspend(SuspendTypes aSuspend) {
3058 MaybeNotifyMediaResumedFromBlock(aSuspend);
3059 mMediaSuspend = aSuspend;
3060 RefreshMediaElementsSuspend(aSuspend);
3063 void nsPIDOMWindowOuter::MaybeNotifyMediaResumedFromBlock(
3064 SuspendTypes aSuspend) {
3065 if (mMediaSuspend == nsISuspendedTypes::SUSPENDED_BLOCK &&
3066 aSuspend == nsISuspendedTypes::NONE_SUSPENDED) {
3067 RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
3068 if (service) {
3069 service->NotifyMediaResumedFromBlock(this);
3074 bool nsPIDOMWindowOuter::GetAudioMuted() const {
3075 BrowsingContext* bc = GetBrowsingContext();
3076 return bc && bc->Top()->GetMuted();
3079 void nsPIDOMWindowOuter::RefreshMediaElementsVolume() {
3080 RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
3081 if (service) {
3082 // TODO: RefreshAgentsVolume can probably be simplified further.
3083 service->RefreshAgentsVolume(this, 1.0f, GetAudioMuted());
3087 void nsPIDOMWindowOuter::RefreshMediaElementsSuspend(SuspendTypes aSuspend) {
3088 RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
3089 if (service) {
3090 service->RefreshAgentsSuspend(this, aSuspend);
3094 mozilla::dom::BrowsingContextGroup*
3095 nsPIDOMWindowOuter::GetBrowsingContextGroup() const {
3096 return mBrowsingContext ? mBrowsingContext->Group() : nullptr;
3099 Nullable<WindowProxyHolder> nsGlobalWindowOuter::GetParentOuter() {
3100 BrowsingContext* bc = GetBrowsingContext();
3101 return bc ? bc->GetParent(IgnoreErrors()) : nullptr;
3105 * GetInProcessScriptableParent used to be called when a script read
3106 * window.parent. Under Fission, that is now handled by
3107 * BrowsingContext::GetParent, and the result is a WindowProxyHolder rather than
3108 * an actual global window. This method still exists for legacy callers which
3109 * relied on the old logic, and require in-process windows. However, it only
3110 * works correctly when no out-of-process frames exist between this window and
3111 * the top-level window, so it should not be used in new code.
3113 * In contrast to GetRealParent, GetInProcessScriptableParent respects <iframe
3114 * mozbrowser> boundaries, so if |this| is contained by an <iframe
3115 * mozbrowser>, we will return |this| as its own parent.
3117 nsPIDOMWindowOuter* nsGlobalWindowOuter::GetInProcessScriptableParent() {
3118 if (!mDocShell) {
3119 return nullptr;
3122 if (BrowsingContext* parentBC = GetBrowsingContext()->GetParent()) {
3123 if (nsCOMPtr<nsPIDOMWindowOuter> parent = parentBC->GetDOMWindow()) {
3124 return parent;
3127 return this;
3131 * Behavies identically to GetInProcessScriptableParent extept that it returns
3132 * null if GetInProcessScriptableParent would return this window.
3134 nsPIDOMWindowOuter* nsGlobalWindowOuter::GetInProcessScriptableParentOrNull() {
3135 nsPIDOMWindowOuter* parent = GetInProcessScriptableParent();
3136 return (nsGlobalWindowOuter::Cast(parent) == this) ? nullptr : parent;
3140 * nsPIDOMWindow::GetParent (when called from C++) is just a wrapper around
3141 * GetRealParent.
3143 already_AddRefed<nsPIDOMWindowOuter> nsGlobalWindowOuter::GetInProcessParent() {
3144 if (!mDocShell) {
3145 return nullptr;
3148 nsCOMPtr<nsIDocShell> parent;
3149 mDocShell->GetSameTypeInProcessParentIgnoreBrowserBoundaries(
3150 getter_AddRefs(parent));
3152 if (parent) {
3153 nsCOMPtr<nsPIDOMWindowOuter> win = parent->GetWindow();
3154 return win.forget();
3157 nsCOMPtr<nsPIDOMWindowOuter> win(this);
3158 return win.forget();
3161 static nsresult GetTopImpl(nsGlobalWindowOuter* aWin, nsIURI* aURIBeingLoaded,
3162 nsPIDOMWindowOuter** aTop, bool aScriptable,
3163 bool aExcludingExtensionAccessibleContentFrames) {
3164 *aTop = nullptr;
3166 MOZ_ASSERT_IF(aExcludingExtensionAccessibleContentFrames, !aScriptable);
3168 // Walk up the parent chain.
3170 nsCOMPtr<nsPIDOMWindowOuter> prevParent = aWin;
3171 nsCOMPtr<nsPIDOMWindowOuter> parent = aWin;
3172 do {
3173 if (!parent) {
3174 break;
3177 prevParent = parent;
3179 if (aScriptable) {
3180 parent = parent->GetInProcessScriptableParent();
3181 } else {
3182 parent = parent->GetInProcessParent();
3185 if (aExcludingExtensionAccessibleContentFrames) {
3186 if (auto* p = nsGlobalWindowOuter::Cast(parent)) {
3187 nsGlobalWindowInner* currentInner = p->GetCurrentInnerWindowInternal();
3188 nsIURI* uri = prevParent->GetDocumentURI();
3189 if (!uri) {
3190 // If our parent doesn't have a URI yet, we have a document that is in
3191 // the process of being loaded. In that case, our caller is
3192 // responsible for passing in the URI for the document that is being
3193 // loaded, so we fall back to using that URI here.
3194 uri = aURIBeingLoaded;
3197 if (currentInner && uri) {
3198 // If we find an inner window, we better find the uri for the current
3199 // window we're looking at. If we can't find it directly, it is the
3200 // responsibility of our caller to provide it to us.
3201 MOZ_DIAGNOSTIC_ASSERT(uri);
3203 // If the new parent has permission to load the current page, we're
3204 // at a moz-extension:// frame which has a host permission that allows
3205 // it to load the document that we've loaded. In that case, stop at
3206 // this frame and consider it the top-level frame.
3208 // Note that it's possible for the set of URIs accepted by
3209 // AddonAllowsLoad() to change at runtime, but we don't need to cache
3210 // the result of this check, since the important consumer of this code
3211 // (which is nsIHttpChannelInternal.topWindowURI) already caches the
3212 // result after computing it the first time.
3213 if (BasePrincipal::Cast(p->GetPrincipal())
3214 ->AddonAllowsLoad(uri, true)) {
3215 parent = prevParent;
3216 break;
3222 } while (parent != prevParent);
3224 if (parent) {
3225 parent.swap(*aTop);
3228 return NS_OK;
3232 * GetInProcessScriptableTop used to be called when a script read window.top.
3233 * Under Fission, that is now handled by BrowsingContext::Top, and the result is
3234 * a WindowProxyHolder rather than an actual global window. This method still
3235 * exists for legacy callers which relied on the old logic, and require
3236 * in-process windows. However, it only works correctly when no out-of-process
3237 * frames exist between this window and the top-level window, so it should not
3238 * be used in new code.
3240 * In contrast to GetRealTop, GetInProcessScriptableTop respects <iframe
3241 * mozbrowser> boundaries. If we encounter a window owned by an <iframe
3242 * mozbrowser> while walking up the window hierarchy, we'll stop and return that
3243 * window.
3245 nsPIDOMWindowOuter* nsGlobalWindowOuter::GetInProcessScriptableTop() {
3246 nsCOMPtr<nsPIDOMWindowOuter> window;
3247 GetTopImpl(this, /* aURIBeingLoaded = */ nullptr, getter_AddRefs(window),
3248 /* aScriptable = */ true,
3249 /* aExcludingExtensionAccessibleContentFrames = */ false);
3250 return window.get();
3253 already_AddRefed<nsPIDOMWindowOuter> nsGlobalWindowOuter::GetInProcessTop() {
3254 nsCOMPtr<nsPIDOMWindowOuter> window;
3255 GetTopImpl(this, /* aURIBeingLoaded = */ nullptr, getter_AddRefs(window),
3256 /* aScriptable = */ false,
3257 /* aExcludingExtensionAccessibleContentFrames = */ false);
3258 return window.forget();
3261 already_AddRefed<nsPIDOMWindowOuter>
3262 nsGlobalWindowOuter::GetTopExcludingExtensionAccessibleContentFrames(
3263 nsIURI* aURIBeingLoaded) {
3264 // There is a parent-process equivalent of this in DocumentLoadListener.cpp
3265 // GetTopWindowExcludingExtensionAccessibleContentFrames
3266 nsCOMPtr<nsPIDOMWindowOuter> window;
3267 GetTopImpl(this, aURIBeingLoaded, getter_AddRefs(window),
3268 /* aScriptable = */ false,
3269 /* aExcludingExtensionAccessibleContentFrames = */ true);
3270 return window.forget();
3273 void nsGlobalWindowOuter::GetContentOuter(JSContext* aCx,
3274 JS::MutableHandle<JSObject*> aRetval,
3275 CallerType aCallerType,
3276 ErrorResult& aError) {
3277 RefPtr<BrowsingContext> content = GetContentInternal(aCallerType, aError);
3278 if (aError.Failed()) {
3279 return;
3282 if (!content) {
3283 aRetval.set(nullptr);
3284 return;
3287 JS::Rooted<JS::Value> val(aCx);
3288 if (!ToJSValue(aCx, WindowProxyHolder{content}, &val)) {
3289 aError.Throw(NS_ERROR_UNEXPECTED);
3290 return;
3293 MOZ_ASSERT(val.isObjectOrNull());
3294 aRetval.set(val.toObjectOrNull());
3297 already_AddRefed<BrowsingContext> nsGlobalWindowOuter::GetContentInternal(
3298 CallerType aCallerType, ErrorResult& aError) {
3299 // First check for a named frame named "content"
3300 if (RefPtr<BrowsingContext> named = GetChildWindow(u"content"_ns)) {
3301 return named.forget();
3304 // If we're in the parent process, and being called by system code, `content`
3305 // should return the current primary content frame (if it's in-process).
3307 // We return `nullptr` if the current primary content frame is out-of-process,
3308 // rather than a remote window proxy, as that is the existing behaviour as of
3309 // bug 1597437.
3310 if (XRE_IsParentProcess() && aCallerType == CallerType::System) {
3311 nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner();
3312 if (!treeOwner) {
3313 aError.Throw(NS_ERROR_FAILURE);
3314 return nullptr;
3317 nsCOMPtr<nsIDocShellTreeItem> primaryContent;
3318 treeOwner->GetPrimaryContentShell(getter_AddRefs(primaryContent));
3319 if (!primaryContent) {
3320 return nullptr;
3323 return do_AddRef(primaryContent->GetBrowsingContext());
3326 // For legacy untrusted callers we always return the same value as
3327 // `window.top`
3328 if (mDoc && aCallerType != CallerType::System) {
3329 mDoc->WarnOnceAbout(DeprecatedOperations::eWindowContentUntrusted);
3332 MOZ_ASSERT(mBrowsingContext->IsContent());
3333 return do_AddRef(mBrowsingContext->Top());
3336 nsresult nsGlobalWindowOuter::GetPrompter(nsIPrompt** aPrompt) {
3337 if (!mDocShell) return NS_ERROR_FAILURE;
3339 nsCOMPtr<nsIPrompt> prompter(do_GetInterface(mDocShell));
3340 NS_ENSURE_TRUE(prompter, NS_ERROR_NO_INTERFACE);
3342 prompter.forget(aPrompt);
3343 return NS_OK;
3346 bool nsGlobalWindowOuter::GetClosedOuter() {
3347 // If someone called close(), or if we don't have a docshell, we're closed.
3348 return mIsClosed || !mDocShell;
3351 bool nsGlobalWindowOuter::Closed() { return GetClosedOuter(); }
3353 Nullable<WindowProxyHolder> nsGlobalWindowOuter::IndexedGetterOuter(
3354 uint32_t aIndex) {
3355 BrowsingContext* bc = GetBrowsingContext();
3356 NS_ENSURE_TRUE(bc, nullptr);
3358 Span<RefPtr<BrowsingContext>> children = bc->Children();
3359 if (aIndex < children.Length()) {
3360 return WindowProxyHolder(children[aIndex]);
3362 return nullptr;
3365 nsIControllers* nsGlobalWindowOuter::GetControllersOuter(ErrorResult& aError) {
3366 if (!mControllers) {
3367 mControllers = new nsXULControllers();
3368 if (!mControllers) {
3369 aError.Throw(NS_ERROR_FAILURE);
3370 return nullptr;
3373 // Add in the default controller
3374 RefPtr<nsBaseCommandController> commandController =
3375 nsBaseCommandController::CreateWindowController();
3376 if (!commandController) {
3377 aError.Throw(NS_ERROR_FAILURE);
3378 return nullptr;
3381 mControllers->InsertControllerAt(0, commandController);
3382 commandController->SetCommandContext(static_cast<nsIDOMWindow*>(this));
3385 return mControllers;
3388 nsresult nsGlobalWindowOuter::GetControllers(nsIControllers** aResult) {
3389 FORWARD_TO_INNER(GetControllers, (aResult), NS_ERROR_UNEXPECTED);
3392 already_AddRefed<BrowsingContext>
3393 nsGlobalWindowOuter::GetOpenerBrowsingContext() {
3394 RefPtr<BrowsingContext> opener = GetBrowsingContext()->GetOpener();
3395 MOZ_DIAGNOSTIC_ASSERT(!opener ||
3396 opener->Group() == GetBrowsingContext()->Group());
3397 if (!opener || opener->Group() != GetBrowsingContext()->Group()) {
3398 return nullptr;
3401 // Catch the case where we're chrome but the opener is not...
3402 if (nsContentUtils::LegacyIsCallerChromeOrNativeCode() &&
3403 GetPrincipal() == nsContentUtils::GetSystemPrincipal()) {
3404 auto* openerWin = nsGlobalWindowOuter::Cast(opener->GetDOMWindow());
3405 if (!openerWin ||
3406 openerWin->GetPrincipal() != nsContentUtils::GetSystemPrincipal()) {
3407 return nullptr;
3411 return opener.forget();
3414 nsPIDOMWindowOuter* nsGlobalWindowOuter::GetSameProcessOpener() {
3415 if (RefPtr<BrowsingContext> opener = GetOpenerBrowsingContext()) {
3416 return opener->GetDOMWindow();
3418 return nullptr;
3421 Nullable<WindowProxyHolder> nsGlobalWindowOuter::GetOpenerWindowOuter() {
3422 if (RefPtr<BrowsingContext> opener = GetOpenerBrowsingContext()) {
3423 return WindowProxyHolder(std::move(opener));
3425 return nullptr;
3428 Nullable<WindowProxyHolder> nsGlobalWindowOuter::GetOpener() {
3429 return GetOpenerWindowOuter();
3432 void nsGlobalWindowOuter::GetStatusOuter(nsAString& aStatus) {
3433 aStatus = mStatus;
3436 void nsGlobalWindowOuter::SetStatusOuter(const nsAString& aStatus) {
3437 mStatus = aStatus;
3439 // We don't support displaying window.status in the UI, so there's nothing
3440 // left to do here.
3443 void nsGlobalWindowOuter::GetNameOuter(nsAString& aName) {
3444 if (mDocShell) {
3445 mDocShell->GetName(aName);
3449 void nsGlobalWindowOuter::SetNameOuter(const nsAString& aName,
3450 mozilla::ErrorResult& aError) {
3451 if (mDocShell) {
3452 aError = mDocShell->SetName(aName);
3456 // Helper functions used by many methods below.
3457 int32_t nsGlobalWindowOuter::DevToCSSIntPixelsForBaseWindow(
3458 int32_t aDevicePixels, nsIBaseWindow* aWindow) {
3459 MOZ_ASSERT(aWindow);
3460 double scale;
3461 aWindow->GetUnscaledDevicePixelsPerCSSPixel(&scale);
3462 double zoom = 1.0;
3463 if (mDocShell && mDocShell->GetPresContext()) {
3464 zoom = mDocShell->GetPresContext()->GetFullZoom();
3466 return std::floor(aDevicePixels / scale / zoom + 0.5);
3469 nsIntSize nsGlobalWindowOuter::DevToCSSIntPixelsForBaseWindow(
3470 nsIntSize aDeviceSize, nsIBaseWindow* aWindow) {
3471 MOZ_ASSERT(aWindow);
3472 double scale;
3473 aWindow->GetUnscaledDevicePixelsPerCSSPixel(&scale);
3474 double zoom = 1.0;
3475 if (mDocShell && mDocShell->GetPresContext()) {
3476 zoom = mDocShell->GetPresContext()->GetFullZoom();
3478 return nsIntSize::Round(
3479 static_cast<float>(aDeviceSize.width / scale / zoom),
3480 static_cast<float>(aDeviceSize.height / scale / zoom));
3483 int32_t nsGlobalWindowOuter::CSSToDevIntPixelsForBaseWindow(
3484 int32_t aCSSPixels, nsIBaseWindow* aWindow) {
3485 MOZ_ASSERT(aWindow);
3486 double scale;
3487 aWindow->GetUnscaledDevicePixelsPerCSSPixel(&scale);
3488 double zoom = 1.0;
3489 if (mDocShell && mDocShell->GetPresContext()) {
3490 zoom = mDocShell->GetPresContext()->GetFullZoom();
3492 return std::floor(aCSSPixels * scale * zoom + 0.5);
3495 nsIntSize nsGlobalWindowOuter::CSSToDevIntPixelsForBaseWindow(
3496 nsIntSize aCSSSize, nsIBaseWindow* aWindow) {
3497 MOZ_ASSERT(aWindow);
3498 double scale;
3499 aWindow->GetUnscaledDevicePixelsPerCSSPixel(&scale);
3500 double zoom = 1.0;
3501 if (mDocShell && mDocShell->GetPresContext()) {
3502 zoom = mDocShell->GetPresContext()->GetFullZoom();
3504 return nsIntSize::Round(static_cast<float>(aCSSSize.width * scale * zoom),
3505 static_cast<float>(aCSSSize.height * scale * zoom));
3508 nsresult nsGlobalWindowOuter::GetInnerSize(CSSSize& aSize) {
3509 EnsureSizeAndPositionUpToDate();
3511 NS_ENSURE_STATE(mDocShell);
3513 RefPtr<nsPresContext> presContext = mDocShell->GetPresContext();
3514 PresShell* presShell = mDocShell->GetPresShell();
3516 if (!presContext || !presShell) {
3517 aSize = {};
3518 return NS_OK;
3521 // Whether or not the css viewport has been overridden, we can get the
3522 // correct value by looking at the visible area of the presContext.
3523 RefPtr<nsViewManager> viewManager = presShell->GetViewManager();
3524 if (viewManager) {
3525 viewManager->FlushDelayedResize(false);
3528 // FIXME: Bug 1598487 - Return the layout viewport instead of the ICB.
3529 nsSize viewportSize = presContext->GetVisibleArea().Size();
3530 if (presContext->GetDynamicToolbarState() == DynamicToolbarState::Collapsed) {
3531 viewportSize =
3532 nsLayoutUtils::ExpandHeightForViewportUnits(presContext, viewportSize);
3535 aSize = CSSPixel::FromAppUnits(viewportSize);
3537 if (StaticPrefs::dom_innerSize_rounded()) {
3538 aSize.width = std::roundf(aSize.width);
3539 aSize.height = std::roundf(aSize.height);
3542 return NS_OK;
3545 double nsGlobalWindowOuter::GetInnerWidthOuter(ErrorResult& aError) {
3546 CSSSize size;
3547 aError = GetInnerSize(size);
3548 return size.width;
3551 nsresult nsGlobalWindowOuter::GetInnerWidth(double* aInnerWidth) {
3552 FORWARD_TO_INNER(GetInnerWidth, (aInnerWidth), NS_ERROR_UNEXPECTED);
3555 void nsGlobalWindowOuter::SetInnerWidthOuter(double aInnerWidth,
3556 CallerType aCallerType,
3557 ErrorResult& aError) {
3558 if (!mDocShell) {
3559 aError.Throw(NS_ERROR_UNEXPECTED);
3560 return;
3563 // We only allow setting integers, for now.
3564 int32_t value = std::round(ToZeroIfNonfinite(aInnerWidth));
3566 CheckSecurityWidthAndHeight(&value, nullptr, aCallerType);
3567 RefPtr<PresShell> presShell = mDocShell->GetPresShell();
3569 // Setting inner width should set the CSS viewport. If the CSS viewport
3570 // has been overridden, change the override.
3571 if (presShell && presShell->UsesMobileViewportSizing()) {
3572 nscoord height = 0;
3574 RefPtr<nsPresContext> presContext;
3575 presContext = presShell->GetPresContext();
3577 nsRect shellArea = presContext->GetVisibleArea();
3578 height = shellArea.Height();
3579 SetCSSViewportWidthAndHeight(CSSPixel::ToAppUnits(value), height);
3580 return;
3583 // Nothing has been overriden, so change the docshell itself.
3584 int32_t height = 0;
3585 int32_t unused = 0;
3587 nsCOMPtr<nsIBaseWindow> docShellAsWin(do_QueryInterface(mDocShell));
3588 docShellAsWin->GetSize(&unused, &height);
3589 aError = SetDocShellWidthAndHeight(
3590 CSSToDevIntPixelsForBaseWindow(value, docShellAsWin), height);
3593 double nsGlobalWindowOuter::GetInnerHeightOuter(ErrorResult& aError) {
3594 CSSSize size;
3595 aError = GetInnerSize(size);
3596 return size.height;
3599 nsresult nsGlobalWindowOuter::GetInnerHeight(double* aInnerHeight) {
3600 FORWARD_TO_INNER(GetInnerHeight, (aInnerHeight), NS_ERROR_UNEXPECTED);
3603 void nsGlobalWindowOuter::SetInnerHeightOuter(double aInnerHeight,
3604 CallerType aCallerType,
3605 ErrorResult& aError) {
3606 if (!mDocShell) {
3607 aError.Throw(NS_ERROR_UNEXPECTED);
3608 return;
3611 int32_t value = std::round(ToZeroIfNonfinite(aInnerHeight));
3612 CheckSecurityWidthAndHeight(nullptr, &value, aCallerType);
3613 RefPtr<PresShell> presShell = mDocShell->GetPresShell();
3615 // Setting inner height should set the CSS viewport. If the CSS viewport
3616 // has been overridden, change the override.
3617 if (presShell && presShell->UsesMobileViewportSizing()) {
3618 nscoord width = 0;
3620 RefPtr<nsPresContext> presContext;
3621 presContext = presShell->GetPresContext();
3623 nsRect shellArea = presContext->GetVisibleArea();
3624 width = shellArea.Width();
3625 SetCSSViewportWidthAndHeight(width, CSSPixel::ToAppUnits(value));
3626 return;
3629 // Nothing has been overriden, so change the docshell itself.
3630 int32_t height = 0;
3631 int32_t width = 0;
3633 nsCOMPtr<nsIBaseWindow> docShellAsWin(do_QueryInterface(mDocShell));
3634 docShellAsWin->GetSize(&width, &height);
3635 aError = SetDocShellWidthAndHeight(
3636 width, CSSToDevIntPixelsForBaseWindow(value, docShellAsWin));
3639 nsIntSize nsGlobalWindowOuter::GetOuterSize(CallerType aCallerType,
3640 ErrorResult& aError) {
3641 if (nsContentUtils::ResistFingerprinting(aCallerType)) {
3642 CSSSize size;
3643 aError = GetInnerSize(size);
3644 return nsIntSize::Round(size.width, size.height);
3647 // Windows showing documents in RDM panes and any subframes within them
3648 // return the simulated device size.
3649 if (mDoc) {
3650 Maybe<CSSIntSize> deviceSize = GetRDMDeviceSize(*mDoc);
3651 if (deviceSize.isSome()) {
3652 const CSSIntSize& size = deviceSize.value();
3653 return nsIntSize(size.width, size.height);
3657 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
3658 if (!treeOwnerAsWin) {
3659 aError.Throw(NS_ERROR_FAILURE);
3660 return nsIntSize(0, 0);
3663 nsIntSize sizeDevPixels;
3664 aError = treeOwnerAsWin->GetSize(&sizeDevPixels.width, &sizeDevPixels.height);
3665 if (aError.Failed()) {
3666 return nsIntSize();
3669 return DevToCSSIntPixelsForBaseWindow(sizeDevPixels, treeOwnerAsWin);
3672 int32_t nsGlobalWindowOuter::GetOuterWidthOuter(CallerType aCallerType,
3673 ErrorResult& aError) {
3674 return GetOuterSize(aCallerType, aError).width;
3677 int32_t nsGlobalWindowOuter::GetOuterHeightOuter(CallerType aCallerType,
3678 ErrorResult& aError) {
3679 return GetOuterSize(aCallerType, aError).height;
3682 void nsGlobalWindowOuter::SetOuterSize(int32_t aLengthCSSPixels, bool aIsWidth,
3683 CallerType aCallerType,
3684 ErrorResult& aError) {
3685 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
3686 if (!treeOwnerAsWin) {
3687 aError.Throw(NS_ERROR_FAILURE);
3688 return;
3691 CheckSecurityWidthAndHeight(aIsWidth ? &aLengthCSSPixels : nullptr,
3692 aIsWidth ? nullptr : &aLengthCSSPixels,
3693 aCallerType);
3695 int32_t width, height;
3696 aError = treeOwnerAsWin->GetSize(&width, &height);
3697 if (aError.Failed()) {
3698 return;
3701 int32_t lengthDevPixels =
3702 CSSToDevIntPixelsForBaseWindow(aLengthCSSPixels, treeOwnerAsWin);
3703 if (aIsWidth) {
3704 width = lengthDevPixels;
3705 } else {
3706 height = lengthDevPixels;
3708 aError = treeOwnerAsWin->SetSize(width, height, true);
3710 CheckForDPIChange();
3713 void nsGlobalWindowOuter::SetOuterWidthOuter(int32_t aOuterWidth,
3714 CallerType aCallerType,
3715 ErrorResult& aError) {
3716 SetOuterSize(aOuterWidth, true, aCallerType, aError);
3719 void nsGlobalWindowOuter::SetOuterHeightOuter(int32_t aOuterHeight,
3720 CallerType aCallerType,
3721 ErrorResult& aError) {
3722 SetOuterSize(aOuterHeight, false, aCallerType, aError);
3725 CSSIntPoint nsGlobalWindowOuter::GetScreenXY(CallerType aCallerType,
3726 ErrorResult& aError) {
3727 // When resisting fingerprinting, always return (0,0)
3728 if (nsContentUtils::ResistFingerprinting(aCallerType)) {
3729 return CSSIntPoint(0, 0);
3732 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
3733 if (!treeOwnerAsWin) {
3734 aError.Throw(NS_ERROR_FAILURE);
3735 return CSSIntPoint(0, 0);
3738 int32_t x = 0, y = 0;
3739 aError = treeOwnerAsWin->GetPosition(&x, &y); // LayoutDevice px values
3741 RefPtr<nsPresContext> presContext = mDocShell->GetPresContext();
3742 if (!presContext) {
3743 return CSSIntPoint(x, y);
3746 // Find the global desktop coordinate of the top-left of the screen.
3747 // We'll use this as a "fake origin" when converting to CSS px units,
3748 // to avoid overlapping coordinates in cases such as a hi-dpi screen
3749 // placed to the right of a lo-dpi screen on Windows. (Instead, there
3750 // may be "gaps" in the resulting CSS px coordinates in some cases.)
3751 nsDeviceContext* dc = presContext->DeviceContext();
3752 nsRect screenRect;
3753 dc->GetRect(screenRect);
3754 LayoutDeviceRect screenRectDev =
3755 LayoutDevicePixel::FromAppUnits(screenRect, dc->AppUnitsPerDevPixel());
3757 DesktopToLayoutDeviceScale scale = dc->GetDesktopToDeviceScale();
3758 DesktopRect screenRectDesk = screenRectDev / scale;
3760 CSSPoint cssPt = LayoutDevicePoint(x - screenRectDev.x, y - screenRectDev.y) /
3761 presContext->CSSToDevPixelScale();
3762 cssPt.x += screenRectDesk.x;
3763 cssPt.y += screenRectDesk.y;
3765 return CSSIntPoint(NSToIntRound(cssPt.x), NSToIntRound(cssPt.y));
3768 int32_t nsGlobalWindowOuter::GetScreenXOuter(CallerType aCallerType,
3769 ErrorResult& aError) {
3770 return GetScreenXY(aCallerType, aError).x;
3773 nsRect nsGlobalWindowOuter::GetInnerScreenRect() {
3774 if (!mDocShell) {
3775 return nsRect();
3778 EnsureSizeAndPositionUpToDate();
3780 if (!mDocShell) {
3781 return nsRect();
3784 PresShell* presShell = mDocShell->GetPresShell();
3785 if (!presShell) {
3786 return nsRect();
3788 nsIFrame* rootFrame = presShell->GetRootFrame();
3789 if (!rootFrame) {
3790 return nsRect();
3793 return rootFrame->GetScreenRectInAppUnits();
3796 Maybe<CSSIntSize> nsGlobalWindowOuter::GetRDMDeviceSize(
3797 const Document& aDocument) {
3798 // RDM device size should reflect the simulated device resolution, and
3799 // be independent of any full zoom or resolution zoom applied to the
3800 // content. To get this value, we get the "unscaled" browser child size,
3801 // and divide by the full zoom. "Unscaled" in this case means unscaled
3802 // from device to screen but it has been affected (multiplied) by the
3803 // full zoom and we need to compensate for that.
3804 MOZ_RELEASE_ASSERT(NS_IsMainThread());
3806 // Bug 1576256: This does not work for cross-process subframes.
3807 const Document* topInProcessContentDoc =
3808 aDocument.GetTopLevelContentDocumentIfSameProcess();
3809 BrowsingContext* bc = topInProcessContentDoc
3810 ? topInProcessContentDoc->GetBrowsingContext()
3811 : nullptr;
3812 if (bc && bc->InRDMPane()) {
3813 nsIDocShell* docShell = topInProcessContentDoc->GetDocShell();
3814 if (docShell) {
3815 nsPresContext* presContext = docShell->GetPresContext();
3816 if (presContext) {
3817 nsCOMPtr<nsIBrowserChild> child = docShell->GetBrowserChild();
3818 if (child) {
3819 // We intentionally use GetFullZoom here instead of
3820 // GetDeviceFullZoom, because the unscaledInnerSize is based
3821 // on the full zoom and not the device full zoom (which is
3822 // rounded to result in integer device pixels).
3823 float zoom = presContext->GetFullZoom();
3824 BrowserChild* bc = static_cast<BrowserChild*>(child.get());
3825 CSSSize unscaledSize = bc->GetUnscaledInnerSize();
3826 return Some(CSSIntSize(gfx::RoundedToInt(unscaledSize / zoom)));
3831 return Nothing();
3834 float nsGlobalWindowOuter::GetMozInnerScreenXOuter(CallerType aCallerType) {
3835 // When resisting fingerprinting, always return 0.
3836 if (nsContentUtils::ResistFingerprinting(aCallerType)) {
3837 return 0.0;
3840 nsRect r = GetInnerScreenRect();
3841 return nsPresContext::AppUnitsToFloatCSSPixels(r.x);
3844 float nsGlobalWindowOuter::GetMozInnerScreenYOuter(CallerType aCallerType) {
3845 // Return 0 to prevent fingerprinting.
3846 if (nsContentUtils::ResistFingerprinting(aCallerType)) {
3847 return 0.0;
3850 nsRect r = GetInnerScreenRect();
3851 return nsPresContext::AppUnitsToFloatCSSPixels(r.y);
3854 double nsGlobalWindowOuter::GetDevicePixelRatioOuter(CallerType aCallerType) {
3855 if (NS_WARN_IF(!mDoc)) {
3856 return 1.0;
3859 RefPtr<nsPresContext> presContext = mDoc->GetPresContext();
3860 if (NS_WARN_IF(!presContext)) {
3861 // We're in an undisplayed subdocument... There's not really an awesome way
3862 // to tell what the right DPI is from here, so we try to walk up our parent
3863 // document chain to the extent that the docs can observe each other.
3864 Document* doc = mDoc;
3865 while (doc->StyleOrLayoutObservablyDependsOnParentDocumentLayout()) {
3866 doc = doc->GetInProcessParentDocument();
3867 presContext = doc->GetPresContext();
3868 if (presContext) {
3869 break;
3873 if (!presContext) {
3874 // Still nothing, oh well.
3875 return 1.0;
3879 if (nsContentUtils::ResistFingerprinting(aCallerType)) {
3880 // Spoofing the DevicePixelRatio causes blurriness in some situations
3881 // on HiDPI displays. pdf.js is a non-system caller; but it can't
3882 // expose the fingerprintable information, so we can safely disable
3883 // spoofing in this situation. It doesn't address the issue for
3884 // web-rendered content (including pdf.js instances on the web.)
3885 // In the future we hope to have a better solution to fix all HiDPI
3886 // blurriness...
3887 nsAutoCString origin;
3888 nsresult rv = this->GetPrincipal()->GetOrigin(origin);
3889 if (NS_FAILED(rv) || origin != "resource://pdf.js"_ns) {
3890 return 1.0;
3894 float overrideDPPX = presContext->GetOverrideDPPX();
3896 if (overrideDPPX > 0) {
3897 return overrideDPPX;
3900 return double(AppUnitsPerCSSPixel()) /
3901 double(presContext->AppUnitsPerDevPixel());
3904 float nsPIDOMWindowOuter::GetDevicePixelRatio(CallerType aCallerType) {
3905 return nsGlobalWindowOuter::Cast(this)->GetDevicePixelRatioOuter(aCallerType);
3908 uint64_t nsGlobalWindowOuter::GetMozPaintCountOuter() {
3909 if (!mDocShell) {
3910 return 0;
3913 PresShell* presShell = mDocShell->GetPresShell();
3914 return presShell ? presShell->GetPaintCount() : 0;
3917 void nsGlobalWindowOuter::SetScreenXOuter(int32_t aScreenX,
3918 CallerType aCallerType,
3919 ErrorResult& aError) {
3920 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
3921 if (!treeOwnerAsWin) {
3922 aError.Throw(NS_ERROR_FAILURE);
3923 return;
3926 int32_t x, y;
3927 aError = treeOwnerAsWin->GetPosition(&x, &y);
3928 if (aError.Failed()) {
3929 return;
3932 CheckSecurityLeftAndTop(&aScreenX, nullptr, aCallerType);
3933 x = CSSToDevIntPixelsForBaseWindow(aScreenX, treeOwnerAsWin);
3935 aError = treeOwnerAsWin->SetPosition(x, y);
3937 CheckForDPIChange();
3940 int32_t nsGlobalWindowOuter::GetScreenYOuter(CallerType aCallerType,
3941 ErrorResult& aError) {
3942 return GetScreenXY(aCallerType, aError).y;
3945 void nsGlobalWindowOuter::SetScreenYOuter(int32_t aScreenY,
3946 CallerType aCallerType,
3947 ErrorResult& aError) {
3948 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
3949 if (!treeOwnerAsWin) {
3950 aError.Throw(NS_ERROR_FAILURE);
3951 return;
3954 int32_t x, y;
3955 aError = treeOwnerAsWin->GetPosition(&x, &y);
3956 if (aError.Failed()) {
3957 return;
3960 CheckSecurityLeftAndTop(nullptr, &aScreenY, aCallerType);
3961 y = CSSToDevIntPixelsForBaseWindow(aScreenY, treeOwnerAsWin);
3963 aError = treeOwnerAsWin->SetPosition(x, y);
3965 CheckForDPIChange();
3968 // NOTE: Arguments to this function should have values scaled to
3969 // CSS pixels, not device pixels.
3970 void nsGlobalWindowOuter::CheckSecurityWidthAndHeight(int32_t* aWidth,
3971 int32_t* aHeight,
3972 CallerType aCallerType) {
3973 #ifdef MOZ_XUL
3974 if (aCallerType != CallerType::System) {
3975 // if attempting to resize the window, hide any open popups
3976 nsContentUtils::HidePopupsInDocument(mDoc);
3978 #endif
3980 // This one is easy. Just ensure the variable is greater than 100;
3981 if ((aWidth && *aWidth < 100) || (aHeight && *aHeight < 100)) {
3982 // Check security state for use in determing window dimensions
3984 if (aCallerType != CallerType::System) {
3985 // sec check failed
3986 if (aWidth && *aWidth < 100) {
3987 *aWidth = 100;
3989 if (aHeight && *aHeight < 100) {
3990 *aHeight = 100;
3996 // NOTE: Arguments to this function should have values in device pixels
3997 nsresult nsGlobalWindowOuter::SetDocShellWidthAndHeight(int32_t aInnerWidth,
3998 int32_t aInnerHeight) {
3999 NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE);
4001 nsCOMPtr<nsIDocShell> docShell = mDocShell;
4002 nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
4003 docShell->GetTreeOwner(getter_AddRefs(treeOwner));
4004 NS_ENSURE_TRUE(treeOwner, NS_ERROR_FAILURE);
4006 NS_ENSURE_SUCCESS(treeOwner->SizeShellTo(docShell, aInnerWidth, aInnerHeight),
4007 NS_ERROR_FAILURE);
4009 return NS_OK;
4012 // NOTE: Arguments to this function should have values in app units
4013 void nsGlobalWindowOuter::SetCSSViewportWidthAndHeight(nscoord aInnerWidth,
4014 nscoord aInnerHeight) {
4015 RefPtr<nsPresContext> presContext = mDocShell->GetPresContext();
4017 nsRect shellArea = presContext->GetVisibleArea();
4018 shellArea.SetHeight(aInnerHeight);
4019 shellArea.SetWidth(aInnerWidth);
4021 // FIXME(emilio): This doesn't seem to be ok, this doesn't reflow or
4022 // anything... Should go through PresShell::ResizeReflow.
4024 // But I don't think this can be reached by content, as we don't allow to set
4025 // inner{Width,Height}.
4026 presContext->SetVisibleArea(shellArea);
4029 // NOTE: Arguments to this function should have values scaled to
4030 // CSS pixels, not device pixels.
4031 void nsGlobalWindowOuter::CheckSecurityLeftAndTop(int32_t* aLeft, int32_t* aTop,
4032 CallerType aCallerType) {
4033 // This one is harder. We have to get the screen size and window dimensions.
4035 // Check security state for use in determing window dimensions
4037 if (aCallerType != CallerType::System) {
4038 #ifdef MOZ_XUL
4039 // if attempting to move the window, hide any open popups
4040 nsContentUtils::HidePopupsInDocument(mDoc);
4041 #endif
4043 if (nsGlobalWindowOuter* rootWindow =
4044 nsGlobalWindowOuter::Cast(GetPrivateRoot())) {
4045 rootWindow->FlushPendingNotifications(FlushType::Layout);
4048 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
4050 RefPtr<nsScreen> screen = GetScreen();
4052 if (treeOwnerAsWin && screen) {
4053 int32_t winLeft, winTop, winWidth, winHeight;
4055 // Get the window size
4056 treeOwnerAsWin->GetPositionAndSize(&winLeft, &winTop, &winWidth,
4057 &winHeight);
4059 // convert those values to CSS pixels
4060 // XXX four separate retrievals of the prescontext
4061 winLeft = DevToCSSIntPixelsForBaseWindow(winLeft, treeOwnerAsWin);
4062 winTop = DevToCSSIntPixelsForBaseWindow(winTop, treeOwnerAsWin);
4063 winWidth = DevToCSSIntPixelsForBaseWindow(winWidth, treeOwnerAsWin);
4064 winHeight = DevToCSSIntPixelsForBaseWindow(winHeight, treeOwnerAsWin);
4066 // Get the screen dimensions
4067 // XXX This should use nsIScreenManager once it's fully fleshed out.
4068 int32_t screenLeft = screen->GetAvailLeft(IgnoreErrors());
4069 int32_t screenWidth = screen->GetAvailWidth(IgnoreErrors());
4070 int32_t screenHeight = screen->GetAvailHeight(IgnoreErrors());
4071 #if defined(XP_MACOSX)
4072 /* The mac's coordinate system is different from the assumed Windows'
4073 system. It offsets by the height of the menubar so that a window
4074 placed at (0,0) will be entirely visible. Unfortunately that
4075 correction is made elsewhere (in Widget) and the meaning of
4076 the Avail... coordinates is overloaded. Here we allow a window
4077 to be placed at (0,0) because it does make sense to do so.
4079 int32_t screenTop = screen->GetTop(IgnoreErrors());
4080 #else
4081 int32_t screenTop = screen->GetAvailTop(IgnoreErrors());
4082 #endif
4084 if (aLeft) {
4085 if (screenLeft + screenWidth < *aLeft + winWidth)
4086 *aLeft = screenLeft + screenWidth - winWidth;
4087 if (screenLeft > *aLeft) *aLeft = screenLeft;
4089 if (aTop) {
4090 if (screenTop + screenHeight < *aTop + winHeight)
4091 *aTop = screenTop + screenHeight - winHeight;
4092 if (screenTop > *aTop) *aTop = screenTop;
4094 } else {
4095 if (aLeft) *aLeft = 0;
4096 if (aTop) *aTop = 0;
4101 int32_t nsGlobalWindowOuter::GetScrollBoundaryOuter(Side aSide) {
4102 FlushPendingNotifications(FlushType::Layout);
4103 if (nsIScrollableFrame* sf = GetScrollFrame()) {
4104 return nsPresContext::AppUnitsToIntCSSPixels(
4105 sf->GetScrollRange().Edge(aSide));
4107 return 0;
4110 CSSPoint nsGlobalWindowOuter::GetScrollXY(bool aDoFlush) {
4111 if (aDoFlush) {
4112 FlushPendingNotifications(FlushType::Layout);
4113 } else {
4114 EnsureSizeAndPositionUpToDate();
4117 nsIScrollableFrame* sf = GetScrollFrame();
4118 if (!sf) {
4119 return CSSIntPoint(0, 0);
4122 nsPoint scrollPos = sf->GetScrollPosition();
4123 if (scrollPos != nsPoint(0, 0) && !aDoFlush) {
4124 // Oh, well. This is the expensive case -- the window is scrolled and we
4125 // didn't actually flush yet. Repeat, but with a flush, since the content
4126 // may get shorter and hence our scroll position may decrease.
4127 return GetScrollXY(true);
4130 return CSSPoint::FromAppUnits(scrollPos);
4133 double nsGlobalWindowOuter::GetScrollXOuter() { return GetScrollXY(false).x; }
4135 double nsGlobalWindowOuter::GetScrollYOuter() { return GetScrollXY(false).y; }
4137 uint32_t nsGlobalWindowOuter::Length() {
4138 BrowsingContext* bc = GetBrowsingContext();
4139 return bc ? bc->Children().Length() : 0;
4142 Nullable<WindowProxyHolder> nsGlobalWindowOuter::GetTopOuter() {
4143 BrowsingContext* bc = GetBrowsingContext();
4144 return bc ? bc->GetTop(IgnoreErrors()) : nullptr;
4147 already_AddRefed<BrowsingContext> nsGlobalWindowOuter::GetChildWindow(
4148 const nsAString& aName) {
4149 NS_ENSURE_TRUE(mBrowsingContext, nullptr);
4151 return do_AddRef(
4152 mBrowsingContext->FindChildWithName(aName, *mBrowsingContext));
4155 bool nsGlobalWindowOuter::DispatchCustomEvent(
4156 const nsAString& aEventName, ChromeOnlyDispatch aChromeOnlyDispatch) {
4157 bool defaultActionEnabled = true;
4159 if (aChromeOnlyDispatch == ChromeOnlyDispatch::eYes) {
4160 nsContentUtils::DispatchEventOnlyToChrome(
4161 mDoc, ToSupports(this), aEventName, CanBubble::eYes, Cancelable::eYes,
4162 &defaultActionEnabled);
4163 } else {
4164 nsContentUtils::DispatchTrustedEvent(mDoc, ToSupports(this), aEventName,
4165 CanBubble::eYes, Cancelable::eYes,
4166 &defaultActionEnabled);
4169 return defaultActionEnabled;
4172 bool nsGlobalWindowOuter::DispatchResizeEvent(const CSSIntSize& aSize) {
4173 ErrorResult res;
4174 RefPtr<Event> domEvent =
4175 mDoc->CreateEvent(u"CustomEvent"_ns, CallerType::System, res);
4176 if (res.Failed()) {
4177 return false;
4180 // We don't init the AutoJSAPI with ourselves because we don't want it
4181 // reporting errors to our onerror handlers.
4182 AutoJSAPI jsapi;
4183 jsapi.Init();
4184 JSContext* cx = jsapi.cx();
4185 JSAutoRealm ar(cx, GetWrapperPreserveColor());
4187 DOMWindowResizeEventDetail detail;
4188 detail.mWidth = aSize.width;
4189 detail.mHeight = aSize.height;
4190 JS::Rooted<JS::Value> detailValue(cx);
4191 if (!ToJSValue(cx, detail, &detailValue)) {
4192 return false;
4195 CustomEvent* customEvent = static_cast<CustomEvent*>(domEvent.get());
4196 customEvent->InitCustomEvent(cx, u"DOMWindowResize"_ns,
4197 /* aCanBubble = */ true,
4198 /* aCancelable = */ true, detailValue);
4200 domEvent->SetTrusted(true);
4201 domEvent->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true;
4203 nsCOMPtr<EventTarget> target = this;
4204 domEvent->SetTarget(target);
4206 return target->DispatchEvent(*domEvent, CallerType::System, IgnoreErrors());
4209 bool nsGlobalWindowOuter::WindowExists(const nsAString& aName,
4210 bool aForceNoOpener,
4211 bool aLookForCallerOnJSStack) {
4212 MOZ_ASSERT(mDocShell, "Must have docshell");
4214 if (aForceNoOpener) {
4215 return aName.LowerCaseEqualsLiteral("_self") ||
4216 aName.LowerCaseEqualsLiteral("_top") ||
4217 aName.LowerCaseEqualsLiteral("_parent");
4220 return !!mBrowsingContext->FindWithName(aName, aLookForCallerOnJSStack);
4223 already_AddRefed<nsIWidget> nsGlobalWindowOuter::GetMainWidget() {
4224 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
4226 nsCOMPtr<nsIWidget> widget;
4228 if (treeOwnerAsWin) {
4229 treeOwnerAsWin->GetMainWidget(getter_AddRefs(widget));
4232 return widget.forget();
4235 nsIWidget* nsGlobalWindowOuter::GetNearestWidget() const {
4236 nsIDocShell* docShell = GetDocShell();
4237 NS_ENSURE_TRUE(docShell, nullptr);
4238 PresShell* presShell = docShell->GetPresShell();
4239 NS_ENSURE_TRUE(presShell, nullptr);
4240 nsIFrame* rootFrame = presShell->GetRootFrame();
4241 NS_ENSURE_TRUE(rootFrame, nullptr);
4242 return rootFrame->GetView()->GetNearestWidget(nullptr);
4245 void nsGlobalWindowOuter::SetFullscreenOuter(bool aFullscreen,
4246 mozilla::ErrorResult& aError) {
4247 aError =
4248 SetFullscreenInternal(FullscreenReason::ForFullscreenMode, aFullscreen);
4251 nsresult nsGlobalWindowOuter::SetFullScreen(bool aFullscreen) {
4252 return SetFullscreenInternal(FullscreenReason::ForFullscreenMode,
4253 aFullscreen);
4256 static void FinishDOMFullscreenChange(Document* aDoc, bool aInDOMFullscreen) {
4257 if (aInDOMFullscreen) {
4258 // Ask the document to handle any pending DOM fullscreen change.
4259 if (!Document::HandlePendingFullscreenRequests(aDoc)) {
4260 // If we don't end up having anything in fullscreen,
4261 // async request exiting fullscreen.
4262 Document::AsyncExitFullscreen(aDoc);
4264 } else {
4265 // If the window is leaving fullscreen state, also ask the document
4266 // to exit from DOM Fullscreen.
4267 Document::ExitFullscreenInDocTree(aDoc);
4271 struct FullscreenTransitionDuration {
4272 // The unit of the durations is millisecond
4273 uint16_t mFadeIn = 0;
4274 uint16_t mFadeOut = 0;
4275 bool IsSuppressed() const { return mFadeIn == 0 && mFadeOut == 0; }
4278 static void GetFullscreenTransitionDuration(
4279 bool aEnterFullscreen, FullscreenTransitionDuration* aDuration) {
4280 const char* pref = aEnterFullscreen
4281 ? "full-screen-api.transition-duration.enter"
4282 : "full-screen-api.transition-duration.leave";
4283 nsAutoCString prefValue;
4284 Preferences::GetCString(pref, prefValue);
4285 if (!prefValue.IsEmpty()) {
4286 sscanf(prefValue.get(), "%hu%hu", &aDuration->mFadeIn,
4287 &aDuration->mFadeOut);
4291 class FullscreenTransitionTask : public Runnable {
4292 public:
4293 FullscreenTransitionTask(const FullscreenTransitionDuration& aDuration,
4294 nsGlobalWindowOuter* aWindow, bool aFullscreen,
4295 nsIWidget* aWidget, nsIScreen* aScreen,
4296 nsISupports* aTransitionData)
4297 : mozilla::Runnable("FullscreenTransitionTask"),
4298 mWindow(aWindow),
4299 mWidget(aWidget),
4300 mScreen(aScreen),
4301 mTransitionData(aTransitionData),
4302 mDuration(aDuration),
4303 mStage(eBeforeToggle),
4304 mFullscreen(aFullscreen) {}
4306 NS_IMETHOD Run() override;
4308 private:
4309 ~FullscreenTransitionTask() override = default;
4312 * The flow of fullscreen transition:
4314 * parent process | child process
4315 * ----------------------------------------------------------------
4317 * | request/exit fullscreen
4318 * <-----|
4319 * BeforeToggle stage |
4321 * ToggleFullscreen stage *1 |----->
4322 * | HandleFullscreenRequests
4324 * <-----| MozAfterPaint event
4325 * AfterToggle stage *2 |
4327 * End stage |
4329 * Note we also start a timer at *1 so that if we don't get MozAfterPaint
4330 * from the child process in time, we continue going to *2.
4332 enum Stage {
4333 // BeforeToggle stage happens before we enter or leave fullscreen
4334 // state. In this stage, the task triggers the pre-toggle fullscreen
4335 // transition on the widget.
4336 eBeforeToggle,
4337 // ToggleFullscreen stage actually executes the fullscreen toggle,
4338 // and wait for the next paint on the content to continue.
4339 eToggleFullscreen,
4340 // AfterToggle stage happens after we toggle the fullscreen state.
4341 // In this stage, the task triggers the post-toggle fullscreen
4342 // transition on the widget.
4343 eAfterToggle,
4344 // End stage is triggered after the final transition finishes.
4345 eEnd
4348 class Observer final : public nsIObserver {
4349 public:
4350 NS_DECL_ISUPPORTS
4351 NS_DECL_NSIOBSERVER
4353 explicit Observer(FullscreenTransitionTask* aTask) : mTask(aTask) {}
4355 private:
4356 ~Observer() = default;
4358 RefPtr<FullscreenTransitionTask> mTask;
4361 static const char* const kPaintedTopic;
4363 RefPtr<nsGlobalWindowOuter> mWindow;
4364 nsCOMPtr<nsIWidget> mWidget;
4365 nsCOMPtr<nsIScreen> mScreen;
4366 nsCOMPtr<nsITimer> mTimer;
4367 nsCOMPtr<nsISupports> mTransitionData;
4369 TimeStamp mFullscreenChangeStartTime;
4370 FullscreenTransitionDuration mDuration;
4371 Stage mStage;
4372 bool mFullscreen;
4375 const char* const FullscreenTransitionTask::kPaintedTopic =
4376 "fullscreen-painted";
4378 NS_IMETHODIMP
4379 FullscreenTransitionTask::Run() {
4380 Stage stage = mStage;
4381 mStage = Stage(mStage + 1);
4382 if (MOZ_UNLIKELY(mWidget->Destroyed())) {
4383 // If the widget has been destroyed before we get here, don't try to
4384 // do anything more. Just let it go and release ourselves.
4385 NS_WARNING("The widget to fullscreen has been destroyed");
4386 return NS_OK;
4388 if (stage == eBeforeToggle) {
4389 PROFILER_MARKER_UNTYPED("Fullscreen transition start", DOM);
4390 mWidget->PerformFullscreenTransition(nsIWidget::eBeforeFullscreenToggle,
4391 mDuration.mFadeIn, mTransitionData,
4392 this);
4393 } else if (stage == eToggleFullscreen) {
4394 PROFILER_MARKER_UNTYPED("Fullscreen toggle start", DOM);
4395 mFullscreenChangeStartTime = TimeStamp::Now();
4396 if (MOZ_UNLIKELY(mWindow->mFullscreen != mFullscreen)) {
4397 // This could happen in theory if several fullscreen requests in
4398 // different direction happen continuously in a short time. We
4399 // need to ensure the fullscreen state matches our target here,
4400 // otherwise the widget would change the window state as if we
4401 // toggle for Fullscreen Mode instead of Fullscreen API.
4402 NS_WARNING("The fullscreen state of the window does not match");
4403 mWindow->mFullscreen = mFullscreen;
4405 // Toggle the fullscreen state on the widget
4406 if (!mWindow->SetWidgetFullscreen(FullscreenReason::ForFullscreenAPI,
4407 mFullscreen, mWidget, mScreen)) {
4408 // Fail to setup the widget, call FinishFullscreenChange to
4409 // complete fullscreen change directly.
4410 mWindow->FinishFullscreenChange(mFullscreen);
4412 // Set observer for the next content paint.
4413 nsCOMPtr<nsIObserver> observer = new Observer(this);
4414 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
4415 obs->AddObserver(observer, kPaintedTopic, false);
4416 // There are several edge cases where we may never get the paint
4417 // notification, including:
4418 // 1. the window/tab is closed before the next paint;
4419 // 2. the user has switched to another tab before we get here.
4420 // Completely fixing those cases seems to be tricky, and since they
4421 // should rarely happen, it probably isn't worth to fix. Hence we
4422 // simply add a timeout here to ensure we never hang forever.
4423 // In addition, if the page is complicated or the machine is less
4424 // powerful, layout could take a long time, in which case, staying
4425 // in black screen for that long could hurt user experience even
4426 // more than exposing an intermediate state.
4427 uint32_t timeout =
4428 Preferences::GetUint("full-screen-api.transition.timeout", 1000);
4429 NS_NewTimerWithObserver(getter_AddRefs(mTimer), observer, timeout,
4430 nsITimer::TYPE_ONE_SHOT);
4431 } else if (stage == eAfterToggle) {
4432 Telemetry::AccumulateTimeDelta(Telemetry::FULLSCREEN_TRANSITION_BLACK_MS,
4433 mFullscreenChangeStartTime);
4434 mWidget->PerformFullscreenTransition(nsIWidget::eAfterFullscreenToggle,
4435 mDuration.mFadeOut, mTransitionData,
4436 this);
4437 } else if (stage == eEnd) {
4438 PROFILER_MARKER_UNTYPED("Fullscreen transition end", DOM);
4439 mWidget->CleanupFullscreenTransition();
4441 return NS_OK;
4444 NS_IMPL_ISUPPORTS(FullscreenTransitionTask::Observer, nsIObserver)
4446 NS_IMETHODIMP
4447 FullscreenTransitionTask::Observer::Observe(nsISupports* aSubject,
4448 const char* aTopic,
4449 const char16_t* aData) {
4450 bool shouldContinue = false;
4451 if (strcmp(aTopic, FullscreenTransitionTask::kPaintedTopic) == 0) {
4452 nsCOMPtr<nsPIDOMWindowInner> win(do_QueryInterface(aSubject));
4453 nsCOMPtr<nsIWidget> widget =
4454 win ? nsGlobalWindowInner::Cast(win)->GetMainWidget() : nullptr;
4455 if (widget == mTask->mWidget) {
4456 // The paint notification arrives first. Cancel the timer.
4457 mTask->mTimer->Cancel();
4458 shouldContinue = true;
4459 PROFILER_MARKER_UNTYPED("Fullscreen toggle end", DOM);
4461 } else {
4462 #ifdef DEBUG
4463 MOZ_ASSERT(strcmp(aTopic, NS_TIMER_CALLBACK_TOPIC) == 0,
4464 "Should only get fullscreen-painted or timer-callback");
4465 nsCOMPtr<nsITimer> timer(do_QueryInterface(aSubject));
4466 MOZ_ASSERT(timer && timer == mTask->mTimer,
4467 "Should only trigger this with the timer the task created");
4468 #endif
4469 shouldContinue = true;
4470 PROFILER_MARKER_UNTYPED("Fullscreen toggle timeout", DOM);
4472 if (shouldContinue) {
4473 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
4474 obs->RemoveObserver(this, kPaintedTopic);
4475 mTask->mTimer = nullptr;
4476 mTask->Run();
4478 return NS_OK;
4481 static bool MakeWidgetFullscreen(nsGlobalWindowOuter* aWindow,
4482 FullscreenReason aReason, bool aFullscreen) {
4483 nsCOMPtr<nsIWidget> widget = aWindow->GetMainWidget();
4484 if (!widget) {
4485 return false;
4488 FullscreenTransitionDuration duration;
4489 bool performTransition = false;
4490 nsCOMPtr<nsISupports> transitionData;
4491 if (aReason == FullscreenReason::ForFullscreenAPI) {
4492 GetFullscreenTransitionDuration(aFullscreen, &duration);
4493 if (!duration.IsSuppressed()) {
4494 performTransition = widget->PrepareForFullscreenTransition(
4495 getter_AddRefs(transitionData));
4498 // We pass nullptr as the screen to SetWidgetFullscreen
4499 // and FullscreenTransitionTask, as we do not wish to override
4500 // the default screen selection behavior. The screen containing
4501 // most of the widget will be selected.
4502 if (!performTransition) {
4503 return aWindow->SetWidgetFullscreen(aReason, aFullscreen, widget, nullptr);
4505 nsCOMPtr<nsIRunnable> task = new FullscreenTransitionTask(
4506 duration, aWindow, aFullscreen, widget, nullptr, transitionData);
4507 task->Run();
4508 return true;
4511 nsresult nsGlobalWindowOuter::SetFullscreenInternal(FullscreenReason aReason,
4512 bool aFullscreen) {
4513 MOZ_ASSERT(nsContentUtils::IsSafeToRunScript(),
4514 "Requires safe to run script as it "
4515 "may call FinishDOMFullscreenChange");
4517 NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE);
4519 MOZ_ASSERT(
4520 aReason != FullscreenReason::ForForceExitFullscreen || !aFullscreen,
4521 "FullscreenReason::ForForceExitFullscreen can "
4522 "only be used with exiting fullscreen");
4524 // Only chrome can change our fullscreen mode. Otherwise, the state
4525 // can only be changed for DOM fullscreen.
4526 if (aReason == FullscreenReason::ForFullscreenMode &&
4527 !nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
4528 return NS_OK;
4531 // SetFullscreen needs to be called on the root window, so get that
4532 // via the DocShell tree, and if we are not already the root,
4533 // call SetFullscreen on that window instead.
4534 nsCOMPtr<nsIDocShellTreeItem> rootItem;
4535 mDocShell->GetInProcessRootTreeItem(getter_AddRefs(rootItem));
4536 nsCOMPtr<nsPIDOMWindowOuter> window =
4537 rootItem ? rootItem->GetWindow() : nullptr;
4538 if (!window) return NS_ERROR_FAILURE;
4539 if (rootItem != mDocShell)
4540 return window->SetFullscreenInternal(aReason, aFullscreen);
4542 // make sure we don't try to set full screen on a non-chrome window,
4543 // which might happen in embedding world
4544 if (mDocShell->ItemType() != nsIDocShellTreeItem::typeChrome)
4545 return NS_ERROR_FAILURE;
4547 // If we are already in full screen mode, just return.
4548 if (mFullscreen == aFullscreen) {
4549 return NS_OK;
4552 // Note that although entering DOM fullscreen could also cause
4553 // consequential calls to this method, those calls will be skipped
4554 // at the condition above.
4555 if (aReason == FullscreenReason::ForFullscreenMode) {
4556 if (!aFullscreen && !mFullscreenMode) {
4557 // If we are exiting fullscreen mode, but we actually didn't
4558 // entered fullscreen mode, the fullscreen state was only for
4559 // the Fullscreen API. Change the reason here so that we can
4560 // perform transition for it.
4561 aReason = FullscreenReason::ForFullscreenAPI;
4562 } else {
4563 mFullscreenMode = aFullscreen;
4565 } else {
4566 // If we are exiting from DOM fullscreen while we initially make
4567 // the window fullscreen because of fullscreen mode, don't restore
4568 // the window. But we still need to exit the DOM fullscreen state.
4569 if (!aFullscreen && mFullscreenMode) {
4570 FinishDOMFullscreenChange(mDoc, false);
4571 return NS_OK;
4575 // Prevent chrome documents which are still loading from resizing
4576 // the window after we set fullscreen mode.
4577 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
4578 nsCOMPtr<nsIAppWindow> appWin(do_GetInterface(treeOwnerAsWin));
4579 if (aFullscreen && appWin) {
4580 appWin->SetIntrinsicallySized(false);
4583 // Set this before so if widget sends an event indicating its
4584 // gone full screen, the state trap above works.
4585 mFullscreen = aFullscreen;
4587 // Sometimes we don't want the top-level widget to actually go fullscreen:
4588 // - in the B2G desktop client, we don't want the emulated screen dimensions
4589 // to appear to increase when entering fullscreen mode; we just want the
4590 // content to fill the entire client area of the emulator window.
4591 // - in FxR Desktop, we don't want fullscreen to take over the monitor, but
4592 // instead we want fullscreen to fill the FxR window in the the headset.
4593 if (!Preferences::GetBool("full-screen-api.ignore-widgets", false) &&
4594 !mForceFullScreenInWidget) {
4595 if (MakeWidgetFullscreen(this, aReason, aFullscreen)) {
4596 // The rest of code for switching fullscreen is in nsGlobalWindowOuter::
4597 // FinishFullscreenChange() which will be called after sizemodechange
4598 // event is dispatched.
4599 return NS_OK;
4603 #if defined(NIGHTLY_BUILD) && defined(XP_WIN)
4604 if (FxRWindowManager::GetInstance()->IsFxRWindow(mWindowID)) {
4605 mozilla::gfx::VRShMem shmem(nullptr, true /*aRequiresMutex*/);
4606 shmem.SendFullscreenState(mWindowID, aFullscreen);
4608 #endif // NIGHTLY_BUILD && XP_WIN
4609 FinishFullscreenChange(aFullscreen);
4610 return NS_OK;
4613 // Support a per-window, dynamic equivalent of enabling
4614 // full-screen-api.ignore-widgets
4615 void nsGlobalWindowOuter::ForceFullScreenInWidget() {
4616 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
4618 mForceFullScreenInWidget = true;
4621 bool nsGlobalWindowOuter::SetWidgetFullscreen(FullscreenReason aReason,
4622 bool aIsFullscreen,
4623 nsIWidget* aWidget,
4624 nsIScreen* aScreen) {
4625 MOZ_ASSERT(this == GetInProcessTopInternal(),
4626 "Only topmost window should call this");
4627 MOZ_ASSERT(!GetFrameElementInternal(), "Content window should not call this");
4628 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
4630 if (!NS_WARN_IF(!IsChromeWindow())) {
4631 if (!NS_WARN_IF(mChromeFields.mFullscreenPresShell)) {
4632 if (PresShell* presShell = mDocShell->GetPresShell()) {
4633 if (nsRefreshDriver* rd = presShell->GetRefreshDriver()) {
4634 mChromeFields.mFullscreenPresShell = do_GetWeakReference(presShell);
4635 MOZ_ASSERT(mChromeFields.mFullscreenPresShell);
4636 rd->SetIsResizeSuppressed();
4637 rd->Freeze();
4642 nsresult rv =
4643 aReason == FullscreenReason::ForFullscreenMode
4645 // If we enter fullscreen for fullscreen mode, we want
4646 // the native system behavior.
4647 aWidget->MakeFullScreenWithNativeTransition(aIsFullscreen, aScreen)
4648 : aWidget->MakeFullScreen(aIsFullscreen, aScreen);
4649 return NS_SUCCEEDED(rv);
4652 /* virtual */
4653 void nsGlobalWindowOuter::FullscreenWillChange(bool aIsFullscreen) {
4654 if (aIsFullscreen) {
4655 DispatchCustomEvent(u"willenterfullscreen"_ns, ChromeOnlyDispatch::eYes);
4656 } else {
4657 DispatchCustomEvent(u"willexitfullscreen"_ns, ChromeOnlyDispatch::eYes);
4661 /* virtual */
4662 void nsGlobalWindowOuter::FinishFullscreenChange(bool aIsFullscreen) {
4663 if (aIsFullscreen != mFullscreen) {
4664 NS_WARNING("Failed to toggle fullscreen state of the widget");
4665 // We failed to make the widget enter fullscreen.
4666 // Stop further changes and restore the state.
4667 if (!aIsFullscreen) {
4668 mFullscreen = false;
4669 mFullscreenMode = false;
4670 } else {
4671 #ifndef XP_MACOSX
4672 MOZ_ASSERT_UNREACHABLE("Failed to exit fullscreen?");
4673 #endif
4674 mFullscreen = true;
4675 // At least on macOS, we may reach here because the system fails
4676 // to let us exit the system fullscreen mode. In that case, we may
4677 // have already exited DOM fullscreen before, so set fullscreen
4678 // mode to true here so that it has a saner state.
4679 mFullscreenMode = true;
4681 return;
4684 // Note that we must call this to toggle the DOM fullscreen state
4685 // of the document before dispatching the "fullscreen" event, so
4686 // that the chrome can distinguish between browser fullscreen mode
4687 // and DOM fullscreen.
4688 FinishDOMFullscreenChange(mDoc, mFullscreen);
4690 // dispatch a "fullscreen" DOM event so that XUL apps can
4691 // respond visually if we are kicked into full screen mode
4692 DispatchCustomEvent(u"fullscreen"_ns, ChromeOnlyDispatch::eYes);
4694 if (!NS_WARN_IF(!IsChromeWindow())) {
4695 if (RefPtr<PresShell> presShell =
4696 do_QueryReferent(mChromeFields.mFullscreenPresShell)) {
4697 if (nsRefreshDriver* rd = presShell->GetRefreshDriver()) {
4698 rd->Thaw();
4700 mChromeFields.mFullscreenPresShell = nullptr;
4705 /* virtual */
4706 void nsGlobalWindowOuter::MacFullscreenMenubarOverlapChanged(
4707 mozilla::DesktopCoord aOverlapAmount) {
4708 ErrorResult res;
4709 RefPtr<Event> domEvent =
4710 mDoc->CreateEvent(u"CustomEvent"_ns, CallerType::System, res);
4711 if (res.Failed()) {
4712 return;
4715 AutoJSAPI jsapi;
4716 jsapi.Init();
4717 JSContext* cx = jsapi.cx();
4718 JSAutoRealm ar(cx, GetWrapperPreserveColor());
4720 JS::Rooted<JS::Value> detailValue(cx);
4721 if (!ToJSValue(cx, aOverlapAmount, &detailValue)) {
4722 return;
4725 CustomEvent* customEvent = static_cast<CustomEvent*>(domEvent.get());
4726 customEvent->InitCustomEvent(cx, u"MacFullscreenMenubarRevealUpdate"_ns,
4727 /* aCanBubble = */ true,
4728 /* aCancelable = */ true, detailValue);
4729 domEvent->SetTrusted(true);
4730 domEvent->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true;
4732 nsCOMPtr<EventTarget> target = this;
4733 domEvent->SetTarget(target);
4735 target->DispatchEvent(*domEvent, CallerType::System, IgnoreErrors());
4738 bool nsGlobalWindowOuter::Fullscreen() const {
4739 NS_ENSURE_TRUE(mDocShell, mFullscreen);
4741 // Get the fullscreen value of the root window, to always have the value
4742 // accurate, even when called from content.
4743 nsCOMPtr<nsIDocShellTreeItem> rootItem;
4744 mDocShell->GetInProcessRootTreeItem(getter_AddRefs(rootItem));
4745 if (rootItem == mDocShell) {
4746 if (!XRE_IsContentProcess()) {
4747 // We are the root window. Return our internal value.
4748 return mFullscreen;
4750 if (nsCOMPtr<nsIWidget> widget = GetNearestWidget()) {
4751 // We are in content process, figure out the value from
4752 // the sizemode of the puppet widget.
4753 return widget->SizeMode() == nsSizeMode_Fullscreen;
4755 return false;
4758 nsCOMPtr<nsPIDOMWindowOuter> window = rootItem->GetWindow();
4759 NS_ENSURE_TRUE(window, mFullscreen);
4761 return nsGlobalWindowOuter::Cast(window)->Fullscreen();
4764 bool nsGlobalWindowOuter::GetFullscreenOuter() { return Fullscreen(); }
4766 bool nsGlobalWindowOuter::GetFullScreen() {
4767 FORWARD_TO_INNER(GetFullScreen, (), false);
4770 void nsGlobalWindowOuter::EnsureReflowFlushAndPaint() {
4771 NS_ASSERTION(mDocShell,
4772 "EnsureReflowFlushAndPaint() called with no "
4773 "docshell!");
4775 if (!mDocShell) return;
4777 RefPtr<PresShell> presShell = mDocShell->GetPresShell();
4778 if (!presShell) {
4779 return;
4782 // Flush pending reflows.
4783 if (mDoc) {
4784 mDoc->FlushPendingNotifications(FlushType::Layout);
4787 // Unsuppress painting.
4788 presShell->UnsuppressPainting();
4791 // static
4792 void nsGlobalWindowOuter::MakeMessageWithPrincipal(
4793 nsAString& aOutMessage, nsIPrincipal* aSubjectPrincipal, bool aUseHostPort,
4794 const char* aNullMessage, const char* aContentMessage,
4795 const char* aFallbackMessage) {
4796 MOZ_ASSERT(aSubjectPrincipal);
4798 aOutMessage.Truncate();
4800 // Try to get a host from the running principal -- this will do the
4801 // right thing for javascript: and data: documents.
4803 nsAutoCString contentDesc;
4805 if (aSubjectPrincipal->GetIsNullPrincipal()) {
4806 nsContentUtils::GetLocalizedString(
4807 nsContentUtils::eCOMMON_DIALOG_PROPERTIES, aNullMessage, aOutMessage);
4808 } else {
4809 auto* addonPolicy = BasePrincipal::Cast(aSubjectPrincipal)->AddonPolicy();
4810 if (addonPolicy) {
4811 nsContentUtils::FormatLocalizedString(
4812 aOutMessage, nsContentUtils::eCOMMON_DIALOG_PROPERTIES,
4813 aContentMessage, addonPolicy->Name());
4814 } else {
4815 nsresult rv = NS_ERROR_FAILURE;
4816 if (aUseHostPort) {
4817 nsCOMPtr<nsIURI> uri = aSubjectPrincipal->GetURI();
4818 if (uri) {
4819 rv = uri->GetDisplayHostPort(contentDesc);
4822 if (!aUseHostPort || NS_FAILED(rv)) {
4823 rv = aSubjectPrincipal->GetExposablePrePath(contentDesc);
4825 if (NS_SUCCEEDED(rv) && !contentDesc.IsEmpty()) {
4826 NS_ConvertUTF8toUTF16 ucsPrePath(contentDesc);
4827 nsContentUtils::FormatLocalizedString(
4828 aOutMessage, nsContentUtils::eCOMMON_DIALOG_PROPERTIES,
4829 aContentMessage, ucsPrePath);
4834 if (aOutMessage.IsEmpty()) {
4835 // We didn't find a host so use the generic heading
4836 nsContentUtils::GetLocalizedString(
4837 nsContentUtils::eCOMMON_DIALOG_PROPERTIES, aFallbackMessage,
4838 aOutMessage);
4841 // Just in case
4842 if (aOutMessage.IsEmpty()) {
4843 NS_WARNING(
4844 "could not get ScriptDlgGenericHeading string from string bundle");
4845 aOutMessage.AssignLiteral("[Script]");
4849 bool nsGlobalWindowOuter::CanMoveResizeWindows(CallerType aCallerType) {
4850 // When called from chrome, we can avoid the following checks.
4851 if (aCallerType != CallerType::System) {
4852 // Don't allow scripts to move or resize windows that were not opened by a
4853 // script.
4854 if (!mBrowsingContext->HadOriginalOpener()) {
4855 return false;
4858 if (!CanSetProperty("dom.disable_window_move_resize")) {
4859 return false;
4862 // Ignore the request if we have more than one tab in the window.
4863 if (XRE_IsContentProcess()) {
4864 nsCOMPtr<nsIDocShell> docShell = GetDocShell();
4865 if (docShell) {
4866 nsCOMPtr<nsIBrowserChild> child = docShell->GetBrowserChild();
4867 bool hasSiblings = true;
4868 if (child && NS_SUCCEEDED(child->GetHasSiblings(&hasSiblings)) &&
4869 hasSiblings) {
4870 return false;
4873 } else {
4874 nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner();
4875 uint32_t itemCount = 0;
4876 if (treeOwner && NS_SUCCEEDED(treeOwner->GetTabCount(&itemCount)) &&
4877 itemCount > 1) {
4878 return false;
4883 if (mDocShell) {
4884 bool allow;
4885 nsresult rv = mDocShell->GetAllowWindowControl(&allow);
4886 if (NS_SUCCEEDED(rv) && !allow) return false;
4889 if (nsGlobalWindowInner::sMouseDown &&
4890 !nsGlobalWindowInner::sDragServiceDisabled) {
4891 nsCOMPtr<nsIDragService> ds =
4892 do_GetService("@mozilla.org/widget/dragservice;1");
4893 if (ds) {
4894 nsGlobalWindowInner::sDragServiceDisabled = true;
4895 ds->Suppress();
4898 return true;
4901 bool nsGlobalWindowOuter::AlertOrConfirm(bool aAlert, const nsAString& aMessage,
4902 nsIPrincipal& aSubjectPrincipal,
4903 ErrorResult& aError) {
4904 // XXX This method is very similar to nsGlobalWindowOuter::Prompt, make
4905 // sure any modifications here don't need to happen over there!
4906 if (!AreDialogsEnabled()) {
4907 // Just silently return. In the case of alert(), the return value is
4908 // ignored. In the case of confirm(), returning false is the same thing as
4909 // would happen if the user cancels.
4910 return false;
4913 // Reset popup state while opening a modal dialog, and firing events
4914 // about the dialog, to prevent the current state from being active
4915 // the whole time a modal dialog is open.
4916 AutoPopupStatePusher popupStatePusher(PopupBlocker::openAbused, true);
4918 // Before bringing up the window, unsuppress painting and flush
4919 // pending reflows.
4920 EnsureReflowFlushAndPaint();
4922 nsAutoString title;
4923 MakeMessageWithPrincipal(title, &aSubjectPrincipal, false,
4924 "ScriptDlgNullPrincipalHeading", "ScriptDlgHeading",
4925 "ScriptDlgGenericHeading");
4927 // Remove non-terminating null characters from the
4928 // string. See bug #310037.
4929 nsAutoString final;
4930 nsContentUtils::StripNullChars(aMessage, final);
4931 nsContentUtils::PlatformToDOMLineBreaks(final);
4933 nsresult rv;
4934 nsCOMPtr<nsIPromptFactory> promptFac =
4935 do_GetService("@mozilla.org/prompter;1", &rv);
4936 if (NS_FAILED(rv)) {
4937 aError.Throw(rv);
4938 return false;
4941 nsCOMPtr<nsIPrompt> prompt;
4942 aError =
4943 promptFac->GetPrompt(this, NS_GET_IID(nsIPrompt), getter_AddRefs(prompt));
4944 if (aError.Failed()) {
4945 return false;
4948 // Always allow content modal prompts for alert and confirm.
4949 if (nsCOMPtr<nsIWritablePropertyBag2> promptBag = do_QueryInterface(prompt)) {
4950 promptBag->SetPropertyAsUint32(u"modalType"_ns,
4951 nsIPrompt::MODAL_TYPE_CONTENT);
4954 bool result = false;
4955 nsAutoSyncOperation sync(mDoc, SyncOperationBehavior::eSuspendInput);
4956 if (ShouldPromptToBlockDialogs()) {
4957 bool disallowDialog = false;
4958 nsAutoString label;
4959 MakeMessageWithPrincipal(
4960 label, &aSubjectPrincipal, true, "ScriptDialogLabelNullPrincipal",
4961 "ScriptDialogLabelContentPrincipal", "ScriptDialogLabelNullPrincipal");
4963 aError = aAlert
4964 ? prompt->AlertCheck(title.get(), final.get(), label.get(),
4965 &disallowDialog)
4966 : prompt->ConfirmCheck(title.get(), final.get(), label.get(),
4967 &disallowDialog, &result);
4969 if (disallowDialog) DisableDialogs();
4970 } else {
4971 aError = aAlert ? prompt->Alert(title.get(), final.get())
4972 : prompt->Confirm(title.get(), final.get(), &result);
4975 return result;
4978 void nsGlobalWindowOuter::AlertOuter(const nsAString& aMessage,
4979 nsIPrincipal& aSubjectPrincipal,
4980 ErrorResult& aError) {
4981 AlertOrConfirm(/* aAlert = */ true, aMessage, aSubjectPrincipal, aError);
4984 bool nsGlobalWindowOuter::ConfirmOuter(const nsAString& aMessage,
4985 nsIPrincipal& aSubjectPrincipal,
4986 ErrorResult& aError) {
4987 return AlertOrConfirm(/* aAlert = */ false, aMessage, aSubjectPrincipal,
4988 aError);
4991 void nsGlobalWindowOuter::PromptOuter(const nsAString& aMessage,
4992 const nsAString& aInitial,
4993 nsAString& aReturn,
4994 nsIPrincipal& aSubjectPrincipal,
4995 ErrorResult& aError) {
4996 // XXX This method is very similar to nsGlobalWindowOuter::AlertOrConfirm,
4997 // make sure any modifications here don't need to happen over there!
4998 SetDOMStringToNull(aReturn);
5000 if (!AreDialogsEnabled()) {
5001 // Return null, as if the user just canceled the prompt.
5002 return;
5005 // Reset popup state while opening a modal dialog, and firing events
5006 // about the dialog, to prevent the current state from being active
5007 // the whole time a modal dialog is open.
5008 AutoPopupStatePusher popupStatePusher(PopupBlocker::openAbused, true);
5010 // Before bringing up the window, unsuppress painting and flush
5011 // pending reflows.
5012 EnsureReflowFlushAndPaint();
5014 nsAutoString title;
5015 MakeMessageWithPrincipal(title, &aSubjectPrincipal, false,
5016 "ScriptDlgNullPrincipalHeading", "ScriptDlgHeading",
5017 "ScriptDlgGenericHeading");
5019 // Remove non-terminating null characters from the
5020 // string. See bug #310037.
5021 nsAutoString fixedMessage, fixedInitial;
5022 nsContentUtils::StripNullChars(aMessage, fixedMessage);
5023 nsContentUtils::PlatformToDOMLineBreaks(fixedMessage);
5024 nsContentUtils::StripNullChars(aInitial, fixedInitial);
5026 nsresult rv;
5027 nsCOMPtr<nsIPromptFactory> promptFac =
5028 do_GetService("@mozilla.org/prompter;1", &rv);
5029 if (NS_FAILED(rv)) {
5030 aError.Throw(rv);
5031 return;
5034 nsCOMPtr<nsIPrompt> prompt;
5035 aError =
5036 promptFac->GetPrompt(this, NS_GET_IID(nsIPrompt), getter_AddRefs(prompt));
5037 if (aError.Failed()) {
5038 return;
5041 // Always allow content modal prompts for prompt.
5042 if (nsCOMPtr<nsIWritablePropertyBag2> promptBag = do_QueryInterface(prompt)) {
5043 promptBag->SetPropertyAsUint32(u"modalType"_ns,
5044 nsIPrompt::MODAL_TYPE_CONTENT);
5047 // Pass in the default value, if any.
5048 char16_t* inoutValue = ToNewUnicode(fixedInitial);
5049 bool disallowDialog = false;
5051 nsAutoString label;
5052 label.SetIsVoid(true);
5053 if (ShouldPromptToBlockDialogs()) {
5054 nsContentUtils::GetLocalizedString(
5055 nsContentUtils::eCOMMON_DIALOG_PROPERTIES, "ScriptDialogLabel", label);
5058 nsAutoSyncOperation sync(mDoc, SyncOperationBehavior::eSuspendInput);
5059 bool ok;
5060 aError = prompt->Prompt(title.get(), fixedMessage.get(), &inoutValue,
5061 label.IsVoid() ? nullptr : label.get(),
5062 &disallowDialog, &ok);
5064 if (disallowDialog) {
5065 DisableDialogs();
5068 // XXX Doesn't this leak inoutValue?
5069 if (aError.Failed()) {
5070 return;
5073 nsString outValue;
5074 outValue.Adopt(inoutValue);
5075 if (ok && inoutValue) {
5076 aReturn = std::move(outValue);
5080 void nsGlobalWindowOuter::FocusOuter(CallerType aCallerType,
5081 bool aFromOtherProcess,
5082 uint64_t aActionId) {
5083 nsFocusManager* fm = nsFocusManager::GetFocusManager();
5084 if (!fm) {
5085 return;
5088 auto [canFocus, isActive] = GetBrowsingContext()->CanFocusCheck(aCallerType);
5089 if (aFromOtherProcess) {
5090 // We trust that the check passed in a process that's, in principle,
5091 // untrusted, because we don't have the required caller context available
5092 // here. Also, the worst that the other process can do in this case is to
5093 // raise a window it's not supposed to be allowed to raise.
5094 // https://bugzilla.mozilla.org/show_bug.cgi?id=1677899
5095 MOZ_ASSERT(XRE_IsContentProcess(),
5096 "Parent should not trust other processes.");
5097 canFocus = true;
5100 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
5101 if (treeOwnerAsWin && (canFocus || isActive)) {
5102 bool isEnabled = true;
5103 if (NS_SUCCEEDED(treeOwnerAsWin->GetEnabled(&isEnabled)) && !isEnabled) {
5104 NS_WARNING("Should not try to set the focus on a disabled window");
5105 return;
5108 // XXXndeakin not sure what this is for or if it should go somewhere else
5109 nsCOMPtr<nsIEmbeddingSiteWindow> embeddingWin(
5110 do_GetInterface(treeOwnerAsWin));
5111 if (embeddingWin) embeddingWin->SetFocus();
5114 if (!mDocShell) {
5115 return;
5118 RefPtr<BrowsingContext> parent;
5119 BrowsingContext* bc = GetBrowsingContext();
5120 if (bc) {
5121 parent = bc->GetParent();
5122 if (!parent && XRE_IsParentProcess()) {
5123 parent = bc->Canonical()->GetParentCrossChromeBoundary();
5126 if (parent) {
5127 if (!parent->IsInProcess()) {
5128 if (isActive) {
5129 fm->WindowRaised(this, aActionId);
5130 } else {
5131 ContentChild* contentChild = ContentChild::GetSingleton();
5132 MOZ_ASSERT(contentChild);
5133 contentChild->SendFinalizeFocusOuter(bc, canFocus, aCallerType);
5135 return;
5138 if (Element* frame = mDoc->GetEmbedderElement()) {
5139 nsContentUtils::RequestFrameFocus(*frame, canFocus, aCallerType);
5141 return;
5144 if (canFocus) {
5145 // if there is no parent, this must be a toplevel window, so raise the
5146 // window if canFocus is true. If this is a child process, the raise
5147 // window request will get forwarded to the parent by the puppet widget.
5148 fm->RaiseWindow(this, aCallerType, aActionId);
5152 nsresult nsGlobalWindowOuter::Focus(CallerType aCallerType) {
5153 FORWARD_TO_INNER(Focus, (aCallerType), NS_ERROR_UNEXPECTED);
5156 void nsGlobalWindowOuter::BlurOuter(CallerType aCallerType) {
5157 if (!GetBrowsingContext()->CanBlurCheck(aCallerType)) {
5158 return;
5161 // If embedding apps don't implement nsIEmbeddingSiteWindow, we
5162 // shouldn't throw exceptions to web content.
5164 nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner();
5165 nsCOMPtr<nsIEmbeddingSiteWindow> siteWindow(do_GetInterface(treeOwner));
5166 if (siteWindow) {
5167 // This method call may cause mDocShell to become nullptr.
5168 siteWindow->Blur();
5170 // if the root is focused, clear the focus
5171 nsFocusManager* fm = nsFocusManager::GetFocusManager();
5172 if (fm && mDoc) {
5173 RefPtr<Element> element;
5174 fm->GetFocusedElementForWindow(this, false, nullptr,
5175 getter_AddRefs(element));
5176 if (element == mDoc->GetRootElement()) {
5177 fm->ClearFocus(this);
5183 void nsGlobalWindowOuter::StopOuter(ErrorResult& aError) {
5184 // IsNavigationAllowed checks are usually done in nsDocShell directly,
5185 // however nsDocShell::Stop has a bunch of internal users that would fail
5186 // the IsNavigationAllowed check.
5187 if (!mDocShell || !nsDocShell::Cast(mDocShell)->IsNavigationAllowed()) {
5188 return;
5191 nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mDocShell));
5192 if (webNav) {
5193 aError = webNav->Stop(nsIWebNavigation::STOP_ALL);
5197 void nsGlobalWindowOuter::PrintOuter(ErrorResult& aError) {
5198 if (!AreDialogsEnabled()) {
5199 // We probably want to keep throwing here; silently doing nothing is a bit
5200 // weird given the typical use cases of print().
5201 return aError.ThrowNotSupportedError("Dialogs not enabled for this window");
5204 if (!StaticPrefs::print_tab_modal_enabled() && ShouldPromptToBlockDialogs() &&
5205 !ConfirmDialogIfNeeded()) {
5206 return aError.ThrowNotAllowedError("Prompt was canceled by the user");
5209 // If we're loading, queue the print for later. This is a special-case that
5210 // only applies to the window.print() call, for compat with other engines and
5211 // pre-existing behavior.
5212 if (mShouldDelayPrintUntilAfterLoad) {
5213 if (nsIDocShell* docShell = GetDocShell()) {
5214 if (docShell->GetBusyFlags() & nsIDocShell::BUSY_FLAGS_PAGE_LOADING) {
5215 mDelayedPrintUntilAfterLoad = true;
5216 return;
5221 #ifdef NS_PRINTING
5222 RefPtr<BrowsingContext> top =
5223 mBrowsingContext ? mBrowsingContext->Top() : nullptr;
5224 bool oldIsPrinting = top && top->GetIsPrinting();
5225 if (top) {
5226 Unused << top->SetIsPrinting(true);
5229 auto unset = MakeScopeExit([&] {
5230 if (top) {
5231 Unused << top->SetIsPrinting(oldIsPrinting);
5235 const bool isPreview = StaticPrefs::print_tab_modal_enabled() &&
5236 !StaticPrefs::print_always_print_silent();
5237 Print(nullptr, nullptr, nullptr, IsPreview(isPreview),
5238 IsForWindowDotPrint::Yes, nullptr, aError);
5239 #endif
5242 Nullable<WindowProxyHolder> nsGlobalWindowOuter::Print(
5243 nsIPrintSettings* aPrintSettings, nsIWebProgressListener* aListener,
5244 nsIDocShell* aDocShellToCloneInto, IsPreview aIsPreview,
5245 IsForWindowDotPrint aForWindowDotPrint,
5246 PrintPreviewResolver&& aPrintPreviewCallback, ErrorResult& aError) {
5247 #ifdef NS_PRINTING
5248 nsCOMPtr<nsIPrintSettingsService> printSettingsService =
5249 do_GetService("@mozilla.org/gfx/printsettings-service;1");
5250 if (!printSettingsService) {
5251 // we currently return here in headless mode - should we?
5252 aError.ThrowNotSupportedError("No print settings service");
5253 return nullptr;
5256 RefPtr<Document> docToPrint = mDoc;
5257 if (NS_WARN_IF(!docToPrint)) {
5258 aError.ThrowNotSupportedError("Document is gone");
5259 return nullptr;
5262 RefPtr<BrowsingContext> sourceBC = docToPrint->GetBrowsingContext();
5263 MOZ_DIAGNOSTIC_ASSERT(sourceBC);
5264 if (!sourceBC) {
5265 aError.ThrowNotSupportedError("No browsing context");
5266 return nullptr;
5269 nsAutoSyncOperation sync(docToPrint, SyncOperationBehavior::eAllowInput);
5270 EnterModalState();
5271 auto exitModal = MakeScopeExit([&] { LeaveModalState(); });
5273 nsCOMPtr<nsIContentViewer> cv;
5274 RefPtr<BrowsingContext> bc;
5275 bool hasPrintCallbacks = false;
5276 if (docToPrint->IsStaticDocument() &&
5277 (aIsPreview == IsPreview::Yes ||
5278 StaticPrefs::print_tab_modal_enabled())) {
5279 if (aForWindowDotPrint == IsForWindowDotPrint::Yes) {
5280 aError.ThrowNotSupportedError(
5281 "Calling print() from a print preview is unsupported, did you intend "
5282 "to call printPreview() instead?");
5283 return nullptr;
5285 // We're already a print preview window, just reuse our browsing context /
5286 // content viewer.
5287 bc = sourceBC;
5288 nsCOMPtr<nsIDocShell> docShell = bc->GetDocShell();
5289 if (!docShell) {
5290 aError.ThrowNotSupportedError("No docshell");
5291 return nullptr;
5293 // We could handle this if needed.
5294 if (aDocShellToCloneInto && aDocShellToCloneInto != docShell) {
5295 aError.ThrowNotSupportedError(
5296 "We don't handle cloning a print preview doc into a different "
5297 "docshell");
5298 return nullptr;
5300 docShell->GetContentViewer(getter_AddRefs(cv));
5301 MOZ_DIAGNOSTIC_ASSERT(cv);
5302 } else {
5303 if (aDocShellToCloneInto) {
5304 bc = aDocShellToCloneInto->GetBrowsingContext();
5305 } else {
5306 AutoNoJSAPI nojsapi;
5307 auto printKind = aForWindowDotPrint == IsForWindowDotPrint::Yes
5308 ? PrintKind::WindowDotPrint
5309 : PrintKind::InternalPrint;
5310 aError = OpenInternal(u""_ns, u""_ns, u""_ns,
5311 false, // aDialog
5312 false, // aContentModal
5313 true, // aCalledNoScript
5314 false, // aDoJSFixups
5315 true, // aNavigate
5316 nullptr, nullptr, // No args
5317 nullptr, // aLoadState
5318 false, // aForceNoOpener
5319 printKind, getter_AddRefs(bc));
5320 if (NS_WARN_IF(aError.Failed())) {
5321 return nullptr;
5324 if (!bc) {
5325 aError.ThrowNotAllowedError("No browsing context");
5326 return nullptr;
5329 Unused << bc->Top()->SetIsPrinting(true);
5330 nsCOMPtr<nsIDocShell> cloneDocShell = bc->GetDocShell();
5331 MOZ_DIAGNOSTIC_ASSERT(cloneDocShell);
5332 cloneDocShell->GetContentViewer(getter_AddRefs(cv));
5333 MOZ_DIAGNOSTIC_ASSERT(cv);
5334 if (!cv) {
5335 aError.ThrowNotSupportedError("Didn't end up with a content viewer");
5336 return nullptr;
5339 if (bc != sourceBC) {
5340 MOZ_ASSERT(bc->IsTopContent());
5341 // If we are cloning from a document in a different BrowsingContext, we
5342 // need to make sure to copy over our opener policy information from that
5343 // BrowsingContext. In the case where the source is an iframe, this
5344 // information needs to be copied from the toplevel source
5345 // BrowsingContext, as we may be making a static clone of a single
5346 // subframe.
5347 MOZ_ALWAYS_SUCCEEDS(
5348 bc->SetOpenerPolicy(sourceBC->Top()->GetOpenerPolicy()));
5349 MOZ_DIAGNOSTIC_ASSERT(bc->Group() == sourceBC->Group());
5352 if (RefPtr<Document> doc = cv->GetDocument()) {
5353 if (doc->IsShowing()) {
5354 // We're going to drop this document on the floor, in the SetDocument
5355 // call below. Make sure to run OnPageHide() to keep state consistent
5356 // and avoids assertions in the document destructor.
5357 doc->OnPageHide(false, nullptr);
5361 // TODO(emilio): Should dispatch this to OOP iframes too.
5362 AutoPrintEventDispatcher dispatcher(*docToPrint);
5364 nsAutoScriptBlocker blockScripts;
5365 RefPtr<Document> clone =
5366 docToPrint->CreateStaticClone(cloneDocShell, cv, &hasPrintCallbacks);
5367 if (!clone) {
5368 aError.ThrowNotSupportedError("Clone operation for printing failed");
5369 return nullptr;
5373 nsCOMPtr<nsIWebBrowserPrint> webBrowserPrint = do_QueryInterface(cv);
5374 if (!webBrowserPrint) {
5375 aError.ThrowNotSupportedError(
5376 "Content viewer didn't implement nsIWebBrowserPrint");
5377 return nullptr;
5380 if (aIsPreview == IsPreview::Yes) {
5381 // When using the new print preview UI from window.print() this would be
5382 // wasted work (and use probably-incorrect settings). So skip it, the
5383 // preview UI will take care of calling PrintPreview again.
5384 if (aForWindowDotPrint == IsForWindowDotPrint::No) {
5385 aError = webBrowserPrint->PrintPreview(aPrintSettings, aListener,
5386 std::move(aPrintPreviewCallback));
5387 if (aError.Failed()) {
5388 return nullptr;
5391 } else {
5392 // Historically we've eaten this error.
5393 webBrowserPrint->Print(aPrintSettings, aListener);
5396 // When using window.print() with the new UI, we usually want to block until
5397 // the print dialog is hidden. But we can't really do that if we have print
5398 // callbacks, because we are inside a sync operation, and we want to run
5399 // microtasks / etc that the print callbacks may create. It is really awkward
5400 // to have this subtle behavior difference...
5402 // We also want to do this for fuzzing, so that they can test window.print().
5403 const bool shouldBlock = [&] {
5404 if (aForWindowDotPrint == IsForWindowDotPrint::No) {
5405 return false;
5407 if (aIsPreview == IsPreview::Yes) {
5408 return !hasPrintCallbacks;
5410 return StaticPrefs::dom_window_print_fuzzing_block_while_printing();
5411 }();
5413 if (shouldBlock) {
5414 SpinEventLoopUntil([&] { return bc->IsDiscarded(); });
5417 return WindowProxyHolder(std::move(bc));
5418 #else
5419 return nullptr;
5420 #endif // NS_PRINTING
5423 void nsGlobalWindowOuter::MoveToOuter(int32_t aXPos, int32_t aYPos,
5424 CallerType aCallerType,
5425 ErrorResult& aError) {
5427 * If caller is not chrome and the user has not explicitly exempted the site,
5428 * prevent window.moveTo() by exiting early
5431 if (!CanMoveResizeWindows(aCallerType) || mBrowsingContext->IsFrame()) {
5432 return;
5435 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
5436 if (!treeOwnerAsWin) {
5437 aError.Throw(NS_ERROR_FAILURE);
5438 return;
5441 nsCOMPtr<nsIScreenManager> screenMgr =
5442 do_GetService("@mozilla.org/gfx/screenmanager;1");
5443 nsCOMPtr<nsIScreen> screen;
5444 if (screenMgr) {
5445 CSSSize size;
5446 GetInnerSize(size);
5447 screenMgr->ScreenForRect(aXPos, aYPos, std::round(size.width),
5448 std::round(size.height), getter_AddRefs(screen));
5451 if (screen) {
5452 // On secondary displays, the "CSS px" coordinates are offset so that they
5453 // share their origin with global desktop pixels, to avoid ambiguities in
5454 // the coordinate space when there are displays with different DPIs.
5455 // (See the corresponding code in GetScreenXY() above.)
5456 int32_t screenLeftDeskPx, screenTopDeskPx, w, h;
5457 screen->GetRectDisplayPix(&screenLeftDeskPx, &screenTopDeskPx, &w, &h);
5458 CSSIntPoint cssPos(aXPos - screenLeftDeskPx, aYPos - screenTopDeskPx);
5459 CheckSecurityLeftAndTop(&cssPos.x, &cssPos.y, aCallerType);
5461 double scale;
5462 screen->GetDefaultCSSScaleFactor(&scale);
5463 LayoutDevicePoint devPos = cssPos * CSSToLayoutDeviceScale(scale);
5465 screen->GetContentsScaleFactor(&scale);
5466 DesktopPoint deskPos = devPos / DesktopToLayoutDeviceScale(scale);
5467 aError = treeOwnerAsWin->SetPositionDesktopPix(screenLeftDeskPx + deskPos.x,
5468 screenTopDeskPx + deskPos.y);
5469 } else {
5470 // We couldn't find a screen? Just assume a 1:1 mapping.
5471 CSSIntPoint cssPos(aXPos, aXPos);
5472 CheckSecurityLeftAndTop(&cssPos.x, &cssPos.y, aCallerType);
5473 LayoutDevicePoint devPos = cssPos * CSSToLayoutDeviceScale(1.0);
5474 aError = treeOwnerAsWin->SetPosition(devPos.x, devPos.y);
5477 CheckForDPIChange();
5480 void nsGlobalWindowOuter::MoveByOuter(int32_t aXDif, int32_t aYDif,
5481 CallerType aCallerType,
5482 ErrorResult& aError) {
5484 * If caller is not chrome and the user has not explicitly exempted the site,
5485 * prevent window.moveBy() by exiting early
5488 if (!CanMoveResizeWindows(aCallerType) || mBrowsingContext->IsFrame()) {
5489 return;
5492 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
5493 if (!treeOwnerAsWin) {
5494 aError.Throw(NS_ERROR_FAILURE);
5495 return;
5498 // To do this correctly we have to convert what we get from GetPosition
5499 // into CSS pixels, add the arguments, do the security check, and
5500 // then convert back to device pixels for the call to SetPosition.
5502 int32_t x, y;
5503 aError = treeOwnerAsWin->GetPosition(&x, &y);
5504 if (aError.Failed()) {
5505 return;
5508 // mild abuse of a "size" object so we don't need more helper functions
5509 nsIntSize cssPos(
5510 DevToCSSIntPixelsForBaseWindow(nsIntSize(x, y), treeOwnerAsWin));
5512 cssPos.width += aXDif;
5513 cssPos.height += aYDif;
5515 CheckSecurityLeftAndTop(&cssPos.width, &cssPos.height, aCallerType);
5517 nsIntSize newDevPos(CSSToDevIntPixelsForBaseWindow(cssPos, treeOwnerAsWin));
5519 aError = treeOwnerAsWin->SetPosition(newDevPos.width, newDevPos.height);
5521 CheckForDPIChange();
5524 nsresult nsGlobalWindowOuter::MoveBy(int32_t aXDif, int32_t aYDif) {
5525 ErrorResult rv;
5526 MoveByOuter(aXDif, aYDif, CallerType::System, rv);
5528 return rv.StealNSResult();
5531 void nsGlobalWindowOuter::ResizeToOuter(int32_t aWidth, int32_t aHeight,
5532 CallerType aCallerType,
5533 ErrorResult& aError) {
5535 * If caller is not chrome and the user has not explicitly exempted the site,
5536 * prevent window.resizeTo() by exiting early
5539 if (!CanMoveResizeWindows(aCallerType) || mBrowsingContext->IsFrame()) {
5540 return;
5543 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
5544 if (!treeOwnerAsWin) {
5545 aError.Throw(NS_ERROR_FAILURE);
5546 return;
5549 nsIntSize cssSize(aWidth, aHeight);
5550 CheckSecurityWidthAndHeight(&cssSize.width, &cssSize.height, aCallerType);
5552 nsIntSize devSz(CSSToDevIntPixelsForBaseWindow(cssSize, treeOwnerAsWin));
5554 aError = treeOwnerAsWin->SetSize(devSz.width, devSz.height, true);
5556 CheckForDPIChange();
5559 void nsGlobalWindowOuter::ResizeByOuter(int32_t aWidthDif, int32_t aHeightDif,
5560 CallerType aCallerType,
5561 ErrorResult& aError) {
5563 * If caller is not chrome and the user has not explicitly exempted the site,
5564 * prevent window.resizeBy() by exiting early
5567 if (!CanMoveResizeWindows(aCallerType) || mBrowsingContext->IsFrame()) {
5568 return;
5571 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
5572 if (!treeOwnerAsWin) {
5573 aError.Throw(NS_ERROR_FAILURE);
5574 return;
5577 int32_t width, height;
5578 aError = treeOwnerAsWin->GetSize(&width, &height);
5579 if (aError.Failed()) {
5580 return;
5583 // To do this correctly we have to convert what we got from GetSize
5584 // into CSS pixels, add the arguments, do the security check, and
5585 // then convert back to device pixels for the call to SetSize.
5587 nsIntSize cssSize(
5588 DevToCSSIntPixelsForBaseWindow(nsIntSize(width, height), treeOwnerAsWin));
5590 cssSize.width += aWidthDif;
5591 cssSize.height += aHeightDif;
5593 CheckSecurityWidthAndHeight(&cssSize.width, &cssSize.height, aCallerType);
5595 nsIntSize newDevSize(CSSToDevIntPixelsForBaseWindow(cssSize, treeOwnerAsWin));
5597 aError = treeOwnerAsWin->SetSize(newDevSize.width, newDevSize.height, true);
5599 CheckForDPIChange();
5602 void nsGlobalWindowOuter::SizeToContentOuter(CallerType aCallerType,
5603 ErrorResult& aError) {
5604 if (!mDocShell) {
5605 return;
5609 * If caller is not chrome and the user has not explicitly exempted the site,
5610 * prevent window.sizeToContent() by exiting early
5613 if (!CanMoveResizeWindows(aCallerType) || mBrowsingContext->IsFrame()) {
5614 return;
5617 // The content viewer does a check to make sure that it's a content
5618 // viewer for a toplevel docshell.
5619 nsCOMPtr<nsIContentViewer> cv;
5620 mDocShell->GetContentViewer(getter_AddRefs(cv));
5621 if (!cv) {
5622 aError.Throw(NS_ERROR_FAILURE);
5623 return;
5626 nsIntSize contentSize;
5627 aError = cv->GetContentSize(&contentSize.width, &contentSize.height);
5628 if (aError.Failed()) {
5629 return;
5632 // Make sure the new size is following the CheckSecurityWidthAndHeight
5633 // rules.
5634 nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner();
5635 if (!treeOwner) {
5636 aError.Throw(NS_ERROR_FAILURE);
5637 return;
5640 // Don't use DevToCSSIntPixelsForBaseWindow() nor
5641 // CSSToDevIntPixelsForBaseWindow() here because contentSize is comes from
5642 // nsIContentViewer::GetContentSize() and it's computed with nsPresContext so
5643 // that we need to work with nsPresContext here too.
5644 RefPtr<nsPresContext> presContext = cv->GetPresContext();
5645 MOZ_ASSERT(
5646 presContext,
5647 "Should be non-nullptr if nsIContentViewer::GetContentSize() succeeded");
5648 nsIntSize cssSize(presContext->DevPixelsToIntCSSPixels(contentSize.width),
5649 presContext->DevPixelsToIntCSSPixels(contentSize.height));
5651 CheckSecurityWidthAndHeight(&cssSize.width, &cssSize.height, aCallerType);
5653 nsIntSize newDevSize(presContext->CSSPixelsToDevPixels(cssSize.width),
5654 presContext->CSSPixelsToDevPixels(cssSize.height));
5656 nsCOMPtr<nsIDocShell> docShell = mDocShell;
5657 aError =
5658 treeOwner->SizeShellTo(docShell, newDevSize.width, newDevSize.height);
5661 already_AddRefed<nsPIWindowRoot> nsGlobalWindowOuter::GetTopWindowRoot() {
5662 nsPIDOMWindowOuter* piWin = GetPrivateRoot();
5663 if (!piWin) {
5664 return nullptr;
5667 nsCOMPtr<nsPIWindowRoot> window =
5668 do_QueryInterface(piWin->GetChromeEventHandler());
5669 return window.forget();
5672 void nsGlobalWindowOuter::FirePopupBlockedEvent(
5673 Document* aDoc, nsIURI* aPopupURI, const nsAString& aPopupWindowName,
5674 const nsAString& aPopupWindowFeatures) {
5675 MOZ_ASSERT(aDoc);
5677 // Fire a "DOMPopupBlocked" event so that the UI can hear about
5678 // blocked popups.
5679 PopupBlockedEventInit init;
5680 init.mBubbles = true;
5681 init.mCancelable = true;
5682 // XXX: This is a different object, but webidl requires an inner window here
5683 // now.
5684 init.mRequestingWindow = GetCurrentInnerWindowInternal();
5685 init.mPopupWindowURI = aPopupURI;
5686 init.mPopupWindowName = aPopupWindowName;
5687 init.mPopupWindowFeatures = aPopupWindowFeatures;
5689 RefPtr<PopupBlockedEvent> event =
5690 PopupBlockedEvent::Constructor(aDoc, u"DOMPopupBlocked"_ns, init);
5692 event->SetTrusted(true);
5694 aDoc->DispatchEvent(*event);
5697 // static
5698 bool nsGlobalWindowOuter::CanSetProperty(const char* aPrefName) {
5699 // Chrome can set any property.
5700 if (nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
5701 return true;
5704 // If the pref is set to true, we can not set the property
5705 // and vice versa.
5706 return !Preferences::GetBool(aPrefName, true);
5709 /* If a window open is blocked, fire the appropriate DOM events. */
5710 void nsGlobalWindowOuter::FireAbuseEvents(
5711 const nsAString& aPopupURL, const nsAString& aPopupWindowName,
5712 const nsAString& aPopupWindowFeatures) {
5713 // fetch the URI of the window requesting the opened window
5714 nsCOMPtr<Document> currentDoc = GetDoc();
5715 nsCOMPtr<nsIURI> popupURI;
5717 // build the URI of the would-have-been popup window
5718 // (see nsWindowWatcher::URIfromURL)
5720 // first, fetch the opener's base URI
5722 nsIURI* baseURL = nullptr;
5724 nsCOMPtr<Document> doc = GetEntryDocument();
5725 if (doc) baseURL = doc->GetDocBaseURI();
5727 // use the base URI to build what would have been the popup's URI
5728 nsCOMPtr<nsIIOService> ios(do_GetService(NS_IOSERVICE_CONTRACTID));
5729 if (ios)
5730 ios->NewURI(NS_ConvertUTF16toUTF8(aPopupURL), nullptr, baseURL,
5731 getter_AddRefs(popupURI));
5733 // fire an event block full of informative URIs
5734 FirePopupBlockedEvent(currentDoc, popupURI, aPopupWindowName,
5735 aPopupWindowFeatures);
5738 Nullable<WindowProxyHolder> nsGlobalWindowOuter::OpenOuter(
5739 const nsAString& aUrl, const nsAString& aName, const nsAString& aOptions,
5740 ErrorResult& aError) {
5741 RefPtr<BrowsingContext> bc;
5742 nsresult rv = OpenJS(aUrl, aName, aOptions, getter_AddRefs(bc));
5743 if (rv == NS_ERROR_MALFORMED_URI) {
5744 aError.ThrowSyntaxError("Unable to open a window with invalid URL '"_ns +
5745 NS_ConvertUTF16toUTF8(aUrl) + "'."_ns);
5746 return nullptr;
5749 // XXX Is it possible that some internal errors are thrown here?
5750 aError = rv;
5752 if (!bc) {
5753 return nullptr;
5755 return WindowProxyHolder(std::move(bc));
5758 nsresult nsGlobalWindowOuter::Open(const nsAString& aUrl,
5759 const nsAString& aName,
5760 const nsAString& aOptions,
5761 nsDocShellLoadState* aLoadState,
5762 bool aForceNoOpener,
5763 BrowsingContext** _retval) {
5764 return OpenInternal(aUrl, aName, aOptions,
5765 false, // aDialog
5766 false, // aContentModal
5767 true, // aCalledNoScript
5768 false, // aDoJSFixups
5769 true, // aNavigate
5770 nullptr, nullptr, // No args
5771 aLoadState, aForceNoOpener, PrintKind::None, _retval);
5774 nsresult nsGlobalWindowOuter::OpenJS(const nsAString& aUrl,
5775 const nsAString& aName,
5776 const nsAString& aOptions,
5777 BrowsingContext** _retval) {
5778 return OpenInternal(aUrl, aName, aOptions,
5779 false, // aDialog
5780 false, // aContentModal
5781 false, // aCalledNoScript
5782 true, // aDoJSFixups
5783 true, // aNavigate
5784 nullptr, nullptr, // No args
5785 nullptr, // aLoadState
5786 false, // aForceNoOpener
5787 PrintKind::None, _retval);
5790 // like Open, but attaches to the new window any extra parameters past
5791 // [features] as a JS property named "arguments"
5792 nsresult nsGlobalWindowOuter::OpenDialog(const nsAString& aUrl,
5793 const nsAString& aName,
5794 const nsAString& aOptions,
5795 nsISupports* aExtraArgument,
5796 BrowsingContext** _retval) {
5797 return OpenInternal(aUrl, aName, aOptions,
5798 true, // aDialog
5799 false, // aContentModal
5800 true, // aCalledNoScript
5801 false, // aDoJSFixups
5802 true, // aNavigate
5803 nullptr, aExtraArgument, // Arguments
5804 nullptr, // aLoadState
5805 false, // aForceNoOpener
5806 PrintKind::None, _retval);
5809 // Like Open, but passes aNavigate=false.
5810 /* virtual */
5811 nsresult nsGlobalWindowOuter::OpenNoNavigate(const nsAString& aUrl,
5812 const nsAString& aName,
5813 const nsAString& aOptions,
5814 BrowsingContext** _retval) {
5815 return OpenInternal(aUrl, aName, aOptions,
5816 false, // aDialog
5817 false, // aContentModal
5818 true, // aCalledNoScript
5819 false, // aDoJSFixups
5820 false, // aNavigate
5821 nullptr, nullptr, // No args
5822 nullptr, // aLoadState
5823 false, // aForceNoOpener
5824 PrintKind::None, _retval);
5827 Nullable<WindowProxyHolder> nsGlobalWindowOuter::OpenDialogOuter(
5828 JSContext* aCx, const nsAString& aUrl, const nsAString& aName,
5829 const nsAString& aOptions, const Sequence<JS::Value>& aExtraArgument,
5830 ErrorResult& aError) {
5831 nsCOMPtr<nsIJSArgArray> argvArray;
5832 aError =
5833 NS_CreateJSArgv(aCx, aExtraArgument.Length(), aExtraArgument.Elements(),
5834 getter_AddRefs(argvArray));
5835 if (aError.Failed()) {
5836 return nullptr;
5839 RefPtr<BrowsingContext> dialog;
5840 aError = OpenInternal(aUrl, aName, aOptions,
5841 true, // aDialog
5842 false, // aContentModal
5843 false, // aCalledNoScript
5844 false, // aDoJSFixups
5845 true, // aNavigate
5846 argvArray, nullptr, // Arguments
5847 nullptr, // aLoadState
5848 false, // aForceNoOpener
5849 PrintKind::None, getter_AddRefs(dialog));
5850 if (!dialog) {
5851 return nullptr;
5853 return WindowProxyHolder(std::move(dialog));
5856 WindowProxyHolder nsGlobalWindowOuter::GetFramesOuter() {
5857 RefPtr<nsPIDOMWindowOuter> frames(this);
5858 FlushPendingNotifications(FlushType::ContentAndNotify);
5859 return WindowProxyHolder(mBrowsingContext);
5862 /* static */
5863 bool nsGlobalWindowOuter::GatherPostMessageData(
5864 JSContext* aCx, const nsAString& aTargetOrigin, BrowsingContext** aSource,
5865 nsAString& aOrigin, nsIURI** aTargetOriginURI,
5866 nsIPrincipal** aCallerPrincipal, nsGlobalWindowInner** aCallerInnerWindow,
5867 nsIURI** aCallerURI, Maybe<nsID>* aCallerAgentClusterId,
5868 nsACString* aScriptLocation, ErrorResult& aError) {
5870 // Window.postMessage is an intentional subversion of the same-origin policy.
5871 // As such, this code must be particularly careful in the information it
5872 // exposes to calling code.
5874 // http://www.whatwg.org/specs/web-apps/current-work/multipage/section-crossDocumentMessages.html
5877 // First, get the caller's window
5878 RefPtr<nsGlobalWindowInner> callerInnerWin =
5879 nsContentUtils::CallerInnerWindow();
5880 nsIPrincipal* callerPrin;
5881 if (callerInnerWin) {
5882 RefPtr<Document> doc = callerInnerWin->GetExtantDoc();
5883 if (!doc) {
5884 return false;
5886 NS_IF_ADDREF(*aCallerURI = doc->GetDocumentURI());
5888 // Compute the caller's origin either from its principal or, in the case the
5889 // principal doesn't carry a URI (e.g. the system principal), the caller's
5890 // document. We must get this now instead of when the event is created and
5891 // dispatched, because ultimately it is the identity of the calling window
5892 // *now* that determines who sent the message (and not an identity which
5893 // might have changed due to intervening navigations).
5894 callerPrin = callerInnerWin->GetPrincipal();
5895 } else {
5896 // In case the global is not a window, it can be a sandbox, and the
5897 // sandbox's principal can be used for the security check.
5898 nsIGlobalObject* global = GetIncumbentGlobal();
5899 NS_ASSERTION(global, "Why is there no global object?");
5900 callerPrin = global->PrincipalOrNull();
5901 if (callerPrin) {
5902 BasePrincipal::Cast(callerPrin)->GetScriptLocation(*aScriptLocation);
5905 if (!callerPrin) {
5906 return false;
5909 // if the principal has a URI, use that to generate the origin
5910 if (!callerPrin->IsSystemPrincipal()) {
5911 nsAutoCString asciiOrigin;
5912 callerPrin->GetAsciiOrigin(asciiOrigin);
5913 CopyUTF8toUTF16(asciiOrigin, aOrigin);
5914 } else if (callerInnerWin) {
5915 if (!*aCallerURI) {
5916 return false;
5918 // otherwise use the URI of the document to generate origin
5919 nsContentUtils::GetUTFOrigin(*aCallerURI, aOrigin);
5920 } else {
5921 // in case of a sandbox with a system principal origin can be empty
5922 if (!callerPrin->IsSystemPrincipal()) {
5923 return false;
5926 NS_IF_ADDREF(*aCallerPrincipal = callerPrin);
5928 // "/" indicates same origin as caller, "*" indicates no specific origin is
5929 // required.
5930 if (!aTargetOrigin.EqualsASCII("/") && !aTargetOrigin.EqualsASCII("*")) {
5931 nsCOMPtr<nsIURI> targetOriginURI;
5932 if (NS_FAILED(NS_NewURI(getter_AddRefs(targetOriginURI), aTargetOrigin))) {
5933 aError.Throw(NS_ERROR_DOM_SYNTAX_ERR);
5934 return false;
5937 nsresult rv = NS_MutateURI(targetOriginURI)
5938 .SetUserPass(""_ns)
5939 .SetPathQueryRef(""_ns)
5940 .Finalize(aTargetOriginURI);
5941 if (NS_FAILED(rv)) {
5942 return false;
5946 if (!nsContentUtils::IsCallerChrome() && callerInnerWin &&
5947 callerInnerWin->GetOuterWindowInternal()) {
5948 NS_ADDREF(*aSource = callerInnerWin->GetOuterWindowInternal()
5949 ->GetBrowsingContext());
5950 } else {
5951 *aSource = nullptr;
5954 if (aCallerAgentClusterId && callerInnerWin &&
5955 callerInnerWin->GetDocGroup()) {
5956 *aCallerAgentClusterId =
5957 Some(callerInnerWin->GetDocGroup()->AgentClusterId());
5960 callerInnerWin.forget(aCallerInnerWindow);
5962 return true;
5965 bool nsGlobalWindowOuter::GetPrincipalForPostMessage(
5966 const nsAString& aTargetOrigin, nsIURI* aTargetOriginURI,
5967 nsIPrincipal* aCallerPrincipal, nsIPrincipal& aSubjectPrincipal,
5968 nsIPrincipal** aProvidedPrincipal) {
5970 // Window.postMessage is an intentional subversion of the same-origin policy.
5971 // As such, this code must be particularly careful in the information it
5972 // exposes to calling code.
5974 // http://www.whatwg.org/specs/web-apps/current-work/multipage/section-crossDocumentMessages.html
5977 // Convert the provided origin string into a URI for comparison purposes.
5978 nsCOMPtr<nsIPrincipal> providedPrincipal;
5980 if (aTargetOrigin.EqualsASCII("/")) {
5981 providedPrincipal = aCallerPrincipal;
5983 // "*" indicates no specific origin is required.
5984 else if (!aTargetOrigin.EqualsASCII("*")) {
5985 OriginAttributes attrs = aSubjectPrincipal.OriginAttributesRef();
5986 if (aSubjectPrincipal.IsSystemPrincipal()) {
5987 auto principal = BasePrincipal::Cast(GetPrincipal());
5989 if (attrs != principal->OriginAttributesRef()) {
5990 nsAutoCString targetURL;
5991 nsAutoCString sourceOrigin;
5992 nsAutoCString targetOrigin;
5994 if (NS_FAILED(principal->GetAsciiSpec(targetURL)) ||
5995 NS_FAILED(principal->GetOrigin(targetOrigin)) ||
5996 NS_FAILED(aSubjectPrincipal.GetOrigin(sourceOrigin))) {
5997 NS_WARNING("Failed to get source and target origins");
5998 return false;
6001 nsContentUtils::LogSimpleConsoleError(
6002 NS_ConvertUTF8toUTF16(nsPrintfCString(
6003 R"(Attempting to post a message to window with url "%s" and )"
6004 R"(origin "%s" from a system principal scope with mismatched )"
6005 R"(origin "%s".)",
6006 targetURL.get(), targetOrigin.get(), sourceOrigin.get())),
6007 "DOM", !!principal->PrivateBrowsingId(),
6008 principal->IsSystemPrincipal());
6010 attrs = principal->OriginAttributesRef();
6014 // Create a nsIPrincipal inheriting the app/browser attributes from the
6015 // caller.
6016 providedPrincipal =
6017 BasePrincipal::CreateContentPrincipal(aTargetOriginURI, attrs);
6018 if (NS_WARN_IF(!providedPrincipal)) {
6019 return false;
6021 } else {
6022 // We still need to check the originAttributes if the target origin is '*'.
6023 // But we will ingore the FPD here since the FPDs are possible to be
6024 // different.
6025 auto principal = BasePrincipal::Cast(GetPrincipal());
6026 NS_ENSURE_TRUE(principal, false);
6028 OriginAttributes targetAttrs = principal->OriginAttributesRef();
6029 OriginAttributes sourceAttrs = aSubjectPrincipal.OriginAttributesRef();
6030 // We have to exempt the check of OA if the subject prioncipal is a system
6031 // principal since there are many tests try to post messages to content from
6032 // chrome with a mismatch OA. For example, using the ContentTask.spawn() to
6033 // post a message into a private browsing window. The injected code in
6034 // ContentTask.spawn() will be executed under the system principal and the
6035 // OA of the system principal mismatches with the OA of a private browsing
6036 // window.
6037 MOZ_DIAGNOSTIC_ASSERT(aSubjectPrincipal.IsSystemPrincipal() ||
6038 sourceAttrs.EqualsIgnoringFPD(targetAttrs));
6040 // If 'privacy.firstparty.isolate.block_post_message' is true, we will block
6041 // postMessage across different first party domains.
6042 if (OriginAttributes::IsBlockPostMessageForFPI() &&
6043 !aSubjectPrincipal.IsSystemPrincipal() &&
6044 sourceAttrs.mFirstPartyDomain != targetAttrs.mFirstPartyDomain) {
6045 return false;
6049 providedPrincipal.forget(aProvidedPrincipal);
6050 return true;
6053 void nsGlobalWindowOuter::PostMessageMozOuter(JSContext* aCx,
6054 JS::Handle<JS::Value> aMessage,
6055 const nsAString& aTargetOrigin,
6056 JS::Handle<JS::Value> aTransfer,
6057 nsIPrincipal& aSubjectPrincipal,
6058 ErrorResult& aError) {
6059 RefPtr<BrowsingContext> sourceBc;
6060 nsAutoString origin;
6061 nsCOMPtr<nsIURI> targetOriginURI;
6062 nsCOMPtr<nsIPrincipal> callerPrincipal;
6063 RefPtr<nsGlobalWindowInner> callerInnerWindow;
6064 nsCOMPtr<nsIURI> callerURI;
6065 Maybe<nsID> callerAgentClusterId = Nothing();
6066 nsAutoCString scriptLocation;
6067 if (!GatherPostMessageData(
6068 aCx, aTargetOrigin, getter_AddRefs(sourceBc), origin,
6069 getter_AddRefs(targetOriginURI), getter_AddRefs(callerPrincipal),
6070 getter_AddRefs(callerInnerWindow), getter_AddRefs(callerURI),
6071 &callerAgentClusterId, &scriptLocation, aError)) {
6072 return;
6075 nsCOMPtr<nsIPrincipal> providedPrincipal;
6076 if (!GetPrincipalForPostMessage(aTargetOrigin, targetOriginURI,
6077 callerPrincipal, aSubjectPrincipal,
6078 getter_AddRefs(providedPrincipal))) {
6079 return;
6082 // Create and asynchronously dispatch a runnable which will handle actual DOM
6083 // event creation and dispatch.
6084 RefPtr<PostMessageEvent> event = new PostMessageEvent(
6085 sourceBc, origin, this, providedPrincipal,
6086 callerInnerWindow ? callerInnerWindow->WindowID() : 0, callerURI,
6087 scriptLocation, callerAgentClusterId);
6089 JS::CloneDataPolicy clonePolicy;
6091 if (GetDocGroup() && callerAgentClusterId.isSome() &&
6092 GetDocGroup()->AgentClusterId().Equals(callerAgentClusterId.value())) {
6093 clonePolicy.allowIntraClusterClonableSharedObjects();
6096 if (callerInnerWindow && callerInnerWindow->IsSharedMemoryAllowed()) {
6097 clonePolicy.allowSharedMemoryObjects();
6100 event->Write(aCx, aMessage, aTransfer, clonePolicy, aError);
6101 if (NS_WARN_IF(aError.Failed())) {
6102 return;
6105 event->DispatchToTargetThread(aError);
6108 class nsCloseEvent : public Runnable {
6109 RefPtr<nsGlobalWindowOuter> mWindow;
6110 bool mIndirect;
6112 nsCloseEvent(nsGlobalWindowOuter* aWindow, bool aIndirect)
6113 : mozilla::Runnable("nsCloseEvent"),
6114 mWindow(aWindow),
6115 mIndirect(aIndirect) {}
6117 public:
6118 static nsresult PostCloseEvent(nsGlobalWindowOuter* aWindow, bool aIndirect) {
6119 nsCOMPtr<nsIRunnable> ev = new nsCloseEvent(aWindow, aIndirect);
6120 nsresult rv = aWindow->Dispatch(TaskCategory::Other, ev.forget());
6121 return rv;
6124 NS_IMETHOD Run() override {
6125 if (mWindow) {
6126 if (mIndirect) {
6127 return PostCloseEvent(mWindow, false);
6129 mWindow->ReallyCloseWindow();
6131 return NS_OK;
6135 bool nsGlobalWindowOuter::CanClose() {
6136 if (mIsChrome) {
6137 nsCOMPtr<nsIBrowserDOMWindow> bwin;
6138 GetBrowserDOMWindow(getter_AddRefs(bwin));
6140 bool canClose = true;
6141 if (bwin && NS_SUCCEEDED(bwin->CanClose(&canClose))) {
6142 return canClose;
6146 if (!mDocShell) {
6147 return true;
6150 nsCOMPtr<nsIContentViewer> cv;
6151 mDocShell->GetContentViewer(getter_AddRefs(cv));
6152 if (cv) {
6153 bool canClose;
6154 nsresult rv = cv->PermitUnload(&canClose);
6155 if (NS_SUCCEEDED(rv) && !canClose) return false;
6158 // If we still have to print, we delay the closing until print has happened.
6159 if (mShouldDelayPrintUntilAfterLoad && mDelayedPrintUntilAfterLoad) {
6160 mDelayedCloseForPrinting = true;
6161 return false;
6164 return true;
6167 void nsGlobalWindowOuter::CloseOuter(bool aTrustedCaller) {
6168 if (!mDocShell || IsInModalState() || mBrowsingContext->IsFrame()) {
6169 // window.close() is called on a frame in a frameset, on a window
6170 // that's already closed, or on a window for which there's
6171 // currently a modal dialog open. Ignore such calls.
6172 return;
6175 if (mHavePendingClose) {
6176 // We're going to be closed anyway; do nothing since we don't want
6177 // to double-close
6178 return;
6181 if (mBlockScriptedClosingFlag) {
6182 // A script's popup has been blocked and we don't want
6183 // the window to be closed directly after this event,
6184 // so the user can see that there was a blocked popup.
6185 return;
6188 // Don't allow scripts from content to close non-neterror windows that
6189 // were not opened by script.
6190 if (mDoc) {
6191 nsAutoString url;
6192 nsresult rv = mDoc->GetURL(url);
6193 NS_ENSURE_SUCCESS_VOID(rv);
6195 if (!StringBeginsWith(url, u"about:neterror"_ns) &&
6196 !mBrowsingContext->HadOriginalOpener() && !aTrustedCaller &&
6197 !IsOnlyTopLevelDocumentInSHistory()) {
6198 bool allowClose =
6199 mAllowScriptsToClose ||
6200 Preferences::GetBool("dom.allow_scripts_to_close_windows", true);
6201 if (!allowClose) {
6202 // We're blocking the close operation
6203 // report localized error msg in JS console
6204 nsContentUtils::ReportToConsole(
6205 nsIScriptError::warningFlag, "DOM Window"_ns,
6206 mDoc, // Better name for the category?
6207 nsContentUtils::eDOM_PROPERTIES, "WindowCloseBlockedWarning");
6209 return;
6214 if (!mInClose && !mIsClosed && !CanClose()) {
6215 return;
6218 // Fire a DOM event notifying listeners that this window is about to
6219 // be closed. The tab UI code may choose to cancel the default
6220 // action for this event, if so, we won't actually close the window
6221 // (since the tab UI code will close the tab in stead). Sure, this
6222 // could be abused by content code, but do we care? I don't think
6223 // so...
6225 bool wasInClose = mInClose;
6226 mInClose = true;
6228 if (!DispatchCustomEvent(u"DOMWindowClose"_ns, ChromeOnlyDispatch::eYes)) {
6229 // Someone chose to prevent the default action for this event, if
6230 // so, let's not close this window after all...
6232 mInClose = wasInClose;
6233 return;
6236 FinalClose();
6239 bool nsGlobalWindowOuter::IsOnlyTopLevelDocumentInSHistory() {
6240 NS_ENSURE_TRUE(mDocShell && mBrowsingContext, false);
6241 // Disabled since IsFrame() is buggy in Fission
6242 // MOZ_ASSERT(mBrowsingContext->IsTop());
6244 if (mozilla::SessionHistoryInParent()) {
6245 return mBrowsingContext->GetIsSingleToplevelInHistory();
6248 RefPtr<ChildSHistory> csh = nsDocShell::Cast(mDocShell)->GetSessionHistory();
6249 if (csh && csh->LegacySHistory()) {
6250 return csh->LegacySHistory()->IsEmptyOrHasEntriesForSingleTopLevelPage();
6253 return false;
6256 nsresult nsGlobalWindowOuter::Close() {
6257 CloseOuter(/* aTrustedCaller = */ true);
6258 return NS_OK;
6261 void nsGlobalWindowOuter::ForceClose() {
6262 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
6264 if (mBrowsingContext->IsFrame() || !mDocShell) {
6265 // This may be a frame in a frameset, or a window that's already closed.
6266 // Ignore such calls.
6267 return;
6270 if (mHavePendingClose) {
6271 // We're going to be closed anyway; do nothing since we don't want
6272 // to double-close
6273 return;
6276 mInClose = true;
6278 DispatchCustomEvent(u"DOMWindowClose"_ns, ChromeOnlyDispatch::eYes);
6280 FinalClose();
6283 void nsGlobalWindowOuter::FinalClose() {
6284 // Flag that we were closed.
6285 mIsClosed = true;
6287 if (!mBrowsingContext->IsDiscarded()) {
6288 MOZ_ALWAYS_SUCCEEDS(mBrowsingContext->SetClosed(true));
6291 // If we get here from CloseOuter then it means that the parent process is
6292 // going to close our window for us. It's just important to set mIsClosed.
6293 if (XRE_GetProcessType() == GeckoProcessType_Content) {
6294 return;
6297 // This stuff is non-sensical but incredibly fragile. The reasons for the
6298 // behavior here don't make sense today and may not have ever made sense,
6299 // but various bits of frontend code break when you change them. If you need
6300 // to fix up this behavior, feel free to. It's a righteous task, but involves
6301 // wrestling with various download manager tests, frontend code, and possible
6302 // broken addons. The chrome tests in toolkit/mozapps/downloads are a good
6303 // testing ground.
6305 // In particular, if some inner of |win| is the entry global, we must
6306 // complete _two_ round-trips to the event loop before the call to
6307 // ReallyCloseWindow. This allows setTimeout handlers that are set after
6308 // FinalClose() is called to run before the window is torn down.
6309 nsCOMPtr<nsPIDOMWindowInner> entryWindow =
6310 do_QueryInterface(GetEntryGlobal());
6311 bool indirect = entryWindow && entryWindow->GetOuterWindow() == this;
6312 if (NS_FAILED(nsCloseEvent::PostCloseEvent(this, indirect))) {
6313 ReallyCloseWindow();
6314 } else {
6315 mHavePendingClose = true;
6319 void nsGlobalWindowOuter::ReallyCloseWindow() {
6320 // Make sure we never reenter this method.
6321 mHavePendingClose = true;
6323 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
6324 if (!treeOwnerAsWin) {
6325 return;
6328 treeOwnerAsWin->Destroy();
6329 CleanUp();
6332 void nsGlobalWindowOuter::EnterModalState() {
6333 // GetInProcessScriptableTop, not GetInProcessTop, so that EnterModalState
6334 // works properly with <iframe mozbrowser>.
6335 nsGlobalWindowOuter* topWin = GetInProcessScriptableTopInternal();
6337 if (!topWin) {
6338 NS_ERROR("Uh, EnterModalState() called w/o a reachable top window?");
6339 return;
6342 // If there is an active ESM in this window, clear it. Otherwise, this can
6343 // cause a problem if a modal state is entered during a mouseup event.
6344 EventStateManager* activeESM = static_cast<EventStateManager*>(
6345 EventStateManager::GetActiveEventStateManager());
6346 if (activeESM && activeESM->GetPresContext()) {
6347 PresShell* activePresShell = activeESM->GetPresContext()->GetPresShell();
6348 if (activePresShell && (nsContentUtils::ContentIsCrossDocDescendantOf(
6349 activePresShell->GetDocument(), mDoc) ||
6350 nsContentUtils::ContentIsCrossDocDescendantOf(
6351 mDoc, activePresShell->GetDocument()))) {
6352 EventStateManager::ClearGlobalActiveContent(activeESM);
6354 PresShell::ReleaseCapturingContent();
6356 if (activePresShell) {
6357 RefPtr<nsFrameSelection> frameSelection =
6358 activePresShell->FrameSelection();
6359 frameSelection->SetDragState(false);
6364 // If there are any drag and drop operations in flight, try to end them.
6365 nsCOMPtr<nsIDragService> ds =
6366 do_GetService("@mozilla.org/widget/dragservice;1");
6367 if (ds) {
6368 ds->EndDragSession(true, 0);
6371 // Clear the capturing content if it is under topDoc.
6372 // Usually the activeESM check above does that, but there are cases when
6373 // we don't have activeESM, or it is for different document.
6374 Document* topDoc = topWin->GetExtantDoc();
6375 nsIContent* capturingContent = PresShell::GetCapturingContent();
6376 if (capturingContent && topDoc &&
6377 nsContentUtils::ContentIsCrossDocDescendantOf(capturingContent, topDoc)) {
6378 PresShell::ReleaseCapturingContent();
6381 if (topWin->mModalStateDepth == 0) {
6382 NS_ASSERTION(!topWin->mSuspendedDoc, "Shouldn't have mSuspendedDoc here!");
6384 topWin->mSuspendedDoc = topDoc;
6385 if (topDoc) {
6386 topDoc->SuppressEventHandling();
6389 if (nsGlobalWindowInner* inner = topWin->GetCurrentInnerWindowInternal()) {
6390 inner->Suspend();
6393 topWin->mModalStateDepth++;
6396 void nsGlobalWindowOuter::LeaveModalState() {
6397 nsGlobalWindowOuter* topWin = GetInProcessScriptableTopInternal();
6399 if (!topWin) {
6400 NS_WARNING("Uh, LeaveModalState() called w/o a reachable top window?");
6401 return;
6404 MOZ_ASSERT(topWin->mModalStateDepth != 0);
6405 MOZ_ASSERT(IsSuspended());
6406 MOZ_ASSERT(topWin->IsSuspended());
6407 topWin->mModalStateDepth--;
6409 nsGlobalWindowInner* inner = topWin->GetCurrentInnerWindowInternal();
6411 if (topWin->mModalStateDepth == 0) {
6412 if (inner) {
6413 inner->Resume();
6416 if (topWin->mSuspendedDoc) {
6417 nsCOMPtr<Document> currentDoc = topWin->GetExtantDoc();
6418 topWin->mSuspendedDoc->UnsuppressEventHandlingAndFireEvents(
6419 currentDoc == topWin->mSuspendedDoc);
6420 topWin->mSuspendedDoc = nullptr;
6424 // Remember the time of the last dialog quit.
6425 if (inner) {
6426 inner->mLastDialogQuitTime = TimeStamp::Now();
6429 if (topWin->mModalStateDepth == 0) {
6430 RefPtr<Event> event = NS_NewDOMEvent(inner, nullptr, nullptr);
6431 event->InitEvent(u"endmodalstate"_ns, true, false);
6432 event->SetTrusted(true);
6433 event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true;
6434 topWin->DispatchEvent(*event);
6438 bool nsGlobalWindowOuter::IsInModalState() {
6439 nsGlobalWindowOuter* topWin = GetInProcessScriptableTopInternal();
6441 if (!topWin) {
6442 // IsInModalState() getting called w/o a reachable top window is a bit
6443 // iffy, but valid enough not to make noise about it. See bug 404828
6444 return false;
6447 return topWin->mModalStateDepth != 0;
6450 void nsGlobalWindowOuter::NotifyWindowIDDestroyed(const char* aTopic) {
6451 nsCOMPtr<nsIRunnable> runnable =
6452 new WindowDestroyedEvent(this, mWindowID, aTopic);
6453 Dispatch(TaskCategory::Other, runnable.forget());
6456 Element* nsGlobalWindowOuter::GetFrameElement(nsIPrincipal& aSubjectPrincipal) {
6457 // Per HTML5, the frameElement getter returns null in cross-origin situations.
6458 Element* element = GetFrameElement();
6459 if (!element) {
6460 return nullptr;
6463 if (!aSubjectPrincipal.SubsumesConsideringDomain(element->NodePrincipal())) {
6464 return nullptr;
6467 return element;
6470 Element* nsGlobalWindowOuter::GetFrameElement() {
6471 if (!mBrowsingContext || mBrowsingContext->IsTop()) {
6472 return nullptr;
6474 return mBrowsingContext->GetEmbedderElement();
6477 namespace {
6478 class ChildCommandDispatcher : public Runnable {
6479 public:
6480 ChildCommandDispatcher(nsPIWindowRoot* aRoot, nsIBrowserChild* aBrowserChild,
6481 nsPIDOMWindowOuter* aWindow, const nsAString& aAction)
6482 : mozilla::Runnable("ChildCommandDispatcher"),
6483 mRoot(aRoot),
6484 mBrowserChild(aBrowserChild),
6485 mWindow(aWindow),
6486 mAction(aAction) {}
6488 NS_IMETHOD Run() override {
6489 nsTArray<nsCString> enabledCommands, disabledCommands;
6490 mRoot->GetEnabledDisabledCommands(enabledCommands, disabledCommands);
6491 if (enabledCommands.Length() || disabledCommands.Length()) {
6492 BrowserChild* bc = static_cast<BrowserChild*>(mBrowserChild.get());
6493 bc->SendEnableDisableCommands(mWindow->GetBrowsingContext(), mAction,
6494 enabledCommands, disabledCommands);
6497 return NS_OK;
6500 private:
6501 nsCOMPtr<nsPIWindowRoot> mRoot;
6502 nsCOMPtr<nsIBrowserChild> mBrowserChild;
6503 nsCOMPtr<nsPIDOMWindowOuter> mWindow;
6504 nsString mAction;
6507 class CommandDispatcher : public Runnable {
6508 public:
6509 CommandDispatcher(nsIDOMXULCommandDispatcher* aDispatcher,
6510 const nsAString& aAction)
6511 : mozilla::Runnable("CommandDispatcher"),
6512 mDispatcher(aDispatcher),
6513 mAction(aAction) {}
6515 NS_IMETHOD Run() override { return mDispatcher->UpdateCommands(mAction); }
6517 nsCOMPtr<nsIDOMXULCommandDispatcher> mDispatcher;
6518 nsString mAction;
6520 } // anonymous namespace
6522 void nsGlobalWindowOuter::UpdateCommands(const nsAString& anAction,
6523 Selection* aSel, int16_t aReason) {
6524 // If this is a child process, redirect to the parent process.
6525 if (nsIDocShell* docShell = GetDocShell()) {
6526 if (nsCOMPtr<nsIBrowserChild> child = docShell->GetBrowserChild()) {
6527 nsCOMPtr<nsPIWindowRoot> root = GetTopWindowRoot();
6528 if (root) {
6529 nsContentUtils::AddScriptRunner(
6530 new ChildCommandDispatcher(root, child, this, anAction));
6532 return;
6536 nsPIDOMWindowOuter* rootWindow = GetPrivateRoot();
6537 if (!rootWindow) {
6538 return;
6541 Document* doc = rootWindow->GetExtantDoc();
6543 if (!doc) {
6544 return;
6546 // selectionchange action is only used for mozbrowser, not for XUL. So we
6547 // bypass XUL command dispatch if anAction is "selectionchange".
6548 if (!anAction.EqualsLiteral("selectionchange")) {
6549 // Retrieve the command dispatcher and call updateCommands on it.
6550 nsIDOMXULCommandDispatcher* xulCommandDispatcher =
6551 doc->GetCommandDispatcher();
6552 if (xulCommandDispatcher) {
6553 nsContentUtils::AddScriptRunner(
6554 new CommandDispatcher(xulCommandDispatcher, anAction));
6559 Selection* nsGlobalWindowOuter::GetSelectionOuter() {
6560 if (!mDocShell) {
6561 return nullptr;
6564 PresShell* presShell = mDocShell->GetPresShell();
6565 if (!presShell) {
6566 return nullptr;
6568 return presShell->GetCurrentSelection(SelectionType::eNormal);
6571 already_AddRefed<Selection> nsGlobalWindowOuter::GetSelection() {
6572 RefPtr<Selection> selection = GetSelectionOuter();
6573 return selection.forget();
6576 bool nsGlobalWindowOuter::FindOuter(const nsAString& aString,
6577 bool aCaseSensitive, bool aBackwards,
6578 bool aWrapAround, bool aWholeWord,
6579 bool aSearchInFrames, bool aShowDialog,
6580 ErrorResult& aError) {
6581 Unused << aShowDialog;
6583 nsCOMPtr<nsIWebBrowserFind> finder(do_GetInterface(mDocShell));
6584 if (!finder) {
6585 aError.Throw(NS_ERROR_NOT_AVAILABLE);
6586 return false;
6589 // Set the options of the search
6590 aError = finder->SetSearchString(aString);
6591 if (aError.Failed()) {
6592 return false;
6594 finder->SetMatchCase(aCaseSensitive);
6595 finder->SetFindBackwards(aBackwards);
6596 finder->SetWrapFind(aWrapAround);
6597 finder->SetEntireWord(aWholeWord);
6598 finder->SetSearchFrames(aSearchInFrames);
6600 // the nsIWebBrowserFind is initialized to use this window
6601 // as the search root, but uses focus to set the current search
6602 // frame. If we're being called from JS (as here), this window
6603 // should be the current search frame.
6604 nsCOMPtr<nsIWebBrowserFindInFrames> framesFinder(do_QueryInterface(finder));
6605 if (framesFinder) {
6606 framesFinder->SetRootSearchFrame(this); // paranoia
6607 framesFinder->SetCurrentSearchFrame(this);
6610 if (aString.IsEmpty()) {
6611 return false;
6614 // Launch the search with the passed in search string
6615 bool didFind = false;
6616 aError = finder->FindNext(&didFind);
6617 return didFind;
6620 //*****************************************************************************
6621 // EventTarget
6622 //*****************************************************************************
6624 nsPIDOMWindowOuter* nsGlobalWindowOuter::GetOwnerGlobalForBindingsInternal() {
6625 return this;
6628 bool nsGlobalWindowOuter::DispatchEvent(Event& aEvent, CallerType aCallerType,
6629 ErrorResult& aRv) {
6630 FORWARD_TO_INNER(DispatchEvent, (aEvent, aCallerType, aRv), false);
6633 bool nsGlobalWindowOuter::ComputeDefaultWantsUntrusted(ErrorResult& aRv) {
6634 // It's OK that we just return false here on failure to create an
6635 // inner. GetOrCreateListenerManager() will likewise fail, and then
6636 // we won't be adding any listeners anyway.
6637 FORWARD_TO_INNER_CREATE(ComputeDefaultWantsUntrusted, (aRv), false);
6640 EventListenerManager* nsGlobalWindowOuter::GetOrCreateListenerManager() {
6641 FORWARD_TO_INNER_CREATE(GetOrCreateListenerManager, (), nullptr);
6644 EventListenerManager* nsGlobalWindowOuter::GetExistingListenerManager() const {
6645 FORWARD_TO_INNER(GetExistingListenerManager, (), nullptr);
6648 //*****************************************************************************
6649 // nsGlobalWindowOuter::nsPIDOMWindow
6650 //*****************************************************************************
6652 nsPIDOMWindowOuter* nsGlobalWindowOuter::GetPrivateParent() {
6653 nsCOMPtr<nsPIDOMWindowOuter> parent = GetInProcessParent();
6655 if (this == parent) {
6656 nsCOMPtr<nsIContent> chromeElement(do_QueryInterface(mChromeEventHandler));
6657 if (!chromeElement)
6658 return nullptr; // This is ok, just means a null parent.
6660 Document* doc = chromeElement->GetComposedDoc();
6661 if (!doc) return nullptr; // This is ok, just means a null parent.
6663 return doc->GetWindow();
6666 return parent;
6669 nsPIDOMWindowOuter* nsGlobalWindowOuter::GetPrivateRoot() {
6670 nsCOMPtr<nsPIDOMWindowOuter> top = GetInProcessTop();
6672 nsCOMPtr<nsIContent> chromeElement(do_QueryInterface(mChromeEventHandler));
6673 if (chromeElement) {
6674 Document* doc = chromeElement->GetComposedDoc();
6675 if (doc) {
6676 nsCOMPtr<nsPIDOMWindowOuter> parent = doc->GetWindow();
6677 if (parent) {
6678 top = parent->GetInProcessTop();
6683 return top;
6686 // This has a caller in Windows-only code (nsNativeAppSupportWin).
6687 Location* nsGlobalWindowOuter::GetLocation() {
6688 // This method can be called on the outer window as well.
6689 FORWARD_TO_INNER(Location, (), nullptr);
6692 void nsGlobalWindowOuter::SetIsBackground(bool aIsBackground) {
6693 bool changed = aIsBackground != IsBackground();
6694 SetIsBackgroundInternal(aIsBackground);
6696 nsGlobalWindowInner* inner = GetCurrentInnerWindowInternal();
6698 if (inner && changed) {
6699 inner->mTimeoutManager->UpdateBackgroundState();
6702 if (aIsBackground) {
6703 // Notify gamepadManager we are at the background window,
6704 // we need to stop vibrate.
6705 // Stop the vr telemery time spent when it switches to
6706 // the background window.
6707 if (inner && changed) {
6708 inner->StopGamepadHaptics();
6709 inner->StopVRActivity();
6710 // true is for asking to set the delta time to
6711 // the telemetry.
6712 inner->ResetVRTelemetry(true);
6714 return;
6717 if (inner) {
6718 // When switching to be as a top tab, restart the telemetry.
6719 // false is for only resetting the timestamp.
6720 inner->ResetVRTelemetry(false);
6721 inner->SyncGamepadState();
6722 inner->StartVRActivity();
6726 void nsGlobalWindowOuter::SetIsBackgroundInternal(bool aIsBackground) {
6727 mIsBackground = aIsBackground;
6730 void nsGlobalWindowOuter::SetChromeEventHandler(
6731 EventTarget* aChromeEventHandler) {
6732 SetChromeEventHandlerInternal(aChromeEventHandler);
6733 // update the chrome event handler on all our inner windows
6734 RefPtr<nsGlobalWindowInner> inner;
6735 for (PRCList* node = PR_LIST_HEAD(this); node != this;
6736 node = PR_NEXT_LINK(inner)) {
6737 // This cast is only safe if `node != this`, as nsGlobalWindowOuter is also
6738 // in the list.
6739 inner = static_cast<nsGlobalWindowInner*>(node);
6740 NS_ASSERTION(!inner->mOuterWindow || inner->mOuterWindow == this,
6741 "bad outer window pointer");
6742 inner->SetChromeEventHandlerInternal(aChromeEventHandler);
6746 void nsGlobalWindowOuter::SetFocusedElement(Element* aElement,
6747 uint32_t aFocusMethod,
6748 bool aNeedsFocus) {
6749 FORWARD_TO_INNER_VOID(SetFocusedElement,
6750 (aElement, aFocusMethod, aNeedsFocus));
6753 uint32_t nsGlobalWindowOuter::GetFocusMethod() {
6754 FORWARD_TO_INNER(GetFocusMethod, (), 0);
6757 bool nsGlobalWindowOuter::ShouldShowFocusRing() {
6758 FORWARD_TO_INNER(ShouldShowFocusRing, (), false);
6761 void nsGlobalWindowOuter::SetKeyboardIndicators(
6762 UIStateChangeType aShowFocusRings) {
6763 nsPIDOMWindowOuter* piWin = GetPrivateRoot();
6764 if (!piWin) {
6765 return;
6768 MOZ_ASSERT(piWin == this);
6770 bool oldShouldShowFocusRing = ShouldShowFocusRing();
6772 // only change the flags that have been modified
6773 nsCOMPtr<nsPIWindowRoot> windowRoot = do_QueryInterface(mChromeEventHandler);
6774 if (!windowRoot) {
6775 return;
6778 if (aShowFocusRings != UIStateChangeType_NoChange) {
6779 windowRoot->SetShowFocusRings(aShowFocusRings == UIStateChangeType_Set);
6782 nsContentUtils::SetKeyboardIndicatorsOnRemoteChildren(this, aShowFocusRings);
6784 bool newShouldShowFocusRing = ShouldShowFocusRing();
6785 if (mInnerWindow && nsGlobalWindowInner::Cast(mInnerWindow)->mHasFocus &&
6786 mInnerWindow->mFocusedElement &&
6787 oldShouldShowFocusRing != newShouldShowFocusRing) {
6788 // Update focusedNode's state.
6789 if (newShouldShowFocusRing) {
6790 mInnerWindow->mFocusedElement->AddStates(NS_EVENT_STATE_FOCUSRING);
6791 } else {
6792 mInnerWindow->mFocusedElement->RemoveStates(NS_EVENT_STATE_FOCUSRING);
6797 bool nsGlobalWindowOuter::TakeFocus(bool aFocus, uint32_t aFocusMethod) {
6798 FORWARD_TO_INNER(TakeFocus, (aFocus, aFocusMethod), false);
6801 void nsGlobalWindowOuter::SetReadyForFocus() {
6802 FORWARD_TO_INNER_VOID(SetReadyForFocus, ());
6805 void nsGlobalWindowOuter::PageHidden() {
6806 FORWARD_TO_INNER_VOID(PageHidden, ());
6809 already_AddRefed<nsICSSDeclaration>
6810 nsGlobalWindowOuter::GetComputedStyleHelperOuter(Element& aElt,
6811 const nsAString& aPseudoElt,
6812 bool aDefaultStylesOnly) {
6813 if (!mDoc) {
6814 return nullptr;
6817 RefPtr<nsICSSDeclaration> compStyle = NS_NewComputedDOMStyle(
6818 &aElt, aPseudoElt, mDoc,
6819 aDefaultStylesOnly ? nsComputedDOMStyle::eDefaultOnly
6820 : nsComputedDOMStyle::eAll);
6822 return compStyle.forget();
6825 //*****************************************************************************
6826 // nsGlobalWindowOuter::nsIInterfaceRequestor
6827 //*****************************************************************************
6829 nsresult nsGlobalWindowOuter::GetInterfaceInternal(const nsIID& aIID,
6830 void** aSink) {
6831 NS_ENSURE_ARG_POINTER(aSink);
6832 *aSink = nullptr;
6834 if (aIID.Equals(NS_GET_IID(nsIWebNavigation))) {
6835 nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mDocShell));
6836 webNav.forget(aSink);
6837 } else if (aIID.Equals(NS_GET_IID(nsIDocShell))) {
6838 nsCOMPtr<nsIDocShell> docShell = mDocShell;
6839 docShell.forget(aSink);
6841 #ifdef NS_PRINTING
6842 else if (aIID.Equals(NS_GET_IID(nsIWebBrowserPrint))) {
6843 if (mDocShell) {
6844 nsCOMPtr<nsIContentViewer> viewer;
6845 mDocShell->GetContentViewer(getter_AddRefs(viewer));
6846 if (viewer) {
6847 nsCOMPtr<nsIWebBrowserPrint> webBrowserPrint(do_QueryInterface(viewer));
6848 webBrowserPrint.forget(aSink);
6852 #endif
6853 else if (aIID.Equals(NS_GET_IID(nsILoadContext))) {
6854 nsCOMPtr<nsILoadContext> loadContext(do_QueryInterface(mDocShell));
6855 loadContext.forget(aSink);
6858 return *aSink ? NS_OK : NS_ERROR_NO_INTERFACE;
6861 NS_IMETHODIMP
6862 nsGlobalWindowOuter::GetInterface(const nsIID& aIID, void** aSink) {
6863 nsresult rv = GetInterfaceInternal(aIID, aSink);
6864 if (rv == NS_ERROR_NO_INTERFACE) {
6865 return QueryInterface(aIID, aSink);
6867 return rv;
6870 bool nsGlobalWindowOuter::IsSuspended() const {
6871 MOZ_ASSERT(NS_IsMainThread());
6872 // No inner means we are effectively suspended
6873 if (!mInnerWindow) {
6874 return true;
6876 return mInnerWindow->IsSuspended();
6879 bool nsGlobalWindowOuter::IsFrozen() const {
6880 MOZ_ASSERT(NS_IsMainThread());
6881 // No inner means we are effectively frozen
6882 if (!mInnerWindow) {
6883 return true;
6885 return mInnerWindow->IsFrozen();
6888 nsresult nsGlobalWindowOuter::FireDelayedDOMEvents(bool aIncludeSubWindows) {
6889 FORWARD_TO_INNER(FireDelayedDOMEvents, (aIncludeSubWindows),
6890 NS_ERROR_UNEXPECTED);
6893 //*****************************************************************************
6894 // nsGlobalWindowOuter: Window Control Functions
6895 //*****************************************************************************
6897 nsPIDOMWindowOuter* nsGlobalWindowOuter::GetInProcessParentInternal() {
6898 nsCOMPtr<nsPIDOMWindowOuter> parent = GetInProcessParent();
6900 if (parent && parent != this) {
6901 return parent;
6904 return nullptr;
6907 void nsGlobalWindowOuter::UnblockScriptedClosing() {
6908 mBlockScriptedClosingFlag = false;
6911 class AutoUnblockScriptClosing {
6912 private:
6913 RefPtr<nsGlobalWindowOuter> mWin;
6915 public:
6916 explicit AutoUnblockScriptClosing(nsGlobalWindowOuter* aWin) : mWin(aWin) {
6917 MOZ_ASSERT(mWin);
6919 ~AutoUnblockScriptClosing() {
6920 void (nsGlobalWindowOuter::*run)() =
6921 &nsGlobalWindowOuter::UnblockScriptedClosing;
6922 nsCOMPtr<nsIRunnable> caller = NewRunnableMethod(
6923 "AutoUnblockScriptClosing::~AutoUnblockScriptClosing", mWin, run);
6924 mWin->Dispatch(TaskCategory::Other, caller.forget());
6928 nsresult nsGlobalWindowOuter::OpenInternal(
6929 const nsAString& aUrl, const nsAString& aName, const nsAString& aOptions,
6930 bool aDialog, bool aContentModal, bool aCalledNoScript, bool aDoJSFixups,
6931 bool aNavigate, nsIArray* argv, nsISupports* aExtraArgument,
6932 nsDocShellLoadState* aLoadState, bool aForceNoOpener, PrintKind aPrintKind,
6933 BrowsingContext** aReturn) {
6934 #ifdef DEBUG
6935 uint32_t argc = 0;
6936 if (argv) argv->GetLength(&argc);
6937 #endif
6939 MOZ_ASSERT(!aExtraArgument || (!argv && argc == 0),
6940 "Can't pass in arguments both ways");
6941 MOZ_ASSERT(!aCalledNoScript || (!argv && argc == 0),
6942 "Can't pass JS args when called via the noscript methods");
6944 mozilla::Maybe<AutoUnblockScriptClosing> closeUnblocker;
6946 // Calls to window.open from script should navigate.
6947 MOZ_ASSERT(aCalledNoScript || aNavigate);
6949 *aReturn = nullptr;
6951 nsCOMPtr<nsIWebBrowserChrome> chrome = GetWebBrowserChrome();
6952 if (!chrome) {
6953 // No chrome means we don't want to go through with this open call
6954 // -- see nsIWindowWatcher.idl
6955 return NS_ERROR_NOT_AVAILABLE;
6958 NS_ASSERTION(mDocShell, "Must have docshell here");
6960 NS_ConvertUTF16toUTF8 optionsUtf8(aOptions);
6962 WindowFeatures features;
6963 if (!features.Tokenize(optionsUtf8)) {
6964 return NS_ERROR_FAILURE;
6967 bool forceNoOpener = aForceNoOpener;
6968 if (features.Exists("noopener")) {
6969 forceNoOpener = features.GetBool("noopener");
6970 features.Remove("noopener");
6973 bool forceNoReferrer = false;
6974 if (features.Exists("noreferrer")) {
6975 forceNoReferrer = features.GetBool("noreferrer");
6976 if (forceNoReferrer) {
6977 // noreferrer implies noopener
6978 forceNoOpener = true;
6980 features.Remove("noreferrer");
6983 nsAutoCString options;
6984 features.Stringify(options);
6986 // If noopener is force-enabled for the current document, then set noopener to
6987 // true, and clear the name to "_blank".
6988 nsAutoString windowName(aName);
6989 if (nsDocShell::Cast(GetDocShell())->NoopenerForceEnabled()) {
6990 // FIXME: Eventually bypass force-enabling noopener if `aPrintKind !=
6991 // PrintKind::None`, so that we can print pages with noopener force-enabled.
6992 // This will require relaxing assertions elsewhere.
6993 if (aPrintKind != PrintKind::None) {
6994 NS_WARNING(
6995 "printing frames with noopener force-enabled isn't supported yet");
6996 return NS_ERROR_FAILURE;
6999 MOZ_DIAGNOSTIC_ASSERT(aNavigate,
7000 "cannot OpenNoNavigate if noopener is force-enabled");
7002 forceNoOpener = true;
7003 windowName = u"_blank"_ns;
7006 bool windowExists = WindowExists(windowName, forceNoOpener, !aCalledNoScript);
7008 // XXXbz When this gets fixed to not use LegacyIsCallerNativeCode()
7009 // (indirectly) maybe we can nix the AutoJSAPI usage OnLinkClickEvent::Run.
7010 // But note that if you change this to GetEntryGlobal(), say, then
7011 // OnLinkClickEvent::Run will need a full-blown AutoEntryScript.
7012 const bool checkForPopup =
7013 !nsContentUtils::LegacyIsCallerChromeOrNativeCode() && !aDialog &&
7014 !windowExists;
7016 // Note: the Void handling here is very important, because the window watcher
7017 // expects a null URL string (not an empty string) if there is no URL to load.
7018 nsCString url;
7019 url.SetIsVoid(true);
7020 nsresult rv = NS_OK;
7022 nsCOMPtr<nsIURI> uri;
7024 // It's important to do this security check before determining whether this
7025 // window opening should be blocked, to ensure that we don't FireAbuseEvents
7026 // for a window opening that wouldn't have succeeded in the first place.
7027 if (!aUrl.IsEmpty()) {
7028 AppendUTF16toUTF8(aUrl, url);
7030 // It's safe to skip the security check below if we're not a dialog
7031 // because window.openDialog is not callable from content script. See bug
7032 // 56851.
7034 // If we're not navigating, we assume that whoever *does* navigate the
7035 // window will do a security check of their own.
7036 if (!url.IsVoid() && !aDialog && aNavigate)
7037 rv = SecurityCheckURL(url.get(), getter_AddRefs(uri));
7040 if (NS_FAILED(rv)) return rv;
7042 PopupBlocker::PopupControlState abuseLevel =
7043 PopupBlocker::GetPopupControlState();
7044 if (checkForPopup) {
7045 abuseLevel = mBrowsingContext->RevisePopupAbuseLevel(abuseLevel);
7046 if (abuseLevel >= PopupBlocker::openBlocked) {
7047 if (!aCalledNoScript) {
7048 // If script in some other window is doing a window.open on us and
7049 // it's being blocked, then it's OK to close us afterwards, probably.
7050 // But if we're doing a window.open on ourselves and block the popup,
7051 // prevent this window from closing until after this script terminates
7052 // so that whatever popup blocker UI the app has will be visible.
7053 nsCOMPtr<nsPIDOMWindowInner> entryWindow =
7054 do_QueryInterface(GetEntryGlobal());
7055 // Note that entryWindow can be null here if some JS component was the
7056 // place where script was entered for this JS execution.
7057 if (entryWindow && entryWindow->GetOuterWindow() == this) {
7058 mBlockScriptedClosingFlag = true;
7059 closeUnblocker.emplace(this);
7063 FireAbuseEvents(aUrl, windowName, aOptions);
7064 return aDoJSFixups ? NS_OK : NS_ERROR_FAILURE;
7068 RefPtr<BrowsingContext> domReturn;
7070 nsCOMPtr<nsIWindowWatcher> wwatch =
7071 do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
7072 NS_ENSURE_TRUE(wwatch, rv);
7074 NS_ConvertUTF16toUTF8 name(windowName);
7076 nsCOMPtr<nsPIWindowWatcher> pwwatch(do_QueryInterface(wwatch));
7077 NS_ENSURE_STATE(pwwatch);
7079 MOZ_ASSERT_IF(checkForPopup, abuseLevel < PopupBlocker::openBlocked);
7080 // At this point we should know for a fact that if checkForPopup then
7081 // abuseLevel < PopupBlocker::openBlocked, so we could just check for
7082 // abuseLevel == PopupBlocker::openControlled. But let's be defensive just in
7083 // case and treat anything that fails the above assert as a spam popup too, if
7084 // it ever happens.
7085 bool isPopupSpamWindow =
7086 checkForPopup && (abuseLevel >= PopupBlocker::openControlled);
7088 const auto wwPrintKind = [&] {
7089 switch (aPrintKind) {
7090 case PrintKind::None:
7091 return nsPIWindowWatcher::PRINT_NONE;
7092 case PrintKind::InternalPrint:
7093 return nsPIWindowWatcher::PRINT_INTERNAL;
7094 case PrintKind::WindowDotPrint:
7095 return nsPIWindowWatcher::PRINT_WINDOW_DOT_PRINT;
7097 MOZ_ASSERT_UNREACHABLE("Wat");
7098 return nsPIWindowWatcher::PRINT_NONE;
7099 }();
7102 // Reset popup state while opening a window to prevent the
7103 // current state from being active the whole time a modal
7104 // dialog is open.
7105 AutoPopupStatePusher popupStatePusher(PopupBlocker::openAbused, true);
7107 if (!aCalledNoScript) {
7108 // We asserted at the top of this function that aNavigate is true for
7109 // !aCalledNoScript.
7110 rv = pwwatch->OpenWindow2(this, url, name, options,
7111 /* aCalledFromScript = */ true, aDialog,
7112 aNavigate, argv, isPopupSpamWindow,
7113 forceNoOpener, forceNoReferrer, wwPrintKind,
7114 aLoadState, getter_AddRefs(domReturn));
7115 } else {
7116 // Force a system caller here so that the window watcher won't screw us
7117 // up. We do NOT want this case looking at the JS context on the stack
7118 // when searching. Compare comments on
7119 // nsIDOMWindow::OpenWindow and nsIWindowWatcher::OpenWindow.
7121 // Note: Because nsWindowWatcher is so broken, it's actually important
7122 // that we don't force a system caller here, because that screws it up
7123 // when it tries to compute the caller principal to associate with dialog
7124 // arguments. That whole setup just really needs to be rewritten. :-(
7125 Maybe<AutoNoJSAPI> nojsapi;
7126 if (!aContentModal) {
7127 nojsapi.emplace();
7130 rv = pwwatch->OpenWindow2(this, url, name, options,
7131 /* aCalledFromScript = */ false, aDialog,
7132 aNavigate, aExtraArgument, isPopupSpamWindow,
7133 forceNoOpener, forceNoReferrer, wwPrintKind,
7134 aLoadState, getter_AddRefs(domReturn));
7138 NS_ENSURE_SUCCESS(rv, rv);
7140 // success!
7142 if (!aCalledNoScript && !windowExists && uri && !forceNoOpener) {
7143 MaybeAllowStorageForOpenedWindow(uri);
7146 if (domReturn && aDoJSFixups) {
7147 nsCOMPtr<nsIDOMChromeWindow> chrome_win(
7148 do_QueryInterface(domReturn->GetDOMWindow()));
7149 if (!chrome_win) {
7150 // A new non-chrome window was created from a call to
7151 // window.open() from JavaScript, make sure there's a document in
7152 // the new window. We do this by simply asking the new window for
7153 // its document, this will synchronously create an empty document
7154 // if there is no document in the window.
7155 // XXXbz should this just use EnsureInnerWindow()?
7157 // Force document creation.
7158 if (nsPIDOMWindowOuter* win = domReturn->GetDOMWindow()) {
7159 nsCOMPtr<Document> doc = win->GetDoc();
7160 Unused << doc;
7165 domReturn.forget(aReturn);
7166 return NS_OK;
7169 void nsGlobalWindowOuter::MaybeAllowStorageForOpenedWindow(nsIURI* aURI) {
7170 nsGlobalWindowInner* inner = GetCurrentInnerWindowInternal();
7171 if (NS_WARN_IF(!inner)) {
7172 return;
7175 // No 3rd party URL/window.
7176 if (!nsContentUtils::IsThirdPartyWindowOrChannel(inner, nullptr, aURI)) {
7177 return;
7180 Document* doc = inner->GetDoc();
7181 if (!doc) {
7182 return;
7184 nsCOMPtr<nsIPrincipal> principal = BasePrincipal::CreateContentPrincipal(
7185 aURI, doc->NodePrincipal()->OriginAttributesRef());
7187 // We don't care when the asynchronous work finishes here.
7188 Unused << ContentBlocking::AllowAccessFor(principal, GetBrowsingContext(),
7189 ContentBlockingNotifier::eOpener);
7192 //*****************************************************************************
7193 // nsGlobalWindowOuter: Helper Functions
7194 //*****************************************************************************
7196 already_AddRefed<nsIDocShellTreeOwner> nsPIDOMWindowOuter::GetTreeOwner() {
7197 // If there's no docShellAsItem, this window must have been closed,
7198 // in that case there is no tree owner.
7200 if (!mDocShell) {
7201 return nullptr;
7204 nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
7205 mDocShell->GetTreeOwner(getter_AddRefs(treeOwner));
7206 return treeOwner.forget();
7209 already_AddRefed<nsIBaseWindow> nsPIDOMWindowOuter::GetTreeOwnerWindow() {
7210 nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
7212 // If there's no mDocShell, this window must have been closed,
7213 // in that case there is no tree owner.
7215 if (mDocShell) {
7216 mDocShell->GetTreeOwner(getter_AddRefs(treeOwner));
7219 nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(treeOwner);
7220 return baseWindow.forget();
7223 already_AddRefed<nsIWebBrowserChrome>
7224 nsPIDOMWindowOuter::GetWebBrowserChrome() {
7225 nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner();
7227 nsCOMPtr<nsIWebBrowserChrome> browserChrome = do_GetInterface(treeOwner);
7228 return browserChrome.forget();
7231 nsIScrollableFrame* nsGlobalWindowOuter::GetScrollFrame() {
7232 if (!mDocShell) {
7233 return nullptr;
7236 PresShell* presShell = mDocShell->GetPresShell();
7237 if (presShell) {
7238 return presShell->GetRootScrollFrameAsScrollable();
7240 return nullptr;
7243 nsresult nsGlobalWindowOuter::SecurityCheckURL(const char* aURL,
7244 nsIURI** aURI) {
7245 nsCOMPtr<nsPIDOMWindowInner> sourceWindow =
7246 do_QueryInterface(GetEntryGlobal());
7247 if (!sourceWindow) {
7248 sourceWindow = GetCurrentInnerWindow();
7250 AutoJSContext cx;
7251 nsGlobalWindowInner* sourceWin = nsGlobalWindowInner::Cast(sourceWindow);
7252 JSAutoRealm ar(cx, sourceWin->GetGlobalJSObject());
7254 // Resolve the baseURI, which could be relative to the calling window.
7256 // Note the algorithm to get the base URI should match the one
7257 // used to actually kick off the load in nsWindowWatcher.cpp.
7258 nsCOMPtr<Document> doc = sourceWindow->GetDoc();
7259 nsIURI* baseURI = nullptr;
7260 auto encoding = UTF_8_ENCODING; // default to utf-8
7261 if (doc) {
7262 baseURI = doc->GetDocBaseURI();
7263 encoding = doc->GetDocumentCharacterSet();
7265 nsCOMPtr<nsIURI> uri;
7266 nsresult rv = NS_NewURI(getter_AddRefs(uri), nsDependentCString(aURL),
7267 encoding, baseURI);
7268 if (NS_WARN_IF(NS_FAILED(rv))) {
7269 return NS_ERROR_DOM_SYNTAX_ERR;
7272 if (NS_FAILED(nsContentUtils::GetSecurityManager()->CheckLoadURIFromScript(
7273 cx, uri))) {
7274 return NS_ERROR_FAILURE;
7277 uri.forget(aURI);
7278 return NS_OK;
7281 void nsGlobalWindowOuter::FlushPendingNotifications(FlushType aType) {
7282 if (mDoc) {
7283 mDoc->FlushPendingNotifications(aType);
7287 void nsGlobalWindowOuter::EnsureSizeAndPositionUpToDate() {
7288 // If we're a subframe, make sure our size is up to date. Make sure to go
7289 // through the document chain rather than the window chain to not flush on
7290 // detached iframes, see bug 1545516.
7291 if (mDoc && mDoc->StyleOrLayoutObservablyDependsOnParentDocumentLayout()) {
7292 RefPtr<Document> parent = mDoc->GetInProcessParentDocument();
7293 parent->FlushPendingNotifications(FlushType::Layout);
7297 already_AddRefed<nsISupports> nsGlobalWindowOuter::SaveWindowState() {
7298 if (!mContext || !GetWrapperPreserveColor()) {
7299 // The window may be getting torn down; don't bother saving state.
7300 return nullptr;
7303 nsGlobalWindowInner* inner = GetCurrentInnerWindowInternal();
7304 NS_ASSERTION(inner, "No inner window to save");
7306 // Don't do anything else to this inner window! After this point, all
7307 // calls to SetTimeoutOrInterval will create entries in the timeout
7308 // list that will only run after this window has come out of the bfcache.
7309 // Also, while we're frozen, we won't dispatch online/offline events
7310 // to the page.
7311 inner->Freeze();
7313 nsCOMPtr<nsISupports> state = new WindowStateHolder(inner);
7315 MOZ_LOG(gPageCacheLog, LogLevel::Debug,
7316 ("saving window state, state = %p", (void*)state));
7318 return state.forget();
7321 nsresult nsGlobalWindowOuter::RestoreWindowState(nsISupports* aState) {
7322 if (!mContext || !GetWrapperPreserveColor()) {
7323 // The window may be getting torn down; don't bother restoring state.
7324 return NS_OK;
7327 nsCOMPtr<WindowStateHolder> holder = do_QueryInterface(aState);
7328 NS_ENSURE_TRUE(holder, NS_ERROR_FAILURE);
7330 MOZ_LOG(gPageCacheLog, LogLevel::Debug,
7331 ("restoring window state, state = %p", (void*)holder));
7333 // And we're ready to go!
7334 nsGlobalWindowInner* inner = GetCurrentInnerWindowInternal();
7336 // if a link is focused, refocus with the FLAG_SHOWRING flag set. This makes
7337 // it easy to tell which link was last clicked when going back a page.
7338 RefPtr<Element> focusedElement = inner->GetFocusedElement();
7339 if (nsContentUtils::ContentIsLink(focusedElement)) {
7340 if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
7341 fm->SetFocus(focusedElement, nsIFocusManager::FLAG_NOSCROLL |
7342 nsIFocusManager::FLAG_SHOWRING);
7346 inner->Thaw();
7348 holder->DidRestoreWindow();
7350 return NS_OK;
7353 void nsGlobalWindowOuter::AddSizeOfIncludingThis(
7354 nsWindowSizes& aWindowSizes) const {
7355 aWindowSizes.mDOMOtherSize += aWindowSizes.mState.mMallocSizeOf(this);
7358 uint32_t nsGlobalWindowOuter::GetAutoActivateVRDisplayID() {
7359 uint32_t retVal = mAutoActivateVRDisplayID;
7360 mAutoActivateVRDisplayID = 0;
7361 return retVal;
7364 void nsGlobalWindowOuter::SetAutoActivateVRDisplayID(
7365 uint32_t aAutoActivateVRDisplayID) {
7366 mAutoActivateVRDisplayID = aAutoActivateVRDisplayID;
7369 already_AddRefed<nsWindowRoot> nsGlobalWindowOuter::GetWindowRootOuter() {
7370 nsCOMPtr<nsPIWindowRoot> root = GetTopWindowRoot();
7371 return root.forget().downcast<nsWindowRoot>();
7374 nsIDOMWindowUtils* nsGlobalWindowOuter::WindowUtils() {
7375 if (!mWindowUtils) {
7376 mWindowUtils = new nsDOMWindowUtils(this);
7378 return mWindowUtils;
7381 bool nsGlobalWindowOuter::IsInSyncOperation() {
7382 return GetExtantDoc() && GetExtantDoc()->IsInSyncOperation();
7385 // Note: This call will lock the cursor, it will not change as it moves.
7386 // To unlock, the cursor must be set back to Auto.
7387 void nsGlobalWindowOuter::SetCursorOuter(const nsACString& aCursor,
7388 ErrorResult& aError) {
7389 auto cursor = StyleCursorKind::Auto;
7390 if (!Servo_CursorKind_Parse(&aCursor, &cursor)) {
7391 // FIXME: It's a bit weird that this doesn't throw but stuff below does, but
7392 // matches previous behavior so...
7393 return;
7396 RefPtr<nsPresContext> presContext;
7397 if (mDocShell) {
7398 presContext = mDocShell->GetPresContext();
7401 if (presContext) {
7402 // Need root widget.
7403 PresShell* presShell = mDocShell->GetPresShell();
7404 if (!presShell) {
7405 aError.Throw(NS_ERROR_FAILURE);
7406 return;
7409 nsViewManager* vm = presShell->GetViewManager();
7410 if (!vm) {
7411 aError.Throw(NS_ERROR_FAILURE);
7412 return;
7415 nsView* rootView = vm->GetRootView();
7416 if (!rootView) {
7417 aError.Throw(NS_ERROR_FAILURE);
7418 return;
7421 nsIWidget* widget = rootView->GetNearestWidget(nullptr);
7422 if (!widget) {
7423 aError.Throw(NS_ERROR_FAILURE);
7424 return;
7427 // Call esm and set cursor.
7428 aError = presContext->EventStateManager()->SetCursor(
7429 cursor, nullptr, Nothing(), widget, true);
7433 NS_IMETHODIMP
7434 nsGlobalWindowOuter::GetBrowserDOMWindow(nsIBrowserDOMWindow** aBrowserWindow) {
7435 MOZ_RELEASE_ASSERT(IsChromeWindow());
7436 FORWARD_TO_INNER(GetBrowserDOMWindow, (aBrowserWindow), NS_ERROR_UNEXPECTED);
7439 nsIBrowserDOMWindow* nsGlobalWindowOuter::GetBrowserDOMWindowOuter() {
7440 MOZ_ASSERT(IsChromeWindow());
7441 return mChromeFields.mBrowserDOMWindow;
7444 void nsGlobalWindowOuter::SetBrowserDOMWindowOuter(
7445 nsIBrowserDOMWindow* aBrowserWindow) {
7446 MOZ_ASSERT(IsChromeWindow());
7447 mChromeFields.mBrowserDOMWindow = aBrowserWindow;
7450 ChromeMessageBroadcaster* nsGlobalWindowOuter::GetMessageManager() {
7451 if (!mInnerWindow) {
7452 NS_WARNING("No inner window available!");
7453 return nullptr;
7455 return GetCurrentInnerWindowInternal()->MessageManager();
7458 ChromeMessageBroadcaster* nsGlobalWindowOuter::GetGroupMessageManager(
7459 const nsAString& aGroup) {
7460 if (!mInnerWindow) {
7461 NS_WARNING("No inner window available!");
7462 return nullptr;
7464 return GetCurrentInnerWindowInternal()->GetGroupMessageManager(aGroup);
7467 void nsGlobalWindowOuter::InitWasOffline() { mWasOffline = NS_IsOffline(); }
7469 #if defined(MOZ_WIDGET_ANDROID)
7470 int16_t nsGlobalWindowOuter::Orientation(CallerType aCallerType) const {
7471 return nsContentUtils::ResistFingerprinting(aCallerType)
7473 : WindowOrientationObserver::OrientationAngle();
7475 #endif
7477 #if defined(_WINDOWS_) && !defined(MOZ_WRAPPED_WINDOWS_H)
7478 # pragma message( \
7479 "wrapper failure reason: " MOZ_WINDOWS_WRAPPER_DISABLED_REASON)
7480 # error "Never include unwrapped windows.h in this file!"
7481 #endif
7483 // Helper called by methods that move/resize the window,
7484 // to ensure the presContext (if any) is aware of resolution
7485 // change that may happen in multi-monitor configuration.
7486 void nsGlobalWindowOuter::CheckForDPIChange() {
7487 if (mDocShell) {
7488 RefPtr<nsPresContext> presContext = mDocShell->GetPresContext();
7489 if (presContext) {
7490 if (presContext->DeviceContext()->CheckDPIChange()) {
7491 presContext->UIResolutionChanged();
7497 nsresult nsGlobalWindowOuter::Dispatch(
7498 TaskCategory aCategory, already_AddRefed<nsIRunnable>&& aRunnable) {
7499 MOZ_RELEASE_ASSERT(NS_IsMainThread());
7500 if (GetDocGroup()) {
7501 return GetDocGroup()->Dispatch(aCategory, std::move(aRunnable));
7503 return DispatcherTrait::Dispatch(aCategory, std::move(aRunnable));
7506 nsISerialEventTarget* nsGlobalWindowOuter::EventTargetFor(
7507 TaskCategory aCategory) const {
7508 MOZ_RELEASE_ASSERT(NS_IsMainThread());
7509 if (GetDocGroup()) {
7510 return GetDocGroup()->EventTargetFor(aCategory);
7512 return DispatcherTrait::EventTargetFor(aCategory);
7515 AbstractThread* nsGlobalWindowOuter::AbstractMainThreadFor(
7516 TaskCategory aCategory) {
7517 MOZ_RELEASE_ASSERT(NS_IsMainThread());
7518 if (GetDocGroup()) {
7519 return GetDocGroup()->AbstractMainThreadFor(aCategory);
7521 return DispatcherTrait::AbstractMainThreadFor(aCategory);
7524 void nsGlobalWindowOuter::MaybeResetWindowName(Document* aNewDocument) {
7525 MOZ_ASSERT(aNewDocument);
7527 if (!StaticPrefs::privacy_window_name_update_enabled()) {
7528 return;
7531 // We only reset the window name for the top-level content as well as storing
7532 // in session entries.
7533 if (!GetBrowsingContext()->IsTopContent()) {
7534 return;
7537 // Following implements https://html.spec.whatwg.org/#history-traversal:
7538 // Step 4.2. Check if the loading document has a different origin than the
7539 // previous document.
7541 // We don't need to do anything if we haven't loaded a non-initial document.
7542 if (!GetBrowsingContext()->GetHasLoadedNonInitialDocument()) {
7543 return;
7546 // If we have an existing doucment, directly check the document prinicpals
7547 // with the new document to know if it is cross-origin.
7549 // Note that there will be an issue of initial document handling in Fission
7550 // when running the WPT unset_context_name-1.html. In the test, the first
7551 // about:blank page would be loaded with the principal of the testing domain
7552 // in Fission and the window.name will be set there. Then, The window.name
7553 // won't be reset after navigating to the testing page because the principal
7554 // is the same. But, it won't be the case for non-Fission mode that the first
7555 // about:blank will be loaded with a null principal and the window.name will
7556 // be reset when loading the test page.
7557 if (mDoc && mDoc->NodePrincipal()->EqualsConsideringDomain(
7558 aNewDocument->NodePrincipal())) {
7559 return;
7562 // If we don't have an existing document, and if it's not the initial
7563 // about:blank, we could be loading a document because of the
7564 // process-switching. In this case, this should be a cross-origin navigation.
7566 // Step 4.2.1 Store the window.name into all session history entries that have
7567 // the same origin as the privious document.
7568 nsDocShell::Cast(mDocShell)->StoreWindowNameToSHEntries();
7570 // Step 4.2.2 Clear the window.name if the browsing context is the top-level
7571 // content and doesn't have an opener.
7573 // We need to reset the window name in case of a cross-origin navigation,
7574 // without an opener.
7575 RefPtr<BrowsingContext> opener = GetOpenerBrowsingContext();
7576 if (opener) {
7577 return;
7580 Unused << mBrowsingContext->SetName(EmptyString());
7583 nsGlobalWindowOuter::TemporarilyDisableDialogs::TemporarilyDisableDialogs(
7584 nsGlobalWindowOuter* aWindow)
7585 : mSavedDialogsEnabled(false) {
7586 MOZ_ASSERT(aWindow);
7587 nsGlobalWindowOuter* topWindowOuter =
7588 aWindow->GetInProcessScriptableTopInternal();
7589 if (!topWindowOuter) {
7590 NS_ERROR(
7591 "nsGlobalWindowOuter::TemporarilyDisableDialogs used without a top "
7592 "window?");
7593 return;
7596 // TODO: Warn if no top window?
7597 nsGlobalWindowInner* topWindow =
7598 topWindowOuter->GetCurrentInnerWindowInternal();
7599 if (topWindow) {
7600 mTopWindow = topWindow;
7601 mSavedDialogsEnabled = mTopWindow->mAreDialogsEnabled;
7602 mTopWindow->mAreDialogsEnabled = false;
7606 nsGlobalWindowOuter::TemporarilyDisableDialogs::~TemporarilyDisableDialogs() {
7607 if (mTopWindow) {
7608 mTopWindow->mAreDialogsEnabled = mSavedDialogsEnabled;
7612 /* static */
7613 already_AddRefed<nsGlobalWindowOuter> nsGlobalWindowOuter::Create(
7614 nsDocShell* aDocShell, bool aIsChrome) {
7615 uint64_t outerWindowID = aDocShell->GetOuterWindowID();
7616 RefPtr<nsGlobalWindowOuter> window = new nsGlobalWindowOuter(outerWindowID);
7617 if (aIsChrome) {
7618 window->mIsChrome = true;
7620 window->SetDocShell(aDocShell);
7622 window->InitWasOffline();
7623 return window.forget();
7626 nsIURI* nsPIDOMWindowOuter::GetDocumentURI() const {
7627 return mDoc ? mDoc->GetDocumentURI() : mDocumentURI.get();
7630 void nsPIDOMWindowOuter::MaybeCreateDoc() {
7631 MOZ_ASSERT(!mDoc);
7632 if (nsIDocShell* docShell = GetDocShell()) {
7633 // Note that |document| here is the same thing as our mDoc, but we
7634 // don't have to explicitly set the member variable because the docshell
7635 // has already called SetNewDocument().
7636 nsCOMPtr<Document> document = docShell->GetDocument();
7637 Unused << document;
7641 void nsPIDOMWindowOuter::SetChromeEventHandlerInternal(
7642 EventTarget* aChromeEventHandler) {
7643 // Out-of-line so we don't need to include ContentFrameMessageManager.h in
7644 // nsPIDOMWindow.h.
7645 mChromeEventHandler = aChromeEventHandler;
7647 // mParentTarget and mMessageManager will be set when the next event is
7648 // dispatched or someone asks for our message manager.
7649 mParentTarget = nullptr;
7650 mMessageManager = nullptr;
7653 mozilla::dom::DocGroup* nsPIDOMWindowOuter::GetDocGroup() const {
7654 Document* doc = GetExtantDoc();
7655 if (doc) {
7656 return doc->GetDocGroup();
7658 return nullptr;
7661 nsPIDOMWindowOuter::nsPIDOMWindowOuter(uint64_t aWindowID)
7662 : mFrameElement(nullptr),
7663 mModalStateDepth(0),
7664 mIsBackground(false),
7665 mMediaSuspend(StaticPrefs::media_block_autoplay_until_in_foreground()
7666 ? nsISuspendedTypes::SUSPENDED_BLOCK
7667 : nsISuspendedTypes::NONE_SUSPENDED),
7668 mDesktopModeViewport(false),
7669 mIsRootOuterWindow(false),
7670 mInnerWindow(nullptr),
7671 mWindowID(aWindowID),
7672 mMarkedCCGeneration(0) {}
7674 nsPIDOMWindowOuter::~nsPIDOMWindowOuter() = default;